Changed and refactored block and transaction structures. Moved signature from transaction scope to block.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "alfis"
|
name = "alfis"
|
||||||
version = "0.1.6"
|
version = "0.2.0"
|
||||||
authors = ["Revertron <alfis@revertron.com>"]
|
authors = ["Revertron <alfis@revertron.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|||||||
+27
-16
@@ -15,7 +15,7 @@ pub struct Block {
|
|||||||
pub index: u64,
|
pub index: u64,
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
pub difficulty: usize,
|
pub difficulty: u32,
|
||||||
pub random: u32,
|
pub random: u32,
|
||||||
pub nonce: u64,
|
pub nonce: u64,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
@@ -24,25 +24,30 @@ pub struct Block {
|
|||||||
pub prev_block_hash: Bytes,
|
pub prev_block_hash: Bytes,
|
||||||
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
|
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
|
||||||
pub hash: Bytes,
|
pub hash: Bytes,
|
||||||
|
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
|
||||||
|
pub pub_key: Bytes,
|
||||||
|
#[serde(default, skip_serializing_if = "Bytes::is_zero")]
|
||||||
|
pub signature: Bytes,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl Block {
|
||||||
pub fn new(index: u64, timestamp: i64, version: u32, prev_block_hash: Bytes, transaction: Option<Transaction>) -> Self {
|
pub fn new(transaction: Option<Transaction>, pub_key: Bytes, prev_block_hash: Bytes) -> Self {
|
||||||
Block {
|
Block {
|
||||||
index,
|
index: 0,
|
||||||
timestamp,
|
timestamp: 0,
|
||||||
version,
|
version: 0,
|
||||||
// TODO make difficulty parameter
|
difficulty: 0,
|
||||||
difficulty: 20,
|
|
||||||
random: 0,
|
random: 0,
|
||||||
nonce: 0,
|
nonce: 0,
|
||||||
transaction,
|
transaction,
|
||||||
prev_block_hash,
|
prev_block_hash,
|
||||||
hash: Bytes::default(),
|
hash: Bytes::default(),
|
||||||
|
pub_key,
|
||||||
|
signature: Bytes::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_all_params(index: u64, timestamp: i64, version: u32, difficulty: usize, random: u32, nonce: u64, prev_block_hash: Bytes, hash: Bytes, transaction: Option<Transaction>) -> Self {
|
pub fn from_all_params(index: u64, timestamp: i64, version: u32, difficulty: u32, random: u32, nonce: u64, prev_block_hash: Bytes, hash: Bytes, pub_key: Bytes, signature: Bytes, transaction: Option<Transaction>) -> Self {
|
||||||
Block {
|
Block {
|
||||||
index,
|
index,
|
||||||
timestamp,
|
timestamp,
|
||||||
@@ -53,18 +58,24 @@ impl Block {
|
|||||||
transaction,
|
transaction,
|
||||||
prev_block_hash,
|
prev_block_hash,
|
||||||
hash,
|
hash,
|
||||||
|
pub_key,
|
||||||
|
signature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash(data: &[u8]) -> Bytes {
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
|
||||||
let mut digest = Sha256::new();
|
|
||||||
digest.input(data);
|
|
||||||
digest.result(&mut buf);
|
|
||||||
Bytes::new(buf.to_vec())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_genesis(&self) -> bool {
|
pub fn is_genesis(&self) -> bool {
|
||||||
self.index == 0 && self.transaction.is_none() && self.prev_block_hash == Bytes::default()
|
self.index == 0 && self.transaction.is_none() && self.prev_block_hash == Bytes::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_bytes(&self) -> Vec<u8> {
|
||||||
|
Vec::from(serde_json::to_string(&self).unwrap().as_bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash(data: &[u8]) -> Bytes {
|
||||||
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
|
let mut digest = Sha256::new();
|
||||||
|
digest.input(data);
|
||||||
|
digest.result(&mut buf);
|
||||||
|
Bytes::new(buf.to_vec())
|
||||||
}
|
}
|
||||||
@@ -1,11 +1,15 @@
|
|||||||
use sqlite::{Connection, State, Statement};
|
use sqlite::{Connection, State, Statement};
|
||||||
|
|
||||||
use crate::{Block, Bytes, Keystore, Transaction};
|
use crate::{Block, Bytes, Keystore, Transaction, hash_is_good};
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{trace, debug, info, warn, error};
|
use log::{trace, debug, info, warn, error};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use chrono::Utc;
|
||||||
|
use crate::blockchain::transaction::hash_identity;
|
||||||
|
use crate::blockchain::blockchain::BlockQuality::*;
|
||||||
|
use crate::blockchain::BLOCK_DIFFICULTY;
|
||||||
|
|
||||||
const DB_NAME: &str = "blockchain.db";
|
const DB_NAME: &str = "blockchain.db";
|
||||||
|
|
||||||
@@ -58,30 +62,19 @@ impl Blockchain {
|
|||||||
'nonce' INTEGER,
|
'nonce' INTEGER,
|
||||||
'transaction' TEXT,
|
'transaction' TEXT,
|
||||||
'prev_block_hash' BINARY,
|
'prev_block_hash' BINARY,
|
||||||
'hash' BINARY
|
'hash' BINARY,
|
||||||
|
'pub_key' BINARY,
|
||||||
|
'signature' BINARY
|
||||||
);
|
);
|
||||||
CREATE INDEX block_index ON blocks (id);
|
CREATE INDEX block_index ON blocks (id);
|
||||||
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, confirmation BINARY, method TEXT, data TEXT, pub_key BINARY, signature BINARY);
|
CREATE TABLE transactions (id INTEGER PRIMARY KEY AUTOINCREMENT, identity BINARY, confirmation BINARY, method TEXT, data TEXT, pub_key BINARY);
|
||||||
CREATE INDEX ids ON transactions (identity);"
|
CREATE INDEX ids ON transactions (identity);"
|
||||||
).expect("Error creating blocks table");
|
).expect("Error creating blocks table");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_block(&mut self, block: Block) -> Result<(), &str> {
|
pub fn add_block(&mut self, block: Block) {
|
||||||
match &self.last_block {
|
|
||||||
None => {}
|
|
||||||
Some(last_block) => {
|
|
||||||
if last_block.index >= block.index && last_block.hash == block.hash {
|
|
||||||
info!("Ignoring block {}, we already have it", block.index);
|
|
||||||
return Err("Already have that block");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !self.check_block(&block, &self.last_block) {
|
|
||||||
warn!("Bad block found, ignoring:\n{:?}", &block);
|
|
||||||
return Err("Bad block found, ignoring");
|
|
||||||
}
|
|
||||||
info!("Adding block:\n{:?}", &block);
|
info!("Adding block:\n{:?}", &block);
|
||||||
self.blocks.push(block.clone());
|
self.blocks.push(block.clone());
|
||||||
self.last_block = Some(block.clone());
|
self.last_block = Some(block.clone());
|
||||||
@@ -90,9 +83,9 @@ impl Blockchain {
|
|||||||
{
|
{
|
||||||
// Adding block to DB
|
// Adding block to DB
|
||||||
let mut statement = self.db.prepare("INSERT INTO blocks (\
|
let mut statement = self.db.prepare("INSERT INTO blocks (\
|
||||||
id, timestamp, version, difficulty, random,\
|
id, timestamp, version, difficulty, random, nonce, 'transaction',\
|
||||||
nonce, 'transaction', prev_block_hash, hash)\
|
prev_block_hash, hash, pub_key, signature)\
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);").unwrap();
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);").unwrap();
|
||||||
statement.bind(1, block.index as i64).expect("Error in bind");
|
statement.bind(1, block.index as i64).expect("Error in bind");
|
||||||
statement.bind(2, block.timestamp as i64).expect("Error in bind");
|
statement.bind(2, block.timestamp as i64).expect("Error in bind");
|
||||||
statement.bind(3, block.version as i64).expect("Error in bind");
|
statement.bind(3, block.version as i64).expect("Error in bind");
|
||||||
@@ -107,28 +100,23 @@ impl Blockchain {
|
|||||||
}
|
}
|
||||||
statement.bind(8, block.prev_block_hash.as_bytes()).expect("Error in bind");
|
statement.bind(8, block.prev_block_hash.as_bytes()).expect("Error in bind");
|
||||||
statement.bind(9, block.hash.as_bytes()).expect("Error in bind");
|
statement.bind(9, block.hash.as_bytes()).expect("Error in bind");
|
||||||
|
statement.bind(10, block.pub_key.as_bytes()).expect("Error in bind");
|
||||||
|
statement.bind(11, block.signature.as_bytes()).expect("Error in bind");
|
||||||
statement.next().expect("Error adding block to DB");
|
statement.next().expect("Error adding block to DB");
|
||||||
}
|
}
|
||||||
|
|
||||||
match &transaction {
|
if let Some(transaction) = transaction {
|
||||||
None => {
|
self.add_transaction(&transaction);
|
||||||
Err("Error adding transaction!")
|
|
||||||
}
|
|
||||||
Some(transaction) => {
|
|
||||||
self.add_transaction(transaction);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_transaction(&mut self, t: &Transaction) {
|
fn add_transaction(&mut self, t: &Transaction) {
|
||||||
let mut statement = self.db.prepare("INSERT INTO transactions (identity, confirmation, method, data, pub_key, signature) VALUES (?, ?, ?, ?, ?, ?)").unwrap();
|
let mut statement = self.db.prepare("INSERT INTO transactions (identity, confirmation, method, data, pub_key) VALUES (?, ?, ?, ?, ?)").unwrap();
|
||||||
statement.bind(1, t.identity.as_bytes()).expect("Error in bind");
|
statement.bind(1, t.identity.as_bytes()).expect("Error in bind");
|
||||||
statement.bind(2, t.confirmation.as_bytes()).expect("Error in bind");
|
statement.bind(2, t.confirmation.as_bytes()).expect("Error in bind");
|
||||||
statement.bind(3, t.method.as_ref() as &str).expect("Error in bind");
|
statement.bind(3, t.method.as_ref() as &str).expect("Error in bind");
|
||||||
statement.bind(4, t.data.as_ref() as &str).expect("Error in bind");
|
statement.bind(4, t.data.as_ref() as &str).expect("Error in bind");
|
||||||
statement.bind(5, t.pub_key.as_bytes()).expect("Error in bind");
|
statement.bind(5, t.pub_key.as_bytes()).expect("Error in bind");
|
||||||
statement.bind(6, t.signature.as_bytes()).expect("Error in bind");
|
|
||||||
statement.next().expect("Error adding transaction to DB");
|
statement.next().expect("Error adding transaction to DB");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +149,7 @@ impl Blockchain {
|
|||||||
if domain.is_empty() {
|
if domain.is_empty() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let identity_hash = Transaction::hash_identity(domain);
|
let identity_hash = hash_identity(domain, None);
|
||||||
let mut statement = self.db.prepare("SELECT pub_key FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
let mut statement = self.db.prepare("SELECT pub_key FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
||||||
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
||||||
while let State::Row = statement.next().unwrap() {
|
while let State::Row = statement.next().unwrap() {
|
||||||
@@ -189,7 +177,7 @@ impl Blockchain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Checking for existing zone in DB
|
// Checking for existing zone in DB
|
||||||
let identity_hash = Transaction::hash_identity(zone);
|
let identity_hash = hash_identity(zone, None);
|
||||||
let mut statement = self.db.prepare("SELECT identity FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
let mut statement = self.db.prepare("SELECT identity FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
||||||
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
||||||
while let State::Row = statement.next().unwrap() {
|
while let State::Row = statement.next().unwrap() {
|
||||||
@@ -204,7 +192,7 @@ impl Blockchain {
|
|||||||
if domain.is_empty() {
|
if domain.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let identity_hash = Transaction::hash_identity(domain);
|
let identity_hash = hash_identity(domain, None);
|
||||||
|
|
||||||
let mut statement = self.db.prepare("SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
let mut statement = self.db.prepare("SELECT * FROM transactions WHERE identity = ? ORDER BY id DESC LIMIT 1;").unwrap();
|
||||||
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
statement.bind(1, identity_hash.as_bytes()).expect("Error in bind");
|
||||||
@@ -214,10 +202,9 @@ impl Blockchain {
|
|||||||
let method = statement.read::<String>(3).unwrap();
|
let method = statement.read::<String>(3).unwrap();
|
||||||
let data = statement.read::<String>(4).unwrap();
|
let data = statement.read::<String>(4).unwrap();
|
||||||
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(5).unwrap().as_slice());
|
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(5).unwrap().as_slice());
|
||||||
let signature = Bytes::from_bytes(statement.read::<Vec<u8>>(6).unwrap().as_slice());
|
let transaction = Transaction { identity, confirmation, method, data, pub_key };
|
||||||
let transaction = Transaction { identity, confirmation, method, data, pub_key, signature };
|
|
||||||
debug!("Found transaction for domain {}: {:?}", domain, &transaction);
|
debug!("Found transaction for domain {}: {:?}", domain, &transaction);
|
||||||
if transaction.check_for(domain) {
|
if transaction.check_identity(domain) {
|
||||||
return Some(transaction);
|
return Some(transaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,75 +241,97 @@ impl Blockchain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*pub fn check(&self) -> bool {
|
pub fn check_new_block(&self, block: &Block) -> BlockQuality {
|
||||||
let mut prev_block = None;
|
let timestamp = Utc::now().timestamp();
|
||||||
for block in self.blocks.iter() {
|
if block.timestamp > timestamp {
|
||||||
if !self.check_block(block, &prev_block) {
|
warn!("Ignoring block from the future:\n{:?}", &block);
|
||||||
println!("Block {:?} is bad", block);
|
return Bad;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
prev_block = Some(block);
|
|
||||||
}
|
}
|
||||||
true
|
if !hash_is_good(block.hash.as_bytes(), BLOCK_DIFFICULTY as usize) {
|
||||||
}*/
|
warn!("Ignoring block with low difficulty:\n{:?}", &block);
|
||||||
|
return Bad;
|
||||||
fn check_block(&self, block: &Block, prev_block: &Option<Block>) -> bool {
|
|
||||||
if !check_block_hash(block) {
|
|
||||||
warn!("{:?} has wrong hash! Ignoring!", &block);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
// TODO make transaction not Optional
|
if !hash_is_good(block.hash.as_bytes(), block.difficulty as usize) {
|
||||||
let transaction = block.transaction.as_ref().unwrap();
|
warn!("Ignoring block with low difficulty:\n{:?}", &block);
|
||||||
if !check_transaction_signature(&transaction) {
|
return Bad;
|
||||||
warn!("{:?} has wrong signature! Ignoring block!", &transaction);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
match prev_block {
|
match &self.last_block {
|
||||||
None => {
|
None => {
|
||||||
if block.index != 0 {
|
if !block.is_genesis() {
|
||||||
return false;
|
return Future;
|
||||||
}
|
}
|
||||||
|
if !self.origin.is_zero() && block.hash != self.origin {
|
||||||
if self.origin.is_zero() && block.index > 0 {
|
warn!("Mining gave us a bad block:\n{:?}", &block);
|
||||||
panic!("Error adding block {} without origin! Please, fill in origin in config!", block.index);
|
return Bad;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.origin.is_zero() || block.hash.eq(&self.origin)
|
|
||||||
}
|
}
|
||||||
Some(prev) => {
|
Some(last_block) => {
|
||||||
if block.index != prev.index + 1 {
|
if block.timestamp < last_block.timestamp && block.index > last_block.index {
|
||||||
info!("Discarding block with index {} as not needed now", block.index);
|
warn!("Ignoring block with timestamp/index collision:\n{:?}", &block);
|
||||||
return false;
|
return Bad;
|
||||||
|
}
|
||||||
|
if last_block.index + 1 < block.index {
|
||||||
|
warn!("Got block from the future");
|
||||||
|
return Future;
|
||||||
|
}
|
||||||
|
if last_block.index >= block.index && last_block.hash == block.hash {
|
||||||
|
warn!("Ignoring block {}, we already have it", block.index);
|
||||||
|
return Twin;
|
||||||
|
}
|
||||||
|
if last_block.index == block.index && last_block.hash != block.hash {
|
||||||
|
warn!("Got forked block {} with hash {:?} instead of {:?}", block.index, block.hash, last_block.hash);
|
||||||
|
return Fork;
|
||||||
}
|
}
|
||||||
block.prev_block_hash.eq(&prev.hash)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !check_block_hash(block) {
|
||||||
|
warn!("Block {:?} has wrong hash! Ignoring!", &block);
|
||||||
|
return Bad;
|
||||||
|
}
|
||||||
|
if !check_block_signature(&block) {
|
||||||
|
warn!("Block {:?} has wrong signature! Ignoring!", &block);
|
||||||
|
return Bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
Good
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_from_statement(statement: &mut Statement) -> Option<Block> {
|
fn get_block_from_statement(statement: &mut Statement) -> Option<Block> {
|
||||||
let index = statement.read::<i64>(0).unwrap() as u64;
|
let index = statement.read::<i64>(0).unwrap() as u64;
|
||||||
let timestamp = statement.read::<i64>(1).unwrap();
|
let timestamp = statement.read::<i64>(1).unwrap();
|
||||||
let version = statement.read::<i64>(2).unwrap() as u32;
|
let version = statement.read::<i64>(2).unwrap() as u32;
|
||||||
let difficulty = statement.read::<i64>(3).unwrap() as usize;
|
let difficulty = statement.read::<i64>(3).unwrap() as u32;
|
||||||
let random = statement.read::<i64>(4).unwrap() as u32;
|
let random = statement.read::<i64>(4).unwrap() as u32;
|
||||||
let nonce = statement.read::<i64>(5).unwrap() as u64;
|
let nonce = statement.read::<i64>(5).unwrap() as u64;
|
||||||
let transaction = Transaction::from_json(&statement.read::<String>(6).unwrap());
|
let transaction = Transaction::from_json(&statement.read::<String>(6).unwrap());
|
||||||
let prev_block_hash = Bytes::from_bytes(statement.read::<Vec<u8>>(7).unwrap().as_slice());
|
let prev_block_hash = Bytes::from_bytes(statement.read::<Vec<u8>>(7).unwrap().as_slice());
|
||||||
let hash = Bytes::from_bytes(statement.read::<Vec<u8>>(8).unwrap().as_slice());
|
let hash = Bytes::from_bytes(statement.read::<Vec<u8>>(8).unwrap().as_slice());
|
||||||
Some(Block::from_all_params(index, timestamp, version, difficulty, random, nonce, prev_block_hash, hash, transaction))
|
let pub_key = Bytes::from_bytes(statement.read::<Vec<u8>>(9).unwrap().as_slice());
|
||||||
|
let signature = Bytes::from_bytes(statement.read::<Vec<u8>>(10).unwrap().as_slice());
|
||||||
|
Some(Block::from_all_params(index, timestamp, version, difficulty, random, nonce, prev_block_hash, hash, pub_key, signature, transaction))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum BlockQuality {
|
||||||
|
Good,
|
||||||
|
Twin,
|
||||||
|
Future,
|
||||||
|
Bad,
|
||||||
|
Fork
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_block_hash(block: &Block) -> bool {
|
pub fn check_block_hash(block: &Block) -> bool {
|
||||||
let mut copy: Block = block.clone();
|
let mut copy: Block = block.clone();
|
||||||
copy.hash = Bytes::default();
|
copy.hash = Bytes::default();
|
||||||
|
copy.signature = Bytes::default();
|
||||||
let data = serde_json::to_string(©).unwrap();
|
let data = serde_json::to_string(©).unwrap();
|
||||||
Block::hash(data.as_bytes()) == block.hash
|
crate::blockchain::block::hash(data.as_bytes()) == block.hash
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_transaction_signature(transaction: &Transaction) -> bool {
|
pub fn check_block_signature(block: &Block) -> bool {
|
||||||
let mut copy = transaction.clone();
|
let mut copy = block.clone();
|
||||||
copy.signature = Bytes::zero64();
|
copy.signature = Bytes::zero64();
|
||||||
let data = copy.get_bytes();
|
let data = serde_json::to_string(©).unwrap();
|
||||||
Keystore::check(data.as_slice(), copy.pub_key.as_bytes(), transaction.signature.as_bytes())
|
Keystore::check(data.as_bytes(), copy.pub_key.as_bytes(), block.signature.as_bytes())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
pub const BLOCK_DIFFICULTY: u32 = 24;
|
||||||
|
pub const CHAIN_VERSION: u32 = 1;
|
||||||
@@ -2,7 +2,9 @@ pub mod transaction;
|
|||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod blockchain;
|
pub mod blockchain;
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
|
pub mod constants;
|
||||||
|
|
||||||
pub use transaction::Transaction;
|
pub use transaction::Transaction;
|
||||||
pub use block::Block;
|
pub use block::Block;
|
||||||
pub use blockchain::Blockchain;
|
pub use blockchain::Blockchain;
|
||||||
|
pub use constants::*;
|
||||||
@@ -16,18 +16,17 @@ pub struct Transaction {
|
|||||||
pub method: String,
|
pub method: String,
|
||||||
pub data: String,
|
pub data: String,
|
||||||
pub pub_key: Bytes,
|
pub pub_key: Bytes,
|
||||||
pub signature: Bytes,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Transaction {
|
impl Transaction {
|
||||||
pub fn from_str(identity: String, method: String, data: String, pub_key: Bytes) -> Self {
|
pub fn from_str(identity: String, method: String, data: String, pub_key: Bytes) -> Self {
|
||||||
let hash = Self::hash_identity(&identity);
|
let hash = hash_identity(&identity, None);
|
||||||
let confirmation = Self::hash_with_key(&identity, &pub_key);
|
let confirmation = hash_identity(&identity, Some(&pub_key));
|
||||||
return Self::new(hash, confirmation, method, data, pub_key);
|
return Self::new(hash, confirmation, method, data, pub_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(identity: Bytes, confirmation: Bytes, method: String, data: String, pub_key: Bytes) -> Self {
|
pub fn new(identity: Bytes, confirmation: Bytes, method: String, data: String, pub_key: Bytes) -> Self {
|
||||||
Transaction { identity, confirmation, method, data, pub_key, signature: Bytes::zero64() }
|
Transaction { identity, confirmation, method, data, pub_key }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_json(json: &str) -> Option<Self> {
|
pub fn from_json(json: &str) -> Option<Self> {
|
||||||
@@ -37,10 +36,6 @@ impl Transaction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_signature(&mut self, hash: Bytes) {
|
|
||||||
self.signature = hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_bytes(&self) -> Vec<u8> {
|
pub fn get_bytes(&self) -> Vec<u8> {
|
||||||
// Let it panic if something is not okay
|
// Let it panic if something is not okay
|
||||||
serde_json::to_vec(&self).unwrap()
|
serde_json::to_vec(&self).unwrap()
|
||||||
@@ -51,26 +46,9 @@ impl Transaction {
|
|||||||
serde_json::to_string(&self).unwrap()
|
serde_json::to_string(&self).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_identity(identity: &str) -> Bytes {
|
pub fn check_identity(&self, domain: &str) -> bool {
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let hash = hash_identity(&domain, None);
|
||||||
let mut digest = Sha256::new();
|
let confirmation = hash_identity(&domain, Some(&self.pub_key));
|
||||||
digest.input_str(identity);
|
|
||||||
digest.result(&mut buf);
|
|
||||||
Bytes::from_bytes(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_with_key(identity: &str, key: &Bytes) -> Bytes {
|
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
|
||||||
let mut digest = Sha256::new();
|
|
||||||
digest.input_str(identity);
|
|
||||||
digest.input(key.as_bytes());
|
|
||||||
digest.result(&mut buf);
|
|
||||||
Bytes::from_bytes(&buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_for(&self, domain: &str) -> bool {
|
|
||||||
let hash = Self::hash_identity(&domain);
|
|
||||||
let confirmation = Self::hash_with_key(&domain, &self.pub_key);
|
|
||||||
self.identity.eq(&hash) && self.confirmation.eq(&confirmation)
|
self.identity.eq(&hash) && self.confirmation.eq(&confirmation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,20 +61,29 @@ impl fmt::Debug for Transaction {
|
|||||||
.field("method", &self.method)
|
.field("method", &self.method)
|
||||||
.field("data", &self.data)
|
.field("data", &self.data)
|
||||||
.field("pub", &&self.pub_key)
|
.field("pub", &&self.pub_key)
|
||||||
.field("sign", &&self.signature)
|
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for Transaction {
|
impl Serialize for Transaction {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where S: Serializer {
|
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where S: Serializer {
|
||||||
let mut structure = serializer.serialize_struct("Transaction", 6).unwrap();
|
let mut structure = serializer.serialize_struct("Transaction", 4).unwrap();
|
||||||
structure.serialize_field("identity", &self.identity)?;
|
structure.serialize_field("identity", &self.identity)?;
|
||||||
structure.serialize_field("confirmation", &self.confirmation)?;
|
structure.serialize_field("confirmation", &self.confirmation)?;
|
||||||
structure.serialize_field("method", &self.method)?;
|
structure.serialize_field("method", &self.method)?;
|
||||||
structure.serialize_field("data", &self.data)?;
|
structure.serialize_field("data", &self.data)?;
|
||||||
structure.serialize_field("pub_key", &self.pub_key)?;
|
structure.serialize_field("pub_key", &self.pub_key)?;
|
||||||
structure.serialize_field("signature", &self.signature)?;
|
|
||||||
structure.end()
|
structure.end()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hash_identity(identity: &str, key: Option<&Bytes>) -> Bytes {
|
||||||
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
|
let mut digest = Sha256::new();
|
||||||
|
digest.input_str(identity);
|
||||||
|
if let Some(key) = key {
|
||||||
|
digest.input(key.as_bytes());
|
||||||
|
}
|
||||||
|
digest.result(&mut buf);
|
||||||
|
Bytes::from_bytes(&buf)
|
||||||
}
|
}
|
||||||
+20
@@ -16,6 +16,8 @@ use serde::de::{Error as DeError, Visitor};
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{trace, debug, info, warn, error};
|
use log::{trace, debug, info, warn, error};
|
||||||
use crate::hash_is_good;
|
use crate::hash_is_good;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
use num_bigint::BigUint;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Keystore {
|
pub struct Keystore {
|
||||||
@@ -160,6 +162,24 @@ impl PartialEq for Bytes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for Bytes {}
|
||||||
|
|
||||||
|
impl PartialOrd for Bytes {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
|
let self_hash_int = BigUint::from_bytes_be(&self.data);
|
||||||
|
let other_hash_int = BigUint::from_bytes_be(&other.data);
|
||||||
|
Some(self_hash_int.cmp(&other_hash_int))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Bytes {
|
||||||
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
|
let self_hash_int = BigUint::from_bytes_be(&self.data);
|
||||||
|
let other_hash_int = BigUint::from_bytes_be(&other.data);
|
||||||
|
self_hash_int.cmp(&other_hash_int)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Bytes {
|
impl fmt::Debug for Bytes {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fmt.write_str(&crate::utils::to_hex(&self.data))
|
fmt.write_str(&crate::utils::to_hex(&self.data))
|
||||||
|
|||||||
+8
-26
@@ -22,7 +22,7 @@ use simple_logger::{SimpleLogger};
|
|||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{trace, debug, info, warn, error, LevelFilter};
|
use log::{trace, debug, info, warn, error, LevelFilter};
|
||||||
|
|
||||||
use alfis::{Blockchain, Bytes, Context, Keystore, Transaction, check_domain};
|
use alfis::{Blockchain, Bytes, Context, Keystore, Transaction, check_domain, Block};
|
||||||
use alfis::event::Event;
|
use alfis::event::Event;
|
||||||
use alfis::miner::Miner;
|
use alfis::miner::Miner;
|
||||||
use alfis::p2p::Network;
|
use alfis::p2p::Network;
|
||||||
@@ -35,10 +35,6 @@ use alfis::blockchain::filter::BlockchainFilter;
|
|||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate serde_json;
|
extern crate serde_json;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
const ONE_YEAR: u16 = 365;
|
|
||||||
const GENESIS_ZONE: &str = "ygg";
|
|
||||||
const GENESIS_ZONE_DIFFICULTY: u16 = 20;
|
|
||||||
const KEYSTORE_DIFFICULTY: usize = 24;
|
const KEYSTORE_DIFFICULTY: usize = 24;
|
||||||
const SETTINGS_FILENAME: &str = "alfis.cfg";
|
const SETTINGS_FILENAME: &str = "alfis.cfg";
|
||||||
|
|
||||||
@@ -150,12 +146,11 @@ fn start_dns_server(context: &Arc<Mutex<Context>>, settings: &Settings) {
|
|||||||
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
fn create_genesis_if_needed(context: &Arc<Mutex<Context>>, miner: &Arc<Mutex<Miner>>) {
|
||||||
// If there is no origin in settings and no blockchain in DB, generate genesis block
|
// If there is no origin in settings and no blockchain in DB, generate genesis block
|
||||||
let context = context.lock().unwrap();
|
let context = context.lock().unwrap();
|
||||||
// TODO compare first block's hash to origin
|
|
||||||
let last_block = context.get_blockchain().last_block();
|
let last_block = context.get_blockchain().last_block();
|
||||||
let origin = context.settings.origin.clone();
|
let origin = context.settings.origin.clone();
|
||||||
if origin.eq("") && last_block.is_none() {
|
if origin.eq("") && last_block.is_none() {
|
||||||
// If blockchain is empty, we are going to mine a Genesis block
|
// If blockchain is empty, we are going to mine a Genesis block
|
||||||
create_genesis(miner.clone(), GENESIS_ZONE, &context.get_keystore(), GENESIS_ZONE_DIFFICULTY);
|
create_genesis(miner.clone(), &context.get_keystore());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,33 +385,20 @@ fn run_interface(context: Arc<Mutex<Context>>, miner: Arc<Mutex<Miner>>) {
|
|||||||
interface.exit();
|
interface.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_genesis<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, keystore: &Keystore, difficulty: u16) {
|
fn create_genesis(miner: Arc<Mutex<Miner>>, keystore: &Keystore) {
|
||||||
let mut transaction = Transaction::from_str(name.into(), "zone".to_owned(), difficulty.to_string(), keystore.get_public().clone());
|
let block = Block::new(None, keystore.get_public(), Bytes::default());
|
||||||
// Signing it with private key from Signature
|
|
||||||
let sign_hash = keystore.sign(&transaction.get_bytes());
|
|
||||||
transaction.set_signature(Bytes::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_block(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, keystore: &Keystore) {
|
fn create_domain<S: Into<String>>(miner: Arc<Mutex<Miner>>, name: S, data: S, keystore: &Keystore) {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
info!("Generating domain or zone {}", name);
|
info!("Generating domain or zone {}", name);
|
||||||
//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 tags_vector: Vec<String> = tags.into().trim().split(",").map(|s| s.trim()).map(String::from).collect();
|
||||||
let transaction = create_transaction(keystore, name, "domain".into(), data.into());
|
let transaction = Transaction::from_str(name.into(), "domain".into(), data.into(), keystore.get_public().clone());
|
||||||
|
let block = Block::new(Some(transaction), keystore.get_public(), Bytes::default());
|
||||||
let mut miner_guard = miner.lock().unwrap();
|
let mut miner_guard = miner.lock().unwrap();
|
||||||
miner_guard.add_transaction(transaction);
|
miner_guard.add_block(block);
|
||||||
}
|
|
||||||
|
|
||||||
fn create_transaction<S: Into<String>>(keystore: &Keystore, name: S, method: S, data: S) -> Transaction {
|
|
||||||
// Creating transaction
|
|
||||||
// TODO Do not use owner for now, make a field in UI and use it if filled
|
|
||||||
let mut transaction = Transaction::from_str(name.into(), method.into(), data.into(), keystore.get_public().clone());
|
|
||||||
// Signing it with private key from Signature
|
|
||||||
let sign_hash = keystore.sign(&transaction.get_bytes());
|
|
||||||
transaction.set_signature(Bytes::from_bytes(&sign_hash));
|
|
||||||
transaction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_key(context: Arc<Mutex<Context>>) {
|
fn create_key(context: Arc<Mutex<Context>>) {
|
||||||
|
|||||||
+33
-47
@@ -6,16 +6,18 @@ use std::time::Duration;
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use crypto::digest::Digest;
|
use crypto::digest::Digest;
|
||||||
use crypto::sha2::Sha256;
|
use crypto::sha2::Sha256;
|
||||||
use num_cpus;
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use log::{trace, debug, info, warn, error};
|
use log::{debug, error, info, trace, warn};
|
||||||
|
use num_cpus;
|
||||||
|
|
||||||
use crate::{Block, Bytes, Context, hash_is_good, Transaction};
|
use crate::{Block, Bytes, Context, hash_is_good};
|
||||||
|
use crate::blockchain::blockchain::BlockQuality;
|
||||||
|
use crate::blockchain::{BLOCK_DIFFICULTY, CHAIN_VERSION};
|
||||||
use crate::event::Event;
|
use crate::event::Event;
|
||||||
|
|
||||||
pub struct Miner {
|
pub struct Miner {
|
||||||
context: Arc<Mutex<Context>>,
|
context: Arc<Mutex<Context>>,
|
||||||
transactions: Arc<Mutex<Vec<Transaction>>>,
|
blocks: Arc<Mutex<Vec<Block>>>,
|
||||||
running: Arc<AtomicBool>,
|
running: Arc<AtomicBool>,
|
||||||
mining: Arc<AtomicBool>,
|
mining: Arc<AtomicBool>,
|
||||||
cond_var: Arc<Condvar>
|
cond_var: Arc<Condvar>
|
||||||
@@ -25,15 +27,15 @@ impl Miner {
|
|||||||
pub fn new(context: Arc<Mutex<Context>>) -> Self {
|
pub fn new(context: Arc<Mutex<Context>>) -> Self {
|
||||||
Miner {
|
Miner {
|
||||||
context: context.clone(),
|
context: context.clone(),
|
||||||
transactions: Arc::new(Mutex::new(Vec::new())),
|
blocks: Arc::new(Mutex::new(Vec::new())),
|
||||||
running: Arc::new(AtomicBool::new(false)),
|
running: Arc::new(AtomicBool::new(false)),
|
||||||
mining: Arc::new(AtomicBool::new(false)),
|
mining: Arc::new(AtomicBool::new(false)),
|
||||||
cond_var: Arc::new(Condvar::new())
|
cond_var: Arc::new(Condvar::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_transaction(&mut self, transaction: Transaction) {
|
pub fn add_block(&mut self, block: Block) {
|
||||||
self.transactions.lock().unwrap().push(transaction);
|
self.blocks.lock().unwrap().push(block);
|
||||||
self.cond_var.notify_one();
|
self.cond_var.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ impl Miner {
|
|||||||
|
|
||||||
pub fn start_mining_thread(&mut self) {
|
pub fn start_mining_thread(&mut self) {
|
||||||
let context = self.context.clone();
|
let context = self.context.clone();
|
||||||
let transactions = self.transactions.clone();
|
let blocks = self.blocks.clone();
|
||||||
let running = self.running.clone();
|
let running = self.running.clone();
|
||||||
let mining = self.mining.clone();
|
let mining = self.mining.clone();
|
||||||
let cond_var = self.cond_var.clone();
|
let cond_var = self.cond_var.clone();
|
||||||
@@ -58,12 +60,12 @@ impl Miner {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut lock = transactions.lock().unwrap();
|
let mut lock = blocks.lock().unwrap();
|
||||||
if lock.len() > 0 {
|
if lock.len() > 0 {
|
||||||
info!("Got new transaction to mine");
|
info!("Got new block to mine");
|
||||||
let transaction = lock.remove(0);
|
let block = lock.remove(0);
|
||||||
mining.store(true, Ordering::SeqCst);
|
mining.store(true, Ordering::SeqCst);
|
||||||
Miner::mine_internal(context.clone(), transactions.clone(), transaction, mining.clone(), cond_var.clone());
|
Miner::mine_internal(context.clone(), block, mining.clone());
|
||||||
} else {
|
} else {
|
||||||
let _ = cond_var.wait(lock).expect("Error in wait lock!");
|
let _ = cond_var.wait(lock).expect("Error in wait lock!");
|
||||||
}
|
}
|
||||||
@@ -82,46 +84,27 @@ impl Miner {
|
|||||||
self.running.load(Ordering::Relaxed)
|
self.running.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mine_internal(context: Arc<Mutex<Context>>, transactions: Arc<Mutex<Vec<Transaction>>>, mut transaction: Transaction, mining: Arc<AtomicBool>, cond_var: Arc<Condvar>) {
|
fn mine_internal(context: Arc<Mutex<Context>>, mut block: Block, mining: Arc<AtomicBool>) {
|
||||||
let version= {
|
// Clear signature and hash just in case
|
||||||
let mut c = context.lock().unwrap();
|
block.signature = Bytes::default();
|
||||||
c.bus.post(Event::MinerStarted);
|
block.hash = Bytes::default();
|
||||||
c.settings.version
|
block.version = CHAIN_VERSION;
|
||||||
};
|
block.difficulty = BLOCK_DIFFICULTY;
|
||||||
let block = {
|
block.index = context.lock().unwrap().blockchain.height();
|
||||||
if transaction.signature.is_zero() {
|
block.prev_block_hash = match context.lock().unwrap().blockchain.last_block() {
|
||||||
// Signing it with private key from Keystore
|
None => { Bytes::default() }
|
||||||
let c = context.lock().unwrap();
|
Some(block) => { block.hash }
|
||||||
let sign_hash = c.keystore.sign(&transaction.get_bytes());
|
|
||||||
transaction.set_signature(Bytes::from_bytes(&sign_hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get last block for mining
|
|
||||||
let last_block = { context.lock().unwrap().blockchain.last_block() };
|
|
||||||
match last_block {
|
|
||||||
None => {
|
|
||||||
warn!("Mining genesis block");
|
|
||||||
// Creating a block with that signed transaction
|
|
||||||
Block::new(0, Utc::now().timestamp(), version, Bytes::zero32(), Some(transaction.clone()))
|
|
||||||
},
|
|
||||||
Some(block) => {
|
|
||||||
// Creating a block with that signed transaction
|
|
||||||
Block::new(block.index + 1, Utc::now().timestamp(), version, block.hash.clone(), Some(transaction.clone()))
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
context.lock().unwrap().bus.post(Event::MinerStarted);
|
||||||
let live_threads = Arc::new(AtomicU32::new(0u32));
|
let live_threads = Arc::new(AtomicU32::new(0u32));
|
||||||
let cpus = num_cpus::get();
|
let cpus = num_cpus::get();
|
||||||
debug!("Starting {} threads for mining", cpus);
|
debug!("Starting {} threads for mining", cpus);
|
||||||
for _ in 0..cpus {
|
for _ in 0..cpus {
|
||||||
let transactions = transactions.clone();
|
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
let transaction = transaction.clone();
|
|
||||||
let block = block.clone();
|
let block = block.clone();
|
||||||
let mining = mining.clone();
|
let mining = mining.clone();
|
||||||
let live_threads = live_threads.clone();
|
let live_threads = live_threads.clone();
|
||||||
let cond_var = cond_var.clone();
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
live_threads.fetch_add(1, Ordering::SeqCst);
|
live_threads.fetch_add(1, Ordering::SeqCst);
|
||||||
match find_hash(&mut Sha256::new(), block, mining.clone()) {
|
match find_hash(&mut Sha256::new(), block, mining.clone()) {
|
||||||
@@ -134,14 +117,17 @@ impl Miner {
|
|||||||
context.bus.post(Event::MinerStopped);
|
context.bus.post(Event::MinerStopped);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Some(block) => {
|
Some(mut block) => {
|
||||||
let index = block.index;
|
let index = block.index;
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
if context.blockchain.add_block(block).is_err() {
|
block.signature = Bytes::from_bytes(&context.keystore.sign(&block.as_bytes()));
|
||||||
|
if context.blockchain.check_new_block(&block) != BlockQuality::Good {
|
||||||
warn!("Error adding mined block!");
|
warn!("Error adding mined block!");
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
error!("To mine genesis block you need to make 'origin' an empty string in config.");
|
error!("To mine genesis block you need to make 'origin' an empty string in config.");
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
context.blockchain.add_block(block);
|
||||||
}
|
}
|
||||||
context.bus.post(Event::MinerStopped);
|
context.bus.post(Event::MinerStopped);
|
||||||
mining.store(false, Ordering::SeqCst);
|
mining.store(false, Ordering::SeqCst);
|
||||||
@@ -154,6 +140,7 @@ impl Miner {
|
|||||||
|
|
||||||
fn find_hash(digest: &mut dyn Digest, mut block: Block, running: Arc<AtomicBool>) -> Option<Block> {
|
fn find_hash(digest: &mut dyn Digest, mut block: Block, running: Arc<AtomicBool>) -> Option<Block> {
|
||||||
let mut buf: [u8; 32] = [0; 32];
|
let mut buf: [u8; 32] = [0; 32];
|
||||||
|
let difficulty = block.difficulty as usize;
|
||||||
loop {
|
loop {
|
||||||
block.random = rand::random();
|
block.random = rand::random();
|
||||||
debug!("Mining block {}", serde_json::to_string(&block).unwrap());
|
debug!("Mining block {}", serde_json::to_string(&block).unwrap());
|
||||||
@@ -165,13 +152,12 @@ fn find_hash(digest: &mut dyn Digest, mut block: Block, running: Arc<AtomicBool>
|
|||||||
block.nonce = nonce;
|
block.nonce = nonce;
|
||||||
|
|
||||||
digest.reset();
|
digest.reset();
|
||||||
digest.input(serde_json::to_string(&block).unwrap().as_bytes());
|
digest.input(&block.as_bytes());
|
||||||
digest.result(&mut buf);
|
digest.result(&mut buf);
|
||||||
if hash_is_good(&buf, block.difficulty) {
|
if hash_is_good(&buf, difficulty) {
|
||||||
block.hash = Bytes::from_bytes(&buf);
|
block.hash = Bytes::from_bytes(&buf);
|
||||||
return Some(block);
|
return Some(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None
|
|
||||||
}
|
}
|
||||||
+9
-4
@@ -15,6 +15,7 @@ use log::{trace, debug, info, warn, error};
|
|||||||
|
|
||||||
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
|
use crate::{Context, Block, p2p::Message, p2p::State, p2p::Peer, p2p::Peers};
|
||||||
use std::net::{SocketAddr, IpAddr, SocketAddrV4, ToSocketAddrs};
|
use std::net::{SocketAddr, IpAddr, SocketAddrV4, ToSocketAddrs};
|
||||||
|
use crate::blockchain::blockchain::BlockQuality;
|
||||||
|
|
||||||
const SERVER: Token = Token(0);
|
const SERVER: Token = Token(0);
|
||||||
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
const POLL_TIMEOUT: Option<Duration> = Some(Duration::from_millis(3000));
|
||||||
@@ -340,13 +341,13 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
Ok(block) => block,
|
Ok(block) => block,
|
||||||
Err(_) => return State::Error
|
Err(_) => return State::Error
|
||||||
};
|
};
|
||||||
// TODO check here if the block is good before trying to add
|
|
||||||
let context = context.clone();
|
let context = context.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let mut context = context.lock().unwrap();
|
let mut context = context.lock().unwrap();
|
||||||
let max_height = context.blockchain.max_height();
|
let max_height = context.blockchain.max_height();
|
||||||
match context.blockchain.add_block(block) {
|
match context.blockchain.check_new_block(&block) {
|
||||||
Ok(_) => {
|
BlockQuality::Good => {
|
||||||
|
context.blockchain.add_block(block);
|
||||||
let my_height = context.blockchain.height();
|
let my_height = context.blockchain.height();
|
||||||
context.bus.post(crate::event::Event::BlockchainChanged);
|
context.bus.post(crate::event::Event::BlockchainChanged);
|
||||||
// If it was the last block to sync
|
// If it was the last block to sync
|
||||||
@@ -356,7 +357,11 @@ fn handle_message(context: Arc<Mutex<Context>>, message: Message, peers: &mut Pe
|
|||||||
context.bus.post(crate::event::Event::Syncing { have: my_height, height: max_height});
|
context.bus.post(crate::event::Event::Syncing { have: my_height, height: max_height});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => { warn!("Discarded received block"); }
|
BlockQuality::Twin => { debug!("Ignoring duplicate block {}", block.index); }
|
||||||
|
BlockQuality::Future => { debug!("Ignoring future block {}", block.index); }
|
||||||
|
BlockQuality::Bad => { debug!("Ignoring bad block {} with hash {:?}", block.index, block.hash); }
|
||||||
|
// TODO deal with forks
|
||||||
|
BlockQuality::Fork => { debug!("Ignoring forked block {} with hash {:?}", block.index, block.hash); }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
State::idle()
|
State::idle()
|
||||||
|
|||||||
@@ -74,19 +74,6 @@ pub fn hash_is_good(hash: &[u8], difficulty: usize) -> bool {
|
|||||||
return hash_int < target;
|
return hash_int < target;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 _ in 0..length {
|
|
||||||
let position: usize = rng.gen::<usize>() % chars.len();
|
|
||||||
let c: char = *chars.get(position).unwrap();
|
|
||||||
result.push(c);
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::check_domain;
|
use crate::check_domain;
|
||||||
|
|||||||
Reference in New Issue
Block a user