]> Untitled Git - bdk/commitdiff
feat(chain,wallet)!: rm `ConfirmationTime`
author志宇 <hello@evanlinjin.me>
Tue, 8 Oct 2024 09:15:55 +0000 (09:15 +0000)
committer志宇 <hello@evanlinjin.me>
Tue, 8 Oct 2024 09:15:55 +0000 (09:15 +0000)
We rm `ConfirmationTime` because it is essentially the same thing as
`ChainPosition<ConfirmationBlockTime>` without the block hash.

We also impl `serde::Deserialize` and `serde::Serialize` for
`ChainPosition`.

crates/chain/src/chain_data.rs
crates/wallet/src/types.rs
crates/wallet/src/wallet/coin_selection.rs
crates/wallet/src/wallet/mod.rs
crates/wallet/src/wallet/tx_builder.rs
crates/wallet/tests/common.rs
crates/wallet/tests/wallet.rs

index ce6076c515adbf7ecc1b1f284efb6b6509094759..e0202e1afd92267b443555e9efb1a92101b6b4db 100644 (file)
@@ -1,4 +1,3 @@
-use crate::ConfirmationBlockTime;
 use bitcoin::{OutPoint, TxOut, Txid};
 
 use crate::{Anchor, COINBASE_MATURITY};
@@ -7,6 +6,14 @@ use crate::{Anchor, COINBASE_MATURITY};
 ///
 /// The generic `A` should be a [`Anchor`] implementation.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, core::hash::Hash)]
+#[cfg_attr(
+    feature = "serde",
+    derive(serde::Deserialize, serde::Serialize),
+    serde(bound(
+        deserialize = "A: Ord + serde::Deserialize<'de>",
+        serialize = "A: Ord + serde::Serialize",
+    ))
+)]
 pub enum ChainPosition<A> {
     /// The chain data is seen as confirmed, and in anchored by `A`.
     Confirmed(A),
@@ -41,48 +48,6 @@ impl<A: Anchor> ChainPosition<A> {
     }
 }
 
