Rand Protocol
ドキュメント
ガイド

ブリッジ

実装されているとおりのガーディアンブリッジ。ソースチェーンでのロック、Dilithium2 の共同署名を伴うガーディアンのアテステーション、note を預け入れる BridgeAttest、1 つのバンドルで引き出す BridgeBurn を、ワイヤフォーマット、zUSD の 7 つのバッキング、zUSD のミントとバーンにかかる手数料、ミント上限と一時停止、所有証明と遅延を伴う rules-v2 のローテーション、そして chain 20 で稼働している内容とともに解説します。

このページは、ブリッジとの統合に使います。ソースチェーン上でのデポジットの構築、Rand へのアテステーションの送信、ブリッジ済みアセットのバーン(送り戻し)、ブリッジの公開状態の読み取りを扱います。現行のテストネットである Chain 20(2026-10-01 06:17 UTC から稼働、fullnode v0.6.8)は bridge セクション付きでカットされました。内容は次のとおりです。ガーディアンセットのインデックス 1 から始まる 8 人のガーディアン、8 人のポスト量子ガーディアン、一時停止キー、Ethereum・BSC・Tron・Solana 向けのエミッター(前の 3 つは 2026-09-30 に再デプロイされたエンドポイント)、グローバルミント上限が 24 時間のローリングあたり 4,000 zUSD である rules-v2 グループ、インバウンドのリプレイ下限。さらに v0.6.8 で新設された 2 つのグループがあります。1 つは、あらゆるミントとバーンに zUSD で徴収される 10 bps の手数料です。もう 1 つはローテーションルールで、ポスト量子セットまたは一時停止キーのローテーションに、新しいキーの所有証明と 1 日の待機を求めます。Chain 14 とは異なり、そのジェネシスは zUSD を 7 つのバッキングすべてとともに掲載し、Chain 14 から各カットを経て引き継がれてきたカストディを引き継いでいます(Chain 14 からの引き継ぎ)。rand_getAssets とエクスプローラーの /bridge/assets は、各バッキングの現在の locked を返します。

フロー

source chain (Ethereum / BSC / Tron / Solana)                 Rand
┌────────────────────────────────┐                           ┌────────────────────────────────┐
│ lock tokens, emit              │  guardians observe,       │ BridgeAttest transaction        │
│ (amount, token, to, fee)       │  sign mu = keccak²(body)  │   admission → one deposit note  │
└────────────────────────────────┘  threshold n·2/3 + 1      │   computed by the chain          │
              ▲                     + Dilithium2 co-signers  │                                  │
   release after guardian         guardian committee         │ BridgeBurn transaction           │
   signatures on the burn  ◄───── secp256k1 keys ◄─────────  │   one bundle: slots 0–1 burn the │
   message                        read the burn log          │   asset, 2–3 pay the RAND fee;   │
                                                             │   emits a sequenced burn message │
                                                             └────────────────────────────────┘

インバウンド:ソースチェーン上で価値がロックされ、ガーディアンがアテステーションを行い、誰でもそのアテステーションを Rand に送信でき、台帳は受取人のデポジットノートを 1 つ追加します。このノートの asset ワードは、ブリッジ済みトークンのレジストリインデックスです。アウトバウンド:Rand のトランザクションがそのアセットのノートをバーンしてメッセージを発行し、ガーディアンがそのメッセージをアテステーションし、ソースチェーンのコントラクトが払い出します。Rand はソースチェーンのヘッダーや状態証明を一切検証しません。ガーディアン委員会の署名が信頼モデルのすべてです。

信頼モデルとチェーン ID

コントラクトと共有されるブリッジローカルのチェーン ID です(bridge-codec)。

チェーンID
Rand1
Ethereum2
BSC3
Tron4
Solana5

ガーディアンは secp256k1 キーの委員会です。ガーディアンの識別子は keccak256(uncompressed_pubkey[1..]) の下位 20 バイトで、これは Ethereum のアドレス導出と同じであるため、1 つのキーが EVM コントラクト、Solana プログラム、Rand のいずれでも使えます。サイズ n のセットのクォーラムは n·2/3 + 1 です。すべての署名は low-s(s ≤ n/2)でなければならず、これにより展性(malleability)を排除します。ガーディアンが署名するダイジェストは mu = keccak256(keccak256(body_bytes)) です。ガーディアンセットのローテーションは、ガバナンスエミッター (chain 1, keccak256("rand-bridge-governance")) からのアテステーションであり、new_index == current + 1 を満たし、置き換え済みのセットではなく現行のセットによって署名されていなければなりません。退任するセットは、処理中の転送のために 86,400 秒の猶予期間を保持します。(これは ECDSA セットの話です。ポスト量子セットと一時停止キーはそれぞれ独自のアクションでローテーションされ、Chain 20 ではそこに 1 日の遅延が課されます。後述。)n·2/3 + 1 人のガーディアンが結託すれば、任意の価値をミントできます。ブリッジの堅牢化はその量と速度に上限を設けますが、この可能性をなくすものではありません。

トランザクションバインディング。 Rand のトランザクションは署名を持ちません。バーンは、そのバンドルの STARK 証明によって承認されます。Chain 14 以降、すべてのバンドル証明は Transaction::binding に対して作成され、検証されます。これはトランザクション全体(チェーン ID、4 つのエンベロープを持つ 1 つのバンドル、アクション)のハッシュで、証明バイトだけが空にされます。Chain 17 以降、バンドルは 2 つの証明、すなわちバンドル証明と支出承認証明を持ちます。バインディングは両方を空にしつつバンドルの auth_commit を保持し、両方の証明がそれに対して検証されます。保留中のバーンの to、relayer_fee、(to_chain, token) を変更したコピー、あるいはアテステーションの time やエンベロープを変更したコピーは、証明の検証に失敗します(TxError::InvalidBundleProof)。Chain 20 では、バインディングにジェネシスハッシュも含まれます(ジェネシスの binding_domain: 1:ジェネシスハッシュ、チェーン ID、バンドル、アクションに対する rand-tx-bind-2)。そのため、同じチェーン ID を共有する別のチェーン向けに作られた証明も検証を通りません。ウォレットとリレイヤーは、送信先のチェーン向けにビルドされたバイナリを実行しなければなりません。Chain 20 では v0.6.8(Chain 18 と 19 では v0.6.7)です。それより古い rand は、Chain 20 が拒否する証明と署名を生成します。

ガーディアンとリレイヤーのデーモンはフルノードには含まれません。フルノードが持つのは、共有コーデックと受信側の処理です。

ワイヤーフォーマット

すべての整数はビッグエンディアンで、ワイヤーバージョンは 1 です。

アテステーションエンベロープ

オフセット長さフィールド
01version = 1
14guardian_set_index
51n_sigs
666·n_sigs署名:index (1)、r (32)、s (32)、v (1)
6 + 66·n_sigs…body

ボディヘッダー — 51 バイト、その後にペイロード

オフセット長さフィールド
04timestamp
44nonce
82emitter_chain
1032emitter_address
428sequence
501consistency_level
51…payload

