Relay-Mediated Proving
Split a typical ZK proof flow into two roles. The client signs a portable signed message offline. A relay generates the SNARK over that message and submits it on-chain. To prevent a front-runner from lifting the proof off the mempool and re-submitting it from their own address, the proof binds to the relay's submitter address as a public input that the application contract checks against `msg.sender`.
Split a typical ZK proof flow into two roles. The client signs a portable signed message offline. A relay generates the SNARK over that message and submits it on-chain. To prevent a front-runner from lifting the proof off the mempool and re-submitting it from their own address, the proof binds to the relay's submitter address as a public input that the application contract checks against msg.sender.
The pattern is distinct from pattern-safe-proof-delegation.md, which addresses intent-based delegation in a wallet UX context (the client could prove locally but chooses to delegate). Relay-mediated proving is for clients that cannot prove locally at all.
- Client-side signing primitive: a signature the client device already supports (ECDSA-secp256k1 with RFC 6979 plus canonical-s, EdDSA, or RSA-PSS). The client never holds witness data the relay can extract beyond what the signed message carries.
- In-circuit signature verification: Noir
std::ecdsa_secp256k1, in-circuit EdDSA, or equivalent gadget. The circuit recomputes the signed-message encoding bit-identically and verifies against the client's public key. - Submitter binding: the proof's public inputs include a
submitterfield. The circuit asserts the field equals a witness-shared value; the application contract on-chain assertspublicInputs.submitter == msg.sender. Together these close proof-stealing front-running. - Relay set: multiple relays per deployment. Clients fan out to
k < Nrelays. Single-relay deployments do not satisfy the privacy assumption.
- Front-running resistance: a front-runner who lifts the proof from the mempool cannot re-submit from a different address; the proof reverts on a different
msg.sender. - Witness confidentiality from the chain: only public inputs are revealed on-chain. Private inputs (signature, membership path, internal state) stay with the relay during proof generation.
- No client-prover requirement: the client device runs only its native signature primitive.
- Threat model: adversaries include front-runners, the chain itself, and a non-quorum of compelled relays. Out of scope: a fully compelled or breached relay set; signed-message-encoding mismatches between client and circuit (a known historical bug class).
- The relay sees the signed message in cleartext. Mitigations: client fan-out across relays, relay-set jurisdictional diversity, decryption-key rotation.
- Pinned signed-message encoding. Client-side serialization and circuit-side recomputation must match byte-for-byte. Audit must verify equality.
- Witness-binding correctness. All circuit gadgets that consume the client's public key (signature verification, membership leaf hash, nullifier hash) MUST share a single witness variable. Independent equality constraints have shipped historically as a soundness bug.
- Submitter binding precludes transferable proofs. Applications that mint transferable proof artifacts need a different proof-stealing mitigation.
- Relay-economic recovery is open. Relays pay gas for submitted (and reverted) transactions. Deployment-level compensation models are out of scope.
A privacy-preserving token airdrop with offline eligibility credentials. Eligibility is pre-distributed through a tamper-resistant medium (paper QR with embedded signing key, low-power hardware token). To redeem, the recipient signs a redemption message and forwards it to one of N airdrop-relay operators. The relay generates an UltraHonk proof of eligibility-Merkle-tree membership and signature validity, with publicInputs.relaySubmitter set to the relay's rotating submission EOA. The airdrop contract verifies the proof and asserts msg.sender == publicInputs.relaySubmitter. A front-runner who lifts the proof cannot redirect the airdrop because the submitter check fails.