-/// Block height and timestamp at which a transaction is confirmed.
-#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub enum ConfirmationTime {
-    /// The transaction is confirmed
-    Confirmed {
-        /// Confirmation height.
-        height: u32,
-        /// Confirmation time in unix seconds.
-        time: u64,
-    },
-    /// The transaction is unconfirmed
-    Unconfirmed {
-        /// The last-seen timestamp in unix seconds.
-        last_seen: u64,
-    },
-}
-
-impl ConfirmationTime {
-    /// Construct an unconfirmed variant using the given `last_seen` time in unix seconds.
-    pub fn unconfirmed(last_seen: u64) -> Self {
-        Self::Unconfirmed { last_seen }
-    }
-
-    /// Returns whether [`ConfirmationTime`] is the confirmed variant.
-    pub fn is_confirmed(&self) -> bool {
-        matches!(self, Self::Confirmed { .. })
-    }
-}
-
-impl From<ChainPosition<ConfirmationBlockTime>> for ConfirmationTime {
-    fn from(observed_as: ChainPosition<ConfirmationBlockTime>) -> Self {
-        match observed_as {
-            ChainPosition::Confirmed(a) => Self::Confirmed {
-                height: a.block_id.height,
-                time: a.confirmation_time,
-            },
-            ChainPosition::Unconfirmed(last_seen) => Self::Unconfirmed { last_seen },
-        }
-    }
-}
-
 /// A `TxOut` with as much data as we can retrieve about it
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
 pub struct FullTxOut<A> {
@@ -159,6 +124,8 @@ impl<A: Anchor> FullTxOut<A> {
 
 #[cfg(test)]
 mod test {
+    use bdk_core::ConfirmationBlockTime;
+
     use crate::BlockId;
 
     use super::*;
index 6ed17b5757675b09bf0ba82446966e7d4838654b..c4626fbf30ed5497674cb61d0d7c0347320e8bf8 100644 (file)
@@ -10,9 +10,9 @@
 // licenses.
 
 use alloc::boxed::Box;
+use chain::{ChainPosition, ConfirmationBlockTime};
 use core::convert::AsRef;
 
-use bdk_chain::ConfirmationTime;
 use bitcoin::transaction::{OutPoint, Sequence, TxOut};
 use bitcoin::{psbt, Weight};
 
@@ -61,8 +61,8 @@ pub struct LocalOutput {
     pub is_spent: bool,
     /// The derivation index for the script pubkey in the wallet
     pub derivation_index: u32,
-    /// The confirmation time for transaction containing this utxo
-    pub confirmation_time: ConfirmationTime,
+    /// The position of the output in the blockchain.
+    pub chain_position: ChainPosition<ConfirmationBlockTime>,
 }
 
 /// A [`Utxo`] with its `satisfaction_weight`.
index 381ff65b245896b99a5aeea85a040fb47be229de..0f0e4a88e0ebdfb3feac576b85adbdc89f7bc0ca 100644 (file)
@@ -278,7 +278,7 @@ impl CoinSelectionAlgorithm for OldestFirstCoinSelection {
         // For utxo that doesn't exist in DB, they will have lowest priority to be selected
         let utxos = {
             optional_utxos.sort_unstable_by_key(|wu| match &wu.utxo {
-                Utxo::Local(local) => Some(local.confirmation_time),
+                Utxo::Local(local) => Some(local.chain_position),
                 Utxo::Foreign { .. } => None,
             });
 
@@ -733,11 +733,12 @@ where
 #[cfg(test)]
 mod test {
     use assert_matches::assert_matches;
+    use bitcoin::hashes::Hash;
+    use chain::{BlockId, ChainPosition, ConfirmationBlockTime};
     use core::str::FromStr;
     use rand::rngs::StdRng;
 
-    use bdk_chain::ConfirmationTime;
-    use bitcoin::{Amount, ScriptBuf, TxIn, TxOut};
+    use bitcoin::{Amount, BlockHash, ScriptBuf, TxIn, TxOut};
 
     use super::*;
     use crate::types::*;
@@ -752,7 +753,34 @@ mod test {
 
     const FEE_AMOUNT: u64 = 50;
 
-    fn utxo(value: u64, index: u32, confirmation_time: ConfirmationTime) -> WeightedUtxo {
+    fn unconfirmed_utxo(value: u64, index: u32, last_seen: u64) -> WeightedUtxo {
+        utxo(value, index, ChainPosition::Unconfirmed(last_seen))
+    }
+
+    fn confirmed_utxo(
+        value: u64,
+        index: u32,
+        confirmation_height: u32,
+        confirmation_time: u64,
+    ) -> WeightedUtxo {
+        utxo(
+            value,
+            index,
+            ChainPosition::Confirmed(ConfirmationBlockTime {
+                block_id: chain::BlockId {
+                    height: confirmation_height,
+                    hash: bitcoin::BlockHash::all_zeros(),
+                },
+                confirmation_time,
+            }),
+        )
+    }
+
+    fn utxo(
+        value: u64,
+        index: u32,
+        chain_position: ChainPosition<ConfirmationBlockTime>,
+    ) -> WeightedUtxo {
         assert!(index < 10);
         let outpoint = OutPoint::from_str(&format!(
             "000000000000000000000000000000000000000000000000000000000000000{}:0",
@@ -770,49 +798,24 @@ mod test {
                 keychain: KeychainKind::External,
                 is_spent: false,
                 derivation_index: 42,
-                confirmation_time,
+                chain_position,
             }),
         }
     }
 
     fn get_test_utxos() -> Vec<WeightedUtxo> {
         vec![
-            utxo(100_000, 0, ConfirmationTime::Unconfirmed { last_seen: 0 }),
-            utxo(
-                FEE_AMOUNT - 40,
-                1,
-                ConfirmationTime::Unconfirmed { last_seen: 0 },
-            ),
-            utxo(200_000, 2, ConfirmationTime::Unconfirmed { last_seen: 0 }),
+            unconfirmed_utxo(100_000, 0, 0),
+            unconfirmed_utxo(FEE_AMOUNT - 40, 1, 0),
+            unconfirmed_utxo(200_000, 2, 0),
         ]
     }
 
     fn get_oldest_first_test_utxos() -> Vec<WeightedUtxo> {
         // ensure utxos are from different tx
-        let utxo1 = utxo(
-            120_000,
-            1,
-            ConfirmationTime::Confirmed {
-                height: 1,
-                time: 1231006505,
-            },
-        );
-        let utxo2 = utxo(
-            80_000,
-            2,
-            ConfirmationTime::Confirmed {
-                height: 2,
-                time: 1231006505,
-            },
-        );
-        let utxo3 = utxo(
-            300_000,
-            3,
-            ConfirmationTime::Confirmed {
-                height: 3,
-                time: 1231006505,
-            },
-        );
+        let utxo1 = confirmed_utxo(120_000, 1, 1, 1231006505);
+        let utxo2 = confirmed_utxo(80_000, 2, 2, 1231006505);
+        let utxo3 = confirmed_utxo(300_000, 3, 3, 1231006505);
         vec![utxo1, utxo2, utxo3]
     }
 
@@ -834,13 +837,16 @@ mod test {
                     keychain: KeychainKind::External,
                     is_spent: false,
                     derivation_index: rng.next_u32(),
-                    confirmation_time: if rng.gen_bool(0.5) {
-                        ConfirmationTime::Confirmed {
-                            height: rng.next_u32(),
-                            time: rng.next_u64(),
-                        }
+                    chain_position: if rng.gen_bool(0.5) {
+                        ChainPosition::Confirmed(ConfirmationBlockTime {
+                            block_id: chain::BlockId {
+                                height: rng.next_u32(),
+                                hash: BlockHash::all_zeros(),
+                            },
+                            confirmation_time: rng.next_u64(),
+                        })
                     } else {
-                        ConfirmationTime::Unconfirmed { last_seen: 0 }
+                        ChainPosition::Unconfirmed(0)
                     },
                 }),
             });
@@ -865,7 +871,7 @@ mod test {
                     keychain: KeychainKind::External,
                     is_spent: false,
                     derivation_index: 42,
-                    confirmation_time: ConfirmationTime::Unconfirmed { last_seen: 0 },
+                    chain_position: ChainPosition::Unconfirmed(0),
                 }),
             })
             .collect()
@@ -1222,7 +1228,7 @@ mod test {
         optional.push(utxo(
             500_000,
             3,
-            ConfirmationTime::Unconfirmed { last_seen: 0 },
+            ChainPosition::<ConfirmationBlockTime>::Unconfirmed(0),
         ));
 
         // Defensive assertions, for sanity and in case someone changes the test utxos vector.
@@ -1584,10 +1590,13 @@ mod test {
                     keychain: KeychainKind::External,
                     is_spent: false,
                     derivation_index: 0,
-                    confirmation_time: ConfirmationTime::Confirmed {
-                        height: 12345,
-                        time: 12345,
-                    },
+                    chain_position: ChainPosition::Confirmed(ConfirmationBlockTime {
+                        block_id: BlockId {
+                            height: 12345,
+                            hash: BlockHash::all_zeros(),
+                        },
+                        confirmation_time: 12345,
+                    }),
                 }),
             }
         }
index 19604f4416209eb5189405cb7b7588396dae621c..562e684c1994773eeb3d636739c63b54d91574ec 100644 (file)
@@ -34,8 +34,8 @@ use bdk_chain::{
         SyncResult,
     },
     tx_graph::{CanonicalTx, TxGraph, TxNode, TxUpdate},
-    BlockId, ChainPosition, ConfirmationBlockTime, ConfirmationTime, DescriptorExt, FullTxOut,
-    Indexed, IndexedTxGraph, Merge,
+    BlockId, ChainPosition, ConfirmationBlockTime, DescriptorExt, FullTxOut, Indexed,
+    IndexedTxGraph, Merge,
 };
 use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
 use bitcoin::{
@@ -1660,11 +1660,10 @@ impl Wallet {
                     .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?;
                 let txout = &prev_tx.output[txin.previous_output.vout as usize];
 
-                let confirmation_time: ConfirmationTime = graph
+                let chain_position = graph
                     .get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
                     .ok_or(BuildFeeBumpError::UnknownUtxo(txin.previous_output))?
-                    .cloned()
-                    .into();
+                    .cloned();
 
                 let weighted_utxo = match txout_index.index_of_spk(txout.script_pubkey.clone()) {
                     Some(&(keychain, derivation_index)) => {
@@ -1679,7 +1678,7 @@ impl Wallet {
                                 keychain,
                                 is_spent: true,
                                 derivation_index,
-                                confirmation_time,
+                                chain_position,
                             }),
                             satisfaction_weight,
                         }
@@ -2051,33 +2050,33 @@ impl Wallet {
                     Some(tx) => tx,
                     None => return false,
                 };
-                let confirmation_time: ConfirmationTime = match self
-                    .indexed_graph
-                    .graph()
-                    .get_chain_position(&self.chain, chain_tip, txid)
-                {
-                    Some(chain_position) => chain_position.cloned().into(),
+                let chain_position = match self.indexed_graph.graph().get_chain_position(
+                    &self.chain,
+                    chain_tip,
+                    txid,
+                {
+                    Some(chain_position) => chain_position.cloned(),
                     None => return false,
                 };
 
                 // Whether the UTXO is mature and, if needed, confirmed
                 let mut spendable = true;
-                if must_only_use_confirmed_tx && !confirmation_time.is_confirmed() {
+                if must_only_use_confirmed_tx && !chain_position.is_confirmed() {
                     return false;
                 }
                 if tx.is_coinbase() {
                     debug_assert!(
-                        confirmation_time.is_confirmed(),
+                        chain_position.is_confirmed(),
                         "coinbase must always be confirmed"
                     );
                     if let Some(current_height) = current_height {
-                        match confirmation_time {
-                            ConfirmationTime::Confirmed { height, .. } => {
+                        match chain_position {
+                            ChainPosition::Confirmed(a) => {
                                 // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
-                                spendable &=
-                                    (current_height.saturating_sub(height)) >= COINBASE_MATURITY;
+                                spendable &= (current_height.saturating_sub(a.block_id.height))
+                                    >= COINBASE_MATURITY;
                             }
-                            ConfirmationTime::Unconfirmed { .. } => spendable = false,
+                            ChainPosition::Unconfirmed { .. } => spendable = false,
                         }
                     }
                 }
@@ -2546,7 +2545,7 @@ fn new_local_utxo(
         outpoint: full_txo.outpoint,
         txout: full_txo.txout,
         is_spent: full_txo.spent_by.is_some(),
-        confirmation_time: full_txo.chain_position.into(),
+        chain_position: full_txo.chain_position,
         keychain,
         derivation_index,
     }
index c3dde73305981c894e6f9e62bee23afae2af8b7d..343734a8df35b26559eb7743931e0bb208b0524d 100644 (file)
@@ -858,7 +858,6 @@ mod test {
         };
     }
 
-    use bdk_chain::ConfirmationTime;
     use bitcoin::consensus::deserialize;
     use bitcoin::hex::FromHex;
     use bitcoin::TxOut;
@@ -1018,7 +1017,7 @@ mod test {
                 txout: TxOut::NULL,
                 keychain: KeychainKind::External,
                 is_spent: false,
-                confirmation_time: ConfirmationTime::Unconfirmed { last_seen: 0 },
+                chain_position: chain::ChainPosition::Unconfirmed(0),
                 derivation_index: 0,
             },
             LocalOutput {
@@ -1029,10 +1028,13 @@ mod test {
                 txout: TxOut::NULL,
                 keychain: KeychainKind::Internal,
                 is_spent: false,
-                confirmation_time: ConfirmationTime::Confirmed {
-                    height: 32,
-                    time: 42,
-                },
+                chain_position: chain::ChainPosition::Confirmed(chain::ConfirmationBlockTime {
+                    block_id: chain::BlockId {
+                        height: 32,
+                        hash: bitcoin::BlockHash::all_zeros(),
+                    },
+                    confirmation_time: 42,
+                }),
                 derivation_index: 1,
             },
         ]
index 375d680d17620b844b6747a67e80276ae0c53760..a2870c807d48054ee9bda595f70a33bb468ff5f9 100644 (file)
@@ -1,5 +1,5 @@
 #![allow(unused)]
-use bdk_chain::{tx_graph, BlockId, ConfirmationBlockTime, ConfirmationTime, TxGraph};
+use bdk_chain::{tx_graph, BlockId, ChainPosition, ConfirmationBlockTime, TxGraph};
 use bdk_wallet::{CreateParams, KeychainKind, LocalOutput, Update, Wallet};
 use bitcoin::{
     hashes::Hash, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, Transaction,
@@ -89,20 +89,26 @@ pub fn get_funded_wallet_with_change(descriptor: &str, change: &str) -> (Wallet,
     insert_anchor_from_conf(
         &mut wallet,
         tx0.compute_txid(),
-        ConfirmationTime::Confirmed {
-            height: 1_000,
-            time: 100,
-        },
+        ChainPosition::Confirmed(ConfirmationBlockTime {
+            block_id: BlockId {
+                height: 1_000,
+                hash: BlockHash::all_zeros(),
+            },
+            confirmation_time: 100,
+        }),
     );
 
     wallet.insert_tx(tx1.clone());
     insert_anchor_from_conf(
         &mut wallet,
         tx1.compute_txid(),
-        ConfirmationTime::Confirmed {
-            height: 2_000,
-            time: 200,
-        },
+        ChainPosition::Confirmed(ConfirmationBlockTime {
+            block_id: BlockId {
+                height: 2_000,
+                hash: BlockHash::all_zeros(),
+            },
+            confirmation_time: 200,
+        }),
     );
 
     (wallet, tx1.compute_txid())
@@ -205,19 +211,12 @@ pub fn feerate_unchecked(sat_vb: f64) -> FeeRate {
 /// 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| ConfirmationBlockTime {
-                block_id: anchor_cp.block_id(),
-                confirmation_time: time,
-            })
-            .expect("confirmation height cannot be greater than tip");
-
+pub fn insert_anchor_from_conf(
+    wallet: &mut Wallet,
+    txid: Txid,
+    position: ChainPosition<ConfirmationBlockTime>,
+) {
+    if let ChainPosition::Confirmed(anchor) = position {
         wallet
             .apply_update(Update {
                 tx_update: tx_graph::TxUpdate {
index 5914ce2897afa8f75bc5ed55f61712e39c43e073..44b194a83d2e3ee48d5ac42249b159d0deb6b90f 100644 (file)
@@ -6,7 +6,7 @@ use std::str::FromStr;
 use anyhow::Context;
 use assert_matches::assert_matches;
 use bdk_chain::{tx_graph, COINBASE_MATURITY};
-use bdk_chain::{BlockId, ConfirmationTime};
+use bdk_chain::{BlockId, ChainPosition, ConfirmationBlockTime};
 use bdk_wallet::coin_selection::{self, LargestFirstCoinSelection};
 use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor};
 use bdk_wallet::error::CreateTxError;
@@ -32,7 +32,11 @@ use rand::SeedableRng;
 mod common;
 use common::*;
 
-fn receive_output(wallet: &mut Wallet, value: u64, height: ConfirmationTime) -> OutPoint {
+fn receive_output(
+    wallet: &mut Wallet,
+    value: u64,
+    height: ChainPosition<ConfirmationBlockTime>,
+) -> OutPoint {
     let addr = wallet.next_unused_address(KeychainKind::External).address;
     receive_output_to_address(wallet, addr, value, height)
 }
@@ -41,7 +45,7 @@ fn receive_output_to_address(
     wallet: &mut Wallet,
     addr: Address,
     value: u64,
-    height: ConfirmationTime,
+    height: ChainPosition<ConfirmationBlockTime>,
 ) -> OutPoint {
     let tx = Transaction {
         version: transaction::Version::ONE,
@@ -57,10 +61,10 @@ fn receive_output_to_address(
     wallet.insert_tx(tx);
 
     match height {
-        ConfirmationTime::Confirmed { .. } => {
+        ChainPosition::Confirmed { .. } => {
             insert_anchor_from_conf(wallet, txid, height);
         }
-        ConfirmationTime::Unconfirmed { last_seen } => {
+        ChainPosition::Unconfirmed(last_seen) => {
             insert_seen_at(wallet, txid, last_seen);
         }
     }
@@ -72,9 +76,12 @@ fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint {
     let latest_cp = wallet.latest_checkpoint();
     let height = latest_cp.height();
     let anchor = if height == 0 {
-        ConfirmationTime::Unconfirmed { last_seen: 0 }
+        ChainPosition::Unconfirmed(0)
     } else {
-        ConfirmationTime::Confirmed { height, time: 0 }
+        ChainPosition::Confirmed(ConfirmationBlockTime {
+            block_id: latest_cp.block_id(),
+            confirmation_time: 0,
+        })
     };
     receive_output(wallet, value, anchor)
 }
@@ -1209,14 +1216,11 @@ fn test_create_tx_add_utxo() {
     };
     let txid = small_output_tx.compute_txid();
     wallet.insert_tx(small_output_tx);
-    insert_anchor_from_conf(
-        &mut wallet,
-        txid,
-        ConfirmationTime::Confirmed {
-            height: 2000,
-            time: 200,
-        },
-    );
+    let chain_position = ChainPosition::Confirmed(ConfirmationBlockTime {
+        block_id: wallet.latest_checkpoint().get(2000).unwrap().block_id(),
+        confirmation_time: 200,
+    });
+    insert_anchor_from_conf(&mut wallet, txid, chain_position);
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
         .unwrap()
@@ -1259,14 +1263,11 @@ fn test_create_tx_manually_selected_insufficient() {
     };
     let txid = small_output_tx.compute_txid();
     wallet.insert_tx(small_output_tx.clone());
-    insert_anchor_from_conf(
-        &mut wallet,
-        txid,
-        ConfirmationTime::Confirmed {
-            height: 2000,
-            time: 200,
-        },
-    );
+    let chain_position = ChainPosition::Confirmed(ConfirmationBlockTime {
+        block_id: wallet.latest_checkpoint().get(2000).unwrap().block_id(),
+        confirmation_time: 200,
+    });
+    insert_anchor_from_conf(&mut wallet, txid, chain_position);
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
         .unwrap()
@@ -1491,7 +1492,7 @@ fn test_create_tx_increment_change_index() {
             .create_wallet_no_persist()
             .unwrap();
         // fund wallet
-        receive_output(&mut wallet, amount, ConfirmationTime::unconfirmed(0));
+        receive_output(&mut wallet, amount, ChainPosition::Unconfirmed(0));
         // create tx
         let mut builder = wallet.build_tx();
         builder.add_recipient(recipient.clone(), Amount::from_sat(test.to_send));
@@ -1822,14 +1823,12 @@ fn test_bump_fee_confirmed_tx() {
     let txid = tx.compute_txid();
 
     wallet.insert_tx(tx);
-    insert_anchor_from_conf(
-        &mut wallet,
-        txid,
-        ConfirmationTime::Confirmed {
-            height: 42,
-            time: 42_000,
-        },
-    );
+
+    let chain_position = ChainPosition::Confirmed(ConfirmationBlockTime {
+        block_id: wallet.latest_checkpoint().get(42).unwrap().block_id(),
+        confirmation_time: 42_000,
+    });
+    insert_anchor_from_conf(&mut wallet, txid, chain_position);
 
     wallet.build_fee_bump(txid).unwrap().finish().unwrap();
 }
@@ -2101,16 +2100,12 @@ fn test_bump_fee_drain_wallet() {
         }],
     };
     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 chain_position = ChainPosition::Confirmed(ConfirmationBlockTime {
+        block_id: wallet.latest_checkpoint().block_id(),
+        confirmation_time: 42_000,
+    });
+    insert_anchor_from_conf(&mut wallet, txid, chain_position);
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
         .unwrap()
@@ -2166,13 +2161,12 @@ fn test_bump_fee_remove_output_manually_selected_only() {
             value: Amount::from_sat(25_000),
         }],
     };
-    let position: ConfirmationTime = wallet
+    let position: ChainPosition<ConfirmationBlockTime> = wallet
         .transactions()
         .last()
         .unwrap()
         .chain_position
-        .cloned()
-        .into();
+        .cloned();
 
     wallet.insert_tx(init_tx.clone());
     insert_anchor_from_conf(&mut wallet, init_tx.compute_txid(), position);
@@ -2220,13 +2214,12 @@ fn test_bump_fee_add_input() {
         }],
     };
     let txid = init_tx.compute_txid();
-    let pos: ConfirmationTime = wallet
+    let pos: ChainPosition<ConfirmationBlockTime> = wallet
         .transactions()
         .last()
         .unwrap()
         .chain_position
-        .cloned()
-        .into();
+        .cloned();
     wallet.insert_tx(init_tx);
     insert_anchor_from_conf(&mut wallet, txid, pos);
 
@@ -2621,11 +2614,7 @@ fn test_bump_fee_unconfirmed_inputs_only() {
     let psbt = builder.finish().unwrap();
     // Now we receive one transaction with 0 confirmations. We won't be able to use that for
     // fee bumping, as it's still unconfirmed!
-    receive_output(
-        &mut wallet,
-        25_000,
-        ConfirmationTime::Unconfirmed { last_seen: 0 },
-    );
+    receive_output(&mut wallet, 25_000, ChainPosition::Unconfirmed(0));
     let mut tx = psbt.extract_tx().expect("failed to extract tx");
     let txid = tx.compute_txid();
     for txin in &mut tx.input {
@@ -2651,7 +2640,7 @@ fn test_bump_fee_unconfirmed_input() {
         .assume_checked();
     // We receive a tx with 0 confirmations, which will be used as an input
     // in the drain tx.
-    receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0));
+    receive_output(&mut wallet, 25_000, ChainPosition::Unconfirmed(0));
     let mut builder = wallet.build_tx();
     builder.drain_wallet().drain_to(addr.script_pubkey());
     let psbt = builder.finish().unwrap();
@@ -3855,12 +3844,11 @@ fn test_spend_coinbase() {
         .unwrap();
 
     let confirmation_height = 5;
-    wallet
-        .insert_checkpoint(BlockId {
-            height: confirmation_height,
-            hash: BlockHash::all_zeros(),
-        })
-        .unwrap();
+    let confirmation_block_id = BlockId {
+        height: confirmation_height,
+        hash: BlockHash::all_zeros(),
+    };
+    wallet.insert_checkpoint(confirmation_block_id).unwrap();
     let coinbase_tx = Transaction {
         version: transaction::Version::ONE,
         lock_time: absolute::LockTime::ZERO,
@@ -3877,14 +3865,11 @@ fn test_spend_coinbase() {
     };
     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 chain_position = ChainPosition::Confirmed(ConfirmationBlockTime {
+        block_id: confirmation_block_id,
+        confirmation_time: 30_000,
+    });
+    insert_anchor_from_conf(&mut wallet, txid, chain_position);
 
     let not_yet_mature_time = confirmation_height + COINBASE_MATURITY - 1;
     let maturity_time = confirmation_height + COINBASE_MATURITY;
@@ -4126,15 +4111,14 @@ fn test_keychains_with_overlapping_spks() {
         .last()
         .unwrap()
         .address;
-    let _outpoint = receive_output_to_address(
-        &mut wallet,
-        addr,
-        8000,
-        ConfirmationTime::Confirmed {
-            height: 2000,
-            time: 0,
+    let chain_position = ChainPosition::Confirmed(ConfirmationBlockTime {
+        block_id: BlockId {
+            height: 8000,
+            hash: BlockHash::all_zeros(),
         },
-    );
+        confirmation_time: 0,
+    });
+    let _outpoint = receive_output_to_address(&mut wallet, addr, 8000, chain_position);
     assert_eq!(wallet.balance().confirmed, Amount::from_sat(58000));
 }
 
@@ -4266,11 +4250,7 @@ fn single_descriptor_wallet_can_create_tx_and_receive_change() {
         .unwrap();
     assert_eq!(wallet.keychains().count(), 1);
     let amt = Amount::from_sat(5_000);
-    receive_output(
-        &mut wallet,
-        2 * amt.to_sat(),
-        ConfirmationTime::Unconfirmed { last_seen: 2 },
-    );
+    receive_output(&mut wallet, 2 * amt.to_sat(), ChainPosition::Unconfirmed(2));
     // create spend tx that produces a change output
     let addr = Address::from_str("bcrt1qc6fweuf4xjvz4x3gx3t9e0fh4hvqyu2qw4wvxm")
         .unwrap()
@@ -4297,7 +4277,7 @@ fn single_descriptor_wallet_can_create_tx_and_receive_change() {
 #[test]
 fn test_transactions_sort_by() {
     let (mut wallet, _txid) = get_funded_wallet_wpkh();
-    receive_output(&mut wallet, 25_000, ConfirmationTime::unconfirmed(0));
+    receive_output(&mut wallet, 25_000, ChainPosition::Unconfirmed(0));
 
     // sort by chain position, unconfirmed then confirmed by descending block height
     let sorted_txs: Vec<WalletTx> =