Rand Protocol
文档
指南

程序

机密程序模型——在 zkVM 中证明的 RISC-V guest,一次部署,以私密输入调用——以及构建、部署、调用、invoke 和回执命令,自 chain 20 起的 RPL-2 程序状态,并如实列出目前尚不可能实现的功能。

使用本页将程序部署上链并调用:程序是什么、如何构建、一次调用公开什么又隐藏什么、如何读回回执,以及你会遇到的限制。每个程序都是公开代码;每次调用都是一份证明。示例程序提供完整的应用,randprotocol/example-programs 提供部署在 chain 20 上的程序源码(chain 21 于 2026-10-12 割接,启动时没有任何程序:程序不会跨割接保留,示例程序也尚未在其上重新部署),其中包括 RPL-2 程序(见下文)。

什么是程序

程序是面向 Rand zkVM 的 RV32IM 机器码:{ base_pc: u32, words: Vec<u32> },字数最多不超过该链的程序上限(chain 21 上为 65,535 个字,自 chain 13 起每条链皆然;在没有 max_program_words 的创世配置上为 4,096,如 chain 12),base_pc 为 4 的倍数,每个字都能解码为一条指令(无法解码的字在部署时即被拒绝,绝不会拖到运行时)。与 chain 19 和 chain 20 一样,chain 21 还会拒绝部署任何无法被一次调用容纳的程序:没有公共段时超过 8,180 个字,有公共段时则更少(见下文)。程序按内容寻址且不可变:

program_id = blake3("rand-program" || base_pc || words)       // the id, with no public input
program_id = blake3("rand-program-2", base_pc || u32_le(len(words)) || words || u32_le(len(public)) || public)   // deployed with --public
code_hash  = hc = Poseidon2 digest of (base_pc, len, words)    // what calls are verified against

一次调用是一份 STARK 证明,证明摘要为 hc 的程序在某些私有输入上运行过,消耗的 gas 不超过声明的 gas 上限,并发布了八个输出字。节点验证该证明、记录一份回执,仅此而已:输出是数据,不是副作用。调用不能转移 RAND;由调用者在为该调用付费的交易包中转移价值,程序的输出具有何种含义,由程序与其调用者约定。自 chain 20 起,Invoke 是例外:其证明担保一次声明的状态转换,账本随后应用该转换(程序状态(RPL-2))。

guest 程序可以做什么:

类别指令
高位立即数 / 跳转LUI AUIPC JAL JALR
分支BEQ BNE BLT BGE BLTU BGEU
加载 / 存储LW SW LB LH LBU LHU SB SH
ALUADDI SLTI SLTIU XORI ORI ANDI SLLI SRLI SRAI ADD SUB SLL SLT SLTU XOR SRL SRA OR AND
RV32MMUL MULH MULHU MULHSU DIV DIVU REM REMU
系统ECALL;不含 FENCE、CSR、EBREAK,也不含 A 扩展和 C 扩展

系统调用,按 a7 中的编号:

#系统调用作用
0HALT结束运行
1WRITE_OUTPUT slot word固定公开输出 slot < 8;每个槽位至多写一次,未写入的槽位为零
2READ_INPUT idx返回私有输入字 idx,与加盐承诺 H_IN 绑定
3POSEIDON2 ptr n用 Poseidon2 海绵就地哈希 n ≤ 4096 个字
4KECCAK ptr就地执行一次 Keccak-f[1600] 置换;使证明携带 keccak 表
5SHA256 ptr就地执行一次 SHA-256 压缩;使证明携带 sha256 表
6READ_PUBLIC idx返回公共段的字 idx;自 chain 20 起公共段在部署时固定(rand program deploy --public);在没有 max_program_public_words 的创世配置上它为空,此调用什么也读不到
7POSEIDON2_LEN ptr n在海绵中绑定消息长度的 POSEIDON2,因此 [a] 与 [a, 0] 的哈希不同(约束集 7,chain 16 及之后)。circuits aeacf31 处的 rand-guest check 仍会拒绝它(见下一节)

内存:32 个寄存器,x0 恒为零;RAM 初始为零;所有地址均低于 2³⁰;编译型 guest 拥有 64 KiB 的栈。用 rand-guest 构建的程序在镜像容器中携带其只读数据和已初始化数据,并带有一段属于程序本身的加载前导代码,在程序自身代码运行之前写入这些数据(RISC-V ISA)。票据承诺、废止符和 Merkle 检查不是系统调用,而是建立在 POSEIDON2 之上的库例程。

