Skip to content
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

LocalChain with hardwired genesis block #1178

Merged
merged 13 commits into from
Nov 16, 2023

Conversation

evanlinjin
Copy link
Member

@evanlinjin evanlinjin commented Oct 15, 2023

closes #1079
closes #1107

Description

Many methods of TxGraph require a chain_tip: BlockId input to use against a ChainOracle implementation. This is used to ask the ChainOracle implementation whether a certain block exists in the chain identified by the chain_tip. This guarantees that the TxGraph methods will return a consistent history of transactions.

However, the ChainOracle trait's get_chain_tip method returns an option of BlockId. It becomes unclear what to do when get_chain_tip returns None.

This PR changes the ChainOracle::get_chain_tip method to always return a BlockId (no Option). LocalChain now hardwires the genesis block in order to implement ChainOracle.

bdk::Wallet and bdk_file_store::Store are changed to have separate constructor methods for initializing a fresh instance and recovering a previous instance from persistence.

Notes to the reviewers

Changelog notice

  • Changed ChainOracle::get_chain_tip method to return a BlockId instead of an Option of a BlockId.
  • Refactored LocalChain so that the genesis BlockId is hardwired. This way, the ChainOracle::get_chain_tip implementation can always return a tip.
  • Add is_empty method to PersistBackend. This returns true when there is no data in the persistence.
  • Changed Wallet::new to initialize a fresh wallet only.
  • Added Wallet::load to restore an instance of a wallet.
  • Replaced Store::new with separate methods to create/open the database file.

Checklists

All Submissions:

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

New Features:

  • I've added tests for the new feature
  • I've added docs for the new feature

crates/bdk/README.md Outdated Show resolved Hide resolved
@nondiremanuel nondiremanuel added this to the 1.0.0-alpha.3 milestone Oct 22, 2023
@evanlinjin evanlinjin self-assigned this Oct 23, 2023
@evanlinjin evanlinjin force-pushed the issue/1079 branch 6 times, most recently from 7c6164d to eb39808 Compare October 27, 2023 07:31
@evanlinjin evanlinjin changed the title WIP: LocalChain with hardwired genesis block LocalChain with hardwired genesis block Oct 27, 2023
@evanlinjin

This comment was marked as resolved.

Copy link
Contributor

@LLFourn LLFourn left a comment

Choose a reason for hiding this comment

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

ACK 632b806

///
/// `start_height` is the block height to start emitting blocks from.
pub fn from_height(client: &'c C, start_height: u32) -> Self {
/// TODO
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder what we can do so that we don't forget to write this doc.

Copy link
Member

Choose a reason for hiding this comment

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

Looks like docs here should be something like:

Suggested change
/// TODO
/// Construct a new [`Emitter`] with the given RPC `client`, `last_cp` and `start_height`.
///
/// `last_cp` is the check point used to find the latest block which is still part of the best chain.
/// `start_height` is the block height to start emitting blocks from.

Copy link
Contributor

@vladimirfomene vladimirfomene left a comment

Choose a reason for hiding this comment

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

Great work Evan! I just have some nits.

impl<D> Wallet<D> {
/// Create a wallet from a `descriptor` (and an optional `change_descriptor`) and load related
/// transaction data from `db`.
/// Initialize an empty [`Wallet`].
Copy link
Contributor

Choose a reason for hiding this comment

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

What does empty here mean? From my understanding, it means the wallet has an empty state (empty IndexedTxGraph, empty localchain). We might want to improve the doc here.

Self::load_from_changeset(descriptor, change_descriptor, db, changeset)
}

fn load_from_changeset<E: IntoWalletDescriptor>(
Copy link
Contributor

Choose a reason for hiding this comment

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

nits: I will change the name of this method to init_from_changeset. Feel free to ignore if it doesn't resonate.

{
/// Creates a new store from a [`File`].
/// Create a new [`Store`] file in write-only mode; error if the file exists.
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it really in write only mode given that the file is also given read option on line 65

Copy link
Member

@notmandatory notmandatory left a comment

Choose a reason for hiding this comment

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

ACK e342a8c

Looks good, nice to have that chain tip Option removed!

///
/// `start_height` is the block height to start emitting blocks from.
pub fn from_height(client: &'c C, start_height: u32) -> Self {
/// TODO
Copy link
Member

Choose a reason for hiding this comment

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

Looks like docs here should be something like:

Suggested change
/// TODO
/// Construct a new [`Emitter`] with the given RPC `client`, `last_cp` and `start_height`.
///
/// `last_cp` is the check point used to find the latest block which is still part of the best chain.
/// `start_height` is the block height to start emitting blocks from.

@notmandatory
Copy link
Member

This should also close #1107 .

@notmandatory
Copy link
Member

@evanlinjin looks like this one has a conflict so needs a rebase before I can merge it.

This ensures that `LocalChain` will always have a tip. The `ChainOracle`
trait's `get_chain_tip` method no longer needs to return an option.
`Wallet::new` now creates a new wallet. `Wallet::load` loads an existing
wallet. The network type is now recoverable from persistence. Error
types have been simplified.
These methods try to load wallet from persistence and initializes the
wallet instead if non-existant.

An internal helper method `create_signers` is added to reuse code.
Documentation is also improved.
`PersistBackend::is_empty` is removed. Instead, `load_from_persistence`
returns an option of the changeset. `None` means persistence is empty.
This is a better API than a separate method. We can now differentiate
between a persisted single changeset and nothing persisted.

`Store::aggregate_changeset` is refactored to return a `Result` instead
of a tuple. A new error type (`AggregateChangesetsError`) is introduced
to include the partially-aggregated changeset in the error. This is a
more idiomatic API.
@evanlinjin
Copy link
Member Author

@notmandatory don't merge yet, I want to address comments and make clippy happier.

@notmandatory notmandatory merged commit bc8d6a3 into bitcoindevkit:master Nov 16, 2023
12 checks passed
@ValuedMammal ValuedMammal mentioned this pull request Nov 20, 2023
8 tasks
@notmandatory notmandatory mentioned this pull request Jan 3, 2024
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Archived in project
5 participants