From 180ee9f09c7f18e695734645a7b65678d98e747c Mon Sep 17 00:00:00 2001 From: Wei Chen Date: Mon, 1 Jul 2024 13:40:26 +0800 Subject: [PATCH] feat(chain)!: Implement `ConfirmationBlockTime` Both `bdk_electrum` and `bdk_esplora` now report the exact block that the transaction is in, which removes the need for having the old `ConfirmationTimeHeightAnchor` and `ConfirmationHeightAnchor`. This PR introduces a new, simpler anchor type that can be modified to support additional data in the future. --- crates/chain/src/chain_data.rs | 85 +++++-------------- crates/chain/src/spk_client.rs | 8 +- crates/chain/src/tx_data_traits.rs | 37 ++------ crates/chain/tests/test_indexed_tx_graph.rs | 15 ++-- crates/chain/tests/test_tx_graph.rs | 31 ++++--- crates/electrum/src/bdk_electrum_client.rs | 19 ++--- crates/electrum/tests/test_electrum.rs | 8 +- crates/esplora/src/async_ext.rs | 8 +- crates/esplora/src/blocking_ext.rs | 8 +- crates/esplora/src/lib.rs | 9 +- crates/sqlite/src/store.rs | 33 ++----- crates/wallet/src/wallet/export.rs | 8 +- crates/wallet/src/wallet/mod.rs | 38 +++++---- crates/wallet/tests/common.rs | 6 ++ .../example_bitcoind_rpc_polling/src/main.rs | 4 +- example-crates/example_electrum/src/main.rs | 13 +-- example-crates/example_esplora/src/main.rs | 4 +- 17 files changed, 132 insertions(+), 202 deletions(-) diff --git a/crates/chain/src/chain_data.rs b/crates/chain/src/chain_data.rs index ae0976de57..853508bae9 100644 --- a/crates/chain/src/chain_data.rs +++ b/crates/chain/src/chain_data.rs @@ -74,11 +74,11 @@ impl ConfirmationTime { } } -impl From> for ConfirmationTime { - fn from(observed_as: ChainPosition) -> Self { +impl From> for ConfirmationTime { + fn from(observed_as: ChainPosition) -> Self { match observed_as { ChainPosition::Confirmed(a) => Self::Confirmed { - height: a.confirmation_height, + height: a.block_id.height, time: a.confirmation_time, }, ChainPosition::Unconfirmed(last_seen) => Self::Unconfirmed { last_seen }, @@ -145,7 +145,7 @@ impl From<(&u32, &BlockHash)> for BlockId { } } -/// An [`Anchor`] implementation that also records the exact confirmation height of the transaction. +/// An [`Anchor`] implementation that also records the exact confirmation time of the transaction. /// /// Note that the confirmation block and the anchor block can be different here. /// @@ -156,70 +156,27 @@ impl From<(&u32, &BlockHash)> for BlockId { derive(serde::Deserialize, serde::Serialize), serde(crate = "serde_crate") )] -pub struct ConfirmationHeightAnchor { - /// The exact confirmation height of the transaction. - /// - /// It is assumed that this value is never larger than the height of the anchor block. - pub confirmation_height: u32, +pub struct ConfirmationBlockTime { /// The anchor block. - pub anchor_block: BlockId, -} - -impl Anchor for ConfirmationHeightAnchor { - fn anchor_block(&self) -> BlockId { - self.anchor_block - } - - fn confirmation_height_upper_bound(&self) -> u32 { - self.confirmation_height - } -} - -impl AnchorFromBlockPosition for ConfirmationHeightAnchor { - fn from_block_position(_block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self { - Self { - anchor_block: block_id, - confirmation_height: block_id.height, - } - } -} - -/// An [`Anchor`] implementation that also records the exact confirmation time and height of the -/// transaction. -/// -/// Note that the confirmation block and the anchor block can be different here. -/// -/// Refer to [`Anchor`] for more details. -#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)] -#[cfg_attr( - feature = "serde", - derive(serde::Deserialize, serde::Serialize), - serde(crate = "serde_crate") -)] -pub struct ConfirmationTimeHeightAnchor { - /// The confirmation height of the transaction being anchored. - pub confirmation_height: u32, + pub block_id: BlockId, /// The confirmation time of the transaction being anchored. pub confirmation_time: u64, - /// The anchor block. - pub anchor_block: BlockId, } -impl Anchor for ConfirmationTimeHeightAnchor { +impl Anchor for ConfirmationBlockTime { fn anchor_block(&self) -> BlockId { - self.anchor_block + self.block_id } fn confirmation_height_upper_bound(&self) -> u32 { - self.confirmation_height + self.block_id.height } } -impl AnchorFromBlockPosition for ConfirmationTimeHeightAnchor { +impl AnchorFromBlockPosition for ConfirmationBlockTime { fn from_block_position(block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self { Self { - anchor_block: block_id, - confirmation_height: block_id.height, + block_id, confirmation_time: block.header.time as _, } } @@ -305,19 +262,19 @@ mod test { #[test] fn chain_position_ord() { - let unconf1 = ChainPosition::::Unconfirmed(10); - let unconf2 = ChainPosition::::Unconfirmed(20); - let conf1 = ChainPosition::Confirmed(ConfirmationHeightAnchor { - confirmation_height: 9, - anchor_block: BlockId { - height: 20, + let unconf1 = ChainPosition::::Unconfirmed(10); + let unconf2 = ChainPosition::::Unconfirmed(20); + let conf1 = ChainPosition::Confirmed(ConfirmationBlockTime { + confirmation_time: 20, + block_id: BlockId { + height: 9, ..Default::default() }, }); - let conf2 = ChainPosition::Confirmed(ConfirmationHeightAnchor { - confirmation_height: 12, - anchor_block: BlockId { - height: 15, + let conf2 = ChainPosition::Confirmed(ConfirmationBlockTime { + confirmation_time: 15, + block_id: BlockId { + height: 12, ..Default::default() }, }); diff --git a/crates/chain/src/spk_client.rs b/crates/chain/src/spk_client.rs index 24bd9b4a7f..839c46f83d 100644 --- a/crates/chain/src/spk_client.rs +++ b/crates/chain/src/spk_client.rs @@ -1,8 +1,8 @@ //! Helper types for spk-based blockchain clients. use crate::{ - collections::BTreeMap, keychain::Indexed, local_chain::CheckPoint, - ConfirmationTimeHeightAnchor, TxGraph, + collections::BTreeMap, keychain::Indexed, local_chain::CheckPoint, ConfirmationBlockTime, + TxGraph, }; use alloc::boxed::Box; use bitcoin::{OutPoint, Script, ScriptBuf, Txid}; @@ -177,7 +177,7 @@ impl SyncRequest { /// Data returned from a spk-based blockchain client sync. /// /// See also [`SyncRequest`]. -pub struct SyncResult { +pub struct SyncResult { /// The update to apply to the receiving [`TxGraph`]. pub graph_update: TxGraph, /// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain). @@ -318,7 +318,7 @@ impl FullScanRequest { /// Data returned from a spk-based blockchain client full scan. /// /// See also [`FullScanRequest`]. -pub struct FullScanResult { +pub struct FullScanResult { /// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain). pub graph_update: TxGraph, /// The update to apply to the receiving [`TxGraph`]. diff --git a/crates/chain/src/tx_data_traits.rs b/crates/chain/src/tx_data_traits.rs index 7944f95c06..2971f3899e 100644 --- a/crates/chain/src/tx_data_traits.rs +++ b/crates/chain/src/tx_data_traits.rs @@ -20,8 +20,7 @@ use alloc::vec::Vec; /// # use bdk_chain::local_chain::LocalChain; /// # use bdk_chain::tx_graph::TxGraph; /// # use bdk_chain::BlockId; -/// # use bdk_chain::ConfirmationHeightAnchor; -/// # use bdk_chain::ConfirmationTimeHeightAnchor; +/// # use bdk_chain::ConfirmationBlockTime; /// # use bdk_chain::example_utils::*; /// # use bitcoin::hashes::Hash; /// // Initialize the local chain with two blocks. @@ -50,39 +49,19 @@ use alloc::vec::Vec; /// }, /// ); /// -/// // Insert `tx` into a `TxGraph` that uses `ConfirmationHeightAnchor` as the anchor type. -/// // This anchor records the anchor block and the confirmation height of the transaction. -/// // When a transaction is anchored with `ConfirmationHeightAnchor`, the anchor block and -/// // confirmation block can be different. However, the confirmation block cannot be higher than -/// // the anchor block and both blocks must be in the same chain for the anchor to be valid. -/// let mut graph_b = TxGraph::::default(); -/// let _ = graph_b.insert_tx(tx.clone()); -/// graph_b.insert_anchor( -/// tx.compute_txid(), -/// ConfirmationHeightAnchor { -/// anchor_block: BlockId { -/// height: 2, -/// hash: Hash::hash("second".as_bytes()), -/// }, -/// confirmation_height: 1, -/// }, -/// ); -/// -/// // Insert `tx` into a `TxGraph` that uses `ConfirmationTimeHeightAnchor` as the anchor type. -/// // This anchor records the anchor block, the confirmation height and time of the transaction. -/// // When a transaction is anchored with `ConfirmationTimeHeightAnchor`, the anchor block and -/// // confirmation block can be different. However, the confirmation block cannot be higher than -/// // the anchor block and both blocks must be in the same chain for the anchor to be valid. -/// let mut graph_c = TxGraph::::default(); +/// // Insert `tx` into a `TxGraph` that uses `ConfirmationBlockTime` as the anchor type. +/// // This anchor records the anchor block and the confirmation time of the transaction. When a +/// // transaction is anchored with `ConfirmationBlockTime`, the anchor block and confirmation block +/// // of the transaction is the same block. +/// let mut graph_c = TxGraph::::default(); /// let _ = graph_c.insert_tx(tx.clone()); /// graph_c.insert_anchor( /// tx.compute_txid(), -/// ConfirmationTimeHeightAnchor { -/// anchor_block: BlockId { +/// ConfirmationBlockTime { +/// block_id: BlockId { /// height: 2, /// hash: Hash::hash("third".as_bytes()), /// }, -/// confirmation_height: 1, /// confirmation_time: 123, /// }, /// ); diff --git a/crates/chain/tests/test_indexed_tx_graph.rs b/crates/chain/tests/test_indexed_tx_graph.rs index 4266e184a3..5db43dca9d 100644 --- a/crates/chain/tests/test_indexed_tx_graph.rs +++ b/crates/chain/tests/test_indexed_tx_graph.rs @@ -10,7 +10,7 @@ use bdk_chain::{ indexed_tx_graph::{self, IndexedTxGraph}, keychain::{self, Balance, KeychainTxOutIndex}, local_chain::LocalChain, - tx_graph, Append, ChainPosition, ConfirmationHeightAnchor, DescriptorExt, + tx_graph, Append, ChainPosition, ConfirmationBlockTime, DescriptorExt, }; use bitcoin::{ secp256k1::Secp256k1, Amount, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut, @@ -31,7 +31,7 @@ fn insert_relevant_txs() { let spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey(); let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey(); - let mut graph = IndexedTxGraph::>::new( + let mut graph = IndexedTxGraph::>::new( KeychainTxOutIndex::new(10), ); let _ = graph @@ -139,7 +139,7 @@ fn test_list_owned_txouts() { let (desc_2, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), common::DESCRIPTORS[3]).unwrap(); - let mut graph = IndexedTxGraph::>::new( + let mut graph = IndexedTxGraph::>::new( KeychainTxOutIndex::new(10), ); @@ -249,9 +249,9 @@ fn test_list_owned_txouts() { local_chain .get(height) .map(|cp| cp.block_id()) - .map(|anchor_block| ConfirmationHeightAnchor { - anchor_block, - confirmation_height: anchor_block.height, + .map(|block_id| ConfirmationBlockTime { + block_id, + confirmation_time: 100, }), ) })); @@ -260,8 +260,7 @@ fn test_list_owned_txouts() { // A helper lambda to extract and filter data from the graph. let fetch = - |height: u32, - graph: &IndexedTxGraph>| { + |height: u32, graph: &IndexedTxGraph>| { let chain_tip = local_chain .get(height) .map(|cp| cp.block_id()) diff --git a/crates/chain/tests/test_tx_graph.rs b/crates/chain/tests/test_tx_graph.rs index 21b955d212..274790ee9f 100644 --- a/crates/chain/tests/test_tx_graph.rs +++ b/crates/chain/tests/test_tx_graph.rs @@ -7,7 +7,7 @@ use bdk_chain::{ collections::*, local_chain::LocalChain, tx_graph::{ChangeSet, TxGraph}, - Anchor, Append, BlockId, ChainOracle, ChainPosition, ConfirmationHeightAnchor, + Anchor, Append, BlockId, ChainOracle, ChainPosition, ConfirmationBlockTime, }; use bitcoin::{ absolute, hashes::Hash, transaction, Amount, BlockHash, OutPoint, ScriptBuf, SignedAmount, @@ -935,7 +935,7 @@ fn test_chain_spends() { ..common::new_tx(0) }; - let mut graph = TxGraph::::default(); + let mut graph = TxGraph::::default(); let _ = graph.insert_tx(tx_0.clone()); let _ = graph.insert_tx(tx_1.clone()); @@ -944,9 +944,12 @@ fn test_chain_spends() { for (ht, tx) in [(95, &tx_0), (98, &tx_1)] { let _ = graph.insert_anchor( tx.compute_txid(), - ConfirmationHeightAnchor { - anchor_block: tip.block_id(), - confirmation_height: ht, + ConfirmationBlockTime { + block_id: BlockId { + hash: tip.get(ht).unwrap().hash(), + height: ht, + }, + confirmation_time: 100, }, ); } @@ -959,9 +962,12 @@ fn test_chain_spends() { OutPoint::new(tx_0.compute_txid(), 0) ), Some(( - ChainPosition::Confirmed(&ConfirmationHeightAnchor { - anchor_block: tip.block_id(), - confirmation_height: 98 + ChainPosition::Confirmed(&ConfirmationBlockTime { + block_id: BlockId { + hash: tip.get(98).unwrap().hash(), + height: 98, + }, + confirmation_time: 100 }), tx_1.compute_txid(), )), @@ -971,9 +977,12 @@ fn test_chain_spends() { assert_eq!( graph.get_chain_position(&local_chain, tip.block_id(), tx_0.compute_txid()), // Some(ObservedAs::Confirmed(&local_chain.get_block(95).expect("block expected"))), - Some(ChainPosition::Confirmed(&ConfirmationHeightAnchor { - anchor_block: tip.block_id(), - confirmation_height: 95 + Some(ChainPosition::Confirmed(&ConfirmationBlockTime { + block_id: BlockId { + hash: tip.get(95).unwrap().hash(), + height: 95, + }, + confirmation_time: 100 })) ); diff --git a/crates/electrum/src/bdk_electrum_client.rs b/crates/electrum/src/bdk_electrum_client.rs index f333d3c1fd..55cb2aadb7 100644 --- a/crates/electrum/src/bdk_electrum_client.rs +++ b/crates/electrum/src/bdk_electrum_client.rs @@ -4,7 +4,7 @@ use bdk_chain::{ local_chain::CheckPoint, spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult}, tx_graph::TxGraph, - Anchor, BlockId, ConfirmationTimeHeightAnchor, + Anchor, BlockId, ConfirmationBlockTime, }; use electrum_client::{ElectrumApi, Error, HeaderNotification}; use std::{ @@ -123,7 +123,7 @@ impl BdkElectrumClient { ) -> Result, Error> { let (tip, latest_blocks) = fetch_tip_and_latest_blocks(&self.inner, request.chain_tip.clone())?; - let mut graph_update = TxGraph::::default(); + let mut graph_update = TxGraph::::default(); let mut last_active_indices = BTreeMap::::new(); for (keychain, keychain_spks) in request.spks_by_keychain { @@ -203,7 +203,7 @@ impl BdkElectrumClient { /// Checkpoints (in `cps`) are used to create anchors. The `tx_cache` is self-explanatory. fn populate_with_spks( &self, - graph_update: &mut TxGraph, + graph_update: &mut TxGraph, mut spks: impl Iterator, stop_gap: usize, batch_size: usize, @@ -251,7 +251,7 @@ impl BdkElectrumClient { /// Checkpoints (in `cps`) are used to create anchors. The `tx_cache` is self-explanatory. fn populate_with_outpoints( &self, - graph_update: &mut TxGraph, + graph_update: &mut TxGraph, outpoints: impl IntoIterator, ) -> Result<(), Error> { for outpoint in outpoints { @@ -299,7 +299,7 @@ impl BdkElectrumClient { /// Populate the `graph_update` with transactions/anchors of the provided `txids`. fn populate_with_txids( &self, - graph_update: &mut TxGraph, + graph_update: &mut TxGraph, txids: impl IntoIterator, ) -> Result<(), Error> { for txid in txids { @@ -335,7 +335,7 @@ impl BdkElectrumClient { // An anchor is inserted if the transaction is validated to be in a confirmed block. fn validate_merkle_for_anchor( &self, - graph_update: &mut TxGraph, + graph_update: &mut TxGraph, txid: Txid, confirmation_height: i32, ) -> Result<(), Error> { @@ -364,10 +364,9 @@ impl BdkElectrumClient { if is_confirmed_tx { let _ = graph_update.insert_anchor( txid, - ConfirmationTimeHeightAnchor { - confirmation_height: merkle_res.block_height as u32, + ConfirmationBlockTime { confirmation_time: header.time as u64, - anchor_block: BlockId { + block_id: BlockId { height: merkle_res.block_height as u32, hash: header.block_hash(), }, @@ -382,7 +381,7 @@ impl BdkElectrumClient { // which we do not have by default. This data is needed to calculate the transaction fee. fn fetch_prev_txout( &self, - graph_update: &mut TxGraph, + graph_update: &mut TxGraph, ) -> Result<(), Error> { let full_txs: Vec> = graph_update.full_txs().map(|tx_node| tx_node.tx).collect(); diff --git a/crates/electrum/tests/test_electrum.rs b/crates/electrum/tests/test_electrum.rs index 441afe4fc1..6e6ded409f 100644 --- a/crates/electrum/tests/test_electrum.rs +++ b/crates/electrum/tests/test_electrum.rs @@ -3,14 +3,14 @@ use bdk_chain::{ keychain::Balance, local_chain::LocalChain, spk_client::SyncRequest, - ConfirmationTimeHeightAnchor, IndexedTxGraph, SpkTxOutIndex, + ConfirmationBlockTime, IndexedTxGraph, SpkTxOutIndex, }; use bdk_electrum::BdkElectrumClient; use bdk_testenv::{anyhow, bitcoincore_rpc::RpcApi, TestEnv}; fn get_balance( recv_chain: &LocalChain, - recv_graph: &IndexedTxGraph>, + recv_graph: &IndexedTxGraph>, ) -> anyhow::Result { let chain_tip = recv_chain.tip().block_id(); let outpoints = recv_graph.index.outpoints().clone(); @@ -45,7 +45,7 @@ fn scan_detects_confirmed_tx() -> anyhow::Result<()> { // Setup receiver. let (mut recv_chain, _) = LocalChain::from_genesis_hash(env.bitcoind.client.get_block_hash(0)?); - let mut recv_graph = IndexedTxGraph::::new({ + let mut recv_graph = IndexedTxGraph::::new({ let mut recv_index = SpkTxOutIndex::default(); recv_index.insert_spk((), spk_to_track.clone()); recv_index @@ -135,7 +135,7 @@ fn tx_can_become_unconfirmed_after_reorg() -> anyhow::Result<()> { // Setup receiver. let (mut recv_chain, _) = LocalChain::from_genesis_hash(env.bitcoind.client.get_block_hash(0)?); - let mut recv_graph = IndexedTxGraph::::new({ + let mut recv_graph = IndexedTxGraph::::new({ let mut recv_index = SpkTxOutIndex::default(); recv_index.insert_spk((), spk_to_track.clone()); recv_index diff --git a/crates/esplora/src/async_ext.rs b/crates/esplora/src/async_ext.rs index dff52fdd77..cbfadf0f59 100644 --- a/crates/esplora/src/async_ext.rs +++ b/crates/esplora/src/async_ext.rs @@ -6,7 +6,7 @@ use bdk_chain::{ bitcoin::{BlockHash, OutPoint, ScriptBuf, TxOut, Txid}, collections::BTreeMap, local_chain::CheckPoint, - BlockId, ConfirmationTimeHeightAnchor, TxGraph, + BlockId, ConfirmationBlockTime, TxGraph, }; use bdk_chain::{Anchor, Indexed}; use esplora_client::{Amount, TxStatus}; @@ -240,10 +240,10 @@ async fn full_scan_for_index_and_graph( >, stop_gap: usize, parallel_requests: usize, -) -> Result<(TxGraph, BTreeMap), Error> { +) -> Result<(TxGraph, BTreeMap), Error> { type TxsOfSpkIndex = (u32, Vec); let parallel_requests = Ord::max(parallel_requests, 1); - let mut graph = TxGraph::::default(); + let mut graph = TxGraph::::default(); let mut last_active_indexes = BTreeMap::::new(); for (keychain, spks) in keychain_spks { @@ -333,7 +333,7 @@ async fn sync_for_index_and_graph( txids: impl IntoIterator + Send> + Send, outpoints: impl IntoIterator + Send> + Send, parallel_requests: usize, -) -> Result, Error> { +) -> Result, Error> { let mut graph = full_scan_for_index_and_graph( client, [( diff --git a/crates/esplora/src/blocking_ext.rs b/crates/esplora/src/blocking_ext.rs index 5093b4ef55..a7ca80b167 100644 --- a/crates/esplora/src/blocking_ext.rs +++ b/crates/esplora/src/blocking_ext.rs @@ -6,7 +6,7 @@ use bdk_chain::spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncRe use bdk_chain::{ bitcoin::{Amount, BlockHash, OutPoint, ScriptBuf, TxOut, Txid}, local_chain::CheckPoint, - BlockId, ConfirmationTimeHeightAnchor, TxGraph, + BlockId, ConfirmationBlockTime, TxGraph, }; use bdk_chain::{Anchor, Indexed}; use esplora_client::TxStatus; @@ -219,10 +219,10 @@ fn full_scan_for_index_and_graph_blocking( keychain_spks: BTreeMap>>, stop_gap: usize, parallel_requests: usize, -) -> Result<(TxGraph, BTreeMap), Error> { +) -> Result<(TxGraph, BTreeMap), Error> { type TxsOfSpkIndex = (u32, Vec); let parallel_requests = Ord::max(parallel_requests, 1); - let mut tx_graph = TxGraph::::default(); + let mut tx_graph = TxGraph::::default(); let mut last_active_indices = BTreeMap::::new(); for (keychain, spks) in keychain_spks { @@ -315,7 +315,7 @@ fn sync_for_index_and_graph_blocking( txids: impl IntoIterator, outpoints: impl IntoIterator, parallel_requests: usize, -) -> Result, Error> { +) -> Result, Error> { let (mut tx_graph, _) = full_scan_for_index_and_graph_blocking( client, { diff --git a/crates/esplora/src/lib.rs b/crates/esplora/src/lib.rs index 535167ff25..718d3cf9c8 100644 --- a/crates/esplora/src/lib.rs +++ b/crates/esplora/src/lib.rs @@ -16,7 +16,7 @@ //! [`TxGraph`]: bdk_chain::tx_graph::TxGraph //! [`example_esplora`]: https://github.com/bitcoindevkit/bdk/tree/master/example-crates/example_esplora -use bdk_chain::{BlockId, ConfirmationTimeHeightAnchor}; +use bdk_chain::{BlockId, ConfirmationBlockTime}; use esplora_client::TxStatus; pub use esplora_client; @@ -31,7 +31,7 @@ mod async_ext; #[cfg(feature = "async")] pub use async_ext::*; -fn anchor_from_status(status: &TxStatus) -> Option { +fn anchor_from_status(status: &TxStatus) -> Option { if let TxStatus { block_height: Some(height), block_hash: Some(hash), @@ -39,9 +39,8 @@ fn anchor_from_status(status: &TxStatus) -> Option .. } = status.clone() { - Some(ConfirmationTimeHeightAnchor { - anchor_block: BlockId { height, hash }, - confirmation_height: height, + Some(ConfirmationBlockTime { + block_id: BlockId { height, hash }, confirmation_time: time, }) } else { diff --git a/crates/sqlite/src/store.rs b/crates/sqlite/src/store.rs index 4cd81f50b2..8b4faea6ce 100644 --- a/crates/sqlite/src/store.rs +++ b/crates/sqlite/src/store.rs @@ -547,8 +547,7 @@ mod test { use bdk_chain::miniscript::Descriptor; use bdk_chain::CombinedChangeSet; use bdk_chain::{ - indexed_tx_graph, keychain, tx_graph, BlockId, ConfirmationHeightAnchor, - ConfirmationTimeHeightAnchor, DescriptorExt, + indexed_tx_graph, keychain, tx_graph, BlockId, ConfirmationBlockTime, DescriptorExt, }; use std::str::FromStr; use std::sync::Arc; @@ -560,37 +559,15 @@ mod test { } #[test] - fn insert_and_load_aggregate_changesets_with_confirmation_time_height_anchor() { + fn insert_and_load_aggregate_changesets_with_confirmation_block_time_anchor() { let (test_changesets, agg_test_changesets) = - create_test_changesets(&|height, time, hash| ConfirmationTimeHeightAnchor { - confirmation_height: height, + create_test_changesets(&|height, time, hash| ConfirmationBlockTime { confirmation_time: time, - anchor_block: (height, hash).into(), + block_id: (height, hash).into(), }); let conn = Connection::open_in_memory().expect("in memory connection"); - let mut store = Store::::new(conn) - .expect("create new memory db store"); - - test_changesets.iter().for_each(|changeset| { - store.write(changeset).expect("write changeset"); - }); - - let agg_changeset = store.read().expect("aggregated changeset"); - - assert_eq!(agg_changeset, Some(agg_test_changesets)); - } - - #[test] - fn insert_and_load_aggregate_changesets_with_confirmation_height_anchor() { - let (test_changesets, agg_test_changesets) = - create_test_changesets(&|height, _time, hash| ConfirmationHeightAnchor { - confirmation_height: height, - anchor_block: (height, hash).into(), - }); - - let conn = Connection::open_in_memory().expect("in memory connection"); - let mut store = Store::::new(conn) + let mut store = Store::::new(conn) .expect("create new memory db store"); test_changesets.iter().for_each(|changeset| { diff --git a/crates/wallet/src/wallet/export.rs b/crates/wallet/src/wallet/export.rs index 0aa3ec887c..16441ae75f 100644 --- a/crates/wallet/src/wallet/export.rs +++ b/crates/wallet/src/wallet/export.rs @@ -128,7 +128,7 @@ impl FullyNodedExport { let blockheight = if include_blockheight { wallet.transactions().next().map_or(0, |canonical_tx| { match canonical_tx.chain_position { - bdk_chain::ChainPosition::Confirmed(a) => a.confirmation_height, + bdk_chain::ChainPosition::Confirmed(a) => a.block_id.height, bdk_chain::ChainPosition::Unconfirmed(_) => 0, } }) @@ -229,6 +229,12 @@ mod test { version: transaction::Version::non_standard(0), lock_time: bitcoin::absolute::LockTime::ZERO, }; + wallet + .insert_checkpoint(BlockId { + height: 5000, + hash: BlockHash::all_zeros(), + }) + .unwrap(); wallet .insert_checkpoint(BlockId { height: 5001, diff --git a/crates/wallet/src/wallet/mod.rs b/crates/wallet/src/wallet/mod.rs index 400ecde1d0..c531fad57d 100644 --- a/crates/wallet/src/wallet/mod.rs +++ b/crates/wallet/src/wallet/mod.rs @@ -28,8 +28,8 @@ use bdk_chain::{ }, spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult}, tx_graph::{CanonicalTx, TxGraph}, - Append, BlockId, ChainPosition, ConfirmationTime, ConfirmationTimeHeightAnchor, FullTxOut, - Indexed, IndexedTxGraph, + Append, BlockId, ChainPosition, ConfirmationBlockTime, ConfirmationTime, FullTxOut, Indexed, + IndexedTxGraph, }; use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::sighash::{EcdsaSighashType, TapSighashType}; @@ -98,7 +98,7 @@ pub struct Wallet { signers: Arc, change_signers: Arc, chain: LocalChain, - indexed_graph: IndexedTxGraph>, + indexed_graph: IndexedTxGraph>, stage: ChangeSet, network: Network, secp: SecpCtx, @@ -114,7 +114,7 @@ pub struct Update { pub last_active_indices: BTreeMap, /// Update for the wallet's internal [`TxGraph`]. - pub graph: TxGraph, + pub graph: TxGraph, /// Update for the wallet's internal [`LocalChain`]. /// @@ -143,7 +143,7 @@ impl From for Update { } /// The changes made to a wallet by applying an [`Update`]. -pub type ChangeSet = bdk_chain::CombinedChangeSet; +pub type ChangeSet = bdk_chain::CombinedChangeSet; /// A derived address and the index it was found at. /// For convenience this automatically derefs to `Address` @@ -1030,7 +1030,7 @@ impl Wallet { /// match canonical_tx.chain_position { /// ChainPosition::Confirmed(anchor) => println!( /// "tx is confirmed at height {}, we know this since {}:{} is in the best chain", - /// anchor.confirmation_height, anchor.anchor_block.height, anchor.anchor_block.hash, + /// anchor.block_id.height, anchor.block_id.height, anchor.block_id.hash, /// ), /// ChainPosition::Unconfirmed(last_seen) => println!( /// "tx is last seen at {}, it is unconfirmed as it is not anchored in the best chain", @@ -1043,7 +1043,7 @@ impl Wallet { pub fn get_tx( &self, txid: Txid, - ) -> Option, ConfirmationTimeHeightAnchor>> { + ) -> Option, ConfirmationBlockTime>> { let graph = self.indexed_graph.graph(); Some(CanonicalTx { @@ -1112,9 +1112,13 @@ impl Wallet { tip_height: self.chain.tip().height(), tx_height: height, }) - .map(|anchor_cp| ConfirmationTimeHeightAnchor { - anchor_block: anchor_cp.block_id(), - confirmation_height: height, + .map(|anchor_cp| ConfirmationBlockTime { + block_id: BlockId { + // This expect statement is only a holdover until #1416 is merged. #1416 + // will change `insert_tx` to no longer insert anchors. + hash: anchor_cp.get(height).expect("block must exist").hash(), + height, + }, confirmation_time: time, })?; @@ -1141,8 +1145,7 @@ impl Wallet { /// Iterate over the transactions in the wallet. pub fn transactions( &self, - ) -> impl Iterator, ConfirmationTimeHeightAnchor>> + '_ - { + ) -> impl Iterator, ConfirmationBlockTime>> + '_ { self.indexed_graph .graph() .list_chain_txs(&self.chain, self.chain.tip().block_id()) @@ -1873,7 +1876,7 @@ impl Wallet { .graph() .get_chain_position(&self.chain, chain_tip, input.previous_output.txid) .map(|chain_position| match chain_position { - ChainPosition::Confirmed(a) => a.confirmation_height, + ChainPosition::Confirmed(a) => a.block_id.height, ChainPosition::Unconfirmed(_) => u32::MAX, }); let current_height = sign_options @@ -2317,7 +2320,7 @@ impl Wallet { } /// Get a reference to the inner [`TxGraph`]. - pub fn tx_graph(&self) -> &TxGraph { + pub fn tx_graph(&self) -> &TxGraph { self.indexed_graph.graph() } @@ -2437,8 +2440,8 @@ impl Wallet { } } -impl AsRef> for Wallet { - fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph { +impl AsRef> for Wallet { + fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph { self.indexed_graph.graph() } } @@ -2477,7 +2480,7 @@ where fn new_local_utxo( keychain: KeychainKind, derivation_index: u32, - full_txo: FullTxOut, + full_txo: FullTxOut, ) -> LocalOutput { LocalOutput { outpoint: full_txo.outpoint, @@ -2561,6 +2564,7 @@ macro_rules! doctest_wallet { script_pubkey: address.script_pubkey(), }], }; + let _ = wallet.insert_checkpoint(BlockId { height: 500, hash: BlockHash::all_zeros() }); let _ = wallet.insert_checkpoint(BlockId { height: 1_000, hash: BlockHash::all_zeros() }); let _ = wallet.insert_tx(tx.clone(), ConfirmationTime::Confirmed { height: 500, diff --git a/crates/wallet/tests/common.rs b/crates/wallet/tests/common.rs index bab75e275c..ef39051890 100644 --- a/crates/wallet/tests/common.rs +++ b/crates/wallet/tests/common.rs @@ -65,6 +65,12 @@ pub fn get_funded_wallet_with_change(descriptor: &str, change: &str) -> (Wallet, ], }; + wallet + .insert_checkpoint(BlockId { + height: 42, + hash: BlockHash::all_zeros(), + }) + .unwrap(); wallet .insert_checkpoint(BlockId { height: 1_000, diff --git a/example-crates/example_bitcoind_rpc_polling/src/main.rs b/example-crates/example_bitcoind_rpc_polling/src/main.rs index 38e796474c..c0c3f2c63c 100644 --- a/example-crates/example_bitcoind_rpc_polling/src/main.rs +++ b/example-crates/example_bitcoind_rpc_polling/src/main.rs @@ -15,7 +15,7 @@ use bdk_chain::{ bitcoin::{constants::genesis_block, Block, Transaction}, indexed_tx_graph, keychain, local_chain::{self, LocalChain}, - Append, ConfirmationTimeHeightAnchor, IndexedTxGraph, + Append, ConfirmationBlockTime, IndexedTxGraph, }; use example_cli::{ anyhow, @@ -37,7 +37,7 @@ const DB_COMMIT_DELAY: Duration = Duration::from_secs(60); type ChangeSet = ( local_chain::ChangeSet, - indexed_tx_graph::ChangeSet>, + indexed_tx_graph::ChangeSet>, ); #[derive(Debug)] diff --git a/example-crates/example_electrum/src/main.rs b/example-crates/example_electrum/src/main.rs index 44febf7f5d..6736463dcd 100644 --- a/example-crates/example_electrum/src/main.rs +++ b/example-crates/example_electrum/src/main.rs @@ -10,7 +10,7 @@ use bdk_chain::{ keychain, local_chain::{self, LocalChain}, spk_client::{FullScanRequest, SyncRequest}, - Append, ConfirmationHeightAnchor, + Append, ConfirmationBlockTime, }; use bdk_electrum::{ electrum_client::{self, Client, ElectrumApi}, @@ -100,7 +100,7 @@ pub struct ScanOptions { type ChangeSet = ( local_chain::ChangeSet, - indexed_tx_graph::ChangeSet>, + indexed_tx_graph::ChangeSet>, ); fn main() -> anyhow::Result<()> { @@ -338,17 +338,12 @@ fn main() -> anyhow::Result<()> { let chain_changeset = chain.apply_update(chain_update)?; let mut indexed_tx_graph_changeset = - indexed_tx_graph::ChangeSet::::default(); + indexed_tx_graph::ChangeSet::::default(); if let Some(keychain_update) = keychain_update { let keychain_changeset = graph.index.reveal_to_target_multi(&keychain_update); indexed_tx_graph_changeset.append(keychain_changeset.into()); } - indexed_tx_graph_changeset.append(graph.apply_update(graph_update.map_anchors(|a| { - ConfirmationHeightAnchor { - confirmation_height: a.confirmation_height, - anchor_block: a.anchor_block, - } - }))); + indexed_tx_graph_changeset.append(graph.apply_update(graph_update)); (chain_changeset, indexed_tx_graph_changeset) }; diff --git a/example-crates/example_esplora/src/main.rs b/example-crates/example_esplora/src/main.rs index 3f9b872103..39f3c295e5 100644 --- a/example-crates/example_esplora/src/main.rs +++ b/example-crates/example_esplora/src/main.rs @@ -10,7 +10,7 @@ use bdk_chain::{ keychain, local_chain::{self, LocalChain}, spk_client::{FullScanRequest, SyncRequest}, - Append, ConfirmationTimeHeightAnchor, + Append, ConfirmationBlockTime, }; use bdk_esplora::{esplora_client, EsploraExt}; @@ -26,7 +26,7 @@ const DB_PATH: &str = ".bdk_esplora_example.db"; type ChangeSet = ( local_chain::ChangeSet, - indexed_tx_graph::ChangeSet>, + indexed_tx_graph::ChangeSet>, ); #[derive(Subcommand, Debug, Clone)]