pub trait ContentAddressingProvesIntegrity: CorrectValidator { }Expand description
Lemma (Content addressing proves integrity, not validity). For a blob, the key determines
the value: a BlobId is a hash of the content together with its BlobType, so bytes stored
under it are either the ones the key names or detectably wrong, with no appeal to who wrote
them. Certificates are keyed the same way, by the hash of the block they confirm.
This settles substitution and nothing else. That a blob was paid for, and that anyone is
obliged to keep its bytes available, are separate claims with a separate proof —
BlobValidityRestsOnCertificates.
Proof. RootKey::BlobId(blob.id()) derives the storage root from Blob::id, which hashes the
content. Recomputing the id of what comes back and comparing it to the key is therefore a
complete check, requiring no committee, no signature and no trust in the store. ∎
Where the check is actually performed. At trust boundaries, and only there. A blob arriving
from another node goes through RemoteNode::download_blob, which builds Blob::new(blob) —
recomputing the id — and rejects the response when blob.id() != blob_id. A blob read back from
this node’s own storage does not: DbStorage::read_blob constructs it with
Blob::new_with_id_unchecked, taking the store at its word.
That asymmetry is deliberate and worth stating, because it locates the residual trust exactly.
Content addressing does not make storage trustworthy; it makes storage auditable, and the
implementation spends that audit where data crosses from a party it does not trust. Within a
validator, a store that returns the wrong bytes under a blob key is undetected — which
StorageAtomicity does not cover either, being about whether a write lands, not about whether
a read is faithful.
This is what AccountabilityScope means when it says blob integrity is free, and what
CheckpointRestoresExecutionState relies on when it says a node fetching an execution-state dump
cannot be handed different bytes.