Skip to content

State and proofs

Application state lives in an authenticated trie. Every finalized block produces one root that commits to all of it, identical on every member. A client can then verify an individual record against that root without trusting the node that served it.

State is stored in an MPF (Merkle Patricia Forestry, Aiken-compatible) trie. After applying a block, the resulting root is:

  • identical on every honest member — that is what members sign;
  • anchorable — the same bytes go into a Cardano metadata or script anchor; and
  • provable — it supports both inclusion and exclusion proofs.

Effect records are committed transitively through a count-bound effectsRoot leaf, so an authorized-but-not-yet-executed external action is as provable as ordinary state.

Proof subjects: application language, not trie internals

Section titled “Proof subjects: application language, not trie internals”

Raw key/value proofs exist, but they force the caller to know the physical layout. A proof subject is a typed descriptor that resolves an application-level identity to its canonical state key and evaluates declared claims against the proof-carried value.

Terminal window
curl -s -X POST \
"http://127.0.0.1:7070/api/v1/app-chain/chains/orders-chain/proof-subjects/finalized-message-v1/proof" \
-H 'Content-Type: application/json' \
-d '{
"coordinates": {"message-id": "<id>"},
"view": "latest",
"claim": {"claimId": "recorded", "operands": {}},
"includeEvidence": false
}' | jq

You supply coordinates in your own vocabulary (message-id, an account, a registry key, a document id). The subject derives the canonical key, decodes only the value the verified proof carries, and evaluates only the claims it declares.

Stock v1 subjects cover finalized block messages, finalized message records, balances, registry entries, document heads, basic approval outcomes, authenticated-map entries, actor roles, role approval outcomes, and composite profile markers. Composite subjects are automatically rebound to their component namespace and get component-qualified ids.

This is the part people get wrong. Each of these statements means something different, and a proof response keeps them separate:

ResultWhat it actually asserts
Message inclusionThe message id is a leaf under one app block’s messagesRoot.
Finality certificateA caller-pinned membership threshold signed the block.
Authenticated block recordFinalized state contains [height, messagesRoot, messageCount].
State recordingThe application wrote a typed fact under its canonical state key.
Anchor bindingA trusted Cardano output commits the selected application identity or root.
Claim satisfiedThe proof-carried canonical value satisfies the selected bounded predicate.
Locally retainedThis node has the bytes now. Durable availability is separately NOT_PROVEN.

An exclusion proof shows a key is absent from the trie. It does not show that the underlying business fact never happened — the dataset might simply be incomplete.

Do not expose a business-level absence predicate unless a marker, a paired subject, or an authenticated snapshot descriptor proves dataset completeness.

Proof history is retained, not infinite. Monitor oldestProvableHeight:

Terminal window
./yano.sh appchain state identity --url http://node:8080/api/v1 --chain registry
./yano.sh appchain state oldest --url http://node:8080/api/v1 --chain registry

A pruned proof is unavailable. It is not evidence that the fact was absent.

New chains enable the state-index:finalized-block-messages-v1 subject by default. Its enabled flag and configuration digest are part of the application identity, so an existing database with a different configuration fails startup rather than silently drifting. Disable it only in a new genesis profile.

Rough raw growth for that index, before MPF/JMT node and RocksDB amplification — one 32-byte key plus a ~38–47 byte canonical CBOR value per block:

Block intervalRecords/dayRaw logical growth/day
1 second86,4005.8–6.5 MiB
5 seconds17,2801.15–1.30 MiB
20 seconds4,3200.29–0.33 MiB

Capacity planning must measure backend amplification, compaction, snapshots, and retained proof history on the intended workload.

Proofs are portable. The Java client SDK (yano-x-client) verifies them client-side, and the composite client (yano-x-composite-client) additionally verifies governed-profile finality, one-root MPF, epoch chains, and authorization policy.

Offline verification from the CLI:

Terminal window
./yano.sh appchain state entry --url http://node:8080/api/v1 \
--chain registry --key 0123
./yano.sh appchain state proof --url http://node:8080/api/v1 \
--chain registry --key 0123

For custom subjects, implement ProofSubjectProvider next to the module that owns the canonical key and value codec. Each descriptor is closed data — coordinates, fact fields, claims, completeness, verification targets, retention hints, and fixed bounds — and its descriptorDigest goes into the capability manifest. The runtime activates the provider only when subject id, version, component id, and digest all match. Resolution must be deterministic and side-effect free.

  • Proof Lab — message, typed-state, imported, and on-chain proof workflows, plus the independent-verifier and Cardano-validator guides.
  • Tutorial 2 — retrieve and read a proof.
  • Tutorial 7 — bind a root to L1.
  • Authenticated snapshots — archiving and proving large immutable period datasets.