-
Notifications
You must be signed in to change notification settings - Fork 419
feat(core)!: Add prev_blockhash to trait ToBlockHash
#2091
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
base: master
Are you sure you want to change the base?
feat(core)!: Add prev_blockhash to trait ToBlockHash
#2091
Conversation
|
How does this relate to PR #2024? That PR addresses the same issue and handles validation in I'm also wondering if this is meant to show that we can achieve simpler validation by not introducing optional data fields. However, there is a gap: without tracking "ghost checkpoints" (positions inferred from For example, if I insert a checkpoint at height 100 with
The current PR doesn't address either path, so I don't think it fully resolves #2021. Is there a different approach you have in mind for handling these cases? |
|
Here we don't intend to make the It's missing coverage for when The previous /// Get the previous [`BlockId`] of this checkpoint.
///
/// I.e. the height and hash of the block immediately preceding this one by consensus,
/// if it can be inferred from the `prev_blockhash` of the inner block data.
pub fn prev_block_id(&self) -> Option<BlockId> {
let prev_height = self.height().checked_sub(1)?;
let prev_hash = self.0.data.prev_blockhash()?;
Some(BlockId {
height: prev_height,
hash: prev_hash,
})
} |
0f7db83 to
a2ea12f
Compare
- `merge_chains` now errors if update conflicts with genesis hash - Refactored `merge_chains` to be more easily understood by adding code comments. Updated test expectations of `update_local_chain` in `tests/test_local_chain.rs`.
`prev_blockhash` returns the hash of the previous block data, if it is known, and `None` otherwise. Implement `prev_blockhash` for `Header`, which returns the header's previous blockhash. Fixed `CheckPoint::push` to now check that if the height being pushed directly follows the current CP height, and the `data.prev_blockhash` contains some value, the previous hash of `data` is the same as the current CP hash. This is necessary to prevent inconsistent or invalid chains from being constructed. Fixed `insert` to displace conflicting blocks by reverting to a previous base if it's found to conflict with the inserted data.
…rom `extend` Now that `extend` detects conflicts (via `push`) we need to handle the error and propagate it up to `apply_update` in case `merge_chains` fails. Added `ApplyChangeSetError` struct internally but don't expose it. Change `LocalChain::from_blocks` to propagate the error from `CheckPoint::from_blocks`.
Previously, methods of `CheckPoint` were constrained by `D: Copy`, and this was to prevent a possible memory leak caused by the use of `mem::forget` in the Drop impl for `CheckPoint`. Since the memory leak concern has been addressed (bitcoindevkit#2056), we relax the constraint to `Clone` instead which permits instances of `CheckPoint<D>` where `D` is not necessarily a Copy type.
a2ea12f to
d5b2513
Compare
prev_blockhash to trait ToBlockHashprev_blockhash to trait ToBlockHash
evanlinjin
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you're taking over ticket #2021, feel free to checkout my unfinished work for inspiration: https://github.com/bitcoindevkit/bdk/compare/cp_entry.
| // implication invalid. | ||
| tail = vec![]; | ||
| break cp.prev().expect("can't be called on genesis block"); | ||
| // We're replacing a block, so the tail is no longer valid. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also displace the tail if:
data'sprev_blockhashconflicts with the block directly below. The block directly below should also be displaced.data'sblockhashconflicts with the theprev_blockhashof the block directly above.
To achieve this, I recommend using CheckPointEntry instead of CheckPoint and add a method such as CheckPointEntry::prev() - this requires the CheckPointEntry::BlockId variant to have a CheckPoint field.
Naming suggestion
/// An entry yielded by [`CheckPointEntryIter`].
#[derive(Debug, Clone)]
pub enum CheckPointEntry<D> {
/// A placeholder entry: there is no checkpoint stored st this height,
/// but the checkpoint one height above links back here via it's `prev_blockhash`.
Placeholder {
/// The block ID at *this* height.
block_id: BlockId,
/// The checkpoint one height *above* that links back to `block_id`.
checkpoint_above: CheckPoint<D>,
},
/// A real checkpoint recorded at this height.
Occupied(CheckPoint<D>),
}| /// (a.k.a. the update and original chain both have a block above the point of agreement, but | ||
| /// their heights do not overlap). | ||
| /// - The update attempts to replace the genesis block of the original chain. | ||
| fn merge_chains( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The refactored merge_chains does not do anything meaningfully different. It also does not make it easier to read.
The important change to make is to make sure merge_chains takes prev_blockhashes into consideration. The refactoring does not do that.
Suggested changes
- Iterate over
CheckPointEntryinstead ofCheckPoint. Rationale:prev_blockhashes are also block hashes so they can be points of agreements/invalidation. - Only insert
Some(data)intoChangeSetif there is an actualCheckPointin the update chain at that height. - Only insert
NoneintoChangeSetif there is an actualCheckPointin the original chain at that height.
Suggested tests
These tests uses a data type that returns Some for prev_blockhash:
prev_blockhash invalidates
- Original:
1:A,2:B,4:D - Update:
1:A,3:C' (prev=2:B') - Result:
1:A,3:C' (prev=2:B')
Connects due to prev_blockhash
- Original:
2:B,3:C - Update:
2:B,4:D (prev=3:C) - Result:
2:B,3:C,4:D
prev_blockhashreturns the hash of the previous block data, if it is known, andNoneotherwise.Implement
prev_blockhashforHeader, which returns the header's previous blockhash.Fixed
CheckPoint::pushto now check that if the height being pushed directly follows the current CP height, and thedata.prev_blockhashcontains some value, the previous hash ofdatais the same as the current CP hash. This is necessary to prevent inconsistent or invalid chains from being constructed.fix #2021
Description
Notes to the reviewers
Changelog notice
Checklists
All Submissions:
New Features:
Bugfixes: