diff --git a/crates/wallet/src/wallet/export.rs b/crates/wallet/src/wallet/export.rs index 0aa3ec88..c4a3c096 100644 --- a/crates/wallet/src/wallet/export.rs +++ b/crates/wallet/src/wallet/export.rs @@ -214,7 +214,7 @@ mod test { use core::str::FromStr; use crate::std::string::ToString; - use bdk_chain::{BlockId, ConfirmationTime}; + use bdk_chain::{BlockId, ConfirmationTimeHeightAnchor}; use bitcoin::hashes::Hash; use bitcoin::{transaction, BlockHash, Network, Transaction}; @@ -229,21 +229,21 @@ mod test { version: transaction::Version::non_standard(0), lock_time: bitcoin::absolute::LockTime::ZERO, }; - wallet - .insert_checkpoint(BlockId { - height: 5001, - hash: BlockHash::all_zeros(), - }) - .unwrap(); - wallet - .insert_tx( - transaction, - ConfirmationTime::Confirmed { - height: 5000, - time: 0, - }, - ) - .unwrap(); + let txid = transaction.compute_txid(); + let block_id = BlockId { + height: 5001, + hash: BlockHash::all_zeros(), + }; + wallet.insert_checkpoint(block_id).unwrap(); + wallet.insert_tx(transaction); + wallet.insert_anchor( + txid, + ConfirmationTimeHeightAnchor { + confirmation_height: 5000, + confirmation_time: 0, + anchor_block: block_id, + }, + ); wallet } diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs index 400ecde1..85c47aed 100644 --- a/crates/wallet/src/wallet/mod.rs +++ b/crates/wallet/src/wallet/mod.rs @@ -284,35 +284,6 @@ impl fmt::Display for NewOrLoadError { #[cfg(feature = "std")] impl std::error::Error for NewOrLoadError {} -/// An error that may occur when inserting a transaction into [`Wallet`]. -#[derive(Debug)] -pub enum InsertTxError { - /// The error variant that occurs when the caller attempts to insert a transaction with a - /// confirmation height that is greater than the internal chain tip. - ConfirmationHeightCannotBeGreaterThanTip { - /// The internal chain's tip height. - tip_height: u32, - /// The introduced transaction's confirmation height. - tx_height: u32, - }, -} - -impl fmt::Display for InsertTxError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - InsertTxError::ConfirmationHeightCannotBeGreaterThanTip { - tip_height, - tx_height, - } => { - write!(f, "cannot insert tx with confirmation height ({}) higher than internal tip height ({})", tx_height, tip_height) - } - } - } -} - -#[cfg(feature = "std")] -impl std::error::Error for InsertTxError {} - /// An error that may occur when applying a block to [`Wallet`]. #[derive(Debug)] pub enum ApplyBlockError { @@ -906,6 +877,23 @@ impl Wallet { self.stage.append(additions.into()); } + /// Inserts an `anchor` for a transaction with the given `txid`. + /// + /// This stages the changes, you must persist them later. + pub fn insert_anchor(&mut self, txid: Txid, anchor: ConfirmationTimeHeightAnchor) { + let indexed_graph_changeset = self.indexed_graph.insert_anchor(txid, anchor); + self.stage.append(indexed_graph_changeset.into()); + } + + /// Inserts a unix timestamp of when a transaction is seen in the mempool. + /// + /// This is used for transaction conflict resolution where the transaction with the + /// later last-seen is prioritized. This stages the changes, you must persist them later. + pub fn insert_seen_at(&mut self, txid: Txid, seen_at: u64) { + let indexed_graph_changeset = self.indexed_graph.insert_seen_at(txid, seen_at); + self.stage.append(indexed_graph_changeset.into()); + } + /// Calculates the fee of a given transaction. Returns [`Amount::ZERO`] if `tx` is a coinbase transaction. /// /// To calculate the fee for a [`Transaction`] with inputs not owned by this wallet you must @@ -1079,63 +1067,21 @@ impl Wallet { /// Add a transaction to the wallet's internal view of the chain. This stages the change, /// you must persist it later. /// - /// Returns whether anything changed with the transaction insertion (e.g. `false` if the - /// transaction was already inserted at the same position). + /// This method inserts the given `tx` and returns whether anything changed after insertion, + /// which will be false if the same transaction already exists in the wallet's transaction + /// graph. Any changes are staged but not committed. /// - /// A `tx` can be rejected if `position` has a height greater than the [`latest_checkpoint`]. - /// Therefore you should use [`insert_checkpoint`] to insert new checkpoints before manually - /// inserting new transactions. + /// # Note /// - /// **WARNING**: If `position` is confirmed, we anchor the `tx` to the lowest checkpoint that - /// is >= the `position`'s height. The caller is responsible for ensuring the `tx` exists in our - /// local view of the best chain's history. - /// - /// You must persist the changes resulting from one or more calls to this method if you need - /// the inserted tx to be reloaded after closing the wallet. - /// - /// [`commit`]: Self::commit - /// [`latest_checkpoint`]: Self::latest_checkpoint - /// [`insert_checkpoint`]: Self::insert_checkpoint - pub fn insert_tx( - &mut self, - tx: Transaction, - position: ConfirmationTime, - ) -> Result { - let (anchor, last_seen) = match position { - ConfirmationTime::Confirmed { height, time } => { - // anchor tx to checkpoint with lowest height that is >= position's height - let anchor = self - .chain - .range(height..) - .last() - .ok_or(InsertTxError::ConfirmationHeightCannotBeGreaterThanTip { - tip_height: self.chain.tip().height(), - tx_height: height, - }) - .map(|anchor_cp| ConfirmationTimeHeightAnchor { - anchor_block: anchor_cp.block_id(), - confirmation_height: height, - confirmation_time: time, - })?; - - (Some(anchor), None) - } - ConfirmationTime::Unconfirmed { last_seen } => (None, Some(last_seen)), - }; - + /// By default the inserted `tx` won't be considered "canonical" because it's not known + /// whether the transaction exists in the best chain. To know whether it exists, the tx + /// must be broadcast to the network and the wallet synced via a chain source. + pub fn insert_tx(&mut self, tx: Transaction) -> bool { let mut changeset = ChangeSet::default(); - let txid = tx.compute_txid(); changeset.append(self.indexed_graph.insert_tx(tx).into()); - if let Some(anchor) = anchor { - changeset.append(self.indexed_graph.insert_anchor(txid, anchor).into()); - } - if let Some(last_seen) = last_seen { - changeset.append(self.indexed_graph.insert_seen_at(txid, last_seen).into()); - } - - let changed = !changeset.is_empty(); + let ret = !changeset.is_empty(); self.stage.append(changeset); - Ok(changed) + ret } /// Iterate over the transactions in the wallet. @@ -2540,7 +2486,7 @@ macro_rules! floating_rate { macro_rules! doctest_wallet { () => {{ use $crate::bitcoin::{BlockHash, Transaction, absolute, TxOut, Network, hashes::Hash}; - use $crate::chain::{ConfirmationTime, BlockId}; + use $crate::chain::{ConfirmationTimeHeightAnchor, BlockId}; use $crate::{KeychainKind, wallet::Wallet}; let descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/0/*)"; let change_descriptor = "tr([73c5da0a/86'/0'/0']tprv8fMn4hSKPRC1oaCPqxDb1JWtgkpeiQvZhsr8W2xuy3GEMkzoArcAWTfJxYb6Wj8XNNDWEjfYKK4wGQXh3ZUXhDF2NcnsALpWTeSwarJt7Vc/1/*)"; @@ -2561,11 +2507,19 @@ macro_rules! doctest_wallet { script_pubkey: address.script_pubkey(), }], }; - let _ = wallet.insert_checkpoint(BlockId { height: 1_000, hash: BlockHash::all_zeros() }); - let _ = wallet.insert_tx(tx.clone(), ConfirmationTime::Confirmed { - height: 500, - time: 50_000 - }); + let txid = tx.txid(); + let block = BlockId { height: 1_000, hash: BlockHash::all_zeros() }; + let _ = wallet.insert_checkpoint(block); + let _ = wallet.insert_tx(tx); + wallet + .insert_anchor( + txid, + ConfirmationTimeHeightAnchor { + confirmation_height: 500, + confirmation_time: 50_000, + anchor_block: block, + } + ); wallet }} diff --git a/crates/wallet/tests/common.rs b/crates/wallet/tests/common.rs index bab75e27..2d4bb852 100644 --- a/crates/wallet/tests/common.rs +++ b/crates/wallet/tests/common.rs @@ -1,7 +1,7 @@ #![allow(unused)] use bdk_chain::indexed_tx_graph::Indexer; -use bdk_chain::{BlockId, ConfirmationTime}; +use bdk_chain::{BlockId, ConfirmationTime, ConfirmationTimeHeightAnchor}; use bdk_wallet::{KeychainKind, LocalOutput, Wallet}; use bitcoin::hashes::Hash; use bitcoin::{ @@ -77,24 +77,26 @@ pub fn get_funded_wallet_with_change(descriptor: &str, change: &str) -> (Wallet, hash: BlockHash::all_zeros(), }) .unwrap(); - wallet - .insert_tx( - tx0, - ConfirmationTime::Confirmed { - height: 1_000, - time: 100, - }, - ) - .unwrap(); - wallet - .insert_tx( - tx1.clone(), - ConfirmationTime::Confirmed { - height: 2_000, - time: 200, - }, - ) - .unwrap(); + + wallet.insert_tx(tx0.clone()); + insert_anchor_from_conf( + &mut wallet, + tx0.compute_txid(), + ConfirmationTime::Confirmed { + height: 1_000, + time: 100, + }, + ); + + wallet.insert_tx(tx1.clone()); + insert_anchor_from_conf( + &mut wallet, + tx1.compute_txid(), + ConfirmationTime::Confirmed { + height: 2_000, + time: 200, + }, + ); (wallet, tx1.compute_txid()) } @@ -192,3 +194,24 @@ pub fn feerate_unchecked(sat_vb: f64) -> FeeRate { let sat_kwu = (sat_vb * 250.0).ceil() as u64; FeeRate::from_sat_per_kwu(sat_kwu) } + +/// Simulates confirming a tx with `txid` at the specified `position` by inserting an anchor +/// at the lowest height in local chain that is greater or equal to `position`'s height, +/// assuming the confirmation time matches `ConfirmationTime::Confirmed`. +pub fn insert_anchor_from_conf(wallet: &mut Wallet, txid: Txid, position: ConfirmationTime) { + if let ConfirmationTime::Confirmed { height, time } = position { + // anchor tx to checkpoint with lowest height that is >= position's height + let anchor = wallet + .local_chain() + .range(height..) + .last() + .map(|anchor_cp| ConfirmationTimeHeightAnchor { + anchor_block: anchor_cp.block_id(), + confirmation_height: height, + confirmation_time: time, + }) + .expect("confirmation height cannot be greater than tip"); + + wallet.insert_anchor(txid, anchor); + } +} diff --git a/crates/wallet/tests/wallet.rs b/crates/wallet/tests/wallet.rs index ef5575ac..bf5075e8 100644 --- a/crates/wallet/tests/wallet.rs +++ b/crates/wallet/tests/wallet.rs @@ -42,12 +42,19 @@ fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> }], }; - wallet.insert_tx(tx.clone(), height).unwrap(); + let txid = tx.compute_txid(); + wallet.insert_tx(tx); - OutPoint { - txid: tx.compute_txid(), - vout: 0, + match height { + ConfirmationTime::Confirmed { .. } => { + insert_anchor_from_conf(wallet, txid, height); + } + ConfirmationTime::Unconfirmed { last_seen } => { + wallet.insert_seen_at(txid, last_seen); + } } + + OutPoint { txid, vout: 0 } } fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint { @@ -1180,12 +1187,7 @@ fn test_create_tx_add_utxo() { version: transaction::Version::non_standard(0), lock_time: absolute::LockTime::ZERO, }; - wallet - .insert_tx( - small_output_tx.clone(), - ConfirmationTime::Unconfirmed { last_seen: 0 }, - ) - .unwrap(); + wallet.insert_tx(small_output_tx.clone()); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX") .unwrap() @@ -1230,12 +1232,7 @@ fn test_create_tx_manually_selected_insufficient() { lock_time: absolute::LockTime::ZERO, }; - wallet - .insert_tx( - small_output_tx.clone(), - ConfirmationTime::Unconfirmed { last_seen: 0 }, - ) - .unwrap(); + wallet.insert_tx(small_output_tx.clone()); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX") .unwrap() @@ -1281,9 +1278,7 @@ fn test_create_tx_policy_path_no_csv() { value: Amount::from_sat(50_000), }], }; - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap(); let root_id = external_policy.id; @@ -1674,9 +1669,7 @@ fn test_bump_fee_irreplaceable_tx() { let tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); wallet.build_fee_bump(txid).unwrap().finish().unwrap(); } @@ -1692,15 +1685,15 @@ fn test_bump_fee_confirmed_tx() { let tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); - wallet - .insert_tx( - tx, - ConfirmationTime::Confirmed { - height: 42, - time: 42_000, - }, - ) - .unwrap(); + wallet.insert_tx(tx); + insert_anchor_from_conf( + &mut wallet, + txid, + ConfirmationTime::Confirmed { + height: 42, + time: 42_000, + }, + ); wallet.build_fee_bump(txid).unwrap().finish().unwrap(); } @@ -1719,9 +1712,7 @@ fn test_bump_fee_low_fee_rate() { let tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder.fee_rate(FeeRate::BROADCAST_MIN); @@ -1752,9 +1743,7 @@ fn test_bump_fee_low_abs() { let tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder.fee_absolute(Amount::from_sat(10)); @@ -1774,9 +1763,7 @@ fn test_bump_fee_zero_abs() { let tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder.fee_absolute(Amount::ZERO); @@ -1800,9 +1787,7 @@ fn test_bump_fee_reduce_change() { let tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let feerate = FeeRate::from_sat_per_kwu(625); // 2.5 sat/vb let mut builder = wallet.build_fee_bump(txid).unwrap(); @@ -1898,9 +1883,7 @@ fn test_bump_fee_reduce_single_recipient() { let original_sent_received = wallet.sent_and_received(&tx); let original_fee = check_fee!(wallet, psbt); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let feerate = FeeRate::from_sat_per_kwu(625); // 2.5 sat/vb let mut builder = wallet.build_fee_bump(txid).unwrap(); @@ -1946,9 +1929,7 @@ fn test_bump_fee_absolute_reduce_single_recipient() { let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder @@ -1991,15 +1972,18 @@ fn test_bump_fee_drain_wallet() { value: Amount::from_sat(25_000), }], }; - wallet - .insert_tx( - tx.clone(), - ConfirmationTime::Confirmed { - height: wallet.latest_checkpoint().height(), - time: 42_000, - }, - ) - .unwrap(); + let txid = tx.compute_txid(); + let tip = wallet.latest_checkpoint().height(); + wallet.insert_tx(tx.clone()); + insert_anchor_from_conf( + &mut wallet, + txid, + ConfirmationTime::Confirmed { + height: tip, + time: 42_000, + }, + ); + let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX") .unwrap() .assume_checked(); @@ -2019,9 +2003,7 @@ fn test_bump_fee_drain_wallet() { let original_sent_received = wallet.sent_and_received(&tx); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); assert_eq!(original_sent_received.0, Amount::from_sat(25_000)); // for the new feerate, it should be enough to reduce the output, but since we specify @@ -2056,18 +2038,17 @@ fn test_bump_fee_remove_output_manually_selected_only() { value: Amount::from_sat(25_000), }], }; - wallet - .insert_tx( - init_tx.clone(), - wallet - .transactions() - .last() - .unwrap() - .chain_position - .cloned() - .into(), - ) - .unwrap(); + let position: ConfirmationTime = wallet + .transactions() + .last() + .unwrap() + .chain_position + .cloned() + .into(); + + wallet.insert_tx(init_tx.clone()); + insert_anchor_from_conf(&mut wallet, init_tx.compute_txid(), position); + let outpoint = OutPoint { txid: init_tx.compute_txid(), vout: 0, @@ -2086,9 +2067,7 @@ fn test_bump_fee_remove_output_manually_selected_only() { let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); assert_eq!(original_sent_received.0, Amount::from_sat(25_000)); let mut builder = wallet.build_fee_bump(txid).unwrap(); @@ -2112,14 +2091,16 @@ fn test_bump_fee_add_input() { value: Amount::from_sat(25_000), }], }; - let pos = wallet + let txid = init_tx.compute_txid(); + let pos: ConfirmationTime = wallet .transactions() .last() .unwrap() .chain_position .cloned() .into(); - wallet.insert_tx(init_tx, pos).unwrap(); + wallet.insert_tx(init_tx); + insert_anchor_from_conf(&mut wallet, txid, pos); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX") .unwrap() @@ -2132,9 +2113,7 @@ fn test_bump_fee_add_input() { let tx = psbt.extract_tx().expect("failed to extract tx"); let original_details = wallet.sent_and_received(&tx); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(50)); @@ -2189,9 +2168,7 @@ fn test_bump_fee_absolute_add_input() { let tx = psbt.extract_tx().expect("failed to extract tx"); let original_sent_received = wallet.sent_and_received(&tx); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder.fee_absolute(Amount::from_sat(6_000)); @@ -2255,9 +2232,7 @@ fn test_bump_fee_no_change_add_input_and_change() { let tx = psbt.extract_tx().expect("failed to extract tx"); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); // Now bump the fees, the wallet should add an extra input and a change output, and leave // the original output untouched. @@ -2325,9 +2300,7 @@ fn test_bump_fee_add_input_change_dust() { assert_eq!(tx.input.len(), 1); assert_eq!(tx.output.len(), 2); let txid = tx.compute_txid(); - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); // We set a fee high enough that during rbf we are forced to add @@ -2396,9 +2369,7 @@ fn test_bump_fee_force_add_input() { for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature } - wallet - .insert_tx(tx.clone(), ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx.clone()); // the new fee_rate is low enough that just reducing the change would be fine, but we force // the addition of an extra input with `add_utxo()` let mut builder = wallet.build_fee_bump(txid).unwrap(); @@ -2463,9 +2434,7 @@ fn test_bump_fee_absolute_force_add_input() { for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature } - wallet - .insert_tx(tx.clone(), ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx.clone()); // the new fee_rate is low enough that just reducing the change would be fine, but we force // the addition of an extra input with `add_utxo()` @@ -2542,9 +2511,7 @@ fn test_bump_fee_unconfirmed_inputs_only() { for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature } - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder.fee_rate(FeeRate::from_sat_per_vb_unchecked(25)); builder.finish().unwrap(); @@ -2575,9 +2542,7 @@ fn test_bump_fee_unconfirmed_input() { for txin in &mut tx.input { txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature } - wallet - .insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 }) - .unwrap(); + wallet.insert_tx(tx); let mut builder = wallet.build_fee_bump(txid).unwrap(); builder @@ -3774,15 +3739,16 @@ fn test_spend_coinbase() { value: Amount::from_sat(25_000), }], }; - wallet - .insert_tx( - coinbase_tx, - ConfirmationTime::Confirmed { - height: confirmation_height, - time: 30_000, - }, - ) - .unwrap(); + let txid = coinbase_tx.compute_txid(); + wallet.insert_tx(coinbase_tx); + insert_anchor_from_conf( + &mut wallet, + txid, + ConfirmationTime::Confirmed { + height: confirmation_height, + time: 30_000, + }, + ); let not_yet_mature_time = confirmation_height + COINBASE_MATURITY - 1; let maturity_time = confirmation_height + COINBASE_MATURITY;