Skip to content

refactor!: Implement generics for CheckPoint, LocalChain, and spk_client types #1582

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

LagginTimes
Copy link
Contributor

@LagginTimes LagginTimes commented Aug 29, 2024

Implements #1937.

Description

This PR is a step towards header checkpointing for bdk_electrum. The goal is to be able to store whole headers in CheckPoint so 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.

TODO:

  • Change CheckPoint to take in a generic.
  • Change LocalChange to take in a generic.
  • Change spk_client types to take in generics.

Notes to the reviewers

Changelog notice

  • CheckPoint takes in a generic.
  • LocalChain and ChangeSet take in generics.
  • spk_client types can take in generics.

Checklists

All Submissions:

  • I've signed all my commits
  • I followed the contribution guidelines
  • I ran cargo fmt and cargo clippy before committing

@LagginTimes LagginTimes marked this pull request as draft August 29, 2024 09:59
@LagginTimes LagginTimes changed the title refactor(core): CheckPoint takes a generic refactor(core): Implement generics for CheckPoint and LocalChain Aug 29, 2024
@LagginTimes LagginTimes force-pushed the generic_checkpoint branch 3 times, most recently from a899049 to 7c13781 Compare September 2, 2024 02:53
@LagginTimes LagginTimes force-pushed the generic_checkpoint branch 2 times, most recently from ea1cf8b to bf90ea5 Compare September 3, 2024 06:15
@LagginTimes LagginTimes force-pushed the generic_checkpoint branch 5 times, most recently from c278804 to 6300d7c Compare September 5, 2024 03:43
@LagginTimes LagginTimes changed the title refactor(core): Implement generics for CheckPoint and LocalChain refactor(core): Implement generics for CheckPoint, LocalChain, and spk_client types Sep 5, 2024
@notmandatory notmandatory added this to the 1.2.0 milestone Feb 24, 2025
@notmandatory notmandatory removed this from BDK Wallet Mar 4, 2025
@notmandatory notmandatory moved this to In Progress in BDK Chain Mar 4, 2025
@notmandatory notmandatory removed this from the 1.2.0 milestone Mar 4, 2025
@LagginTimes LagginTimes force-pushed the generic_checkpoint branch 3 times, most recently from 765d584 to 7488001 Compare March 10, 2025 20:16
@notmandatory notmandatory moved this from In Progress to Needs Review in BDK Chain Mar 11, 2025
@notmandatory notmandatory moved this from Needs Review to In Progress in BDK Chain Mar 11, 2025
@LagginTimes LagginTimes marked this pull request as draft March 11, 2025 18:12
@luisschwab
Copy link
Member

luisschwab commented Mar 17, 2025

Dropping in to note that this would be incredible useful for bdk_kyoto. It is likely that a LocalChain generic over Header would allow the CBF system to simply use the LocalChain the chain backend, and the persistence may be shared between a Wallet and the node.

+1 for this feature, bdk_floresta could share persistence with bdk_wallet as well.

@ValuedMammal
Copy link
Collaborator

ValuedMammal commented Mar 31, 2025

At a high level I think the API would be cleaner if the generic methods had the simpler names (new, push, extend, insert) and the old methods were renamed push_block_id, extend_block_ids, etc.

CheckPoint::data method can be two methods:

fn data_ref(&self) -> &B {}

fn data(&self) -> B where B: Clone {} // or Copy

LocalChain should have more of a unified interface. Most of the methods should be generic and the only methods under impl LocalChain<BlockHash> {} should be the ones that only make sense in the old design (from_genesis_hash, insert_block, etc). Having two versions of apply_changeset and _check_changeset_is_applied isn't very helpful.

Remaining questions:

  1. Will it be possible to migrate an existing sqlite database to one that persists a local chain of headers, or will it require adding a new SQL table?
  2. Are there plans to enable syncing a local chain of headers using bdk_electrum / esplora?

Edit: I added my review with some API suggestions in LagginTimes#5

@notmandatory notmandatory added this to the 2.0.0 milestone Apr 22, 2025
@evanlinjin
Copy link
Member

@LagginTimes is this ready to review again? Remember to mark it as so and request reviewers, otherwise no one will know about it.

