Skip to main content

State Model

Every blockchain has state — the running tally of who owns what. On Ethereum, that state is transparent: anyone can look up any address and see its balance, its transaction history, and every contract it has ever interacted with. On Specter, the state model is designed from the ground up to store proofs of knowledge rather than the knowledge itself.

Here is what lives on-chain and how it all fits together.

Commitment Storage: The Poseidon Merkle Tree

At the heart of Ghost Protocol is a Poseidon Merkle tree — a binary hash tree that stores every commitment ever made on the network.

  • Depth: 20 levels, giving a capacity of roughly 1 million commitments per tree.
  • Hash function: Poseidon, a hash function specifically designed to be efficient inside zero-knowledge proof circuits. Where keccak256 (Ethereum's hash function) would make ZK proofs prohibitively expensive, Poseidon keeps them fast and affordable.
  • Root stored on-chain: Only the Merkle root — a single 32-byte value that summarizes the entire tree — is stored in the smart contract. This is all the chain needs to verify proofs.
  • Full tree maintained off-chain: Relayer services (Merkle tree indexers) maintain the complete tree so that users can look up their leaf position and compute the Merkle path needed to generate a proof.

Root History

When you generate a zero-knowledge proof, you prove that your commitment exists in the tree as of a particular root. But the root changes every time a new commitment is added. If only the latest root were valid, you would have a tiny window to submit your proof before it became stale.

To solve this, Ghost Protocol keeps the last 100 Merkle roots valid. This means you can generate a proof against a recent root and still have it accepted even if new commitments have been added in the meantime. It is a simple mechanism that makes the user experience dramatically smoother.

Nullifier Registry

Nullifiers are what prevent double-spending in a privacy system. When you reveal a commitment (for example, when you Summon tokens), the contract records a nullifier — a unique value derived from your secret and the commitment itself. If anyone tries to reveal the same commitment again, the contract checks the nullifier registry and rejects the transaction.

The registry is straightforward:

mapping(bytes32 => bool)

Once a nullifier is recorded, it stays forever. There is no undo, no expiry, no admin override. This permanence is a feature, not a limitation — it is what makes double-spending mathematically impossible.

The nullifier itself reveals nothing about the original commitment. It is derived using Poseidon hashing in a way that makes it impossible to link a nullifier back to the commitment it came from, unless you already know the secret.

Quantum Commitments

Zero-knowledge proofs today rely on mathematical assumptions (specifically, the difficulty of the discrete logarithm problem on elliptic curves) that a sufficiently powerful quantum computer could theoretically break. Specter includes a defense-in-depth layer for this scenario.

When you create a commitment, the contract also stores a quantum commitment:

keccak256(quantumSecret)

This is a plain hash of a separate secret. If the ZK proof system is ever compromised, users can still prove ownership by revealing the preimage of their quantum commitment. Keccak256 is a traditional hash function whose security does not depend on the same mathematical assumptions as ZK proofs — it remains secure even against known quantum algorithms.

Think of it as a backup lock on your front door. You hope you never need it, but you sleep better knowing it is there.

Policy Bindings

Ghost Protocol's programmable policies (introduced in v4.4) need to be tamper-proof. When a commitment is created with a policy — say, a timelock that prevents early redemption — that policy must be cryptographically bound to the commitment so that no one can swap in a different policy later.

This is handled through two on-chain mappings:

  • commitmentPolicies: Maps each commitment hash to its associated policy contract address.
  • commitmentPolicyParamsHashes: Maps each commitment hash to a hash of the policy's parameters (e.g., the expiry timestamp for a timelock, or the allowed destination for a restriction).

Because the policy address and parameter hash are included as inputs to the Poseidon7 commitment hash itself, any attempt to tamper with the policy would change the commitment hash — and the proof would fail. The policy is as immutable as the commitment it is attached to.

Putting It All Together

When you interact with Ghost Protocol, here is what actually happens to the chain's state:

  1. Commit: A new leaf is added to the Merkle tree. The root is updated. Policy bindings are stored. A quantum commitment is recorded. Your tokens are burned (if applicable). None of this reveals what you committed.
  2. Reveal: You submit a ZK proof and a nullifier. The contract verifies the proof against a recent root, checks that the nullifier has not been used, records the nullifier permanently, and executes the reveal action (minting tokens, releasing data, etc.).

The beauty of this model is that commits and reveals are completely unlinkable on-chain. The Merkle tree, the nullifier registry, and the quantum commitments are all that the chain stores — and none of them reveal the connection between a commit and its corresponding reveal.