自 chain 18(约束集 8)起,每次运行都按 gas 计量。普通指令以及系统调用 0、1、2 和 6 各花费 1 gas,对 n 个字的 POSEIDON2 花费 3 + 3 × ⌈n / 4⌉,一次 KECCAK 置换花费 192,一次 SHA256 压缩花费 64。示例程序给出了该表,以及一次调用需要声明的内容。

构建程序

三种方式,均产出 RV32IM 字:

**内置汇编器。**钱包自带五个手工汇编的 guest 程序,并将其汇编为 { "base_pc", "words" } JSON 文件:

rand program build --guest private_payment --arg 1000 --out pp.json   # prints the program id
rand program build --guest balance_check --arg 1000 --out bc.json
# guests: fib, memcpy, bubble_sort, balance_check, private_payment, public_echo, rpl2_counter (v0.6.8); --arg is repeatable

**用 rand-guest 编译的程序。**circuits 仓库中的 rand-guest 可将 no_std Rust crate(riscv32im-unknown-none-elf,Rust 1.98.1)或 C 目录(clang 23.1.1)构建为镜像容器,按机器的解码器检查每个字,并打印 hc 与程序 id。构建是封闭的,因此同一份源码在任何机器上都得到同一个 hc。对于 chain 18 至 chain 20,请在 circuits 提交 aeacf31(分支 feat/v067-circuits)处构建工具链,fullnode v0.6.7 和 v0.6.8 都固定在该提交。在该提交处,rand-guest check 只列出系统调用 0 至 6,因此调用 guest_sdk::poseidon2_len(系统调用 7)的 guest 会在构建时被拒绝,尽管机器能够运行它(zkp-circuits issue #1)。分步说明:部署 Rust 程序与部署 RISC-V 程序。

转译程序。sbpf2rv 转译 Solana sBPF ELF,evm2rv 转译 EVM 运行时字节码,所得程序的输出与 Rand 解释器 guest 的输出逐字相同:Solana 转译器与 Ethereum 转译器。

钱包本身不含编译器。rand program deploy 接受 .json、由小端字组成的原始 .bin,或 rand-guest 写出的镜像容器(fullnode v0.3 及之后)。

部署

rand program deploy pp.json          # a bundle pays the floor; waits for the commit; prints the id
rand program show <id>               # id, base_pc, words_len, code_hash, deployed_at, public_words_len, public_digest

testnet 是 chain 21(自 2026-10-12 起上线),在其上部署、调用或 invoke 需要 fullnode v0.8.0 的 rand 钱包。旧版钱包根本无法在那里发送交易:chain 21 运行约束集 9,它更改了每一个验证器密钥和证明编码,因此 v0.6.8 或 v0.7.x 钱包的证明甚至无法解码;并且,与 chain 20 一样,创世配置设置了 binding_domain: 1,因此每个证明绑定和每条签名消息都包含创世哈希。chain 21 启动时没有任何程序:在 chain 20 上部署的内容不会跨割接保留,示例程序也尚未在其上重新部署。公共端点 https://rpc.randprotocol.org 为钱包的同步、部署和调用提供服务(设置)。

钱包首先打印程序 id、字数和 hc,并通过 rand_estimateFee 向节点询问费用;若程序超过该链的上限,该方法会在任何证明之前就拒绝它。随后,钱包构建一个零价值的自转账交易包(每笔交易都携带的那个 4 入 4 出的隐藏资产交易包;费用从其 RAND 槽位中扣除),用其自身的票据支付 0.001 RAND + 100,000 units × words(公共段的字按代码字计),附上 Action::Deploy { base_pc, words, public },针对该交易的绑定对交易包生成证明并提交。自 chain 17 起这是两份证明,钱包为每份各打印一行:一份 tier-10 花费授权(数秒,约 1.37 MB)和 tier-14 交易包(笔记本上约两分钟,约 1.5 MB)。在 chain 17 之前只有交易包证明,在 chain 15 上约 100 秒、约 1.42 MB。部署费用没有 gas 项或字节项:gas 只为调用定价。节点检查每个字都能解码,计算 hc,存储记录,并在后台开始构建验证器密钥。代码是公开的;谁为它付了费则不公开:rand_getProgram 没有 deployer 字段。对同一组字重复部署是幂等的。

→ {"method":"rand_getProgram","params":["675adeea…0065"]}
← { "id": "675adeea…0065", "base_pc": 0, "words_len": 42, "code_hash": "…", "deployed_at": 10,
    "public_words_len": 0, "public_digest": null }
→ {"method":"rand_getProgramCode","params":["675adeea…0065"]}
← { "base_pc": 0, "words": [ … ] }

调用

rand call <program id> --input 400 --input 250 --input 300 --input 75
rand receipt <tx hash>

钱包从节点获取代码和公共段,并检查它们的哈希与 id 一致。它在模拟器中运行该调用,并在生成任何证明之前打印 gas 和费用:

gas: 5636 (declaring 6144, tier 12)
fee 0.006191437 RAND (incl. two price steps of headroom)

随后它封存输入记录(见下文),在该链的 FRI 配置下以输入作为私有向量在本地对调用生成证明,再对支付费用的交易包生成证明,提交、等待并打印回执。自 chain 20 起,调用证明绑定到其自身的交易(该绑定包含创世哈希),交易包为两份证明,因此一次调用共有三份:调用证明、tier-10 花费授权和 tier-14 交易包。上面两行来自 2026-09-30 在 chain 18 上对匿名投票程序的调用(匿名投票)。

费用。 自链 20 起,费用下限为 BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉。其中 BUNDLE_BASE 为 0.001 RAND,字节数为调用证明与输入信封之和;两项单价起始为 100 和 800 个单位(一个单位为 10⁻⁹ RAND),并随区块充盈度浮动。链 20 和链 21 将它们的上限设为 10,000 和 80,000 个单位,且只有需要支付字节单价的字节(调用的证明与信封)才计入其负载(byte_load: "paying");在链 18 和链 19 上,单价没有上限,每笔交易的字节都会推动字节单价。账本中固定的 0.001 RAND + call_fee(tier) 费率表,即链 15 上的规则,在这些链上不再适用。钱包在证明生成之前就要确定费用,因此按该链的证明上限(链 20 为 4 MiB,链 21 为 6 MiB)计价,并额外留出两个价格步长的余量。在链 21 上,整个费用下限都被销毁(创世配置 fees.burn_floor):提议者只保留发送方在下限之上多付的部分,rand_getSupply.base_fees_burned 统计被销毁的数额(费用)。在链 18 上实测的四次调用,每次花费 0.0057 至 0.0075 RAND(计算过程)。rand fee call <tier> --gas <limit> --bytes <B> 会向节点查询费用下限本身。private_payment 读取四个私有余额,若其总和不小于阈值,则公开盈余部分:输出为 [1, 0, 25, 0, 0, 0, 0, 0]。链上能看到证明、tier 以及这八个字;看不到各项余额。

标志:--tier T 强制指定 tier;--gas-limit <N|max> 声明确切的 gas 限额或该 tier 的上限,以取代默认值,默认值为将确切 gas 向上取整到 2^(tier − 2) 的倍数;--fee <RAND> 设定费用;--expect-public <FILE> 在证明之前拒绝执行,除非程序的公开段正是该文件中的字;--auditor <rand1…> 同时为指定的审计方封存转录;--print-call-key 打印单次调用密钥;--no-envelope 不发布转录;--cuda 选择 GPU 后端(尚未在硬件上测试,且与封存信封不兼容)。链 20 在调用上直接拒绝 --cuda:与其交易绑定的调用证明在 CPU 后端上生成。

回执

每次已提交的调用都会存储一份回执,并在节点同步或验证其链时重新计算:

→ {"method":"rand_getReceipt","params":["b082…09b0"]}
← { "tx": "b082…09b0", "program": "675adeea…0065", "tier": 14,
    "outputs": [1, 0, 25, 0, 0, 0, 0, 0], "height": 105, "index": 0, "h_in": "9c0e…7f", "h_pub": null }

h_in 是证明对私有输入的加盐承诺;单独来看,它不披露任何信息。h_pub 是程序在部署时的公开段的摘要,即程序的 public_digest;对于部署时没有公开段的程序,则为 null。回执中没有 effect 字段,声明的 gas 限额也不在回执中。对于 call,浏览器在 GET /transactions/:hash 上以 receipt 展示同一对象,在 GET /programs/:id 上列出某个程序的调用,并支持 GET /transactions?kind=call&program=<id> 过滤。

输入信封:事后披露一次调用

默认情况下,rand call 会把调用的 (salt, inputs) 转录封存在一个 call-input 信封中发布,这样日后无需第二次证明即可展示输入:

部分字节含义
body12 + 16 + 4·n + 16在全新的单次调用密钥 K_call 下的 salt ‖ inputs
to_sender60包裹给调用方出站查看密钥的 K_call
kem_ct1,088 或 0向审计方的 ML-KEM-768 封装;无审计方时为空
to_auditor60 或 0在该封装的秘密之下包裹的 K_call

body 的关联数据为 H_IN,因此被转移到另一次调用上的信封对任何人都无法打开。链只检查大小(至多为 max_call_envelope_bytes:链 20 和链 21 为 65,536 字节,没有该字段的创世配置为 18,432 字节),并通过 rand_getCallEnvelope(tx) 原样提供。有三种密钥可以打开它,仅此而已:调用方的查看密钥(该钱包发出的每一次调用)、单次调用密钥(仅那一次调用)、审计方的查看密钥(仅那一次调用)。

rand open-call <txhash>                    # as the caller
rand open-call <txhash> --as-auditor       # as the auditor
rand open-call <txhash> --call-key <hex>   # with the per-call key alone

open-call 会打开转录,检查 input_digest(salt, inputs) == H_IN,通过模拟器在恢复出的输入上重新运行程序,并将输出与回执中的输出比较;若任一检查失败,则以非零状态退出,因此脚本不会把警告误当作肯定。--no-envelope 不可逆:盐一旦丢失,任何人(包括调用方)都无法再打开那次调用。

自 v0.6.8 起,重新运行是在证明所基于的公开段上进行的:即程序在部署时的公开字,以及在 hardening_v6 之下紧随其后的八字调用绑定(fullnode issue #116)。在 v0.6.7 中,open-call 只适用于部署时没有公开段的程序;对于有公开段的程序,它会先打印输入,然后以 re-running the program on these inputs failed: PublicIndex(0) 终止,以非零状态退出,两项检查都没有执行。

程序状态(RPL-2)

链 20 是第一条带有 RPL-2 的链,由其创世配置的 program_state 部分开启;链 21 沿用该部分,取值相同。程序因此获得三样此前没有的东西(fullnode docs/program-state.md,v0.6.8 的 crates/randprotocol-core/src/ledger/program_state.rs):

cells每个程序一张从 8 字键到 8 字值的映射。不存在的 cell 读出为八个零;写入八个零即移除它。提交在状态根中
vault每种资产一份公开余额(0 为 RAND)。价值通过一次 invoke 的 bundle 进入,并以链计算出的票据形式流出
专属代币用 rand token create --program <id> 注册的代币(铸造权限为 Program(id),无初始供应量)只能由该程序的 invoke 铸造和销毁

cell 和 vault 余额是公开的,与代币的供应量一样。其中没有任何东西是票据。

Invoke(Action 33)是一种同时声明状态转换的调用,其调用证明表明程序恰好接受该转换:

Invoke     { program, proof, input_envelope, transition }
Transition { reads:  [{ key, value }]     // cells read, with the values read; keys strictly ascending
             writes: [{ key, value }]     // cells written; keys strictly ascending
             inflow: none | deposit | burn
             pays:   [{ asset, amount, recipient, r, envelope }]   // notes out of the vault
             mints:  [{ asset, amount, recipient, r, envelope }] } // new units of the program's own token

流入的是 bundle 所销毁的部分:burn_r 是存入 vault 的 RAND;burn_asset 的 burn_a 是被存入(流入类型 deposit)或被销毁(流入类型 burn,仅限程序自己的代币)的代币。流出的是每笔支付对应的一张由链计算的票据,先支付后铸造,金额与接收方的 pk 为公开,与铸造的情形相同。只有当程序存在、形状合法、reads 中的每个 cell 仍持有所声明的值、vault(计入本交易自身的存入之后)足以覆盖每一笔支付、每一次铸造都是程序自己的代币,并且证明验证通过时,账本才会应用该转换。不再匹配的读取会以 StaleRead 被拒绝;钱包会重新读取并重新证明。

invoke 与 call 的区别。

Call(RPL-1)Invoke(RPL-2)
证明所基于的公开段public ‖ call_bindingpublic ‖ call_binding ‖ context,即以字表示的状态转换
成功时链的动作存储回执;输出是数据存储回执并应用状态转换:写入 cell、移动 vault、追加支付票据
价值仅限 bundle 自身的转账通过 bundle 的销毁存入 vault;支付与铸造从中流出
是否依赖状态否证明不依赖(公开段由交易构造),但读取检查依赖,在准入时和交易池中进行
费用call 费用下限call 费用下限,外加每创建一个 cell 的 cell_fee

程序用 READ_PUBLIC(public_len + 8 + i) 读取上下文字 i。字 0 为版本(1),字 1 至 4 为读取、写入、支付和铸造的数量,随后是 burn_r、流入类型、burn_asset 和 burn_a,再后是每个读取和写入的键与值(各 8 个字),最后是每笔支付的资产与金额。接收方不在上下文中:程序决定金额,由绑定确定谁被支付。程序必须约束其所见到的每一个上下文字,并且不得有任何会 panic 的路径,因为 guest-sdk 的 panic 处理程序会停机,而停机的运行是可证明的运行。

链 21 的限制(rand_getLimits.program_state,与链 20 相同):

限制链 21
cell_fee每创建一个 cell(在原本没有存储的位置写入非零值)10,000,000 个单位,即 0.01 RAND;重写或删除 cell 免费
max_reads8 个 cell
max_writes8 个 cell
max_payouts4 张票据,支付与铸造合计

公开段规则通常更紧:整个公开段必须能放入对同一程序的调用所基于的公开表,对于没有公开输入的程序为 127 个字。这样剩下 119 个上下文字,例如三次读取、三次写入和四笔支付。

命令与 RPC(钱包 v0.6.8 及更高版本):

rand program build --guest rpl2_counter --out counter.json   # one cell, incremented by one per invoke
rand program invoke <id> --transition step.json [--input N | --inputs-file F.json]
rand program state <id> [--cell <64 hex>]                   # rand_getProgramCell / rand_getProgramCells
rand program vault <id>                                      # rand_getProgramVault

program invoke 在证明之前会对照链上实时 cell 检查每一个 reads 条目,遇到过期读取时以退出码 3 退出(可能发生在证明之前,也可能发生在上链确认时),因此脚本需要重新读取并重试。带有 "kind": "invoke" 的 rand_estimateFee 用于报出费用。transition 文件的格式见 fullnode 的 docs/cli.md。部署在 chain 20 上的完整 RPL-2 程序(amm、stableswap、orderbook、stablecoin、lending、perp、crowdfund、stoploss),以及 RPL-1 程序(sealed-auction、ballot、eligibility、private-join、credit-score),均收录于 randprotocol/example-programs。

Chain 21 还承载了 RPL-3,永续期货,它是创世时的 perps 部分,而非已部署的程序:只有一个市场 BTC-PERP,以 zUSD 结算,其撮合引擎是一个固定的 guest(engine_hc 为 f286a611…1573,由交易场所自己的 prover 在链下以 tier 18 证明),其状态拥有独立的创世根。它有六个自己的操作(标签 34 至 39)和四个读取接口:rand_getPerps、rand_getPerpAccount、rand_getPerpAccounts 和 rand_getPerpInputs(参见 Transactions、JSON-RPC)。你部署的程序无法调用它。

哪些是公开的,哪些是隐藏的

公开隐藏
程序 id 及其代码;tier;声明的 gas 上限;八个输出;H_IN;bundle 的费用和公开字段;信封的大小;调用使用的是 Keccak 还是 SHA-256,以及大致的使用频率(证明头部声明了每个哈希表的高度)私有输入;寄存器、内存、分支;真实的周期数和确切的 gas;H_IN 的盐;调用者是谁,以及用什么支付

同一次运行的两个证明,字节并不相同。在 chain 15 及更早的链上,这一点并不足够:私有字数不超过 31 的调用,其证明的表过小而无法隐藏,输入可以从证明中恢复出来。自 v0.6 起,prover 已将这些表的下限设为 128 行;约束集 7(chain 16 及之后)则对仍会泄露的各表总量进行了盲化。hc 具有绑定性,但不具有隐藏性:任何能够枚举候选程序的人,都可以确认部署的是哪一个。

目前尚不可能的事项

以 fullnode 自身的表述为准:

  • 没有跨程序调用,RPL-2 之外也没有状态。 一次 Call 是对一次运行的一个证明;除调用者回传的内容外,调用与调用之间不会携带任何东西。自 chain 20 起,Invoke 可以读写程序自己的 cell 和金库(见上文);chain 20 之前的链都没有程序状态,且 invoke 无法触碰其他程序的 cell。
  • 金库之外没有由程序驱动的转账。 效果类型 1,即由程序向账户支付的转账,已随账户一并删除,Call 的输出只是声明。自 chain 20 起,Invoke 从程序自己的金库中支付,至多四个 note,金额和收款方均为公开。
  • 钱包中没有编译器。 程序要么是内置的已汇编 guest,要么是在电路仓库中用 rand-guest 构建的镜像。
  • 公开输入在部署时固定,而非每次调用时指定。 约束集 6 增加了 READ_PUBLIC 和 H_PUB。自 chain 20 起,一次部署最多可携带 32,768 个公开字,程序 id 绑定这些字,每次调用的证明都会对照它们的摘要进行检验。不存在每次调用单独的公开字。
  • 哈希系统调用会产生很大的证明。 携带 keccak 的证明约为 3.2 MB,直接超过默认 2 MiB 的证明上限;携带 sha256 的证明会再增加约 400 KB。Chain 21 的上限是 6,291,456 字节(6 MiB,在其切换时为 private-multisig 授权证明而提高;chain 17 至 20 为 4 MiB,chain 13 至 16 为 8 MiB),两者都可容纳:在 chain 18 上,带 Keccak 表的 tier-12 调用实测为 3.35 MB,带 SHA-256 表的 tier-14 调用为 1.91 MB(Example programs)。一次调用还限制为 128 次 Keccak-f 置换和 128 次 SHA-256 压缩(见下文),且证明的每一 KiB 都按字节价格计费。
  • EVM 和 Solana 程序无法在 chain 21 上部署,其调用也超出了调用 tier 上限。 解释器 guest(evm.bin,18,009 字;sbpf.bin,8,317 字)和翻译后的镜像(ERC-20,11,686 字;SPL Token,65,096 字)超出默认的 4,096 字上限,但低于 chain 13 设定并被此后每条链保留的 65,535 字上限。两个翻译后的镜像均于 2026-09-19 在 chain 13 上部署,SPL Token 以其 ELF 作为公开输入。Chain 14 和 15 是全新切换的,两者上均未重新部署任何一个镜像。与 chain 19 和 20 一样,chain 21 拒绝部署这四者中的任何一个:根据其 hardening_v6 规则,凡是没有任何 tier-14 调用能够容纳的程序(无公开段时超过 8,180 字,有公开段时更少)在部署时即被拒绝(ProgramUncallable),这样就不会为无人能调用的程序支付部署费。ERC-20 调用在 tier 16 或 18 证明:tier-16 的 approve 于 2026-09-19 在 chain 13 上以 3,412,405 字节的生产证明被调用(Ethereum transpiler),而 chain 21 拒绝任何高于 tier 14 的调用(见下一条),因此目前那里不接纳任何 ERC-20 调用。SPL Token 调用为 tier 20,需要约 330 GB;目前不存在 SPL 调用证明(证明内存)。EVM 区块上下文操作码会触发陷阱,链上也没有 EVM 状态。
  • 调用在 tier 14 或以下证明。 自 fullnode v0.5.6(2026-09-25)起,高于 tier 14 的调用证明会被拒绝(CallTierTooHigh),超过 128 次 Keccak-f 置换或 128 次 SHA-256 压缩的证明同样被拒绝。Chain 13 上 tier-16 的 ERC-20 approve 在 chain 21 上会被拒绝,正如它在 chain 15 和 chain 18 至 20 上本会被拒绝一样,每一个 tier-18 的 ERC-20 调用或 tier-20 的 SPL Token 调用也是如此;这些仍可在链下证明(预算)。
  • GPU 证明从未在硬件上运行过。 --cuda 需要 --features cuda 构建和已提交的 PTX;在 chain 20 切换时(2026-10-01),集群上两者都不存在。在集群上,调用证明由 CPU 后端生成;自 v0.7.1 起,--features cuda 构建在检测到 GPU 时默认选用 GPU,--cpu 则强制使用 CPU。
  • hc 不具有隐藏性,如上所述,而且见证请求仍会告诉节点钱包花费的是哪一片叶子。

最近一次核对所依据的 fullnode 提交为 c0878c3f(链 21),日期为 2026-10-12。 若代码与本页不一致,以代码为准;请告知我们。