Skip to content

Conversation

@ValuedMammal
Copy link
Collaborator

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.

fix #2021

Description

Notes to the reviewers

Changelog notice

Checklists

All Submissions:

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

`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.
@evanlinjin
Copy link
Member

How does this relate to PR #2024? That PR addresses the same issue and handles validation in insert() and merge_chains as well. As written, this only validates push() with contiguous heights - insert() and merge_chains can still create inconsistent chains. Is this intended as a simpler alternative approach, or a foundation for the work in #2024?

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 prev_blockhash but not yet populated), we can't properly validate in insert() or merge_chains().

For example, if I insert a checkpoint at height 100 with prev_blockhash = X, and later insert at height 99 with blockhash = Y where X != Y, we need to detect that conflict. Either:

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?

@ValuedMammal
Copy link
Collaborator Author

Here we don't intend to make the CheckPoint::data field optional, instead displacing a block will just remove it from the chain.

It's missing coverage for when insert properly displaces a conflicting block at the previous height.

The previous BlockId inferred by the current chain could in theory be implemented directly on the CheckPoint like this. I haven't tackled the issues with merge_chains yet.

/// 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,
    })
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Ensure CheckPoint chain methods validate and link via previous blockhash

2 participants