Infrastructure, not intermediaries
OP_NEXT 2026
Sigbash
Named by analogy with Oblivious DNS; the resolver answers queries without learning what you looked up. Two phases: Key Creation and Transaction Signing
What transactions are allowed, under what conditions, with what limits
Random user key aggregated with randomly-derived child of server key
Locally, in WASM, without revealing the transaction to anyone
Sees only a proof bundle and original opaque commitment; never amounts or addresses
Takeaway: the verifier can't sign transactions by itself; it can only provide a signature share in exchange for a valid proof bundle.
An oblivious signer can emulate covenant restrictions right now. No consensus changes needed, works with any wallet that supports Taproot today.
When (if?) covenants activate, the cosigner can serve the happy path - key path spend that looks like a plain multisig (or even just a single key!) - and the covenant can serve as the fallback path. Happy path stays private, fallback guarantees good behavior.
Right now every cosigner sees and stores users keys, balance and full transaction graph. Either we acknowledge the problem and fix it, or we all dump our coins and invest in companies that sell $5 wrenches.
Fuchsbauer & Wolf (2024), Concurrently Secure Blind Schnorr Signatures
POET: Policy Operand Expression Trees. Boolean abstract syntax trees condensed to a compact 32-byte commitment. Real policies: nested boolean logic, roles, temporal conditions, rate limits, recovery paths.
Clients register nullifiers at policy creation time - either n-use (path can be used N times) and wallclock (time-bounded windows). Client submits proof of non-membership; server records when nullifiers are burned.
2-of-2 MuSig2: one randomly generated client-side key, one derived from server master key. Blind aggregation; verifier never sees transaction or policy. Produces standard Taproot Schnorr signature.
Proof circuit embeds the sighash derivation. Links the policy proof to the signing session cryptographically, preventing substitution attacks.
Basic logic gates (9):
Advanced threshold (5):
Constrained parameters:
Example: CEO always allowed · (CFO AND Friday AND payroll address) · recovery after 90 days · treasury requires (2-of-3 AND NOT frozen)
Set inclusion is the natural model for Bitcoin collections: addresses, outputs, signatures.
Nullifiers enable stateful policies (the signer verifies compliance without learning the constraints).
Unlike prior work: we hide the policy structure itself; verifier proves compliance without learning the rules.
What the verifier actually receives: a leaf hash · a Merkle path · an opaque ZK proof. It cannot read the clause, count the branches, or learn the policy structure.
Blinded 2-Party MuSig2 has no formal security proof but there are several well known "folklore" approaches. (https://gnusha.org/pi/bitcoindev/ca674cee-6fe9-f325-7e09-f3efda082b6b@gmail.com/). They all require SHA256-in-ZK - can we get around that?
Honest prover can only be guaranteed with software attestation or firmware authenticity checks - WebAssembly must be assumed to be possibly malicious
Gap: proof and signing session are separate cryptographic objects.
The fix: SHA256-in-ZK chains policy root → sighash → challenge e, binding proof and session into one object.
Adversary A controls client WASM execution and can construct arbitrary proofs and signing requests (either malware hijacking a user's machine or a purpose-built malicious client with valid credentials; e.g. a corp user trying to subvert organization spending policy).
A generates compliant PSBT for TxA (satisfies PolicyRoot P)
A constructs ZK proof π: "Policy P satisfied"; witness contains sighashA
A submits π to verifier. Verifier accepts (proof is valid).
A opens MuSig2 session with sighashB (non-compliant TxB). Gets partial sig for TxB.
π must contain a valid in-circuit SHA256 derivation: prove SHA256(tx_data) = sighashX
Verifier checks: sighash in proof = sighash in signing session
If A substitutes TxB: sighashB ≠ sighashA in proof → signing session rejected
Substitution infeasible without breaking SHA256 pre-image resistance
SHA256 is hard to prove in ZK circuits. Bitcoin doesn't have the luxury of choosing a friendlier hash.
| System | Prove | Verify | Notes |
|---|---|---|---|
| Groth16 | 39 s | 400 ms | Ceremony |
| Plonk | 2m 29s | 12 ms | Ceremony |
| Jolt (WASM) | 6.27 s | 0.81 s | RISC-V |
| Longfellow-zk | 795 ms | 466 ms | Transparent |
Data sources: First two from Fuchsbauer-Wolf paper; Jolt from live Jolt.rs demo; Longfellow-zk from their documentation. All numbers except Jolt are for native code.
Longfellow-zk is Google's algorithm; a combination of the Ligero scheme (Lightweight Sublinear Arguments Without a Trusted Setup, 2022) and GKR Sum-Check (Delegating Computation: Interactive Proofs for Muggles, 2008). Independently audited by Trail of Bits and ISRG.
POET is polynomial: policy nodes evaluate to bits; AND/OR are Boolean gates encoded as (z − xy = 0). Nullifiers, range checks, and threshold logic all become field equations. No bytecode interpreter needed.
See: Thaler, Sum-Check Is All You Need: An Opinionated Survey on Fast Provers in SNARK Design, a16z crypto research, 2025.
Initial
45s proof
60s full
Unworkable
WASM penalty: Roughly 1.5–2x slower than bare metal on average; potentially 25–50x slower for ZK operations due to missing SIMD and unimplemented instructions (swizzle, cmul).
Jolt.rs proves SHA256 in 6+ seconds for a single SHA256 string in WASM. We need to prove ~13 SHA256 blocks.
Initial
45s proof
60s full
Unworkable
Optimized
~3s full
PSBT → proof → signed
Key optimizations: Constraint batching via Web Workers; custom polynomial evaluation with circuit-specific unrolling; aggressive inlining of hash operations; commitment tree caching.
Policy says:
Alice ∈ outputs AND amount ≥ 5 BTC
The gap: Only checks Alice is in the set. Does not limit other outputs. Client adds Eve receiving 100 BTC. Policy satisfied.
Policy says:
outputs[0] = 1 BTC to Bob
The gap: Input is 10 BTC, Bob gets 1. The other 9 BTC unspecified. Client sends it to themselves as change. Policy satisfied.
Policy says:
outputs[0] = 2 BTC to Carol
outputs[1] = remainder to self
The gap: Input 3 BTC. Carol 2 BTC. Remainder 1 BTC, but fee unbounded. Client and miner collude: miner claims 0.99 BTC as fees instead of fair amount.
Policy says:
amount ≤ 0.5 BTC per transaction
The gap: No memory across signing sessions. Client spams 100 transactions of 0.5 BTC each = 50 BTC total, even if wallet holds 1 BTC. Nullifiers required.
Perfect world (covenants exist): Happy path uses nested MuSig2 (or MuSig-in-FROST) subset between client and server, emulating the covenant privately. Fallback path: covenant opcode binds all participants, enforcing identical conditions if signers don't cooperate. Privacy maintained on happy path; fairness guaranteed on fallback.
Custodian enforces withdrawal policies but can't freeze specific addresses or selectively delay. Doesn't know which withdrawal belongs to which client.
After a timelock, cosigner signs for any valid heir. Can't tell heirs apart, can't be bribed to accelerate. Executor can't collude with one beneficiary.
CEO+CFO together: unlimited. Each alone: capped. HR: payroll addresses only, Fridays only. Intern: per diem daily limit. Same cosigner enforces all four policies without knowing which role is signing.
Federation validates peg-outs but learns nothing about who, how much, or where. Can't censor users or build a withdrawal graph.
✓ Compliant PSBT: proof verifies, partial signature returned, transaction signed.
✗ Non-compliant PSBT: proof generation fails at the constraint level; no proof bundle produced, no signature issued.
Oblivious signing is live at
SDK launching to integration partners in two weeks
Thanks for feedback & constructive criticism: