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:
parent
01f37cc238
commit
4703ae6f49
@ -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"] }
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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() }
|
||||
}
|
||||
}
|
12
src/keys.rs
12
src/keys.rs
@ -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
|
||||
|
118
src/main.rs
118
src/main.rs
@ -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 {
|
||||
|
142
src/miner.rs
142
src/miner.rs
@ -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);
|
||||
|
||||
|
@ -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) {
|
||||
|
14
src/utils.rs
14
src/utils.rs
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user