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

feat(wallet): add check for network and genesis_hash consistency #1777

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

oleonardolima
Copy link
Contributor

@oleonardolima oleonardolima commented Dec 16, 2024

fixes #1716

Description

Adds a new check on both create_with_params and load_with_params to validate that the given genesis_hash parameter matches the genesis_hash for expected Network, returns a new DescriptorError::Mismatch or LoadError::LoadMismatch error variants, respectively.

Notes to the reviewers

I'm not sure if adding the check on load_with_params is needed, and if that branch will ever be reached, at least it seems not trivial to test it using the standard of tests we currently have, that its: 1) creating a wallet; 2) loading and asserting the expected checks.

Changelog notice

  • Adds a new check for consistency of genesis_hash and network parameters.
  • Adds a new error MismatchError enum and Descriptor::Mismatch variant.

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

Bugfixes:

  • This pull request breaks the existing API
  • I've added tests to reproduce the issue which are now passing
  • I'm linking the issue being fixed by this PR

@oleonardolima oleonardolima force-pushed the feat/add-check-for-network-consistency branch 2 times, most recently from c287ce3 to e60e101 Compare December 16, 2024 18:50
Comment on lines +521 to +527
let network_genesis_hash = genesis_block(network).block_hash();
if network_genesis_hash != exp_genesis_hash {
return Err(LoadError::Mismatch(LoadMismatch::Genesis {
loaded: network_genesis_hash,
expected: exp_genesis_hash,
}));
}
Copy link
Contributor Author

@oleonardolima oleonardolima Dec 16, 2024

Choose a reason for hiding this comment

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

I'm still unsure of how to properly test this one (or if it's even needed), because I can't initially create a wallet with network and genesis_hash that are valid (consistent) on create_with_params, but it will fail with an inconsistent check_genesis_hash on load_with_params.

It'll always fail on the previous check.

@oleonardolima oleonardolima self-assigned this Dec 16, 2024
@oleonardolima oleonardolima added module-wallet audit Suggested as result of external code audit labels Dec 16, 2024
@oleonardolima oleonardolima marked this pull request as ready for review December 16, 2024 19:02
@oleonardolima oleonardolima changed the title wip(feat): panics when genesis hash and network mismatch feat(wallet): add check for network and genesis_hash consistency Dec 16, 2024
- add new `MismatchError::Genesis` and `DescriptorError::Mismatch` error
  variants, when the genesis_hash parameter and network are
  inconsistent.
- add check on `create_with_params` to check that `genesis_hash` and
  `network` genesis hash are consistent, return `Descriptor::Mismatch`
  error otherwise.
- add check on `load_with_params` to check that `check_genesis_hash`
  is also consistent with the genesis hash of current loaded `network`,
  return the `LoadMismatch` error variant otherwise.
@ValuedMammal
Copy link
Contributor

I had previously understood that bdk allowed you to use a custom genesis hash, meaning one that doesn't match the chain hash of the specified network. I interpreted the issue as saying the network isn't checked to be among the valid networks of a descriptor, but as you said that logic exists in into_wallet_descriptor.

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.

Approach-NACK, Concept-NACK

It seems the intention of the PR is to enforce a 1-possible-genesis-hash-per-network-variant invariance. The correct way to do this is to change the datastructure, not enforce the invariance ad-hoc with errors.

So now the question is whether we want to be able have the caller provide a custom genesis hash. I think we still should, but maybe our approach needs to be a bit different.

It is true that the Network enum is intended to represent a single network per variant so the current code is hard to reason with. To fix this, maybe we can introduce a new enum:

pub enum BdkNetwork {
    Network(bitcoin::Network),
    Custom(BlockHash),
}

impl BdkNetwork {
    /// Handy to work with keys.
    ///
    /// The `Custom` variant will always be `Text`.
    pub fn kind(&self) -> bitcoin::NetworkKind {}
}

Comment on lines 375 to +385
let genesis_hash = params
.genesis_hash
.unwrap_or(genesis_block(network).block_hash());

if genesis_hash.ne(&genesis_block(network).block_hash()) {
return Err(DescriptorError::Mismatch(MismatchError::Genesis {
network: genesis_block(network).block_hash(),
parameter: genesis_hash,
}));
}

Copy link
Member

Choose a reason for hiding this comment

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

As mentioned by @ValuedMammal, the intention here is so the caller can provide a custom genesis hash.

However, if the intention is to enforce a 1-possible-genesis-hash-per-network-variant invariance, changing the data structure to disallow the invariance would make a lot more sense. I.e. remove params.genesis_hash.

@oleonardolima
Copy link
Contributor Author

Approach-NACK, Concept-NACK

It seems the intention of the PR is to enforce a 1-possible-genesis-hash-per-network-variant invariance. The correct way to do this is to change the datastructure, not enforce the invariance ad-hoc with errors.

Agree, if we opt-out from allowing custom genesis_hash/networks, we can just remove the parameter altogether.

So now the question is whether we want to be able have the caller provide a custom genesis hash. I think we still should, but maybe our approach needs to be a bit different.

Yes, I agree with also allowing a custom genesis_hash. I didn't have that in mind when working on the changes. However, by the initial issue description, I reasoned that the current implementation is too loose and would allow a user to use a custom Genesis hash on Mainnet and Testnet.

It is true that the Network enum is intended to represent a single network per variant so the current code is hard to reason with. To fix this, maybe we can introduce a new enum:

pub enum BdkNetwork {
    Network(bitcoin::Network),
    Custom(BlockHash),
}

impl BdkNetwork {
    /// Handy to work with keys.
    ///
    /// The `Custom` variant will always be `Text`.
    pub fn kind(&self) -> bitcoin::NetworkKind {}
}

Thanks, this seems like a better approach.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
audit Suggested as result of external code audit module-wallet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Verify network consistency between chain genesis and descriptors
3 participants