Skip to content

Witness segregation

A frame transaction is roughly 90% signature bytes. An ML-DSA-65 witness is 3,309 bytes; the transaction wrapped around it is a few hundred. On a classical chain signature data is a rounding error; here it is nearly all of the chain.

At scale that is not a detail. Measured under sustained load, the execution layer grows on the order of gigabytes per day, and the consensus layer several times faster. Retaining all of it forever, on every node, is not a strategy.

A signature’s value decays, and it decays sharply at a specific moment.

Before a block is final, the witness is what proves the transaction was authorised — it is the evidence. After the block is final and the chain has built on it, the network has already agreed the transaction was authorised. The witness is now several kilobytes of data whose only remaining function is letting someone re-derive a conclusion everyone already reached.

That is a strong argument for storing it differently from the things that do retain their value: the transaction’s effects, its position, and the commitment that it was there.

Frost separates the two. Each block header carries a strippedTransactionsRoot: a Merkle commitment over the signature-stripped encoding of the block’s transactions. The signature bytes live in a separately prunable sidecar.

The Merkle leaves bind each transaction’s sig-elided encoding and its canonical transaction ID together, which is the detail that makes the scheme safe. A node serving historical data cannot substitute a different transaction or misreport which one it is; both are committed.

So a node can discard witness bytes and still:

  • prove exactly which transactions were in a block,
  • serve a stripped record with an inclusion proof against the header,
  • let a client verify that proof without trusting the node.

Pruning removes local storage, not verifiability. That distinction is the whole design.

Witness segregation slots into EIP-8141 more cleanly than it would into a standard transaction format, because the sig-hash elision rule already excludes empty-msg witness bytes from the signature hash.

Those bytes were never covered by the signature in the first place. Committing to a transaction without them is not a weakening of anything — it is the same set of bytes the signer signed.

An ARBITRARY signature entry with a non-empty msg signs msg rather than the sig-hash, so it is hashed as-is and can never be elided. Those bytes live in permanent history forever.

Left unpriced, that would be an obvious abuse: a cheap way to pin arbitrary data into permanent storage. So Frost prices it. Permanent witness bytes carry a surcharge of 40 gas per byte on top of ordinary calldata gas, and are capped at 4,096 bytes per entry.

The elidable channel is capped too — 16,384 bytes per entry and per transaction. Elided bytes are not committed in the sig-hash but do count toward intrinsic gas, and bounding them bounds what a relayer can inflate. The signed transaction-level gas_limit closes the rest of that gap: padding can only consume slack the signer explicitly declared.

Historical queries become tier-dependent. A request for an old block body may be served by one node and refused by another. That is by design, but it is real friction, and clients must handle it rather than assuming any endpoint has everything. frost_historyStatus exists so they can ask.

Clients must not re-hash a stripped transaction. Below the pruning horizon, eth_getTransactionByHash returns the transaction with elided entries empty. Those bytes decode fine — and do not hash to the canonical transaction ID. A client that re-hashes will conclude the node lied. Reassemble from the sidecar, or trust the proof.

More moving parts. A header field, a sidecar format, serving RPCs, proof verification on the client side, and an archive path. Each is a place a bug can live, which is why the SDKs implement the consuming side rather than leaving it to applications.

Witnesses are not simply deleted. Before anything may be pruned locally, the data is exported to cold object storage — certified consensus segments and full-witness block groups — and hash-verified against commitments the chain already carries.

The storage is explicitly untrusted. A provider that corrupts or loses data is detected rather than believed, because the verification is against the chain’s own commitments rather than the provider’s word.