6. Encoding and digests
Every consensus decision is taken over canonical bytes, and every commitment is one of two digests. This chapter states the rules that govern both: which encoding is canonical, which digest function secures which value, and what the choice of Keccak256 on the proof path depends on. Byte-level field order, types, tags, and proof framing are not restated here; the encoding contract is their authority.
6.1 Canonical encoding
R-6.1. Canonical bytes follow the commonware-codec rules and are byte-equal to
commonware-codec at the pinned version (chapter 2), with:
a. fixed-width big-endian integers;
b. shortest-form varuint32 length prefixes;
c. one-byte booleans, option tags, and enum discriminants;
d. sequences with counted, bounded lengths;
e. sets and maps as strictly increasing sequences;
f. no field names, no defaults, and no unknown-field slots;
g. exact consumption;
h. re-encode equality.
R-6.2. A transport encoding, including Connect or gRPC JSON and protobuf, is never hashed.
The encoding contract keeps a normative prose description of this subset and of its application schemas, so that a non-Rust implementation can be built from it. The canonical Rust schema and release types are executable authority, and transport descriptions and bindings derive from them. The format manifest records the chosen revision, tags, domains, pins, and digests, rather than generating Rust from a second schema language. Explicit reviewed byte vectors pin equality, and hash fixtures alone do not.
6.2 The two-hash rule
The proof path is everything a verifier must hash to get from a finalization certificate to a proven fact:
block_hash;- the merkleization of the three authenticated databases, namely
state_rootand the state target'sops_root(the root of the validity state's operation log, section 9), the membership root, and the block-history root; - the membership leaf,
Keccak256(project_id || scheme_id || native_id)(section 9); - the block-history leaf;
command_root, whose leaves areKeccak256(canonical(SignedCommand)), one per command in execution order (section 11).
R-6.3. Two hash functions are used for the protocol's own digests, and each such digest uses exactly one of them. The hash steps that external signature standards fix inside a signing family (SHA-256 for P256 direct and WebAuthn, SHA-512 inside SSHSIG, Keccak256 inside EIP-712) belong to that family's verification in section 7.4 and are not protocol digests.
R-6.4. Every digest on the proof path uses Keccak256.
R-6.5. Every other digest uses BLAKE3 with an ASCII domain prefix registered in one table (encoding section 11), namely:
chain_id;- content identity and
CommitmentId; - state keys;
intent_digest;node_digest;command_id;EventId;release_digest;project_id;genesis_epoch_info_hash, under domainmakechain.genesis-epoch-info;dealer_log_hash, under domainmakechain.dealer-log;dkg_outcome_hash, under domainmakechain.dkg-outcome;- every other domain-separated preimage.
R-6.6. genesis_epoch_info_hash, dealer_log_hash, and dkg_outcome_hash are each taken over
the exact pinned native inner encoding of the value they name.
Those three are header fields, covered by block_hash, and are not part of the Keccak256 proof
path.
R-6.7. Every proof-path leaf is Keccak256 over that leaf's own preimage, so a verifier
recomputes it from the values the claim already names and never from a BLAKE3 identity: a
membership leaf is Keccak256(project_id || scheme_id || native_id) over the commitment's
registered scheme identifier and native identifier (section 9), and a command-tree leaf is
Keccak256(canonical(SignedCommand)) over the complete envelope (section 11). A BLAKE3 value
reaches the proof path only as opaque bytes inside a Keccak256 preimage, a header field, or a state
value, and a state key stays a BLAKE3 digest of its record identity (section 9).
R-6.8. A state value carries the identity it is keyed by, so a verifier never recomputes a state key.
Keccak256 also appears in exactly three pinned Tempo formulas, defined in chapter 7: the account
address derivation, config_commitment, and the in-place upgrade salt. Those three are Tempo's
format and are not part of the proof path.
R-6.9. Every digest the protocol defines is on exactly one of the two lists of this section, or is one of those three pinned Tempo formulas.
6.3 Library dependency and calibration
The Keccak256 hasher, and the Solidity verifiers for threshold certificates, QMDB, MMR, and BMT
proofs, are on the library's main branch after the pinned tag, at commit
v2026.9.0-69-gffe2cf2af2, and carry the library's ALPHA stability marker. Makechain supplies
Keccak256 as its own implementation of the pinned version's hasher trait, so that one library
version serves the node and the store, and reviewed vectors cross-check that implementation against
the upstream one. Merkleization cost of the three databases and the command root under Keccak256 at
the target throughput, measured against SHA-256 and BLAKE3 on the same workload, is a calibration
item (chapter 19), and the proof-path hash choice of section 6.2 is final only when that measurement
passes.
6.4 Sizes and identifiers
R-6.10. Signatures are 64 bytes, plus a recovery byte for secp256k1 root signatures.
R-6.11. An identifier is never truncated anywhere it is stored, routed, or hashed.
6.5 Rationale
This subsection is informative.
Reusing the library's codec rules avoids a second meta-format that would have to be kept byte-equal with the first, and the byte vectors exist so that a non-Rust implementation can be conformance-tested rather than reverse-engineered. The proof path uses Keccak256 because a fact that can be verified only off chain is worth less than one a contract can verify: every finalization certificate verifies under one threshold key that never changes, so a small contract on Tempo or another EVM chain can hold that key, and native Keccak256 makes the rest of the proof cheap there. BLAKE3 stays everywhere else because it is faster, it is the content store's own identity function, and none of those digests has to be recomputed by a contract. Keccak256 is slower in software and merkleization is the dominant execution cost, which is why the choice carries a measurement. Each proof-path leaf is hashed from its own preimage rather than from a BLAKE3 identity, so a contract that holds only a Keccak256 hasher can bind a proven leaf to the exact command bytes, or to the scheme and native identifier a publication names, without a second hasher; reading current state there still needs BLAKE3, because a state key is a BLAKE3 digest of its record identity.
What the chain records and who may change it.