From 4ea539b0626fac66e56a54194cd4f98d8579e891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 11:01:47 +0300 Subject: [PATCH 01/13] tests: Add generate_to_address integration test. --- tests/address.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tests/address.rs b/tests/address.rs index ab175d2..cda63e0 100644 --- a/tests/address.rs +++ b/tests/address.rs @@ -4,11 +4,19 @@ use bitcoin_mock_rpc::{Client, RpcApiWrapper}; use bitcoincore_rpc::{Auth, RpcApi}; use std::thread; +#[test] +fn generate_to_address() { + let rpc = Client::new("", Auth::None).unwrap(); + let address = rpc.get_new_address(None, None).unwrap().assume_checked(); + + let initial_balance = rpc.get_balance(None, None).unwrap(); + + rpc.generate_to_address(101, &address).unwrap(); + assert!(rpc.get_balance(None, None).unwrap() > initial_balance); +} + #[test] fn generate_to_address_multi_threaded() { - // Bacause `thread::spawn` moves value to closure, cloning a new is needed. This is good, - // because cloning an rpc struct should have a persistent ledger even though there are more than - // one accessors. let rpc = Client::new("", Auth::None).unwrap(); let cloned_rpc = rpc.clone(); let address = rpc.get_new_address(None, None).unwrap().assume_checked(); From 38fbf25cf2c460b04da9626277e7881d662cbc8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 11:29:36 +0300 Subject: [PATCH 02/13] rpc_api: Update function comments. --- src/client/rpc_api.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/client/rpc_api.rs b/src/client/rpc_api.rs index fb37eea..9117cfe 100644 --- a/src/client/rpc_api.rs +++ b/src/client/rpc_api.rs @@ -18,14 +18,18 @@ use bitcoincore_rpc::{ }; impl RpcApi for Client { + /// TL;DR: If this function is called for `cmd`, it's corresponding mock is + /// not yet implemented. Please consider implementing it. Ellerinden oper + /// diyorum anlamadiysan. + /// /// This function normally talks with Bitcoin network. Therefore, other /// functions calls this to send requests. In a mock environment though, /// other functions won't be talking to a regulated interface. Rather will - /// talk with a temporary interfaces, like in-memory databases. + /// access a temporary in-memory database. /// /// This is the reason, this function will only throw errors in case of a - /// function is not -yet- implemented. Tester should implement corresponding - /// function in this impl block, if this function called for `cmd`. + /// function calls this. Tester should implement corresponding function in + /// this impl block. fn call serde::de::Deserialize<'a>>( &self, cmd: &str, @@ -48,6 +52,8 @@ impl RpcApi for Client { Ok(tx.compute_txid()) } + /// Because there are no blocks, this function works pretty much same as + /// `get_transaction`. fn get_raw_transaction( &self, txid: &bitcoin::Txid, @@ -151,6 +157,8 @@ impl RpcApi for Client { Ok(self.ledger.add_transaction_unconditionally(tx)?) } + /// Creates a random secret/public key pair and generates a Bitcoin address + /// from witness program. fn get_new_address( &self, _label: Option<&str>, @@ -162,7 +170,8 @@ impl RpcApi for Client { Ok(credential.address.as_unchecked().to_owned()) } - /// Generates `block_num` amount of block rewards to user. + /// Generates `block_num` amount of block rewards to user. Block reward is + /// fixed to 1 BTC, regardless of which and how many blocks are generated. fn generate_to_address( &self, block_num: u64, @@ -180,6 +189,8 @@ impl RpcApi for Client { Ok(vec![BlockHash::all_zeros(); block_num as usize]) } + /// Returns user's balance. Balance is calculated using addresses that are + /// generated with `get_new_address` rpc call. fn get_balance( &self, _minconf: Option, From f1354287f2ee3023f3c54b13728dad3298a9d3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 11:46:02 +0300 Subject: [PATCH 03/13] tests: common: Allow unused for common functions. --- tests/common/mod.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 04be080..a2b452c 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -9,10 +9,12 @@ use bitcoin_mock_rpc::Client; use bitcoincore_rpc::RpcApi; use std::str::FromStr; +#[allow(unused)] pub async fn send_raw_transaction_async(rpc: Client, tx: Transaction) { rpc.send_raw_transaction(&tx).unwrap(); } +#[allow(unused)] pub fn create_witness() -> (WitnessProgram, Witness) { let secp = bitcoin::secp256k1::Secp256k1::new(); let internal_key = UntweakedPublicKey::from( @@ -45,12 +47,14 @@ pub fn create_witness() -> (WitnessProgram, Witness) { (witness_program, witness) } +#[allow(unused)] pub fn create_address_from_witness() -> Address { let witness_program = create_witness().0; Address::from_witness_program(witness_program, bitcoin::Network::Regtest) } +#[allow(unused)] pub fn create_txin(txid: Txid, vout: u32) -> TxIn { TxIn { previous_output: OutPoint { txid, vout }, @@ -58,6 +62,7 @@ pub fn create_txin(txid: Txid, vout: u32) -> TxIn { } } +#[allow(unused)] pub fn create_txout(value: Amount, script_pubkey: ScriptBuf) -> TxOut { TxOut { value, @@ -65,6 +70,7 @@ pub fn create_txout(value: Amount, script_pubkey: ScriptBuf) -> TxOut { } } +#[allow(unused)] pub fn create_transaction(tx_ins: Vec, tx_outs: Vec) -> Transaction { bitcoin::Transaction { version: bitcoin::transaction::Version(2), From d32ab52b914e6b99a61a5b2aa93b58da48d9a1ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 12:06:53 +0300 Subject: [PATCH 04/13] tests: Initial raw_transaction integration tests. --- tests/raw_transactions.rs | 205 ++++++++++++++++++++++++++++++++++++++ tests/transaction.rs | 102 +------------------ 2 files changed, 206 insertions(+), 101 deletions(-) create mode 100644 tests/raw_transactions.rs diff --git a/tests/raw_transactions.rs b/tests/raw_transactions.rs new file mode 100644 index 0000000..91bd20d --- /dev/null +++ b/tests/raw_transactions.rs @@ -0,0 +1,205 @@ +//! Integration tests for `raw_transaction` calls. + +use bitcoin::{hashes::Hash, Amount, OutPoint, TxIn, TxOut, Txid}; +use bitcoin_mock_rpc::{Client, RpcApiWrapper}; +use bitcoincore_rpc::{Auth, RpcApi}; +use common::send_raw_transaction_async; +use tokio::join; + +mod common; + +#[test] +fn send_get_raw_transaction_without_change() { + let rpc = Client::new("", Auth::None).unwrap(); + + let address = rpc.get_new_address(None, None).unwrap().assume_checked(); + let deposit_address = common::create_address_from_witness(); + + // Generate funds to user. + let txid = rpc + .send_to_address( + &address, + Amount::from_sat(0x45 * 0x45), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + assert_eq!( + rpc.get_balance(None, None).unwrap(), + Amount::from_sat(0x45 * 0x45) + ); + + let txin = common::create_txin(txid, 0); + let txout = common::create_txout(Amount::from_sat(0x45), deposit_address.script_pubkey()); + let tx = common::create_transaction(vec![txin], vec![txout]); + let txid = rpc.send_raw_transaction(&tx).unwrap(); + + // Because we burned UTXO, we should not have any money. + assert_eq!(rpc.get_balance(None, None).unwrap(), Amount::from_sat(0)); + + assert_eq!(rpc.get_raw_transaction(&txid, None).unwrap(), tx); +} + +#[tokio::test] +async fn send_get_raw_transaction_async() { + let rpc = Client::new("", Auth::None).unwrap(); + + let address = rpc.get_new_address(None, None).unwrap().assume_checked(); + let deposit_address = common::create_address_from_witness(); + + // Create some funds to user. + let txid1 = rpc + .send_to_address( + &address, + Amount::from_sat(0x45 * 0x45), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + let txid2 = rpc + .send_to_address( + &address, + Amount::from_sat(0x45 * 0x45), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + assert_eq!( + rpc.get_balance(None, None).unwrap(), + Amount::from_sat(0x45 * 0x45 * 2) + ); + + let txin1 = TxIn { + previous_output: OutPoint { + txid: txid1, + vout: 0, + }, + ..Default::default() + }; + let txout = TxOut { + value: Amount::from_sat(0x45), + script_pubkey: address.script_pubkey(), + }; + let tx1 = common::create_transaction(vec![txin1.clone()], vec![txout]); + + let txin2 = TxIn { + previous_output: OutPoint { + txid: txid2, + vout: 0, + }, + ..Default::default() + }; + let txout = TxOut { + value: Amount::from_sat(0x1F), + script_pubkey: address.script_pubkey(), + }; + let tx2 = common::create_transaction(vec![txin2.clone()], vec![txout]); + + let async_thr1 = send_raw_transaction_async(rpc.clone(), tx1.clone()); + let async_thr2 = send_raw_transaction_async(rpc.clone(), tx2.clone()); + + join!(async_thr1, async_thr2); + + // We burned our money. We should only have the amount we send it to ourselves. + assert_eq!( + rpc.get_balance(None, None).unwrap(), + Amount::from_sat(0x1F + 0x45) + ); + + // Send some funds to some other user. + let txin = common::create_txin(tx1.compute_txid(), 0); + let txout = TxOut { + value: Amount::from_sat(0x45), + script_pubkey: deposit_address.script_pubkey(), + }; + let tx1 = common::create_transaction(vec![txin], vec![txout]); + + let txin = common::create_txin(tx2.compute_txid(), 0); + let txout = TxOut { + value: Amount::from_sat(0x1F), + script_pubkey: deposit_address.script_pubkey(), + }; + let tx2 = common::create_transaction(vec![txin], vec![txout]); + + let async_thr1 = send_raw_transaction_async(rpc.clone(), tx1); + let async_thr2 = send_raw_transaction_async(rpc.clone(), tx2); + + join!(async_thr1, async_thr2); + + // Balance should be lower now. + assert_eq!(rpc.get_balance(None, None).unwrap(), Amount::from_sat(0)); +} + +#[test] +#[should_panic] +fn send_raw_transaction_invalid_input() { + let rpc = Client::new("", Auth::None).unwrap(); + + let address = rpc.get_new_address(None, None).unwrap().assume_checked(); + + assert_eq!(rpc.get_balance(None, None).unwrap(), Amount::from_sat(0)); + + let txin = common::create_txin(Txid::all_zeros(), 0); + let txout = common::create_txout(Amount::from_sat(0x45), address.script_pubkey()); + let tx = common::create_transaction(vec![txin], vec![txout]); + + // Input is not valid, this should panic. + rpc.send_raw_transaction(&tx).unwrap(); + + // This should also panic. User don't have any funds. + assert_eq!(rpc.get_balance(None, None).unwrap(), Amount::from_sat(0x45)); +} + +#[test] +#[should_panic] +fn send_raw_transaction_unsufficient_funds() { + let rpc = Client::new("", Auth::None).unwrap(); + + let address = rpc.get_new_address(None, None).unwrap().assume_checked(); + + // Generate funds to user. + let txid = rpc + .send_to_address( + &address, + Amount::from_sat(0x45 * 0x45), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + assert_eq!( + rpc.get_balance(None, None).unwrap(), + Amount::from_sat(0x45 * 0x45) + ); + + let txin = common::create_txin(txid, 0); + let txout = common::create_txout( + Amount::from_sat(0x45 * 0x45 * 0x1F), + address.script_pubkey(), + ); + let tx = common::create_transaction(vec![txin], vec![txout]); + + // Input is not enough for output, this should panic. + rpc.send_raw_transaction(&tx).unwrap(); + + // This should also panic. User don't have that funds. + assert_eq!( + rpc.get_balance(None, None).unwrap(), + Amount::from_sat(0x45 * 0x45 * 0x1F) + ); +} diff --git a/tests/transaction.rs b/tests/transaction.rs index 4a759a3..6888074 100644 --- a/tests/transaction.rs +++ b/tests/transaction.rs @@ -1,11 +1,9 @@ //! Transaction related integration tests. -use bitcoin::{Amount, OutPoint, TxIn, TxOut}; +use bitcoin::{Amount, OutPoint, TxIn}; use bitcoin_mock_rpc::{Client, RpcApiWrapper}; use bitcoincore_rpc::{Auth, RpcApi}; -use common::send_raw_transaction_async; use std::thread; -use tokio::join; mod common; @@ -86,101 +84,3 @@ fn use_utxo_from_send_to_address() { assert!(false); }; } - -#[tokio::test] -async fn send_get_raw_transaction_async() { - let rpc = Client::new("", Auth::None).unwrap(); - - let address = rpc.get_new_address(None, None).unwrap().assume_checked(); - let deposit_address = common::create_address_from_witness(); - - // Create some funds to user. - let txid1 = rpc - .send_to_address( - &address, - Amount::from_sat(0x45 * 0x45), - None, - None, - None, - None, - None, - None, - ) - .unwrap(); - let txid2 = rpc - .send_to_address( - &address, - Amount::from_sat(0x45 * 0x45), - None, - None, - None, - None, - None, - None, - ) - .unwrap(); - assert_eq!( - rpc.get_balance(None, None).unwrap(), - Amount::from_sat(0x45 * 0x45 * 2) - ); - - let txin1 = TxIn { - previous_output: OutPoint { - txid: txid1, - vout: 0, - }, - ..Default::default() - }; - let txout = TxOut { - value: Amount::from_sat(0x45), - script_pubkey: address.script_pubkey(), - }; - let tx1 = common::create_transaction(vec![txin1.clone()], vec![txout]); - - let txin2 = TxIn { - previous_output: OutPoint { - txid: txid2, - vout: 0, - }, - ..Default::default() - }; - let txout = TxOut { - value: Amount::from_sat(0x1F), - script_pubkey: address.script_pubkey(), - }; - let tx2 = common::create_transaction(vec![txin2.clone()], vec![txout]); - - let async_thr1 = send_raw_transaction_async(rpc.clone(), tx1.clone()); - let async_thr2 = send_raw_transaction_async(rpc.clone(), tx2.clone()); - - join!(async_thr1, async_thr2); - - // We burned our money. We should only have the amount we send it to ourselves. - assert_eq!( - rpc.get_balance(None, None).unwrap(), - Amount::from_sat(0x1F + 0x45) - ); - - // Send some funds to some other user. - let txin = common::create_txin(tx1.compute_txid(), 0); - let txout = TxOut { - value: Amount::from_sat(0x45), - script_pubkey: deposit_address.script_pubkey(), - }; - let tx1 = common::create_transaction(vec![txin], vec![txout]); - - let txin = common::create_txin(tx2.compute_txid(), 0); - let txout = TxOut { - value: Amount::from_sat(0x1F), - script_pubkey: deposit_address.script_pubkey(), - }; - let tx2 = common::create_transaction(vec![txin], vec![txout]); - - let async_thr1 = send_raw_transaction_async(rpc.clone(), tx1); - let async_thr2 = send_raw_transaction_async(rpc.clone(), tx2); - - join!(async_thr1, async_thr2); - - // Balance should be lower now. - assert_eq!(rpc.get_balance(None, None).unwrap(), Amount::from_sat(0)); -} From 3798a51be04c07daf4b2a1b59cc63d0431dacd8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 12:35:13 +0300 Subject: [PATCH 05/13] ledger: transactions: Fix wrong vout while creating new UTXO's. --- src/ledger/transactions.rs | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/ledger/transactions.rs b/src/ledger/transactions.rs index 35521bb..a5a7f0f 100644 --- a/src/ledger/transactions.rs +++ b/src/ledger/transactions.rs @@ -22,7 +22,11 @@ impl Ledger { // Remove UTXO's that are being used used in this transaction. transaction.input.iter().for_each(|input| { if let Ok(tx) = self.get_transaction(input.previous_output.txid) { - let utxo = tx.output.get(0).unwrap().to_owned(); + let utxo = tx + .output + .get(input.previous_output.vout as usize) + .unwrap() + .to_owned(); let script_pubkey = utxo.script_pubkey; for i in 0..credentials.len() { if credentials[i].address.script_pubkey() == script_pubkey { @@ -35,19 +39,23 @@ impl Ledger { }); // Add new UTXO's to address. - transaction.output.iter().for_each(|output| { - for i in 0..credentials.len() { - if credentials[i].address.script_pubkey() == output.script_pubkey { - let utxo = OutPoint { - txid, - vout: i as u32, - }; - self.add_utxo(credentials[i].address.clone(), utxo); - - break; + transaction + .output + .iter() + .enumerate() + .for_each(|(vout, output)| { + for i in 0..credentials.len() { + if credentials[i].address.script_pubkey() == output.script_pubkey { + let utxo = OutPoint { + txid, + vout: vout as u32, + }; + self.add_utxo(credentials[i].address.clone(), utxo); + + break; + } } - } - }); + }); // Add transaction to blockchain. add_item_to_vec!(self.transactions, transaction.clone()); From 98f0e8396ec489791412b3ed72651ee2b02bc0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 12:35:29 +0300 Subject: [PATCH 06/13] tests: raw_transactions: Add send_get_raw_transaction_with_change. --- tests/raw_transactions.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/tests/raw_transactions.rs b/tests/raw_transactions.rs index 91bd20d..20fc849 100644 --- a/tests/raw_transactions.rs +++ b/tests/raw_transactions.rs @@ -8,6 +8,45 @@ use tokio::join; mod common; +#[test] +fn send_get_raw_transaction_with_change() { + let rpc = Client::new("", Auth::None).unwrap(); + + let address = rpc.get_new_address(None, None).unwrap().assume_checked(); + let deposit_address = common::create_address_from_witness(); + + // Generate funds to user. + let txid = rpc + .send_to_address( + &address, + Amount::from_sat(0x45 * 0x45), + None, + None, + None, + None, + None, + None, + ) + .unwrap(); + assert_eq!( + rpc.get_balance(None, None).unwrap(), + Amount::from_sat(0x45 * 0x45) + ); + + let txin = common::create_txin(txid, 0); + let txout0 = common::create_txout(Amount::from_sat(0x45), deposit_address.script_pubkey()); + let txout1 = common::create_txout(Amount::from_sat(0x45 * 0x44), address.script_pubkey()); + let tx = common::create_transaction(vec![txin], vec![txout0, txout1]); + let txid = rpc.send_raw_transaction(&tx).unwrap(); + + assert_eq!( + rpc.get_balance(None, None).unwrap(), + Amount::from_sat(0x45 * 0x44) + ); + + assert_eq!(rpc.get_raw_transaction(&txid, None).unwrap(), tx); +} + #[test] fn send_get_raw_transaction_without_change() { let rpc = Client::new("", Auth::None).unwrap(); From 7edaff11cdc60e6267727486f1b8b9038a23541a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 12:43:40 +0300 Subject: [PATCH 07/13] ledger: transactions: Add input UTXO check for calculate_transaction_input_value. --- src/ledger/transactions.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ledger/transactions.rs b/src/ledger/transactions.rs index a5a7f0f..58363e9 100644 --- a/src/ledger/transactions.rs +++ b/src/ledger/transactions.rs @@ -128,8 +128,18 @@ impl Ledger { transaction: Transaction, ) -> Result { let mut amount = Amount::from_sat(0); + let utxos = self.get_user_utxos(); for input in transaction.input { + // Check if input is in UTXO list. + if utxos.iter().all(|utxo| *utxo != input.previous_output) { + return Err(LedgerError::Transaction(format!( + "Input {:?} not found in UTXO list", + input.previous_output + ))); + } + + // Add valid input amount to total. amount += self .get_transaction(input.previous_output.txid)? .output From 8b1cfca2d0e3adaa82fa5362e87b91af130f098a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 12:50:40 +0300 Subject: [PATCH 08/13] tests: Typo fix in function name. --- tests/raw_transactions.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/raw_transactions.rs b/tests/raw_transactions.rs index 20fc849..2a185c5 100644 --- a/tests/raw_transactions.rs +++ b/tests/raw_transactions.rs @@ -203,7 +203,7 @@ fn send_raw_transaction_invalid_input() { #[test] #[should_panic] -fn send_raw_transaction_unsufficient_funds() { +fn send_raw_transaction_insufficient_funds() { let rpc = Client::new("", Auth::None).unwrap(); let address = rpc.get_new_address(None, None).unwrap().assume_checked(); From dc1ff55b8721f5f8b6c9addea1381c19b6d569eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 14:19:06 +0300 Subject: [PATCH 09/13] client: Add dump_ledger. --- src/client/mod.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/client/mod.rs b/src/client/mod.rs index c5ac870..3047e67 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -3,6 +3,7 @@ //! Client crate mocks the `Client` struct in `bitcoincore-rpc`. use crate::ledger::Ledger; +use bitcoin::Txid; use bitcoincore_rpc::{Auth, RpcApi}; mod rpc_api; @@ -43,6 +44,35 @@ impl RpcApiWrapper for Client { } } +/// Dumps complete ledger to a string and returns it. This can help identify +/// bugs as it draws a picture of the mock blockchain. +pub fn dump_ledger(rpc: Client) -> String { + let mut dump = String::new(); + + const DELIMETER: &str = "\n-----\n"; + + let utxos = rpc.ledger.get_user_utxos(); + let transactions = rpc.ledger.get_transactions(); + let credentials = rpc.ledger.get_credentials(); + + dump += format!("UTXO's: {:?}", utxos).as_str(); + dump += DELIMETER; + dump += format!("Transaction's: {:?}", transactions).as_str(); + dump += DELIMETER; + dump += format!( + "Txid's: {:?}", + transactions + .iter() + .map(|tx| tx.compute_txid()) + .collect::>() + ) + .as_str(); + dump += DELIMETER; + dump += format!("Credentials's: {:?}", credentials).as_str(); + + dump +} + #[cfg(test)] mod tests { use super::*; From c3bfaf567b8a7f2357cb604777c51e3de94afda4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 14:19:56 +0300 Subject: [PATCH 10/13] ledger: Remove _ prefix for get_transactions. --- src/client/rpc_api.rs | 6 +----- src/ledger/transactions.rs | 10 +++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/client/rpc_api.rs b/src/client/rpc_api.rs index 9117cfe..1c7ed71 100644 --- a/src/client/rpc_api.rs +++ b/src/client/rpc_api.rs @@ -366,11 +366,7 @@ mod tests { // Wallet has funds now. It should not be rejected. let txin = rpc.ledger._create_txin( - rpc.ledger - ._get_transactions() - .get(0) - .unwrap() - .compute_txid(), + rpc.ledger.get_transactions().get(0).unwrap().compute_txid(), 0, ); let txout = rpc diff --git a/src/ledger/transactions.rs b/src/ledger/transactions.rs index 58363e9..5cee96b 100644 --- a/src/ledger/transactions.rs +++ b/src/ledger/transactions.rs @@ -78,7 +78,7 @@ impl Ledger { Ok(tx) } /// Returns user's list of transactions. - pub fn _get_transactions(&self) -> Vec { + pub fn get_transactions(&self) -> Vec { return_vec_item!(self.transactions); } @@ -200,7 +200,7 @@ mod tests { fn transactions_without_checks() { let ledger = Ledger::new(); - assert_eq!(ledger._get_transactions().len(), 0); + assert_eq!(ledger.get_transactions().len(), 0); let txout = ledger.create_txout(Amount::from_sat(0x45), ScriptBuf::new()); let tx = ledger.create_transaction(vec![], vec![txout]); @@ -211,7 +211,7 @@ mod tests { ledger.add_transaction_unconditionally(tx.clone()).unwrap() ); - let txs = ledger._get_transactions(); + let txs = ledger.get_transactions(); assert_eq!(txs.len(), 1); let tx2 = txs.get(0).unwrap().to_owned(); @@ -229,7 +229,7 @@ mod tests { let credential = Ledger::generate_credential_from_witness(); ledger.add_credential(credential.clone()); - assert_eq!(ledger._get_transactions().len(), 0); + assert_eq!(ledger.get_transactions().len(), 0); // First, add some funds to user, for free. let txout = ledger.create_txout( @@ -259,7 +259,7 @@ mod tests { let txid = tx.compute_txid(); assert_eq!(txid, ledger.add_transaction(tx.clone()).unwrap()); - let txs = ledger._get_transactions(); + let txs = ledger.get_transactions(); assert_eq!(txs.len(), 2); let read_tx = txs.get(1).unwrap().to_owned(); From 5e68205569da150886cfb16320ca2025bf477663 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 15:03:16 +0300 Subject: [PATCH 11/13] client: Add inner ledger dump function. --- src/client/mod.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 3047e67..71f3247 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -45,15 +45,20 @@ impl RpcApiWrapper for Client { } /// Dumps complete ledger to a string and returns it. This can help identify -/// bugs as it draws a picture of the mock blockchain. +/// bugs as it draws the big picture of the mock blockchain. pub fn dump_ledger(rpc: Client) -> String { + dump_ledger_inner(rpc.ledger) +} +/// Parent of `dump_ledger`. This function accepts private `Ledger` struct. This +/// useful for only crate tests. +pub fn dump_ledger_inner(ledger: Ledger) -> String { let mut dump = String::new(); const DELIMETER: &str = "\n-----\n"; - let utxos = rpc.ledger.get_user_utxos(); - let transactions = rpc.ledger.get_transactions(); - let credentials = rpc.ledger.get_credentials(); + let utxos = ledger.get_user_utxos(); + let transactions = ledger.get_transactions(); + let credentials = ledger.get_credentials(); dump += format!("UTXO's: {:?}", utxos).as_str(); dump += DELIMETER; From 47c6f20ccee125c03e97533e81c29b0f5348bea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 15:09:25 +0300 Subject: [PATCH 12/13] client: Add pretty option to dump. --- src/client/mod.rs | 51 +++++++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/src/client/mod.rs b/src/client/mod.rs index 71f3247..8fb97fc 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -46,12 +46,12 @@ impl RpcApiWrapper for Client { /// Dumps complete ledger to a string and returns it. This can help identify /// bugs as it draws the big picture of the mock blockchain. -pub fn dump_ledger(rpc: Client) -> String { - dump_ledger_inner(rpc.ledger) +pub fn dump_ledger(rpc: Client, pretty: bool) -> String { + dump_ledger_inner(rpc.ledger, pretty) } /// Parent of `dump_ledger`. This function accepts private `Ledger` struct. This /// useful for only crate tests. -pub fn dump_ledger_inner(ledger: Ledger) -> String { +pub fn dump_ledger_inner(ledger: Ledger, pretty: bool) -> String { let mut dump = String::new(); const DELIMETER: &str = "\n-----\n"; @@ -60,20 +60,37 @@ pub fn dump_ledger_inner(ledger: Ledger) -> String { let transactions = ledger.get_transactions(); let credentials = ledger.get_credentials(); - dump += format!("UTXO's: {:?}", utxos).as_str(); - dump += DELIMETER; - dump += format!("Transaction's: {:?}", transactions).as_str(); - dump += DELIMETER; - dump += format!( - "Txid's: {:?}", - transactions - .iter() - .map(|tx| tx.compute_txid()) - .collect::>() - ) - .as_str(); - dump += DELIMETER; - dump += format!("Credentials's: {:?}", credentials).as_str(); + if pretty { + dump += format!("UTXOs: {:#?}", utxos).as_str(); + dump += DELIMETER; + dump += format!("Transactions: {:#?}", transactions).as_str(); + dump += DELIMETER; + dump += format!( + "Txids: {:#?}", + transactions + .iter() + .map(|tx| tx.compute_txid()) + .collect::>() + ) + .as_str(); + dump += DELIMETER; + dump += format!("Credentials: {:#?}", credentials).as_str(); + } else { + dump += format!("UTXOs: {:?}", utxos).as_str(); + dump += DELIMETER; + dump += format!("Transactions: {:?}", transactions).as_str(); + dump += DELIMETER; + dump += format!( + "Txids: {:?}", + transactions + .iter() + .map(|tx| tx.compute_txid()) + .collect::>() + ) + .as_str(); + dump += DELIMETER; + dump += format!("Credentials: {:?}", credentials).as_str(); + } dump } From 2f31c417bda569e98f3e6ea3b3dec93aedcbb4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ceyhun=20=C5=9Een?= Date: Thu, 4 Jul 2024 15:35:59 +0300 Subject: [PATCH 13/13] changelog: Add initial version: 0.0.1. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0026354..f74d847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [0.0.1] - 2024-07-04 ### Added @@ -22,4 +22,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `generate_to_address` - `get_balance` -[unreleased]: https://github.com/chainwayxyz/bitcoin-mock-rpc +[0.0.1]: https://github.com/chainwayxyz/bitcoin-mock-rpc/releases/tag/v0.0.1