Rand Protocol
文档
指南

运行节点

构建二进制文件,创建密钥,根据 chain 21 创世区块初始化数据目录,连接引导节点,绑定 RPC,裁剪历史数据,并验证链——可作为观察者或 validator 运行。

使用本页在 testnet 上运行你自己的 rand-node,这也是私密使用钱包的推荐方式:你自己运行的节点,不会向你之外的任何人泄露你即将花费哪一个叶子。观察者节点负责同步、重新验证每一个证明并提供 JSON-RPC 服务;验证者节点做同样的事,并在其密钥进入某一纪元的验证者集合后参与共识。

构建

要求:Rust 1.98.1(在 rust-toolchain.toml 中固定;rustup 会安装它)、C++ 编译器,以及用于 RocksDB 的 cmake。zkVM crate 对证明系统仓库(randprotocol/zkp-circuits)的同级 circuits/ 检出目录存在路径依赖,该目录必须与 fullnode/ 并列,且位于该发布版本所引入(vendored)的提交:即 crates/randprotocol-zkvm/guests-compiled/PROVENANCE.md 中的 circuits: 行(v0.6.8 为 aeacf31,v0.6.7 同)。首次构建需要 10 到 20 分钟。

git clone https://github.com/randprotocol/fullnode
cargo build --release          # target/release/rand-node, target/release/rand, target/release/rand-prover

运行 v0.8.0(git checkout v0.8.0,提交 c0878c3f,是集群自 2026-10-12 起运行的发布版本;rand_getVersion 会报告节点的 sha)。其 GitHub 发布版本附带 rand-node、rand、rand-prover、msauth-vk-production.bin、SHA256SUMS 和 SHA256SUMS.sig,由发布工作流从该 tag 构建,并由发布密钥签名(首批签名发布为 v0.7.0,位于 chain 20)。Chain 21 需要它:v0.8.0 运行约束集 9(rVM 第 4 阶段的 Merkle 区间与 MODQ 芯片),在其下每个证明的编码和每个验证密钥都已改变,因此该构建在启动时拒绝 Chain 18 至 20,此前的任何构建都无法解码 Chain 21 的证明。每个 bundle 仍在其 bundle 证明旁携带一个授权证明(拆分授权,自 Chain 17 起),创世文件固定了与 Chain 20 相同的两个 guest 摘要,即 bundle guest v3(hc_bundle 60af094a…)和 auth guest(hc_auth 1e4e347f…),外加第三个,即私有多签 auth guest(hc_auth_multisig 9705f8e2…),其固定的验证密钥 msauth-vk-production.bin(23,754 字节,SHA-256 68facb06…cd30)必须与 rand-node 放在一起:run 和 verify 在启动时加载它,缺少则拒绝启动。创世文件还包含 gas 部分、hardening_v6、envelope_bytes,以及新的 fees、perps 和 multisig 部分。每个节点都运行集群的构建。

324a4ab1029d39e67fc2de36b036341b0f88f13089997d7fe24c596363a58278  rand-node
e3a38e74ee28743a960fc1c3eb1ac6c538e18dc259411e10afff7f54e268bbf4  rand
da9df98aa4f9f1748a0be82934e12f0213e41b798daf0963a3023ff39e27c475  rand-prover
68facb06c7f321aaf1d4d541acf6b5c0e9d7f17788ed1c9add6c5a8a8f7acd30  msauth-vk-production.bin

Chain 21:全网停机的硬分叉

Chain 21 于 2026-10-12 上线:创世文件在 10:22 UTC 切出,全部 26 个验证者在 10:31 UTC 切换,全部停止、全部启动,每个节点都以 v0.8.0 在 Chain 21 上启动。它由 chain 20 切出:桥的状态来自 chain 20 在高度 814,702(2026-10-12 07:41 UTC,桥先停止)的快照,运营者钱包的余额来自 chain 20 仍在运行时对其所做的扫描。这不是滚动更新:Chain 21 是新的创世,因此与 chain 20 没有任何共享区块,而且 v0.8.0 的约束集 9 完全无法验证 chain 20 的证明。全部 26 个验证者及其密钥、peer id 和引导节点均已沿用;法定人数仍为 26 个中的 18 个。Chain 15 至 20 已退役。Chain 20 本身(2026-10-01,v0.6.8,是 chain 19 在高度 7,258 处的硬分叉)带来了创世绑定、RPL-2 以及审计 v6 的 CON-4 和 CH-1;v0.7.0 和 v0.7.1 随后逐节点滚动升级到其上。