Transfer ペイロード — id 1、133 バイト

オフセット長さフィールド
01id = 1
132amount(u256。Rand は下位 128 ビットを使用し、デポジットはノートの u64 にも収まらなければなりません)
3332token_address
652token_chain
6732to — 受取人ハッシュ。下記参照
992to_chain
10132fee (u256):amount のうちリレイヤーの取り分

GuardianSetUpgrade ペイロード — id 2:id (1)、new_index (4)、n (1)、続いて n 個の 20 バイトキー。

Ethereum、BSC、Tron のアドレスは 20 バイトで、32 バイトのスロット内では 12 個のゼロバイトで左詰めパディングされます。Solana のアドレスはスロットをそのまま埋めます。Transfer は、emitter_chain に対して登録されたそのチェーンのエミッターから来なければならず、token_chain は発行元のチェーンと等しくなければなりません。アテステーションは最大 16 KiB です。ブリッジの堅牢化は、ワイヤーフォーマット、コーデック、デプロイ済みのソースチェーンエンドポイントのいずれも変更していません。ポスト量子の共同署名とガバナンスメッセージは、アテステーションではなく Rand のトランザクションに載って運ばれます。

32 バイトの受取人

シールドアドレスは 1,216 バイトですが、ワイヤーに用意された領域は 32 バイトです。to フィールドにはそのハッシュが入ります。

to = blake3("rand-shielded-recipient" || pk_bytes || kem_ek)     // ShieldedAddress::recipient_hash

デポジットする人がソースチェーン上でそのハッシュを指定し、ガーディアンがそれに署名し、送信者が完全なアドレスを BridgeAttest アクションに入れ、台帳がハッシュを再計算して不一致を拒否します。そうしなければ、送信者が他人のデポジットの受取人を選べてしまうからです。ソースチェーンのフロントエンドはこの形式を受け付けなければならず、ウォレットがそれを出力します。Chain 11 では一時的にこのスロットを受信者 ID そのものにしていましたが、そのアドレス形式は Chain 12 で元に戻されました(アカウントとアドレス)。

1 つのトークン、多数のバッキング:zUSD

アセットレジストリはもはやブリッジのものではありません。ネイティブトークンとブリッジ済みトークンが共有する、台帳レベルの RPL トークンレジストリに移り、チェーンの状態ルートは tokens_root としてそれにコミットします。ブリッジ済みトークンは、ミント権限が Bridge { backings } であるレジストリエントリであり、もはや 1 つの (chain, token) ペアには対応しません。最初のトークンである zUSD は、4 つのチェーンにロックされた USDT と USDC によってバッキングされています。Ethereum、Binance Smart Chain、Solana は両方のコインを扱い、Tron は USDT のみです(Circle は Tron での USDC を終了しました)。

chain  token (32-byte wire form)                                                  coin  decimals
2      000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7           USDT  6
2      000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48           USDC  6
3      00000000000000000000000055d398326f99059ff775485246999027b3197955           USDT  18
3      0000000000000000000000008ac76a51cc950d9822d68b83fe1ad97b32cd580d           USDC  18
4      000000000000000000000000a614f803b6fd780986a42c78ec9c7f77e6ded13c           USDT  6
5      ce010e60afedb22717bd63192f54145a3f965a33bb82d2c7029eb2ce1e208264           USDT  6
5      c6fa7af3bedbad3a3d65f36aabc97431b1bbe4c2d2f6e0e47ca60203452f5d61           USDC  6

Chain 20 では、zUSD はインデックス 1、rpl1xtj6k2x8strx8c2d5fjs50ltztck5ykms4ve7nmzmstf6fhn0v0spelqtx、名称は “Shielded USD”、小数桁数は 8 で、Chain 15〜19 と同様にジェネシスファイルに掲載されています。名称、シンボル、ソルトは Chain 14 のものと同じため、そのアセット ID(32e5ab28c782c663e14da2650a3feb12f16a12db85599f4f62dc169d26f37b1f)と rpl1… ID も変わらず引き継がれました(エクスプローラー:GET /tokens/1)。Chain 14 では、register_bridged_token トランザクションによって高さ 256 で登録されました。

Backing { chain: u16, token: [u8; 32], decimals: u8, locked: u64, minted_today: u64, mint_day: u32 }

1 つの (chain, token) ペアがバッキングできるトークンは、レジストリ全体で最大 1 つです(TokenError::BackingTaken)。1 つのトークンは 1 〜 32 個のバッキングを持ちます。decimals はソースコイン自身の桁数で 0 〜 18 であり、ブリッジ済みトークンの 8 ではありません。プールされたバッキングは、明示されたリスクです。7 つのコインは par で zUSD と相互に交換可能なため、そのうち 1 つでもデペッグすればすべての zUSD 保有者が希薄化を被り、1 つのバッキングのシェアに対する Rand 側の上限もありません。

zUSD はバスケットです。 保有者が負うのは、預け入れたコインのリスクではなく、リストされているすべてのバッキングのリスクです。1 zUSD はプールに対する請求権であり、十分な locked を持つどのバッキングに対しても行使できます。バッキングの価格を決めるものは何もありません。リストされたどのコインでも 1 単位をロックすれば 1 zUSD がミントされ、バーンは locked が賄えるどのバッキングでも指定できます。そのため、ペッグを失ったコインを額面どおりに預け入れ、その zUSD をペッグを保っているコインに向けてバーンすることが可能で、健全なバッキングが尽きるまで続きます。損失の上限は、ローリング方式のミント枠と、実際にロックされているカストディによって決まります。chain 20 では、24 時間あたりのミント量は全バッキング合計で最大 4,000 zUSD、バッキングごとには 100,000 までです(rules v2)。また、健全なバッキングを通じて流出できる額は、そのバッキングの保有額を超えません。この枠が制限するのは速度であって総額ではありません。得られるのは、ミントを停止するまでの時間です。ミントを停止すれば、弱いコインの新規預け入れは止まり、バーンは引き続き可能です。バッキングごとの償還上限も、バッキングごとの一時停止もありません。rules v2 以前の chain 14 では、ソース側エンドポイント自身の pauser とトークンごとのレート上限だけが制限でした。

不変条件:total_supply == Σ backings.locked。 TokenRegistry::lock(BridgeAttest)は、証明された額をバッキングの locked とトークンの total_supply の両方に 1 回の呼び出しで加算します。TokenRegistry::release(BridgeBurn)は、解放された額を両方から減算します。この 2 つのフィールドに書き込む処理はほかにありません。また、トークンのリーフは各バッキングの locked を tokens_root にコミットするため、どちらかの値について食い違うノード同士は、ステートルートで分岐します。ソース側エンドポイントはロック時に手数料を取らず、解放時には証明された額の全額を差し引くため、処理中のメッセージを除けば、各バッキングの locked はそのエンドポイントのカストディと等しくなります。chain 20 の手数料はこれを損ないません。手数料は発行済みでバッキングされたままの zUSD ノートであり、預け入れでは総額がロックされ、バーンでは amount − fee だけが解放されます。chain 20 では、エンドポイント独自の解放時 10 bps の USDT 手数料はカット時に 0 に設定されました。手数料は現在、Rand 上で徴収されます。

