Beyond treasury governance: what privacy would ask of this primitive
This is Note 03 in my ongoing research.
Continuing from Note 2
Note 2 closed on a question I had handed forward from Note 1: how to layer privacy onto this design without breaking what already works. Before going there, I want to be honest about what I think the answer isn’t.
The current voting PoC is designed for Nervos DAO treasury governance. In that context, public attribution is part of the design, not a missing feature. Each vote’s legitimacy depends on its publicly verifiable tie to real on-chain stake. Adding privacy to that link would weaken the mechanism that makes the tally trustworthy.
So I want to say upfront: this note is not a privacy proposal for the existing design. It is a different question. The primitive underneath the voting PoC (zkVM-verified history proof over a block range) is general. It could serve other voting and governance applications. Some of those would need privacy. What would the primitive have to look like to serve them?
That is the design space this note explores. The current implementation is the floor it is built on, not the target it is critiquing.
Use cases that would need privacy
Three scenarios come to mind where privacy has proven to be needed, or clearly is.
Anti-collusion governance (MACI’s domain). When votes are public, a briber can pay only on confirmed delivery. “Vote yes on proposal X, I pay you.” The verification is free if the vote is on-chain. The threat is structural in any DAO where individual votes carry economic weight.
Whistleblower and politically sensitive votes. Boards voting on internal misconduct, members voting on controversial issues, workers voting on union representation. Retaliation is real, and the public ledger becomes the attacker’s tool.
Stake-weighted votes with uneven stake distribution. A nullifier on the vote cell wouldn’t help here. The amount field fingerprints against publicly indexable balances. If three voters hold 1000, 2000, and 5000 CKB respectively, a vote with amount: 5000 identifies the third one. The leak is in the stake, not the cell. (See Note 2.)
The first two are what I named “membership proofs without identity disclosure” in Note 1 The third asks for more: hiding voter weight, not just identity.
The three leak points to address
Three places in the vote cell where identity becomes public. Any privacy proposal has to address all three, or the leak survives.
-
lock_hashon the vote cell. The current design uses it as the dedup key. Anyone reading the chain learns who voted. -
dao_indexin the cell’s data. Points at the voter’s DAO deposit cell, which has its own publicly attributable lock script. Even if the vote cell hid the voter’s lock, this pointer would walk back to it. -
amountin the cell’s data. The stake weight, published so the guest can verify the tally. Cross-referenced against publicly indexable DAO deposits, it fingerprints the voter when stake amounts are non-uniform.
A nullifier on the vote cell only addresses (1). The leak survives at (2) and (3) regardless. Any honest privacy proposal has to address all three. (Note 2 walks through this in more depth.)
Candidate approaches
There are at least three starting points I have worked through. Each addresses some of the leak points and not others, and none of them feel clean to me.
Direct nullifier addition. Replace lock_hash on the vote cell with a nullifier derived from a voter secret, with a Merkle tree of voter commitments anchoring eligibility. Vote cells carry (nullifier, vote, amount, dao_index).
This fixes leak (1). It does nothing about leaks (2) and (3). The dao_index still walks back to the staker’s deposit; the amount still fingerprints. A reader of the chain still learns who voted, just through a different path. I read this as a building block for the approaches below, not a solution on its own.
MACI-style separation of identity from stake. Identity proven via Merkle membership over voter commitments (anonymous). Stake proven separately via a commitment scheme: voters pre-commit to “I have X stake in this set” rather than referencing a specific deposit. The vote cell publishes a nullifier, a stake-range proof, and the vote choice.
This addresses all three leaks if done right. It requires more circuit work and a separate stake-commitment registry on-chain. The hard part is keeping stake commitments honest. The DAO deposit still has to be verifiable, but the link between deposit and voter has to be hidden. This is the most promising path I can see, and also the most engineering work.
Mixing-pool / stake-aggregation. Voters deposit into a shared anonymous pool. Voting eligibility comes from pool membership, not from individual DAO deposit references. Nullifiers prevent double-voting. The vote cell carries a pool-membership proof instead of dao_index.
This addresses leaks (1) and (2) cleanly but changes the deposit mechanism fundamentally. The existing Nervos DAO interaction would have to be rewired through the pool, and there are liquidity questions (when can voters exit the pool?). It is a well-understood pattern from Tornado-style mixers, but in my read, a heavier change to the DAO model than the other two.
None of these is a free upgrade. The vote cell is not the leak. The stake is. Any honest privacy proposal has to redesign how stake is represented and verified, not just how votes are recorded.
What this would ask of the primitive
The primitive underneath the voting PoC is general by design: prove something happened in blocks N through M, commit the conclusion publicly. The voting design is one application of it. Privacy applications would be others. The question is what would have to change at the primitive layer rather than the application layer to make those work.
Three changes feel load-bearing to me.
Commitments as first-class. Today the primitive proves things about cell data directly: the guest reads amount, sums it, commits the total. For privacy, that has to become: the guest reads a commitment to amount, aggregates commitments, commits an aggregate commitment. The primitive needs to be comfortable working with hidden values, not just cleartext ones.
Aggregation without per-element disclosure. The voting design currently computes yes_vote + no_vote and publishes both. For weight-hiding use cases, the primitive would need to support proving the aggregate is correct without exposing the per-vote weights that produced it. Homomorphic commitment schemes and proof composition are the obvious candidates.
Nullifier-set management as a shared concern. The voting design handles dedup application-specifically. If privacy applications across CKB share infrastructure, nullifier sets need a standard primitive form (how they are stored, updated, and queried) rather than each application reinventing one.
These changes don’t have to land all at once. They are also separable from the voting application itself. My read is that this is the conversation worth having about generalizing the primitive: not “should the voting design have privacy,” but “what does the primitive owe its other applications.”
What I am taking from this
I want this to land clearly: privacy is not the voting PoC’s job. The design was built for treasury governance, where public attribution is the point, not the cost. Adding privacy to it would work against what makes the design fit its use case.
The primitive underneath, though, has a separate life. It can serve voting applications with very different threat models. The privacy-needing use cases I listed are real. Adding privacy to those applications won’t come from a small change to the voting design. It will come from changing how stake is represented and verified, plus targeted changes at the primitive layer.
Where the line between primitive and application actually sits is the kind of question best worked out in conversation with people closer to the design. I would welcome that. The point of this note was to name the design space, not close it.
References:
- XuJiandong’s voting PoC: github.com/XuJiandong/ckb-vote-poc.
- Nervos DAO treasury discussion: talk.nervos.org.
- MACI (Minimum Anti-Collusion Infrastructure): github.com/privacy-scaling-explorations/maci.