Rand Protocol
文檔
指南

質押

註冊驗證者、由驗證者集合投票決定准入、從錢包綁定質押、解除綁定與提取、收款地址、epoch、質押登記額度,以及實時鏈上為何沒有罰沒機制。

使用本頁可於運行中的鏈加入驗證者集合、退出集合,以及讀取登記冊。共識權重必須公開,因此驗證者登記冊是這條鏈上唯一以明文記載金額的表;質押款項仍從一張屏蔽 note 支出,並回到另一張屏蔽 note。

登記冊

每個曾經綁定質押的驗證者佔一行,以其 base58 地址為鍵:

ValidatorEntry {
    public_key,                  // Dilithium2: signs blocks, and this validator's staking actions
    stake:    u64,               // bonded: the weight an epoch's set gives it
    pending:  Vec<(u64, u64)>,   // unbonding, as (release_epoch, amount), oldest first
    rewards:  u64,               // fees earned as a block proposer, unpaid
    payout:   ShieldedAddress,   // rand1… — where a withdraw pays
    nonce:    u64,               // replay protection for its signed actions
    activation_epoch: u64,       // under a `staking` section: the first epoch this row may be in a set
}

每個欄位均屬公開,登記冊亦會被雜湊進每個區塊的狀態根。行不會被刪除:全數解除綁定的驗證者仍保留一行,其 stake = 0。

常數數值含義
MIN_STAKE1,000 RAND一行要進入某個 epoch 的集合所需的質押額
MAX_VALIDATORS100一個 epoch 的集合上限
UNBONDING_EPOCHS2已解除綁定的款項須等待多少個 epoch 方可提取
epoch_blockschain 20 為 1,000每個 epoch 的區塊數;屬創世參數

Epoch

epoch(h) = h / epoch_blocks。epoch e ≥ 1 的集合,取自 epoch e − 1 最後一個區塊時的登記冊:所有 stake ≥ MIN_STAKE 的行,按 (stake descending, address ascending) 取前 MAX_VALIDATORS 名,再按地址排序。某個 view 的領導者、區塊的投票者,以及證書的驗證,均使用該區塊所屬 epoch 的集合。由此有兩點結果:

  • 綁定的質押要到之後的邊界才成為權重。 它即時進入登記冊,但集合只在邊界處變更——在沒有 staking 區段的鏈上是下一個邊界,在 chain 20 上則較遲(見下文)。
  • 解除綁定會在下一個邊界減去權重,而該款項要等兩個 epoch 才可提取。驗證者遠在可提取之前,已停止提出區塊。

若推導結果為空,則沿用上一個集合。以 --validator 啟動、其密鑰不在任何現有集合內的節點,會處於觀察狀態,直至某個 epoch 接納它,因此在創世後才綁定質押的驗證者無須重新啟動;rand_status 會分別回報 is_validator(密鑰在此)與 active_validator(已在當前集合內)。

staking 區段

創世可載有 staking 區段(fullnode v0.5.4,audit v4 STAKE-2)。沒有此區段時,上述規則即為全部規則,chain 14 即是如此。自 chain 15 起的每條鏈均載有此區段。chain 20 啟用的項目如下:

"staking": {
  "faucet_budget_per_epoch": "10000000000000", "bond_activation_epochs": 2,
  "max_weight_bps": 3333, "max_stake_entry_bps_per_epoch": 2500,
  "admission_by_vote": true, "registration_v2": true,
  "faucet_recipients": ["rand1…", "…16 wallets…"],
  "faucet_minters": ["<validator address>", "…18 validators…"]
}
欄位chain 20規則
bond_activation_epochs2在 epoch e 套用的綁定,會排入 epoch e + 1 + 2 生效;登記與追加均同。其金額即時計入 stake,但在此之前不屬權重,而排隊中的質押不可解除綁定
max_stake_entry_bps_per_epoch2,500(25 %)在一個邊界處可成為權重的質押上限(登記與追加合計),以當時已生效權重的比例計算:⌊active · bps / 10,000⌋。超出上限的到期行須待下一個邊界,按綁定次序處理。以 chain 20 的 26,000 RAND 生效質押計,每個邊界為 6,500 RAND。無論規模大小,新進者在 k 個邊界後所佔份額至多為 1 − 1.25^−k,兩個邊界後約為三分之一
admission_by_votetrue登記新密鑰的綁定會被拒絕(NotAdmitted),直至持有逾三分之二投票權重的驗證者投票接納該密鑰(見下文);追加無須投票
max_weight_bps3,333任何驗證者的權重均不得超過其集合總權重的 33.33 %;超出部分會在集合中被截減,登記冊的 stake 不變
registration_v2true登記須就創世雜湊、chain id、驗證者地址及收款地址簽署 rand-register-2;v1 登記會被拒絕
faucet_budget_per_epoch10,000 RANDfaucet 每個 epoch 至多鑄造此數額(超出即為 FaucetBudgetExhausted;計數器於下一個 epoch 重置)
faucet_recipients16 個錢包faucet 的 Mint 只可付予這些錢包(FaucetRecipientNotAllowed);此名單使 faucet 得以與橋並存
faucet_minters18 名驗證者faucet 的 Mint 只可由這些驗證者密鑰簽署(MinterNotAllowed),在接納時及區塊套用時均會檢查,不論另一把密鑰已綁定多久。在 chain 20 上,他們是 26 名創世驗證者中的 18 名。此名單自 chain 16 起已載於每份創世
slashing無沒有 slashing 區段,故沒有驗證者會被罰沒或監禁(公開內容)

