Every module’s version is committed by a single Merkle root. The layout is
RFC 6962 over lexicographically sorted leaves:
Two decisions, each closing something the paper leaves open:
- Sorting makes the root a pure function of the set of blocks, so two parties that assembled the
same knowledge in different orders agree.
- RFC 6962 avoids the duplicate-leaf ambiguity a naive tree admits
(CVE-2012-2459), by hashing leaves and internal nodes
under different prefixes.
Internal nodes are derived, not stored. A module with no blocks still has a well-defined root.
Computing a root
Inclusion proofs
Membership is provable in O(log n), without holding the rest of the module.
A proof binds a block to one composition, not to any composition: verifying it against a different
root fails. That is what makes a root a version identifier rather than a checksum.
Swapping the layout
MerkleLayout is a protocol, so a deployment can commit to a different tree — but both sides must agree,
which is why the layout name travels in every ModuleRef.
Two implementations can only compare roots if they agree on the layout. A ModuleRef records
layout for exactly that reason — a root computed under another layout is not a smaller or larger
number, it is a different question’s answer.
Diffing two versions
What a consumer must fetch to move between versions falls out of the composition, with no server-side
computation:
This is the mechanism behind an incremental update: the layers whose root did not change are reused by
digest rather than transferred again.