article

Escape hatches in MPC, and who may open them

14 min read

Protocols that hold real funds tend to have an escape hatch. Rollups have forced inclusion and a withdrawal path that runs against the settlement layer when the sequencer stops answering. Nobody treats this as exotic. The escape hatch is the fallback: when the system that holds your funds stops answering, you can still get them out yourself.

Privacy protocols are now going live to hold real value. The same expectation has not arrived with them.

Where a recovery path does exist, it usually belongs to the protocol rather than to the user. The operator assesses the failure and decides to release funds, by different routes depending on how severe the outage is and how recoverable it looks. That is a reasonable thing to build. It is not the same thing as a user being able to leave.

Two questions separate them. Can a user withdraw without the nodes? And who decides when assets get unshielded?

We build private payment infrastructure on MPC and collaborative SNARKs, so what follows is an answer from inside that architecture rather than a survey. Balances are secret-shared across a network of nodes, the chain holds a commitment, and the nodes produce a proof that the chain verifies. In this variant, sender and receiver are hidden from the chain, while the nodes themselves see them, which is part of why the escape hatch takes work that public identities would not need. The escape hatch described here is designed against that specific shape, and the pieces it needs, notes, nullifiers, published openings, a fraud proof, exist because of properties particular to it. A system built on different foundations would need a different construction, though it would face the same question at the end.

What is already guaranteed

Start with what the MPC network already cannot do to you, because the gap only looks sharp once the guarantees are clear. Balances live on chain as a commitment, the real values sit secret-shared across the nodes, and every state update carries a proof the chain verifies before accepting it. Three properties follow.

Privacy rests on the nodes not colluding. A threshold of them working together can reconstruct your balance.

Integrity rests on the proof, assuming the transfer circuit correctly constrains value conservation and the collaborative SNARK is sound, which is where audit effort concentrates. Granted that, a fully colluding network can read balances in the system and still cannot move a token it does not own, because no valid proof exists for that.

Censorship resistance is deliberate. Intents queue on chain and the contract only accepts work on the head of the queue, and the network’s proof stays valid even when the sender has insufficient balance: it opens a boolean flag, applies the state change only if the flag is true, and proves it did exactly that. An underfunded transfer is a provable no-op rather than a request the network is unable to answer.

None of that is liveness. The network cannot reroute your funds and cannot censor you. Yet it can still simply stop. Your funds could then be provably intact and completely unreachable, for as long as the operator chooses.

Why this is harder when the protocol is private

A rollup escape hatch works because the settlement layer already holds what it needs: published state, legible balances, and an address to authenticate against. Putting balances into an MPC network and hiding who transacts with whom removes all three.

The chain doesn’t know what you own. Onchain state is a single commitment: the root of a Merkle tree accumulating every user’s balance commitment, so no individual user holds an opening to the thing the chain is actually storing. Redeeming means opening your own leaf, which takes the balance and its blinding value, and in normal operation the only party holding those is the network that has stopped answering.

The chain doesn’t know who you are. Ownership has to be established with a proof rather than a signature from a known address, and since there is no per-user slot to clear, double redemption has to be prevented with a nullifier.

The data that makes escape possible has to be published in advance. Users cannot derive their own openings, so the network has to hand them over as it goes, encrypted, on chain, on every transfer. Which creates a new failure mode: a network intending to strand its users could publish well-formed garbage for months, and nobody would find out until the day they needed it.

The state you would be escaping from

There are four moving parts, all of them things the MPC network is holding on your behalf.

A registry. Users sign up once and are assigned an index i, which the protocol uses in place of their address. Their public key is recorded against that index in a public registry with its own contract, so a proof can be checked against a registry root the chain agrees is current.

The balances. Each user has a balance b and a blinding value r, secret-shared across the network. The network holds the commitments C = commit(b; r) in the clear and accumulates them into a Merkle tree whose root is the entire onchain state. One root for everybody is what hides sender and receiver, since per-user slots would announce who transacted.