ワイヤ上の金額は 8 桁小数の単位です。 ソースとなるコイン自身の小数桁数にかかわりません。Ethereum からの 1 USDT(6 桁)の預け入れは、100_000_000 単位としてロックされます。このチェーン自身の 9 桁の小数を持つのは、インデックス 0 の RAND だけです。

トークンは最初の預け入れの前にリストされていなければなりません。 初回確認時の登録は廃止されました。誰もリストしていない (chain, token) を指定したアテステーションは、BridgeError::UnlistedToken(token … of chain … is not listed on this chain)で拒否されます。ポスト量子ガーディアンのクォーラムがそのコインをリストするまで、資金はソース側エンドポイントのカストディに留まります(Listing after genesis)。

asset_id = blake3("rand-bridge-asset" || token_chain (2 bytes BE) || token_address (32 bytes))

rand_bridgeAssetId(token_chain, token_address) は、現在もどのチェーンでもこの値を計算します。Transfer ペイロードの (token_chain, token_address) はこの値に解決されますが、もはやレジストリのキーではありません。ノートとウォレットが保持するのはトークンのインデックス、すなわち密な u32 です。インデックス 0 は RAND であり、レジストリには含まれません。rand_getAssets はバッキングごとに 1 行を返し、1 つのトークンのすべての行は同じインデックスを共有します。

[{ "index": 1, "chain": 2, "token": "000000000000000000000000dac17f…", "asset_id": "…", "decimals": 6,
   "locked": "0", "mint_cap_per_day": "10000000000000", "minted_today": "0", "mint_day": 20716 }]

locked、mint_cap_per_day、minted_today は、chain 14 以降 RPC が返すすべての u64 の金額と同様に、8 桁小数単位の 10 進文字列です。index、chain、decimals、mint_day は数値のままです。トークン単位のメソッド(rand_getTokens、rand_getToken、rand_getTokenSupply)は、ブリッジされたトークンを、すべてのバッキングを含む 1 行として返します(RPL tokens)。エクスプローラーの GET /bridge/tokens は、ブリッジが受け入れるコインの静的な許可リストです。

Inbound: BridgeAttest

Action::BridgeAttest { attestation: Vec<u8>, recipient: ShieldedAddress, r: Word8, time: u32,
                       asset: u32, envelope: Envelope, pq_signatures: Vec<PqSignature> }

PqSignature { index: u8, signature: Vec<u8> }        // Dilithium2, 2,420 bytes

通常のシールドトランザクションで運ばれます。このトランザクションの隠しアセットバンドルは 1 つで、支払うのは 0.001 RAND のベース手数料のみです。リレイヤーは、まだ RAND を持たない預け入れ者の代わりに支払っています。預け入れノート { pk: recipient.pk, from: 0, amount, asset: <index>, time, r } は、送信者ではなくチェーンが、ガーディアンが署名した額から計算します。したがってリレイヤーがミント額を水増ししたり、宛先を変更したりすることはできません。そのコミットメントは、トランザクションがワイヤ上に持たない唯一のリーフです。chain 20 では、ノートの amount は総額からミント手数料を引いた額となり、チェーンは 2 つ目のノートとして手数料分をトレジャリーに追加します(Fees on mint and burn)。

ブラインディングは選ぶものではなく、導出されます。 r は次の値でなければなりません。

r = blake3("rand-deposit-r-1" || mu)        // read as eight little-endian words; mu = keccak256(keccak256(body))

これはガーディアンのクォーラムが署名したダイジェストです。それ以外の値は BridgeError::WrongDepositBlinding となり、署名の検証に入る前に拒否されます。このフィールドはワイヤ上に残り、ウォレットの公開フィールドからの再構築がそこから読み取りますが、制約されるのみです。このルールはローテーションのアテストにも適用されます。これ以前は、リレイヤーの保留中のトランザクションを見た誰もが、自分のブラインディングで同じアテステーションを送信できました。受取人にはノートが渡りますが、リレイヤーのトランザクションは、証明のために支払いを済ませた後に無効になりました。r が固定されたことで、同じ time で同じアテステーションを送った 2 者は同じノートを指すことになり、プールはそのコミットメントで両者を競合として扱います。

time と asset は、トランザクションが存在する前にエンベロープを封印できるようにするための、預け入れ者による 2 つの予測です。time にはバンドルのものと同じウィンドウが課されます(chain 20 では 1,024 ブロックで、genesis の proof_window_blocks に由来します。それ以前は 256)。意図的に導出はされません。すべての送信者が合意できる time を与えるルールがないためで、フロントランナーはウィンドウ内の別の time を選べます。それは同じ受取人、金額、アセットの別のノートであり、受取人のウォレットは同じ方法でそれを見つけます。この場合、2 つの送信はアテステーションのダイジェストで衝突します。asset は、レジストリがすでに (chain, token) に与えているインデックスと等しくなければならず、そうでなければ TxError::AttestAssetMismatch となります。

Transfer ペイロードの fee は保持されますが、誰にも支払われません。支払うべき送信者の身元がないため、預け入れは総額となり、genesis がミント手数料を設定している場合(chain 20)にのみ、そのチェーン独自のミント手数料が差し引かれます。リプレイ保護はダイジェストで行われます。消費されたすべての mu はブリッジの spent セットに入り、メモリプールは同じアテステーションを競う 2 人目のリレイヤーを拒否します。預け入れにはバッキングのミント上限も課され、chain 20 ではグローバル上限も課されます。ミントが一時停止されている間は拒否されます(B1、rules v2)。どちらの上限も総額で数えます。genesis がリプレイフロアを設定しているチェーン(chain 20 を含む)では、sequence がソースチェーンのフロアを下回る転送は、署名が検証される前に BelowReplayFloor として拒否されます(Carried over from chain 14)。

rand bridge-mint <attestation hex or @file> --pq <JSON or @file> [--to rand1…] [--fee <RAND>] [--no-wait]
rand bridge-rotate <rotation hex or @file> --pq <JSON or @file> [--fee <RAND>] [--no-wait]

--pq は必須です。ガーディアンの Dilithium2 共同署名クォーラムを [{"index":0,"signature":"<4840 hex>"},…] の形式で渡します。証明の前に、ウォレットはアテステーションをデコードし、クォーラムをノードの pq_guardians とチェーン ID に照らして検証し、受取人が証明された受取人にハッシュされることを確認し、リストされたインデックスを読み取ります。その後 r を導出し、受取人のエンベロープを封印し(手数料のあるチェーンでは純額分)、自身の RAND の 1 つのバンドルで支払います。bridge-rotate はガーディアンセットのローテーション(ペイロード 2)を送信します。これは何も預け入れず、Rand のセットを 1 つ進めるもの以外は拒否します。預け入れノートのすべてのフィールドは、その 1 つのトランザクションで公開されるため、受取人のウォレットは、エンベロープを開くことなく、コミット済みの bridge_attest トランザクションをたどることで、自分宛ての預け入れノートを再構築します。悪意あるリレイヤーはゴミを封印できても、ノートを隠すことはできません。

