15. Proofs, light clients, and publication receipts
This chapter defines what a client can prove about the chain without trusting the party that serves the proof: trust inputs, verification order, the proof kinds and the claim each one supports, the three disjoint result kinds, and the composition of a publication receipt. It also states the property that lets a contract on another chain verify the same facts. The encoding contract fixes proof framing and byte layout; this chapter fixes what must be authenticated, against what, and in what order.
15.1 Trust inputs
A trust input is a value a verifier holds before it reads any response, and against which every authentication is performed. The group public key is invariant across reshares and membership changes (section 13), so one key and the chain-bound namespace verify a finalization certificate at any epoch.
R-15.1. A verifier's trust inputs MUST be chain_id together with either the published genesis
block, which carries the group public key, or a previously verified release checkpoint.
R-15.2. A verifier MUST NOT take a trust input from a response.
INV-PROOF-01. Verifier trust inputs are never learned from a response.
15.2 Proof framing and verification order
The acyclic ProofEnvelopeV1 and its typed payloads are defined by
encoding section 10.
R-15.3. A verifier MUST authenticate the proof's target header and the proof format before it verifies the proof itself, and MUST check the proof against both the committed root and the committed count of the structure it opens.
R-15.4. Verification MUST use the selected native types for every root, count, and range check, MUST verify the native encoded append operation including its tag for a membership proof and a block-history proof, and MUST verify command identifiers under the binary Merkle tree for a command proof.
INV-PROOF-04. A proof is verified only after its target header and proof format are authenticated, against both root and count.
15.3 Proof kinds and the claims they support
Every proof kind is defined over the concrete Commonware types at the pinned version. A
membership proof is a keyless inclusion proof by location under the membership root at a stated
size. A command-inclusion proof is a binary Merkle tree proof under command_root.
The following matrix fixes what each claim can be checked against. The last two rows are claims that no proof kind establishes; section 15.8 states what a consumer does about them.
| Claim | Evidence | What the verifier hashes |
|---|---|---|
These command bytes were finalized at height h |
command inclusion under command_root, plus a finality proof |
Keccak256 over the canonical envelope bytes, which are the leaf, then over the command tree and the header (sections 6 and 11) |
| This commitment was published to this project | membership inclusion under membership_target.root at a stated size, with the (scheme_id, native_id) preimage |
Keccak256 over the project id followed by that scheme id and native id, which is the leaf, and over its merkle path (section 9) |
| This account published this commitment to this project at this ordered position | a publication receipt: the signed PUSH, command inclusion, and finality (section 15.9) |
as for command inclusion and finality |
| This value is current | a current-state proof at an authenticated anchor, interpreted with expiry, control generation, and delegation epoch using the relevant parent-state proofs | Keccak256 over the validity-state merkle structure and the activity bitmap (section 6), over an operation whose state key is the BLAKE3 digest of the record identity (section 9) |
This value was current at past height H |
a current-state proof captured while H was the head, which stays verifiable against H's certified header forever |
as for a current-state proof; not provable retroactively (section 15.4) |
Block h is final |
a direct finalization certificate for h, or a certificate for a descendant plus a block-history ancestry proof (section 15.5) |
threshold verification of the certificate over block_hash, then Keccak256 over the accumulator leaf and its path (section 9) |
| This release was in force | a release-lineage proof (section 15.6) | Keccak256 over the validity-state merkle structure; release_digest enters as an opaque BLAKE3 input |
| This derived event or charge is correct | bounded replay from certified inputs, or explicitly unauthenticated | not a proof kind (section 15.8) |
| This list is complete or latest | never provable from row proofs | not a proof kind (section 15.8) |
R-15.5. A membership proof MUST carry the (scheme_id, native_id) preimage of the commitment it
proves, because that preimage is what the leaf is computed from. The requester supplies it, or the
indexer looks it up in the stored block; the proof service and the verifier recompute
Keccak256(project_id || scheme_id || native_id) and check that it equals the proven leaf.
15.4 Current-state proofs and historical proofs
A current-state proof establishes a current value together with its activeness under state_root
at an authenticated anchor. The library's historical operation proofs prove operations without the
activeness overlay, so a value's activeness at a past height is not provable from the operation log
alone. Historical current-state proofs are therefore outside this specification's proof kinds, and
the guarantee for authenticated history is command inclusion, membership inclusion, and finality. A
later release may add a historical current-state proof by batch replay of stored blocks into a
fresh instance, which is the only way to rebuild the activeness overlay at a past height.
R-15.6. A historical current-state proof is not a proof kind of this specification, because the
library's historical operation proofs carry no activeness overlay; a later release MAY add one by
batch replay of stored blocks into a fresh instance. The claim that a named value was current at a
past height, such as "ref R of project P pointed at commitment C at height H", or the
controller, role, or username resolution at H, is therefore provable only by a current-state proof
captured while H was the head, which stays verifiable against H's certified header forever, and
a consumer that may have to evidence such a decision later captures the proof at the time. A
publication receipt establishes the ref target the command set at publication time and nothing about
the target at any later height (section 15.9).
R-15.7. Serving a proof from the exporter's live databases MUST NOT mutate them, and proof-service reconstruction from the store MUST NOT touch an executing node.
INV-PROOF-06. Serving a proof from the exporter's live databases never mutates them; proof-service reconstruction from the store never touches an executing node.
15.5 Finality and ancestry
A finality proof for height h is the direct finalization certificate for h whenever one exists,
and otherwise a finalization certificate for a descendant together with a block-history accumulator
proof (section 9) that h is an ancestor of that descendant.
R-15.8. Either form MUST authenticate the exact block hash of h.
R-15.9. An ancestry proof MUST be anchored at a directly certified descendant, and MUST be logarithmic in height under every legal history.
INV-PROOF-02. A finality proof authenticates the exact block hash through a direct certificate or an accumulator ancestry proof anchored at a directly certified descendant, logarithmic in height under every legal history.
15.6 Release lineage
R-15.10. A release-lineage proof is one predecessor chain together with the schedule-state proof, which is required even when the schedule state it proves is empty.
INV-PROOF-03. Release lineage is one predecessor chain with the schedule state always proved.
15.7 Result kinds
A proof result is exactly one of three disjoint kinds. "Current" is a service freshness claim and is not a proof kind.
| Kind | Results |
|---|---|
| Cryptographic | Value, Absent, Member |
| Service | NotYetMaterialized, ProofUnavailable, UnsupportedHeight, UnsupportedRelease |
| Invalid | InvalidProof |
R-15.11. A service outcome MUST NOT decode as an authenticated absence.
R-15.12. Absent MUST be returned only for a logically supported absence proof, and the
unordered validity-state database grants no general exclusion proof (section 9).
R-15.13. A verifier that does not support the ruleset of the requested height MUST answer
UnsupportedRelease (section 18).
15.8 Claims that no proof kind establishes
Row validity never implies completeness or freshness, and a malicious server can conceal newer
state. An availability layer checking upload authority and a client about to sign are
authorization-critical consumers. The derived views are described in
../docs/history-query.md.
R-15.14. A derived view MUST NOT create a consensus fact, and a query answer is evidence only when its rows are checked against proofs.
R-15.15. A consumer whose decision is authorization-critical MUST:
- (a) use verified current state under a stated maximum lag, or declare its trust in its indexer;
- (b) never fall back to older history;
- (c) take freshness from an independently maintained checkpoint.
INV-PROOF-05. A derived view never creates a consensus fact; a query answer is evidence only when its rows are checked against proofs.
INV-PROOF-07. Row validity never implies completeness or freshness; an authorization-critical consumer uses verified current state under a stated maximum lag.
15.9 Publication receipts
A publication receipt composes existing command-inclusion and finality proofs into one portable object that establishes a single publication act. The indexer assembles it from stored blocks. It is not a new proof primitive, a state record, or a consensus command.
The occurrence identity of a publication is the full tuple
(chain_id, block_hash, command_index, commitment_index). A ref-only PUSH has no publication
occurrence and uses an ordinary command-inclusion proof instead.
R-15.16. A PublicationReceipt MUST carry:
- the block header;
- finality evidence;
- release-lineage evidence;
- the exact canonical
SignedCommandenvelope included in that block; - its zero-based
command_index; - its binary Merkle tree inclusion proof;
- a zero-based
commitment_indexinto that command'sPUSH.commitments.
R-15.17. The finality evidence and the release-lineage evidence MUST reach the trust anchor the verifier holds, which is either the published genesis block or a previously verified release checkpoint (section 15.1).
R-15.18. A verifier that already holds a verified release checkpoint covering the block's release MAY accept release-lineage evidence that terminates at that checkpoint rather than at genesis, and a service MAY share authenticated checkpoint material across a bounded response; neither changes the verification target.
R-15.19. A verifier MUST take its trust inputs from its own configuration and never from the receipt.
R-15.20. A verifier MUST then, in order:
- (a) authenticate the header and the release before any further check;
- (b) check the intent's chain and ruleset against that authenticated context;
- (c) recompute the envelope's command-tree leaf,
Keccak256(canonical(SignedCommand)); - (d) check the binary Merkle tree position and the leaf count against
command_count; - (e) require that the included command is a
PUSH; - (f) bounds-check
commitment_indexagainst that command's commitment list.
R-15.21. Actor, project, commitment, key, generation, sequence, intent digest, and execution rules MUST be derived from those authenticated bytes, never from duplicate response fields.
R-15.22. Occurrences on one authenticated chain MUST be ordered by
(height, command_index, commitment_index), never by hash order and never by a publisher timestamp.
R-15.23. A response that also supplies a membership location MUST separately verify membership, and a matching leaf alone MUST NOT be taken to bind a location to an occurrence, because duplicate leaves exist.
R-15.24. An event, charge, resulting ref revision, or current ref target attached to a receipt is not authenticated by the command proof, and MUST NOT be treated as authenticated without the evidence stated in section 15.8.
A verified receipt proves accepted execution under the chain's Byzantine fault assumptions, and is
not an independent replay of the historical authorization state. The verified claim is exactly that
account A published commitment C into project P at that position. It proves publication of the
identifier only: neither authorship, nor first publication anywhere, nor object availability, nor
content validity, nor creation time. Checking that the identifier identifies a commit object and
binds particular files is the native scheme verifier's job, outside Makechain consensus. A
membership proof is unnecessary for this claim, because the included PUSH commitment entry is
sufficient. The API companion specifies the bounded service and
verification contract.
INV-PROOF-08. A publication receipt derives its actor, project, commitment, and occurrence from a finalized included PUSH, never from response summaries, object authors, or membership leaf equality.
15.10 The reference verifier
Proof formats are versioned by ruleset_id (section 18), and one artifact verifies proofs under
every ruleset. A hand-transcribed second implementation of proof verification is not required.
R-15.25. The reference verifier is the Rust verifier compiled to wasm32, and is the only mandatory browser artifact.
15.11 Verification on another chain
This subsection is informative. It states a property and imposes no requirement on a Makechain node.
Every finalization certificate verifies under one threshold public key that is invariant across
reshares and membership changes (section 13), and the proof path is Keccak256 (section 6). A
contract on an EVM chain that stores that key can therefore verify a finalization certificate and
recompute block_hash from the header bytes, and then, with Keccak256 as its only hash function,
establish that a block is final, that a command with exactly these envelope bytes was included at a
stated position in it, that a commitment with this scheme_id and this native_id belongs to this
project, and that one height is an ancestor of a certified descendant. Three limits follow. Reading
current state there needs BLAKE3 as well, or a precomputed key mapping, because a state key is the
BLAKE3 digest of its record identity (section 9), so such a contract pins the ruleset whose
encodings it decodes under, or trusts that mapping. Release-lineage evidence (section 15.9) is
current state of exactly that kind, so a Keccak-only contract cannot verify release lineage itself
and cannot complete a publication receipt without it. And no proof of any kind says anything about
the content an identifier names, which belongs to the native scheme. The library's Solidity
verifiers for threshold certificates, QMDB, MMR, and BMT proofs are the intended implementation of
such a contract; they are on the library's main branch after the pinned tag and carry its ALPHA
stability marker (section 6). No such contract is part of this specification; the property is what
the hash choice preserves.
15.12 Rationale
This subsection is informative.
One reference verifier compiled to WebAssembly is mandated because a hand-transcribed second implementation of proof verification is the most likely place for a divergence that a conformance vector would not catch. Results are three disjoint kinds so that no service outcome can be mistaken for an authenticated absence. Historical current-state proofs are absent because the library's historical operation proofs do not carry the activeness overlay, and rebuilding one would mean replaying stored blocks into a fresh instance. Finality falls back to an accumulator ancestry proof rather than to a header-chain walk because Simplex finalizes ancestors transitively without issuing their certificates. Simplex places no bound on how many consecutive heights that can cover under an asynchronous period, so a header-chain fallback would have no worst-case bound. The accumulator gives one.