ZK Color Sort 通过零知识证明在链上验证每日谜题成绩

一款带密码学记分牌的每日谜题
ZK Color Sort 看起来就像你在等咖啡时会在手机上玩的那种倒色拼图:12 根试管、10 种颜色,不断倾倒,直到每根试管只剩一种颜色。它的特别之处在于:游戏结束时,页面会向你索要一样东西。在 'Scores' 按钮和步数计数器旁边,写着'连接你的 Algorand 钱包以解锁此功能'。这并非付费墙。连接一个受支持的钱包——目前是 Lute 或 Pera——才是通向游戏真正核心的入口:把成绩连同零知识证明一起提交给 Algorand 主网上的智能合约。零知识证明是一种密码学凭证,能在不透露背后数据的情况下证明某个论断为真。在这里,论断是'我用 N 步解开了这个特定谜题',而被保密的数据则是走法序列本身。玩谜题完全不需要钱包;真正属于 链上的,是以证明为门槛的记分牌。
当天的谜题本身就取自链上。前端向 Algorand 索引器请求 UTC 午夜之后的第一个区块头,再从该区块头的种子确定性地推导出棋盘。因此谜题是公开的,任何人都能依据链上历史复现,而不是由开发者指定。游戏规则遵循标准 Color Sort 格式,在项目 README 中有精确定义:12 根试管,容量为 4;10 种颜色,每种恰好出现 4 次;2 根空试管用于倾倒;一次合法倾倒会把同一种颜色的最大连续段倒入空试管或顶部颜色匹配的试管;当所有非空试管都完全单色时,谜题即告解开。成绩即步数,越少越好;本地最佳成绩连同完整的走法历史保存在浏览器 localStorage 中。链上注册表绝不接受未能刷新玩家本人历史最佳的成绩。
零知识证明究竟证明了什么
任何技能类游戏的链上排行榜都面临可信度难题:一个接受数字的合约,任何数字都会照单全收。由可信服务器来校验游戏过程,等于重新引入中心化;而把获胜解法公之于众,则会把它背后的策略一并泄露。ZK Color Sort 尝试了第三条路——证明它,而非展示它。
玩家提交时,浏览器通过开源 JavaScript 证明库 snarkjs 运行 Circom 电路,生成 Groth16 证明——这是应用最广泛的零知识方案之一。首次提交需要下载游戏以静态资源形式分发的约 55 MB 证明密钥;此后整条流水线全部在客户端运行,游戏背后没有任何证明服务器。这套技术栈的选择是刻意的:snarkjs-algorand 库的 README 指出,另一款验证器 AlgoPlonk 背后的编译器 gnark 不支持 WebAssembly,因而无法在浏览器内生成证明——snarkjs 基于 TypeScript,能在玩家所在的任何地方运行。
该电路针对这款游戏的具体参数量身定制——12 根试管、容量 4、最多 120 步、10 种颜色、2 根空试管——并强制完整的游戏语义:合法的初始棋盘、符合上述倾倒规则的合法走法、每一步之后正确的状态转换、完全解出的最终棋盘,以及与实际操作步数相等的计步。整个设计的全部意义,就在于什么公开、什么保密:
| 概念 | 实际含义 |
|---|---|
| 公开:起始棋盘 | 任何人都能确认某条成绩对应哪一局谜题 |
| 公开:步数 | 注册表存储并被排行榜排名的那个数字 |
| 公开:谜题标识与钱包地址 | 与证明绑定,为某个谜题或某个账户铸造的证明无法被挪用于其他谜题或账户 |
| 保密:完整走法序列 | 观察者可以验证论断,却无法照抄获胜策略——而策略泄露通常是链上谜题排行榜的死因 |
链上成绩注册表
接收端是一个名为 PuzzleScores 的智能合约,使用 Algorand TypeScript 编写——这是一种经 Puya 编译的现代合约语言,已取代更早的 PyTeal。每个(谜题,钱包)组合在应用的 box 存储中各存一条成绩——box 存储是附着在合约本身的键值状态。键由 20 字节的谜题代码与 32 字节的钱包地址拼接而成(共 52 字节);值则只有一个字节,因此存储的成绩上限为 255 步,远超解开这个谜题实际所需的步数。创建一条记录的费用恰好等于 box 的最低余额要求——合约在运行时动态计算(基础 2,500 microALGO,每字节再加 400)——若玩家通过 removeScore 删除记录,这笔费用会退还。只读方法允许玩家查询自己的成绩,也可以查询任何其他人的成绩。
写入路径有 2 条:addScore 用于首次登记;updateScore 只有在新成绩严格低于已存成绩时才会被合约接受。两条路径都要求在同一原子组中附带一笔'验证者'交易,证明正是在这里被实际检查。前端会组合 3 笔交易:一笔由逻辑签名账户签名的零值支付——该账户的权限来自程序而非私钥,由游戏的验证密钥派生——加上一笔最低余额支付,以及一次应用调用。合约会验证这笔证明支付是否来自配置好的验证者地址,并验证证明的公开信号是否与所声称的成绩、谜题代码和发送方一致;Groth16 的验证运算本身在逻辑签名程序内部完成,只有在该程序认可见证数据时,才会产生这笔证明支付。由于玩家的成绩、谜题和钱包都被绑定在同一个证明中,为某个谜题或某个账户生成的提交,无法被转用于其他谜题或账户。在前端,同一个 box 存储还支撑着一个百分位视图:连接钱包后,游戏会扫描当天谜题的链上记录,告诉玩家一共超过了多少人。
已在主网运行——但谁在玩?
这个合约真实存在、已部署且仍在运行:应用 3603459425 于 2026 年 6 月 16 日在第 62,209,315 轮被创建,创建者是项目背后的账户(该账户拥有 .algo 名称 tools.orange.algo),验证者在几分钟后完成配置。它目前存有 94 个成绩 box;账本显示,最近的应用调用发生在第 64,130,520 轮——即 2026 年 8 月 16 日,比本文写作早 2 天。网页打包产物确认前端默认网络为主网,与仓库网络配置中的应用 ID 一致。
不过,发送方账本揭示的采用情况要平淡得多。开发者自己的钱包在交易日志和 box 键中都占据主导;另一个提交成绩的钱包在发布当天创建,由开发者注入 1 ALGO——这是一个测试账户。另外 2 个钱包的首次入金可追溯到创建于 2022 年和 2024 年的无关账户,这与外部玩家的特征相符,尽管两者都没有 .algo 名称。诚实的解读是:这是一个仍在运行但规模很小的注册表,大多数记录可归因于开发者自己的测试,目前尚无证据表明已形成可观的玩家群体。
该项目本身出自一位开发者之手——GitHub 账户 funk-af,其主页显示所有者名为'Andrew'——同一账户还发布了其他 Algorand 合约作品,包括被描述为 Baanx 智能合约、以及 Immersve flexi-card 充值协议智能合约的仓库。游戏仓库显示 2026 年 6 月共有 18 次提交,6 月 18 日之后再无活动;而已部署的前端和链上合约仍在持续运行。
信任模型的局限
有三点告诫值得留意,前提是你并不只想把这个注册表当作一款游戏。第一,每日谜题的推导只是前端约定,并非合约规则:合约从不检查提交的谜题代码是否对应当天由区块派生的谜题,电路也只验证谜题是否结构合法、是否已解开。来自同一电路参数、针对任何结构合法谜题的证明都会被接受;链上没有任何机制把提交锚定到当前日期。第二,验证者地址和合约本身都由创建者控制——setVerifier 和 updateApplication 仅限创建者钱包调用——因此注册表的完整性最终取决于一点:这位开发者不会偷换验证者,也不会重写合约。第三,证明技术栈未经审计:snarkjs-algorand 的 README 警告称,该 SDK'仍在开发中,尚未稳定',并写道'此仓库中的代码未经审计。使用风险自负!'
所有这些都不减损此处真正值得一提的东西:这是一次端到端可运行的技术示范——在浏览器端生成 Groth16 证明,送入 Algorand 主网上的链上验证器,而每日谜题的种子正来自链自身的区块头。倘若这一模式走向成熟——验证器经过审计、谜题锚定由合约强制、并出现真正的玩家——那么经 ZK 验证的技能类游戏,将成为一个拥有可信根基的细分领域。就目前而言,这是一场诚实的小型实验,而且它确实行得通。
来源
- ZK Color Sort
- funk-af/zk-colorsort
- snarkjs-algorand
- snarkjs
- Algorand
- https://mainnet-idx.algonode.cloud/v2/transactions?application-id=3603459425&limit=20
- raw.githubusercontent.com
- https://mainnet-idx.algonode.cloud/v2/transactions?application-id=3603459425&min-round=63000000&limit=50
- https://mainnet-idx.algonode.cloud/v2/transactions?application-id=3603459425&min-round=64000000&limit=5
- https://mainnet-idx.algonode.cloud/v2/transactions?application-id=3603459425&min-round=64000000&limit=50
- https://mainnet-idx.algonode.cloud/v2/applications/3603459425/boxes?limit=5