@LagginTimes LagginTimes force-pushed the generic_checkpoint branch from 4148e29 to 3d7f48c Compare May 14, 2025 19:07
@LagginTimes LagginTimes force-pushed the generic_checkpoint branch from 3d7f48c to 69d8643 Compare May 14, 2025 19:22
@LagginTimes LagginTimes marked this pull request as ready for review May 14, 2025 20:02
@LagginTimes LagginTimes requested a review from evanlinjin May 14, 2025 20:02
@evanlinjin
Copy link
Member

What was the discussion around breaking changes? Were we okay to break the API?

@LagginTimes LagginTimes changed the title refactor: Implement generics for CheckPoint, LocalChain, and spk_client types refactor!: Implement generics for CheckPoint, LocalChain, and spk_client types May 15, 2025
@LagginTimes
Copy link
Contributor Author

What was the discussion around breaking changes? Were we okay to break the API?

Iirc the consensus was for this to be a breaking change to be a cleaner API.

Copy link
Member

@evanlinjin evanlinjin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pushing this forward.

Please double check (brownie points for triple check) over all the methods of a struct to see whether they are coherent, whether any more methods can be made generic and whether the method names and generic parameter names are coherent and make sense.

Let's make all generic parameter names for block data be consistent across the board. I'm in favor of B.

pub struct LocalChain {
tip: CheckPoint,
#[derive(Debug, Clone)]
pub struct LocalChain<H = BlockHash> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would use B (as in block) or D (as in data) for the type generic instead of H (which I'm assuming stands for header, but we aren't necessarily using headers here).

self.tip.get(0).expect("genesis must exist").hash()
}

impl LocalChain<BlockHash> {
/// Construct [`LocalChain`] from genesis `hash`.
#[must_use]
pub fn from_genesis_hash(hash: BlockHash) -> (Self, ChangeSet) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove this and have from_genesis(data: B) instead which would work with all types.

self.tip.get(0).expect("genesis must exist").hash()
}

impl LocalChain<BlockHash> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make these following methods generic for all Hs.

impl LocalChain<D> {
    pub fn from_genesis(data: D) -> (Self, ChangeSet<D>);
    pub fn from_changeset(changeset: ChangeSet<D>) -> Result<Self>;
    pub fn from_blocks(blocks: BTreeMap<u32, D>) -> Result<Self>;
    pub fn apply_update(&mut self, update: CheckPoint<D>) -> Result<ChangeSet>;

Comment on lines +248 to +252
pub fn apply_blockhash_changeset(
&mut self,
changeset: &ChangeSet,
) -> Result<(), MissingGenesisError> {
self.apply_changeset(changeset)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look helpful as .apply_changeset does the exact same thing.

@@ -279,29 +258,7 @@ impl LocalChain {
///
/// Replacing the block hash of an existing checkpoint will result in an error.
pub fn insert_block(&mut self, block_id: BlockId) -> Result<ChangeSet, AlterCheckPointError> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is no longer useful as .insert_data does the same thing. Just rename .insert_data to .insert_block.

Comment on lines +143 to +145
pub fn from_block_ids(
block_ids: impl IntoIterator<Item = BlockId>,
) -> Result<Self, Option<Self>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's rework this method to be generic over D:

Suggested change
pub fn from_block_ids(
block_ids: impl IntoIterator<Item = BlockId>,
) -> Result<Self, Option<Self>> {
pub fn from_blocks(
blocks: impl IntoIterator<Item = (u32, D)>,
) -> Result<Self, Option<Self>> {

Comment on lines +159 to +169
pub fn extend_block_ids(
self,
blockdata: impl IntoIterator<Item = BlockId>,
) -> Result<Self, Self> {
self.extend(
blockdata
.into_iter()
.map(|block| (block.height, block.hash)),
)
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this as extend does the same thing.

Comment on lines +178 to +180
pub fn insert_block_id(self, block_id: BlockId) -> Self {
self.insert(block_id.height, block_id.hash)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove.

Comment on lines +186 to +188
pub fn push_block_id(self, block: BlockId) -> Result<Self, Self> {
self.push(block.height, block.hash)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove.

@@ -387,14 +388,14 @@ impl<I> SyncRequest<I> {
/// See also [`SyncRequest`].
#[must_use]
#[derive(Debug)]
pub struct SyncResponse<A = ConfirmationBlockTime> {
pub struct SyncResponse<B = BlockHash, A = ConfirmationBlockTime> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this is a breaking change, but introducing a generic parameter before one that already exists can be really annoying. Introduce it at the end please.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
module-blockchain new feature New feature or request
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

8 participants