Outbound: BridgeBurn

Transaction {
  chain_id,
  bundle: <hidden-asset bundle: slots 0–1 spend the token, slots 2–3 pay the RAND fee;
           burn_asset = asset, burn_a = amount, burn_r = 0, fee >= BRIDGE_BURN_FEE>,
  action: BridgeBurn { asset, amount, relayer_fee, to_chain, token, to }
}

chain 14 の隠しアセットバンドル以降、バーンは1 つのバンドルです(以前は 2 つでした)。スロット 0〜1 は、バーンする者がそのアセットのノートを合計で amount 以上所有していたことを証明し、burn_a がプールから出ていく価値となります。スロット 2〜3 は RAND で手数料を支払います。手数料は BRIDGE_BURN_FEE、0.01 RAND で、バンドルのベース手数料を賄います。relayer_fee は amount の一部(fee ≤ amount)であり、宛先チェーンで解放コントラクトによって差し引かれるもので、追加の請求ではありません。したがって burn_a はちょうど amount です。chain 20 では、チェーンは amount の 10 bps のバーン手数料も zUSD ノートとしてトレジャリーに保持し、解放額は amount − fee となります。リレイヤーの取り分は、この解放額に収まらなければなりません(Fees on mint and burn)。

バーンはコインを指定します。 (to_chain, token) はバーンされるアセットのバッキングでなければならず(そうでなければ TokenError::NotABacking)、バンドルの証明の前に、アクションの段階で 2 つのルールが検査されます。

  • 解放単位。 ソース側の小数桁数が d < 8 のバッキングは、10^(8−d) の単位で解放されます(6 桁のコインでは 100、18 桁のコインでは 1)。解放額と relayer_fee は、それぞれこの単位の整数倍でなければならず、そうでなければ TokenError::NotReleasable(… is not a multiple of the release unit …)となります。ソースコインの最小単位に満たない端数は、カストディに取り残されてしまうからです。
  • バッキング自身のカストディ。 解放額は、そのバッキングの locked 以下でなければならず、そうでなければ TokenError::InsufficientBacking(only … is locked in that backing, not the … asked for)となります。トークンの総供給量が解放額を賄えても、問われるのはこの 1 つのバッキングのカストディです。

解放額は、手数料のないチェーンでは amount、chain 20 では amount − fee です。

to は正しい形式でなければなりません(チェーン 2、3、4 ではゼロ埋めされた 20 バイトで、すべてゼロは不可)。台帳は BridgeBurnRecord を、厳密に増加する burn_sequence のもとに、送信者スロットにトランザクションハッシュを入れて記録し、ガーディアンはバーンログを読み取ります。

→ {"method":"rand_getBridgeBurn","params":[13]}
← { "sequence": 13, "amount": "99900000", "fee": "99900", "release_amount": "99800100",
    "body_hex": "…", "digest": "a4bc1c7d…63f4", "tx": "ceb9e3c2…0be7", "height": 2899 }

body_hex は、ガーディアンがハッシュ化して署名するアウトバウンドメッセージそのままです。その Transfer の金額が release_amount です。上のレコードは chain 20 での最初のバーンで、高さ 2,899 で BSC USDT 宛てに 0.999 zUSD がバーンされ、そのうち 0.000999 がトレジャリーに入り、0.998001 が払い出されました。

rand bridge-burn <ASSET> <AMOUNT> <TO_CHAIN> <TOKEN> <TO> [--relayer-fee N] [--fee <RAND>] [--no-wait] [--yes]
rand bridge-message <SEQUENCE>       # one outbound message, verbatim, for a guardian to sign

ウォレットはまず、証明を行う前に、ブリッジの状態とトークンレジストリを一度読み取って次を確認します。チェーンにブリッジがあること、ASSET がレジストリにあること、TO_CHAIN 上の TOKEN がそれを裏付けていて少なくとも AMOUNT がロックされていること、そして AMOUNT とリレイヤー手数料が払い出し単位の整数倍であることです。手数料のあるチェーンでは、続いてブリッジ手数料と払い出される金額を表示し、証明の前に確認を求めます(--yes で確認を省略できます)。その後、バーンは 1 つのバンドルを証明します。ノートパソコンで約 100 秒、証明は約 1.5 MB です。chain 20 では、chain 17 以降のすべてのバンドルと同様に、これと並んで支出認可の証明(数秒、約 1.37 MB)も作成されます。

ミントとバーンの手数料

fullnode v0.6.8 で新たに導入され、chain 20 ではジェネシスグループによって設定されています

"bridge": { …, "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" } }

チェーンは、ブリッジされたすべてのミントとすべてのバーンに対して、ブリッジされたトークンで、Rand 上のシールドされたノートとして、トレジャリーアドレス recipient への手数料を徴収します。各レートは最大 100 bps(1 %)で、chain 20 ではどちらも 10 bps、0.1 % です。手数料は ⌊amount · bps / 10⁴⌋ を、裏付け資産の払い出し単位(10^(8 − source decimals)。小数 6 桁の USDT なら 100 単位、小数 18 桁のコインなら 1 単位)の整数倍に切り捨てた値です。したがって手数料と残りはどちらも整数単位になり、手数料が 0 に丸められた場合はノートは作られません(ledger/bridge_notes.rs、bridge_fee)。

総額 g のデポジット(BridgeAttest)amount のバーン(BridgeBurn)
手数料⌊g · mint_bps / 10⁴⌋、払い出し単位に切り捨て⌊amount · burn_bps / 10⁴⌋、払い出し単位に切り捨て
ユーザー側g − fee のデポジットノートamount がバーンされる(burn_a == amount、ゲストのお釣りなし)。アウトバウンド本体は release_amount = amount − fee を証明し、エンドポイントはちょうどその額を払い出す
トレジャリーのノートfee、ブラインディングは blake3("rand-bridge-fee-r-1" ‖ mu)、デポジットの time とアセットfee、ブラインディングは blake3("rand-bridge-burn-fee-r-1" ‖ txid)、バンドルの time
locked と total_supplyg 増加release_amount 減少
検査の対象g に対するバッキングごとの上限とグローバルのミント上限release_amount に対する InsufficientBacking と NotReleasable。リレイヤーの取り分はその範囲内に収まる必要がある(FeeExceedsAmount)

どちらのノートも発行され、裏付けられています。したがって total_supply == Σ locked == custody は引き続き成り立ちます。手数料ノートも他と同じ zUSD であり、その価値は誰かがバーンするまでソースチェーン上でロックされたままです。BRIDGE_BURN_FEE、すなわちバーンが RAND スロットで支払う 0.01 RAND は変更されず、別のものです。

