Skip to content

Commit 77d3595

Browse files
committed
feat(chain)!: rm local_chain::Update
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.
1 parent 1269b06 commit 77d3595

File tree

12 files changed

+39
-122
lines changed

12 files changed

+39
-122
lines changed

crates/bdk/src/wallet/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pub struct Update {
107107
/// Update for the wallet's internal [`LocalChain`].
108108
///
109109
/// [`LocalChain`]: local_chain::LocalChain
110-
pub chain: Option<local_chain::Update>,
110+
pub chain: Option<CheckPoint>,
111111
}
112112

113113
/// The changes made to a wallet by applying an [`Update`].

crates/bitcoind_rpc/tests/test_emitter.rs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bdk_bitcoind_rpc::Emitter;
44
use bdk_chain::{
55
bitcoin::{Address, Amount, Txid},
66
keychain::Balance,
7-
local_chain::{self, CheckPoint, LocalChain},
7+
local_chain::{CheckPoint, LocalChain},
88
Append, BlockId, IndexedTxGraph, SpkTxOutIndex,
99
};
1010
use bdk_testenv::TestEnv;
@@ -47,10 +47,7 @@ pub fn test_sync_local_chain() -> anyhow::Result<()> {
4747
);
4848

4949
assert_eq!(
50-
local_chain.apply_update(local_chain::Update {
51-
tip: emission.checkpoint,
52-
introduce_older_blocks: false,
53-
})?,
50+
local_chain.apply_update(emission.checkpoint,)?,
5451
BTreeMap::from([(height, Some(hash))]),
5552
"chain update changeset is unexpected",
5653
);
@@ -95,10 +92,7 @@ pub fn test_sync_local_chain() -> anyhow::Result<()> {
9592
);
9693

