]> Untitled Git - bdk/commitdiff
feat(chain)!: rm `local_chain::Update`
author志宇 <hello@evanlinjin.me>
Wed, 17 Apr 2024 02:02:12 +0000 (10:02 +0800)
committer志宇 <hello@evanlinjin.me>
Wed, 17 Apr 2024 02:57:50 +0000 (10:57 +0800)
The intention is to remove the `Update::introduce_older_blocks`
parameter and update the local chain directly with `CheckPoint`.

This simplifies the API and there is a way to do this efficiently.

12 files changed:
crates/bdk/src/wallet/mod.rs
crates/bitcoind_rpc/tests/test_emitter.rs
crates/chain/src/local_chain.rs
crates/chain/tests/common/mod.rs
crates/chain/tests/test_local_chain.rs
crates/electrum/src/electrum_ext.rs
crates/esplora/src/async_ext.rs
crates/esplora/src/blocking_ext.rs
crates/esplora/src/lib.rs
crates/esplora/tests/async_ext.rs
crates/esplora/tests/blocking_ext.rs
example-crates/example_bitcoind_rpc_polling/src/main.rs

index 846878823c4895d8ff44cad5d4e90975aa68c8af..f11abb30bcb8fa729b208a5efee71e2b4d883ca4 100644 (file)
@@ -107,7 +107,7 @@ pub struct Update {
     /// Update for the wallet's internal [`LocalChain`].
     ///
     /// [`LocalChain`]: local_chain::LocalChain
-    pub chain: Option<local_chain::Update>,
+    pub chain: Option<CheckPoint>,
 }
 
 /// The changes made to a wallet by applying an [`Update`].
index f2e2a5d59c41cb2937e6cef664186aa59b3dc48b..c517740f513cc9475560fbcc462aa40dd2fcce32 100644 (file)
@@ -4,7 +4,7 @@ use bdk_bitcoind_rpc::Emitter;
 use bdk_chain::{
     bitcoin::{Address, Amount, Txid},
     keychain::Balance,
-    local_chain::{self, CheckPoint, LocalChain},
+    local_chain::{CheckPoint, LocalChain},
     Append, BlockId, IndexedTxGraph, SpkTxOutIndex,
 };
 use bdk_testenv::TestEnv;
@@ -47,10 +47,7 @@ pub fn test_sync_local_chain() -> anyhow::Result<()> {
         );
 
         assert_eq!(
-            local_chain.apply_update(local_chain::Update {
-                tip: emission.checkpoint,
-                introduce_older_blocks: false,
-            })?,
+            local_chain.apply_update(emission.checkpoint,)?,
             BTreeMap::from([(height, Some(hash))]),
             "chain update changeset is unexpected",
         );