The intent. A transfer starts with the client. It posts commitments to the sender index, the receiver index and the amount, encrypts the values and shares to each MPC party, and proves in zero knowledge that the bundle is consistent: it knows the key at the sender index, the commitments open to the values it shared out, the ciphertexts are correct, the amount is bounded, sender and receiver differ. The contract verifies and queues it. That verified intent is the ground truth the network’s later proof gets measured against.

The update. The network takes the head of the queue, moves the amount between the two balances, samples fresh blinding values, opens the two new commitments, rebuilds the affected Merkle paths and opens the sufficiency flag. One CoSNARK covers all of it, including a recomputation of the intent commitments, which binds the network’s work to the user’s request.

Building the hatch

There are two requirements on top of the three difficulties. The trigger must belong to nobody, and the contract must settle alone.

Notes and nullifiers

Put the user’s index inside the leaf commitment.

C = commit(b, i; r)     instead of     C = commit(b; r)

That turns a leaf into a spendable note, and stops it being spendable by whoever happens to learn its contents. Redemption requires proving knowledge of the key at index i, and the sealed index is what pins the note to that registry position. Knowing b and r is never sufficient.

The contract gains an append-only list of notes, and the network appends the two new commitments on every transfer. Publishing them costs nothing in privacy, since a note is a commitment and reveals neither the balance nor whose it is. Retiring a note needs a nullifier, published once and refused thereafter:

n = hash(C, r)

ZEXE keys its nullifiers on the owner’s secret key. That is not available here, because two parties have to derive the same value: the network when it supersedes a note during normal operation, and the user when redeeming one after a halt. The network holds no secret keys and must never hold one. It does hold r, because it generated it. That is why r is the key ingredient.

Getting the opening to the user

To redeem, a user needs b and r, so every user has to already hold the opening information for their current note before anything goes wrong. The network therefore encrypts (b, r) under the recipient’s registered key and posts the ciphertext on chain, once for the sender and once for the receiver, as a routine part of every transfer.

Proving that encryption inside the collaborative SNARK would make a correct ciphertext a precondition of a valid state transition. It is also unaffordable. The elliptic curve group operations involved cost more to prove inside a CoSNARK than the entire rest of the transfer, paid on every transaction to insure against an event that may never happen.

The distinction is who is proving. The client’s intent proof is generated locally by a single prover, so it can absorb the cost of proving an encryption, which is exactly what the intent step above does. The equivalent client proof in the published confidential design runs in around 135 ms on a consumer laptop. The state update proof is collaborative, generated across the MPC parties over secret-shared data, and it needs the intermediate values of the curve arithmetic as witness, so the parties have to compute those under secret sharing too. Curve operations are not especially hostile to MPC alone, nor to ZK alone. It is the combination that is expensive. Appendix F of the Merces paper measures this for curve-based commitments, and its preliminary numbers put witness extension for a batch of 50 transfers at roughly 1.5 seconds against under 50 ms, about thirty times more, and several times the cost of the whole batch including proof generation.

So the ciphertexts go up unproven and the scheme becomes optimistic. The user gets a fraud proof, which establishes:

I know the key for my registered public key, decrypting the posted ciphertext yields (b', r'), and commit(b', i; r') is not the note the network published.

That is broken as stated, and the repair is the interesting part. Proving a decryption mismatch is trivial: just supply the wrong key. A passer-by could slash the network over a perfectly good ciphertext. So the proof has to be bound to the user it concerns without revealing who that is. The challenger reopens the identity commitment that gets posted at intent registration, which only the party knowing its blinding value can do. Senders chose that value. Receivers never saw it, so the sender must additionally encrypt the receiver’s blinding value under the receiver’s registered key, with the intent proof extended to cover that encryption and a registry Merkle proof that the key really is the one sitting at the receiver’s index.

The trigger, and the withdrawal