因此,将节点从 chain 20 迁移到 Chain 21,需要新的二进制文件、与之并列的验证密钥、新的创世文件和全新的数据目录,如下所示。钱包需要 v0.8.0 才能在 Chain 21 上发送交易:旧版钱包的证明属于约束集 8,Chain 21 无法解码;而且自 chain 20 起,每个证明和每条签名消息无论如何都已绑定到创世哈希(binding_domain: 1)。

密钥

rand-node keygen --out node.key.json      # prints the address
rand-node address --key node.key.json     # address, public key, libp2p peer id

该文件为 { "seed", "address", "public_key" },权限为 0600;只有 seed 是机密。观察者节点同样需要一个密钥——它是 p2p 身份。节点密钥不是钱包密钥:钱包请使用 rand keygen。

请自行生成密钥,并将其保存在检出目录之外。fullnode 仓库不再跟踪任何密钥(审计 v3,OPS-1):为 chain 8 至 13 提交的 deploy/node-*.key.json 和 deploy/payout/*.key.json 文件,其种子已被公开,绝不可使用。Chain 21 的验证者密钥沿用自 chain 20,均在仓库之外生成,从未进入过仓库:其 26 个创世验证者是为 chain 14 生成的十八个密钥,加上在 chain 15 上质押的八个,历经每一次切换沿用至今。

自 v0.6.8 起,rand-node keygen 拒绝覆盖已存在的 --out 文件;v0.6.7 会不加询问地覆盖它。该文件是种子的唯一副本。在任何质押依赖它之前,请先备份。

从创世文件初始化

同一条链上的每个节点都需要字节完全相同的创世文件;rand-node init 会将其复制到数据目录并打印其哈希,对于 Chain 21,该哈希必须为 ca76c2766ed6090ac6ba5dad86b724dd5d2b80c0edfaed287cd8b129e663ca9a。该文件是 deploy/genesis-chain21.json(sha256 88d42b38…8ca1)。它与切换记录(deploy/cut-records/chain21.record,提交 246cb5f9)一起,在打 tag 之后提交到 fullnode 的 main,因此 v0.8.0 的检出中没有它:请从 main 获取。对于包含 hc_auth_multisig 的链,init 同样需要验证密钥与二进制文件并列放置。

git -C fullnode show origin/main:deploy/genesis-chain20.json > genesis-chain20.json
rand-node init --datadir ./data-6210cf07 --genesis genesis-chain20.json

这会创建 <datadir>/db(RocksDB),其中包含区块 0、作为树的最初几片叶子的九个创世票据(七个 RAND 票据,承载来自 chain 19 的运营者钱包共 5,184.243719258 RAND;两个 zUSD 票据,分别为 10 和 89 zUSD,即锁定在桥托管中的 99 zUSD),以及验证者登记表。使用同一创世文件重复运行是空操作;不同的创世文件会被拒绝(already initialized with a different genesis 表示该数据目录属于另一条链)。请像集群那样,以创世哈希的前八个字符命名数据目录(data-<name>-6210cf07),这样它就不会与 chain 19 的(-a3defc93)冲突。

创世文件的结构:

{
  "chain_id": 20,
  "timestamp_ms": 1790835251661,
  "validators": [ { "public_key": "<hex>", "stake": 1000000000000, "payout": "rand1…" } ],
  "alloc": [ { "cm": "<64 hex>", "envelope": { "kem_ct": "…", "to_receiver": "…", "to_sender": "…", "body": "…" }, "amount": 1000000000000,
               "opening": { "pk": "<64 hex>", "time": 0, "r": "<64 hex>" } },
             { "cm": "…", "envelope": { … }, "amount": 1000000000,
               "opening": { "pk": "<64 hex>", "time": 0, "r": "<64 hex>", "asset": 1 } } ],
  "faucet": true,
  "confidential": true,
  "fri_profile": "production",
  "hc_bundle": "60af094acfe65d85fdb18fb3d06cf9085dcf28c96e59e87f1ee527226e6e3fce",
  "bridge": { "emitter": "<64 hex>", "guardians": [ "<40 hex>" ], "emitters": { "2": "<64 hex>", "3": "…", "4": "…", "5": "…" },
              "pq_guardians": [ "<hex>" ], "pause_key": "<hex>",
              "rules_v2": { "global_mint_cap_per_window": 400000000000, "cap_window_secs": 86400 },
              "guardian_set_index": 1, "burn_sequence": 13,
              "min_inbound_sequence": { "2": 3, "3": 3, "4": 3, "5": 7 },
              "rotation": { "delay_secs": 86400, "needs_possession": true },
              "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" } },
  "tokens": { "registration_fee": 1000000000, "mint_cap_per_day": 10000000000000,
              "tokens": [ { "name": "Shielded USD", "symbol": "zUSD", "salt": "<64 hex>",
                            "backings": [ { "chain": 2, "token": "<64 hex>", "decimals": 6 }, … ] } ],
              "max_tokens": 4096, "burn_registration_fee": true, "bound_note_value": true },
  "consensus_domain": 1,
  "staking": { "faucet_budget_per_epoch": "10000000000000", "bond_activation_epochs": 2,
               "max_weight_bps": 3333, "registration_v2": true,
               "faucet_recipients": [ "rand1…" ], "faucet_minters": [ "<validator address>" ],
               "max_stake_entry_bps_per_epoch": 2500, "admission_by_vote": true },
  "epoch_blocks": 1000,
  "max_program_words": 65535,
  "max_proof_bytes": 4194304,
  "max_block_bytes": 20971520,
  "max_call_envelope_bytes": 65536,
  "max_program_public_words": 32768,
  "envelope_bytes": 1860,
  "hardening_v6": true,
  "hc_auth": "1e4e347f44cf86750b30a9a4bdf9ec9256efe353d4ff8017451eca7d195639c1",
  "gas": { "gas_price": "100", "byte_price": "800", "bundle_gas_limit": 20479, "metering": "circuit",
           "dynamic": { "target_block_bytes": 10485760, "target_block_gas": 262144,
                        "adjust_bps": 1250, "min_gas_price": "100", "min_byte_price": "800",
                        "max_gas_price": "10000", "max_byte_price": "80000", "byte_load": "paying" } },
  "testnet": true,
  "binding_domain": 1,
  "proof_window_blocks": 1024,
  "program_state": { "cell_fee": 10000000 }
}

每个字段都计入创世哈希。hc_bundle 固定了每个 bundle 证明所对照检查的 bundle guest,hc_auth 固定了每个授权证明所对照检查的 auth guest;若某节点的构建组装出不同的 guest,它会拒绝启动并给出两个摘要。五个 max_* 字段为可选,仅在存在时才并入哈希;Chain 21 设置了全部五个。max_proof_bytes 在 Chain 21 上为 6 MiB(chain 17 至 20 为 4 MiB,chain 13 至 16 为 8 MiB):自 chain 17 起,一笔交易可携带三个证明(bundle、授权、调用),带有 hc_auth 的创世文件其 max_block_bytes 必须至少为 max_proof_bytes 的三倍加 1 MiB,而 Chain 21 的第 18 档多签授权证明需要更宽的上限——因此 max_block_bytes 为 19 MiB(chain 20 为 20 MiB),字节目标为 9,961,472。每个 alloc 票据都带有其 opening(pk、time、r,代币票据另有 asset),因此创世票据的所有者和金额按设计是公开的。Chain 21 的内容如下:

  • 一个 bridge 部分,承自 chain 20 在高度 814,702 处的快照:索引 1 处的守护者集合 1,含八名守护者;源链 2、3、4 和 5(chain 19 的)的发射方;八个 Dilithium2 pq_guardians;一个 pause_key;burn_sequence 为 14;源链 2 和 4 的 min_inbound_sequence 为 3,源链 3 为 5,源链 5 为 7;以及 rules_v2(每 24 h 窗口全局铸造上限为 4,000 zUSD)。自 chain 20 起新增:rotation,在其规则下,PQ 守护者或暂停密钥的轮换须由新密钥提供持有证明,并在提交后 86,400 s 生效;以及 fees,在其规则下,链对每笔存入和每笔销毁各收取 10 bps,并以 zUSD 票据形式支付给一个金库地址,因此存入记入的是总额减去手续费,销毁释放的是其金额减去手续费;
  • 一个 tokens 部分:注册费为 1 RAND,予以销毁而不支付给提议者;每种抵押的 mint_cap_per_day 为 100,000 个代币,精度为八位小数;注册表上限为 4,096 个代币;票据价值的 2^63 上界;以及在创世时列入的 zUSD 及其七种抵押,对应两张创世 zUSD 票据,锁定 99 zUSD(Ethereum USDT 22、BSC USDT 22、Tron USDT 30、Solana USDT 25);
  • consensus_domain: 1:每个共识签名都绑定到此创世(共识);
  • 一个 staking 部分:水龙头的允许名单为 16 个钱包和 18 个铸造验证者密钥,每个纪元的预算为 10,000 RAND,质押保证金延迟两个纪元,权重上限为 3,333 bps,并采用 v2 注册,与 chain 19 和 chain 20 相同;自 chain 20 起新增 admission_by_vote(新的验证者密钥只有在验证者集合投票通过后才能注册)和每个纪元边界为活跃权重 2,500 bps 的准入预算,而 chain 19 为固定的 10,000 RAND(质押)。没有 slashing;
  • 一个 gas 部分:一次调用按其证明所声明的 gas 上限以及其字节数付费,价格起始为每 gas 100 单位和每 KiB 800 单位,并在每个区块之后根据该区块的充盈度与目标 9,961,472 字节(chain 20 为 10,485,760)和 262,144 gas 的对比而变动,每个区块最多变动 1,250 bps,永不低于起始价格,且自 chain 20 起永不高于 10,000 和 80,000。只有需要支付字节价格的字节,即调用的证明和输入信封,才计入字节价格的负载(byte_load: "paying")。每个捆绑包证明都声明相同的上限 20,479(费用);
  • envelope_bytes: 1860:每个票据信封恰好为 1,860 字节,并带有一个加密的备注字段;
  • hardening_v6: true,即 v0.6 加固开关,自 chain 16 起每条链都已设置;
  • testnet: true,该标记使水龙头可以与 bridge 部分并存;
  • binding_domain: 1:每笔交易的证明和每条已签名的操作消息都包含创世哈希,因此为一条链证明或签名的内容无法在另一条链上通过验证;
  • proof_window_blocks: 1024:捆绑包的锚点可以是最近 1,024 个区块末根之一,其 time 最多可落后高度 1,024 个区块(chain 19 为 256),这给缓慢的或受委托的证明者留出约二十分钟;
  • program_state,即 RPL-2:程序单元、程序金库和 Invoke 操作,每次调用创建的每个单元收取 0.1 RAND 的单元费用(限制见 JSON-RPC)。

它没有 aggregation 部分,并且构建在遇到带有该部分的创世时会拒绝启动。

timestamp_ms 是链的时钟零点。当你自行生成创世时,请将其设置得接近实际启动时间,且绝不要晚于启动时间。若过于久远,区块时间会落后于挂钟时间,直到每个区块 60 s 的步进上限使其追上。若比当前时间晚超过 15 秒,则区块 1 根本无法提交:每一张投票,包括领导者的投票,都会被扣留,直到挂钟时间到达创世时间戳。请对照启动节点集群的机器的时钟来核对,而不是生成该文件的机器。

运行

rand-node run --datadir ./data --key node.key.json \
    --listen /ip4/0.0.0.0/tcp/30303 --rpc 127.0.0.1:8545 --no-mdns \
    --bootstrap /ip4/164.90.239.200/tcp/30303/p2p/12D3KooWEQEbUwZRgqhDPXhW7vNBmcRcnhUDGUADe81VFdv3ALYe \
    --bootstrap /ip4/165.245.173.74/tcp/30303/p2p/12D3KooWJyc5oDHggAr89e9QrRFBy9Tg8TxXh18yzrb1SM6KKLYw \
    --bootstrap /ip4/188.166.235.187/tcp/30303/p2p/12D3KooWSyX84P1DAbWByLHpRBMLRWD53r3Jj4aqVt6tHoF8B2ZQ
参数默认值含义
--datadir必需已初始化的数据目录
--key必需密钥文件:验证者身份、p2p 身份、手续费接收方
--listen/ip4/0.0.0.0/tcp/30303libp2p 监听 multiaddr,可重复
--bootstrap无启动时以及断开连接期间每 30 s 拨号的对等节点,可重复
--rpc127.0.0.1:8545运维者的 JSON-RPC 和 WebSocket 监听器:包含全部方法,并信任回环地址。请将其保持在回环地址上,切勿将公共端点转发到它
--public-rpc <ADDR>关闭v0.6.8:第二个公共监听器,供反向代理或 SSH 转发指向:一组固定的方法集,不含查看密钥方法、rand_mint 或 rand_getPeers,不支持批量请求,不支持 WebSocket,所有调用方共用一个计量器(JSON-RPC)
--rpc-viewing-token-file <PATH>关闭v0.6.8:要求 --rpc 的查看密钥方法提供一个 bearer 令牌(文件的第一行,32 个或更多字符)
--reserved-peer <PEER_ID>无v0.6.8,可重复:越过入站连接上限而被准入的对等节点;参见保留对等节点
--strict-gossip关闭v0.6.8:只接受带有有效作者签名、序列号和来源的 gossip;逐节点滚动生效
--validator关闭此节点持有验证者密钥,并在其密钥属于当前集合时参与共识
--no-mdns关闭禁用局域网发现;建议在服务器上使用
--block-interval-ms1000提议之间的最小间隔
--view-timeout-ms3000基础视图超时,每连续超时一次翻倍,最多至 8×
--verify-chainquick启动完整性检查:off、quick(结构和账本重放)、full(另含每个签名和 QC)
--keep-raw-proofs关闭区块聚合的归档模式;对 chain 21 无效
--verify-chain <MODE>quickv0.8.0:若同一个干净构建已将存储验证至其链头(启动检查点),则跳过启动检查;replay 强制遍历,off 跳过
--apply-proposals-inline关闭v0.8.0:像以前一样在共识循环上应用收到的提议;默认情况下,跟随者在阻塞线程上应用它们
--msauth-vk <PATH>二进制文件旁的 msauth-vk-<profile>.bin($RAND_MSAUTH_VK)v0.8.0:固定的多签验证者密钥,在指明 hc_auth_multisig 的链上为必需,chain 21 即如此
--prune-history关闭(保留全部)保留账本以及仅这么多的区块历史:<n>m、<n>h 或 <n>d,至少 1h。testnet 的验证者运行 24h,两个归档节点除外;参见历史修剪
--min-free-disk-mb1024当数据目录所在文件系统的可用空间少于此值时拒绝启动;在低于其四倍时,rand_getHealth 回答 disk_low。0 禁用该保护
--prover <ADDR>关闭在此地址上托管一个受委托的证明者,这是其自身的监听器,绝不是 RPC;参见下文
--gas-price, --byte-price100, 800在没有 gas 部分的链上,交易池对调用的价格下限。chain 21 的创世与 chain 20 的一样,设置了自己的价格,因此节点在那里忽略这两个标志并记录一条警告

--prover-accept-spend-key 已被废弃:v0.6.8 节点在收到它时拒绝启动,因此请将其从任何由 v0.6.7 沿用而来的 unit 文件中删除。

上面的三个引导节点是验证者 C、D 和 E,自 chain 19 起保持不变。chain 21 运行在为 chain 14 生成的验证者密钥上,因此它们的 peer id 就是 chain 14 所用的那些;p2p 身份由验证者密钥派生。大多数验证者只保留一天的区块,因此从创世开始同步的节点,或落后超过一天的节点,还需要一个全历史对等节点。请添加两个归档节点之一:

    --bootstrap /ip4/168.144.46.203/tcp/30303/p2p/12D3KooWS5usdMPvsbaWSaT2xtKndHvDYurwmueoNvaRRarb2wRf    # obs1
    --bootstrap /ip4/206.81.29.236/tcp/30303/p2p/12D3KooWPocyZGcNB9Vp68VSfgVvNXhSzBBoJa4h9R1yMkp71XHh    # rand-archive-2

省略 --validator 即以观察者节点运行。启动过程:打开存储,对照创世的 hc_bundle 和 hc_auth 检查此构建的 guest,运行完整性检查(截断受损的尾部并保留投票安全状态),从持久化的链头恢复共识,启动网络和 RPC,然后从任何领先的对等节点同步。RUST_LOG 默认为 info,libp2p=warn,libp2p_mdns=off;Ctrl-C 或 SIGTERM(systemd 的停止信号)会使其正常关闭。从同一个 --datadir 重启会从持久化的链头恢复。

来自 fullnode 的拓扑规则:位于 NAT 之后的节点向具有公网地址的节点拨号;在公共节点上开放入站 TCP 30303;除非有防火墙保护,否则请将 RPC 绑定到 127.0.0.1——任何能够访问它的人都可以向它提交交易,并且一旦向其中导入了查看密钥,就应将该端口视为承载密钥的端口。请将 --block-interval-ms 保持在 1000 或更慢:一个捆绑包证明约需两分钟,其锚点在 chain 20 上有效期为 1,024 个区块(chain 19 为 256)。

验证者需要同步的时钟。 自 chain 14 起,墙钟时间成为共识的一部分(bridge hardening B2)。时间戳比其父块晚超过 60 s 的区块无效,而验证者会拒绝对时间戳比自身时钟超前超过 15 s 的区块投票。因此,时钟偏慢的验证者会停止投票且不记录任何故障,而在 chain 21 上,二十六个验证者中必须有十八个投票才能提交。请在每个验证者上运行 NTP,并在启动节点之前检查:

timedatectl                     # want "System clock synchronized: yes" and "NTP service: active"
chronyc tracking
apt-get install -y chrony && systemctl enable --now chrony    # if it is not synchronised

保留对等节点

自 v0.6.8 起,节点最多接受 256 个已建立的入站连接和 64 个半开入站连接,而生成一个 libp2p 身份不需要任何成本,因此陌生节点可能占满所有名额。三类对等节点被保留,可超出上限被接纳,并从验证者的同步预算份额中获得服务:每个 --bootstrap 对等节点、每个 --reserved-peer,以及从签名的对等节点绑定中获知的每个验证者身份。每个 --validator 节点用其验证者密钥对自己的 libp2p 身份签名并广播;从验证者集合中知道该密钥的节点会保留该对等节点,并在重启后保留该记录。新切出的链的存储中没有任何绑定,因此在第一批公告到达之前,只有前两类被保留:切链时,请用 --reserved-peer 传入整个集群的验证者 peer id,或将它们放入引导列表(rand-node address --key <file> 会打印密钥的 peer id)。rand_status.reserved_peers 统计这些节点;在集群节点上,它应在启动后一分钟内达到验证者数量。

检查

rand-node status                # rand_status verbatim
rand --rpc http://127.0.0.1:8545 status
rand peers

rand_status 报告 height、view、syncing、sync_target、peer_count、connected_peers、reserved_peers、mempool_size、is_validator、active_validator、faucet、confidential、testnet、fri_profile、programs、notes、nullifiers、tree_root、hc_bundle、hc_auth、hc_auth_multisig、msauth_key、binding_domain、gas_prices、compact_fetched、节点的 address 和 peer_id,以及同步诊断信息(sync_inflight_age_ms、sync_failures、sync_late_batches)、磁盘信息(disk_free_bytes、disk_low)和保留下限(prune_floor,保留全部数据的节点上为 0;prune_history_secs,未设置时为 null)。在 chain 21 上,应当看到 hc_bundle 60af094a…、hc_auth 1e4e347f…、hc_auth_multisig 9705f8e2…、msauth_key "loaded"、faucet true、confidential true、testnet true、binding_domain 1、fri_profile "production",以及介于每 gas 100 至 10,000 和每 KiB 800 至 80,000 之间的 gas_prices。rand_getGenesisHash 必须返回 ca76c276…ca9a,rand_getVersion 必须返回 0.8.0,且 git_sha c0878c3f…。

当可用空间低于 --min-free-disk-mb 的四倍时,rand_getHealth 会在 ok、syncing 或 behind 之前返回 disk_low(附带 free_bytes)。

历史修剪

以 --prune-history 24h 启动的节点会完整保留账本(note、nullifier、anchor、验证者登记表、程序),并删除较旧的区块及其证书、交易和回执。每提交 16 个区块执行一次修剪,每次最多删除 512 个早于区块头时间戳减去时间窗口的区块;创世块、区块头及其父块永远不会被删除,且下限只会上升。磁盘空间在 RocksDB 压缩之后才会释放。在 chain 21 上,与 chain 19 和 chain 20 一样,26 个验证者中有 24 个运行 24h,保留约一天的区块。chain 14 开启修剪时(2026-09-25),待积压数据处理完毕后,每个验证者的区块表从 19 GB 降至 3.3–4.4 GB;另外两个验证者 obs1 和 rand-archive-2 自 2026-09-27 起同样是验证者,不使用该标志运行,保留全部数据。主网节点绝不传入该标志。

在已修剪的节点上,低于下限的按高度寻址的查询(rand_getBlockByHeight、按高度的 rand_getFinality,以及触及下限的 rand_getBlocks 或 rand_getCompactBlocks 范围)会返回错误 -32010 并附带 data.floor:请向归档节点查询。按哈希寻址的查询对于节点不再持有的区块返回 null,与从未存在过的区块相同。钱包扫描不受影响,因为 note 和 nullifier 从不被修剪。

已修剪节点的启动检查是结构性的(区块 0 与保留的区块、它们的 QC、父链接和领导者),而不是从创世块重放,且它不会自行修复:若检查失败,请从归档节点重新同步。除非以 --verify-chain off 运行,否则不要用早于 v0.5.7 的构建打开已修剪的数据目录:其默认检查会从创世块重放,发现区块 1 缺失,并将账本截断到创世块。

托管 prover

自 chain 17 起,钱包为每个 bundle 生成两个证明,较大的那个需要约 6 GB 内存(在 chain 18 上测得;chain 21 上尚未测量)。私有多签花费(命令行)是例外:其 tier-18 授权证明始终在签名者自己的机器上生成,需要约 76 GB。无法承载该证明的钱包可以将其交给与之配对的委托 prover:即独立的 rand-prover 二进制文件,或由节点托管的同一服务。

rand-prover --home /root/prover keygen
rand-prover --home /root/prover pair --name laptop --url https://prover.example.net
rand-node run --datadir ./data --key node.key.json --prover 127.0.0.1:8600 --prover-home /root/prover

pair 会打印一个 randprover: 链接。钱包使用 rand prover pair <link> 接收该链接,随后通过全局 --prover 选项经由它生成证明。prover 的监听器是独立的,绝不是 RPC 的一个方法:与 --rpc 相同的地址会被拒绝。使用 --prover 时,除非 <prover-home>/prover.key.json 存在(默认为 <datadir>/prover),且每个证明槽位有 5.74 GB 加 1 GiB 的可用内存,否则节点拒绝启动;--prover-max-parallel 设置槽位数,默认为一个。

自 chain 20 起,发送给 prover 的是钱包的查看密钥和一个新的盐值,而不是花费密钥。授权证明由钱包自己基于花费密钥生成,因此配对的 prover 可以读取该钱包的全部历史,但无法花费。除非有终止 TLS 的代理置于其前端,否则请将监听器保持在回环地址上,且不要将其托管在提供公共 RPC 的机器上。停止操作会等待进行中的证明完成,因此请为 systemd 单元设置 TimeoutStopSec=180。

验证与修复

rand-node verify --datadir ./data                  # full: hashes, QCs, signatures, proofs, ledger replay, the supply counters
rand-node verify --datadir ./data --mode quick     # structure and ledger replay
rand-node verify --datadir ./data --repair         # truncate a damaged tail; peers resync the rest

请先停止节点。若一致则退出码为 0;若发现问题且未指定 --repair,则退出码为 2。重放会重新验证每个 bundle 证明,因此,若某个构建的约束集与生成该链的约束集不同,它会在第一个 bundle 处停止。

成为验证者

验证者通过质押加入运行中的链,而不是通过新的创世:使用 --validator 启动节点,用 rand-node register --key node.key.json --payout <rand1…> --v2 打印注册信息(chain 20 和 21 拒绝 v1 注册),由钱包用 rand bond <address> 1000 --registration <hex> 质押至少 1,000 RAND,质押生效后节点开始出块:在 chain 21 上,最早为质押之后的第三个 1,000 区块的 epoch 边界。自 chain 20 起,质押在后。创世设置了 staking.admission_by_vote,因此注册新密钥的质押会被拒绝,直到持有超过三分之二投票权重的验证者投票接纳该密钥:每个验证者使用 rand-node admit sign --candidate <key or registration> --key <validator key> --genesis-hash ca76c2766ed6090ac6ba5dad86b724dd5d2b80c0edfaed287cd8b129e663ca9a 签名,其中一个验证者再用 rand-node admit submit 提交这些投票(命令行)。rand_getAdmitted 列出已被投票接纳但尚未注册的密钥。在一个 epoch 边界,新增权重不得超过活跃权重的 2,500 bps。质押对此有详细说明。对于私有测试链,可用 rand-node genesis 切出一条:

rand-node genesis --chain-id 88 \
    --validator a.key.json,1000,rand1<a's payout address> \
    --validator <hex public key of b>,1000,rand1<b's payout address> \
    --alloc rand1<address>=1000 \
    --epoch-blocks 1000 --faucet --fri-profile production \
    --out genesis.json

而 scripts/local-testnet.sh 会在一台机器上运行两个验证者,RPC 分别在 8545 和 8546 上。在不加其他标志的情况下,创世没有 hc_auth、没有 gas 部分,也没有 memo。Chain 20 是在此基础上加上 --hardening-v6 --bundle-guest v3 --auth-guest --gas-price 100 --byte-price 800 --bundle-gas-limit 20479 --gas-dynamic 10485760,262144,1250 --max-gas-price 10000 --max-byte-price 80000 --gas-byte-load paying --envelope-bytes 1860 --testnet --binding-domain 1 --proof-window-blocks 1024 --program-state-cell-fee 10000000 切出的(chain 18 和 19 的标志到 --envelope-bytes 为止),其 bridge 和 staking 部分以及 consensus_domain 由切链脚本拼接而成。Chain 21(deploy/cut-chain21-genesis.sh)增加了 --max-proof-bytes 6291456 --max-block-bytes 19922944 --gas-dynamic 9961472,262144,1250 --fees fees.json --multisig MULTISIG.JSON --auth-multisig-guest,并且 perps 部分在 zUSD 之后拼接。你自己的新链应传入 --binding-domain 1:自 v0.6.8 起,钱包在 chain id 14 至 19 之外的每条链上都生成与创世绑定的形式。

在服务器上部署

仓库的 deploy/ 目录存放着节点集群所用的脚本:deploy/push-to-vps.sh <ip> <letter> "<bootstraps>" [validator|observer] 用于配置一台 Linux 服务器(通过 rsync 同步源码、编译、安装 /usr/local/bin/{rand-node,rand}、以创世哈希为键初始化数据目录,并安装 rand-node systemd 服务),而 deploy/rebuild-vps.sh <ip> 则在有新提交时重新编译并重启,同时保留数据。有两点需要注意。配置脚本使用 deploy/genesis.json 进行初始化,而该文件并非链 21 的创世文件,因此测试网节点需使用 deploy/genesis-chain21.json(位于 main 分支,见上文)手动设置;集群自身的切换使用的是 deploy/cutover-fleet-chain21.sh,滚动更新构建版本则使用 deploy/roll-all.sh,它同时会安装验证器密钥。紧凑区块(v0.8.0,仅限节点的网络协议变更:领导者广播区块头、其签名以及交易哈希,副本则根据各自的内存池重建区块):旧节点会拒绝新消息,因此涉及协议变更的构建应先滚动更新观察者节点和归档节点,然后一次性更新所有验证者。此外,两个脚本都会使用 --delete 参数对 /root/fullnode 执行 rsync,因此存放在该目录树内的密钥会在下次重新编译时被删除:请像集群那样把密钥保存在 /root/keys 中,v0.6.8 的配置脚本现在默认也是如此(v0.6.7 的脚本会把密钥写入 /root/fullnode/deploy/)。服务管理使用 systemctl status rand-node 和 journalctl -u rand-node -f。服务器需要 build-essential clang cmake pkg-config libssl-dev 以及 Rust 工具链。配备 1 个 vCPU、2 GB 内存的服务器无法编译这些二进制文件;请在其他机器上编译后再复制过去。运行验证者和聚合者中给出了配置规格和分步设置说明。

浏览器

randscan.org 与验证者 E 并排运行,为链 21 建立索引;其 REST API 和 WebSocket 数据流是无需节点即可读取链上数据的最简便方式(Explorer API)。该浏览器是开源的(randprotocol/randscan),其索引器只需要节点的 rand_* JSON-RPC 和一个 PostgreSQL 数据库,因此可以针对您自己的节点运行。

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