使用本頁可了解交易何時最終確定、須有多少名驗證者在線,以及在 RPC 與區塊瀏覽器中所見的區塊欄位代表甚麼。共識為鏈式 HotStuff BFT,採用按權益加權的法定人數;在 chain 21 上,一如 chain 20,即 26 名驗證者中的 18 名。
節點所執行的規則
| 規則 | 實際實現 |
|---|---|
| 法定人數 | 法定人數證書(QC)是由持有總權益嚴格多於 2/3 的驗證者所作出的一組 Dilithium2 投票 |
| 領導者排程 | 純輪流輪替:視圖 v 的領導者為 validators[v mod n],驗證者集合按地址排序。權益只為投票加權,不影響領導者的選取 |
| 提案 | 視圖 v 的領導者在取得 v − 1 的 QC,或取得 v 的法定人數 NewView 消息後提出提案。若提案會套用無效交易,則丟棄該交易,而非丟棄整個區塊 |
| 投票與安全性 | 驗證者每個視圖只投一次票,且只為相對於其鎖定而言安全的區塊投票(其 justify QC 比鎖定的 QC 較新,或該區塊延伸自被鎖定的區塊)。安全狀態在投票離開節點之前先以 fsync 寫入 RocksDB |
| 簽署域 | 在 consensus_domain: 1 的創世區塊上(chain 21,以及自 15 起的每條鏈),每一個投票、new-view 及提案簽名均涵蓋創世哈希(rand-vote-2、rand-newview-2、rand-block-2),因此為某一條鏈所作的簽名無法在任何其他鏈上通過驗證。Chain 14 及更早的鏈簽署時不含此項。自 chain 20 起,binding_domain: 1 將同一規則擴展至交易證明及已簽署的操作(交易) |
| 同層提案 | 副本對每個(視圖,領導者)只保留一個區塊;該領導者在同一視圖提出的第二個不同區塊,會被視為自相矛盾的雙重提案而拒絕。比副本自身視圖超前多於 8 個視圖的提案(PROPOSAL_VIEW_WINDOW),在檢查其簽名之前即被拒絕;已滿的推測樹會先逐出不在主分支上的區塊,然後才拒絕任何提案 |
| 鎖定釋放 | 鎖定絕不會自動降低(CON-4,v0.6.8)。只有當某個提案的 justify QC 級別高於鎖定,或被鎖定的區塊到達時,鎖定才會讓位。一個法定人數的已簽署 NotHeld 回應只會記錄給運營者,並不釋放任何鎖定;驗證者亦會記錄自己的投票,因此絕不會為自己曾投票的區塊簽署 NotHeld。每個高於頭部的已認證區塊都會持久化,並在重啟時還原,因此整個節點群組重啟時,不會讓 QC 落在無人持有的區塊上。若驗證者被鎖定的區塊已永久遺失,則須離線以人手釋放:rand-node safety release-lock |
| QC 組裝 | 每名驗證者都根據廣播的投票在本地組裝 QC,因此即使下一任領導者離線,鏈仍會持續提交 |
| 提交 | 鏈式三 QC 規則:當三份證書在連續的視圖 v, v+1, v+2 中鏈接在某個區塊之上,且該鏈回溯連接到已提交的頭部時,該區塊即告提交 |
| 超時與視圖切換 | 基準 --view-timeout-ms 為 3,000 ms,每連續超時一次即加倍,任何進展即重置。採用超時證書的節奏器(CH-1,v0.6.8):計時器觸發的副本停留在其視圖內,並簽署一個要求進入下一視圖的 NewView。當它收到前一視圖的 QC,或持有多於 2/3 權益的驗證者已要求進入該視圖或其後的視圖時,便進入該視圖;當多於 1/3 的權益要求進入超越其自身的視圖時,它會加入(簽署自己的要求)。單一驗證者的 NewView 不能令任何人移動。視圖設有上限(MAX_VIEW_AHEAD = 10⁶) |
| 節奏 | 當沒有待處理的交易時,領導者會提出空區塊,時間為任何提案者所見最後一個區塊之後至少 --block-interval-ms(默認 1,000 ms) |
| 投票與廣播限制 | 超前多於 8 個視圖的投票會被忽略,每個(視圖,投票者)只計一票;共識廣播按轉發對等節點計量(突發 256,每秒 64);父區塊未知的提案,只有在其提案者於當前紀元的集合或下一紀元的集合中領導該視圖、其位於已提交頭部的樹範圍之內,且其大小與交易根檢查無誤時,才會作為孤塊保留(孤塊池上限為 64 MiB,自 v0.6.1 起每名提案者上限為 16 個孤塊)。自 v0.6.8 起,只有當提案的交易列表的哈希等於其區塊頭所承諾的根,且沒有重複交易時,提案才會被轉發、孤塊才會被保留(GOSSIP-2) |
| 區塊限制 | 交易位元組數以鏈的 max_block_bytes 為限(默認 4 MiB;chain 21 的創世區塊為 19 MiB,即 19,922,944 位元組;chain 20 為 20 MiB)及 2,000 筆交易,由提案者執行,並由每個副本在 Ledger::apply_block 中再次執行 |
| 緊湊區塊(v0.8.0,僅限節點) | 領導者廣播 CompactProposal(共識主題上的標籤 4):包含區塊頭、其簽名及交易哈希,在 26 名驗證者及 2,000 個哈希下為 166,416 位元組,以取代完整區塊。副本預先篩查區塊頭,從其內存池及最近所見交易的快取重建區塊,並只按哈希取回其缺少的交易主體(SyncRequest::Transactions,每次請求 512 筆;rand_status.compact_fetched 為其計數)。較舊的節點無法解碼該幀,因此此項變更隨 chain 21 的切換一併推出;混合的驗證者集合將會停止提交 |
| 脫離主循環套用(v0.8.0,僅限節點) | 追隨者在阻塞線程上套用收到的提案(同時最多 4 個,最多 8 個等待,每名提案者最多 2 個),而預先檢查與插入仍在共識循環上進行;領導者在同一視圖的第二個區塊會排在第一個之後等待,並在第一個插入後被視為雙重提案而拒絕。rand-node run --apply-proposals-inline 可恢復內聯套用 |
| 區塊時間 | 在設有 bridge 部分的鏈上:區塊的 timestamp_ms 不得早於其父區塊的時間戳(TimestampRewind),亦不得比其超前多於 60 秒(TimestampLeap),而驗證者會對超前自身時鐘多於 15 秒的區塊保留其投票;見下文 |
三個連續視圖的提交規則是承重的設計:放寬此規則會重新引入衝突最終性的錯誤,fullnode 對此設有回歸測試。客戶端透過 RPC 所見的一切均已提交:只提供已提交的區塊,而 WebSocket 的 newHeads 串流會按順序為每個已提交區塊發送一則通知。
此表對應 v0.8.0(c0878c3f),即 chain 21 自 2026-10-12 起運行的版本;其中兩項 v0.8.0 的行屬於僅限節點的變更,不改變任何投票或提交規則,其餘則為 v0.6.8 為 chain 20 所帶來的內容。其中三項規則為 v0.6.8(審計 v6)新增,並自 chain 20 於 2026-10-01 上線起生效:超時證書節奏器(CH-1)、不自動釋放鎖定(CON-4),以及提案交易列表檢查(GOSSIP-2)。在 chain 18 與 chain 19(v0.6.7)上,單一驗證者簽署的 NewView 便能令每個副本移至其視圖,而鎖定亦會在持有法定人數權益的驗證者簽署 NotHeld 回應後被降低。節奏器與鎖定規則改變了驗證者簽署的內容與時機,因此 v0.6.8 隨 chain 20 的切換以全部停止、全部啟動的方式推出,絕不逐個節點滾動升級。
測試網的數字
Chain 21 有 26 名驗證者,每名質押恰好 1,000 RAND,全部 26 名均在其創世區塊中,所用的密鑰與對等節點標識沿用 chain 17 至 20。在 chain 15 上,創世區塊中有十八名,使用 chain 14 的密鑰,另外八名於 2026-09-27 以 v2 註冊綁定質押,並自紀元 43 起生效。權益一致,因此法定人數即是人頭數:26 名中須有 18 名在線才能提交任何內容。若只有 17 名在線,視圖會推進,但沒有任何內容會最終確定。由 n 名權益相等的驗證者組成的集合,須有多於 2/3 的權益:2 名中的 2 名、4 名中的 3 名、6 名中的 5 名、18 名中的 13 名、26 名中的 18 名。創世區塊的權重上限(每名驗證者 3,333 bps)對權益相等的集合並不構成約束。
區塊以默認的 1,000 ms 為節奏。在 chain 15 上,實際約每 1.2 秒出一個區塊(首 29 小時平均為 1.18 秒);在 chain 20 上,區塊瀏覽器於高度 3,270 報告平均 1.34 秒(2026-10-01,avg_block_time_ms),而在 chain 21 上於高度 2,283 為 1.17 秒(2026-10-12)。區塊瀏覽器報告其所觀察到的平均區塊時間(GET /stats 中的 avg_block_time_ms)。每當視圖超時,驗證者的視圖便會領先於高度,因此 rand_getHead 同時報告兩者。
紀元
驗證者集合每 epoch_blocks = 1,000 個區塊從登記冊重新推導一次(創世參數)。epoch(h) = h / 1000;紀元 e ≥ 1 的集合,取自紀元 e − 1 最後一個區塊時的登記冊:每一個 stake ≥ 1,000 RAND 的行,按質押量取前 100 名,再按地址排序。在沒有 staking 區段的鏈上,質押於下一個邊界即成為權重。在鏈 20 上,它還須再等待兩個紀元,並受每個紀元的進入額度所限:額度為邊界時活躍質押權重的 2,500 bps(集合持有 26,000 時為 6,500 RAND);而登記新驗證者的質押,除非集合已先以 AdmitValidator 投票接納該密鑰(staking.admission_by_vote),否則會被拒絕。鏈 19 的額度是每個紀元固定 10,000 RAND,並且沒有接納投票。解除質押於下一個邊界即扣減權重,而金額須再等待兩個紀元才可提取。rand_getEpoch 返回紀元、紀元長度和下一個集合的預測。參見質押。
區塊攜帶甚麼
{
"hash": "647b…", "height": 50, "view": 92, "parent": "2d41…",
"proposer": "3v3VBJ…", "timestamp_ms": 1788000123456,
"tx_root": "0000…", "state_root": "a1b2…", "justify_view": 91,
"tx_count": 0, "transactions": []
}
view 是區塊被提議時所在的視圖,justify_view 是它為其父區塊所攜帶的 QC 的視圖。timestamp_ms 由提議者設定,須在下一節所述的界限之內。state_root 承諾區塊之後的賬本。在鏈 21 上(即鏈 20 的欄位,並如下所述併入永續合約與多重簽名狀態):
inner = blake3("rand-state-8" || tree_root || nullifier_root || validators_root || programs_root
|| bridge_root || tokens_root
|| faucet_epoch || faucet_minted_in_epoch || bond_queue_root
|| gas_price || byte_price
|| program_state_root)
state_root = blake3("rand-state-admitted-1" || inner || admitted_root)
鏈 19 承諾相同的欄位,但沒有 program_state_root,域為 rand-state-7,且沒有外層封裝。
各組成部分取決於創世配置。沒有任何可選區段的鏈,以 rand-state-2 承諾前四項。bridge 區段會附加 bridge_root。tokens 區段隨後附加 tokens_root,並把整個哈希的域重設為 rand-state-4;具有 bridge 區段的創世必須同時具有 tokens 區段,因此鏈 21 與鏈 20 一樣兩者皆有。aggregation 區段接著附加聚合者根(在沒有 tokens 的鏈上域為 rand-state-3);鏈 21 沒有此區段。staking 區段最後附加水龍頭的兩個紀元計數器和質押隊列的根(rand-bond-queue-1),並把整個哈希的域重設為 rand-state-5,不論其他區段是否存在;鏈 14 沒有此區段,以 rand-state-4 為域計算哈希,而鏈 15 至 17 以 rand-state-5 為域。vesting 區段在其後附加歸屬登記冊的根,並把哈希的域重設為 rand-state-6;鏈 21 沒有此區段。帶有 dynamic 控制器的 gas 區段,在其他所有內容之後附加即時的 gas_price 和 byte_price(各為大端序 u64),並把哈希的域重設為 rand-state-7。那是鏈 19 的域,而鏈 20 和鏈 21 把價格保留在其根之中:價格每個區塊變動一次,發生在區塊的交易之後、計算根之前(費用)。價格固定的 gas 區段不在根之內。program_state 區段(RPL-2)在價格之後附加每個程序的單元和保險庫的根,並把哈希的域重設為 rand-state-8:這是鏈 20 的域。perps 區段(RPL-3,v0.8.0)在其後附加交易所的狀態,並把哈希的域重設為 rand-state-9:這是鏈 21 的域;其 multisig 區段再以 rand-state-multisig-1 把多重簽名賬戶的登記冊包裹一層(而 fees.usd_subsidy 區段,鏈 21 沒有,則會以 rand-state-price-1 加入經投票決定的價格)。Audit v6 的質押狀態是折疊在其外圍,而非置於其內:在 staking.admission_by_vote 之下(鏈 20 和 21),根為 blake3("rand-state-admitted-1" || inner || admitted_root),而在 staking.slashing 之下(尚無鏈採用),第二層封裝會加入被監禁的集合。tokens_root 是由 blake3("rand-token-leaf-1" || bincode(TokenInfo)) 葉節點按索引順序構成的默克爾根,因此每個代幣的元數據、授權者、mint_nonce、供應量,以及每個支持資產的 locked 數額,都屬於共識狀態:兩個節點若對某一代幣行有分歧,即對該鏈有分歧。在鏈 20 上,代幣根還以 rand-token-registry-3 折入註冊擴展(max_tokens 上限、已銷毀的註冊費、票據價值的界限,以及橋的滾動鑄造上限窗口)。橋根涵蓋發行方、守護者集合、已使用的證明摘要、銷毀序號、PQ 守護者密鑰、暫停密鑰、暫停標誌和兩個治理 nonce;資產登記冊已移出其中,改入 tokens_root。鏈 20 的橋具有 rules_v2,因此附加輪換 nonce 和全局上限參數,域為 rand-bridge-state-5(鏈 14 的為 rand-bridge-state-4);該根隨後再由入站重放下限(rand-bridge-replay-floor-1)包裹,在鏈 20 上還由輪換規則和待處理的輪換(rand-bridge-rotation-1)以及費用組(rand-bridge-fees-1)包裹。驗證者登記冊被哈希為 validators_root(在鏈 20 上葉節點的域為 rand-validator-leaf-4,並帶有每一行的 activation_epoch;在沒有 staking 區段的鏈上為 rand-validator-leaf-2),因此節點若對登記冊的某一行有分歧,即對該鏈有分歧。
區塊時間有界 (B2)
在接入橋的鏈上,區塊時間戳決定守護者集合的到期時間,以及某個支持資產的每日鑄造上限按哪一天計算;因此,若領導者能把時鐘向前撥快,便可提早令已輪換集合的寬限期屆滿,或跳過某一天的上限。鏈 21 與鏈 20 一樣具有 bridge 區段,因此適用三條規則,全部以它為前提(perps 區段亦以同樣方式限制區塊時間):
| 規則 | 界限 | 類型 |
|---|---|---|
| 不得倒退 | timestamp_ms ≥ parent.timestamp_ms(允許相等),否則為 BlockError::TimestampRewind;提議者輸出 max(now, parent.timestamp_ms) | 有效性規則,重放和同步時亦會檢查 |
| 不得跳躍 | timestamp_ms ≤ parent.timestamp_ms + MAX_TIMESTAMP_STEP_MS(60,000 ms),否則為 BlockError::TimestampLeap | 有效性規則,重放和同步時亦會檢查 |
| 時鐘漂移 | 驗證者拒絕為比自身時鐘超前超過 MAX_CLOCK_DRIFT_MS(15,000 ms)的區塊投票 | 僅為投票規則:該區塊仍留在推測樹中,時鐘一致的法定人數仍可為其認證,而重放已提交的歷史時從不套用此規則 |
接入橋的鏈上,每一個驗證者都需要 NTP。 時鐘偏差超過 15 秒的驗證者,既是有故障的領導者(其提議可能得不到投票),也是有故障的投票者(它可能拒絕誠實領導者準時提出的區塊)。只要誠實多數的時鐘保持一致,兩者都不會令鏈停頓,但未同步的節點群會無謂地損失活躍性餘裕。
創世的 timestamp_ms 必須接近真實的啟動時間。若過於久遠,區塊時間會落後於掛鐘時間,直至步進界限讓它追上,每個區塊最多追趕 60 秒。若在未來超過 15 秒,區塊 1 根本不會提交:每一票,包括領導者自己的一票,都會被扣起,直至掛鐘時間到達該時刻。長時間停頓之後,同樣的追趕可令鑄造上限的日子比每個真實日一次翻得更快,每累積一整日的落後,最多多出一日的上限。
客戶端的最終性
- 當
rand_getTransaction(hash)返回一個區塊時,交易即為最終。rand_sendTransaction的接受,只表示已獲准進入內存池。 - 由 RPC 提供或由瀏覽器索引的區塊,即為已提交。 沒有需要等待的最終性窗口;瀏覽器的指南亦如此說明。
- 來自
rand_subscribe["newHeads"]的區塊頭,是節點不會丟失的區塊頭:在區塊提交到存儲之前,不會發送任何內容。 - 落後的節點會重新驗證一切:在接受同步而來的區塊之前,驗證每一份證書、每一個捆綁包和調用證明、每一張回執。最終性簽名從不取代證明而被信任。
恢復速查表,出自全節點自身的說明:高度不前進而 peer_count 偏低,是網絡問題;高度不前進而對等節點正常,表示在線的質押不足 2/3;啟動時出現 CORRUPT CHAIN,表示節點已截去受損的尾部,正在重新同步。