在該區段下,faucet 計數器與綁定隊列會附加到狀態根,域為 rand-state-5,而驗證者葉為 rand-validator-leaf-4。chain 20 另有動態 gas 價格及 RPL-2 程式狀態,附加於其後,使其域變為 rand-state-8,而投票接納會將該根再包裹一次,成為 H("rand-state-admitted-1", root ‖ admitted_root)。chain 15 沒有 faucet_minters 名單:在該鏈上,節點只有在創世驗證者簽署 faucet 的 Mint 時(MinterNotAllowed,屬節點政策),才會將其放入池中並轉發,使創世後才綁定質押的驗證者無法透過該池耗盡預算。有了此名單,同一項拒絕便成為有效性規則。

沿革:chain 15 至 19 沒有 admission_by_vote,因此任何持有 1,000 RAND 的密鑰均可登記,而且它們將入場預算設為固定的 max_stake_entry_per_epoch,即每個邊界 10,000 RAND。固定數額是按單一創世度身訂造的;比例則讓新進者無論質押多少,所佔份額都相同。兩個欄位不可同時設定。

投票接納

新見於 fullnode v0.6.8(audit v6,STAKE-2),並於 chain 20 啟用。在 26 把密鑰各持 1,000 RAND 的情況下,集合中可構成阻擋的三分之一約為 13,000 RAND 的購入質押,而且不會有任何罰沒。在此規則下,由集合決定哪些新密鑰可以登記。

  • 投票是一項操作,即 AdmitValidator { candidate, signatures },不附 bundle,亦無費用。每個簽名均為某驗證者對 blake3("rand-admit-validator-1" ‖ genesis hash ‖ candidate address) 的 Dilithium2 簽名,按投票者地址升序排列。投票者所持權重必須嚴格超過三分之二的投票集合權重,即仲裁證書的算術:在 chain 20 上為 26 名驗證者中的 18 名。投票綁定於單一創世,無須 nonce:接納會因其所許可的登記而被消耗。
  • 投票集合即登記冊就當前 epoch 所作的推導。其中排隊中的綁定不屬權重,而解除綁定會即時使其離開。候選者不能為自己投票接納。
  • 已接納集合屬共識狀態,最多有 256 把等待登記的密鑰(超出即為 AdmissionSetFull)。rand_getAdmitted 提供該集合;rand_getLimits.admission_by_vote 說明某條鏈是否需要投票。
# each voting validator, offline
rand-node admit sign --candidate <registration hex or public key> --key node.key.json --genesis-hash <hash>
# whoever collects the lines
rand-node admit submit --candidate <…> --signature <line> --signature <line> … --rpc http://127.0.0.1:8545

之後綁定照下文進行。rand_getGenesisHash 會返回 admit sign 所需的雜湊。

四個指令

哪個二進位檔負責哪項操作,取決於由哪把密鑰簽署:綁定會花費 note,因此屬錢包指令;解除綁定與提取則由驗證者的 Dilithium2 密鑰簽署,而該密鑰只有節點持有。

操作指令依附於支付
登記rand-node register無——輸出一個 blob無
Bondrand bond一個以 burn_r 為質押額的 bundle由錢包的 note 支付 0.001 RAND
Unbondrand-node unbond無(bundle: null)無
Withdrawrand-node withdraw無(bundle: null)由提取金額中支付 0.001 RAND

登記

