diff --git a/examples/mine_derivative.rs b/examples/mine_derivative.rs new file mode 100644 index 0000000..61ed49b --- /dev/null +++ b/examples/mine_derivative.rs @@ -0,0 +1,74 @@ +use blockchaintree::block::Block as _; +use blockchaintree::tools; +use blockchaintree::{blockchaintree::BlockChainTree, static_values}; +use primitive_types::U256; +use std::time::{SystemTime, UNIX_EPOCH}; + +fn main() { + let rt = tokio::runtime::Runtime::new().unwrap(); + + let mut tree = BlockChainTree::new().unwrap(); + + let wallet = [1u8; 33]; + + let chain = tree.get_derivative_chain(&wallet).unwrap(); + + loop { + println!("Current height: {}", chain.get_height()); + println!( + "Current miner gas amount: {}", + tree.get_gas(&wallet).unwrap() + ); + let mut nonce = U256::zero(); + let (prev_hash, difficulty, _prev_timestamp, _height) = + if let Some(block) = chain.get_last_block().unwrap() { + ( + block.hash().unwrap(), + block.get_info().difficulty, + block.get_info().timestamp, + block.get_info().height, + ) + } else { + let block = tree + .get_main_chain() + .find_by_hash(&chain.genesis_hash) + .unwrap() + .unwrap(); + ( + block.hash().unwrap(), + static_values::BEGINNING_DIFFICULTY, + block.get_info().timestamp, + U256::zero(), + ) + }; + println!( + "Current difficulty: {}", + tools::count_leading_zeros(&difficulty) + ); + while nonce < U256::MAX { + let mut pow = [0u8; 32]; + nonce.to_big_endian(&mut pow); + if tools::check_pow(&prev_hash, &difficulty, &pow) { + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + println!("Found nonce! {}", nonce); + + let block = rt + .block_on(tree.emmit_new_derivative_block(&pow, &wallet, timestamp)) + .unwrap(); + + tree.add_gas(&wallet, *static_values::MAIN_CHAIN_PAYMENT) + .unwrap(); + + println!("Added new block! {:?}\n", block.hash().unwrap()); + + rt.block_on(chain.flush()).unwrap(); + break; + } + nonce += U256::one(); + } + } +} diff --git a/src/block.rs b/src/block.rs index 45d6724..4c1e079 100644 --- a/src/block.rs +++ b/src/block.rs @@ -237,7 +237,7 @@ impl TransactionBlock { #[derive(Debug)] pub struct DerivativeBlock { pub default_info: BasicInfo, - pub payment_transaction: Hash, + //pub payment_transaction: Hash, } pub trait Block { @@ -255,7 +255,7 @@ pub trait Block { impl Block for DerivativeBlock { fn get_dump_size(&self) -> usize { - self.default_info.get_dump_size() + 32 + 1 + self.default_info.get_dump_size() + 1 } fn get_info(&self) -> &BasicInfo { &self.default_info @@ -268,7 +268,6 @@ impl Block for DerivativeBlock { let mut to_return = Vec::::with_capacity(size); to_return.push(Headers::DerivativeBlock as u8); - to_return.extend(self.payment_transaction.iter()); self.default_info.dump(&mut to_return)?; Ok(to_return) @@ -277,7 +276,8 @@ impl Block for DerivativeBlock { Ok(tools::hash(&self.dump()?)) } fn get_merkle_root(&self) -> Hash { - self.payment_transaction + unimplemented!() + //self.payment_transaction } fn verify_block(&self, prev_hash: &Hash) -> bool { self.default_info.previous_hash.eq(prev_hash) @@ -335,13 +335,10 @@ impl DerivativeBlock { .attach_printable("data.len() < 32"), ); } - let mut index: usize = 0; - let payment_transaction: Hash = unsafe { data[0..32].try_into().unwrap_unchecked() }; // read payment transaction hash - index += 32; - let default_info: BasicInfo = BasicInfo::parse(&data[index..])?; + let default_info: BasicInfo = BasicInfo::parse(data)?; Ok(DerivativeBlock { default_info, - payment_transaction, + //payment_transaction, }) } } diff --git a/src/blockchaintree.rs b/src/blockchaintree.rs index 2859a31..1f4975a 100644 --- a/src/blockchaintree.rs +++ b/src/blockchaintree.rs @@ -1,12 +1,12 @@ use std::{collections::HashMap, path::Path, sync::Arc}; use crate::{ - block::{self, BlockArc, TransactionBlock}, + block::{self, Block as _, BlockArc, TransactionBlock}, chain, - errors::{BCTreeErrorKind, BlockChainTreeError}, + errors::{BCTreeErrorKind, BlockChainTreeError, ChainErrorKind}, merkletree, static_values::{ - AMMOUNT_SUMMARY, BLOCKS_PER_EPOCH, COINS_PER_CYCLE, GAS_SUMMARY, MAIN_CHAIN_PAYMENT, + self, AMMOUNT_SUMMARY, BLOCKS_PER_EPOCH, COINS_PER_CYCLE, GAS_SUMMARY, MAIN_CHAIN_PAYMENT, OLD_AMMOUNT_SUMMARY, OLD_GAS_SUMMARY, ROOT_PUBLIC_ADDRESS, }, tools, @@ -21,7 +21,7 @@ use std::fs; pub struct BlockChainTree { main_chain: chain::MainChain, - pub derivative_chains: HashMap<[u8; 32], chain::DerivativeChain>, + derivative_chains: HashMap<[u8; 33], chain::DerivativeChain>, summary_db: Db, old_summary_db: Db, gas_db: Db, @@ -78,6 +78,21 @@ impl BlockChainTree { }) } + pub fn get_derivative_chain( + &mut self, + owner: &[u8; 33], + ) -> Result> { + if let Some(chain) = self.derivative_chains.get(owner) { + return Ok(chain.clone()); + } + let last_block = self.main_chain.get_last_block()?.unwrap(); // practically cannot fail + let derivative_chain = + chain::DerivativeChain::new(&hex::encode(owner), &last_block.hash().unwrap())?; + self.derivative_chains + .insert(owner.clone(), derivative_chain.clone()); + Ok(derivative_chain) + } + pub fn get_main_chain(&self) -> chain::MainChain { self.main_chain.clone() } @@ -204,11 +219,7 @@ impl BlockChainTree { Ok(()) } - pub fn add_gas_amount( - &self, - owner: &[u8], - amount: U256, - ) -> Result<(), Report> { + pub fn add_gas(&self, owner: &[u8], amount: U256) -> Result<(), Report> { self.gas_db .transaction( |db| -> Result<(), sled::transaction::ConflictableTransactionError<()>> { @@ -227,11 +238,7 @@ impl BlockChainTree { Ok(()) } - pub fn sub_gas_amount( - &self, - owner: &[u8], - amount: U256, - ) -> Result<(), Report> { + pub fn sub_gas(&self, owner: &[u8], amount: U256) -> Result<(), Report> { self.gas_db .transaction( |db| -> Result<(), sled::transaction::ConflictableTransactionError<()>> { @@ -253,7 +260,7 @@ impl BlockChainTree { Ok(()) } - pub fn get_gas_amount(&self, owner: &[u8; 33]) -> Result> { + pub fn get_gas(&self, owner: &[u8; 33]) -> Result> { match self .gas_db .get(owner) @@ -349,6 +356,52 @@ impl BlockChainTree { Ok(*merkle_tree.get_root()) } + pub async fn emmit_new_derivative_block( + &mut self, + pow: &[u8; 32], + founder: &[u8; 33], + timestamp: u64, + ) -> Result> { + let derivative_chain = self.get_derivative_chain(founder)?; + let (prev_hash, mut difficulty, prev_timestamp, height) = + if let Some(block) = derivative_chain.get_last_block()? { + ( + block.hash().unwrap(), + block.get_info().difficulty, + block.get_info().timestamp, + block.get_info().height, + ) + } else { + let block = self + .main_chain + .find_by_hash(&derivative_chain.genesis_hash)? + .ok_or(BlockChainTreeError::Chain(ChainErrorKind::FindByHashE))?; + ( + block.hash().unwrap(), + static_values::BEGINNING_DIFFICULTY, + block.get_info().timestamp, + U256::zero(), + ) + }; + + if !tools::check_pow(&prev_hash, &difficulty, pow) { + return Err(BlockChainTreeError::BlockChainTree(BCTreeErrorKind::WrongPow).into()); + }; + tools::recalculate_difficulty(prev_timestamp, timestamp, &mut difficulty); + let default_info = block::BasicInfo { + timestamp, + pow: *pow, + previous_hash: prev_hash, + height: height + 1, + difficulty, + founder: *founder, + }; + + let block = block::DerivativeBlock { default_info }; + derivative_chain.add_block(&block)?; + Ok(Arc::new(block)) + } + pub async fn emmit_new_main_block( &mut self, pow: &[u8; 32], @@ -362,10 +415,10 @@ impl BlockChainTree { .change_context(BlockChainTreeError::BlockChainTree(BCTreeErrorKind::DumpDb)) .attach_printable("failed to hash block")?; - if !tools::check_pow(&prev_hash, &last_block.get_info().difficulty, pow) { + let mut difficulty = last_block.get_info().difficulty; + if !tools::check_pow(&prev_hash, &difficulty, pow) { return Err(BlockChainTreeError::BlockChainTree(BCTreeErrorKind::WrongPow).into()); }; - let mut difficulty = last_block.get_info().difficulty; tools::recalculate_difficulty(last_block.get_info().timestamp, timestamp, &mut difficulty); let fee = tools::recalculate_fee(&difficulty); let default_info = block::BasicInfo { diff --git a/src/chain.rs b/src/chain.rs index 8b88c3e..0e2d5cd 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -416,18 +416,18 @@ impl MainChain { } } +#[derive(Clone)] pub struct DerivativeChain { blocks: Db, height_reference: Db, - transactions: Db, height: Arc>, - genesis_hash: Arc<[u8; 32]>, + pub genesis_hash: Arc<[u8; 32]>, difficulty: Arc>, chain_owner: String, } impl DerivativeChain { - pub async fn new( + pub fn new( chain_owner: &str, provided_genesis_hash: &[u8; 32], ) -> Result> { @@ -435,12 +435,10 @@ impl DerivativeChain { let path_blocks_st = root.clone() + BLOCKS_FOLDER; let path_references_st = root.clone() + REFERENCES_FOLDER; - let path_transactions_st = root.clone() + TRANSACTIONS_FOLDER; let path_height_st = root + CONFIG_FILE; let path_blocks = Path::new(&path_blocks_st); let path_reference = Path::new(&path_references_st); - let path_transactions = Path::new(&path_transactions_st); let path_height = Path::new(&path_height_st); // open blocks DB @@ -453,11 +451,6 @@ impl DerivativeChain { .change_context(BlockChainTreeError::Chain(ChainErrorKind::Init)) .attach_printable("failed to open references db")?; - // open transactions DB - let transactions = sled::open(path_transactions) - .change_context(BlockChainTreeError::Chain(ChainErrorKind::Init)) - .attach_printable("failed to open transactions db")?; - let file = File::open(path_height); let (height, difficulty, genesis_hash) = if let Ok(mut file) = file { @@ -494,11 +487,15 @@ impl DerivativeChain { difficulty: Arc::new(RwLock::new(difficulty)), genesis_hash: Arc::new(genesis_hash), chain_owner: chain_owner.to_string(), - transactions, }; Ok(chain) } + + pub fn get_height(&self) -> U256 { + *self.height.read() + } + /// Dump config /// /// Dumps chain's config @@ -549,79 +546,15 @@ impl DerivativeChain { .change_context(BlockChainTreeError::Chain(ChainErrorKind::DumpConfig)) .attach_printable("failed to flush height references")?; - self.transactions - .flush_async() - .await - .change_context(BlockChainTreeError::Chain(ChainErrorKind::DumpConfig)) - .attach_printable("failed to flush transactions")?; - - Ok(()) - } - - pub async fn add_transaction( - &self, - transaction: &impl transaction::Transactionable, - ) -> Result<(), Report> { - let dump = transaction - .dump() - .change_context(BlockChainTreeError::Chain( - ChainErrorKind::AddingTransaction, - ))?; - self.transactions - .insert(tools::hash(&dump), dump) - .change_context(BlockChainTreeError::Chain( - ChainErrorKind::AddingTransaction, - )) - .attach_printable("Failed to insert transaction")?; - self.transactions - .flush_async() - .await - .change_context(BlockChainTreeError::Chain( - ChainErrorKind::AddingTransaction, - )) - .attach_printable("Failed to insert transaction")?; Ok(()) } - pub fn get_transaction_raw( - &self, - transaction_hash: &[u8; 32], - ) -> Result>, Report> { - let transaction = self - .transactions - .get(transaction_hash) - .change_context(BlockChainTreeError::Chain(ChainErrorKind::FindByHashE))?; - Ok(transaction.map(|v| v.to_vec())) - } - - pub fn get_transaction( - &self, - transaction_hash: &[u8; 32], - ) -> Result, Report> { - let raw_transaction = self.get_transaction_raw(transaction_hash)?; - - if let Some(tr) = raw_transaction { - if !tr.first().unwrap_or(&10).eq(&(Headers::Transaction as u8)) { - return Err(BlockChainTreeError::Chain(ChainErrorKind::FindByHashE).into()); - } - return Ok(Some( - transaction::Transaction::parse(&tr[1..]) - .change_context(BlockChainTreeError::Chain(ChainErrorKind::FindByHashE))?, - )); - } - - Ok(None) - } - /// Adds new block to the chain db /// /// Adds block and sets heigh reference for it /// /// Checks for blocks validity, adds it directly to the end of the chain - pub async fn add_block( - &self, - block: &DerivativeBlock, - ) -> Result<(), Report> { + pub fn add_block(&self, block: &DerivativeBlock) -> Result<(), Report> { let dump = block .dump() .change_context(BlockChainTreeError::Chain(ChainErrorKind::AddingBlock))?; @@ -630,7 +563,7 @@ impl DerivativeChain { let mut height = self.height.write(); - if block.get_info().height != *height { + if block.get_info().height != *height + 1 { return Err(BlockChainTreeError::Chain(ChainErrorKind::AddingBlock)).attach_printable( "The height of the chain is different from the height of the block", ); @@ -651,23 +584,11 @@ impl DerivativeChain { *height += U256::one(); - self.blocks - .flush_async() - .await - .change_context(BlockChainTreeError::Chain(ChainErrorKind::AddingBlock)) - .attach_printable("Failed to flush blocks db")?; - - self.height_reference - .flush_async() - .await - .change_context(BlockChainTreeError::Chain(ChainErrorKind::AddingBlock)) - .attach_printable("Failed to flush height reference db")?; - Ok(()) } /// Get serialized block by it's height - pub async fn find_raw_by_height( + pub fn find_raw_by_height( &self, height: &U256, ) -> Result>, Report> { @@ -691,7 +612,7 @@ impl DerivativeChain { } /// Get serialized block by it's hash - pub async fn find_raw_by_hash( + pub fn find_raw_by_hash( &self, hash: &[u8; 32], ) -> Result>, Report> { @@ -708,17 +629,16 @@ impl DerivativeChain { let block = self .find_raw_by_height(&height) - .await .change_context(BlockChainTreeError::Chain(ChainErrorKind::FindByHashE))?; Ok(block) } - pub async fn find_by_hash( + pub fn find_by_hash( &self, hash: &[u8; 32], ) -> Result>, Report> { - let dump = self.find_raw_by_hash(hash).await?; + let dump = self.find_raw_by_hash(hash)?; let deserialized = if let Some(data) = dump { Some(Arc::new( @@ -737,28 +657,29 @@ impl DerivativeChain { } /// Get serialized last block of the chain - pub async fn get_last_raw_block(&self) -> Result>, Report> { + pub fn get_last_raw_block(&self) -> Result>, Report> { let height = self.height.read(); + if height.is_zero() { + return Ok(None); + } let last_block_index = *height - 1; drop(height); - self.find_raw_by_height(&last_block_index).await + self.find_raw_by_height(&last_block_index) } /// Get deserialized latest block - pub async fn get_last_block( - &self, - ) -> Result>, Report> { - let dump = self.get_last_raw_block().await?; + pub fn get_last_block(&self) -> Result, Report> { + let dump = self.get_last_raw_block()?; let deserialized = if let Some(data) = dump { - Some(Arc::new( + Some( block::DerivativeBlock::parse(&data[1..]) .change_context(BlockChainTreeError::Chain(ChainErrorKind::FindByHeight)) .attach_printable( "Failed to deserialize latest main chain block".to_string(), )?, - )) + ) } else { None }; @@ -767,11 +688,11 @@ impl DerivativeChain { } /// Get deserialized block by height - pub async fn find_by_height( + pub fn find_by_height( &self, height: &U256, ) -> Result>, Report> { - let dump = self.find_raw_by_height(height).await?; + let dump = self.find_raw_by_height(height)?; let deserialized = if let Some(data) = dump { Some(Arc::new( diff --git a/tests/block_test.rs b/tests/block_test.rs index 835e8b0..aabc2bf 100644 --- a/tests/block_test.rs +++ b/tests/block_test.rs @@ -165,10 +165,9 @@ fn dump_parse_derivative_block() { difficulty: [101; 32], founder: [6; 33], }; - let payment_transaction = [0; 32]; let derivative_block = DerivativeBlock { default_info: basic_data, - payment_transaction, + //payment_transaction, }; let dumped_block = derivative_block.dump().unwrap(); let parsed_block = DerivativeBlock::parse(&dumped_block[1..]).unwrap(); @@ -197,15 +196,10 @@ fn dump_parse_derivative_block() { derivative_block.default_info.founder, parsed_block.default_info.founder ); - assert_eq!( - derivative_block.payment_transaction, - parsed_block.payment_transaction - ); } #[test] fn validate_derivative_block() { - let payment_transaction = [0; 32]; let basic_data = block::BasicInfo { timestamp: 160000, pow: [0; 32], @@ -216,9 +210,7 @@ fn validate_derivative_block() { }; let prev_block = DerivativeBlock { default_info: basic_data, - payment_transaction, }; - let payment_transaction = [0; 32]; let basic_data = block::BasicInfo { timestamp: 160000, pow: [0; 32], @@ -229,7 +221,6 @@ fn validate_derivative_block() { }; let derivative_block = DerivativeBlock { default_info: basic_data, - payment_transaction, }; assert!(!derivative_block diff --git a/tests/blockchaintree_test.rs b/tests/blockchaintree_test.rs index 9bcae30..13db1ba 100644 --- a/tests/blockchaintree_test.rs +++ b/tests/blockchaintree_test.rs @@ -1,25 +1,21 @@ -use std::str::FromStr; - use blockchaintree::blockchaintree::BlockChainTree; use primitive_types::U256; #[tokio::test] async fn test_amounts() { - let tree = BlockChainTree::new().await.unwrap(); + let tree = BlockChainTree::new().unwrap(); let address_a = [0; 33]; let address_b = [1; 33]; tree.add_amount(&address_a, U256::from_dec_str("10000000000").unwrap()) - .await .unwrap(); - let amount = tree.get_amount(&address_a).await.unwrap(); + let amount = tree.get_amount(&address_a).unwrap(); assert_eq!(amount, U256::from_dec_str("10000000000").unwrap()); tree.send_amount(&address_a, &address_b, U256::from_dec_str("100").unwrap()) - .await .unwrap(); - let amount_a = tree.get_amount(&address_a).await.unwrap(); - let amount_b = tree.get_amount(&address_b).await.unwrap(); + let amount_a = tree.get_amount(&address_a).unwrap(); + let amount_b = tree.get_amount(&address_b).unwrap(); println!("{:?}", amount_a); println!("{:?}", amount_b); assert_eq!( diff --git a/tests/chain_test.rs b/tests/chain_test.rs index 5c8bf1b..eab7a22 100644 --- a/tests/chain_test.rs +++ b/tests/chain_test.rs @@ -8,15 +8,15 @@ use primitive_types::U256; #[tokio::test] async fn init_flush_get_block_by_height_chain_test() { - let main_chain = chain::MainChain::new().await.unwrap(); + let main_chain = chain::MainChain::new().unwrap(); main_chain.flush().await.unwrap(); drop(main_chain); - let main_chain = chain::MainChain::new().await.unwrap(); + let main_chain = chain::MainChain::new().unwrap(); - let height = main_chain.get_height().await; + let height = main_chain.get_height(); // generate block let basic_data = block::BasicInfo { @@ -34,10 +34,10 @@ async fn init_flush_get_block_by_height_chain_test() { vec![[0; 32], [1; 32]], ); - main_chain.add_block(Arc::new(main_block)).await.unwrap(); + main_chain.add_block(Arc::new(main_block)).unwrap(); - let height = main_chain.get_height().await; - let block = main_chain.find_by_height(&(height - 1)).await.unwrap(); + let height = main_chain.get_height(); + let block = main_chain.find_by_height(&(height - 1)).unwrap(); assert!(block.is_some()); @@ -54,7 +54,7 @@ async fn init_flush_get_block_by_height_chain_test() { #[tokio::test] async fn init_get_transaction_chain_test() { - let main_chain = chain::MainChain::new().await.unwrap(); + let main_chain = chain::MainChain::new().unwrap(); let transaction = transaction::Transaction::new_signed( [10; 33], @@ -66,10 +66,7 @@ async fn init_get_transaction_chain_test() { [33; 64], ); - main_chain - .add_transactions(&[transaction.clone()]) - .await - .unwrap(); + main_chain.add_transactions(&[transaction.clone()]).unwrap(); let got_transaction = main_chain .get_transaction(&tools::hash(&transaction.dump().unwrap())) @@ -94,7 +91,6 @@ async fn init_flush_get_block_by_height_deriv_chain_test() { 35, 169, 60, 208, 8, 94, 13, 60, 218, 72, 73, 207, 80, ], ) - .await .unwrap(); deriv_chain.flush().await.unwrap(); @@ -107,7 +103,6 @@ async fn init_flush_get_block_by_height_deriv_chain_test() { 35, 169, 60, 208, 8, 94, 13, 60, 218, 72, 73, 207, 80, ], ) - .await .unwrap(); // generate block @@ -119,14 +114,12 @@ async fn init_flush_get_block_by_height_deriv_chain_test() { difficulty: [101; 32], founder: [6; 33], }; - let payment_transaction = [0; 32]; let derivative_block = block::DerivativeBlock { default_info: basic_data, - payment_transaction, }; - deriv_chain.add_block(&derivative_block).await.unwrap(); + deriv_chain.add_block(&derivative_block).unwrap(); - let block = deriv_chain.find_by_height(&U256::zero()).await.unwrap(); + let block = deriv_chain.find_by_height(&U256::zero()).unwrap(); assert!(block.is_some()); @@ -153,8 +146,4 @@ async fn init_flush_get_block_by_height_deriv_chain_test() { derivative_block.default_info.founder, block.default_info.founder ); - assert_eq!( - derivative_block.payment_transaction, - block.payment_transaction - ); }