Gm! I’m looking for feedback on the direction I am considering for noir-ckb
before I commit to the next stage of development.
The proposed direction is a toolkit where a developer writes a supported Noir
circuit and a typed Cell-binding specification, then generates the CKB Type
Script, proof artifacts, adversarial tests, and versioned manifest needed to
make that proof authorize one specific Cell transition.
The security rule behind the idea is:
proof verifies mathematically
!=
proof authorizes the intended CKB state transition
The background and original problem are in
The Proof Is Valid. The Transition Might Not Be.
This post is specifically a direction check: whether the proposed generator is
useful, whether its boundary with existing CKB tools is correct, and whether
the proposed second circuit is a convincing way to test it.
What works today
The current supported example takes one pinned, public-first Noir circuit
through:
Noir source and ABI
-> ACIR, R1CS and witness checks
-> development BN254 Groth16 proof
-> strict arkworks and Molecule conversion
-> generic Groth16 verification in CKB-VM
-> Type Script binding to the actual Cell transition
The proof’s seven public values describe the Capsule identity, old state, old
nullifier, new state, action, new nullifier, and replay domain. The generic
verifier checks the proof, while the Capsule Type Script reconstructs that same
ordered statement from the transaction. Both scripts must accept.
The retained matrix contains one accepted transition and eleven expected
rejections. It rejects a valid proof paired with the wrong new state, Capsule
identity, or replay domain, as well as an invalid proof, missing verification
key, malformed data, a changed verifier lock, and ambiguous input groups.
The accepted path used approximately 101.6 million CKB-VM cycles in the retained
development runs. That is an observation for the exact pinned binaries and
fixtures, not a performance guarantee.
Repository: wamimi/noir-ckb-verifier
Reviewer instructions: Week 12 reviewer quickstart
How I understand the relationship with existing work
noir-ckb reuses Cecilia Mulandi’s groth16-ckbverification libraries and wire format.
Cecilia has also published [zk-Lock] (GitHub - CECILIA-MULANDI/zk-Lock: Reusable CKB lock script that conditions cell spending on a valid Groth16 proof. · GitHub),
a reusable Lock Script that gates Cell spending on a Groth16 proof. Its current
flow commits the verification-key hash and public-input hash into lock.args
when the Cell is created. At spending time, the proof becomes the authorization
to unlock that Cell.
The boundary I am exploring is different but complementary. The noir-ckb
prototype uses a Type Script to derive the public statement from the transaction
being attempted: the consumed state, created state, application identity,
action, and replay context. The Lock answers whether the Cell may be spent; the
Type Script answers whether this particular state change is valid. They can
occupy different script slots in the same transaction.
The proposed long-term product
Today, the transaction binding is described twice: once declaratively in
noir-ckb.toml, and once by hand in Rust.
[binding]
public_sources = [
"type_args.capsule_id",
"input_cell.data.state_commitment",
"input_cell.data.nullifier",
"output_cell.data.state_commitment",
"constant.update_action_id",
"output_cell.data.nullifier",
"type_args.replay_domain",
]
The direction I am considering is to make the binding specification generate
the application layer.
A developer would write a supported Noir circuit and a typed binding manifest.
noir-ckb build would then:
- check the Noir ABI, public parameter names, order, and supported compiler
profile; - generate the Rust Type Script that reads the declared transaction fields and
reconstructs the public vector; - generate adversarial tests for changed state, identity, action, replay
context, malformed encodings, and ambiguous groups; and - emit a versioned manifest tying the circuit, verification key, binding, and
generated script together.
The generated Type Script and test matrix would be the main deliverables. The
goal is to reduce the amount of hand-written byte-offset and public-input
mapping code, because a mistake at that boundary can leave a proof valid while
changing what the application believes it authorizes.
Why I am considering Battleship as the second circuit
I need a second, unrelated circuit to show that the generator is infrastructure
rather than a tool that only reproduces the existing Capsule fixture.
My current candidate is a small on-chain Battleship game. Each player commits
to a board and later proves that each hit-or-miss response is consistent with
that commitment. The proof must also be bound to the game, turn, coordinate,
and exact old-to-new game Cell transition.
It provides visible cheating tests like wrong coordinates, stale turns, cross-game
proofs, inconsistent responses, and altered state updates, without first
depending on an external issuer. The game would demonstrate the toolkit; it
would not define the whole product. Ecosystem feedback may point to a better
CKB-native example.
Important limitations
- The Capsule circuit is a wiring fixture, not a private protocol. Its arithmetic
relations expose simple functions of the private value and do not constitute
a hiding commitment scheme. - The current path supports one pinned, public-first Noir compatibility profile,
not arbitrary Noir circuits. - The public-first restriction is a workaround for a public-wire ordering issue
in the experimental Noir-to-Groth16 backend. I retained the failure as a
regression and reported it in
Noir-Groth16 issue #1. - The Groth16 setup is development-only. There is no production ceremony,
audit, mainnet deployment, or independent clean-clone result yet. - BN254 Groth16 is not post-quantum. This is application-layer verifiable
computation, not a replacement for CKB’s work on post-quantum asset
authorization.
How to test the current preview
The shortest path uses only public retained fixtures. It does not require a CKB
node, wallet, devnet deployment, or generation of trusted-setup material.
Please follow the
reviewer quickstart
and run:
./scripts/reviewer-smoke.sh
It builds both RISC-V scripts and runs the 12-case CKB-VM matrix. Hosted CI has
passed the retained fixture, but no independent external developer has returned
a clean-clone result yet. A complete failure log is as valuable as a pass
because independent reproduction is the next release gate.
Feedback I am looking for
- Is the Lock/Type boundary useful: proof-gated spending in a Lock Script and
proof-bound state-transition validation in a Type Script? - Would a typed binding manifest that generates the Type Script and adversarial
tests be useful, or would CKB developers prefer to write that application
binding directly? - Is Battleship a convincing second-circuit test of the generator, or is there
a better CKB-native problem that needs proof-bound transitions? - Is approximately 101.6 million cycles practical for occasional proof-bound
authorization, and what cycle target should the project design around? - Which pieces should be contributed to
groth16-ckbor the Noir-Groth16
backend rather than maintained innoir-ckb? - What is the smallest post-program milestone that would produce useful
ecosystem evidence before any Community Fund proposal?
@Hanssen, @RetricSu, and @matt_ckb — Neon mentioned that your directional
guidance would be especially valuable. I would appreciate your thoughts on the
architecture boundary, reference application, and the smallest useful next
milestone. Tagging @neon.bit as well for the CKBuilder context.
Thanks for reading.