rand-node register --key node.key.json --payout "$(rand --key payout.key.json address)" --v2 [--rpc http://127.0.0.1:8545]

輸出驗證者的地址及一個十六進位的 Registration:其公鑰、收款地址及一個簽名。使用 --v2(chain 20 要求如此)時,簽名是就該鏈的創世雜湊、chain id、驗證者地址及收款地址對 rand-register-2 所作的簽署;不使用時則為 v1 形式,即 blake3("rand-register" ‖ bincode(chain_id, payout)),chain 20 會拒絕此形式(在 binding_domain: 1 的鏈上,例如 chain 20,登記訊息無論如何都是 v2 的那一種)。程式會讀取 RPC 以取得 chain id,使用 --v2 時亦取得創世雜湊,因此為某條鏈簽署的登記,在另一條鏈上會被拒絕。收款地址是錢包地址,並非節點密鑰,亦是日後追加無法更改的唯一欄位。請將該十六進位字串交給持有質押款項的一方。

綁定

rand bond <validator base58> <amount in RAND> [--registration <hex>] [--fee <RAND>] [--no-wait] [--cuda]

一般的屏蔽交易:錢包證明每筆交易自 chain 14 起均附帶的 4 入 4 出隱藏資產捆綁包(bundle),質押金額包含在其 RAND 銷毀量 burn_r 內,而賬本只在 burn_r == amount 時才接納一筆質押。在 chain 20 上,捆綁包附帶兩份證明,自 chain 17 起每個捆綁包皆然:tier-14 捆綁包證明,以及針對花費密鑰的 tier-10 授權證明。兩者預設均在本機生成,在 Apple Silicon 手提電腦上約需兩分鐘(證明記憶體)。若使用錢包的 --prover 選項,捆綁包證明改由配對的 prover 生成,授權證明則仍留在錢包所在的機器上。--registration 只在驗證者尚未登記於登記冊時才需要,若已登記則會被拒絕;錢包會先查詢登記冊。在 chain 20 上,除非驗證者集合已先投票接納該密鑰,否則登記質押會被以 NotAdmitted 拒絕(投票接納);追加質押則無須投票。登記質押最少為 1,000 RAND;追加質押可為任何大於零的金額。鏈上得知的只是這個驗證者的質押增加了這個數額;至於由哪些 note 支付、屬於誰,則與轉賬一樣被隱藏——任何人都可以向某個驗證者質押,而除金額外不會洩露任何資料。

submitted bond 8c3f…e1
  0 RAND out, 1000 RAND burned, 0.999 RAND change, fee 0.001 RAND, anchored at height 412
2nRdFC…: stake 1000 RAND, counting as consensus weight from epoch 69

錢包印出的 epoch 是下一個邊界。它不計入啟用延遲:在 chain 20 上,於 epoch 68 提交的質押,最早由 epoch 71 起計入權重;若入場額度(每個邊界為活躍權重的四分之一)已用盡,則更遲。

解除質押

rand-node unbond <amount in RAND> --key node.key.json [--rpc …] [--no-wait]

將 amount 由 stake 移入 pending,其 release_epoch = epoch + 2,並將 nonce 加一。免費、不附捆綁包,簽署內容在 chain 14 至 19 上為 blake3("rand-unbond" ‖ bincode(chain_id, validator, amount, nonce)),在 chain 20 上則為 blake3("rand-unbond-2" ‖ bincode(genesis_hash, chain_id, validator, amount, nonce))(創世設定 binding_domain: 1);它在一個區塊的時間內提交。rand-node v0.6.8 會按所連接的鏈選用相應的形式。解除質押的數額超過質押量或為零,會被拒絕;同一 epoch 內的多次解除質押會合併為一列 pending。

提取

rand-node withdraw <amount in RAND> --key node.key.json [--rpc …] [--no-wait]

將已釋放的價值——rewards 加上 release_epoch 已到的 pending 列,按最早者優先——支付到登記冊中 payout 地址的一個存款 note。它由驗證者簽署(在 chain 20 上使用 rand-withdraw-2,與解除質押一樣先對創世雜湊簽署),不附捆綁包,但並非免費:0.001 RAND 的基本費用會從該金額中扣除,付予處理該筆交易的區塊的提議者,因此提取 1,000 RAND 會產生一個價值 999.999 的 note,而不足以支付基本費用的金額則會被拒絕。note 由鏈自行計算:

cm = H_CM(payout.pk, from = 0, amount − 0.001 RAND, asset 0, time, r)

time 是命令執行時的最新區塊高度(會印出,並受簽署約束),可在捆綁包窗口內的任何位置被接納(chain 20 上為 1,024 個區塊,由其創世設定的 proof_window_blocks 決定;較早的鏈上為 256 個);r 會被公開,令鏈永不從網絡傳輸中接收承諾。其後,payout 錢包透過掃描找到該 note,無須被告知任何資料:

rand --key payout.key.json balance      # balance: 999.999 RAND

加入與退出

# on the joining validator's machine
rand-node register --key node.key.json --payout "$(rand --key payout.key.json address)" --v2
rand-node run --datadir ./data --key node.key.json --validator --bootstrap /ip4/…/p2p/…
#   it syncs and observes; rand_status says is_validator true, active_validator false

# on chain 20: validators holding more than two thirds of the weight vote the key in
rand-node admit sign --candidate <hex from above> --key <their node.key.json> --genesis-hash <hash>
rand-node admit submit --candidate <hex from above> --signature <line> … --rpc http://127.0.0.1:8545

# on the machine holding the stake
rand bond <validator base58> 1000 --registration <hex>
rand validators                       # the new row, active false until it activates

# from its activation epoch on (on chain 20, three boundaries after the bond at the earliest)
rand-node status                      # active_validator: true
rand-node unbond 1000 --key node.key.json     # out of the set at the next boundary
#   wait two epochs
rand-node withdraw 1000 --key node.key.json    # a note worth 999.999 RAND at the payout address

解除質押前請先核對法定人數的算術:一個有 n 名成員的集合,需要多於 2/3 的質押在線。在 chain 20 上,全部 26 名驗證者的質押均剛好等於 MIN_STAKE,因此任何解除質押都會令該節點退出下一個 epoch 的集合,而 26 名中必須有 18 名保持運行。在線但不投票的驗證者同樣計入法定人數的缺額:自 chain 14 起,驗證者會對時間比自身時鐘快超過 15 秒的區塊不投票,因此時鐘偏慢的驗證者會在沒有任何錯誤的情況下停止投票。請在其上運行 NTP(運行驗證者)。

創世直接為登記冊設定初始內容——每項為 --validator <key or hex pubkey>,<stake>,<payout>——並拒絕低於最低限額的質押。chain 20 的創世設定了 26 列,每列 1,000 RAND,密鑰與 chain 19 相同。

自 v0.5.11 起,該版本亦帶有創世歸屬(vesting):一個公開的鎖定分配登記冊,其中不可撤銷的條目可將鎖定的 RAND 質押為驗證者的質押(rand-node vesting bond),並可取回(rand-node vesting unbond)。這需要創世設定中有 vesting 部分。沒有任何一條鏈設有此部分,chain 20 亦然,若沒有該部分,這些操作在接納時會被拒絕。(v0.6.8 亦容許可撤銷的歸屬條目只能由一個簽署者門檻向固定的國庫撤銷,同樣只限於有 vesting 部分的情況。)

公開與隱藏的內容

公開隱藏
登記冊每一列:地址、質押、解除質押隊列、未支付的獎勵、payout 地址、nonce無
Bond驗證者、金額、是否附有登記、捆綁包的 burn_r由哪些 note 支付、誰擁有它們、找贖
AdmitValidator(chain 20)候選者及每名投票者連同其簽署不存在其他內容
Unbond驗證者、金額、nonce不存在其他內容
Withdraw驗證者、金額、nonce、note 的 time 和 r誰能打開該 note,以及其後對它的每一次花費

獎勵是驗證者所提議區塊的費用。沒有區塊獎勵,亦沒有通脹,而在 chain 20 上驗證者沒有罰沒,亦沒有監禁:行為不當的驗證者不會損失其質押,補救由營運者負責。Fullnode v0.6.8 確實帶有針對領導者雙重簽署的罰沒規則(審計 v6,STAKE-1:同一密鑰在同一視圖中簽署兩個區塊頭,會銷毀質押的 equivocation_bps 並監禁該密鑰),但只限於創世設定中有 staking.slashing 部分的情況,而 chain 20 沒有(rand_getLimits.slashing 為 null);fullnode 建議只在質押由多於一名營運者持有後才啟用。(區塊聚合代碼亦帶有 SlashAggregator 操作,針對在同一 nonce 簽署兩個不同聚合的 aggregator,它在 chain 20 上並未啟用,因為該鏈沒有聚合部分。)

讀回

rand validators        # the whole register
rand status            # is_validator / active_validator for the node asked
→ {"method":"rand_getValidators","params":[]}
← [{ "address": "2nRdFC…", "stake": "1000000000000",
     "pending": [{ "release_epoch": 71, "amount": "1000000000000" }],
     "rewards": "4000000", "payout": "rand1…", "nonce": 3, "active": true, "jailed_until": null }]
→ {"method":"rand_getAdmitted","params":[]}
← { "admission_by_vote": true, "admitted": [], "max": 256 }
→ {"method":"rand_getEpoch","params":[]}
← { "epoch": 69, "epoch_blocks": 1000, "next_set": ["2nRdFC…", "ByDkxs…"] }

金額為以單位計的十進位字串。active 表示該列是否屬於正在運行當前 epoch 的集合;jailed_until 除非設有罰沒部分,否則為 null;admitted 列出已獲投票接納但尚未登記的密鑰;next_set 是一個預測,邊界前的每次質押與解除質押都會令它改變。瀏覽器的 GET /validators 另外提供 share_percent、blocks_proposed 及最後提議的高度。

最後一次對照 fullnode 提交 c0878c3f(chain 21)核實的日期為 2026-10-12。 如代碼與本頁內容有出入,以代碼為準;請告知我們。