Security and assurance
Trust boundaries
What a successful StateSync-GKR verification establishes, what the verifier needs from your application, which assumptions a result rests on and which guarantees sit outside the component.
Read this page before you integrate StateSync-GKR. It describes what the verifier checks, what it assumes and what your application must provide so that an accepted proof means what you expect.
Component boundary#
StateSync-GKR is a proof component, not a chain or a production service. It owns the state-transition relation, circuit compilation, the GKR proof path, the public encodings, one selected external proof identity and a receipt-gated reference transition.
It does not own identity providers, source-network governance, receipt-network operation, production signing custody, a deployed destination contract, native rollup proving, parent-chain settlement or finality on a destination chain.
What a successful verification means#
The sparse-Merkle verification entry points (verify_sync_op, verify_sync_op_prepared and verify_encoded_sync_op) return true only after these checks pass:
- For prepared entry points, the request's operation kind matches the prepared kind.
- For a received proof, the complete byte string decodes under the strict
inner-proof-v1rules, and its circuit identity, including the full circuit commitment held by the verifier's prepared state, equals the verifier's own. - The verifier rebuilds the circuit input from the supplied witness: the leaf hash and every sibling hash along the old path, and the new path for an update.
- It replays the transcript over the domain tag, circuit shape and commitment, public inputs, claimed outputs and proof messages, and it checks every layer's sumcheck rounds and wiring.
- It checks both residual input claims against the input it rebuilt, together with its public-input guards.
A true result means that these checks passed for this request. The cryptographic meaning of that result rests on the assumptions listed in Assumptions behind a result.
A false result covers every failure class, including a rejected proof, an invalid request, a wrong operation kind, a failed binding and a decoding or identity failure. The boolean does not tell you which one occurred. A successful proving call is not an acceptance verdict, so always check the matching verification result.
The verifier needs the private witness#
The current sparse-Merkle verifier takes the private leaf and the full sibling path and recomputes their native Poseidon2 hashes before it checks the GKR proof. The Merkle hashing work therefore stays with the verifier, and the verifier needs the full witness as well as the public inputs.
An encoded proof carries the circuit identity, the public inputs and the proof messages. It never carries the witness, so the verifying side must already hold the witness for the request it checks. Design your data flow so that the verifying party is entitled to see the leaf and its path.
The proof is built for integrity#
The inner GKR proof is built for integrity. The verifying side holds the witness for the operation it checks, including the record and its sibling path, so use it where the verifier may see that data.
Public inputs can also carry information. A non-membership proof accepts a slot that was never used or a slot whose record was deleted, and the two states hash to different leaf values. The public value digest of a non-membership proof therefore shows which of the two the slot is in. If that distinction is sensitive in your application, account for it before you share proofs.
Root provenance belongs to your application#
Your application obtains trusted roots and witnesses from its own state store. StateSync-GKR does not authenticate an external database, chain or registry, and it does not establish consensus. Recomputing a root from caller-supplied siblings does not show that the root came from a trusted source. Authenticate each root through your own trusted source before you rely on a verification result.
Your application's responsibilities#
- Authenticate the roots you place in a request.
- Check the verification result for every proof. A matching proof hash or a successful proving call is not acceptance.
- Keep each original request, with its configuration, roots and value digest, and verify received proofs against it. Stop on a semantic or identity mismatch.
- Bind asynchronous replies to their original requests by request ID.
- Choose practical configuration bounds before you accept untrusted requests. Configuration values do not limit resource use.
- Do not retry malformed input indefinitely, discard a
falseresult, weaken the verifier or replace an expected identity after a mismatch.
For integration patterns, see Production integration.
Custom frontends must discharge residual claims#
The sumcheck verifier returns a residual Subclaim, and the GKR verifier returns residual input claims. The sparse-Merkle facade discharges them for its supported request format. A custom frontend must discharge them against its own bound input data and supply its own circuit, witness, output claim and transcript conventions.
The shipped proving functions use KoalaBear values, a degree-four extension field for challenges and Poseidon2. Replacing the field, hash, transcript or compiler is a new implementation and verification task, and the existing proof and model claims do not transfer to it automatically. See Custom frontends.
External proof route#
The repository records one external proof route, separate from ordinary inner proofs. It wraps one preserved inner proof in a native RISC Zero v3.0.4 succinct proof for a fixed program identity, image ID dc9a5f0da608178bbe56e98604cb895060c555c18329846b453f71d562d16530. That proof was checked through the zkVerify RISC0 pallet on zkVerify's public Volta test network, and the resulting receipt is recorded in the repository. The identity is that of the historical 1.1 program. The current 1.1.1 distribution keeps 78 of the 79 protected files byte for byte and changes the root Cargo license field, so the external proof is evidence for the recorded program only.
- Fixed scope: The recorded guest program is a fixed depth-24 membership example that checks one preserved inner proof with its fixed request. The general
wrap_relationAPI does not turn that program into a proof service for arbitrary requests. A different workload needs its own program, identity and verification work. - Fixed identity: The external proof applies only to the fixed protected program identity and its public statement. Modified source, a fork, a different build environment or a different deployment falls outside that evidence until it is independently verified.
- Receipt meaning: The receipt and the receipt-gated local transition are component integration evidence. They do not grant proof-submission authority, identify production signers, activate a destination, prove source consensus inside a destination, deploy contracts, establish native rollup proving, confirm parent settlement or create finality on a destination chain.
- Privacy: Saved-proof verification shows that the exact proof matches the fixed image ID and public journal. It does not establish confidentiality for arbitrary applications, operator confidentiality, side-channel resistance or a general zero-knowledge property.
- Local transition: The reference transition is host-only and in memory. An accepted candidate is not a transaction and always reports
secondary_finalized = false.
Assumptions behind a result#
| Assumption | What it means for you | Details |
|---|---|---|
| Fiat-Shamir | The executable verifier is non-interactive. Carrying the interactive model bound over to it needs a multi-round Fiat-Shamir reduction, and no such reduction is part of this repository. | Formal verification |
| Challenge distribution | The model draws uniform, independent extension-field challenges. That the executable transcript produces such challenges is an idealization, not an established property. | Formal verification |
| Input binding | The transcript absorbs the public inputs and the circuit commitment, not the input vector derived from the witness. An implementation-facing argument must bind that input before challenges are drawn or account for every witness an adversary tries. This is a missing argument, not a demonstrated false acceptance. | Formal verification |
| Rust and model correspondence | The connection between the Rust code and the model is partial. The acceptance theorem carries a value-correspondence premise that is not discharged against the compiled program. | Formal verification |
| Primitives | Field arithmetic, the Poseidon2 permutation and the challenger come from pinned Plonky3 0.4.3 crates and are treated as black boxes under their standard cryptographic assumptions. | Formal verification |
What the repository checks#
- The Rust toolchain is pinned, every dependency is locked by
Cargo.lock, and the workspace contains nounsafecode. - Release-mode unit, integration, differential, adversarial, codec and deterministic proof-digest tests pass.
- Proof bytes do not depend on worker count or CPU features. CI compares a baseline x86-64 build against a
-Ctarget-cpu=nativebuild of the same binary. - The protected source is checked against a byte-exact manifest that anyone can re-run; the 1.1.1 license-field change is recorded as the one checked difference.
- Decoding is strict and fails closed: unknown versions are rejected, counts are checked before any allocation, and non-canonical field elements and trailing bytes are rejected.
- Malformed input, unsupported versions, mismatched identity, a wrong statement, a wrong receipt, replay, conflict and predecessor mismatch fail closed. An unavailable external dependency never becomes a success.
Boundary summary#
| Boundary | Trusted or assumed | Checked in the repository | Outside the component |
|---|---|---|---|
| Field and hash | Pinned implementations and explicit black-box assumptions | Interface shape, domain separation and deterministic vectors | Cryptanalytic security and side channels |
| Compiler | Native meaning standard and reviewed layout | Differential and adversarial tests and model results | Unsupported operations and whole-program refinement |
| GKR | Fiat-Shamir model, field arithmetic and fixed circuit commitment | Model theorems, verifier tests and proof-digest equality | Arbitrary circuits and malicious external libraries |
| zkVM | Fixed program identity and verifier version | Saved-proof verification and exact hashes | Operator confidentiality and future verifier behavior |
| Receipt | Fixed statement, domain, root and path | Codec equality and receipt checks | Receipt-network governance |
| Destination | Immutable policy inputs and reference state | Replay, conflict, predecessor and local-candidate behavior | Production code, roles, custody, monitoring and finality |
Outside the component#
- Primitive security rests on the pinned Plonky3 implementations. Side channels, denial of service, deployed destination behavior and operational safety belong to the deployment around the component.
- Availability, timing, resource limits, recovery time and incident response are properties of your deployment.
- Deployed contracts, key handling, operational recovery, native rollup proving and parent-chain settlement are outside the repository.
- You are responsible for your own review, testing, security assessment, deployment decisions and compliance obligations. Nothing in these docs is legal, regulatory, financial, investment or operational advice.
To report a suspected vulnerability, follow the Security policy.