@@ -95,10 +92,7 @@ pub fn test_sync_local_chain() -> anyhow::Result<()> {
         );
 
         assert_eq!(
-            local_chain.apply_update(local_chain::Update {
-                tip: emission.checkpoint,
-                introduce_older_blocks: false,
-            })?,
+            local_chain.apply_update(emission.checkpoint,)?,
             if exp_height == exp_hashes.len() - reorged_blocks.len() {
                 core::iter::once((height, Some(hash)))
                     .chain((height + 1..exp_hashes.len() as u32).map(|h| (h, None)))
@@ -168,10 +162,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
 
     while let Some(emission) = emitter.next_block()? {
         let height = emission.block_height();
-        let _ = chain.apply_update(local_chain::Update {
-            tip: emission.checkpoint,
-            introduce_older_blocks: false,
-        })?;
+        let _ = chain.apply_update(emission.checkpoint)?;
         let indexed_additions = indexed_tx_graph.apply_block_relevant(&emission.block, height);
         assert!(indexed_additions.is_empty());
     }
@@ -232,10 +223,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
     {
         let emission = emitter.next_block()?.expect("must get mined block");
         let height = emission.block_height();
-        let _ = chain.apply_update(local_chain::Update {
-            tip: emission.checkpoint,
-            introduce_older_blocks: false,
-        })?;
+        let _ = chain.apply_update(emission.checkpoint)?;
         let indexed_additions = indexed_tx_graph.apply_block_relevant(&emission.block, height);
         assert!(indexed_additions.graph.txs.is_empty());
         assert!(indexed_additions.graph.txouts.is_empty());
@@ -294,8 +282,7 @@ fn process_block(
     block: Block,
     block_height: u32,
 ) -> anyhow::Result<()> {
-    recv_chain
-        .apply_update(CheckPoint::from_header(&block.header, block_height).into_update(false))?;
+    recv_chain.apply_update(CheckPoint::from_header(&block.header, block_height))?;
     let _ = recv_graph.apply_block(block, block_height);
     Ok(())
 }
index 6fa85d422e3e6487e07bc9362e119b411d7fbb46..792b987f5ad89f2a637443c4846188386ffce599 100644 (file)
@@ -96,16 +96,6 @@ impl CheckPoint {
             .expect("must construct checkpoint")
     }
 
-    /// Convenience method to convert the [`CheckPoint`] into an [`Update`].
-    ///
-    /// For more information, refer to [`Update`].
-    pub fn into_update(self, introduce_older_blocks: bool) -> Update {
-        Update {
-            tip: self,
-            introduce_older_blocks,
-        }
-    }
-
     /// Puts another checkpoint onto the linked list representing the blockchain.
     ///
     /// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the one you
@@ -251,31 +241,6 @@ impl IntoIterator for CheckPoint {
     }
 }
 
-/// Used to update [`LocalChain`].
-///
-/// This is used as input for [`LocalChain::apply_update`]. It contains the update's chain `tip` and
-/// a flag `introduce_older_blocks` which signals whether this update intends to introduce missing
-/// blocks to the original chain.
-///
-/// Block-by-block syncing mechanisms would typically create updates that builds upon the previous
-/// tip. In this case, `introduce_older_blocks` would be `false`.
-///
-/// Script-pubkey based syncing mechanisms may not introduce transactions in a chronological order
-/// so some updates require introducing older blocks (to anchor older transactions). For
-/// script-pubkey based syncing, `introduce_older_blocks` would typically be `true`.
-#[derive(Debug, Clone, PartialEq)]
-pub struct Update {
-    /// The update chain's new tip.
-    pub tip: CheckPoint,
-
-    /// Whether the update allows for introducing older blocks.
-    ///
-    /// Refer to [struct-level documentation] for more.
-    ///
-    /// [struct-level documentation]: Update
-    pub introduce_older_blocks: bool,
-}
-
 /// This is a local implementation of [`ChainOracle`].
 #[derive(Debug, Clone, PartialEq)]
 pub struct LocalChain {
@@ -390,23 +355,16 @@ impl LocalChain {
     /// the existing chain and invalidate the block after it (if it exists) by including a block at
     /// the same height but with a different hash to explicitly exclude it as a connection point.
     ///
-    /// Additionally, an empty chain can be updated with any chain, and a chain with a single block
-    /// can have it's block invalidated by an update chain with a block at the same height but
-    /// different hash.
+    /// Additionally, a chain with a single block can have it's block invalidated by an update
+    /// chain with a block at the same height but different hash.
     ///
     /// # Errors
     ///
     /// An error will occur if the update does not correctly connect with `self`.
     ///
-    /// Refer to [`Update`] for more about the update struct.
-    ///
     /// [module-level documentation]: crate::local_chain
-    pub fn apply_update(&mut self, update: Update) -> Result<ChangeSet, CannotConnectError> {
-        let changeset = merge_chains(
-            self.tip.clone(),
-            update.tip.clone(),
-            update.introduce_older_blocks,
-        )?;
+    pub fn apply_update(&mut self, update: CheckPoint) -> Result<ChangeSet, CannotConnectError> {
+        let changeset = merge_chains(self.tip.clone(), update.clone())?;
         // `._check_index_is_consistent_with_tip` and `._check_changeset_is_applied` is called in
         // `.apply_changeset`
         self.apply_changeset(&changeset)
@@ -464,11 +422,8 @@ impl LocalChain {
             conn => Some(conn),
         };
 
-        let update = Update {
-            tip: CheckPoint::from_block_ids([conn, prev, Some(this)].into_iter().flatten())
-                .expect("block ids must be in order"),
-            introduce_older_blocks: false,
-        };
+        let update = CheckPoint::from_block_ids([conn, prev, Some(this)].into_iter().flatten())
+            .expect("block ids must be in order");
 
         self.apply_update(update)
             .map_err(ApplyHeaderError::CannotConnect)
@@ -769,7 +724,6 @@ impl std::error::Error for ApplyHeaderError {}
 fn merge_chains(
     original_tip: CheckPoint,
     update_tip: CheckPoint,
-    introduce_older_blocks: bool,
 ) -> Result<ChangeSet, CannotConnectError> {
     let mut changeset = ChangeSet::default();
     let mut orig = original_tip.into_iter();
@@ -829,11 +783,9 @@ fn merge_chains(
                     }
                     point_of_agreement_found = true;
                     prev_orig_was_invalidated = false;
-                    // OPTIMIZATION 1 -- If we know that older blocks cannot be introduced without
-                    // invalidation, we can break after finding the point of agreement.
                     // OPTIMIZATION 2 -- if we have the same underlying pointer at this point, we
                     // can guarantee that no older blocks are introduced.
-                    if !introduce_older_blocks || Arc::as_ptr(&o.0) == Arc::as_ptr(&u.0) {
+                    if Arc::as_ptr(&o.0) == Arc::as_ptr(&u.0) {
                         return Ok(changeset);
                     }
                 } else {
index 6116484ce9621b79778f007fdbf4033012d1c45f..586e640432d914316a95d95836a2d145dffefef7 100644 (file)
@@ -32,12 +32,9 @@ macro_rules! local_chain {
 macro_rules! chain_update {
     [ $(($height:expr, $hash:expr)), * ] => {{
         #[allow(unused_mut)]
-        bdk_chain::local_chain::Update {
-            tip: bdk_chain::local_chain::LocalChain::from_blocks([$(($height, $hash).into()),*].into_iter().collect())
-                .expect("chain must have genesis block")
-                .tip(),
-            introduce_older_blocks: true,
-        }
+        bdk_chain::local_chain::LocalChain::from_blocks([$(($height, $hash).into()),*].into_iter().collect())
+            .expect("chain must have genesis block")
+            .tip()
     }};
 }
 
index c331008db80a39671aed5016ca47ffdfe4e77993..cff4a8e73c246f92107103b8831ba57565511f4f 100644 (file)
@@ -3,7 +3,7 @@ use std::ops::{Bound, RangeBounds};
 use bdk_chain::{
     local_chain::{
         AlterCheckPointError, ApplyHeaderError, CannotConnectError, ChangeSet, CheckPoint,
-        LocalChain, MissingGenesisError, Update,
+        LocalChain, MissingGenesisError,
     },
     BlockId,
 };
@@ -17,7 +17,7 @@ mod common;
 struct TestLocalChain<'a> {
     name: &'static str,
     chain: LocalChain,
-    update: Update,
+    update: CheckPoint,
     exp: ExpectedResult<'a>,
 }
 
index 3ff467fcee31eff1e9dc44d585043e189559aa27..5a6c5d1163fd2e0ff79acc4d691ffe21f017ba5d 100644 (file)
@@ -1,6 +1,6 @@
 use bdk_chain::{
     bitcoin::{OutPoint, ScriptBuf, Transaction, Txid},
-    local_chain::{self, CheckPoint},
+    local_chain::CheckPoint,
     tx_graph::{self, TxGraph},
     Anchor, BlockId, ConfirmationHeightAnchor, ConfirmationTimeHeightAnchor,
 };
@@ -124,7 +124,7 @@ impl RelevantTxids {
 #[derive(Debug)]
 pub struct ElectrumUpdate {
     /// Chain update
-    pub chain_update: local_chain::Update,
+    pub chain_update: CheckPoint,
     /// Transaction updates from electrum
     pub relevant_txids: RelevantTxids,
 }
@@ -232,10 +232,7 @@ impl<A: ElectrumApi> ElectrumExt for A {
                 continue; // reorg
             }
 
-            let chain_update = local_chain::Update {
-                tip,
-                introduce_older_blocks: true,
-            };
+            let chain_update = tip;
 
             let keychain_update = request_spks
                 .into_keys()
index 1abc28c8167b33986bf0fd3eb8c343ec9f6694ba..9d02646c62d4a84722873a10fe97bbea21e026fa 100644 (file)
@@ -5,7 +5,7 @@ use bdk_chain::Anchor;
 use bdk_chain::{
     bitcoin::{BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
     collections::BTreeMap,
-    local_chain::{self, CheckPoint},
+    local_chain::CheckPoint,
     BlockId, ConfirmationTimeHeightAnchor, TxGraph,
 };
 use esplora_client::{Amount, TxStatus};
@@ -47,7 +47,7 @@ pub trait EsploraAsyncExt {
     ///
     /// A `stop_gap` of 0 will be treated as a `stop_gap` of 1.
     ///
-    /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+    /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
     async fn full_scan<K: Ord + Clone + Send>(
         &self,
         local_tip: CheckPoint,
@@ -71,7 +71,7 @@ pub trait EsploraAsyncExt {
     /// If the scripts to sync are unknown, such as when restoring or importing a keychain that
     /// may include scripts that have been used, use [`full_scan`] with the keychain.
     ///
-    /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+    /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
     /// [`full_scan`]: EsploraAsyncExt::full_scan
     async fn sync(
         &self,
@@ -180,7 +180,7 @@ async fn chain_update<A: Anchor>(
     latest_blocks: &BTreeMap<u32, BlockHash>,
     local_tip: &CheckPoint,
     anchors: &BTreeSet<(A, Txid)>,
-) -> Result<local_chain::Update, Error> {
+) -> Result<CheckPoint, Error> {
     let mut point_of_agreement = None;
     let mut conflicts = vec![];
     for local_cp in local_tip.iter() {
@@ -225,10 +225,7 @@ async fn chain_update<A: Anchor>(
         tip = tip.insert(BlockId { height, hash });
     }
 
-    Ok(local_chain::Update {
-        tip,
-        introduce_older_blocks: true,
-    })
+    Ok(tip)
 }
 
 /// This performs a full scan to get an update for the [`TxGraph`] and
index 5b7cd628894a4353f06862730b6a527f896b941f..7037385690729540974b4d2e54510a9453e5399c 100644 (file)
@@ -6,7 +6,7 @@ use bdk_chain::collections::BTreeMap;
 use bdk_chain::Anchor;
 use bdk_chain::{
     bitcoin::{Amount, BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
-    local_chain::{self, CheckPoint},
+    local_chain::CheckPoint,
     BlockId, ConfirmationTimeHeightAnchor, TxGraph,
 };
 use esplora_client::TxStatus;
@@ -47,7 +47,7 @@ pub trait EsploraExt {
     ///
     /// A `stop_gap` of 0 will be treated as a `stop_gap` of 1.
     ///
-    /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+    /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
     fn full_scan<K: Ord + Clone>(
         &self,
         local_tip: CheckPoint,
@@ -68,7 +68,7 @@ pub trait EsploraExt {
     /// If the scripts to sync are unknown, such as when restoring or importing a keychain that
     /// may include scripts that have been used, use [`full_scan`] with the keychain.
     ///
-    /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+    /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
     /// [`full_scan`]: EsploraExt::full_scan
     fn sync(
         &self,
@@ -178,7 +178,7 @@ fn chain_update<A: Anchor>(
     latest_blocks: &BTreeMap<u32, BlockHash>,
     local_tip: &CheckPoint,
     anchors: &BTreeSet<(A, Txid)>,
-) -> Result<local_chain::Update, Error> {
+) -> Result<CheckPoint, Error> {
     let mut point_of_agreement = None;
     let mut conflicts = vec![];
     for local_cp in local_tip.iter() {
@@ -223,10 +223,7 @@ fn chain_update<A: Anchor>(
         tip = tip.insert(BlockId { height, hash });
     }
 
-    Ok(local_chain::Update {
-        tip,
-        introduce_older_blocks: true,
-    })
+    Ok(tip)
 }
 
 /// This performs a full scan to get an update for the [`TxGraph`] and
@@ -752,7 +749,6 @@ mod test {
             )?;
 
             let update_blocks = chain_update
-                .tip
                 .iter()
                 .map(|cp| cp.block_id())
                 .collect::<BTreeSet<_>>();
index c422a0833bf30a20efd47aedff9a729a32a72b74..37d7dd26e5dfb5023d731b233ffcf6561ffb5e88 100644 (file)
@@ -18,7 +18,7 @@
 
 use std::collections::BTreeMap;
 
-use bdk_chain::{local_chain, BlockId, ConfirmationTimeHeightAnchor, TxGraph};
+use bdk_chain::{local_chain::CheckPoint, BlockId, ConfirmationTimeHeightAnchor, TxGraph};
 use esplora_client::TxStatus;
 
 pub use esplora_client;
@@ -53,8 +53,8 @@ fn anchor_from_status(status: &TxStatus) -> Option<ConfirmationTimeHeightAnchor>
 
 /// Update returns from a full scan.
 pub struct FullScanUpdate<K> {
-    /// The update to apply to the receiving [`LocalChain`](local_chain::LocalChain).
-    pub local_chain: local_chain::Update,
+    /// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
+    pub local_chain: CheckPoint,
     /// The update to apply to the receiving [`TxGraph`].
     pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
     /// Last active indices for the corresponding keychains (`K`).
@@ -63,8 +63,8 @@ pub struct FullScanUpdate<K> {
 
 /// Update returned from a sync.
 pub struct SyncUpdate {
-    /// The update to apply to the receiving [`LocalChain`](local_chain::LocalChain).
-    pub local_chain: local_chain::Update,
+    /// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
+    pub local_chain: CheckPoint,
     /// The update to apply to the receiving [`TxGraph`].
     pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
 }
index 5946bb4d84acdac7ca1fb9e6f96f377f5d50d227..f6954fe11f477654b9f17f4746b7b4fc813828ba 100644 (file)
@@ -69,7 +69,6 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
         {
             let update_cps = sync_update
                 .local_chain
-                .tip
                 .iter()
                 .map(|cp| cp.block_id())
                 .collect::<BTreeSet<_>>();
index 3f8ff6932000c929d508b333330deaddc783a37a..40e446a4ed944f56e8036762907bab56ba9f6dbc 100644 (file)
@@ -67,7 +67,6 @@ pub fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
         {
             let update_cps = sync_update
                 .local_chain
-                .tip
                 .iter()
                 .map(|cp| cp.block_id())
                 .collect::<BTreeSet<_>>();
index 88b83067b53bf392842e092d57e979ca50182aea..be9e1839f48654a10dd27e3f2604c7a026d32f22 100644 (file)
@@ -188,10 +188,7 @@ fn main() -> anyhow::Result<()> {
                 let mut db = db.lock().unwrap();
 
                 let chain_changeset = chain
-                    .apply_update(local_chain::Update {
-                        tip: emission.checkpoint,
-                        introduce_older_blocks: false,
-                    })
+                    .apply_update(emission.checkpoint)
                     .expect("must always apply as we receive blocks in order from emitter");
                 let graph_changeset = graph.apply_block_relevant(&emission.block, height);
                 db.stage((chain_changeset, graph_changeset));
@@ -301,12 +298,8 @@ fn main() -> anyhow::Result<()> {
                 let changeset = match emission {
                     Emission::Block(block_emission) => {
                         let height = block_emission.block_height();
-                        let chain_update = local_chain::Update {
-                            tip: block_emission.checkpoint,
-                            introduce_older_blocks: false,
-                        };
                         let chain_changeset = chain
-                            .apply_update(chain_update)
+                            .apply_update(block_emission.checkpoint)
                             .expect("must always apply as we receive blocks in order from emitter");
                         let graph_changeset =
                             graph.apply_block_relevant(&block_emission.block, height);