diff --git a/examples/mine.rs b/examples/mine.rs index 2b40c03..bf9437f 100644 --- a/examples/mine.rs +++ b/examples/mine.rs @@ -11,7 +11,10 @@ fn main() { let main_chain = tree.get_main_chain(); - let wallet = [1u8; 33]; + let wallet: [u8; 33] = [ + 2, 178, 140, 81, 31, 206, 208, 171, 143, 240, 128, 134, 115, 82, 188, 63, 146, 189, 14, 59, + 85, 8, 11, 28, 137, 161, 145, 216, 251, 95, 93, 137, 159, + ]; loop { println!("Current height: {}", main_chain.get_height()); @@ -55,6 +58,7 @@ fn main() { .block_on(tree.emmit_new_main_block(&pow, &wallet, transactions, timestamp)) .unwrap(); + // Node should handle this tree.send_amount( &static_values::ROOT_PUBLIC_ADDRESS, &wallet, @@ -62,6 +66,11 @@ fn main() { ) .unwrap(); + let fee = tools::recalculate_fee(&last_block.get_info().difficulty); + for _ in transactions { + tree.add_amount(&wallet, fee).unwrap(); + } + println!("Added new block! {:?}\n", block.hash().unwrap()); rt.block_on(tree.flush()).unwrap(); diff --git a/examples/mine_derivative.rs b/examples/mine_derivative.rs index 61ed49b..a2c4cfe 100644 --- a/examples/mine_derivative.rs +++ b/examples/mine_derivative.rs @@ -9,7 +9,10 @@ fn main() { let mut tree = BlockChainTree::new().unwrap(); - let wallet = [1u8; 33]; + let wallet: [u8; 33] = [ + 2, 178, 140, 81, 31, 206, 208, 171, 143, 240, 128, 134, 115, 82, 188, 63, 146, 189, 14, 59, + 85, 8, 11, 28, 137, 161, 145, 216, 251, 95, 93, 137, 159, + ]; let chain = tree.get_derivative_chain(&wallet).unwrap(); @@ -60,12 +63,14 @@ fn main() { .block_on(tree.emmit_new_derivative_block(&pow, &wallet, timestamp)) .unwrap(); + // Node should handle this tree.add_gas(&wallet, *static_values::MAIN_CHAIN_PAYMENT) .unwrap(); println!("Added new block! {:?}\n", block.hash().unwrap()); rt.block_on(chain.flush()).unwrap(); + rt.block_on(tree.flush()).unwrap(); break; } nonce += U256::one(); diff --git a/examples/send_transaction.rs b/examples/send_transaction.rs new file mode 100644 index 0000000..38672db --- /dev/null +++ b/examples/send_transaction.rs @@ -0,0 +1,95 @@ +use blockchaintree::static_values::BLOCKS_PER_EPOCH; +use blockchaintree::transaction::Transactionable; +use blockchaintree::{blockchaintree::BlockChainTree, static_values}; +use blockchaintree::{tools, transaction}; +use primitive_types::U256; +use std::str::FromStr; +use std::time::{SystemTime, UNIX_EPOCH}; + +fn main() { + let rt = tokio::runtime::Runtime::new().unwrap(); + + let mut tree = BlockChainTree::new().unwrap(); + + let main_chain = tree.get_main_chain(); + + let wallet_private: [u8; 32] = [ + 25, 53, 50, 224, 180, 250, 177, 186, 87, 47, 28, 80, 183, 208, 219, 119, 101, 60, 173, 157, + 190, 29, 208, 231, 98, 69, 82, 211, 107, 185, 192, 224, + ]; + let wallet = [ + 2, 178, 140, 81, 31, 206, 208, 171, 143, 240, 128, 134, 115, 82, 188, 63, 146, 189, 14, 59, + 85, 8, 11, 28, 137, 161, 145, 216, 251, 95, 93, 137, 159, + ]; + let receiver = static_values::ROOT_PUBLIC_ADDRESS; + + println!("Sender amount: {}", tree.get_amount(&wallet).unwrap()); + println!("Sender gas amount: {}", tree.get_gas(&wallet).unwrap()); + println!("Receiver amount: {}", tree.get_amount(&receiver).unwrap()); + + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + let transaction = transaction::Transaction::new( + wallet.clone(), + receiver, + timestamp, + U256::from_str_radix("228", 10).unwrap(), + wallet_private, + None, + ); + let transaction_hash = transaction.hash(); + tree.send_transaction(&transaction).unwrap(); + + println!("Transaction created: {:?}", &transaction_hash); + println!("Sender amount: {}", tree.get_amount(&wallet).unwrap()); + println!("Sender gas amount: {}", tree.get_gas(&wallet).unwrap()); + println!("Receiver amount: {}", tree.get_amount(&receiver).unwrap()); + + // MINING + let mut nonce = U256::zero(); + let last_block = main_chain.get_last_block().unwrap().unwrap(); + let prev_hash = last_block.hash().unwrap(); + let difficulty = last_block.get_info().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(); + + let transactions: Vec<[u8; 32]> = + if ((last_block.get_info().height + 1) % BLOCKS_PER_EPOCH).is_zero() { + Vec::with_capacity(0) + } else { + vec![transaction_hash] + }; + + let block = rt + .block_on(tree.emmit_new_main_block(&pow, &wallet, &transactions, timestamp)) + .unwrap(); + + // Node should handle this + tree.send_amount( + &static_values::ROOT_PUBLIC_ADDRESS, + &wallet, + *static_values::MAIN_CHAIN_PAYMENT, + ) + .unwrap(); + + let fee = tools::recalculate_fee(&last_block.get_info().difficulty); + for _ in transactions { + tree.add_amount(&wallet, fee).unwrap(); + } + + println!("Added new block! {:?}\n", block.hash().unwrap()); + + rt.block_on(tree.flush()).unwrap(); + break; + } + nonce += U256::one(); + } +} diff --git a/src/blockchaintree.rs b/src/blockchaintree.rs index 1cfbb33..6208846 100644 --- a/src/blockchaintree.rs +++ b/src/blockchaintree.rs @@ -6,11 +6,13 @@ use crate::{ errors::{BCTreeErrorKind, BlockChainTreeError, ChainErrorKind}, merkletree, static_values::{ - self, AMMOUNT_SUMMARY, BLOCKS_PER_EPOCH, COINS_PER_CYCLE, GAS_SUMMARY, - OLD_AMMOUNT_SUMMARY, OLD_GAS_SUMMARY, ROOT_PUBLIC_ADDRESS, + self, AMMOUNT_SUMMARY, BLOCKS_PER_EPOCH, BYTE_GAS_PRICE, COINS_PER_CYCLE, GAS_SUMMARY, + MAIN_CHAIN_PAYMENT, OLD_AMMOUNT_SUMMARY, OLD_GAS_SUMMARY, ROOT_PUBLIC_ADDRESS, }, tools, transaction::Transaction, + transaction::Transactionable, + txpool, types::Hash, }; use error_stack::{Report, ResultExt}; @@ -154,7 +156,7 @@ impl BlockChainTree { if prev_amount < amount { return Err(sled::transaction::ConflictableTransactionError::Abort(())); } - let new_amount = prev_amount + amount; + let new_amount = prev_amount - amount; let mut buf: Vec = Vec::with_capacity(tools::u256_size(&new_amount)); tools::dump_u256(&new_amount, &mut buf).unwrap(); db.insert(owner, buf)?; @@ -248,7 +250,7 @@ impl BlockChainTree { if prev_amount < amount { return Err(sled::transaction::ConflictableTransactionError::Abort(())); } - let new_amount = prev_amount + amount; + let new_amount = prev_amount - amount; let mut buf: Vec = Vec::with_capacity(tools::u256_size(&new_amount)); tools::dump_u256(&new_amount, &mut buf).unwrap(); db.insert(owner, buf)?; @@ -470,6 +472,38 @@ impl BlockChainTree { Ok(new_block) } + pub fn send_transaction( + &self, + transaction: &dyn Transactionable, + ) -> Result<(), Report> { + let sender_gas_amount = self.get_gas(transaction.get_sender())?; + let sender_amount = self.get_amount(transaction.get_sender())?; + let amount_of_bytes = transaction.get_dump_size(); + let gas_required = *BYTE_GAS_PRICE * amount_of_bytes; + if sender_gas_amount < gas_required { + return Err(BlockChainTreeError::BlockChainTree( + BCTreeErrorKind::NewTransaction, + )) + .attach_printable("not enough gas for the transaction"); + } + let last_block = self.main_chain.get_last_block()?.unwrap(); // practically cannot fail + let fee = tools::recalculate_fee(&last_block.get_info().difficulty); + if sender_amount < fee + transaction.get_amount().unwrap_or(U256::zero()) { + return Err(BlockChainTreeError::BlockChainTree( + BCTreeErrorKind::NewTransaction, + )) + .attach_printable("not enough coins to pay the fee"); + } + self.main_chain.add_transaction(transaction)?; + if let Some(amount) = transaction.get_amount() { + // TODO: make into sled transaction + self.send_amount(transaction.get_sender(), transaction.get_receiver(), amount)?; + self.sub_amount(transaction.get_sender(), fee)?; + self.sub_gas(transaction.get_sender(), gas_required)?; + } + Ok(()) + } + pub async fn flush(&self) -> Result<(), Report> { self.main_chain.flush().await?; self.summary_db diff --git a/src/chain.rs b/src/chain.rs index 0e2d5cd..5f2f880 100644 --- a/src/chain.rs +++ b/src/chain.rs @@ -218,6 +218,24 @@ impl MainChain { Ok(()) } + pub fn add_transaction( + &self, + transaction: &dyn 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")?; + Ok(()) + } + pub fn transaction_exists( &self, transaction_hash: &[u8; 32], diff --git a/src/static_values.rs b/src/static_values.rs index 3dd2934..61470e3 100644 --- a/src/static_values.rs +++ b/src/static_values.rs @@ -40,14 +40,15 @@ pub static ROOT_PUBLIC_ADDRESS: [u8; 33] = [ pub static INCEPTION_TIMESTAMP: u64 = 1597924800; -pub static BLOCKS_PER_EPOCH: usize = 4; +pub static BLOCKS_PER_EPOCH: usize = 1000000; -pub static TIME_PER_BLOCK: u64 = 4; +pub static TIME_PER_BLOCK: u64 = 600; lazy_static! { pub static ref COIN_FRACTIONS: U256 = U256::from_dec_str("1000000000000000000").unwrap(); pub static ref INITIAL_FEE: U256 = U256::from_dec_str("25000000000000000").unwrap(); // 100_000_000//4 - pub static ref FEE_STEP: U256 = U256::from_dec_str("625000000000").unwrap(); // 100_000_000//255 + pub static ref FEE_STEP: U256 = U256::from_dec_str("62500").unwrap(); // 100_000_000//255 pub static ref MAIN_CHAIN_PAYMENT: U256 = *INITIAL_FEE; pub static ref COINS_PER_CYCLE: U256 = (*MAIN_CHAIN_PAYMENT*2000usize*BLOCKS_PER_EPOCH) + *COIN_FRACTIONS*10000usize; + pub static ref BYTE_GAS_PRICE: U256 = U256::from_dec_str("625000000000").unwrap(); } diff --git a/src/transaction.rs b/src/transaction.rs index aeeb25b..76ad09a 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -80,7 +80,7 @@ pub struct Transaction { timestamp: u64, signature: [u8; 64], amount: U256, - gas_amount: U256, + //gas_amount: U256, data: Option>, hash: [u8; 32], } @@ -91,19 +91,14 @@ impl Transaction { receiver: &[u8; 33], timestamp: u64, amount: &U256, - gas_amount: &U256, + //gas_amount: &U256, data: Option<&[u8]>, private_key: &[u8; 32], ) -> [u8; 64] { let mut hasher = Sha256::new(); - let calculated_size: usize = 1 - + 33 - + 33 - + 8 - + tools::u256_size(amount) - + tools::u256_size(gas_amount) - + data.map_or(0, |data| data.len()); + let calculated_size: usize = + 1 + 33 + 33 + 8 + tools::u256_size(amount) + data.map_or(0, |data| data.len()); let mut concatenated_input: Vec = Vec::with_capacity(calculated_size); concatenated_input.push(Headers::Transaction as u8); @@ -120,10 +115,6 @@ impl Transaction { .attach_printable("Error to dump amount") .change_context(TransactionError::Tx(TxErrorKind::Dump)) .unwrap(); - tools::dump_u256(gas_amount, &mut concatenated_input) - .attach_printable("Error to dump gas amount") - .change_context(TransactionError::Tx(TxErrorKind::Dump)) - .unwrap(); if let Some(data) = data { concatenated_input.extend(data.iter()); } @@ -146,7 +137,6 @@ impl Transaction { receiver: [u8; 33], timestamp: u64, amount: U256, - gas_amount: U256, private_key: [u8; 32], data: Option>, ) -> Transaction { @@ -155,7 +145,6 @@ impl Transaction { &receiver, timestamp, &amount, - &gas_amount, data.as_deref(), &private_key, ); @@ -165,7 +154,6 @@ impl Transaction { timestamp, signature, amount, - gas_amount, data, hash: [0; 32], }; @@ -180,7 +168,6 @@ impl Transaction { receiver: [u8; 33], timestamp: u64, amount: U256, - gas_amount: U256, data: Option>, signature: [u8; 64], ) -> Transaction { @@ -190,7 +177,6 @@ impl Transaction { timestamp, signature, amount, - gas_amount, data, hash: [0; 32], }; @@ -213,7 +199,6 @@ impl Transactionable for Transaction { + 33 + 8 + tools::u256_size(&self.amount) - + tools::u256_size(&self.gas_amount) + self.data.as_ref().map_or(0, |data| data.len()); let mut concatenated_input: Vec = Vec::with_capacity(calculated_size); @@ -232,11 +217,6 @@ impl Transactionable for Transaction { .change_context(TransactionError::Tx(TxErrorKind::Dump)) .unwrap(); - tools::dump_u256(&self.gas_amount, &mut concatenated_input) - .attach_printable("Error to dump gas amount") - .change_context(TransactionError::Tx(TxErrorKind::Dump)) - .unwrap(); - if let Some(data) = self.data.as_ref() { concatenated_input.extend(data.iter()); } @@ -302,10 +282,6 @@ impl Transactionable for Transaction { tools::dump_u256(&self.amount, &mut transaction_dump) .change_context(TransactionError::Tx(TxErrorKind::Dump))?; - // gas amount - tools::dump_u256(&self.gas_amount, &mut transaction_dump) - .change_context(TransactionError::Tx(TxErrorKind::Dump))?; - // data if let Some(data) = self.data.as_ref() { transaction_dump.extend(data.iter()); @@ -320,7 +296,6 @@ impl Transactionable for Transaction { + 8 + 64 + tools::u256_size(&self.amount) - + tools::u256_size(&self.gas_amount) + self.data.as_ref().map_or(0, |data| data.len()) } @@ -355,13 +330,6 @@ impl Transactionable for Transaction { index += idx + 1; - // parsing amount - let (gas_amount, idx) = tools::load_u256(&data[index..]) - .attach_printable("Couldn't parse gas amount") - .change_context(TransactionError::Tx(TxErrorKind::Parse))?; - - index += idx + 1; - let tx_data = if index == data.len() { None } else { @@ -377,7 +345,7 @@ impl Transactionable for Transaction { } Ok(Transaction::new_signed( - sender, receiver, timestamp, amount, gas_amount, tx_data, signature, + sender, receiver, timestamp, amount, tx_data, signature, )) } diff --git a/tests/transaction_test.rs b/tests/transaction_test.rs index 1daf34e..6c1cd70 100644 --- a/tests/transaction_test.rs +++ b/tests/transaction_test.rs @@ -9,7 +9,6 @@ fn dump_parse_transaction() { [20; 33], 100, U256::from_dec_str("3627836287").unwrap(), - U256::from_dec_str("3627836287").unwrap(), None, [33; 64], ); @@ -44,7 +43,6 @@ fn hash_transaction() { [20; 33], 100, U256::from_dec_str("3627836287").unwrap(), - U256::from_dec_str("3627836287").unwrap(), None, [33; 64], ); @@ -64,7 +62,6 @@ fn sign_verify_transaction() { public_key.serialize(), 100, U256::from_dec_str("3627836287").unwrap(), - U256::from_dec_str("3627836287").unwrap(), secret_key.secret_bytes(), Some(vec![1, 3, 3, 3, 3, 3, 3]), );