Hi,
We’ve just open-sourced Liquid Cells under MIT, and it’s running live on the Pudge testnet. It’s a small, focused thing: a pooled, operator-trustless micro-payment rollup that lets thousands of people hold and move sub-61-CKB balances behind a single anchor cell, with no trust in the operator.
We’re posting it here for one reason: we want the community to read it, run it, and try to break it. It is pre-alpha and testnet-only.
A enviar: Liquid-Cells-slide-02.png
What we’re after right now is technical scrutiny.
- Repo: GitHub - LusoCryptoLabs/Liquid-Cells: Trust Minimized pooled multi-recipient payment rollup for Nervos CKB · GitHub
- 5-minute read:
LIGHTPAPER.md - To reuse the primitives:
docs/INTEGRATION-GENERIC.md - Every live Pudge hash:
DEPLOY.md
TL;DR
- The gap: CKB’s cell model charges state rent. Every cell locks ~61 CKB. That’s why CKB never bloats, but it means a balance below ~61 CKB cannot exist on Layer 1. The whole consumer micro-payment vertical (tips, game antes, per-view streaming, creator pay, micro-airdrops) is structurally excluded at the base layer.
- The fix: a pooled ledger. Balances live off-chain in a keccak sparse Merkle tree behind one anchor cell. A sequencer orders transactions but is powerless to cheat, because every checkpoint to L1 carries a zk validity proof the chain verifies on-chain. It’s a zk-rollup (validity proofs + on-chain data availability), not a custodial pool.
- It’s real, on a public chain: the anchor type script verifies real SP1 to Groth16 (BN254) proofs inside CKB-VM on Pudge at ~80M cycles (~2.3% of the ~3.50B block limit). Successive checkpoints have chained on-chain, and an unsigned, permissionless checkpoint has advanced a live pool.
- The ask: read it, run it, break it. Bug reports via private disclosure (
SECURITY.md); design feedback welcome right here. - No novelty claim. This is an implementation, not an invention. See the prior-art section below.
1. The problem: the 61-CKB floor
On CKB, every Layer-1 state change consumes and creates cells, and a standard cell needs ~61 CKB of capacity. This is a deliberate, good design. It’s why CKB has no unbounded state bloat. But it has a hard consequence:
A balance below ~61 CKB cannot be a first-class object on Layer 1.
You cannot give someone a 0.3-CKB tip as its own cell. A game ante, a per-view streaming payment, a micro-airdrop: none of them can exist at L1. And this gap is sharper on CKB than on most chains, because an account-model chain lets a tiny balance simply sit, while CKB’s cell minimum cannot.
CKB’s existing primitives each solve part of it, but not the pooled, many-to-many holding shape:
| Primitive | What it gives | What it can’t do |
|---|---|---|
| Direct L1 transfer | Native, trustless | One >=61-CKB cell per payment; no micro-payments |
| ACP (anyone-can-pay) | Receive any amount into a live cell | Needs a live >=61-CKB cell; not a pooled ledger |
| Fiber (payment channels) | Non-custodial routed payments | Point-to-point liquidity graph, not a shared multi-recipient ledger |
Liquid Cells is the missing piece: a pooled, multi-recipient ledger that no current CKB primitive provides in trustless form. (More on why this is complementary to Fiber, not competitive, in §6.)
2. What it is: a pooled ledger, off-chain
Balances live off-chain in a sparse Merkle tree (depth 256, keyed by keccak(pubkey), leaf = {balance, nonce}), anchored by one on-chain cell:
off-chain on-chain (CKB)
participants -- signed intents --> sequencer = state machine
| builds a batch, new root
v
SP1 proof + state-diff blob
|
v
anchor cell (pool funds)
type script verifies:
- the proof - the diff is published
- value conserved - exits honored
The operator (“sequencer”) orders transactions and advances the tree, but it is powerless to cheat, and under permissionless sequencing it isn’t even privileged: anyone with a valid proof can take its place. Every user can rebuild their balance from L1 data and exit unilaterally.
One CKB-idiomatic detail we’re happy with: the heavy part (the ~112KB verifier code cell) is a single shared cell_dep, paid once. Genesis-ing a brand-new pool adds only its 80-byte anchor + lock, not another copy of the binary.
3. The keystone idea: the sequencer is the zk guest
There is exactly one implementation of the state-transition function, the liquid-cells-stf crate. The same bytes are used twice:
liquid_cells_stf::apply(state, batch) -> (new_root, PublicValues)
|- sequencer: runs natively to advance the root, serve the API, post DA
'- SP1 guest: proves the identical run -> Groth16 -> the CKB anchor verifies it
Because both run identical code, they can never disagree on a root, so the entire “divergent root” class of bugs is gone by construction. And the proof attests the two invariants the protocol’s safety rests on:
- No debit without a signature. A leaf’s balance only decreases via a Transfer/Withdrawal carrying a valid secp256k1 signature from that leaf’s owner.
- Value conservation.
Σ balances + fees == total, andnew_total == prev_total + Σ deposits - Σ withdrawals - fee_withdrawal(the sequencer’s fee draw comes only from accumulated fees, never user balances).
Together these make operator theft and balance inflation impossible, not merely disputable. There is no fraud-proof game to get wrong, and no honest-watcher assumption anywhere.
4. The trust model: five guarantees
Each one is implemented and adversarially tested:
| # | Guarantee | What it kills |
|---|---|---|
| 1 | Validity proofs: every root transition carries an SP1 to Groth16 proof the anchor type script verifies | Theft, balance inflation, forged or stale settlement, impossible by construction |
| 2 | On-chain data availability: each checkpoint publishes the plaintext, fixed-width state diff in tx witnesses (fee-priced, not capacity-locked) | Data withholding; any user can rebuild their leaf + exit proof from L1 alone |
| 3 | Force-exit escape hatch: after operator silence for T epochs, anyone withdraws unilaterally against the last committed root (plain SMT inclusion, no proof) |
Censorship and freezing; the operator cannot trap funds by going dark |
| 4 | Atomic-swap egress: hash-locked swaps move sub-61-CKB dust out without its own cell (plus a sender-side on-chain force-refund for timed-out swaps) | Dust entrapment; small balances get a trustless exit at any denomination |
| 5 | Permissionless sequencing: a permissive anchor lock + on-chain lock continuity let anyone advance the root (with a fee reward so it’s self-sustaining) | The privileged operator; sequencing is open and capture-proof |
The only trust assumptions left are cryptographic hardness (secp256k1 + keccak + Groth16) and CKB Layer 1 itself, the same base as any on-chain transaction.
5. What’s actually live on Pudge (proven, not a spec)
This is the part we most want challenged. The usual rollup risk, “can this even work on-chain?”, is retired on a public chain:
| Metric | Result |
|---|---|
| Groth16 (BN254) verify cost on CKB-VM | ~80M cycles, about 2.3% of the ~3.50B block limit |
| STF + contract integration tests | 80 + 111, green |
| Adversarial red-team cases (in CKB-VM + STF) | 56 (counted within the 80 + 111 above), no exploitable break |
| Permissionless checkpoint | an unsigned checkpoint advanced a live pool, gated only by the proof + lock continuity |
The anchor type script verifies real SP1 to Groth16 proofs inside CKB-VM on a public chain, not a simulator, not a spec, and successive checkpoints have landed end-to-end.
A note on the verifier cost, because someone here will ask: the first on-chain attempt was rejected for exceeding the block cycle limit (a 4-pairing substrate-bn verify cost ~3.81B vs the ~3.50B consensus limit). We briefly shipped a 3-pairing e(alpha,beta) precompute hack to fit at ~3.47B (a thin ~0.7% margin), then ported the verifier to ark-bn254: a plain 4-pairing check at ~80M cycles, a ~43x reduction that removed the hack entirely. This lines up with the CKB-VM data points from ckb-zkp (~77M) and the “Optimized SP1 Verifier” thread.
On-chain evidence (Pudge, verify it yourself)
Anchor type script codeHash : 0x83158549bb5b2241997d1e8bb5e24b432a78e74473c8601cea276411bca99f1e
Genesis anchor type hash : 0xf4c1baf2fda7e9a07b762387934ce6b2d275bd4e533ae558c7233907f2f6b7a1
Baked SP1 program vkey : 0x00213af5b4cc8922754e5b8ee7e610de703d4cb17fabb0a187c95c37fe67c1e8
First checkpoint (seq 0->1) : 0xca6bfc76cbfae1b1dfedfbcab02fe84c1ab9019cb711defce65587091c373d51
Checkpoint 2 (seq 1->2) : 0x599085cefc048d76d966d78cf8baa27c0242b6bc999133bcc15070af5d2236a3
Checkpoint 3 (seq 2->3) : 0x80d4612489c59cb630d8dd8c32d5edc3d2ac2009ab8068eee9ae5b879052bec1
ark-bn254 verifier upgrade : 0x68f9730a2cd9bfccb5b34e733807c28646c6b74a84980827c7ffcc6df1452767
(verify 3.47B -> ~80M cycles; binary 293KB -> 112KB)
Permissionless pool, cp2 (seq 1->2, UNSIGNED):
0x3145a2d86ca21c26f32bf14ac2ad86ecb7644c3bf0f21a8cfbff6638c3692767
Full artifact list (deposit lock, type-id’d pools, force-refund + lock-continuity + sequencer-reward upgrades, etc.) is in DEPLOY.md.
6. Honest positioning (this matters more than the demo)
This is a working implementation, not a novel protocol. Nearly every ingredient has prior art, much of it on CKB, and we’d rather say so plainly than have you find out:
- sec-bit’s Clover: a zkRollup on the same CKB cell model. The closest cousin. (Clover’s deposit/withdraw, a direct lock-script commitment update with no operator and no proof, is arguably better than ours, which currently spends a full proven checkpoint to credit a deposit.)
- sec-bit’s ckb-zkp: on-chain Groth16 on CKB-VM (~77M cycles). On-chain Groth16 on CKB is not a first.
- Intmax2 (Ethereum): a pooled, payment-focused zk-rollup with client-side proofs; more scalable and private than us.
- Godwoken, Fiber, and the active “Optimized SP1 Verifier for ckb-vm” effort here on the forum.
Our contribution is engineering and integration on a live chain, plus a couple of CKB-idiomatic choices (anchor-cell-as-pool, lock-continuity permissionlessness). The full side-by-side is in docs/PRIOR-ART.md.
On Fiber specifically: we see Liquid Cells as complementary, not competitive. This isn’t “pool vs. channels.” Any channel design that lets thousands of sporadic long-tail recipients receive without each funding a channel does so through a hub, and the channel-free Fiber tipping hub that exists today is custodial (an internal ledger). Liquid Cells is essentially the non-custodial version of that hub: the operator can’t steal or mint, balances are reconstructable from on-chain data, and users can force-exit. And Fiber is our intended dust-egress rail: a sub-61-CKB balance leaves the pool via an atomic swap sharing one hashlock with a Fiber leg. Fiber serves the payment-flow half of the consumer vertical; Liquid Cells serves the pooled-holding half. The honest, attackable comparison is in docs/COMPARISON-FIBER.md.
7. Ongoing efforts
Sophisticated reviewers have seen enough vaporware to value candor, so here’s the unflattering list:
- Testnet only. Live on Pudge, no real funds.
- Trusted setup. Groth16 inherits a trusted-setup assumption (mitigated by an established public ceremony).
- Finality ~7 min per checkpoint (soft-confirmation receipts give sub-second perceived confirmation, but that’s liveness, not finality).
- Dust egress, Fiber binding. The in-pool hash-locked legs (keccak) and the sender-side on-chain force-refund are live, but the full Fiber-bound atomic swap is staged and live-validated on Fiber testnet, not yet usable on the deployed Pudge guest. Fiber’s HTLC uses SHA-256 and the in-pool HTLC uses keccak; binding them needs the
htlc_sha256change, currently off in the deployed guest, shipping in the post-audit re-bake. - Dust egress, receiver side. The sender of a timed-out swap can force-refund on-chain; the symmetric receiver “force-claim” isn’t expressible under a single anchor, a documented architectural residual (Phase 6).
- Throughput at scale. K-pool sharding is built, so throughput scales by running more pools, but it hasn’t been stress-tested at production volume.
- One upgrade key. A timelocked-upgrade scheme is built but not yet adopted.
- Plaintext DA, no privacy. DA blobs publish
key||balance||nonce. Encrypted DA + amount-hiding is a future-version question, not v1.
None of these are unknowns. They’re scoped, named, and on the roadmap. Details in THREAT-MODEL.md, HARDENING.md, and ROADMAP.md.
8. Run it / review it in 5 minutes
The zero-dependency way to see the whole thing work, with no L1, no prover, no operator needed:
# full off-chain lifecycle: deposit -> transfer -> checkpoint -> reconstruct from DA -> force-exit
cargo run -p liquid-cells-client --example payment_pool
# the contract tests live in their own workspace, so they run separately:
cargo test # 80 STF tests (incl. 13 STF red-team cases)
cd crates/ckb-contract-tests && cargo test # 111 contract tests (incl. 43 contract red-team cases)
And CellTip, the interactive on-chain demo, drives a real pool on Pudge: connect a JoyID wallet (CCC), deposit testnet CKB, tip in sub-1-CKB amounts off-chain, withdraw, with many users sharing one proof. The session key that signs your tips is generated in-browser and never leaves your device.
cargo run -p liquid-cells-demo-ui # then open http://127.0.0.1:8088
Heads-up: a fresh clone won’t run the live CellTip out of the box. The operator deploy/driver scripts live off-repo and the demo-ui needs a generated state fixture. The
payment_poolexample above is the fully self-contained one. (Full repo tour in theREADME.)
Where to point your attention if you’re reviewing:
crates/stf/src/transition.rs: the two invariants, the whole trust corecrates/ckb-contract/: the anchor type script (checkpoint verify, force-exit, force-refund, lock continuity) and the deposit lockcrates/groth16/: the deployedark-bn254BN254 verifiercrates/stf/tests/,crates/ckb-contract-tests/tests/redteam_*.rs: the property + adversarial suitesdocs/ARCHITECTURE.md: the design the sequencer, guest, contract, and client all MUST agree on
9. What we’re asking for
Read it. Run it. Break it. Build on it.
- Find a hole in the trust model or the contract. Especially: the public-input binding, value conservation and solvency, the deposit-lock sweep predicate, the escape-hatch epoch handling, and anchor-capture. If you find a way for an operator to steal, inflate, freeze, or for a checkpoint to verify that shouldn’t, we want to know.
- Report vulnerabilities privately via GitHub’s “Report a vulnerability” (see
SECURITY.md). Testnet-only, safe-harbor for good-faith research. - Design feedback on the Fiber composition, the prior-art positioning, the sequencing model, or the dust-egress residual is very welcome right here in this thread.
- Reuse it. It’s a use-case-neutral pooled-balance core (payments, payroll, escrow, fee-split, swaps). Start with
docs/INTEGRATION-GENERIC.md.
Disclaimers
Liquid Cells is pre-alpha and testnet-only. Nothing here is an offer or a recommendation to deposit funds, and you should not use it with mainnet assets. Licensed under MIT.
Thanks for reading, and thanks in advance to anyone who takes the time to break it. ![]()
大家好,
我们刚刚以 MIT 协议开源了 Liquid Cells,目前已在 Pudge 测试网上线运行。它是一个小而专注的东西:一个池化的、对运营方无需信任的微支付 Rollup,让成千上万的人能在单个锚定 Cell 之后持有并转移低于 61-CKB 的余额,且完全无需信任运营方。
我们在这里发帖只有一个原因:希望社区来读它、运行它、并尝试攻破它。它目前处于 pre-alpha 阶段,仅限测试网。
我们现在想要的,是技术上的审视。
- 代码仓库:GitHub - LusoCryptoLabs/Liquid-Cells: Trust Minimized pooled multi-recipient payment rollup for Nervos CKB · GitHub
- 5 分钟速读:
LIGHTPAPER.md - 复用这些原语:
docs/INTEGRATION-GENERIC.md - Pudge 上每一个实时哈希:
DEPLOY.md
TL;DR
- 缺口: CKB 的 Cell 模型会收取状态租金。每个 Cell 都锁定约 61 CKB。这正是 CKB 永不膨胀的原因,但它也意味着低于约 61 CKB 的余额无法存在于一层(Layer 1)。整个面向消费者的微支付领域(打赏、游戏底注、按次观看的流媒体、创作者收款、微空投)在基础层上被结构性地排除在外。
- 解法: 一个池化账本。余额以 keccak 稀疏 Merkle 树的形式存在于链下,置于单个锚定 Cell 之后。排序器(sequencer)负责对交易排序,但它无力作弊——因为每一次向 L1 提交的检查点都携带一份由链上验证的 zk 有效性证明。它是一个 zk-Rollup(有效性证明 + 链上数据可用性),而非托管式资金池。
- 它是真实的,运行在公链上: 锚定的 Type Script 在 Pudge 上的 CKB-VM 内验证真实的 SP1 → Groth16(BN254)证明,约耗 80M cycles(约为约 3.50B 区块上限的 2.3%)。多次连续的检查点已在链上链接成链,并且一次无签名、无许可的检查点已经推进了一个实时资金池。
- 诉求: 读它、运行它、攻破它。漏洞报告请走私密披露渠道(
SECURITY.md);设计层面的反馈,欢迎直接在本帖讨论。 - 不主张任何原创性。 这是一个实现,而非一项发明。详见下方的现有技术(prior-art)章节。
1. 问题所在:61-CKB 门槛
在 CKB 上,每一次一层状态变更都会消耗并创建 Cell,而一个标准 Cell 需要约 61 CKB 的容量。这是一个刻意为之的良好设计,正是它让 CKB 不存在无上限的状态膨胀。但它带来一个硬性后果:
低于约 61 CKB 的余额,无法成为一层上的一等公民(first-class object)。
你没法把一笔 0.3-CKB 的打赏作为它自己的 Cell 发给别人。一个游戏底注、一笔按次观看的流媒体付款、一次微空投——它们都无法存在于 L1。而且这个缺口在 CKB 上比在大多数链上更尖锐,因为账户模型的链可以让一个极小的余额就那么待着,而 CKB 的 Cell 最低容量做不到。
CKB 现有的各种原语各自解决了问题的一部分,但都无法提供那种池化的、多对多的持有形态:
| 原语 | 它提供什么 | 它无法做到什么 |
|---|---|---|
| L1 直接转账 | 原生、无信任 | 每笔支付需要一个 >=61-CKB 的 Cell;无法微支付 |
| ACP(anyone-can-pay,任何人可支付) | 向一个活跃 Cell 接收任意金额 | 需要一个活跃的 >=61-CKB Cell;不是池化账本 |
| Fiber(支付通道) | 非托管的路由支付 | 点对点的流动性图,而非共享的多接收方账本 |
Liquid Cells 正是缺失的那一块:一个池化的、多接收方的账本,是当前任何 CKB 原语都无法以无信任形式提供的。(关于它为什么与 Fiber 是互补而非竞争关系,详见 §6。)
2. 它是什么:一个链下的池化账本
余额存在于链下的一棵稀疏 Merkle 树中(深度 256,以 keccak(pubkey) 为键,叶子 = {balance, nonce}),由一个链上 Cell 锚定:
链下 链上 (CKB)
参与者 -- 签名意图 --> 排序器 = 状态机
| 构建一个批次,生成新根
v
SP1 证明 + 状态差异 blob
|
v
锚定 Cell(资金池)
Type Script 验证:
- 证明有效 - 差异已发布
- 价值守恒 - 退出被兑现
运营方(“排序器”)对交易排序并推进这棵树,但它无力作弊;而在无许可排序之下,它甚至不具有任何特权:任何持有有效证明的人都可以取而代之。每个用户都能从 L1 数据重建自己的余额,并单方面退出。
有一个我们颇为满意的、符合 CKB 习惯的细节:最重的部分(约 112KB 的验证器代码 Cell)是单个共享的 cell_dep,只需支付一次。新创世一个全新的资金池,只会新增它那 80 字节的锚定 + Lock,而不会再复制一份二进制文件。
3. 核心思想:排序器就是那个 zk guest
状态转换函数只有唯一一个实现,即 liquid-cells-stf 这个 crate。同一份字节被使用了两次:
liquid_cells_stf::apply(state, batch) -> (new_root, PublicValues)
|- 排序器: 原生运行以推进根、提供 API、发布 DA
'- SP1 guest: 对同一次运行进行证明 -> Groth16 -> CKB 锚定验证它
因为两者运行的是完全相同的代码,它们永远不可能在根上产生分歧,所以整类“根分歧”的 bug 在构造上就被消除了。而该证明会证实协议安全性所依赖的两条不变量:
- 没有签名就没有扣款。 一个叶子的余额,只能通过携带该叶子所有者有效 secp256k1 签名的 Transfer/Withdrawal(转账/提现)来减少。
- 价值守恒。
Σ balances + fees == total,且new_total == prev_total + Σ deposits - Σ withdrawals - fee_withdrawal(排序器提取的手续费只来自累积的费用,绝不来自用户余额)。
这两条合在一起,使运营方盗窃和余额增发成为不可能,而不仅仅是可争议的。这里没有任何会出错的欺诈证明博弈,也不存在任何对诚实观察者的假设。
4. 信任模型:五项保证
每一项都已实现,并经过对抗性测试:
| # | 保证 | 它杜绝了什么 |
|---|---|---|
| 1 | 有效性证明:每次根转换都携带一份 SP1 → Groth16 证明,由锚定 Type Script 验证 | 盗窃、余额增发、伪造或过期的结算——在构造上即不可能 |
| 2 | 链上数据可用性:每个检查点都在交易 witness 中发布明文、定宽的状态差异(按手续费计价,而非锁定容量) | 数据扣留;任何用户都能仅凭 L1 重建自己的叶子 + 退出证明 |
| 3 | 强制退出应急通道:在运营方沉默 T 个 epoch 之后,任何人都可针对最后提交的根单方面提现(普通的 SMT 包含证明,无需有效性证明) |
审查与冻结;运营方无法通过下线失联来困住资金 |
| 4 | 原子交换出金:哈希锁定的交换让低于 61-CKB 的粉尘无需自己的 Cell 即可移出(外加针对超时交换的、发送方一侧的链上强制退款) | 粉尘困局;小额余额在任意面额下都能获得无信任的退出路径 |
| 5 | 无许可排序:一个宽松的锚定 Lock + 链上 Lock 连续性,让任何人都能推进根(并附带手续费奖励,使其可自我维持) | 特权运营方;排序是开放的、抗捕获的 |
剩下的唯一信任假设,只有密码学困难性(secp256k1 + keccak + Groth16)以及 CKB 一层本身——这与任何一笔链上交易的基础完全相同。
5. Pudge 上实际运行的部分(已被验证,而非一纸规格)
这是我们最希望被挑战的部分。常见的那个 Rollup 风险——“它到底能不能在链上跑起来?”——在公链上已经被打消了:
| 指标 | 结果 |
|---|---|
| Groth16(BN254)在 CKB-VM 上的验证成本 | 约 80M cycles,约为约 3.50B 区块上限的 2.3% |
| STF + 合约集成测试 | 80 + 111,全部通过(green) |
| 对抗性红队用例(在 CKB-VM + STF 中) | 56 个(已计入上面的 80 + 111 之内),无可利用的攻破 |
| 无许可检查点 | 一次无签名的检查点推进了一个实时资金池,仅由证明 + Lock 连续性把关 |
锚定的 Type Script 在公链的 CKB-VM 内验证真实的 SP1 → Groth16 证明——不是模拟器,不是规格文档——并且连续的检查点已经端到端落地。
关于验证器成本顺带说一句,因为这里肯定会有人问:第一次上链尝试因超出区块 cycle 上限而被拒绝(4 个配对的 substrate-bn 验证成本约 3.81B,而共识上限约为 3.50B)。我们曾短暂上线过一个 3 配对的 e(alpha,beta) 预计算 hack 以压到约 3.47B(仅约 0.7% 的薄弱余量),随后把验证器移植到 ark-bn254:一个朴素的 4 配对检查,约 80M cycles,约 43 倍的下降,彻底去掉了那个 hack。这与来自 ckb-zkp 的 CKB-VM 数据点(约 77M)以及“优化版 SP1 验证器”(Optimized SP1 Verifier)那个帖子的结论一致。
链上证据(Pudge,自行验证)
锚定 Type Script codeHash : 0x83158549bb5b2241997d1e8bb5e24b432a78e74473c8601cea276411bca99f1e
创世锚定 type hash : 0xf4c1baf2fda7e9a07b762387934ce6b2d275bd4e533ae558c7233907f2f6b7a1
烘焙进去的 SP1 程序 vkey : 0x00213af5b4cc8922754e5b8ee7e610de703d4cb17fabb0a187c95c37fe67c1e8
首个检查点 (seq 0->1) : 0xca6bfc76cbfae1b1dfedfbcab02fe84c1ab9019cb711defce65587091c373d51
检查点 2 (seq 1->2) : 0x599085cefc048d76d966d78cf8baa27c0242b6bc999133bcc15070af5d2236a3
检查点 3 (seq 2->3) : 0x80d4612489c59cb630d8dd8c32d5edc3d2ac2009ab8068eee9ae5b879052bec1
ark-bn254 验证器升级 : 0x68f9730a2cd9bfccb5b34e733807c28646c6b74a84980827c7ffcc6df1452767
(验证 3.47B -> 约 80M cycles;二进制 293KB -> 112KB)
无许可资金池, cp2 (seq 1->2, 无签名):
0x3145a2d86ca21c26f32bf14ac2ad86ecb7644c3bf0f21a8cfbff6638c3692767
完整的产物清单(存款 Lock、带 type-id 的资金池,强制退款 + Lock 连续性 + 排序器奖励等升级,等等)见 DEPLOY.md。
6. 诚实的定位(这比演示本身更重要)
这是一个可运行的实现,而非一个新颖的协议。几乎每一个组成要素都有现有技术(prior art),其中很多就在 CKB 上——与其让你自己去发现,我们宁愿坦白说出来:
- sec-bit 的 Clover:一个建立在相同 CKB Cell 模型之上的 zkRollup。最接近的“近亲”。(Clover 的存款/提款,是一次直接的 Lock Script 承诺更新,无需运营方、无需证明,可以说比我们的方案更好——我们目前为了给一笔存款入账,要花掉一个完整的、带证明的检查点。)
- sec-bit 的 ckb-zkp:在 CKB-VM 上的链上 Groth16(约 77M cycles)。在 CKB 上做链上 Groth16,并非首创。
- Intmax2(以太坊):一个池化的、专注支付的 zk-Rollup,采用客户端证明;比我们更具可扩展性、也更具隐私性。
- Godwoken、Fiber,以及本论坛上正在进行的“面向 ckb-vm 的优化版 SP1 验证器”(Optimized SP1 Verifier for ckb-vm)这项工作。
我们的贡献,是在一条真实运行的链上做工程与集成,外加几个符合 CKB 习惯的选择(以锚定 Cell 作为资金池、基于 Lock 连续性的无许可化)。完整的逐项对比见 docs/PRIOR-ART.md。
特别说一下 Fiber:我们认为 Liquid Cells 与它是互补而非竞争。这不是“资金池 vs. 通道”之争。任何让成千上万零散的长尾接收方、无需各自为一条通道注资就能收款的通道设计,都是通过一个中心枢纽(hub)来实现的;而如今已存在的、无需通道的 Fiber 打赏枢纽是托管式的(一个内部账本)。Liquid Cells 本质上就是那个枢纽的非托管版本:运营方既不能盗窃也不能增发,余额可以从链上数据重建,用户还能强制退出。而且 Fiber 正是我们设想的粉尘出金通道:一笔低于 61-CKB 的余额,通过一次原子交换离开资金池,这次交换与一条 Fiber 腿(leg)共享同一个哈希锁。Fiber 服务于这个消费级领域中“支付流转”的那一半;Liquid Cells 服务于“池化持有”的那一半。诚实且可供攻击的对比见 docs/COMPARISON-FIBER.md。
7. 仍在进行中的工作
老练的审阅者见过太多空气项目,因而懂得珍视坦诚——所以这里是一份不那么光鲜的清单:
- 仅限测试网。 在 Pudge 上运行,无真实资金。
- 可信设置。 Groth16 继承了可信设置(trusted setup)假设(通过一个成熟的公开仪式来缓解)。
- 最终性约为每个检查点 7 分钟(软确认回执提供亚秒级的感知确认,但那是活性(liveness),而非最终性)。
- 粉尘出金,Fiber 绑定。 池内的哈希锁定腿(keccak)以及发送方一侧的链上强制退款已经上线,但完整的、与 Fiber 绑定的原子交换仍处于预备阶段——已在 Fiber 测试网上做过实地验证,但尚不能在已部署的 Pudge guest 上使用。Fiber 的 HTLC 使用 SHA-256,而池内 HTLC 使用 keccak;要把它们绑定起来,需要
htlc_sha256这项改动,目前在已部署的 guest 中处于关闭状态,将在审计后的重新烘焙(re-bake)中上线。 - 粉尘出金,接收方一侧。 超时交换的发送方可以在链上强制退款;但对称的接收方“强制领取”(force-claim)在单个锚定之下无法表达,这是一个已记录在案的架构遗留问题(阶段 6)。
- 规模化吞吐。 K 池分片已经构建完成,因此吞吐量可以通过运行更多资金池来扩展,但尚未在生产级别的体量下做过压力测试。
- 单一升级密钥。 一套时间锁升级方案已经构建,但尚未采用。
- 明文 DA,无隐私。 DA blob 发布的是
key||balance||nonce。加密 DA + 金额隐藏,是未来版本的议题,而非 v1。
这些都不是未知数。它们都已被界定范围、被命名,并列入路线图。细节见 THREAT-MODEL.md、HARDENING.md 和 ROADMAP.md。
8. 5 分钟运行 / 审阅它
无需任何依赖即可看到整个流程跑通的方式——不需要 L1、不需要证明器、不需要运营方:
# 完整的链下生命周期:存入 -> 转账 -> 检查点 -> 从 DA 重建 -> 强制退出
cargo run -p liquid-cells-client --example payment_pool
# 合约测试位于它们自己的 workspace 中,因此单独运行:
cargo test # 80 个 STF 测试(含 13 个 STF 红队用例)
cd crates/ckb-contract-tests && cargo test # 111 个合约测试(含 43 个合约红队用例)
而 CellTip——那个交互式的链上演示——在 Pudge 上驱动着一个真实的资金池:连接一个 JoyID 钱包(CCC),存入测试网 CKB,在链下以低于 1-CKB 的金额打赏,再提现,众多用户共享同一份证明。为你的打赏签名的会话密钥是在浏览器内生成的,绝不会离开你的设备。
cargo run -p liquid-cells-demo-ui # 然后打开 http://127.0.0.1:8088
提示:全新克隆下来的仓库,无法开箱即用地运行实时的 CellTip。运营方的部署/驱动脚本在仓库之外,且 demo-ui 需要一个生成好的状态 fixture。上面的
payment_pool示例,才是完全自包含的那个。(完整的仓库导览见README。)
如果你要做审阅,请把注意力放在这些地方:
crates/stf/src/transition.rs:那两条不变量,整个信任核心crates/ckb-contract/:锚定的 Type Script(检查点验证、强制退出、强制退款、Lock 连续性)以及存款 Lockcrates/groth16/:已部署的ark-bn254BN254 验证器crates/stf/tests/、crates/ckb-contract-tests/tests/redteam_*.rs:属性测试 + 对抗性测试套件docs/ARCHITECTURE.md:排序器、guest、合约和客户端都必须一致遵循的设计
9. 我们想要的是什么
读它。运行它。攻破它。在它之上构建。
- 在信任模型或合约中找出漏洞。 尤其是:公共输入绑定(public-input binding)、价值守恒与偿付能力、存款 Lock 的清扫谓词(sweep predicate)、应急通道的 epoch 处理,以及锚定捕获(anchor-capture)。如果你找到一种方法,能让运营方盗窃、增发、冻结,或者让一个本不该通过的检查点通过验证,我们想知道。
- 私密报告漏洞,通过 GitHub 的 “Report a vulnerability”(报告漏洞)功能(见
SECURITY.md)。仅限测试网,为善意研究提供安全港(safe-harbor)。 - 设计反馈——关于 Fiber 的组合方式、现有技术定位、排序模型,或粉尘出金的遗留问题——非常欢迎直接在本帖中提出。
- 复用它。 这是一个与用例无关的池化余额核心(支付、薪资发放、托管、费用分账、交换)。从
docs/INTEGRATION-GENERIC.md开始。
免责声明
Liquid Cells 处于 pre-alpha 阶段,且仅限测试网。这里的任何内容都不构成存入资金的要约或建议,你不应将它用于主网资产。基于 MIT 协议授权。
感谢阅读,也提前感谢每一位愿意花时间来攻破它的人。![]()







