ITADN

When does `LocalChain::apply_update` invalidate checkpoints?

#2095OpenValuedMammal 创建于 2026-01-15
discussion
V
ValuedMammalcommented
**Describe the bug** ### 1. Genesis block replacement The documentation for `merge_chains` says that a `CannotConnectError` occurs when the update attempts to replace the genesis block of the original chain. However, as demonstrated by the test case "fix blockhash before agreement point" in `update_local_chain`, the implementation does allow the genesis hash to be changed if the update provides a new hash at height 0 and the rest of the update is consistent. The function doesn't prevent replacing the genesis block - it only requires that the update is unambiguous and properly invalidates any conflicting blocks above the point of agreement. For users this mismatch between documentation and implementation can lead to confusion. If a user expects that the genesis block can never be changed as the docs suggest, they may be surprised to find that it can be "fixed" by an update. This could affect how they reason about the behavior of `LocalChain` as it relates to invalidation heights and chain reorgs. For correctness, allowing the genesis block to be replaced may be intentional, but the documentation should clearly state this. If it is not intentional, the implementation should be fixed to match the documentation. **To Reproduce** Original `[0, A]` Update `[0', A]` Result `[0', A]` (Ok) **Expected behavior** Result `CannotConnectError { try_include_height: 0 }` (Err) ### 2. Chain reorganization The current behavior of `merge_chains` does not allow a basic reorg by switching forks unless the update explicitly includes a point of agreement. If we continue traversing blocks below the last update height, the function cannot unambiguously determine how to connect the update to the original chain, and thus returns a `CannotConnectError`. However if the update at the invalidation height claims to connect to a block in the original chain, or otherwise doesn't conflict with it, then presumably it ought to connect unambiguously, allowing a reorganization to occur by switching to the longest chain. **To Reproduce** Original `[0, A]` Update `[A', B']` Result `CannotConnectError { try_include_height: 0 }` (Err) **Expected behavior** Result `[0, A', B']` (Ok) **Additional context** - Documentation of [`merge_chains`](https://github.com/bitcoindevkit/bdk/blob/a161ee24d38b038a51161816a9f90a87f90d157d/crates/chain/src/local_chain.rs#L592) - The unfortunate restriction with `LocalChain` https://github.com/bitcoindevkit/bdk/pull/2000#discussion_r2265233190 **Build environment** - BDK tag/commit: a161ee24d38b038a51161816a9f90a87f90d157d - OS+version: macOS 15.7.2 (Sequoia) - Rust/Cargo version: 1.92.0 - Rust/Cargo target: aarch64-apple-darwin **Which backend(s) are relevant (if any)?** - [ ] Electrum - [ ] Esplora - [ ] Bitcoin Core RPC - [x] None / not backend-related (e.g. `bdk_chain`, `bdk_core`) - [ ] Other (please specify): `____` **Is this blocking production use?** - [ ] Yes - [x] No
10 条评论