9794
assert_eq!(
98-
local_chain.apply_update(local_chain::Update {
99-
tip: emission.checkpoint,
100-
introduce_older_blocks: false,
101-
})?,
95+
local_chain.apply_update(emission.checkpoint,)?,
10296
if exp_height == exp_hashes.len() - reorged_blocks.len() {
10397
core::iter::once((height, Some(hash)))
10498
.chain((height + 1..exp_hashes.len() as u32).map(|h| (h, None)))
@@ -168,10 +162,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
168162

169163
while let Some(emission) = emitter.next_block()? {
170164
let height = emission.block_height();
171-
let _ = chain.apply_update(local_chain::Update {
172-
tip: emission.checkpoint,
173-
introduce_older_blocks: false,
174-
})?;
165+
let _ = chain.apply_update(emission.checkpoint)?;
175166
let indexed_additions = indexed_tx_graph.apply_block_relevant(&emission.block, height);
176167
assert!(indexed_additions.is_empty());
177168
}
@@ -232,10 +223,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
232223
{
233224
let emission = emitter.next_block()?.expect("must get mined block");
234225
let height = emission.block_height();
235-
let _ = chain.apply_update(local_chain::Update {
236-
tip: emission.checkpoint,
237-
introduce_older_blocks: false,
238-
})?;
226+
let _ = chain.apply_update(emission.checkpoint)?;
239227
let indexed_additions = indexed_tx_graph.apply_block_relevant(&emission.block, height);
240228
assert!(indexed_additions.graph.txs.is_empty());
241229
assert!(indexed_additions.graph.txouts.is_empty());
@@ -294,8 +282,7 @@ fn process_block(
294282
block: Block,
295283
block_height: u32,
296284
) -> anyhow::Result<()> {
297-
recv_chain
298-
.apply_update(CheckPoint::from_header(&block.header, block_height).into_update(false))?;
285+
recv_chain.apply_update(CheckPoint::from_header(&block.header, block_height))?;
299286
let _ = recv_graph.apply_block(block, block_height);
300287
Ok(())
301288
}

crates/chain/src/local_chain.rs

Lines changed: 7 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -96,16 +96,6 @@ impl CheckPoint {
9696
.expect("must construct checkpoint")
9797
}
9898

99-
/// Convenience method to convert the [`CheckPoint`] into an [`Update`].
100-
///
101-
/// For more information, refer to [`Update`].
102-
pub fn into_update(self, introduce_older_blocks: bool) -> Update {
103-
Update {
104-
tip: self,
105-
introduce_older_blocks,
106-
}
107-
}
108-
10999
/// Puts another checkpoint onto the linked list representing the blockchain.
110100
///
111101
/// 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 {
251241
}
252242
}
253243

254-
/// Used to update [`LocalChain`].
255-
///
256-
/// This is used as input for [`LocalChain::apply_update`]. It contains the update's chain `tip` and
257-
/// a flag `introduce_older_blocks` which signals whether this update intends to introduce missing
258-
/// blocks to the original chain.
259-
///
260-
/// Block-by-block syncing mechanisms would typically create updates that builds upon the previous
261-
/// tip. In this case, `introduce_older_blocks` would be `false`.
262-
///
263-
/// Script-pubkey based syncing mechanisms may not introduce transactions in a chronological order
264-
/// so some updates require introducing older blocks (to anchor older transactions). For
265-
/// script-pubkey based syncing, `introduce_older_blocks` would typically be `true`.
266-
#[derive(Debug, Clone, PartialEq)]
267-
pub struct Update {
268-
/// The update chain's new tip.
269-
pub tip: CheckPoint,
270-
271-
/// Whether the update allows for introducing older blocks.
272-
///
273-
/// Refer to [struct-level documentation] for more.
274-
///
275-
/// [struct-level documentation]: Update
276-
pub introduce_older_blocks: bool,
277-
}
278-
279244
/// This is a local implementation of [`ChainOracle`].
280245
#[derive(Debug, Clone, PartialEq)]
281246
pub struct LocalChain {
@@ -390,23 +355,16 @@ impl LocalChain {
390355
/// the existing chain and invalidate the block after it (if it exists) by including a block at
391356
/// the same height but with a different hash to explicitly exclude it as a connection point.
392357
///
393-
/// Additionally, an empty chain can be updated with any chain, and a chain with a single block
394-
/// can have it's block invalidated by an update chain with a block at the same height but
395-
/// different hash.
358+
/// Additionally, a chain with a single block can have it's block invalidated by an update
359+
/// chain with a block at the same height but different hash.
396360
///
397361
/// # Errors
398362
///
399363
/// An error will occur if the update does not correctly connect with `self`.
400364
///
401-
/// Refer to [`Update`] for more about the update struct.
402-
///
403365
/// [module-level documentation]: crate::local_chain
404-
pub fn apply_update(&mut self, update: Update) -> Result<ChangeSet, CannotConnectError> {
405-
let changeset = merge_chains(
406-
self.tip.clone(),
407-
update.tip.clone(),
408-
update.introduce_older_blocks,
409-
)?;
366+
pub fn apply_update(&mut self, update: CheckPoint) -> Result<ChangeSet, CannotConnectError> {
367+
let changeset = merge_chains(self.tip.clone(), update.clone())?;
410368
// `._check_index_is_consistent_with_tip` and `._check_changeset_is_applied` is called in
411369
// `.apply_changeset`
412370
self.apply_changeset(&changeset)
@@ -464,11 +422,8 @@ impl LocalChain {
464422
conn => Some(conn),
465423
};
466424

467-
let update = Update {
468-
tip: CheckPoint::from_block_ids([conn, prev, Some(this)].into_iter().flatten())
469-
.expect("block ids must be in order"),
470-
introduce_older_blocks: false,
471-
};
425+
let update = CheckPoint::from_block_ids([conn, prev, Some(this)].into_iter().flatten())
426+
.expect("block ids must be in order");
472427

473428
self.apply_update(update)
474429
.map_err(ApplyHeaderError::CannotConnect)
@@ -769,7 +724,6 @@ impl std::error::Error for ApplyHeaderError {}
769724
fn merge_chains(
770725
original_tip: CheckPoint,
771726
update_tip: CheckPoint,
772-
introduce_older_blocks: bool,
773727
) -> Result<ChangeSet, CannotConnectError> {
774728
let mut changeset = ChangeSet::default();
775729
let mut orig = original_tip.into_iter();
@@ -829,11 +783,9 @@ fn merge_chains(
829783
}
830784
point_of_agreement_found = true;
831785
prev_orig_was_invalidated = false;
832-
// OPTIMIZATION 1 -- If we know that older blocks cannot be introduced without
833-
// invalidation, we can break after finding the point of agreement.
834786
// OPTIMIZATION 2 -- if we have the same underlying pointer at this point, we
835787
// can guarantee that no older blocks are introduced.
836-
if !introduce_older_blocks || Arc::as_ptr(&o.0) == Arc::as_ptr(&u.0) {
788+
if Arc::as_ptr(&o.0) == Arc::as_ptr(&u.0) {
837789
return Ok(changeset);
838790
}
839791
} else {

crates/chain/tests/common/mod.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,9 @@ macro_rules! local_chain {
3232
macro_rules! chain_update {
3333
[ $(($height:expr, $hash:expr)), * ] => {{
3434
#[allow(unused_mut)]
35-
bdk_chain::local_chain::Update {
36-
tip: bdk_chain::local_chain::LocalChain::from_blocks([$(($height, $hash).into()),*].into_iter().collect())
37-
.expect("chain must have genesis block")
38-
.tip(),
39-
introduce_older_blocks: true,
40-
}
35+
bdk_chain::local_chain::LocalChain::from_blocks([$(($height, $hash).into()),*].into_iter().collect())
36+
.expect("chain must have genesis block")
37+
.tip()
4138
}};
4239
}
4340

crates/chain/tests/test_local_chain.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::ops::{Bound, RangeBounds};
33
use bdk_chain::{
44
local_chain::{
55
AlterCheckPointError, ApplyHeaderError, CannotConnectError, ChangeSet, CheckPoint,
6-
LocalChain, MissingGenesisError, Update,
6+
LocalChain, MissingGenesisError,
77
},
88
BlockId,
99
};
@@ -17,7 +17,7 @@ mod common;
1717
struct TestLocalChain<'a> {
1818
name: &'static str,
1919
chain: LocalChain,
20-
update: Update,
20+
update: CheckPoint,
2121
exp: ExpectedResult<'a>,
2222
}
2323

crates/electrum/src/electrum_ext.rs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bdk_chain::{
22
bitcoin::{OutPoint, ScriptBuf, Transaction, Txid},
3-
local_chain::{self, CheckPoint},
3+
local_chain::CheckPoint,
44
tx_graph::{self, TxGraph},
55
Anchor, BlockId, ConfirmationHeightAnchor, ConfirmationTimeHeightAnchor,
66
};
@@ -124,7 +124,7 @@ impl RelevantTxids {
124124
#[derive(Debug)]
125125
pub struct ElectrumUpdate {
126126
/// Chain update
127-
pub chain_update: local_chain::Update,
127+
pub chain_update: CheckPoint,
128128
/// Transaction updates from electrum
129129
pub relevant_txids: RelevantTxids,
130130
}
@@ -232,10 +232,7 @@ impl<A: ElectrumApi> ElectrumExt for A {
232232
continue; // reorg
233233
}
234234

235-
let chain_update = local_chain::Update {
236-
tip,
237-
introduce_older_blocks: true,
238-
};
235+
let chain_update = tip;
239236

240237
let keychain_update = request_spks
241238
.into_keys()

crates/esplora/src/async_ext.rs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use bdk_chain::Anchor;
55
use bdk_chain::{
66
bitcoin::{BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
77
collections::BTreeMap,
8-
local_chain::{self, CheckPoint},
8+
local_chain::CheckPoint,
99
BlockId, ConfirmationTimeHeightAnchor, TxGraph,
1010
};
1111
use esplora_client::{Amount, TxStatus};
@@ -47,7 +47,7 @@ pub trait EsploraAsyncExt {
4747
///
4848
/// A `stop_gap` of 0 will be treated as a `stop_gap` of 1.
4949
///
50-
/// [`LocalChain::tip`]: local_chain::LocalChain::tip
50+
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
5151
async fn full_scan<K: Ord + Clone + Send>(
5252
&self,
5353
local_tip: CheckPoint,
@@ -71,7 +71,7 @@ pub trait EsploraAsyncExt {
7171
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
7272
/// may include scripts that have been used, use [`full_scan`] with the keychain.
7373
///
74-
/// [`LocalChain::tip`]: local_chain::LocalChain::tip
74+
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
7575
/// [`full_scan`]: EsploraAsyncExt::full_scan
7676
async fn sync(
7777
&self,
@@ -180,7 +180,7 @@ async fn chain_update<A: Anchor>(
180180
latest_blocks: &BTreeMap<u32, BlockHash>,
181181
local_tip: &CheckPoint,
182182
anchors: &BTreeSet<(A, Txid)>,
183-
) -> Result<local_chain::Update, Error> {
183+
) -> Result<CheckPoint, Error> {
184184
let mut point_of_agreement = None;
185185
let mut conflicts = vec![];
186186
for local_cp in local_tip.iter() {
@@ -225,10 +225,7 @@ async fn chain_update<A: Anchor>(
225225
tip = tip.insert(BlockId { height, hash });
226226
}
227227

228-
Ok(local_chain::Update {
229-
tip,
230-
introduce_older_blocks: true,
231-
})
228+
Ok(tip)
232229
}
233230

234231
/// This performs a full scan to get an update for the [`TxGraph`] and

crates/esplora/src/blocking_ext.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use bdk_chain::collections::BTreeMap;
66
use bdk_chain::Anchor;
77
use bdk_chain::{
88
bitcoin::{Amount, BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
9-
local_chain::{self, CheckPoint},
9+
local_chain::CheckPoint,
1010
BlockId, ConfirmationTimeHeightAnchor, TxGraph,
1111
};
1212
use esplora_client::TxStatus;
@@ -47,7 +47,7 @@ pub trait EsploraExt {
4747
///
4848
/// A `stop_gap` of 0 will be treated as a `stop_gap` of 1.
4949
///
50-
/// [`LocalChain::tip`]: local_chain::LocalChain::tip
50+
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
5151
fn full_scan<K: Ord + Clone>(
5252
&self,
5353
local_tip: CheckPoint,
@@ -68,7 +68,7 @@ pub trait EsploraExt {
6868
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
6969
/// may include scripts that have been used, use [`full_scan`] with the keychain.
7070
///
71-
/// [`LocalChain::tip`]: local_chain::LocalChain::tip
71+
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
7272
/// [`full_scan`]: EsploraExt::full_scan
7373
fn sync(
7474
&self,
@@ -178,7 +178,7 @@ fn chain_update<A: Anchor>(
178178
latest_blocks: &BTreeMap<u32, BlockHash>,
179179
local_tip: &CheckPoint,
180180
anchors: &BTreeSet<(A, Txid)>,
181-
) -> Result<local_chain::Update, Error> {
181+
) -> Result<CheckPoint, Error> {
182182
let mut point_of_agreement = None;
183183
let mut conflicts = vec![];
184184
for local_cp in local_tip.iter() {
@@ -223,10 +223,7 @@ fn chain_update<A: Anchor>(
223223
tip = tip.insert(BlockId { height, hash });
224224
}
225225

226-
Ok(local_chain::Update {
227-
tip,
228-
introduce_older_blocks: true,
229-
})
226+
Ok(tip)
230227
}
231228

232229
/// This performs a full scan to get an update for the [`TxGraph`] and
@@ -752,7 +749,6 @@ mod test {
752749
)?;
753750

754751
let update_blocks = chain_update
755-
.tip
756752
.iter()
757753
.map(|cp| cp.block_id())
758754
.collect::<BTreeSet<_>>();

crates/esplora/src/lib.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
1919
use std::collections::BTreeMap;
2020

21-
use bdk_chain::{local_chain, BlockId, ConfirmationTimeHeightAnchor, TxGraph};
21+
use bdk_chain::{local_chain::CheckPoint, BlockId, ConfirmationTimeHeightAnchor, TxGraph};
2222
use esplora_client::TxStatus;
2323

2424
pub use esplora_client;
@@ -53,8 +53,8 @@ fn anchor_from_status(status: &TxStatus) -> Option<ConfirmationTimeHeightAnchor>
5353

5454
/// Update returns from a full scan.
5555
pub struct FullScanUpdate<K> {
56-
/// The update to apply to the receiving [`LocalChain`](local_chain::LocalChain).
57-
pub local_chain: local_chain::Update,
56+
/// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
57+
pub local_chain: CheckPoint,
5858
/// The update to apply to the receiving [`TxGraph`].
5959
pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
6060
/// Last active indices for the corresponding keychains (`K`).
@@ -63,8 +63,8 @@ pub struct FullScanUpdate<K> {
6363

6464
/// Update returned from a sync.
6565
pub struct SyncUpdate {
66-
/// The update to apply to the receiving [`LocalChain`](local_chain::LocalChain).
67-
pub local_chain: local_chain::Update,
66+
/// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
67+
pub local_chain: CheckPoint,
6868
/// The update to apply to the receiving [`TxGraph`].
6969
pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
7070
}

crates/esplora/tests/async_ext.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ pub async fn test_update_tx_graph_without_keychain() -> anyhow::Result<()> {
6969
{
7070
let update_cps = sync_update
7171
.local_chain
72-
.tip
7372
.iter()
7473
.map(|cp| cp.block_id())
7574
.collect::<BTreeSet<_>>();

0 commit comments

Comments
 (0)