2
0
mirror of https://github.com/Revertron/Alfis synced 2024-11-07 09:20:31 +00:00

Implemented multi-threaded CPU-miner.

This commit is contained in:
Revertron 2021-01-14 18:34:43 +01:00
parent 01f37cc238
commit 4703ae6f49
9 changed files with 299 additions and 119 deletions

View File

@ -8,7 +8,7 @@ edition = "2018"
[dependencies]
rust-crypto = "^0.2"
num_cpus = "1.10.1"
num_cpus = "1.13.0"
byteorder = "1.3.2"
web-view = { version = "0.5.4", features = [] }
serde = { version = "1.0.102", features = ["derive"] }

View File

@ -24,8 +24,9 @@ pub struct Block {
pub nonce: u64,
#[serde(skip_serializing_if = "Option::is_none")]
pub transaction: Option<Transaction>,
#[serde(default, skip_serializing_if = "Key::is_zero")]
pub prev_block_hash: Key,
#[serde(default, skip_serializing_if = "Key::is_empty")]
#[serde(default, skip_serializing_if = "Key::is_zero")]
pub hash: Key,
}
@ -36,7 +37,8 @@ impl Block {
timestamp,
chain_id,
version,
difficulty: 18,
// TODO make difficulty parameter
difficulty: 20,
random: 0,
nonce: 0,
transaction,

View File

@ -19,16 +19,6 @@ impl Blockchain {
block
}
pub fn genesis(chain_id: u32, version: u32) -> Block {
Block::new(0, Utc::now().timestamp(), chain_id, version, Key::zero32(), None)
}
pub fn make_genesis(&mut self) {
let mut genesis = Self::genesis(self.chain_id, self.version);
genesis.mine();
self.add_block(genesis);
}
pub fn add_block(&mut self, block: Block) {
if self.check_block(&block, None) {
println!("Adding block:\n{:?}", &block);

View File

@ -34,6 +34,14 @@ impl Context {
pub fn set_keystore(&mut self, keystore: Keystore) {
self.keystore = keystore;
}
pub fn get_blockchain(&self) -> &Blockchain {
&self.blockchain
}
pub fn add_salt(&mut self, name: String, salt: String) {
&self.settings.salts.insert(name, salt);
}
}
pub struct Settings {
@ -44,7 +52,7 @@ pub struct Settings {
impl Settings {
/// TODO parse settings
pub fn new<S: Into<String>>(chain_id: u32, version: u32, settings: S) -> Settings {
Settings { chain_id, version, salts: HashMap::new() }
pub fn new<S: Into<String>>(settings: S) -> Settings {
Settings { chain_id: 42, version: 0, salts: HashMap::new() }
}
}

View File

@ -83,6 +83,18 @@ impl Key {
self.data.is_empty()
}
pub fn is_zero(&self) -> bool {
if self.data.is_empty() {
return true;
}
for x in self.data.iter() {
if *x != 0 {
return false;
}
}
return true;
}
/// Returns a byte slice of the hash contents.
pub fn as_bytes(&self) -> &[u8] {
&self.data

View File

@ -1,38 +1,56 @@
#![windows_subsystem = "windows"]
extern crate web_view;
use alfis::{Blockchain, Block, Action, Transaction, Keystore, Key, Settings, Context};
use alfis::miner::Miner;
use alfis::utils::random_string;
use web_view::*;
use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex};
extern crate serde;
use serde::{Serialize, Deserialize};
use alfis::miner::Miner;
use std::collections::HashMap;
extern crate serde_json;
const ONE_YEAR: u16 = 365;
const GENESIS_ZONE: &str = "ygg";
const GENESIS_ZONE_DIFFICULTY: u16 = 20;
const SALT_LENGTH: usize = 20;
fn main() {
println!("ALFIS 0.1.0");
let settings = Settings::new(42, 0,"");
let settings = Settings::new("");
let keystore: Keystore = Keystore::from_file("default.key", "").unwrap();
let blockchain: Blockchain = Blockchain::new(settings.chain_id, settings.version);
let context: Arc<Mutex<Context>> = Arc::new(Mutex::new(Context::new(settings, keystore, blockchain)));
let miner: Arc<Mutex<Miner>> = Arc::new(Mutex::new(Miner::new(context.clone())));
let mut miner_obj = Miner::new(context.clone());
miner_obj.start_mining_thread();
let miner: Arc<Mutex<Miner>> = Arc::new(Mutex::new(miner_obj));
// TODO check settings and if there is no mention of bootstrap nodes, generate genesis block
/*let chain_for_genesis = blockchain.clone();
thread::spawn(move || {
let mut block = Blockchain::genesis(42, 0);
// TODO remake genesis to use Miner
block.mine();
chain_for_genesis.lock().unwrap().add_block(block);
println!("Blockchain with genesis block has been created");
});*/
create_genesis_if_needed(&context, &miner);
run_interface(context.clone(), miner.clone());
}
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
// TODO check settings and if there is no mention of bootstrap nodes, generate genesis block
let keystore = {
// This code block makes it possible to contain quick lock here, and let the miner below work
let context_guard = context.lock().unwrap();
if context_guard.get_blockchain().blocks.is_empty() {
// If blockchain is empty, we are going to mine a Genesis block
Some(context_guard.get_keystore())
} else {
None
}
};
if keystore.is_some() {
create_genesis(miner.clone(), GENESIS_ZONE, &keystore.unwrap(), GENESIS_ZONE_DIFFICULTY);
}
}
fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
let file_content = include_str!("index.html");
let styles = inline_style(include_str!("bulma.css"));
@ -65,11 +83,17 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
c.set_keystore(Keystore::new());
}
CreateDomain { name, records, tags } => {
let mut c = context.lock().unwrap();
create_domain(miner.clone(), name, records, tags, c.get_keystore());
let salt = random_string(SALT_LENGTH);
let keystore = {
let mut guard = context.lock().unwrap();
guard.add_salt(name.clone(), salt.clone());
guard.get_keystore()
};
create_domain(miner.clone(), name, salt, &keystore);
}
ChangeDomain { name, records, tags } => {
let keystore = { context.lock().unwrap().get_keystore() };
// TODO
}
RenewDomain { name, days } => {}
TransferDomain { name, owner } => {}
@ -81,18 +105,38 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
.unwrap();
}
fn create_domain(miner: Arc<Mutex<Miner>>, name: String, records: String, tags: String, keystore: Keystore) {
let rec_vector: Vec<String> = records.trim().split("\n").map(|s| s.trim()).map(String::from).collect();
let tags_vector: Vec<String> = tags.trim().split(",").map(|s| s.trim()).map(String::from).collect();
let mut transaction = { transaction_new_domain(keystore, name, rec_vector, tags_vector, 365) };
fn create_genesis<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, keystore: &Keystore, difficulty: u16) {
// Creating transaction
// TODO Do we need to add here an owners key?
let action = Action::genesis(name.into(), Key::zero32(), difficulty);
let mut transaction = Transaction::new(action, keystore.get_public().clone());
// Signing it with private key from Signature
let sign_hash = keystore.sign(&transaction.get_bytes());
transaction.set_signature(Key::from_bytes(&sign_hash));
let mut miner_guard = miner.lock().unwrap();
miner_guard.add_transaction(transaction);
miner_guard.mine();
}
fn transaction_claim_name<S: Into<String>>(keystore: Keystore, name: S, salt: S) -> Transaction {
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, salt: S, keystore: &Keystore) {
//let rec_vector: Vec<String> = records.into().trim().split("\n").map(|s| s.trim()).map(String::from).collect();
//let tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
let mut transaction = { transaction_new_domain(keystore, name.into(), salt.into()) };
let mut miner_guard = miner.lock().unwrap();
miner_guard.add_transaction(transaction);
}
fn create_zone<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, salt: S, keystore: &Keystore) {
//let rec_vector: Vec<String> = records.into().trim().split("\n").map(|s| s.trim()).map(String::from).collect();
//let tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
let mut transaction = { transaction_new_domain(keystore, name.into(), salt.into()) };
let mut miner_guard = miner.lock().unwrap();
miner_guard.add_transaction(transaction);
}
fn transaction_claim_name<S: Into<String>>(keystore: &Keystore, name: S, salt: S) -> Transaction {
// Creating transaction
let action = Action::claim_name(name.into(), salt.into(), &keystore);
// TODO Do not use owner for now, make a field in UI and use it if filled
let action = Action::new_domain(name.into(), salt.into(), Key::zero32());
let mut transaction = Transaction::new(action, keystore.get_public().clone());
// Signing it with private key from Signature
let sign_hash = keystore.sign(&transaction.get_bytes());
@ -100,9 +144,13 @@ fn transaction_claim_name<S: Into<String>>(keystore: Keystore, name: S, salt: S)
transaction
}
fn transaction_new_domain<S: Into<String>>(keystore: Keystore, name: S, records: Vec<String>, tags: Vec<String>, days: u16) -> Transaction {
fn transaction_new_domain<S: Into<String>>(keystore: &Keystore, name: S, salt: S) -> Transaction {
let name_string = name.into();
let salt_string = salt.into();
println!("Generating domain {} with salt: {}", name_string, salt_string);
// Creating transaction
let action = Action::new_domain(name.into(), &keystore, records, tags, days);
// TODO Do not use owner for now, make a field in UI and use it if filled
let action = Action::new_domain(name_string, salt_string, Key::zero32());
let mut transaction = Transaction::new(action, keystore.get_public().clone());
// Signing it with private key from Signature
let sign_hash = keystore.sign(&transaction.get_bytes());
@ -110,6 +158,26 @@ fn transaction_new_domain<S: Into<String>>(keystore: Keystore, name: S, records:
transaction
}
/*fn transaction_claim_zone<S: Into<String>>(keystore: &Keystore, hash: S, difficulty: u16) -> Transaction {
// Creating transaction
let action = Action::new_zone(hash.into(), salt.into(), &keystore, difficulty);
let mut transaction = Transaction::new(action, keystore.get_public().clone());
// Signing it with private key from Signature
let sign_hash = keystore.sign(&transaction.get_bytes());
transaction.set_signature(Key::from_bytes(&sign_hash));
transaction
}*/
/*fn transaction_new_zone<S: Into<String>>(keystore: &Keystore, name: S, salt: S, records: Vec<String>, tags: Vec<String>, days: u16) -> Transaction {
// Creating transaction
let action = Action::fill_domain(name.into(), salt.into(), &keystore, records, tags, days);
let mut transaction = Transaction::new(action, keystore.get_public().clone());
// Signing it with private key from Signature
let sign_hash = keystore.sign(&transaction.get_bytes());
transaction.set_signature(Key::from_bytes(&sign_hash));
transaction
}*/
#[derive(Deserialize)]
#[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd {

View File

@ -1,12 +1,14 @@
use crate::{Transaction, Block, Keystore, Key, Context};
use std::sync::{Mutex, Arc};
use std::sync::{Mutex, Arc, Condvar};
use crypto::digest::Digest;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::{AtomicBool, Ordering, AtomicU32};
use chrono::Utc;
use num_bigint::BigUint;
use num_traits::One;
use crypto::sha2::Sha256;
use std::thread;
use std::time::Duration;
use num_cpus;
pub struct Miner {
context: Arc<Mutex<Context>>,
@ -16,6 +18,8 @@ pub struct Miner {
transactions: Arc<Mutex<Vec<Transaction>>>,
last_block: Option<Block>,
running: Arc<AtomicBool>,
mining: Arc<AtomicBool>,
cond_var: Arc<Condvar>
}
impl Miner {
@ -29,83 +33,145 @@ impl Miner {
transactions: Arc::new(Mutex::new(Vec::new())),
last_block: c.blockchain.blocks.last().cloned(),
running: Arc::new(AtomicBool::new(false)),
mining: Arc::new(AtomicBool::new(false)),
cond_var: Arc::new(Condvar::new())
}
}
pub fn add_transaction(&mut self, transaction: Transaction) {
self.transactions.lock().unwrap().push(transaction);
self.cond_var.notify_one();
}
pub fn stop(&mut self) {
self.mining.store(false, Ordering::Relaxed);
self.running.store(false, Ordering::Relaxed);
self.cond_var.notify_all();
}
pub fn mine(&mut self) {
let transaction = { self.transactions.lock().unwrap().first().cloned() };
match transaction {
Some(transaction) => {
self.mine_internal(transaction);
},
None => {
println!("Nothing to mine");
},
}
pub fn start_mining_thread(&mut self) {
let context = self.context.clone();
let transactions = self.transactions.clone();
let running = self.running.clone();
let mining = self.mining.clone();
let cond_var = self.cond_var.clone();
thread::spawn(move || {
running.store(true, Ordering::Relaxed);
while running.load(Ordering::Relaxed) {
// If some transaction is being mined now, we yield
if mining.load(Ordering::Relaxed) {
thread::sleep(Duration::from_millis(100));
continue;
}
let mut lock = transactions.lock().unwrap();
if lock.len() > 0 {
println!("Starting to mine some transaction");
let transaction = lock.remove(0);
mining.store(true, Ordering::Relaxed);
Miner::mine_internal(context.clone(), transactions.clone(), transaction, mining.clone(), cond_var.clone());
} else {
println!("Waiting for transactions");
cond_var.wait(lock);
println!("Got notified on new transaction");
}
}
});
}
pub fn is_mining(&self) -> bool {
self.running.load(Ordering::Relaxed)
}
fn mine_internal(&mut self, mut transaction: Transaction) {
fn mine_internal(context: Arc<Mutex<Context>>, transactions: Arc<Mutex<Vec<Transaction>>>, mut transaction: Transaction, mining: Arc<AtomicBool>, cond_var: Arc<Condvar>) {
let mut last_block_time = 0i64;
let mut chain_id = 0u32;
let mut version = 0u32;
{
let c = context.lock().unwrap();
chain_id = c.settings.chain_id;
version = c.settings.version;
}
let block = {
// Signing it with private key from Keystore
let sign_hash = self.keystore.sign(&transaction.get_bytes());
transaction.set_signature(Key::from_bytes(&sign_hash));
if transaction.signature.is_zero() {
// Signing it with private key from Keystore
let c = context.lock().unwrap();
let sign_hash = c.keystore.sign(&transaction.get_bytes());
transaction.set_signature(Key::from_bytes(&sign_hash));
}
match &self.last_block {
// Get last block for mining
let last_block = { context.lock().unwrap().blockchain.blocks.last().cloned() };
match last_block {
None => {
println!("Mining genesis block");
// Creating a block with that signed transaction
Block::new(0,Utc::now().timestamp(), self.chain_id, self.version, Key::zero32(), Some(transaction))
Block::new(0, Utc::now().timestamp(), chain_id, version, Key::zero32(), Some(transaction.clone()))
},
Some(block) => {
last_block_time = block.timestamp;
// Creating a block with that signed transaction
Block::new(block.index + 1,Utc::now().timestamp(), self.chain_id, self.version, block.hash.clone(), Some(transaction))
Block::new(block.index + 1, Utc::now().timestamp(), chain_id, version, block.hash.clone(), Some(transaction.clone()))
},
}
};
//let blockchain = self.blockchain.clone();
let transactions = self.transactions.clone();
let running = self.running.clone();
running.store(true, Ordering::Relaxed);
let context = self.context.clone();
thread::spawn(move || {
match find_hash(&mut Sha256::new(), block, last_block_time, running.clone()) {
None => {
println!("Mining stopped");
},
Some(block) => {
//blockchain.lock().unwrap().add_block(block);
transactions.lock().unwrap().remove(0);
running.store(false, Ordering::Relaxed);
context.lock().unwrap().blockchain.add_block(block);
},
}
});
let live_threads = Arc::new(AtomicU32::new(0u32));
let cpus = num_cpus::get();
println!("Starting {} threads for mining", cpus);
for _ in 0..cpus {
let transactions = transactions.clone();
let context = context.clone();
let transaction = transaction.clone();
let block = block.clone();
let mining = mining.clone();
let live_threads = live_threads.clone();
let cond_var = cond_var.clone();
thread::spawn(move || {
live_threads.fetch_add(1, Ordering::Relaxed);
let mut count = 0u32;
match find_hash(&mut Sha256::new(), block, last_block_time, mining.clone()) {
None => {
println!("Mining did not find suitable hash or was stopped");
count = live_threads.fetch_sub(1, Ordering::Relaxed);
// If this is the last thread, but mining was not stopped by another thread
if count == 0 && mining.load(Ordering::Relaxed) {
// If all threads came empty with mining we return transaction to the queue
transactions.lock().unwrap().push(transaction);
mining.store(false, Ordering::Relaxed);
cond_var.notify_one();
}
},
Some(block) => {
count = live_threads.fetch_sub(1, Ordering::Relaxed);
context.lock().unwrap().blockchain.add_block(block);
mining.store(false, Ordering::Relaxed);
},
}
});
}
}
fn get_last_block(&self) -> Option<Block> {
let context = self.context.lock().unwrap();
context.blockchain.blocks.last().cloned()
}
}
fn find_hash(digest: &mut dyn Digest, mut block: Block, prev_block_time: i64, running: Arc<AtomicBool>) -> Option<Block> {
let mut buf: [u8; 32] = [0; 32];
block.random = rand::random();
let start_difficulty = block.difficulty;
println!("Mining block {}", serde_json::to_string(&block).unwrap());
//let start_difficulty = block.difficulty;
for nonce in 0..std::u64::MAX {
if !running.load(Ordering::Relaxed) {
return None;
}
block.timestamp = Utc::now().timestamp();
block.nonce = nonce;
// if nonce % 1000 == 0 {
// println!("Nonce {}", nonce);
// }
// TODO uncomment for real run
//block.difficulty = start_difficulty + get_time_difficulty(prev_block_time, block.timestamp);

View File

@ -1,51 +1,59 @@
use crate::keys::*;
extern crate serde;
extern crate serde_json;
use serde::{Serialize, Deserialize, Serializer};
use serde::ser::SerializeStruct;
use std::fmt;
use crate::transaction::Action::Genesis;
use crypto::sha2::Sha256;
use crypto::digest::Digest;
#[derive(Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
pub enum Action {
ClaimName { hash: String, owner: Key },
NewDomain { name: String, owner: Key, #[serde(default, skip_serializing_if = "Vec::is_empty")] records: Vec<String>, #[serde(default, skip_serializing_if = "Vec::is_empty")] tags: Vec<String>, days: u16 },
ChangeDomain { name: String, records: Vec<String>, tags: Vec<String> },
NewDomain { hash: String, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key },
FillDomain { name: String, salt: String, owner: Key, #[serde(default, skip_serializing_if = "Vec::is_empty")] records: Vec<String>, #[serde(default, skip_serializing_if = "Vec::is_empty")] tags: Vec<String>, days: u16 },
ChangeDomain { name: String, records: Vec<String>, tags: Vec<String>, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key },
RenewDomain { name: String, days: u16 },
MoveDomain { name: String, new_owner: Key },
NewZone { name: String, difficulty: u16},
ChangeZone { name: String, difficulty: u16},
NewZone { hash: String, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key, difficulty: u16 },
ChangeZone { name: String, salt: String, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key, difficulty: u16 },
Genesis { name: String, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key, difficulty: u16 },
}
impl Action {
pub fn claim_name(name: String, salt: String, signature: &Keystore) -> Self {
let hash = format!("{} {}", salt, name);
Action::ClaimName {hash, owner: signature.get_public()}
pub fn new_domain<S: Into<String>>(name: S, salt: S, owner: Key) -> Self {
let hash = format!("{} {}", salt.into(), name.into());
// TODO Do not use owner for now, make a field in UI and use it if filled
Action::NewDomain { hash: Action::get_hash(&hash), owner }
}
pub fn new_domain(name: String, signature: &Keystore, records: Vec<String>, tags: Vec<String>, days: u16) -> Self {
Action::NewDomain {name, owner: signature.get_public(), records, tags, days}
pub fn fill_domain<S: Into<String>>(name: S, salt: S, owner: Key, records: Vec<String>, tags: Vec<String>, days: u16) -> Self {
Action::FillDomain { name: name.into(), salt: salt.into(), owner, records, tags, days }
}
pub fn change_domain(name: String, records: Vec<String>, tags: Vec<String>) -> Self {
Action::ChangeDomain {name, records, tags}
// TODO change new_owner to Key
pub fn change_domain<S: Into<String>>(name: S, records: Vec<String>, tags: Vec<String>, new_owner: [u8; 32]) -> Self {
Action::ChangeDomain { name: name.into(), records, tags, owner: Key::from_bytes(&new_owner) }
}
pub fn renew_domain(name: String, days: u16) -> Self {
Action::RenewDomain {name, days}
pub fn renew_domain<S: Into<String>>(name: S, days: u16) -> Self {
Action::RenewDomain { name: name.into(), days }
}
pub fn move_domain(name: String, new_owner: [u8; 32]) -> Self {
Action::MoveDomain {name, new_owner: Key::from_bytes(&new_owner)}
pub fn new_zone<S: Into<String>>(name: S, salt: S, owner: Key, difficulty: u16) -> Self {
let hash = format!("{} {}", salt.into(), name.into());
Action::NewZone { hash, owner, difficulty }
}
pub fn new_zone(name: String, difficulty: u16) -> Self {
Action::NewZone {name, difficulty}
// TODO change new_owner to Key
pub fn change_zone<S: Into<String>>(name: S, salt: S, new_owner: [u8; 32], difficulty: u16) -> Self {
Action::ChangeZone { name: name.into(), salt: salt.into(), owner: Key::from_bytes(&new_owner), difficulty }
}
pub fn change_zone(name: String, difficulty: u16) -> Self {
Action::ChangeZone {name, difficulty}
pub fn genesis<S: Into<String>>(name: S, owner: Key, difficulty: u16) -> Self {
Genesis { name: name.into(), owner, difficulty }
}
pub fn get_bytes(&self) -> Vec<u8> {
@ -57,54 +65,66 @@ impl Action {
// Let it panic (for now) if something is not okay
serde_json::from_slice(bytes.as_slice()).unwrap()
}
fn get_hash(data: &str) -> String {
let mut digest = Sha256::new();
digest.input(data.as_bytes());
digest.result_str()
}
}
impl fmt::Debug for Action {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self {
Action::ClaimName { hash, owner } => {
fmt.debug_struct("ClaimName")
Action::NewDomain { hash, owner } => {
fmt.debug_struct("NewDomain")
.field("hash", hash)
.field("owner", owner)
.finish()
},
Action::NewDomain { name, owner, records, tags, days } => {
fmt.debug_struct("NewDomain")
}
Action::FillDomain { name, salt, owner, records, tags, days } => {
fmt.debug_struct("FillDomain")
.field("name", name)
.field("salt", salt)
.field("owner", &owner)
.field("records", records)
.field("tags", tags)
.field("days", days)
.finish()
},
Action::ChangeDomain { name, records, tags } => {
}
Action::ChangeDomain { name, records, tags, owner } => {
fmt.debug_struct("ChangeDomain")
.field("name", name)
.field("records", records)
.field("tags", tags)
.field("owner", &owner)
.finish()
},
}
Action::RenewDomain { name, days } => {
fmt.debug_struct("RenewDomain")
.field("name", name)
.field("days", days)
.finish()
},
Action::MoveDomain { name, new_owner } => {
fmt.debug_struct("MoveDomain")
.field("name", name)
.field("new_owner", new_owner)
.finish()
},
Action::NewZone { name, difficulty } => {
}
Action::NewZone { hash, owner, difficulty } => {
fmt.debug_struct("NewZone")
.field("name", name)
.field("hash", hash)
.field("owner", &owner)
.field("difficulty", difficulty)
.finish()
},
Action::ChangeZone { name, difficulty } => {
}
Action::ChangeZone { name, salt, owner, difficulty } => {
fmt.debug_struct("ChangeZone")
.field("name", name)
.field("salt", salt)
.field("owner", &owner)
.field("difficulty", difficulty)
.finish()
}
Action::Genesis { name, owner, difficulty } => {
fmt.debug_struct("Genesis")
.field("name", name)
.field("owner", &owner)
.field("difficulty", difficulty)
.finish()
}
@ -121,7 +141,7 @@ pub struct Transaction {
impl Transaction {
pub fn new(action: Action, pub_key: Key) -> Self {
Transaction {action, pub_key, signature: Key::zero64()}
Transaction { action, pub_key, signature: Key::zero64() }
}
pub fn set_signature(&mut self, hash: Key) {

View File

@ -1,4 +1,5 @@
use std::num;
use rand::Rng;
/// Convert bytes array to HEX format
pub fn to_hex(buf: &[u8]) -> String {
@ -30,4 +31,17 @@ pub fn same_hash(left: &[u8], right: &[u8]) -> bool {
}
}
true
}
/// Generates random string of given length
pub fn random_string(length: usize) -> String {
let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!?".chars().collect();
let mut rng = rand::thread_rng();
let mut result = String::with_capacity(length);
for x in 0..length {
let position: usize = rng.gen::<usize>() % chars.len();
let c: char = *chars.get(position).unwrap();
result.push(c);
}
result
}