Skip to content

History and storage

A secp256k1 signature is 65 bytes. An ML-DSA-65 signature is 3,309. That single ratio is the reason this page exists.

On a classical chain, signature bytes are a rounding error next to state and calldata. On a post-quantum chain they are the dominant term: a frame transaction is roughly 90% signature bytes by volume. Storage growth stops being an operational detail and becomes a first-order design constraint — one the chain has to answer for explicitly, on both the execution and consensus axes.

Frost’s answer has three parts.

The key observation is that a signature’s value decays. Before a block is final, the witness is what proves the transaction was authorised. After the block is final and the chain has moved on, the network has already agreed that it was authorised — and the witness is several kilobytes of data that nobody will check again.

So Frost separates them. Each block header carries a strippedTransactionsRoot: a commitment over the signature-stripped encoding of its transactions. The signature bytes themselves live in a separately prunable sidecar.

This means a node can discard multi-kilobyte witnesses once they are no longer needed, while still holding a commitment that proves exactly what was in the block. Anyone who needs the full witness can fetch it from a node that keeps it and verify it against the commitment — the pruning is not a loss of verifiability, only of local storage.

Not every signature byte is elidable. An ARBITRARY signature entry with a non-empty msg is permanent — it can never be pruned — so those bytes carry an extra gas surcharge on top of ordinary calldata gas, pricing them above the elidable floor.

On the consensus side, the raw DAG of signed blocks is heavy and grows faster than the execution-layer chain does. Retaining all of it forever is not viable.

Instead, validators quorum-sign compact hash-linked checkpoint summaries. Each summary is a couple of hundred bytes and pins three things: the consensus position, the execution-layer head, and the block-key schedule. Together they form a checkpoint chain that certifies the history without containing it.

Once a certified checkpoint covers a range of consensus blocks, the heavy signed block data behind it can be pruned within a rolling window. The checkpoint chain remains, and it is what a new node verifies against.

This is also how joining works. A new node does not replay from genesis. It verifies the checkpoint chain against the committee digest recorded in the genesis block, installs the block-key schedule with every delegation checked against the checkpoint roots, and restores a verified snapshot. Trust bottoms out in the chain definition, not in whoever served the data.

The result is that no single node has to hold everything, and different nodes deliberately hold different amounts:

  • Full-history nodes keep everything, including all witnesses.
  • Transaction-history nodes keep the transaction history but expire full bodies behind a rolling window.
  • Pruned nodes keep a bounded recent window of execution-layer history.
  • Archive is cold object storage: certified consensus segments and full-witness block groups are uploaded to untrusted S3-compatible storage and hash-verified before anything may be pruned locally.

“Untrusted” is doing real work in that sentence. The archive is verified against commitments the chain already carries, so a storage provider that corrupts or loses data is detected rather than believed.

You can ask any node what it holds:

Terminal window
curl -s https://rpc.frostfi.net \
-H 'content-type: application/json' \
--data '{"jsonrpc":"2.0","id":1,"method":"frost_historyStatus","params":[]}'
{
"tier": "tx-history",
"headBlock": "0xdb703",
"earliestFullBody": "0xd8ff3",
"historyCutoff": "0x0",
"persistedState": "0xd8b18"
}

If you need deep history, query a node that serves the tier you need rather than assuming the public endpoint has it. Node types describes each tier.

Very little, day to day, but two things are worth knowing:

Your transactions are large. A post-quantum spend is a few kilobytes of calldata, and calldata gas is most of what you pay. The cheaper ML-DSA-44 account type exists partly for this reason.

Historical queries are tier-dependent. A request for a very old block body may be served by one node and refused by another. That is by design, not a failure — check frost_historyStatus and pick an endpoint that matches what you need.