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
+1 -1
View File
@@ -8,7 +8,7 @@ edition = "2018"
[dependencies] [dependencies]
rust-crypto = "^0.2" rust-crypto = "^0.2"
num_cpus = "1.10.1" num_cpus = "1.13.0"
byteorder = "1.3.2" byteorder = "1.3.2"
web-view = { version = "0.5.4", features = [] } web-view = { version = "0.5.4", features = [] }
serde = { version = "1.0.102", features = ["derive"] } serde = { version = "1.0.102", features = ["derive"] }
+4 -2
View File
@@ -24,8 +24,9 @@ pub struct Block {
pub nonce: u64, pub nonce: u64,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub transaction: Option<Transaction>, pub transaction: Option<Transaction>,
#[serde(default, skip_serializing_if = "Key::is_zero")]
pub prev_block_hash: Key, pub prev_block_hash: Key,
#[serde(default, skip_serializing_if = "Key::is_empty")] #[serde(default, skip_serializing_if = "Key::is_zero")]
pub hash: Key, pub hash: Key,
} }
@@ -36,7 +37,8 @@ impl Block {
timestamp, timestamp,
chain_id, chain_id,
version, version,
difficulty: 18, // TODO make difficulty parameter
difficulty: 20,
random: 0, random: 0,
nonce: 0, nonce: 0,
transaction, transaction,
-10
View File
@@ -19,16 +19,6 @@ impl Blockchain {
block 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) { pub fn add_block(&mut self, block: Block) {
if self.check_block(&block, None) { if self.check_block(&block, None) {
println!("Adding block:\n{:?}", &block); println!("Adding block:\n{:?}", &block);
+10 -2
View File
@@ -34,6 +34,14 @@ impl Context {
pub fn set_keystore(&mut self, keystore: Keystore) { pub fn set_keystore(&mut self, keystore: Keystore) {
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 { pub struct Settings {
@@ -44,7 +52,7 @@ pub struct Settings {
impl Settings { impl Settings {
/// TODO parse settings /// TODO parse settings
pub fn new<S: Into<String>>(chain_id: u32, version: u32, settings: S) -> Settings { pub fn new<S: Into<String>>(settings: S) -> Settings {
Settings { chain_id, version, salts: HashMap::new() } Settings { chain_id: 42, version: 0, salts: HashMap::new() }
} }
} }
+12
View File
@@ -83,6 +83,18 @@ impl Key {
self.data.is_empty() 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. /// Returns a byte slice of the hash contents.
pub fn as_bytes(&self) -> &[u8] { pub fn as_bytes(&self) -> &[u8] {
&self.data &self.data
+93 -25
View File
@@ -1,38 +1,56 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
extern crate web_view; extern crate web_view;
use alfis::{Blockchain, Block, Action, Transaction, Keystore, Key, Settings, Context}; use alfis::{Blockchain, Block, Action, Transaction, Keystore, Key, Settings, Context};
use alfis::miner::Miner;
use alfis::utils::random_string;
use web_view::*; use web_view::*;
use std::thread; use std::thread;
use std::sync::{Arc, Mutex};
use std::sync::{Arc, Mutex};
extern crate serde; extern crate serde;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use alfis::miner::Miner;
use std::collections::HashMap; use std::collections::HashMap;
extern crate serde_json; 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() { fn main() {
println!("ALFIS 0.1.0"); 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 keystore: Keystore = Keystore::from_file("default.key", "").unwrap();
let blockchain: Blockchain = Blockchain::new(settings.chain_id, settings.version); 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 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 create_genesis_if_needed(&context, &miner);
/*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");
});*/
run_interface(context.clone(), miner.clone()); 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>>) { fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
let file_content = include_str!("index.html"); let file_content = include_str!("index.html");
let styles = inline_style(include_str!("bulma.css")); 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()); c.set_keystore(Keystore::new());
} }
CreateDomain { name, records, tags } => { CreateDomain { name, records, tags } => {
let mut c = context.lock().unwrap(); let salt = random_string(SALT_LENGTH);
create_domain(miner.clone(), name, records, tags, c.get_keystore()); 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 } => { ChangeDomain { name, records, tags } => {
let keystore = { context.lock().unwrap().get_keystore() };
// TODO
} }
RenewDomain { name, days } => {} RenewDomain { name, days } => {}
TransferDomain { name, owner } => {} TransferDomain { name, owner } => {}
@@ -81,18 +105,38 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
.unwrap(); .unwrap();
} }
fn create_domain(miner: Arc<Mutex<Miner>>, name: String, records: String, tags: String, keystore: Keystore) { fn create_genesis<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, keystore: &Keystore, difficulty: u16) {
let rec_vector: Vec<String> = records.trim().split("\n").map(|s| s.trim()).map(String::from).collect(); // Creating transaction
let tags_vector: Vec<String> = tags.trim().split(",").map(|s| s.trim()).map(String::from).collect(); // TODO Do we need to add here an owners key?
let mut transaction = { transaction_new_domain(keystore, name, rec_vector, tags_vector, 365) }; 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(); let mut miner_guard = miner.lock().unwrap();
miner_guard.add_transaction(transaction); 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 // 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()); let mut transaction = Transaction::new(action, keystore.get_public().clone());
// Signing it with private key from Signature // Signing it with private key from Signature
let sign_hash = keystore.sign(&transaction.get_bytes()); 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 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 // 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()); let mut transaction = Transaction::new(action, keystore.get_public().clone());
// Signing it with private key from Signature // Signing it with private key from Signature
let sign_hash = keystore.sign(&transaction.get_bytes()); 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 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)] #[derive(Deserialize)]
#[serde(tag = "cmd", rename_all = "camelCase")] #[serde(tag = "cmd", rename_all = "camelCase")]
pub enum Cmd { pub enum Cmd {
+93 -27
View File
@@ -1,12 +1,14 @@
use crate::{Transaction, Block, Keystore, Key, Context}; use crate::{Transaction, Block, Keystore, Key, Context};
use std::sync::{Mutex, Arc}; use std::sync::{Mutex, Arc, Condvar};
use crypto::digest::Digest; use crypto::digest::Digest;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering, AtomicU32};
use chrono::Utc; use chrono::Utc;
use num_bigint::BigUint; use num_bigint::BigUint;
use num_traits::One; use num_traits::One;
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
use std::thread; use std::thread;
use std::time::Duration;
use num_cpus;
pub struct Miner { pub struct Miner {
context: Arc<Mutex<Context>>, context: Arc<Mutex<Context>>,
@@ -16,6 +18,8 @@ pub struct Miner {
transactions: Arc<Mutex<Vec<Transaction>>>, transactions: Arc<Mutex<Vec<Transaction>>>,
last_block: Option<Block>, last_block: Option<Block>,
running: Arc<AtomicBool>, running: Arc<AtomicBool>,
mining: Arc<AtomicBool>,
cond_var: Arc<Condvar>
} }
impl Miner { impl Miner {
@@ -29,83 +33,145 @@ impl Miner {
transactions: Arc::new(Mutex::new(Vec::new())), transactions: Arc::new(Mutex::new(Vec::new())),
last_block: c.blockchain.blocks.last().cloned(), last_block: c.blockchain.blocks.last().cloned(),
running: Arc::new(AtomicBool::new(false)), 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) { pub fn add_transaction(&mut self, transaction: Transaction) {
self.transactions.lock().unwrap().push(transaction); self.transactions.lock().unwrap().push(transaction);
self.cond_var.notify_one();
} }
pub fn stop(&mut self) { pub fn stop(&mut self) {
self.mining.store(false, Ordering::Relaxed);
self.running.store(false, Ordering::Relaxed); self.running.store(false, Ordering::Relaxed);
self.cond_var.notify_all();
} }
pub fn mine(&mut self) { pub fn start_mining_thread(&mut self) {
let transaction = { self.transactions.lock().unwrap().first().cloned() }; let context = self.context.clone();
match transaction { let transactions = self.transactions.clone();
Some(transaction) => { let running = self.running.clone();
self.mine_internal(transaction); let mining = self.mining.clone();
}, let cond_var = self.cond_var.clone();
None => { thread::spawn(move || {
println!("Nothing to mine"); 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 { pub fn is_mining(&self) -> bool {
self.running.load(Ordering::Relaxed) 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 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 = { let block = {
if transaction.signature.is_zero() {
// Signing it with private key from Keystore // Signing it with private key from Keystore
let sign_hash = self.keystore.sign(&transaction.get_bytes()); let c = context.lock().unwrap();
let sign_hash = c.keystore.sign(&transaction.get_bytes());
transaction.set_signature(Key::from_bytes(&sign_hash)); 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 => { None => {
println!("Mining genesis block");
// Creating a block with that signed transaction // 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) => { Some(block) => {
last_block_time = block.timestamp; last_block_time = block.timestamp;
// Creating a block with that signed transaction // 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 live_threads = Arc::new(AtomicU32::new(0u32));
let running = self.running.clone(); let cpus = num_cpus::get();
running.store(true, Ordering::Relaxed); println!("Starting {} threads for mining", cpus);
let context = self.context.clone(); 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 || { thread::spawn(move || {
match find_hash(&mut Sha256::new(), block, last_block_time, running.clone()) { live_threads.fetch_add(1, Ordering::Relaxed);
let mut count = 0u32;
match find_hash(&mut Sha256::new(), block, last_block_time, mining.clone()) {
None => { None => {
println!("Mining stopped"); 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) => { Some(block) => {
//blockchain.lock().unwrap().add_block(block); count = live_threads.fetch_sub(1, Ordering::Relaxed);
transactions.lock().unwrap().remove(0);
running.store(false, Ordering::Relaxed);
context.lock().unwrap().blockchain.add_block(block); 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> { 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]; let mut buf: [u8; 32] = [0; 32];
block.random = rand::random(); 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 { for nonce in 0..std::u64::MAX {
if !running.load(Ordering::Relaxed) { if !running.load(Ordering::Relaxed) {
return None; return None;
} }
block.timestamp = Utc::now().timestamp(); block.timestamp = Utc::now().timestamp();
block.nonce = nonce; block.nonce = nonce;
// if nonce % 1000 == 0 {
// println!("Nonce {}", nonce);
// }
// TODO uncomment for real run // TODO uncomment for real run
//block.difficulty = start_difficulty + get_time_difficulty(prev_block_time, block.timestamp); //block.difficulty = start_difficulty + get_time_difficulty(prev_block_time, block.timestamp);
+61 -41
View File
@@ -1,51 +1,59 @@
use crate::keys::*; use crate::keys::*;
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
use serde::{Serialize, Deserialize, Serializer}; use serde::{Serialize, Deserialize, Serializer};
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
use std::fmt; use std::fmt;
use crate::transaction::Action::Genesis;
use crypto::sha2::Sha256;
use crypto::digest::Digest;
#[derive(Clone, Serialize, Deserialize, PartialEq)] #[derive(Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")] #[serde(tag = "type")]
pub enum Action { pub enum Action {
ClaimName { hash: String, owner: Key }, NewDomain { hash: String, #[serde(default, skip_serializing_if = "Key::is_zero")] 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 }, 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> }, ChangeDomain { name: String, records: Vec<String>, tags: Vec<String>, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key },
RenewDomain { name: String, days: u16 }, RenewDomain { name: String, days: u16 },
MoveDomain { name: String, new_owner: Key }, NewZone { hash: String, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key, difficulty: u16 },
NewZone { name: String, difficulty: u16}, ChangeZone { name: String, salt: String, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key, difficulty: u16 },
ChangeZone { name: String, difficulty: u16}, Genesis { name: String, #[serde(default, skip_serializing_if = "Key::is_zero")] owner: Key, difficulty: u16 },
} }
impl Action { impl Action {
pub fn claim_name(name: String, salt: String, signature: &Keystore) -> Self { pub fn new_domain<S: Into<String>>(name: S, salt: S, owner: Key) -> Self {
let hash = format!("{} {}", salt, name); let hash = format!("{} {}", salt.into(), name.into());
Action::ClaimName {hash, owner: signature.get_public()} // 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 { pub fn fill_domain<S: Into<String>>(name: S, salt: S, owner: Key, records: Vec<String>, tags: Vec<String>, days: u16) -> Self {
Action::NewDomain {name, owner: signature.get_public(), records, tags, days} 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 { // TODO change new_owner to Key
Action::ChangeDomain {name, records, tags} 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 { pub fn renew_domain<S: Into<String>>(name: S, days: u16) -> Self {
Action::RenewDomain {name, days} Action::RenewDomain { name: name.into(), days }
} }
pub fn move_domain(name: String, new_owner: [u8; 32]) -> Self { pub fn new_zone<S: Into<String>>(name: S, salt: S, owner: Key, difficulty: u16) -> Self {
Action::MoveDomain {name, new_owner: Key::from_bytes(&new_owner)} let hash = format!("{} {}", salt.into(), name.into());
Action::NewZone { hash, owner, difficulty }
} }
pub fn new_zone(name: String, difficulty: u16) -> Self { // TODO change new_owner to Key
Action::NewZone {name, difficulty} 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 { pub fn genesis<S: Into<String>>(name: S, owner: Key, difficulty: u16) -> Self {
Action::ChangeZone {name, difficulty} Genesis { name: name.into(), owner, difficulty }
} }
pub fn get_bytes(&self) -> Vec<u8> { pub fn get_bytes(&self) -> Vec<u8> {
@@ -57,54 +65,66 @@ impl Action {
// Let it panic (for now) if something is not okay // Let it panic (for now) if something is not okay
serde_json::from_slice(bytes.as_slice()).unwrap() 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 { impl fmt::Debug for Action {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
Action::ClaimName { hash, owner } => { Action::NewDomain { hash, owner } => {
fmt.debug_struct("ClaimName") fmt.debug_struct("NewDomain")
.field("hash", hash) .field("hash", hash)
.field("owner", owner) .field("owner", owner)
.finish() .finish()
}, }
Action::NewDomain { name, owner, records, tags, days } => { Action::FillDomain { name, salt, owner, records, tags, days } => {
fmt.debug_struct("NewDomain") fmt.debug_struct("FillDomain")
.field("name", name) .field("name", name)
.field("salt", salt)
.field("owner", &owner) .field("owner", &owner)
.field("records", records) .field("records", records)
.field("tags", tags) .field("tags", tags)
.field("days", days) .field("days", days)
.finish() .finish()
}, }
Action::ChangeDomain { name, records, tags } => { Action::ChangeDomain { name, records, tags, owner } => {
fmt.debug_struct("ChangeDomain") fmt.debug_struct("ChangeDomain")
.field("name", name) .field("name", name)
.field("records", records) .field("records", records)
.field("tags", tags) .field("tags", tags)
.field("owner", &owner)
.finish() .finish()
}, }
Action::RenewDomain { name, days } => { Action::RenewDomain { name, days } => {
fmt.debug_struct("RenewDomain") fmt.debug_struct("RenewDomain")
.field("name", name) .field("name", name)
.field("days", days) .field("days", days)
.finish() .finish()
}, }
Action::MoveDomain { name, new_owner } => { Action::NewZone { hash, owner, difficulty } => {
fmt.debug_struct("MoveDomain")
.field("name", name)
.field("new_owner", new_owner)
.finish()
},
Action::NewZone { name, difficulty } => {
fmt.debug_struct("NewZone") fmt.debug_struct("NewZone")
.field("name", name) .field("hash", hash)
.field("owner", &owner)
.field("difficulty", difficulty) .field("difficulty", difficulty)
.finish() .finish()
}, }
Action::ChangeZone { name, difficulty } => { Action::ChangeZone { name, salt, owner, difficulty } => {
fmt.debug_struct("ChangeZone") fmt.debug_struct("ChangeZone")
.field("name", name) .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) .field("difficulty", difficulty)
.finish() .finish()
} }
@@ -121,7 +141,7 @@ pub struct Transaction {
impl Transaction { impl Transaction {
pub fn new(action: Action, pub_key: Key) -> Self { 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) { pub fn set_signature(&mut self, hash: Key) {
+14
View File
@@ -1,4 +1,5 @@
use std::num; use std::num;
use rand::Rng;
/// Convert bytes array to HEX format /// Convert bytes array to HEX format
pub fn to_hex(buf: &[u8]) -> String { pub fn to_hex(buf: &[u8]) -> String {
@@ -31,3 +32,16 @@ pub fn same_hash(left: &[u8], right: &[u8]) -> bool {
} }
true 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
}