Skip to content

Commit 495536f

Browse files
evanlinjinLagginTimes
authored andcommitted
feat!: WIP - CheckPointEntry
What still needs to be done: * Figure out best practice with handling `CheckPointEntry::Backref`s that connect. * Methods that build on `CheckPoint` need to check that `backref`s connect. * Tests. * Docs. (cherry picked from commit 8f374cc)
1 parent b93dcd6 commit 495536f

File tree

6 files changed

+311
-60
lines changed

6 files changed

+311
-60
lines changed

crates/bitcoind_rpc/src/lib.rs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -305,13 +305,13 @@ where
305305
return Ok(PollResponse::Block(res));
306306
}
307307

308-
for cp in emitter.last_cp.iter() {
309-
let res = match client.get_block_info(&cp.hash()) {
308+
for cp_entry in emitter.last_cp.iter() {
309+
let res = match client.get_block_info(&cp_entry.hash()) {
310310
// block not in best chain
311311
Ok(res) if res.confirmations < 0 => continue,
312312
Ok(res) => res,
313313
Err(e) if e.is_not_found_error() => {
314-
if cp.height() > 0 {
314+
if cp_entry.height() > 0 {
315315
continue;
316316
}
317317
// if we can't find genesis block, we can't create an update that connects
@@ -320,6 +320,17 @@ where
320320
Err(e) => return Err(e),
321321
};
322322

323+
// NOTE: Calls to `CheckPoint::<BlockHash>` should alreadys return `Some` as there is no
324+
// prev_blockhash. The handling of the `None` branch here is to avoid oversight when
325+
// changing this to handle `CheckPoint<Header>`.
326+
let cp = match cp_entry.floor_checkpoint() {
327+
Some(cp) => cp,
328+
None => {
329+
let BlockId { height, hash } = cp_entry.block_id();
330+
CheckPoint::new(height, hash)
331+
}
332+
};
333+
323334
// agreement point found
324335
return Ok(PollResponse::AgreementFound(res, cp));
325336
}

crates/chain/src/local_chain.rs

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use core::ops::RangeBounds;
66

77
use crate::collections::BTreeMap;
88
use crate::{BlockId, ChainOracle, Merge};
9-
use bdk_core::ToBlockHash;
109
pub use bdk_core::{CheckPoint, CheckPointIter};
10+
use bdk_core::{CheckPointEntry, ToBlockHash};
1111
use bitcoin::block::Header;
1212
use bitcoin::BlockHash;
1313

@@ -25,7 +25,7 @@ where
2525
// point of agreement
2626
let mut base: Option<CheckPoint<D>> = None;
2727

28-
for cp in init_cp.iter() {
28+
for cp in init_cp.iter().filter_map(|item| item.checkpoint()) {
2929
if cp.height() >= start_height {
3030
extension.insert(cp.height(), cp.data());
3131
} else {
@@ -63,30 +63,42 @@ pub struct LocalChain<D = BlockHash> {
6363
tip: CheckPoint<D>,
6464
}
6565

66-
impl<D> PartialEq for LocalChain<D> {
66+
impl<D: ToBlockHash> PartialEq for LocalChain<D> {
6767
fn eq(&self, other: &Self) -> bool {
6868
self.tip == other.tip
6969
}
7070
}
7171

72-
impl<D> ChainOracle for LocalChain<D> {
72+
impl<D: ToBlockHash> ChainOracle for LocalChain<D> {
7373
type Error = Infallible;
7474

7575
fn is_block_in_chain(
7676
&self,
7777
block: BlockId,
7878
chain_tip: BlockId,
7979
) -> Result<Option<bool>, Self::Error> {
80-
let chain_tip_cp = match self.tip.get(chain_tip.height) {
81-
// we can only determine whether `block` is in chain of `chain_tip` if `chain_tip` can
82-
// be identified in chain
83-
Some(cp) if cp.hash() == chain_tip.hash => cp,
84-
_ => return Ok(None),
85-
};
86-
match chain_tip_cp.get(block.height) {
87-
Some(cp) => Ok(Some(cp.hash() == block.hash)),
88-
None => Ok(None),
89-
}
80+
Ok(|| -> Option<bool> {
81+
let mut cp_iter = self.tip.iter();
82+
let mut cp_item = cp_iter.next()?;
83+
loop {
84+
if cp_item.height() < chain_tip.height {
85+
return None;
86+
}
87+
if cp_item.block_id() == chain_tip {
88+
break;
89+
}
90+
cp_item = cp_iter.next()?;
91+
}
92+
loop {
93+
if cp_item.height() < block.height {
94+
return None;
95+
}
96+
if cp_item.height() == block.height {
97+
return Some(cp_item.hash() == block.hash);
98+
}
99+
cp_item = cp_iter.next()?;
100+
}
101+
}())
90102
}
91103

92104
fn get_chain_tip(&self) -> Result<BlockId, Self::Error> {
@@ -195,7 +207,9 @@ impl<D> LocalChain<D> {
195207
pub fn tip(&self) -> CheckPoint<D> {
196208
self.tip.clone()
197209
}
210+
}
198211

212+
impl<D: ToBlockHash> LocalChain<D> {
199213
/// Get the genesis hash.
200214
pub fn genesis_hash(&self) -> BlockHash {
201215
self.tip.get(0).expect("genesis must exist").block_id().hash
@@ -211,8 +225,8 @@ impl<D> LocalChain<D> {
211225
/// This is a shorthand for calling [`CheckPoint::get`] on the [`tip`].
212226
///
213227
/// [`tip`]: LocalChain::tip
214-
pub fn get(&self, height: u32) -> Option<CheckPoint<D>> {
215-
self.tip.get(height)
228+
pub fn get(&self, height: u32) -> Option<CheckPointEntry<D>> {
229+
self.tip.entry(height)
216230
}
217231

218232
/// Iterate checkpoints over a height range.
@@ -223,7 +237,7 @@ impl<D> LocalChain<D> {
223237
/// This is a shorthand for calling [`CheckPoint::range`] on the [`tip`].
224238
///
225239
/// [`tip`]: LocalChain::tip
226-
pub fn range<R>(&self, range: R) -> impl Iterator<Item = CheckPoint<D>>
240+
pub fn range<R>(&self, range: R) -> impl Iterator<Item = CheckPointEntry<D>>
227241
where
228242
R: RangeBounds<u32>,
229243
{
@@ -325,6 +339,7 @@ where
325339
blocks: self
326340
.tip
327341
.iter()
342+
.filter_map(|entry| entry.checkpoint())
328343
.map(|cp| (cp.height(), Some(cp.data())))
329344
.collect(),
330345
}
@@ -383,14 +398,20 @@ where
383398
) -> Result<ChangeSet<D>, MissingGenesisError> {
384399
let mut remove_from = Option::<CheckPoint<D>>::None;
385400
let mut changeset = ChangeSet::default();
386-
for cp in self.tip().iter() {
387-
let cp_id = cp.block_id();
401+
for cp_entry in self.tip().iter() {
402+
let cp_id = cp_entry.block_id();
388403
if cp_id.height < block_id.height {
389404
break;
390405
}
391406
changeset.blocks.insert(cp_id.height, None);
392407
if cp_id == block_id {
393-
remove_from = Some(cp);
408+
// TODO: This is wrong. What am I doing?
409+
remove_from = match cp_entry.floor_checkpoint() {
410+
Some(floor_cp) if floor_cp.height() < cp_entry.height() => Some(floor_cp),
411+
Some(floor_cp) => floor_cp.prev(),
412+
None => None,
413+
};
414+
break;
394415
}
395416
}
396417
self.tip = match remove_from.map(|cp| cp.prev()) {
@@ -604,8 +625,8 @@ where
604625
let mut curr_orig = None;
605626
let mut curr_update = None;
606627

607-
let mut prev_orig: Option<CheckPoint<D>> = None;
608-
let mut prev_update: Option<CheckPoint<D>> = None;
628+
let mut prev_orig: Option<CheckPointEntry<D>> = None;
629+
let mut prev_update: Option<CheckPointEntry<D>> = None;
609630

610631
let mut point_of_agreement_found = false;
611632

@@ -634,7 +655,9 @@ where
634655
match (curr_orig.as_ref(), curr_update.as_ref()) {
635656
// Update block that doesn't exist in the original chain
636657
(o, Some(u)) if Some(u.height()) > o.map(|o| o.height()) => {
637-
changeset.blocks.insert(u.height(), Some(u.data()));
658+
if let Some(data) = u.data() {
659+
changeset.blocks.insert(u.height(), Some(data));
660+
}
638661
prev_update = curr_update.take();
639662
}
640663
// Original block that isn't in the update
@@ -671,7 +694,12 @@ where
671694
prev_orig_was_invalidated = false;
672695
// OPTIMIZATION 2 -- if we have the same underlying pointer at this point, we
673696
// can guarantee that no older blocks are introduced.
674-
if o.eq_ptr(u) {
697+
// TODO: Is this correct?
698+
let is_eq_ptr = match (o.floor_checkpoint(), u.floor_checkpoint()) {
699+
(Some(o_cp), Some(u_cp)) => o_cp.eq_ptr(&u_cp),
700+
_ => false,
701+
};
702+
if is_eq_ptr {
675703
if is_update_height_superset_of_original {
676704
return Ok((update_tip, changeset));
677705
} else {
@@ -685,7 +713,9 @@ where
685713
} else {
686714
// We have an invalidation height so we set the height to the updated hash and
687715
// also purge all the original chain block hashes above this block.
688-
changeset.blocks.insert(u.height(), Some(u.data()));
716+
if let Some(u_data) = u.data() {
717+
changeset.blocks.insert(u.height(), Some(u_data));
718+
}
689719
for invalidated_height in potentially_invalidated_heights.drain(..) {
690720
changeset.blocks.insert(invalidated_height, None);
691721
}

0 commit comments

Comments
 (0)