Validators sign over BlockHeader, not the whole serialized Block.#6470
Validators sign over BlockHeader, not the whole serialized Block.#6470deuszx wants to merge 8 commits into
BlockHeader, not the whole serialized Block.#6470Conversation
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.
acd0e24 to
96456e7
Compare
BlockHeader, not the whole serialized Block.
| /// 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 { |
There was a problem hiding this comment.
I would call it new_unchecked or with_hash_unchecked
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
The idea is that whatever is inside can have a hash computed and it's memoized.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Motivation
The
BlockHeaderalready commits to every body field through per-field hashes, yet aConfirmedBlockCertificatesignedCryptoHash::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 nowCryptoHash::new(&header)rather than the hash of the whole block.BlockHeaderbecomesBcsHashableandBlockno longer is, so the body can never enter a hash preimage.ConfirmedBlock/ValidatedBlockmemoize that header hash as the signed value, via a newHashed::with_hashconstructor that stores a precomputed hash.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.BlockBuildertest 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 thatthe hash commits to the header).
Test Plan
linera-chainunit test builds a realConfirmedBlockCertificate, asserts its signed hash is reproducible from theBlockHeaderalone, and proves one body field viaBlockHeader::verifieswithout therest of the body.
BlockBuilder.linera-base,linera-chain,linera-core,linera-storage,linera-indexer).Release Plan
Links
linera-bridge) stacks on this branch.