Skip to content

Validators sign over BlockHeader, not the whole serialized Block.#6470

Open
deuszx wants to merge 8 commits into
mainfrom
block-header-committement
Open

Validators sign over BlockHeader, not the whole serialized Block.#6470
deuszx wants to merge 8 commits into
mainfrom
block-header-committement

Conversation

@deuszx

@deuszx deuszx commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Motivation

The BlockHeader already commits to every body field through per-field hashes, yet a ConfirmedBlockCertificate signed CryptoHash::new(&Block) — the hash of the whole serialized block, body included. Verifying a block therefore required relaying its entire body, which defeats the purpose of the header's commitments. Signing the header's hash instead lets a party holding only the header verify the block and prove any single body field against it, without the rest of the body.

Proposal

  • Block::hash() is now CryptoHash::new(&header) rather than the hash of the whole block. BlockHeader becomes BcsHashable and Block no longer is, so the body can never enter a hash preimage.
  • ConfirmedBlock/ValidatedBlock memoize that header hash as the signed value, via a new Hashed::with_hash constructor that stores a precomputed hash.
  • The block wire format is unchanged — the header is recomputed from the body on deserialize — so a tampered body yields a different header hash and fails the certificate's signature check.
  • BlockHeader::verifies(&BlockBodyField) checks a single body field against its matching header hash, giving holders of just the header a per-field inclusion-proof API.
  • Adds a BlockBuilder test helper that assembles a block whose header is consistent with its body, and rebuilds the storage tests through it (hand-built, body-inconsistent headers no longer round-trip now that
    the hash commits to the header).

Test Plan

  • A new linera-chain unit test builds a real ConfirmedBlockCertificate, asserts its signed hash is reproducible from the BlockHeader alone, and proves one body field via BlockHeader::verifies without the
    rest of the body.
  • Storage round-trip tests are rebuilt with populated, header-consistent bodies through the new BlockBuilder.
  • CI runs the full suite across the touched crates (linera-base, linera-chain, linera-core, linera-storage, linera-indexer).

Release Plan

  • These changes will follow regular release cycle.

Links

  • reviewer checklist
  • Follow-up PR adopting this in the EVM light client (linera-bridge) stacks on this branch.

deuszx added 6 commits June 8, 2026 17:37
The block hash signed in a ConfirmedBlockCertificate now commits to the
BlockHeader (which already hashes every body field) rather than the whole
serialized Block: the hash is CryptoHash::new(&header), and BcsHashable is
removed from Block so the body can no longer enter a hash preimage.

The block wire format is unchanged - the header is recomputed from the body on
deserialize - so a tampered body produces a different header hash and fails the
certificate's signature check.
BlockHeader::verifies checks that a single BlockBody field matches the
corresponding hash in the header, so a light client holding only the header can
prove one field belongs to the block.
Builds a real ConfirmedBlockCertificate and checks that its signed hash is
reproducible from the BlockHeader alone, then proves one body field via
BlockHeader::verifies without the rest of the body.
The block hash now commits to the header, which is recomputed from the body on
deserialization, so storage tests that hand-built headers with arbitrary,
body-inconsistent hashes could no longer round-trip. Add a BlockBuilder test
helper to linera-chain that assembles a block (transactions, messages, events,
oracle responses, blobs, operation results) whose header is consistent with its
body, and rebuild the storage test blocks with populated bodies through it.
Comment thread linera-storage/src/db_storage.rs
@deuszx deuszx changed the title Block header committement Block header commitment Jun 9, 2026
@deuszx deuszx force-pushed the block-header-committement branch from acd0e24 to 96456e7 Compare June 9, 2026 14:32
@deuszx deuszx changed the title Block header commitment Validators sign over BlockHeader, not the whole serialized Block. Jun 9, 2026
@deuszx deuszx marked this pull request as ready for review June 9, 2026 14:48
@deuszx deuszx requested a review from ma2bd June 9, 2026 14:49
Comment thread linera-base/src/hashed.rs
/// Creates a [`Hashed`] from a value and a precomputed hash, without recomputing it.
///
/// The caller is responsible for the hash being the canonical hash of `value`.
pub fn with_hash(value: T, hash: CryptoHash) -> Self {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I would call it new_unchecked or with_hash_unchecked

@ma2bd ma2bd Jun 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It feels a bit wrong to use Hashed in the first place if you need this method but maybe that's fine. After all, there is no verify method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The idea is that whatever is inside can have a hash computed and it's memoized.

@ma2bd ma2bd Jun 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ok. In that case, perhaps we want the container to be agnostic then: new should be rename with_crypto_hash and the manual hash (yours) with_unchecked_hash.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The container is agnostic – it accept any type as long as it implements BcsHashable. It's a textbook smart constructor. I will rename the new method to with_unchecked_hash.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants