refactor!: Implement generics for CheckPoint, LocalChain, and spk_client types#1582
Conversation
CheckPoint takes a genericCheckPoint and LocalChain
a899049 to
7c13781
Compare
7c13781 to
d934a6c
Compare
ea1cf8b to
bf90ea5
Compare
c278804 to
6300d7c
Compare
CheckPoint and LocalChainCheckPoint, LocalChain, and spk_client types
6300d7c to
315f687
Compare
|
Tests are passing for me. But I think we should still have the function diff--- a/crates/chain/src/local_chain.rs
+++ b/crates/chain/src/local_chain.rs
@@ -270,8 +270,9 @@ where
};
let (mut chain, _) = Self::from_genesis(genesis_data);
chain.apply_changeset(&changeset)?;
+ debug_assert!(chain._check_changeset_is_applied(&changeset));
Ok(chain)
}
/// Construct a [`LocalChain`] from a given `checkpoint` tip.
@@ -303,16 +304,18 @@ where
update: CheckPoint<D>,
) -> Result<ChangeSet<D>, CannotConnectError> {
let (new_tip, changeset) = merge_chains(self.tip.clone(), update)?;
self.tip = new_tip;
+ debug_assert!(self._check_changeset_is_applied(&changeset));
Ok(changeset)
}
/// Apply the given `changeset`.
pub fn apply_changeset(&mut self, changeset: &ChangeSet<D>) -> Result<(), MissingGenesisError> {
let old_tip = self.tip.clone();
let new_tip = apply_changeset_to_checkpoint(old_tip, changeset)?;
self.tip = new_tip;
+ debug_assert!(self._check_changeset_is_applied(changeset));
Ok(())
}
/// Derives an initial [`ChangeSet`], meaning that it can be applied to an empty chain to
@@ -401,8 +404,30 @@ where
None => return Ok(ChangeSet::default()),
};
Ok(changeset)
}
+
+ fn _check_changeset_is_applied(&self, changeset: &ChangeSet<D>) -> bool {
+ let mut cur = self.tip.clone();
+ for (&exp_height, exp_data) in changeset.blocks.iter().rev() {
+ match cur.get(exp_height) {
+ Some(cp) => {
+ if cp.height() != exp_height
+ || Some(cp.hash()) != exp_data.map(|d| d.to_blockhash())
+ {
+ return false;
+ }
+ cur = cp;
+ }
+ None => {
+ if exp_data.is_some() {
+ return false;
+ }
+ }
+ }
+ }
+ true
+ }
}
|
b5d4872 to
e1e1959
Compare
e1e1959 to
ac0cd4f
Compare
ac0cd4f to
58a5566
Compare
|
ACK dabb869 |
There was a problem hiding this comment.
ACK 0a39fa7 pending a CI fix.
I left comments mostly for discussion. Also there was a comment #1582 (review) suggesting to use B instead of D. I don't have much preference though.
|
I haven't done a deep review but I support merging this now that we're done with the |
There was a problem hiding this comment.
utACK d4bd7e5
The code looks good to me, I only left a minor question and nit. I didn't test it on a complicated example yet.
|
ACK 4dce5ad |
notmandatory
left a comment
There was a problem hiding this comment.
utACK 4dce5ad
I also re-read through the whole history and this one has certainly been a journey so thanks to @LagginTimes and all the reviewers for sticking with it.
I look forward to the followup PRs to integrate chain client specific data in the checkpoints. I expect custom data will break existing persistence so being able to persist the data in a generic way and having a good persistence test suite such as #2012 will be handy.
|
Looking back through the codebase, I noticed that we've introduced a condition that wasn't there before, and I’m wondering about the best way to handle it. Since a One idea is to modify pub trait ToBlockHash {
/* OTHER METHODS */
/// Returns `None` if the type has no knowledge of the previous blockhash.
fn prev_blockhash(&self) -> Option<BlockHash>;
}Then we need to modify
|
Yes in principle I agree with this if the update is strictly extending the parent chain by the next consecutive block. If the type doesn't know the previous hash, like in the case of |
|
I've started experimenting with my suggestion in #1582 (comment). This is the WIP commit: evanlinjin@8f374cc. I'm quite happy with the implementation of it (unless proven otherwise). What is missing:
Feel free to continue this work as you see fit. I'm wondering if we should merge this PR as is, and introduce "ghost-checkpoint"s in a separate PR? |
I think it makes sense to merge #1582 as it is now. The additional work around ghost-checkpoints, backref checks, and more docs/tests can be done in a follow-up PR to keep the scope focused and avoid resetting review. I would be happy to continue the work in a follow-up PR. |
|
@LagginTimes I'll be happy with that. Do we have a plan for back-porting the non-merged chain-source fixes (#2005, #2011, #2000)? Note that the |
For this I would look at things we already have such as |
Implements #1937.
Description
This PR is a step towards header checkpointing for
bdk_electrum. The goal is to be able to store whole headers inCheckPointso they do not have to be re-downloaded. Storing headers this way would be a prerequisite for caching of merkle proofs and for median time passed.Notes to the reviewers
Changelog notice
CheckPointtakes in a generic.LocalChainandChangeSettake in generics.spk_clienttypes can take in generics.Checklists
All Submissions:
cargo fmtandcargo clippybefore committing