diff --git a/.github/workflows/cont_integration.yml b/.github/workflows/cont_integration.yml index de4c96bd..5ecc7e9d 100644 --- a/.github/workflows/cont_integration.yml +++ b/.github/workflows/cont_integration.yml @@ -78,6 +78,7 @@ jobs: name: Test ${{ matrix.blockchain.name }} runs-on: ubuntu-16.04 strategy: + fail-fast: false matrix: blockchain: - name: electrum diff --git a/src/blockchain/compact_filters/mod.rs b/src/blockchain/compact_filters/mod.rs index 97548916..fd138297 100644 --- a/src/blockchain/compact_filters/mod.rs +++ b/src/blockchain/compact_filters/mod.rs @@ -71,7 +71,7 @@ use super::{Blockchain, Capability, ConfigurableBlockchain, Progress}; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; use crate::error::Error; use crate::types::{KeychainKind, LocalUtxo, TransactionDetails}; -use crate::FeeRate; +use crate::{ConfirmationTime, FeeRate}; use peer::*; use store::*; @@ -146,7 +146,7 @@ impl CompactFiltersBlockchain { database: &mut D, tx: &Transaction, height: Option, - timestamp: u64, + timestamp: Option, internal_max_deriv: &mut Option, external_max_deriv: &mut Option, ) -> Result<(), Error> { @@ -206,9 +206,8 @@ impl CompactFiltersBlockchain { transaction: Some(tx.clone()), received: incoming, sent: outgoing, - height, - timestamp, - fees: inputs_sum.saturating_sub(outputs_sum), + confirmation_time: ConfirmationTime::new(height, timestamp), + fee: Some(inputs_sum.saturating_sub(outputs_sum)), }; info!("Saving tx {}", tx.txid); @@ -364,8 +363,8 @@ impl Blockchain for CompactFiltersBlockchain { ); let mut updates = database.begin_batch(); for details in database.iter_txs(false)? { - match details.height { - Some(height) if (height as usize) < last_synced_block => continue, + match details.confirmation_time { + Some(c) if (c.height as usize) < last_synced_block => continue, _ => updates.del_tx(&details.txid, false)?, }; } @@ -387,7 +386,7 @@ impl Blockchain for CompactFiltersBlockchain { database, tx, Some(height as u32), - 0, + None, &mut internal_max_deriv, &mut external_max_deriv, )?; @@ -398,7 +397,7 @@ impl Blockchain for CompactFiltersBlockchain { database, tx, None, - 0, + None, &mut internal_max_deriv, &mut external_max_deriv, )?; diff --git a/src/blockchain/rpc.rs b/src/blockchain/rpc.rs index 40ec0ae6..ab55cc49 100644 --- a/src/blockchain/rpc.rs +++ b/src/blockchain/rpc.rs @@ -33,7 +33,7 @@ use crate::blockchain::{Blockchain, Capability, ConfigurableBlockchain, Progress use crate::database::{BatchDatabase, DatabaseUtils}; use crate::descriptor::{get_checksum, IntoWalletDescriptor}; use crate::wallet::utils::SecpCtx; -use crate::{Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails}; +use crate::{ConfirmationTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails}; use bitcoincore_rpc::json::{ GetAddressInfoResultLabel, ImportMultiOptions, ImportMultiRequest, ImportMultiRequestScriptPubkey, ImportMultiRescanSince, @@ -189,13 +189,15 @@ impl Blockchain for RpcBlockchain { let txid = tx_result.info.txid; list_txs_ids.insert(txid); if let Some(mut known_tx) = known_txs.get_mut(&txid) { - if tx_result.info.blockheight != known_tx.height { + let confirmation_time = + ConfirmationTime::new(tx_result.info.blockheight, tx_result.info.blocktime); + if confirmation_time != known_tx.confirmation_time { // reorg may change tx height debug!( - "updating tx({}) height to: {:?}", - txid, tx_result.info.blockheight + "updating tx({}) confirmation time to: {:?}", + txid, confirmation_time ); - known_tx.height = tx_result.info.blockheight; + known_tx.confirmation_time = confirmation_time; db.set_tx(&known_tx)?; } } else { @@ -224,17 +226,17 @@ impl Blockchain for RpcBlockchain { let td = TransactionDetails { transaction: Some(tx), txid: tx_result.info.txid, - timestamp: tx_result.info.time, + confirmation_time: ConfirmationTime::new( + tx_result.info.blockheight, + tx_result.info.blocktime, + ), received, sent, - //TODO it could happen according to the node situation/configuration that the - // fee is not known [TransactionDetails:fee] should be made [Option] - fees: tx_result.fee.map(|f| f.as_sat().abs() as u64).unwrap_or(0), - height: tx_result.info.blockheight, + fee: tx_result.fee.map(|f| f.as_sat().abs() as u64), }; debug!( "saving tx: {} tx_result.fee:{:?} td.fees:{:?}", - td.txid, tx_result.fee, td.fees + td.txid, tx_result.fee, td.fee ); db.set_tx(&td)?; } @@ -519,7 +521,7 @@ mod test { wallet.sync(noop_progress(), None).unwrap(); assert_eq!( wallet.get_balance().unwrap(), - 100_000 - 50_000 - details.fees + 100_000 - 50_000 - details.fee.unwrap_or(0) ); drop(wallet); diff --git a/src/blockchain/utils.rs b/src/blockchain/utils.rs index ac86520c..0ce96460 100644 --- a/src/blockchain/utils.rs +++ b/src/blockchain/utils.rs @@ -21,7 +21,7 @@ use bitcoin::{BlockHeader, OutPoint, Script, Transaction, Txid}; use super::*; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; use crate::error::Error; -use crate::types::{KeychainKind, LocalUtxo, TransactionDetails}; +use crate::types::{ConfirmationTime, KeychainKind, LocalUtxo, TransactionDetails}; use crate::wallet::time::Instant; use crate::wallet::utils::ChunksIterator; @@ -147,13 +147,14 @@ pub trait ElectrumLikeSync { // save any tx details not in db but in history_txs_id or with different height/timestamp for txid in history_txs_id.iter() { let height = txid_height.get(txid).cloned().flatten(); - let timestamp = *new_timestamps.get(txid).unwrap_or(&0u64); + let timestamp = new_timestamps.get(txid).cloned(); if let Some(tx_details) = txs_details_in_db.get(txid) { - // check if height matches, otherwise updates it - if tx_details.height != height { + // check if tx height matches, otherwise updates it. timestamp is not in the if clause + // because we are not asking headers for confirmed tx we know about + if tx_details.confirmation_time.as_ref().map(|c| c.height) != height { + let confirmation_time = ConfirmationTime::new(height, timestamp); let mut new_tx_details = tx_details.clone(); - new_tx_details.height = height; - new_tx_details.timestamp = timestamp; + new_tx_details.confirmation_time = confirmation_time; batch.set_tx(&new_tx_details)?; } } else { @@ -238,9 +239,13 @@ pub trait ElectrumLikeSync { chunk_size: usize, ) -> Result, Error> { let mut txid_timestamp = HashMap::new(); + let txid_in_db_with_conf: HashSet<_> = txs_details_in_db + .values() + .filter_map(|details| details.confirmation_time.as_ref().map(|_| details.txid)) + .collect(); let needed_txid_height: HashMap<&Txid, u32> = txid_height .iter() - .filter(|(t, _)| txs_details_in_db.get(*t).is_none()) + .filter(|(t, _)| !txid_in_db_with_conf.contains(*t)) .filter_map(|(t, o)| o.map(|h| (t, h))) .collect(); let needed_heights: HashSet = needed_txid_height.values().cloned().collect(); @@ -292,7 +297,7 @@ pub trait ElectrumLikeSync { fn save_transaction_details_and_utxos( txid: &Txid, db: &mut D, - timestamp: u64, + timestamp: Option, height: Option, updates: &mut dyn BatchOperations, utxo_deps: &HashMap, @@ -355,9 +360,8 @@ fn save_transaction_details_and_utxos( transaction: Some(tx), received: incoming, sent: outgoing, - height, - timestamp, - fees: inputs_sum.saturating_sub(outputs_sum), /* if the tx is a coinbase, fees would be negative */ + confirmation_time: ConfirmationTime::new(height, timestamp), + fee: Some(inputs_sum.saturating_sub(outputs_sum)), /* if the tx is a coinbase, fees would be negative */ }; updates.set_tx(&tx_details)?; diff --git a/src/database/memory.rs b/src/database/memory.rs index cae53f10..7ec10bf3 100644 --- a/src/database/memory.rs +++ b/src/database/memory.rs @@ -473,18 +473,18 @@ macro_rules! populate_test_db { }; let txid = tx.txid(); - let height = tx_meta - .min_confirmations - .map(|conf| current_height.unwrap().checked_sub(conf as u32).unwrap()); + let confirmation_time = tx_meta.min_confirmations.map(|conf| ConfirmationTime { + height: current_height.unwrap().checked_sub(conf as u32).unwrap(), + timestamp: 0, + }); let tx_details = TransactionDetails { transaction: Some(tx.clone()), txid, - timestamp: 0, - height, + fee: Some(0), received: 0, sent: 0, - fees: 0, + confirmation_time, }; db.set_tx(&tx_details).unwrap(); diff --git a/src/database/mod.rs b/src/database/mod.rs index 84eb511e..b18ba1c0 100644 --- a/src/database/mod.rs +++ b/src/database/mod.rs @@ -314,11 +314,13 @@ pub mod test { let mut tx_details = TransactionDetails { transaction: Some(tx), txid, - timestamp: 123456, received: 1337, sent: 420420, - fees: 140, - height: Some(1000), + fee: Some(140), + confirmation_time: Some(ConfirmationTime { + timestamp: 123456, + height: 1000, + }), }; tree.set_tx(&tx_details).unwrap(); diff --git a/src/testutils/blockchain_tests.rs b/src/testutils/blockchain_tests.rs index 0b2b559e..c8e12d5f 100644 --- a/src/testutils/blockchain_tests.rs +++ b/src/testutils/blockchain_tests.rs @@ -396,7 +396,7 @@ macro_rules! bdk_blockchain_tests { assert_eq!(list_tx_item.txid, txid, "incorrect txid"); assert_eq!(list_tx_item.received, 50_000, "incorrect received"); assert_eq!(list_tx_item.sent, 0, "incorrect sent"); - assert_eq!(list_tx_item.height, None, "incorrect height"); + assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation time"); } #[test] @@ -454,7 +454,7 @@ macro_rules! bdk_blockchain_tests { assert_eq!(list_tx_item.txid, txid, "incorrect txid"); assert_eq!(list_tx_item.received, 105_000, "incorrect received"); assert_eq!(list_tx_item.sent, 0, "incorrect sent"); - assert_eq!(list_tx_item.height, None, "incorrect height"); + assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation_time"); } #[test] @@ -515,7 +515,7 @@ macro_rules! bdk_blockchain_tests { assert_eq!(list_tx_item.txid, txid, "incorrect txid"); assert_eq!(list_tx_item.received, 50_000, "incorrect received"); assert_eq!(list_tx_item.sent, 0, "incorrect sent"); - assert_eq!(list_tx_item.height, None, "incorrect height"); + assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation_time"); let new_txid = test_client.bump_fee(&txid); @@ -529,7 +529,7 @@ macro_rules! bdk_blockchain_tests { assert_eq!(list_tx_item.txid, new_txid, "incorrect txid after bump"); assert_eq!(list_tx_item.received, 50_000, "incorrect received after bump"); assert_eq!(list_tx_item.sent, 0, "incorrect sent after bump"); - assert_eq!(list_tx_item.height, None, "incorrect height after bump"); + assert_eq!(list_tx_item.confirmation_time, None, "incorrect height after bump"); } // FIXME: I would like this to be cfg_attr(not(feature = "test-esplora"), ignore) but it @@ -552,7 +552,7 @@ macro_rules! bdk_blockchain_tests { let list_tx_item = &wallet.list_transactions(false).unwrap()[0]; assert_eq!(list_tx_item.txid, txid, "incorrect txid"); - assert!(list_tx_item.height.is_some(), "incorrect height"); + assert!(list_tx_item.confirmation_time.is_some(), "incorrect confirmation_time"); // Invalidate 1 block test_client.invalidate(1); @@ -563,7 +563,7 @@ macro_rules! bdk_blockchain_tests { let list_tx_item = &wallet.list_transactions(false).unwrap()[0]; assert_eq!(list_tx_item.txid, txid, "incorrect txid after invalidate"); - assert_eq!(list_tx_item.height, None, "incorrect height after invalidate"); + assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation time after invalidate"); } #[test] @@ -596,6 +596,34 @@ macro_rules! bdk_blockchain_tests { assert_eq!(wallet.list_unspent().unwrap().len(), 1, "incorrect number of unspents"); } + #[test] + #[serial] + fn test_update_confirmation_time_after_generate() { + let (wallet, descriptors, mut test_client) = init_single_sig(); + println!("{}", descriptors.0); + let node_addr = test_client.get_node_address(None); + + let received_txid = test_client.receive(testutils! { + @tx ( (@external descriptors, 0) => 50_000 ) + }); + + wallet.sync(noop_progress(), None).unwrap(); + assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance"); + + let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::>(); + let details = tx_map.get(&received_txid).unwrap(); + assert!(details.confirmation_time.is_none()); + + test_client.generate(1, Some(node_addr)); + wallet.sync(noop_progress(), None).unwrap(); + + let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::>(); + let details = tx_map.get(&received_txid).unwrap(); + assert!(details.confirmation_time.is_some()); + + } + + #[test] #[serial] fn test_sync_outgoing_from_scratch() { @@ -635,7 +663,7 @@ macro_rules! bdk_blockchain_tests { let sent = tx_map.get(&sent_txid).unwrap(); assert_eq!(sent.received, details.received, "incorrect received from sender"); assert_eq!(sent.sent, details.sent, "incorrect sent from sender"); - assert_eq!(sent.fees, details.fees, "incorrect fees from sender"); + assert_eq!(sent.fee.unwrap_or(0), details.fee.unwrap_or(0), "incorrect fees from sender"); } #[test] @@ -662,7 +690,7 @@ macro_rules! bdk_blockchain_tests { wallet.sync(noop_progress(), None).unwrap(); - total_sent += 5_000 + details.fees; + total_sent += 5_000 + details.fee.unwrap_or(0); } wallet.sync(noop_progress(), None).unwrap(); @@ -700,7 +728,7 @@ macro_rules! bdk_blockchain_tests { assert!(finalized, "Cannot finalize transaction"); wallet.broadcast(psbt.extract_tx()).unwrap(); wallet.sync(noop_progress(), None).unwrap(); - assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fees - 5_000, "incorrect balance from fees"); + assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees"); assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance from received"); let mut builder = wallet.build_fee_bump(details.txid).unwrap(); @@ -710,10 +738,10 @@ macro_rules! bdk_blockchain_tests { assert!(finalized, "Cannot finalize transaction"); wallet.broadcast(new_psbt.extract_tx()).unwrap(); wallet.sync(noop_progress(), None).unwrap(); - assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fees - 5_000, "incorrect balance from fees after bump"); + assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees after bump"); assert_eq!(wallet.get_balance().unwrap(), new_details.received, "incorrect balance from received after bump"); - assert!(new_details.fees > details.fees, "incorrect fees"); + assert!(new_details.fee.unwrap_or(0) > details.fee.unwrap_or(0), "incorrect fees"); } #[test] @@ -736,7 +764,7 @@ macro_rules! bdk_blockchain_tests { assert!(finalized, "Cannot finalize transaction"); wallet.broadcast(psbt.extract_tx()).unwrap(); wallet.sync(noop_progress(), None).unwrap(); - assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fees, "incorrect balance after send"); + assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fee.unwrap_or(0), "incorrect balance after send"); assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect received after send"); let mut builder = wallet.build_fee_bump(details.txid).unwrap(); @@ -749,12 +777,12 @@ macro_rules! bdk_blockchain_tests { assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after change removal"); assert_eq!(new_details.received, 0, "incorrect received after change removal"); - assert!(new_details.fees > details.fees, "incorrect fees"); + assert!(new_details.fee.unwrap_or(0) > details.fee.unwrap_or(0), "incorrect fees"); } #[test] #[serial] - fn test_sync_bump_fee_add_input() { + fn test_sync_bump_fee_add_input_simple() { let (wallet, descriptors, mut test_client) = init_single_sig(); let node_addr = test_client.get_node_address(None); @@ -772,8 +800,8 @@ macro_rules! bdk_blockchain_tests { assert!(finalized, "Cannot finalize transaction"); wallet.broadcast(psbt.extract_tx()).unwrap(); wallet.sync(noop_progress(), None).unwrap(); - assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees, "incorrect balance after send"); - assert_eq!(details.received, 1_000 - details.fees, "incorrect received after send"); + assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send"); + assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send"); let mut builder = wallet.build_fee_bump(details.txid).unwrap(); builder.fee_rate(FeeRate::from_sat_per_vb(10.0)); @@ -806,8 +834,8 @@ macro_rules! bdk_blockchain_tests { assert!(finalized, "Cannot finalize transaction"); wallet.broadcast(psbt.extract_tx()).unwrap(); wallet.sync(noop_progress(), None).unwrap(); - assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees, "incorrect balance after send"); - assert_eq!(details.received, 1_000 - details.fees, "incorrect received after send"); + assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send"); + assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send"); let mut builder = wallet.build_fee_bump(details.txid).unwrap(); builder.fee_rate(FeeRate::from_sat_per_vb(123.0)); diff --git a/src/types.rs b/src/types.rs index cdb5d268..c71b87f4 100644 --- a/src/types.rs +++ b/src/types.rs @@ -155,16 +155,35 @@ pub struct TransactionDetails { pub transaction: Option, /// Transaction id pub txid: Txid, - /// Timestamp - pub timestamp: u64, + /// Received value (sats) pub received: u64, /// Sent value (sats) pub sent: u64, - /// Fee value (sats) - pub fees: u64, - /// Confirmed in block height, `None` means unconfirmed - pub height: Option, + /// Fee value (sats) if available + pub fee: Option, + /// If the transaction is confirmed, contains height and timestamp of the block containing the + /// transaction, unconfirmed transaction contains `None`. + pub confirmation_time: Option, +} + +/// Block height and timestamp of the block containing the confirmed transaction +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)] +pub struct ConfirmationTime { + /// confirmation block height + pub height: u32, + /// confirmation block timestamp + pub timestamp: u64, +} + +impl ConfirmationTime { + /// Returns `Some` `ConfirmationTime` if both `height` and `timestamp` are `Some` + pub fn new(height: Option, timestamp: Option) -> Option { + match (height, timestamp) { + (Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }), + _ => None, + } + } } #[cfg(test)] diff --git a/src/wallet/export.rs b/src/wallet/export.rs index 2be646d5..58ff77d2 100644 --- a/src/wallet/export.rs +++ b/src/wallet/export.rs @@ -128,7 +128,7 @@ impl WalletExport { Ok(txs) => { let mut heights = txs .into_iter() - .map(|tx| tx.height.unwrap_or(0)) + .map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(0)) .collect::>(); heights.sort_unstable(); @@ -212,6 +212,7 @@ mod test { use crate::database::{memory::MemoryDatabase, BatchOperations}; use crate::types::TransactionDetails; use crate::wallet::Wallet; + use crate::ConfirmationTime; fn get_test_db() -> MemoryDatabase { let mut db = MemoryDatabase::new(); @@ -221,11 +222,14 @@ mod test { "4ddff1fa33af17f377f62b72357b43107c19110a8009b36fb832af505efed98a", ) .unwrap(), - timestamp: 12345678, + received: 100_000, sent: 0, - fees: 500, - height: Some(5000), + fee: Some(500), + confirmation_time: Some(ConfirmationTime { + timestamp: 12345678, + height: 5000, + }), }) .unwrap(); diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 135b03c2..6127b5fd 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -706,11 +706,10 @@ where let transaction_details = TransactionDetails { transaction: None, txid, - timestamp: time::get_timestamp(), + confirmation_time: None, received, sent, - fees: fee_amount, - height: None, + fee: Some(fee_amount), }; Ok((psbt, transaction_details)) @@ -769,7 +768,7 @@ where let mut details = match self.database.borrow().get_tx(&txid, true)? { None => return Err(Error::TransactionNotFound), Some(tx) if tx.transaction.is_none() => return Err(Error::TransactionNotFound), - Some(tx) if tx.height.is_some() => return Err(Error::TransactionConfirmed), + Some(tx) if tx.confirmation_time.is_some() => return Err(Error::TransactionConfirmed), Some(tx) => tx, }; let mut tx = details.transaction.take().unwrap(); @@ -778,7 +777,7 @@ where } let vbytes = tx.get_weight() as f32 / 4.0; - let feerate = details.fees as f32 / vbytes; + let feerate = details.fee.ok_or(Error::FeeRateUnavailable)? as f32 / vbytes; // remove the inputs from the tx and process them let original_txin = tx.input.drain(..).collect::>(); @@ -854,7 +853,7 @@ where .collect(), utxos: original_utxos, bumping_fee: Some(tx_builder::PreviousFee { - absolute: details.fees, + absolute: details.fee.ok_or(Error::FeeRateUnavailable)?, rate: feerate, }), ..Default::default() @@ -993,7 +992,7 @@ where .database .borrow() .get_tx(&input.previous_output.txid, false)? - .map(|tx| tx.height.unwrap_or(std::u32::MAX)); + .map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(u32::MAX)); let current_height = sign_options.assume_height.or(self.current_height); debug!( @@ -1231,7 +1230,7 @@ where let satisfies_confirmed = match must_only_use_confirmed_tx { true => { - let database = self.database.borrow_mut(); + let database = self.database.borrow(); may_spend .iter() .map(|u| { @@ -1239,7 +1238,7 @@ where .get_tx(&u.0.outpoint.txid, true) .map(|tx| match tx { None => false, - Some(tx) => tx.height.is_some(), + Some(tx) => tx.confirmation_time.is_some(), }) }) .collect::, _>>()? @@ -1986,7 +1985,7 @@ pub(crate) mod test { assert_eq!(psbt.global.unsigned_tx.output.len(), 1); assert_eq!( psbt.global.unsigned_tx.output[0].value, - 50_000 - details.fees + 50_000 - details.fee.unwrap_or(0) ); } @@ -1998,7 +1997,7 @@ pub(crate) mod test { builder.add_recipient(addr.script_pubkey(), 25_000); let (psbt, details) = builder.finish().unwrap(); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::default(), @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::default(), @add_signature); } #[test] @@ -2011,7 +2010,7 @@ pub(crate) mod test { .fee_rate(FeeRate::from_sat_per_vb(5.0)); let (psbt, details) = builder.finish().unwrap(); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature); } #[test] @@ -2025,11 +2024,11 @@ pub(crate) mod test { .fee_absolute(100); let (psbt, details) = builder.finish().unwrap(); - assert_eq!(details.fees, 100); + assert_eq!(details.fee.unwrap_or(0), 100); assert_eq!(psbt.global.unsigned_tx.output.len(), 1); assert_eq!( psbt.global.unsigned_tx.output[0].value, - 50_000 - details.fees + 50_000 - details.fee.unwrap_or(0) ); } @@ -2044,11 +2043,11 @@ pub(crate) mod test { .fee_absolute(0); let (psbt, details) = builder.finish().unwrap(); - assert_eq!(details.fees, 0); + assert_eq!(details.fee.unwrap_or(0), 0); assert_eq!(psbt.global.unsigned_tx.output.len(), 1); assert_eq!( psbt.global.unsigned_tx.output[0].value, - 50_000 - details.fees + 50_000 - details.fee.unwrap_or(0) ); } @@ -2081,7 +2080,7 @@ pub(crate) mod test { assert_eq!(psbt.global.unsigned_tx.output[0].value, 25_000); assert_eq!( psbt.global.unsigned_tx.output[1].value, - 25_000 - details.fees + 25_000 - details.fee.unwrap_or(0) ); } @@ -2095,7 +2094,7 @@ pub(crate) mod test { assert_eq!(psbt.global.unsigned_tx.output.len(), 1); assert_eq!(psbt.global.unsigned_tx.output[0].value, 49_800); - assert_eq!(details.fees, 200); + assert_eq!(details.fee.unwrap_or(0), 200); } #[test] @@ -2126,7 +2125,7 @@ pub(crate) mod test { assert_eq!(psbt.global.unsigned_tx.output.len(), 3); assert_eq!( psbt.global.unsigned_tx.output[0].value, - 10_000 - details.fees + 10_000 - details.fee.unwrap_or(0) ); assert_eq!(psbt.global.unsigned_tx.output[1].value, 10_000); assert_eq!(psbt.global.unsigned_tx.output[2].value, 30_000); @@ -2496,7 +2495,7 @@ pub(crate) mod test { assert_eq!( details.sent - details.received, - 10_000 + details.fees, + 10_000 + details.fee.unwrap_or(0), "we should have only net spent ~10_000" ); @@ -2762,7 +2761,10 @@ pub(crate) mod test { let txid = tx.txid(); // skip saving the utxos, we know they can't be used anyways details.transaction = Some(tx); - details.height = Some(42); + details.confirmation_time = Some(ConfirmationTime { + timestamp: 12345678, + height: 42, + }); wallet.database.borrow_mut().set_tx(&details).unwrap(); wallet.build_fee_bump(txid).unwrap().finish().unwrap(); @@ -2867,10 +2869,10 @@ pub(crate) mod test { assert_eq!(details.sent, original_details.sent); assert_eq!( - details.received + details.fees, - original_details.received + original_details.fees + details.received + details.fee.unwrap_or(0), + original_details.received + original_details.fee.unwrap_or(0) ); - assert!(details.fees > original_details.fees); + assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0)); let tx = &psbt.global.unsigned_tx; assert_eq!(tx.output.len(), 2); @@ -2891,7 +2893,7 @@ pub(crate) mod test { details.received ); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature); } #[test] @@ -2928,14 +2930,14 @@ pub(crate) mod test { assert_eq!(details.sent, original_details.sent); assert_eq!( - details.received + details.fees, - original_details.received + original_details.fees + details.received + details.fee.unwrap_or(0), + original_details.received + original_details.fee.unwrap_or(0) ); assert!( - details.fees > original_details.fees, + details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0), "{} > {}", - details.fees, - original_details.fees + details.fee.unwrap_or(0), + original_details.fee.unwrap_or(0) ); let tx = &psbt.global.unsigned_tx; @@ -2957,7 +2959,7 @@ pub(crate) mod test { details.received ); - assert_eq!(details.fees, 200); + assert_eq!(details.fee.unwrap_or(0), 200); } #[test] @@ -2995,13 +2997,13 @@ pub(crate) mod test { let (psbt, details) = builder.finish().unwrap(); assert_eq!(details.sent, original_details.sent); - assert!(details.fees > original_details.fees); + assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0)); let tx = &psbt.global.unsigned_tx; assert_eq!(tx.output.len(), 1); - assert_eq!(tx.output[0].value + details.fees, details.sent); + assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature); } #[test] @@ -3039,13 +3041,13 @@ pub(crate) mod test { let (psbt, details) = builder.finish().unwrap(); assert_eq!(details.sent, original_details.sent); - assert!(details.fees > original_details.fees); + assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0)); let tx = &psbt.global.unsigned_tx; assert_eq!(tx.output.len(), 1); - assert_eq!(tx.output[0].value + details.fees, details.sent); + assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent); - assert_eq!(details.fees, 300); + assert_eq!(details.fee.unwrap_or(0), 300); } #[test] @@ -3190,7 +3192,7 @@ pub(crate) mod test { let (psbt, details) = builder.finish().unwrap(); assert_eq!(details.sent, original_details.sent + 25_000); - assert_eq!(details.fees + details.received, 30_000); + assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000); let tx = &psbt.global.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -3212,7 +3214,7 @@ pub(crate) mod test { details.received ); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(50.0), @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature); } #[test] @@ -3253,7 +3255,7 @@ pub(crate) mod test { let (psbt, details) = builder.finish().unwrap(); assert_eq!(details.sent, original_details.sent + 25_000); - assert_eq!(details.fees + details.received, 30_000); + assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000); let tx = &psbt.global.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -3275,7 +3277,7 @@ pub(crate) mod test { details.received ); - assert_eq!(details.fees, 6_000); + assert_eq!(details.fee.unwrap_or(0), 6_000); } #[test] @@ -3325,11 +3327,11 @@ pub(crate) mod test { builder.fee_rate(FeeRate::from_sat_per_vb(50.0)); let (psbt, details) = builder.finish().unwrap(); - let original_send_all_amount = original_details.sent - original_details.fees; + let original_send_all_amount = original_details.sent - original_details.fee.unwrap_or(0); assert_eq!(details.sent, original_details.sent + 50_000); assert_eq!( details.received, - 75_000 - original_send_all_amount - details.fees + 75_000 - original_send_all_amount - details.fee.unwrap_or(0) ); let tx = &psbt.global.unsigned_tx; @@ -3349,10 +3351,10 @@ pub(crate) mod test { .find(|txout| txout.script_pubkey != addr.script_pubkey()) .unwrap() .value, - 75_000 - original_send_all_amount - details.fees + 75_000 - original_send_all_amount - details.fee.unwrap_or(0) ); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(50.0), @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature); } #[test] @@ -3394,10 +3396,13 @@ pub(crate) mod test { builder.fee_rate(FeeRate::from_sat_per_vb(140.0)); let (psbt, details) = builder.finish().unwrap(); - assert_eq!(original_details.received, 5_000 - original_details.fees); + assert_eq!( + original_details.received, + 5_000 - original_details.fee.unwrap_or(0) + ); assert_eq!(details.sent, original_details.sent + 25_000); - assert_eq!(details.fees, 30_000); + assert_eq!(details.fee.unwrap_or(0), 30_000); assert_eq!(details.received, 0); let tx = &psbt.global.unsigned_tx; @@ -3412,7 +3417,7 @@ pub(crate) mod test { 45_000 ); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(140.0), @dust_change, @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(140.0), @dust_change, @add_signature); } #[test] @@ -3461,7 +3466,7 @@ pub(crate) mod test { let (psbt, details) = builder.finish().unwrap(); assert_eq!(details.sent, original_details.sent + 25_000); - assert_eq!(details.fees + details.received, 30_000); + assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000); let tx = &psbt.global.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -3483,7 +3488,7 @@ pub(crate) mod test { details.received ); - assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature); + assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature); } #[test] @@ -3532,7 +3537,7 @@ pub(crate) mod test { let (psbt, details) = builder.finish().unwrap(); assert_eq!(details.sent, original_details.sent + 25_000); - assert_eq!(details.fees + details.received, 30_000); + assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000); let tx = &psbt.global.unsigned_tx; assert_eq!(tx.input.len(), 2); @@ -3554,7 +3559,7 @@ pub(crate) mod test { details.received ); - assert_eq!(details.fees, 250); + assert_eq!(details.fee.unwrap_or(0), 250); } #[test]