chain 20 の最初のラウンドトリップをノードから読み取った結果は次のとおりです。高さ 1,664 での BSC からの 1 zUSD のデポジット(4f6e6c54…1a64)により、預け入れた側に 0.999 zUSD、トレジャリーに 0.001 zUSD が渡されました。高さ 2,899(シーケンス 13)での BSC USDT への 0.999 zUSD のバーンでは、0.000999 zUSD が留保され、0.998001 が払い出されました。その後(2026-10-01 に読み取り)、BSC USDT のバッキングは 22.001999 zUSD を保持し、トークンの供給量 99.001999 zUSD はそのバッキングの合計と等しくなっていました。

トレジャリーが自分のノートを見つける方法。 手数料ノートにはエンベロープがありません。そのすべての要素が公開されています(金額はトランザクションとジェネシスのレートから、アセットと time はトランザクションから、ブラインディングは導出され、オーナーはジェネシスの recipient です)。アドレスが recipient であるウォレットは、デポジットノートを再構築するのと同じ公開パスで手数料ノートを再構築します。その際、同期ごとに一度、rand_getBridgeState.fees とバッキングの小数桁数を読み取ります。手数料について嘘をつくノードにできるのは、ノートを見つからなくすることだけで、ノートを不正にクレジットすることはできません。デポジットした側のウォレットも、総額から手数料を引いた額として、自分のデポジットを同じ方法で評価します。これを行うのは v0.6.8 のウォレットだけですが、それより古いウォレットはいずれにせよ chain 20 では送金できません。

グループがない chain 14 から 19 には手数料がありません。デポジットは総額がそのままクレジットされ、バーンは amount が払い出されます。chain 20 までは、ブリッジ手数料はソースエンドポイント自身がホストチェーン上で払い出し時に USDT から差し引く 10 bps のみでしたが、これは chain 20 のカット時に 0 に設定されました。このグループはジェネシスハッシュにコミットされ、rand-bridge-fees-1 の下でブリッジルートに畳み込まれます。エンコーディングは fullnode の docs/bridge.md §25 にあります。

ブリッジの堅牢化

バッキングごとの locked と InsufficientBacking は償還を守ります。これらはミントを制限しません。有効なガーディアンのクォーラムを伴うアテステーションは、そこに書かれた額をミントします。この隙を 3 つの層が塞ぎ、4 つ目の層はチェーンのカットなしでトークンを上場できるようにします。ソースチェーンのエンドポイント、アテステーションのワイヤーフォーマット、bridge-codec はいずれも変更されません。

B1: バッキングごとのミント上限とミントの一時停止

ジェネシスの tokens.mint_cap_per_day はバッキングごとに適用されます。chain 14 以降のすべてのチェーンと同様に、chain 20 では 10000000000000、すなわち 1 バッキングあたり 1 日 100,000 zUSD に設定されています。ルール v2 以前(chain 14)では、1 日はブロックタイムスタンプの timestamp_ms / 86_400_000 です。ルール v2 の下(chain 15 から 20)では、同じ上限がブロックタイムの cap_window_secs 秒のローリングウィンドウで計測され、レジストリ全体のグローバル上限(chain 20 では 86,400 s あたり 4,000 zUSD)のほうが厳しい制約になります(ルール v2)。上限を超えるデポジットは、ガーディアンの署名処理の前に TokenError::MintCapExceeded で拒否され、ウィンドウに再び余裕ができれば受理されます。

ジェネシスの bridge.pause_key は、ポスト量子ガーディアンのいずれとも異なる 1 つの Dilithium2 鍵です。バンドルを伴わず手数料もかからない 2 つのアクションがあります。

  • PauseMints { nonce, signature } — 一時停止鍵のみで署名します。できるのは一時停止だけです。
  • UnpauseMints { nonce, pq_signatures } — ポスト量子ガーディアンのクォーラムを必要とし、一時停止鍵は使えません。

どちらも pause_nonce を持ち、それを進めるため、リプレイされることはありません。一時停止中は、すべての転送 BridgeAttest が BridgeError::MintsPaused で拒否されます。バーンとガーディアンセットのローテーションは開いたままなので、一時停止によって償還が閉じ込められることはありません。ルール v2 の下(chain 15 から 20)では、一時停止中は上場も拒否されます。一時停止鍵は、ガーディアン鍵とは別のマシンで保管することを想定しています。

rand bridge-pause --sig <HEX or @file> [--no-wait]        # no key file and no RAND needed
rand bridge-unpause --pq <JSON or @file> [--no-wait]

一時停止が対象とするのは、ガーディアンのデーモンが騙されたり侵害されたりして偽造されたミントに共同署名してしまい、一方でポスト量子クォーラムの保持者は無事である場合です。そのクォーラム自体が盗まれた場合への防御ではありません。クォーラムがあれば、一時停止の解除、1 つのトークンに対して最大 32 のバッキング(それぞれに独自の日次上限)の上場、さらにルール v2 の下ではポスト量子セットと一時停止鍵の置き換えができます。

B2: ブロックタイムスタンプの制限

ブリッジされたチェーンのブロックタイムスタンプは、ガーディアンセットの失効とミント上限の日を決めるため、リーダーがそれを飛ばせてはなりません。ブリッジのあるチェーンでは次のとおりです。

  • 有効性ルール。リプレイ時にも検査されます。timestamp_ms ≤ parent.timestamp_ms + 60_000 を満たさなければ BlockError::TimestampLeap です。既存の巻き戻しルール(BlockError::TimestampRewind)と合わせて、ブロックタイムは前進のみで、1 ブロックあたり最大 1 分です。
  • 投票ルール。リプレイのルールではありません。バリデータは、自身のクロックより 15,000 ms を超えて先のブロックには投票しません。

したがって、ブリッジされたチェーンのすべてのバリデータには NTP が必要です。15 秒を超えてずれていると、リーダーとしても投票者としても不良と見なされます。停止後にチェーンが追いつく間、ブロックタイムは 1 ブロックあたり最大 60 秒進むことがあるため、ミント上限の日は実際の 1 日より速く切り替わる可能性があります。蓄積した遅れの 1 日分ごとに、最大でさらに 1 日分の上限です。

B3: すべてのミントに対する Dilithium2 共同署名

デポジットもローテーションも含め、すべての BridgeAttest は、ECDSA クォーラムに加えて、次に対する Dilithium2 署名のクォーラムを備えていなければなりません。

M = "rand-bridge-pq-cosign-1" || rand_chain_id (u64 BE) || mu        // 63 bytes

mu は ECDSA クォーラムが署名するダイジェストです。チェーン ID により、testnet の共同署名が別の Rand チェーンで検証を通ることはありません。署名はアクションの pq_signatures に入ってトランザクションのバインディングの内側を通るため、コピー上で取り除いたり差し替えたりすることはできません。検査は安価なものから高価なものの順に行われます。quorum(n) ≤ len ≤ n(PqNoQuorum)、インデックスが厳密に増加していること(PqIndexOrder)、すべてのインデックスが < n であること(PqIndexOutOfRange)、すべての署名がちょうど 2,420 バイトであること(PqBadSignatureLength)、そして最後に、それぞれが pq_guardians[index] の下で検証されること(PqBadSignature)です。2 つのクォーラムは別々に数えられます。ジェネシスの bridge.pq_guardians は、ジェネシスの ECDSA セット(chain 20 ではセット 1)とインデックスが揃っています。payload-2 のローテーションが動かすのは ECDSA セットだけです。ルール v2 がなければポスト量子セットはチェーンの存続期間中固定され、ルール v2 の下では RotatePqGuardians だけがそれを置き換えます(chain 20 では、所有証明と 1 日の遅延を伴うその V2 形式)。chain 20 には 8 つあり、chain 15 から 19 と同じ 8 つの鍵です。ミントにはそのうち 6 つが必要です(chain 14 では 6 つあり、5 つが必要でした)。

