Architecture¶
Cardano Stake CSMT separates deterministic ledger/tree logic from the runtime boundary that follows a node, persists roots, and serves HTTP proofs. The service maintains Cardano ledger state by replaying blocks from Origin, extracts epoch-boundary stake snapshots, commits those snapshots to per-epoch CSMTs, and accumulates epoch roots into a history tree.
Boundary model¶
The pure core modules define:
- extraction of stake snapshots from
ExtLedgerState; - per-epoch CSMT root construction and credential inclusion proofs;
- history tree construction over epoch-root leaves;
- stable CBOR and ledger-CBOR codecs for credentials, coins, epochs, roots, and proofs.
The runtime shell owns:
- node-to-client ChainSync connection to the local Cardano node;
- trusted block application and rollback handling;
- RocksDB-backed stake and history databases;
- HTTP, Swagger, readiness, metrics, and optional latest-header signing.
Replay source¶
The production replay path starts ChainSync from Origin over the node-to-client protocol. It does not query stake distribution through Local State Query. This is important for verifiability: every published stake root is derived from the same trusted block stream and consensus ledger transition rules that a local node uses.
The replay worker starts from the genesis ExtLedgerState in the loaded
ledger configuration. For each fetched block it applies the trusted
ledger reapply path (tickThenReapply) and then applies the returned
table diffs to obtain the next ExtLedgerState.
Epoch snapshots¶
The stake snapshot extractor reads the Shelley-based ledger state inside
ExtLedgerState. Byron has no stake snapshot and is rejected by the
extractor. For Shelley and later eras, the service projects:
esSnapshots . ssStakeMark . ssActiveStake
to a map of staking credential to active stake. The total stake stored with the root is the sum of the projected credential stakes.
Epoch transitions are observed during replay by comparing the epoch at the applied block slot with the replay state's last observed epoch. The boundary event is the point at which the completed snapshot is eligible to become an epoch CSMT.
Per-epoch CSMTs¶
Each epoch snapshot is stored under an epoch namespace. The CSMT key is
the ledger-CBOR staking credential and the CSMT value is the ledger-CBOR
Coin amount hashed into the tree value. The resulting root record is:
EpochRoot {
epochRootHash,
epochRootTotalStake
}
The root record is persisted by epoch. Credential proofs are inclusion proofs against this root and are valid only for the exact credential, stake amount, and epoch root they were built from.
History tree¶
Every completed epoch root is inserted into a second CSMT: the history accumulator. Its leaf key is the ledger-CBOR epoch number. Its leaf value is the hash of the encoded epoch-root record, so the history leaf commits to:
(epoch, stakeRoot, totalStake)
Including totalStake in the history leaf prevents a verifier from
mixing a valid stake root with a different voting denominator. The
current history root is stored separately and exposed through HTTP.
Rollback recovery¶
ChainSync rollback is handled at the replay layer. The checkpoint-aware follower stores replay checkpoints on a configured slot cadence and keeps a bounded tail of recently fetched blocks. On rollback:
- Normalize the rollback point to a checkpoint point.
- Select the nearest stored checkpoint at or before the target.
- Load the replay state saved for that checkpoint.
- Recover the retained tail segment from the checkpoint to the target.
- Replay that tail into the loaded state.
- Truncate the retained tail after the rollback target and resume following the node.
If no usable checkpoint plus tail segment can recover the target, the follower resets its ChainSync intersection instead of pretending the local state is valid.
HTTP surface¶
The API serves the committed data:
| Endpoint | Purpose |
|---|---|
GET /proof/{credential} |
Latest persisted credential stake proof. |
GET /proof/{epoch}/{credential} |
Historical credential stake proof. |
GET /roots |
Ordered epoch root records with totalStake. |
GET /latest-header |
Signed latest (epoch, stakeRoot, totalStake). |
GET /history-root |
Current root of the epoch-root history tree. |
GET /ready |
Readiness JSON for orchestration. |
GET /metrics |
Minimal metrics JSON. |
GET /health |
Plain-text health check. |
When the docs application is enabled, Swagger UI is available at
/swagger-ui and the OpenAPI document at /swagger.json.
Voting verification¶
A voting client verifies a stake proof off-chain before accepting the stake amount as voting power:
- Choose the trusted root. For latest voting rounds, verify
/latest-headerwith the pinned service public key and use its signedepoch,stakeRoot, andtotalStake. For historical rounds, verify the epoch-root leaf(epoch, stakeRoot, totalStake)against the published history root. - Verify the credential inclusion proof against
stakeRoot, using the ledger-CBOR credential as the proof key and the ledger-CBORCoinas the proof value. - Use
stakeas the numerator andtotalStakeas the denominator for quorum or threshold checks.
The denominator is part of both the signed latest header and the history
leaf. A verifier should reject any proof bundle that verifies the stake
branch but supplies totalStake from a different root record.