If a person, a committee or a multisig decides when the hatch opens, the hatch is worth whatever that party’s judgement is worth on the day. The condition has to be one the contract can evaluate unaided: the queue has not advanced for a fixed window, say a day. This is deliberately crude. It cannot distinguish a malicious halt from a failed deployment, and it does not need to. From where the user is standing those are the same event.

To redeem, the user posts a proof along with the note index, the balance b, a nullifier and a registry root. The proof establishes three statements:

I know the key stored at index i in the registry under this root, I know b and r such that the note at that index is commit(b, i; r), and n = hash(C, r).

The contract looks up the note, checks the root, checks the nullifier is fresh, verifies, records the nullifier and pays out.

No MPC. No proof anyone else has to generate first. No signature but the user’s. The ingredients are the note, already on chain, the opening, published in advance, and the key, theirs all along.

Withdrawing to a public chain makes the amount and the receiving address public, as it always does. Redemption is a withdrawal, so the same applies.

Coming back

The network can resume, but it has to reconcile first, because every redemption changed onchain state without it. It holds the commitment tree in the clear, so it scans for each redeemed note, replaces it with commit(0, i; 0), and proves per redemption that the root moved accordingly, before it may touch the queue again. It cannot quietly pretend the exits never happened.

One residual cost sits with the user, who has to watch the chain, though only to be in a position to challenge a bad ciphertext and only for the most recent transaction they took part in. Splitting the registry key into three, one to spend with, one to detect incoming transfers, one to decrypt balances for fraud proofs, with the latter two derived from the first, lets that watching be delegated without delegating the ability to spend.

The whole escape hatch costs two extra commitments, two nullifiers and two ciphertexts on chain per transfer, plus the nullifier hashes inside the proof. Keeping the encryption outside the proof is the decision that holds the number down.

The three modes of the contract, and the cycle between them In Normal mode the network processes the queue, and every transfer publishes new notes, nullifiers and encrypted openings. If the queue stalls for the timeout window the contract enters Escape mode, where each user redeems their own note independently, with no MPC involved. When the network recovers it enters Reconciling, zeroing every redeemed note with a proof per redemption. Only then does it return to Normal. There is no edge from Escape mode straight back to Normal. Every transfer publishes notes, nullifiers, openings Each user redeems alone, no MPC in the loop Queue stalls for the timeout window Network recovers Normal Escape mode Reconciling Every redemption zeroed and proved, before the queue restarts The only way back runs through Reconciling. There is no edge from Escape mode straight to Normal.

Who holds the trigger

Machinery is the easy part, and it is also the part that is specific to MPC. The question underneath it is not. Privacy systems holding real assets have thought about failure, and many have a forced-unshield path of some kind. What separates them is who may open it, and that is the sense in which the rollup norm has not yet arrived.

Three exit models compared by who holds the trigger No exit: the trigger is the operator returning, the operator acts, and operator trust at the exit is total. Governed exit: the trigger is human judgement, a committee or set of signers acts, and operator trust is reintroduced. Unilateral exit: the trigger is a timeout firing, the user acts alone, and no operator trust remains. No exit Governed exit Unilateral exit Trigger Operator returns Human judgement Timeout fires Who acts Operator Committee, signers User, alone Operator trust Total Reintroduced None Colour shows operator trust remaining at the exit: coral total, amber partial, teal none.

The first two reintroduce, at the exit, exactly the trust the architecture spent all its effort removing. The commitments, the proofs, the ordered queue all exist to make the operator irrelevant to whether your money behaves correctly. Governed exit hands them back a veto over the one operation that matters most, at the moment they are least likely to be neutral about it.

If you have built a system whose central claim is that the operator cannot be trusted with your funds, and then the operator decides when you may leave, the claim has a hole in it.


Status. The escape hatch described here is a prototype we are building for Merces, our confidential payments system. The Merces paper sets out the confidential transfer protocol this builds on, in the simpler setting where sender and receiver are public. The wider architecture it sits inside is private shared state.