この共同署名は、chain 20 でもチェーン ID のみに紐づいています。以下のガバナンスメッセージとは異なり、ジェネリスハッシュの背後には移していません。これを生成するガーディアンデーモンは別のリポジトリにあり、変更されていないためです。ガーディアンセットまでコピーした、同じ ID を持つチェーンのコピーは、元のチェーン向けに作られたデポジットの共同署名を受け入れてしまいます。デポジットトランザクション自身の手数料バンドルは chain 20 ではジェネリスに紐づいていますが、その中のアテステーションは紐づいていません。

B4: ジェネシス後のブリッジトークンの上場

通常のアドレスが Bridge 権限のトークンを作成することはできません。次の 2 つのアクションが必要で、それぞれ通常の RAND 手数料バンドルを用い、ポスト量子クォーラムの下で実行されます。

  • RegisterBridgedToken { name, symbol, salt, chain, token, decimals, nonce, pq_signatures } — 次のインデックスに新しいブリッジトークンを作成します。Rand 上では小数 8 桁で、ジェネシスのミント上限が適用され、最初のバッキングが付きます。手数料:バンドルの基本料金にレジストリの registration_fee(chain 20 では 1 RAND。合計 1.001 RAND。chain 20 では登録手数料はプロポーザーに支払われず、バーンされます)を加えた額です。
  • ListBacking { token_index, chain, token, decimals, nonce, pq_signatures } — 登録済みのブリッジトークンに対する追加のバッキングで、locked = 0 から始まります。手数料:バンドルの基本料金です。

どちらも list_nonce を持ち、これを加算します(そうでない場合は BadListNonce { expected, got })。また、どちらも同じ検査を行います。チェーンにエミッターが登録されていること(BridgeError::NoEmitter)、(chain, token) がまだ何のバッキングにもなっていないこと、バッキングが 32 件未満であること、decimals ≤ 18 であることです。

rand token register-bridged --name … --symbol … --salt <hex32> --chain N --token <hex32> --decimals N --pq <JSON or @file>
rand token list-backing --asset <index> --chain N --token <hex32> --decimals N --pq <JSON or @file>

順序に関するルールが 2 つあります。ガーディアンはまず登録に署名し、コミットされるのを待ち、割り当てられたインデックスを読み戻してから、そのインデックスを指定する上場に署名します。ネイティブトークンの登録は誰でも行えるため、先に署名した上場は、そのインデックスが他に使われてしまう可能性があります。また、コインは、ソース側のエンドポイントで setToken により有効化する前に、Rand 上で上場します。逆の順序では、Rand に入金額を計上するバッキングがまだない状態でデポジットがロックされる可能性があり、そのアテステーションは Rand が追いつくまで UnlistedToken として拒否されます。

ガバナンスメッセージ

固定バイト列であり、bincode ではありません。整数はすべてビッグエンディアンです。

M_pause    = "rand-bridge-pause-1"        || chain_id u64 || nonce u64
M_unpause  = "rand-bridge-pq-unpause-1"   || chain_id u64 || nonce u64
M_list     = "rand-bridge-pq-list-1"      || chain_id u64 || nonce u64 || token_index u32 || chain u16 || token [32] || decimals u8
M_register = "rand-bridge-pq-register-1"  || chain_id u64 || nonce u64 || u8 len || name || u8 len || symbol || salt [32] || chain u16 || token [32] || decimals u8
M_rotate_pq    = "rand-bridge-pq-rotate-pq-1"    || chain_id u64 || rotation_nonce u64 || count u32 || key [1312] …   // rules v2
M_rotate_pause = "rand-bridge-pq-rotate-pause-1" || chain_id u64 || rotation_nonce u64 || key [1312]            // rules v2

M_pause/M_unpause は pause_nonce を、M_list/M_register は list_nonce を、M_rotate_pq/M_rotate_pause は rotation_nonce を持ちます。count はキーの数であり、バイト長ではありません。各キーは Dilithium2 公開鍵の生の 1,312 バイトで、新しいセットでの順序に並べます。

chain 20 では、各メッセージがジェネシスハッシュに紐づきます(ジェネシスの binding_domain: 1)。タグのバージョン番号は 2 になり、32 バイトのジェネシスハッシュがタグとチェーン ID の間に入り、末尾は変わりません。M'_pause = "rand-bridge-pause-2" || genesis [32] || chain_id u64 || nonce u64 であり、rand-bridge-pq-unpause-2、rand-bridge-pq-list-2、rand-bridge-pq-register-2、rand-bridge-pq-rotate-pq-2、rand-bridge-pq-rotate-pause-2 も同様です(bridge/gov.rs)。1 つのチェーンのために署名するペアリングキーやクォーラムは、同じチェーン ID とキーを共有する他のどのチェーンに対しても署名したことになりません。…-1 形式で署名されたファイルは chain 20 では拒否されます(BadPauseSignature、PqBadSignature)。chain 14 から 19 は、上記の …-1 のレイアウトをバイト単位でそのまま使います。

Rules v2:ローテーション、ローリング上限、停止中の上場禁止

ジェネシスの bridge セクション内の任意のグループです。chain 14 にはありませんでしたが、15 以降のすべてのチェーンにあり、chain 20 も同じ値です。

"bridge": { …, "rules_v2": { "global_mint_cap_per_window": 400000000000, "cap_window_secs": 86400 } }
  • ポスト量子クォーラムによる 2 種類のローテーション。 RotatePqGuardians { new_pq_guardians, nonce, pq_signatures } はポスト量子セット全体を置き換え、RotatePauseKey { new_pause_key, nonce, pq_signatures } は pause key を置き換えます。どちらも PauseMints と同様にバンドルも手数料も不要で、どちらも現行のポスト量子セットが署名し、どちらも rotation_nonce を持ってこれを加算するため、署名済みのクォーラム 1 つで承認できるローテーションはちょうど 1 回です。新しいセットは、現行の ECDSA セットと同じ長さで、1,312 バイトのキーで構成され、重複がなく、pause key を含まないことが必要です。猶予期間はなく、置き換えられたセットまたはキーは、それ以降何にも署名できません。chain 20 ではこれらの v1 アクションは拒否され、ローテーションでは新しいキーの所持を証明し、1 日待つ必要があります(後述)。
  • ローリングウィンドウの上限。 バッキングごとの mint_cap_per_day は、暦上の日ではなく cap_window_secs のローリングウィンドウで計測されます。さらに、すべてのトークンのすべてのバッキングを合わせて対象とする、レジストリ全体の 2 つ目の上限 global_mint_cap_per_window があります(TokenError::GlobalMintCapExceeded)。chain 20 では 400,000,000,000 ユニット、つまり 86,400 秒あたり 4,000 zUSD です。日単位のルールでは、23:59:59 と 00:00:01 のそれぞれで上限が使えたため、2 秒間で上限の 2 倍になりました。ウィンドウ方式では、2 回目は拒否されます。
  • 停止中の上場禁止。 RegisterBridgedToken と ListBacking は、mint_paused の間は MintsPaused として拒否されます。バーンと 2 種類のローテーションは引き続き可能です。

ローテーション:所持証明と 1 日の遅延

Rules v2 には 3 つの未解決点がありました。新しいキーを誰かが保有していることを何も証明していなかったこと(誤字や、誰も持たないキーを指定すると、ポスト量子クォーラムが永久に使えなくなります)、ローテーションが反映されたブロックで即座に有効になったこと、そして悪意あるローテーションを止める手段がなかったことです。rules_v2(必須)の隣にある 2 つ目の任意のグループが、v0.6.8(audit v6、BRG-14)で追加され、これらを解消します。chain 20 はこれを設定しています。

"bridge": { …, "rotation": { "delay_secs": 86400, "needs_possession": true } }
  • 所持証明。 ローテーションは RotatePqGuardiansV2 { new_pq_guardians, possession, nonce, pq_signatures } または RotatePauseKeyV2 { new_pause_key, possession, nonce, pq_signatures } でなければなりません。possession[i] は、新しいキー i 自身の Dilithium2 署名で、クォーラムが署名するメッセージそのもの(M_rotate_pq または M_rotate_pause。chain 20 では …-2 形式)に対するものであり、キーの順にキー 1 つにつき 1 つ、各 2,420 バイトです。v1 アクションは PossessionRequired として拒否されます。数や長さが誤っている場合はクォーラムの検証前に拒否され、所持署名が不正な場合(BadPossession { index })は検証後に拒否されます。
  • 遅延。 受理されたローテーションは effective_at_secs = block time + delay_secs として保留中に記録され、タイムスタンプがその値に達した最初のブロックの終了時に有効になります。chain 20 では 86,400 秒、つまり 1 日です。それまでは、旧セットがすべてのミントに共同署名し、旧 pause key が停止を行います。ローテーションの受理で rotation_nonce を 1 つ、有効化でさらに 1 つ消費するため、旧セットの下でプールされていたミントのアテステーションは、セットが変わるとプールから外れます。各種別につき保留できるローテーションは同時に 1 つです(RotationPending { effective_at_secs })。
  • キャンセル。 CancelRotation { kind, nonce, signature }(バンドルも手数料も不要)は、kind の保留中のローテーションを取り消します(0 = ポスト量子セット、1 = pause key)。署名するのは現行の pause key、つまりクォーラムとは別に保持されている唯一のキーで、chain 20 では "rand-bridge-cancel-rotation-2" || genesis [32] || chain_id u64 || rotation_nonce u64 || kind u8 に対して署名し、nonce を消費します。したがって、クォーラムを掌握されても 1 日未満でセットを入れ替えることはできず、pause key の保有者はその 1 日の間に阻止できます。新しく入る pause key が、自身の就任を取り消すことはできません。

署名ツールは rand-node bridge-gov です(rotate-pq、rotate-pause、cancel-rotation。それぞれに message、sign、submit の手順があり、submit は送信前にすべての所持証明の行を再検証します)。rand_getBridgeState は rotation_rules と pending_rotations を返し、このグループのないチェーン(chain 14 から 19)ではどちらも null です。ECDSA ガーディアンセットのローテーション(payload 2)は影響を受けず、独自のルールと猶予期間を保ちます。chain 20 ではローテーションはまだ実行されていません。

chain 14 から引き継いだもの

chain 15 は chain 14 のブリッジの状態(同じエミッターとソースエンドポイント)から切り出され、chain 16、17、18 も同様に、それぞれ直前のチェーンから切り出されました。chain 19 は chain 18 の状態から 1 点だけ変更して切り出されました。Ethereum、BSC、Tron のエミッターが、2026-09-30 に再デプロイされたエンドポイント(chain 2 と 3 は 0x7af6b17047c1db6cb54347fdea45cf9179075bfa、chain 4 は 0x6410797df959987a5baf65b5fab97edeb34d5163)になっています。chain 18 はそれらを信頼していなかったため、そこからミントされたものはありません。chain 20 は、ブリッジを先に停止したうえで、高さ 7,258 の chain 19 のスナップショットから切り出され、chain 19 のエミッター、ガーディアン、ポスト量子キー、pause key をそのまま引き継いでいます。追加されたのは手数料とローテーションのグループです。Solana のエミッターと Rand 側のエミッターは、chain 14 以降変わっていません。chain 20 のジェネシスが持つ内容は次のとおりです。

  • カストディ。 99 zUSD(ジェネシスの zUSD ノート2つ、10 zUSD と 89 zUSD)に対し、Ethereum にロックされた 22 USDT、BSC の 22 USDT、Tron の 30 USDT、Solana の 25 USDT があり、USDC のバッキングはすべて 0 です。これはスナップショット時点のチェーン 19 の locked で、エンドポイントのカストディと一致していました。total_supply == Σ locked は、ブロック 0 におけるエンドポイントのカストディと等しくなります。経緯:チェーン 14 に残っていた 10 zUSD は、チェーン 15 から 19 では1つのジェネシスノートでした。チェーン 18 のジェネシスではそれを Tron にロックされた 9 USDT と Solana の 1 USDT として保持し、チェーン 19 ではチェーン 18 でのリバランスの後、Solana の 10 USDT として保持していました。残りの 89 は、チェーン 19 の純入金額からバーン分を差し引いたものです。
  • ガーディアンセットのインデックス 1。ソース側のエンドポイントはすでにこの状態にあり、次の ECDSA ローテーションはインデックス 2 を持たなければなりません。8つのポスト量子鍵とポーズ鍵はチェーン 15 のものから変わっていません。
  • burn_sequence 13:チェーン 14 のバーンは 6 で終わり、チェーン 15 から 18 のジェネシスは 7 から始まり、チェーン 18 では1回のバーン(7)が行われ、チェーン 19 は 8 から始まりました。チェーン 19 では 8 から 12 までの5回のバーンが行われたため、チェーン 20 は 13 から始まり、シーケンスをキーとするエンドポイントやデーモンが同じ値を2度見ることはありません。チェーン 20 の最初のバーンはシーケンス 13 を使用しました。
  • リプレイ。 以前のチェーンのミントは ECDSA のクォーラムを依然として通過しますが、そのポスト量子の共同署名はそのチェーンの ID に束縛されているため、チェーン 20 では拒否されます。チェーン 15 のジェネシスには受信側のリプレイ下限がなく(min_inbound_sequence は null)、そのためここでは、共同署名と、ガーディアンが古いロックを新しいチェーン ID に対して共同署名しないことに依拠していました。チェーン 16 以降、ジェネシスは下限を設定します。これはソースチェーンごとに、そのチェーンのエミッターによる転送アテステーションが持てる最小の sequence です。チェーン 20 の値は { "2": 3, "3": 3, "4": 3, "5": 7 } です。これはチェーン 19 のスナップショット時点での各エンドポイントの次の送信シーケンスであり、それ以前のロックはすべてチェーン 19 でミントされています。(チェーン 18 の値は { "2": 2, "3": 2, "4": 2, "5": 2 }、チェーン 19 の値は { "2": 1, "3": 1, "4": 1, "5": 4 } で、再デプロイされた各エンドポイントのシーケンス 0 はオペレーター自身のロックであり、ミントされてはなりませんでした。)下限未満の転送は、クォーラムや共同署名の有無にかかわらず BelowReplayFloor で拒否されます。ガーディアンセットのローテーションは、この判定の対象外です。下限はジェネシスで固定され、ジェネシスハッシュにコミットされ、ブリッジルートに組み込まれます。

チェーン 14 でのローンチ手順(履歴)

2026年9月、以前のテストネットであるチェーン 14 でブリッジがどのように立ち上がったかを示します。

  1. bridge セクション(pq_guardians と pause_key を含む)と、registration_fee と mint_cap_per_day を設定し、トークンは列挙しない tokens セクションを指定してチェーンを切り出します。2026-09-20 に完了。
  2. デプロイヤーウォレットが RAND を入手できるよう、faucet を有効にします。完了。
  3. デプロイヤーは、list_nonce = 0 における M_register へのクォーラムの署名のもと、最初のバッキングとともに zUSD を登録します。高さ 256 で完了。
  4. ガーディアンはインデックスを読み戻し、残る6件の ListBacking メッセージに署名します。提出は1件ずつ行います。完了:list_nonce は 7 を示しています。
  5. バッキングごとに、ソース側エンドポイントで setToken を行う前に、Rand でリストします。
  6. ガーディアンセットのローテーション(セット 0 → セット 1、現行セットによる 6 件中 5 件の署名)を4つのソース側エンドポイントで行い、続いて同じアテステーションを Rand で行います。Rand はチェーン 14 の存続期間中セット 0 を示しており、チェーン 15 はセット 1 から始まります。
  7. ソースチェーンごとに少額でライブのラウンドトリップ(ブリッジイン、zUSD のミント、転送、バーンして戻す)を行い、各バッキングの locked(rand_getTokenSupply)をエンドポイントのカストディと突き合わせます。

公開される状態

rand_getBridgeState:

{
  "enabled": true,
  "emitter": "01…",  "emitters": { "2": "02…" },
  "guardian_set_index": 0, "guardians": ["aabb…"],
  "pq_guardians": ["…"],
  "mint_paused": false, "pause_nonce": 0, "list_nonce": 0, "pause_key": "…",
  "registration_fee": "1000000000",
  "burn_sequence": 1,
  "rotation_nonce": 0,
  "rules_v2": { "global_mint_cap_per_window": "400000000000", "cap_window_secs": 86400,
                "global_minted_in_window": "…", "global_mint_headroom": "…" },
  "min_inbound_sequence": null,
  "rotation_rules": null, "pending_rotations": null,
  "fees": null,
  "assets": [ … ]
}

pq_guardians は Dilithium2 の公開鍵で、それぞれ 16 進数で 1,312 バイトです。next_index はもうありません。ブリッジされたトークンは、入金される前に、登録時に確定したインデックスのもとでリストされるため、予測すべきインデックスが残っていないからです。rotation_nonce は、次の M_rotate_pq/M_rotate_pause/M_cancel が持たなければならない値です。rules_v2 はそのグループを持たないチェーン(チェーン 14)では null であり、min_inbound_sequence はリプレイ下限を持たないチェーン(チェーン 15 以前)では null です。チェーン 20 では { "2": 3, "3": 3, "4": 3, "5": 7 } です。rotation_rules、pending_rotations、fees は、それらのグループを持たないチェーン(チェーン 14 から 19)では null です。上のサンプルは形を示すものであり、チェーン 20 の値ではありません。チェーン 20 は guardian_set_index 1 と burn_sequence 13 で始まり、"rotation_rules": { "delay_secs": 86400, "needs_possession": true }、"pending_rotations": []、"fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" } を返します。bridge セクションのないチェーンは {"enabled": false} を返します。

残高は含まれません。ブリッジされた価値はノートであり、ウォレットは自身のノートから rand asset-balance [INDEX] で保有分を読み取ります。ブリッジルートは、エミッター、ガーディアンセット、使用済みダイジェスト、バーンシーケンス、ポスト量子ガーディアンセット、ポーズ鍵、ポーズフラグ、ガバナンスのノンス(2つ。ルール v2 では rotation_nonce も)、ジェネシスが設定する場合のリプレイ下限、そしてチェーン 20 ではローテーションルールと保留中のローテーション、およびフィーグループにコミットします。ブリッジを持つチェーンでのみ、ステートルートに組み込まれます。トークンレジストリは、各バッキングの locked とともに、tokens_root として別にコミットされます。エクスプローラーの GET /bridge と GET /bridge/assets は同じ状態を提供し、後者は 8 桁の小数の文字列によるバッキングごとの入金・バーン合計を含みます。GET /tokens と GET /tokens/:id はレジストリを提供します。

公開されるものと秘匿されるもの

項目誰が見られるか
入金の金額、アセットのインデックス、受取人アドレス全員(その1件のトランザクション内で)
受取人が後でどのノートを使うか誰にも見えない
バーンの金額、アセット、コイン、宛先、リレイヤー手数料全員 — ガーディアンが必要とするため
ブリッジ手数料ノートの金額、アセット、所有者、トレジャリー(チェーン 20)全員:公開フィールドから再構築されるため
どのノートがバーンの資金になったか誰にも見えない
ブリッジされた残高保有者、またはビューイングキーの保持者のみ
ガーディアンセット、エミッター、各バッキングの locked を含むレジストリ、バーンログ全員

既知のギャップ

フルノード自身のリストより:Rand ではリレイヤーに報酬が支払われません。入金のエンベロープは何にも束縛されていないため、悪意のあるリレイヤーがでたらめなデータを封入できます。これが、ウォレットがエンベロープではなく公開フィールドから入金ノートを再構築する理由です。ledger レベルの共有テストベクターは、ノートプール上で再構築されていません。bridge-burn は、宛先エンドポイント自身のポリシーを、証明のコストを支払った後でしか知りません。ブリッジされたチェーンでは親と同一のブロックタイムスタンプが許容されるため、結託したリーダーが置き換え済みのガーディアンセットを猶予期間内に保持し続けられます。メモリ上のバーンログには上限がありません。ソースチェーンの状態に対するライトクライアントやオンチェーン検証は存在せず、計画もされていません。

fullnode のコミット c0878c3f(チェーン 21)に対して 2026-10-12 に最終確認しました。 コードとこのページが食い違う場合は、コードが正です。お知らせください。