Rand Protocol
Dokumentation
Anleitungen

Bridge

Die Guardian-Bridge, wie sie gebaut ist – eine Sperrung auf der Quell-Chain, eine Guardian-Attestierung mit einer Dilithium2-Mitsignatur, ein BridgeAttest, der eine Note einzahlt, ein BridgeBurn in einem einzigen Bundle, der abhebt – mit dem Wire-Format, den sieben Backings von zUSD, den zUSD-Gebühren bei Mint und Burn, den Mint-Obergrenzen und der Pause, den Rotationen unter rules-v2 mit ihrem Besitznachweis und ihrer Verzögerung und dem, was auf chain 20 live ist.

Verwenden Sie diese Seite, um die Bridge einzubinden: um auf einer Quell-Chain eine Einzahlung zu erstellen, um auf Rand eine Attestierung einzureichen, um einen überbrückten Vermögenswert wieder zurückzuverbrennen und um den öffentlichen Zustand der Bridge zu lesen. Chain 20, das aktuelle testnet (live seit 2026-10-01 06:17 UTC, fullnode v0.6.8), wurde mit einem bridge-Abschnitt aufgesetzt: acht Guardians ab Guardian-Set-Index 1, acht Post-Quanten-Guardians, ein Pause-Schlüssel, Emitter für Ethereum, BSC, Tron und Solana (die ersten drei sind die am 2026-09-30 neu bereitgestellten Endpunkte), die Gruppe rules-v2, deren globales Mint-Limit 4.000 zUSD pro rollierenden 24 Stunden beträgt, eine Untergrenze für Replay eingehender Transfers sowie zwei in v0.6.8 neue Gruppen: Gebühren von 10 bps auf jedes Minting und jedes Burning, erhoben in zUSD, und Rotationsregeln, nach denen eine Rotation des Post-Quanten-Sets oder des Pause-Schlüssels den Besitz der neuen Schlüssel nachweisen und einen Tag warten muss. Anders als bei Chain 14 führt ihr Genesis zUSD mit allen sieben Backings auf und übernimmt die Verwahrung, die seit Chain 14 mit jedem Schnitt weitergereicht wurde (Von Chain 14 übernommen). rand_getAssets und der Explorer-Endpunkt /bridge/assets liefern das aktuelle locked jedes Backings.

Der Ablauf

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 │
                                                             └────────────────────────────────┘

Eingehend: Der Wert wird auf der Quell-Chain gesperrt, die Guardians attestieren, jeder kann die Attestierung auf Rand einreichen, und das Ledger hängt eine Einzahlungs-Note für den Empfänger an – eine Note, deren asset-Wort der Registry-Index des überbrückten Tokens ist. Ausgehend: Eine Rand-Transaktion verbrennt Noten dieses Vermögenswerts und gibt eine Nachricht aus; die Guardians attestieren diese Nachricht; der Contract auf der Quell-Chain gibt frei. Rand prüft nie einen Header oder Zustandsnachweis der Quell-Chain: Die Signaturen des Guardian-Komitees sind das gesamte Vertrauensmodell.

Vertrauensmodell und Chain-IDs

Bridge-lokale Chain-IDs, gemeinsam mit den Contracts (bridge-codec):

ChainID
Rand1
Ethereum2
BSC3
Tron4
Solana5

Die Guardians bilden ein Komitee aus secp256k1-Schlüsseln; die Identität eines Guardians sind die letzten 20 Bytes von keccak256(uncompressed_pubkey[1..]), also die Adressableitung von Ethereum, sodass ein Schlüssel auf den EVM-Contracts, im Solana-Programm und auf Rand funktioniert. Das Quorum für ein Set der Größe n beträgt n·2/3 + 1. Jede Signatur muss low-s sein (s ≤ n/2), was Malleability ausschließt. Der Digest, den die Guardians signieren, ist mu = keccak256(keccak256(body_bytes)). Eine Rotation des Guardian-Sets ist eine Attestierung des Governance-Emitters (chain 1, keccak256("rand-bridge-governance")), muss new_index == current + 1 tragen und muss vom aktuellen Set signiert sein, nicht von einem abgelösten; das ausscheidende Set behält ein Kulanzfenster von 86.400 Sekunden für laufende Transfers. (Das gilt für das ECDSA-Set. Das Post-Quanten-Set und der Pause-Schlüssel rotieren durch eigene Aktionen, die auf Chain 20 eine Verzögerung von einem Tag abwarten: weiter unten.) Wenn n·2/3 + 1 Guardians kollidieren, können sie beliebigen Wert minten; Bridge-Härtung begrenzt, wie viel und wie schnell, und beseitigt das nicht.

Transaktionsbindung. Eine Rand-Transaktion trägt keine Signatur: Ein Burn wird durch den STARK-Beweis seines Bundles autorisiert. Seit Chain 14 wird jeder Bundle-Beweis über Transaction::binding erstellt und gegen dieses geprüft – einen Hash der gesamten Transaktion (Chain-ID, das eine Bundle mit seinen vier Envelopes, die Aktion), bei dem nur die Beweis-Bytes geleert sind. Seit Chain 17 trägt ein Bundle zwei Beweise, den Bundle-Beweis und den Beweis der Ausgabeautorisierung: Die Bindung leert beide, behält das auth_commit des Bundles, und beide werden dagegen geprüft. Eine Kopie eines ausstehenden Burns mit geändertem to, relayer_fee oder (to_chain, token) oder eines Attests mit geändertem time oder Envelope besteht den Beweis nicht (TxError::InvalidBundleProof). Auf Chain 20 trägt die Bindung zusätzlich den Genesis-Hash (Genesis binding_domain: 1: rand-tx-bind-2 über den Genesis-Hash, die Chain-ID, das Bundle und die Aktion), sodass auch ein Beweis, der für eine andere Chain mit derselben Chain-ID erstellt wurde, nicht verifiziert wird. Wallets und Relayer müssen ein Binary verwenden, das für die Chain gebaut ist, an die sie senden: v0.6.8 auf Chain 20 (v0.6.7 auf den Chains 18 und 19); ein älteres rand erzeugt Beweise und Signaturen, die Chain 20 zurückweist.

Die Guardian- und Relayer-Daemons sind nicht Teil des Full Nodes; der Full Node enthält den gemeinsamen Codec und die empfangende Hälfte.

Wire-Format

Alle Ganzzahlen sind Big-Endian; die Wire-Version ist 1.

Attestierungs-Envelope

OffsetLängeFeld
01version = 1
14guardian_set_index
51n_sigs
666·n_sigsSignaturen: index (1), r (32), s (32), v (1)
6 + 66·n_sigs…body

Body-Header – 51 Bytes, danach die Payload

OffsetLängeFeld
04timestamp
44nonce
82emitter_chain
1032emitter_address
428sequence
501consistency_level
51…payload

Transfer-Payload – ID 1, 133 Bytes

OffsetLängeFeld
01id = 1
132amount (u256; Rand verwendet die unteren 128 Bits, und eine Einzahlung muss außerdem in das u64 einer Note passen)
3332token_address
652token_chain
6732to – der Empfänger-Hash, siehe unten
992to_chain
10132fee (u256): der Anteil des Relayers an amount

GuardianSetUpgrade-Payload – ID 2: id (1), new_index (4), n (1), danach n 20-Byte-Schlüssel.

Adressen von Ethereum, BSC und Tron sind 20 Bytes, in den 32-Byte-Slots links mit 12 Nullbytes aufgefüllt; eine Solana-Adresse füllt den Slot aus. Ein Transfer muss vom registrierten Emitter der Chain für emitter_chain stammen, und token_chain muss der emittierenden Chain entsprechen. Eine Attestierung umfasst höchstens 16 KiB. Keine der Maßnahmen zur Bridge-Härtung hat das Wire-Format, den Codec oder die bereitgestellten Endpunkte auf den Quell-Chains verändert: Die Post-Quanten-Mitsignaturen und die Governance-Nachrichten reisen in der Rand-Transaktion, nicht in der Attestierung.

Der 32-Byte-Empfänger

Eine abgeschirmte Adresse umfasst 1.216 Bytes, und das Wire-Format hat Platz für 32. Das Feld to trägt einen Hash davon:

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

Der Einzahler nennt den Hash auf der Quell-Chain, die Guardians signieren ihn, der Einreicher legt die vollständige Adresse in die Aktion BridgeAttest, und das Ledger berechnet den Hash neu und weist eine Abweichung zurück – andernfalls könnte der Einreicher wählen, wer die Einzahlung eines anderen erhält. Front-Ends auf der Quell-Chain müssen diese Form akzeptieren; die Wallet gibt sie aus. Chain 11 machte den Slot kurzzeitig direkt zur Receiver-ID; diese Adressform wurde mit Chain 12 zurückgenommen (Konten und Adressen).

Ein Token, viele Backings: zUSD

Die Asset-Registry gehört nicht mehr der Bridge. Sie ist in die RPL-Token-Registry auf Ledger-Ebene umgezogen, die native und überbrückte Tokens gemeinsam nutzen, und der State Root der Chain legt sich als tokens_root darauf fest. Ein überbrückter Token ist ein Registry-Eintrag, dessen Mint-Autorität Bridge { backings } ist, und er entspricht nicht mehr einem einzelnen Paar (chain, token). Der erste, zUSD, ist durch USDT und USDC gedeckt, die auf vier Chains gesperrt sind – Ethereum, Binance Smart Chain und Solana führen beide Coins, Tron nur USDT (Circle hat USDC auf Tron eingestellt):

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

Auf Chain 20 ist zUSD der Index 1, rpl1xtj6k2x8strx8c2d5fjs50ltztck5ykms4ve7nmzmstf6fhn0v0spelqtx, Name „Shielded USD“, 8 Dezimalstellen, im Genesis-File aufgeführt, wie auf den Chains 15 bis 19, mit dem Namen, dem Symbol und dem Salt, die er auf Chain 14 hatte, sodass seine Asset-ID (32e5ab28c782c663e14da2650a3feb12f16a12db85599f4f62dc169d26f37b1f) und seine rpl1…-ID unverändert übernommen wurden (Explorer: GET /tokens/1). Auf Chain 14 wurde er bei Höhe 256 durch eine register_bridged_token-Transaktion registriert.

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

Ein Paar (chain, token) deckt in der gesamten Registry höchstens einen Token (TokenError::BackingTaken); ein Token hat 1 bis 32 Backings; decimals ist die eigene Dezimalstellenzahl des Quell-Coins, 0 bis 18, nie die 8 des überbrückten Tokens. Das gebündelte Backing ist ein ausdrücklich benanntes Risiko: Die sieben Coins sind zum Nennwert in zUSD hinein und aus zUSD heraus fungibel, sodass ein Depeg bei einem einzigen von ihnen jeden zUSD-Inhaber verwässert, und es gibt auf Rand-Seite keine Obergrenze für den Anteil eines einzelnen Backings.

zUSD ist ein Korb. Wer zUSD hält, trägt das Risiko aller gelisteten Deckungen, nicht das der Münze, die er eingezahlt hat: Ein zUSD ist ein Anspruch auf den Pool, auf jede Deckung mit ausreichend locked. Nichts bepreist eine Deckung. Eine Sperrung von 1 Einheit einer beliebigen gelisteten Münze prägt 1 zUSD, und ein Burn darf jede Deckung benennen, deren locked ihn abdeckt. Eine Münze, die ihren Peg verloren hat, kann daher zum Nennwert eingezahlt und das zUSD gegen eine Münze verbrannt werden, die ihren Peg gehalten hat, bis die soliden Deckungen leer sind. Der Verlust ist begrenzt durch die rollierenden Mint-Fenster und durch die tatsächlich gesperrte Verwahrung: Auf chain 20 können in beliebigen 24 Stunden höchstens 4,000 zUSD über alle Deckungen und 100,000 je Deckung geprägt werden (rules v2), und über eine solide Deckung kann nicht mehr abfließen, als diese Deckung hält. Das Fenster begrenzt die Rate, nicht die Gesamtsumme: Es verschafft die Zeit, Mints zu pausieren, was neue Einzahlungen der schwachen Münze stoppt, während Burns offen bleiben. Es gibt kein Rücknahmelimit je Deckung und keine Pause je Deckung. Auf chain 14 waren vor rules v2 der eigene Pauser der Quell-Endpunkte und die Ratenobergrenzen je Token die einzige Begrenzung.

Die Invariante: total_supply == Σ backings.locked. TokenRegistry::lock (ein BridgeAttest) addiert den attestierten Betrag in einem Aufruf zum locked der Deckung und zum total_supply des Tokens; TokenRegistry::release (ein BridgeBurn) subtrahiert den freigegebenen Betrag von beiden. Nichts anderes schreibt eines der beiden Felder, und das Token-Blatt bindet das locked jeder Deckung in tokens_root ein, sodass zwei Nodes, die sich über eines uneinig sind, an der State Root auseinanderlaufen. Die Quell-Endpunkte erheben beim Lock keine Gebühr und ziehen beim Release den gesamten attestierten Betrag ab, sodass das locked jeder Deckung der Verwahrung dieses Endpunkts entspricht, abgesehen von Nachrichten in Übertragung. Die Gebühren auf chain 20 verletzen dies nicht: Eine Gebühr ist eine zUSD-Note, die ausgegeben und gedeckt bleibt, sodass die Einzahlung den Bruttobetrag sperrt und der Burn nur amount − fee freigibt. Auf chain 20 wurde der eigene Abzug der Endpunkte von 10 bps USDT beim Release zum Cut auf 0 gesetzt; die Gebühr wird nun auf Rand erhoben.

Beträge sind auf der Leitung Einheiten mit 8 Dezimalstellen, unabhängig von den Dezimalstellen der Quellmünze: Eine Einzahlung von 1 USDT von Ethereum (6 Dezimalstellen) wird als 100_000_000 Einheiten gesperrt. Nur Index 0, RAND, hat die neun Dezimalstellen dieser Chain.

Ein Token muss vor seiner ersten Einzahlung gelistet sein. Eine Registrierung bei erster Sichtung gibt es nicht mehr: Eine Attestierung, die ein (chain, token) benennt, das niemand gelistet hat, wird mit BridgeError::UnlistedToken abgewiesen (token … of chain … is not listed on this chain). Die Mittel verbleiben in der Verwahrung des Quell-Endpunkts, bis ein postquantensicheres Guardian-Quorum die Münze listet (Listing after genesis).

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

rand_bridgeAssetId(token_chain, token_address) berechnet dies weiterhin auf jeder Chain, und es ist das, worauf das (token_chain, token_address) der Payload einer Transfer aufgelöst wird, aber es ist kein Registry-Schlüssel mehr. Was eine Note und eine Wallet tragen, ist der Index des Tokens, ein dichtes u32: Index 0 ist RAND und steht nie in der Registry. rand_getAssets liefert eine Zeile je Deckung, wobei alle Zeilen eines Tokens unter demselben Index stehen:

[{ "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 und minted_today sind Dezimalzeichenfolgen in Einheiten mit 8 Dezimalstellen, wie jeder u64-Betrag, den die RPC seit chain 14 liefert; index, chain, decimals und mint_day bleiben Zahlen. Die Methoden auf Token-Ebene — rand_getTokens, rand_getToken, rand_getTokenSupply — liefern einen überbrückten Token als eine Zeile mit allen seinen Deckungen (RPL tokens). Der GET /bridge/tokens des Explorers ist die statische Allowlist der Münzen, die die Bridge akzeptiert.

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

Getragen von einer gewöhnlichen shielded Transaktion, deren einziges Hidden-Asset-Bundle die Basis von 0.001 RAND zahlt und nichts darüber hinaus: Ihr Relayer zahlt für einen Einzahler, der noch kein RAND hält. Die Chain, nicht der Einreicher, berechnet die Einzahlungsnote { pk: recipient.pk, from: 0, amount, asset: <index>, time, r } aus dem Betrag, den die Guardians signiert haben, sodass ein Relayer weder einen Mint aufblähen noch umleiten kann; ihre Commitment ist das eine Blatt, das eine Transaktion nicht auf der Leitung trägt. Auf chain 20 ist der amount der Note der Bruttobetrag abzüglich der Mint-Gebühr, und die Chain hängt eine zweite Note, die Gebühr, an die Treasury an (Fees on mint and burn).

Die Blinding ist abgeleitet, nicht gewählt. r muss

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

sein, der Digest, den das Guardian-Quorum signiert hat; alles andere ist BridgeError::WrongDepositBlinding und wird vor jeder Signaturarbeit abgewiesen. Das Feld bleibt auf der Leitung — der Wiederaufbau der öffentlichen Felder durch die Wallet liest es dort —, es wird nur eingeschränkt, und die Regel gilt auch für das Attest einer Rotation. Zuvor konnte jeder, der die ausstehende Transaktion eines Relayers sah, dieselbe Attestierung mit einer eigenen Blinding einreichen: Der Empfänger erhielt weiterhin eine Note, aber die Transaktion des Relayers starb, nachdem er für einen Beweis bezahlt hatte. Mit festem r benennen zwei Einreicher einer Attestierung zu einem time dieselbe Note, und der Pool bringt sie an der Commitment in Konflikt.

time und asset sind die beiden Vorhersagen des Einzahlers, getroffen, damit der Umschlag versiegelt werden kann, bevor die Transaktion existiert. time unterliegt demselben Fenster wie das eines Bundles (1,024 Blöcke auf chain 20, aus dem Genesis-Wert proof_window_blocks; zuvor 256) und ist bewusst nicht abgeleitet: Keine Regel liefert ein time, auf das sich alle Einreicher einigen würden, sodass ein Front-Runner weiterhin ein anderes time im Fenster wählen kann — eine andere Note desselben Empfängers, Betrags und Assets, die die Wallet des Empfängers auf dieselbe Weise findet —, und die beiden Einreichungen kollidieren stattdessen am Attestierungs-Digest. asset muss dem Index entsprechen, den die Registry (chain, token) bereits gegeben hat, sonst TxError::AttestAssetMismatch.

Das fee der Transfer-Payload wird mitgeführt und an niemanden gezahlt: Es gibt keine Einreicher-Identität, die man bezahlen könnte, daher ist die Einzahlung der Bruttobetrag, abzüglich allein der eigenen Mint-Gebühr der Chain, wo das Genesis eine setzt (chain 20). Der Replay-Schutz erfolgt über den Digest: Jedes verbrauchte mu geht in die spent-Menge der Bridge, und der Mempool weist einen zweiten Relayer ab, der dieselbe Attestierung einreicht. Eine Einzahlung unterliegt außerdem der Mint-Obergrenze der Deckung, auf chain 20 auch der globalen Obergrenze, und wird abgewiesen, solange Mints pausiert sind (B1, rules v2). Beide Obergrenzen zählen den Bruttobetrag. Auf einer Chain, deren Genesis eine Replay-Untergrenze setzt, chain 20 eingeschlossen, wird ein Transfer, dessen sequence unter der Untergrenze seiner Quellchain liegt, mit BelowReplayFloor abgewiesen, bevor eine Signatur geprüft wird (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 ist erforderlich: das Dilithium2-Mitsignatur-Quorum der Guardians als [{"index":0,"signature":"<4840 hex>"},…]. Vor jedem Beweisen dekodiert die Wallet die Attestierung, prüft das Quorum gegen die pq_guardians des Nodes und die Chain-ID, prüft, dass der Empfänger auf den attestierten Hash abbildet, und liest den gelisteten Index; dann leitet sie r ab, versiegelt den Umschlag des Empfängers (auf einer Chain mit Gebühren für den Nettobetrag) und zahlt mit einem Bundle aus eigenem RAND. bridge-rotate reicht eine Rotation der Guardian-Menge ein (Payload 2), die nichts einzahlt, und weist alles ab, was die Menge von Rand nicht um eins fortschreibt. Weil jedes Feld einer Einzahlungsnote in dieser einen Transaktion öffentlich ist, baut die Wallet eines Empfängers an ihn adressierte Einzahlungsnoten wieder auf, indem sie die committeten bridge_attest-Transaktionen durchläuft, ohne einen Umschlag zu öffnen — ein feindlicher Relayer kann Müll versiegeln, aber die Note nicht verbergen.

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 }
}

Seit dem Hidden-Asset-Bundle von chain 14 ist ein Burn ein Bundle (zuvor waren es zwei): Die Slots 0–1 beweisen, dass der Burner Noten des Assets besaß, die sich auf mindestens amount summieren, wobei burn_a der Wert ist, der den Pool verlässt, und die Slots 2–3 zahlen die Gebühr in RAND. Die Gebühr ist BRIDGE_BURN_FEE, 0.01 RAND, was die Basis des Bundles abdeckt. relayer_fee ist ein Anteil von amount (fee ≤ amount), der auf der Zielchain vom Release-Contract abgezweigt wird, keine zusätzliche Gebühr, sodass burn_a genau amount ist. Auf chain 20 behält die Chain zudem eine Burn-Gebühr von 10 bps von amount als zUSD-Note für die Treasury ein, und das Release beträgt amount − fee; der Anteil des Relayers muss in dieses Release passen (Fees on mint and burn).

Ein Burn benennt seine Münze. (to_chain, token) muss eine Deckung des verbrannten Assets sein (andernfalls TokenError::NotABacking), und zwei Regeln werden im Aktionsschritt vor dem Beweis des Bundles geprüft:

  • Die Release-Einheit. Eine Deckung mit d < 8 Quelldezimalstellen gibt in Einheiten von 10^(8−d) frei (100 bei einer Münze mit 6 Dezimalstellen; 1 bei einer mit 18). Der freigegebene Betrag und relayer_fee müssen je ein ganzes Vielfaches sein, sonst TokenError::NotReleasable (… is not a multiple of the release unit …): Ein Bruchteil unterhalb der kleinsten Einheit der Quellmünze bliebe in der Verwahrung gestrandet.
  • Die eigene Verwahrung der Deckung. Der freigegebene Betrag ≤ locked dieser Deckung, sonst TokenError::InsufficientBacking (only … is locked in that backing, not the … asked for). Der Gesamtbestand des Tokens mag das Release abdecken; entscheidend ist die Verwahrung dieser einen Deckung.

Der freigegebene Betrag ist amount auf einer Chain ohne Gebühren und amount − fee auf chain 20.

to muss die richtige Form haben (mit Nullen aufgefüllte 20 Bytes auf den Chains 2, 3 und 4; nie ausschließlich null). Das Ledger legt einen BridgeBurnRecord unter einer streng steigenden burn_sequence ab, mit dem Transaktions-Hash im Absender-Slot, und die Guardians lesen das Burn-Log:

→ {"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 ist die ausgehende Nachricht genau so, wie Guardians sie hashen und signieren müssen; ihr Transfer-Betrag ist release_amount. Der obige Datensatz ist der erste Burn von Chain 20: 0.999 zUSD wurden bei Höhe 2,899 in Richtung BSC USDT verbrannt, davon gingen 0.000999 an die Treasury und 0.998001 wurden freigegeben.

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

Die Wallet prüft zuerst – anhand eines einzigen Lesevorgangs von Bridge-Zustand und Token-Registry, noch vor jedem Proving –, dass die Chain eine Bridge hat, dass ASSET in der Registry steht, dass TOKEN auf TO_CHAIN es besichert und mindestens AMOUNT gesperrt hält und dass AMOUNT und die Relayer-Gebühr ganze Freigabeeinheiten sind. Auf einer Chain mit Gebühren gibt sie anschließend die Bridge-Gebühr und den freizugebenden Betrag aus und fragt vor dem Proving nach (--yes überspringt die Frage). Ein Burn beweist dann ein Bundle: etwa 100 s auf einem Laptop und etwa 1.5 MB Beweis, und auf Chain 20, wie bei jedem Bundle seit Chain 17, daneben einen Beweis der Ausgabeberechtigung (einige Sekunden, etwa 1.37 MB).

Gebühren bei Mint und Burn

Neu in fullnode v0.6.8 und auf Chain 20 durch die Genesis-Gruppe festgelegt

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

erhebt die Chain auf jeden überbrückten Mint und jeden überbrückten Burn eine Gebühr, im überbrückten Token, auf Rand, als abgeschirmte Note an die Treasury-Adresse recipient. Jeder Satz beträgt höchstens 100 bps (1 %); auf Chain 20 liegen beide bei 10 bps, 0.1 %. Die Gebühr beträgt ⌊amount · bps / 10⁴⌋, abgerundet auf eine ganze Freigabeeinheit der Besicherung (10^(8 − source decimals): 100 Einheiten bei einem USDT mit 6 Dezimalstellen, 1 bei einer Münze mit 18 Dezimalstellen); Gebühr und Rest sind also beide ganze Einheiten, und eine Gebühr, die auf null rundet, erzeugt keine Note (ledger/bridge_notes.rs, bridge_fee).

Einzahlung (BridgeAttest) mit Brutto gBurn (BridgeBurn) von amount
Gebühr⌊g · mint_bps / 10⁴⌋, abgerundet auf eine Freigabeeinheit⌊amount · burn_bps / 10⁴⌋, abgerundet auf eine Freigabeeinheit
Seite des Nutzerseine Einzahlungsnote über g − feeamount verbrannt (burn_a == amount, kein Guest-Wechselgeld); der ausgehende Body bestätigt release_amount = amount − fee, und der Endpunkt gibt genau diesen Betrag frei
Note der Treasuryfee, Blinding blake3("rand-bridge-fee-r-1" ‖ mu), zu time und Asset der Einzahlungfee, Blinding blake3("rand-bridge-burn-fee-r-1" ‖ txid), zu time des Bundles
locked und total_supplysteigen um gfallen um release_amount
geprüft gegendie Mint-Obergrenzen je Besicherung und die globale, auf gInsufficientBacking und NotReleasable auf release_amount; der Anteil des Relayers muss darin Platz finden (FeeExceedsAmount)

Beide Noten werden ausgegeben und sind besichert, daher gilt weiterhin total_supply == Σ locked == custody: Die Gebührennote ist zUSD wie jede andere, und ihr Wert bleibt auf der Quell-Chain gesperrt, bis jemand sie verbrennt. BRIDGE_BURN_FEE, die 0.01 RAND, die ein Burn in seinen RAND-Slots zahlt, bleibt unverändert und davon getrennt.

Die erste Rundreise auf Chain 20, vom Node gelesen: Eine Einzahlung von 1 zUSD aus BSC bei Höhe 1,664 (4f6e6c54…1a64) brachte dem Einzahler 0.999 zUSD und der Treasury 0.001; ein Burn von 0.999 zUSD zurück nach BSC USDT bei Höhe 2,899 (Sequenz 13) behielt 0.000999 zUSD ein und gab 0.998001 frei. Danach (gelesen am 2026-10-01) hielt die BSC-USDT-Besicherung 22.001999 zUSD, und das Angebot des Tokens, 99.001999 zUSD, entsprach der Summe seiner Besicherungen.

Wie die Treasury ihre Noten findet. Eine Gebührennote hat keinen Umschlag: Jedes Wort von ihr ist öffentlich (der Betrag aus der Transaktion und dem Genesis-Satz, das Asset und time aus der Transaktion, das Blinding abgeleitet, der Eigentümer das Genesis-recipient). Die Wallet, deren Adresse recipient ist, rekonstruiert Gebührennoten im selben öffentlichen Durchlauf, der die Einzahlungsnoten rekonstruiert, und liest dabei je Synchronisierung einmal rand_getBridgeState.fees und die Dezimalstellen der Besicherungen. Ein Node, der bei den Gebühren lügt, kann höchstens bewirken, dass eine Note nicht gefunden wird, nie, dass eine gutgeschrieben wird. Die Wallet eines Einzahlers bewertet seine eigene Einzahlung auf dieselbe Weise: brutto abzüglich Gebühr. Das tut nur eine Wallet ab v0.6.8; eine ältere kann auf Chain 20 ohnehin nicht senden.

Ohne die Gruppe, auf den Chains 14 bis 19, gibt es keine Gebühr: Eine Einzahlung schreibt den Bruttobetrag gut, und ein Burn gibt amount frei. Bis Chain 20 war die einzige Bridge-Gebühr der eigene Abzug der Quell-Endpunkte von 10 bps auf USDT bei der Freigabe, auf der Host-Chain; er wurde beim Schnitt von Chain 20 auf 0 gesetzt. Die Gruppe ist im Genesis-Hash festgeschrieben und unter rand-bridge-fees-1 in die Bridge-Wurzel eingefaltet. Die Kodierung steht in fullnode docs/bridge.md §25.

Bridge-Härtung

Die locked je Besicherung und InsufficientBacking schützen die Einlösung. Sie begrenzen nicht das Minting: Eine Attestierung mit gültigem Guardian-Quorum mintet, was sie besagt. Drei Schichten schließen diese Lücke, und eine vierte lässt einen Token ohne Chain-Schnitt listen. Keine ändert die Endpunkte auf der Quell-Chain, das Wire-Format der Attestierung oder bridge-codec.

B1: eine Mint-Obergrenze je Besicherung und eine Mint-Pause

Genesis tokens.mint_cap_per_day gilt je Besicherung: Chain 20 setzt 10000000000000, 100,000 zUSD je Besicherung und Tag, wie jede Chain seit 14. Ohne Regeln v2 (Chain 14) ist ein Tag timestamp_ms / 86_400_000 des Block-Zeitstempels; unter Regeln v2 (Chains 15 bis 20) wird dieselbe Obergrenze über ein gleitendes Fenster von cap_window_secs Blockzeit gemessen, und die registerweite globale Obergrenze – 4,000 zUSD je 86,400 s auf Chain 20 – ist die engere Schranke (Regeln v2). Eine Einzahlung über der Obergrenze wird vor der Guardian-Signaturarbeit mit TokenError::MintCapExceeded abgewiesen und ist zulässig, sobald das Fenster wieder Platz hat.

Genesis bridge.pause_key ist ein einzelner Dilithium2-Schlüssel, verschieden von jedem Post-Quanten-Guardian. Zwei Aktionen ohne Bundle und ohne Gebühr:

  • PauseMints { nonce, signature } – allein vom Pause-Schlüssel signiert. Sie kann nur pausieren.
  • UnpauseMints { nonce, pq_signatures } – braucht ein Post-Quanten-Guardian-Quorum, nie den Pause-Schlüssel.

Beide tragen pause_nonce und erhöhen es, daher kann keine wiederholt werden. Solange pausiert ist, wird jedes Transfer-BridgeAttest mit BridgeError::MintsPaused abgewiesen; Burns und Rotationen der Guardian-Menge bleiben offen, sodass eine Pause die Einlösung nie blockiert. Unter Regeln v2 (Chains 15 bis 20) wird während der Pause auch das Listen abgewiesen. Der Pause-Schlüssel soll auf einer anderen Maschine liegen als die Guardian-Schlüssel.

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]

Was die Pause abdeckt: Guardian-Daemons, die dazu gebracht oder kompromittiert wurden, einen gefälschten Mint mitzusignieren, während die Inhaber des Post-Quanten-Quorums es nicht sind. Sie ist keine Verteidigung gegen den Diebstahl dieses Quorums, das die Pause aufheben, bis zu 32 Besicherungen auf einem Token listen kann, jede mit eigener Tagesobergrenze, und unter Regeln v2 die Post-Quanten-Menge und den Pause-Schlüssel ersetzen kann.

B2: begrenzte Block-Zeitstempel

Der Block-Zeitstempel einer überbrückten Chain bestimmt den Ablauf der Guardian-Menge und den Tag der Mint-Obergrenze, daher darf ein Leader ihn nicht springen lassen können. Auf einer Chain mit Bridge gilt:

  • Eine Gültigkeitsregel, auch beim Replay geprüft: timestamp_ms ≤ parent.timestamp_ms + 60_000, sonst BlockError::TimestampLeap. Mit der bestehenden Rückwärtsregel (BlockError::TimestampRewind) bewegt sich die Blockzeit nur vorwärts, um höchstens eine Minute je Block.
  • Eine Abstimmungsregel, nie eine Replay-Regel: Ein Validator hält seine Stimme für einen Block zurück, der mehr als 15,000 ms seiner eigenen Uhr voraus ist.

Jeder Validator auf einer überbrückten Chain braucht daher NTP: Wer mehr als 15 Sekunden abweicht, ist ein fehlerhafter Leader und ein fehlerhafter Wähler. Während eine Chain nach einem Halt aufholt, kann die Blockzeit um bis zu 60 s je Block vorrücken, sodass ein Mint-Obergrenzen-Tag schneller wechseln kann als ein echter Tag – höchstens eine zusätzliche Tagesobergrenze je vollem Tag aufgelaufenen Rückstands.

B3: eine Dilithium2-Mitsignatur auf jedem Mint

Jedes BridgeAttest, Einzahlung wie Rotation, muss neben seinem ECDSA-Quorum ein Quorum von Dilithium2-Signaturen tragen über

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

mu ist der Digest, den das ECDSA-Quorum signiert; die Chain-ID verhindert, dass eine Mitsignatur von einem Testnet auf einer anderen Rand-Chain verifiziert. Die Signaturen reisen in pq_signatures der Aktion, innerhalb der Transaktionsbindung, sodass sie an einer Kopie nicht entfernt oder getauscht werden können. Geprüft wird billig vor teuer: quorum(n) ≤ len ≤ n (PqNoQuorum), streng aufsteigende Indizes (PqIndexOrder), jeder Index < n (PqIndexOutOfRange), jede Signatur genau 2,420 Bytes (PqBadSignatureLength) und zuletzt, dass jede unter pq_guardians[index] verifiziert (PqBadSignature). Die beiden Quoren werden unabhängig gezählt. Genesis bridge.pq_guardians ist indexgleich mit der ECDSA-Menge des Genesis (Menge 1 auf Chain 20). Eine Payload-2-Rotation bewegt nur die ECDSA-Menge; ohne Regeln v2 ist die Post-Quanten-Menge für die Lebensdauer der Chain fest, und unter Regeln v2 ersetzt sie nur RotatePqGuardians (auf Chain 20 in ihrer V2-Form, mit Besitznachweis und einem Tag Verzögerung). Chain 20 hat acht, dieselben acht Schlüssel wie die Chains 15 bis 19; ein Mint braucht sechs davon (Chain 14 hatte sechs und brauchte fünf).

Diese Mitsignatur bindet auch auf Chain 20 nur die Chain-ID: Anders als die untenstehenden Governance-Nachrichten wurde sie nicht hinter den Genesis-Hash verlegt, denn die Guardian-Daemons, die sie erzeugen, liegen in einem anderen Repository und haben sich nicht geändert. Eine Kopie einer Chain mit derselben ID, die auch die Guardian-Menge kopiert hat, würde die für das Original erstellten Mitsignaturen einer Einzahlung akzeptieren; das Fee-Bundle der Einzahlungstransaktion ist auf Chain 20 an den Genesis gebunden, die darin enthaltene Attestierung nicht.

B4: Listing eines überbrückten Tokens nach dem Genesis

Eine gewöhnliche Adresse kann nie einen Token mit Bridge-Autorität erzeugen. Dafür gibt es zwei Aktionen, jeweils mit einem gewöhnlichen RAND-Fee-Bundle und jeweils unter dem Post-Quanten-Quorum:

  • RegisterBridgedToken { name, symbol, salt, chain, token, decimals, nonce, pq_signatures } — ein neuer überbrückter Token am nächsten Index, mit 8 Dezimalstellen auf Rand, unter dem Mint-Cap des Genesis, mit seinem ersten Backing. Gebühr: die Bundle-Basis plus die registration_fee der Registry (auf Chain 20 1 RAND, also 1,001 RAND; Chain 20 verbrennt die Registrierungsgebühr, statt sie an den Proposer zu zahlen).
  • ListBacking { token_index, chain, token, decimals, nonce, pq_signatures } — ein weiteres Backing für einen registrierten überbrückten Token, beginnend bei locked = 0. Gebühr: die Bundle-Basis.

Beide tragen list_nonce und erhöhen ihn (andernfalls BadListNonce { expected, got }) und führen dieselben Prüfungen aus: Für die Chain ist ein Emitter registriert (BridgeError::NoEmitter), (chain, token) sichert noch nichts ab, weniger als 32 Backings, 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>

Zwei Reihenfolgeregeln. Die Guardians signieren zuerst die Registrierung, warten auf deren Commit, lesen den vergebenen Index zurück und signieren erst dann die Listings, die ihn nennen — die Registrierung eines nativen Tokens ist permissionless, ein vorab signiertes Listing könnte also feststellen, dass sein Index vergeben ist. Und eine Münze wird auf Rand gelistet, bevor setToken sie auf dem Quell-Endpoint freischaltet: Andersherum könnte eine Einzahlung sperren, bevor Rand ein Backing zum Gutschreiben hat, und ihre Attestierung würde mit UnlistedToken abgewiesen, bis Rand aufgeholt hat.

Governance-Nachrichten

Feste Bytes, nie bincode; alle Ganzzahlen Big-Endian:

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 tragen pause_nonce, M_list/M_register tragen list_nonce, und M_rotate_pq/M_rotate_pause tragen rotation_nonce. count ist die Anzahl der Schlüssel, keine Bytelänge; jeder Schlüssel besteht aus den rohen 1.312 Bytes eines Dilithium2-Public-Keys, in der Reihenfolge, die die neue Menge haben wird.

Auf Chain 20 ist jede Nachricht an den Genesis-Hash gebunden (Genesis binding_domain: 1): Die Versionsziffer des Tags wird zu 2, und der 32-Byte-Genesis-Hash steht zwischen dem Tag und der Chain-ID, der Rest bleibt unverändert — M'_pause = "rand-bridge-pause-2" || genesis [32] || chain_id u64 || nonce u64, ebenso rand-bridge-pq-unpause-2, rand-bridge-pq-list-2, rand-bridge-pq-register-2, rand-bridge-pq-rotate-pq-2 und rand-bridge-pq-rotate-pause-2 (bridge/gov.rs). Ein Pause-Schlüssel oder ein Quorum, das für eine Chain signiert, signiert damit für keine andere Chain, die dieselbe Chain-ID und dieselben Schlüssel teilt. Eine in der …-1-Form signierte Datei wird auf Chain 20 abgewiesen (BadPauseSignature, PqBadSignature). Die Chains 14 bis 19 verwenden die obigen …-1-Layouts, Byte für Byte.

Rules v2: Rotationen, rollierende Caps, kein Listing während der Pause

Eine optionale Gruppe innerhalb des bridge-Abschnitts im Genesis. Chain 14 hatte sie nicht; jede Chain seit 15 hat sie, Chain 20 mit denselben Werten:

"bridge": { …, "rules_v2": { "global_mint_cap_per_window": 400000000000, "cap_window_secs": 86400 } }
  • Zwei Rotationen unter dem Post-Quanten-Quorum. RotatePqGuardians { new_pq_guardians, nonce, pq_signatures } ersetzt die gesamte Post-Quanten-Menge; RotatePauseKey { new_pause_key, nonce, pq_signatures } ersetzt den Pause-Schlüssel. Beide kommen wie PauseMints ohne Bundle und ohne Gebühr aus, beide werden von der aktuellen Post-Quanten-Menge signiert, und beide tragen rotation_nonce und erhöhen ihn, sodass ein signiertes Quorum genau eine Rotation autorisiert. Die neue Menge muss die Länge der aktuellen ECDSA-Menge haben, Schlüssel von 1.312 Bytes, keine Wiederholungen und nicht den Pause-Schlüssel enthalten. Es gibt kein Übergangsfenster: Die abgelöste Menge oder der abgelöste Schlüssel signiert nichts mehr. Auf Chain 20 werden diese v1-Aktionen abgewiesen, und eine Rotation muss den Besitz der neuen Schlüssel nachweisen und einen Tag warten (unten).
  • Rollierende Fenster-Caps. Der mint_cap_per_day je Backing wird über ein rollierendes Fenster von cap_window_secs statt über den Kalendertag gemessen, und ein zweiter, registryweiter Cap, global_mint_cap_per_window, deckt alle Backings aller Tokens zusammen ab (TokenError::GlobalMintCapExceeded). Auf Chain 20 sind das 400.000.000.000 Einheiten, 4.000 zUSD pro 86.400 s. Unter der Tagesregel war der Cap um 23:59:59 und erneut um 00:00:01 in zwei Sekunden doppelt so hoch wie der Cap; unter dem Fenster wird die zweite abgewiesen.
  • Kein Listing während der Pause. RegisterBridgedToken und ListBacking werden mit MintsPaused abgewiesen, solange mint_paused gilt. Burns und beide Rotationen bleiben offen.

Rotation: Besitznachweis und eintägige Verzögerung

Rules v2 ließ drei Dinge offen: Nichts bewies, dass jemand die neuen Schlüssel besaß (ein Tippfehler oder ein Schlüssel, den niemand hält, würde das Post-Quanten-Quorum dauerhaft lahmlegen), eine Rotation wurde in dem Block wirksam, in dem sie landete, und nichts konnte eine feindliche Rotation aufhalten. Eine zweite optionale Gruppe neben rules_v2 (das sie voraussetzt), neu in v0.6.8 (Audit v6, BRG-14), schließt sie. Chain 20 setzt sie:

"bridge": { …, "rotation": { "delay_secs": 86400, "needs_possession": true } }
  • Besitznachweis. Eine Rotation muss RotatePqGuardiansV2 { new_pq_guardians, possession, nonce, pq_signatures } oder RotatePauseKeyV2 { new_pause_key, possession, nonce, pq_signatures } sein: possession[i] ist die eigene Dilithium2-Signatur des neuen Schlüssels i über genau die Nachricht, die das Quorum signiert (M_rotate_pq oder M_rotate_pause, auf Chain 20 in ihrer …-2-Form), eine je Schlüssel in Schlüsselreihenfolge, je 2.420 Bytes. Die v1-Aktionen werden mit PossessionRequired abgewiesen. Eine falsche Anzahl oder Länge wird vor der Verifikation des Quorums abgewiesen, eine ungültige Besitznachweis-Signatur (BadPossession { index }) danach.
  • Eine Verzögerung. Eine akzeptierte Rotation wird als ausstehend erfasst, mit effective_at_secs = block time + delay_secs, und wird am Ende des ersten Blocks wirksam, dessen Zeitstempel diesen Wert erreicht. Auf Chain 20 sind das 86.400 s, ein Tag. Bis dahin co-signiert die alte Menge jeden Mint, und der alte Pause-Schlüssel pausiert. Die Annahme einer Rotation verbraucht einen rotation_nonce, ihre Aktivierung einen weiteren, sodass eine unter der alten Menge gepoolte Mint-Attestierung den Pool verlässt, wenn sich die Menge ändert. Von jeder Art kann jeweils eine Rotation ausstehen (RotationPending { effective_at_secs }).
  • Der Abbruch. CancelRotation { kind, nonce, signature } (ohne Bundle, ohne Gebühr) verwirft die ausstehende Rotation der Art kind (0 = die Post-Quanten-Menge, 1 = der Pause-Schlüssel). Sie wird vom aktuellen Pause-Schlüssel signiert, dem einen Schlüssel, der getrennt vom Quorum gehalten wird, und zwar auf Chain 20 über "rand-bridge-cancel-rotation-2" || genesis [32] || chain_id u64 || rotation_nonce u64 || kind u8, und verbraucht den Nonce. Ein übernommenes Quorum kann die Menge daher nicht in weniger als einem Tag rotieren, und der Inhaber des Pause-Schlüssels kann sie in diesem Tag stoppen; der eintreffende Pause-Schlüssel kann seine eigene Ankunft nicht abbrechen.

Der Signierer ist rand-node bridge-gov (rotate-pq, rotate-pause, cancel-rotation, jeweils mit den Schritten message, sign und submit; submit prüft jede Besitznachweis-Zeile erneut, bevor es sendet). rand_getBridgeState liefert rotation_rules und pending_rotations; beide sind auf einer Chain ohne die Gruppe (Chains 14 bis 19) null. Die Rotation der ECDSA-Guardian-Menge (Payload 2) ist nicht betroffen: Sie behält ihre eigenen Regeln und ihr Übergangsfenster. Auf Chain 20 ist noch keine Rotation durchgeführt worden.

Von Chain 14 übernommen

Chain 15 wurde aus der Bridge-Position von Chain 14 geschnitten (dieselben Emitter und Quell-Endpoints), und die Chains 16, 17 und 18 wurden jeweils auf dieselbe Weise aus der ihres Vorgängers geschnitten. Chain 19 wurde mit einer Änderung aus der Position von Chain 18 geschnitten: Ihre Emitter für Ethereum, BSC und Tron sind die am 2026-09-30 neu bereitgestellten Endpoints (0x7af6b17047c1db6cb54347fdea45cf9179075bfa für die Chains 2 und 3, 0x6410797df959987a5baf65b5fab97edeb34d5163 für Chain 4). Chain 18 hat ihnen nie vertraut, daher wurde dort nichts von ihnen gemintet. Chain 20 wurde aus einem Snapshot von Chain 19 bei Höhe 7.258 geschnitten, nachdem die Bridge zuvor gestoppt worden war, und behält die Emitter, Guardians, Post-Quanten-Schlüssel und den Pause-Schlüssel von Chain 19 unverändert; neu hinzu kommen die Gruppen Gebühren und Rotation. Der Emitter von Solana und der Emitter auf der Rand-Seite haben sich seit Chain 14 nicht geändert. Was der Genesis von Chain 20 enthält:

  • Verwahrung. 99 zUSD, als zwei zUSD-Genesis-Notes (10 und 89 zUSD), gegen 22 USDT auf Ethereum, 22 USDT auf BSC, 30 USDT auf Tron und 25 USDT auf Solana gesperrt, und 0 bei jeder USDC-Deckung: das locked von Chain 19 zum Zeitpunkt des Snapshots, das mit der Verwahrung der Endpunkte übereinstimmte. total_supply == Σ locked entspricht der Verwahrung der Endpunkte bei Block 0. Verlauf: Die 10 zUSD, die auf Chain 14 verblieben, waren auf den Chains 15 bis 19 eine Genesis-Note; das Genesis von Chain 18 hielt sie als 9 USDT gesperrt auf Tron und 1 auf Solana, das von Chain 19 als 10 auf Solana nach einer Umschichtung auf Chain 18. Die übrigen 89 sind die Netto-Einzahlungen von Chain 19, abzüglich ihrer Burns.
  • Guardian-Set-Index 1, auf dem die Quell-Endpunkte bereits stehen; die nächste ECDSA-Rotation muss Index 2 tragen. Die acht Post-Quanten-Schlüssel und der Pause-Schlüssel sind unverändert die von Chain 15.
  • burn_sequence 13: Die Burns von Chain 14 endeten bei 6, das Genesis der Chains 15 bis 18 begann bei 7, Chain 18 führte einen Burn aus (7) und Chain 19 begann bei 8. Chain 19 führte fünf Burns aus, die Sequenzen 8 bis 12, daher beginnt Chain 20 bei 13, und kein Endpunkt und kein nach Sequenz indizierter Daemon sieht eine Sequenz zweimal. Der erste Burn von Chain 20 erhielt die Sequenz 13.
  • Replay. Ein Mint einer früheren Chain würde das ECDSA-Quorum noch bestehen, doch seine Post-Quanten-Mitsignatur bindet die ID dieser Chain, sodass er auf Chain 20 abgelehnt wird. Das Genesis von Chain 15 hatte keine Replay-Untergrenze für eingehende Transfers (min_inbound_sequence war null); dort beruhte der Schutz also auf der Mitsignatur und darauf, dass Guardians nie einen alten Lock für die neue Chain-ID mitsignieren. Seit Chain 16 setzt das Genesis eine solche Untergrenze: pro Quell-Chain die niedrigste sequence, die eine Transfer-Attestierung des Emitters dieser Chain tragen darf. Die von Chain 20 lautet { "2": 3, "3": 3, "4": 3, "5": 7 }: die nächste ausgehende Sequenz jedes Endpunkts zum Zeitpunkt des Snapshots von Chain 19, wobei jeder frühere Lock auf Chain 19 bereits gemintet hat. (Die von Chain 18 lautete { "2": 2, "3": 2, "4": 2, "5": 2 }; die von Chain 19 { "2": 1, "3": 1, "4": 1, "5": 4 }, wobei die Sequenz 0 auf jedem neu bereitgestellten Endpunkt der eigene Lock des Betreibers war, der nie minten darf.) Ein Transfer unterhalb der Untergrenze wird mit BelowReplayFloor abgelehnt, gleich welches Quorum und welche Mitsignatur er trägt. Rotationen des Guardian-Sets unterliegen ihr nicht. Die Untergrenze wird im Genesis festgelegt, ist im Genesis-Hash festgeschrieben und in die Bridge-Root eingebunden.

Startreihenfolge auf Chain 14 (Verlauf)

Wie die Bridge im September 2026 auf Chain 14, einem früheren testnet, hochgefahren wurde:

  1. Die Chain mit dem Abschnitt bridge (einschließlich pq_guardians und pause_key) und einem Abschnitt tokens schneiden, der registration_fee und mint_cap_per_day festlegt und keinen Token auflistet. Erledigt am 2026-09-20.
  2. Den Faucet aktivieren, damit ein Deployer-Wallet RAND erhalten kann. Erledigt.
  3. Der Deployer registriert zUSD mit seiner ersten Deckung unter der Signatur des Quorums über M_register bei list_nonce = 0. Erledigt bei Höhe 256.
  4. Die Guardians lesen den Index zurück und signieren die sechs verbleibenden ListBacking-Nachrichten, die nacheinander eingereicht werden. Erledigt: list_nonce steht auf 7.
  5. Für jede Deckung: auf Rand auflisten, bevor setToken auf dem Quell-Endpunkt ausgeführt wird.
  6. Die Rotation des Guardian-Sets (Set 0 → Set 1, von 5 von 6 Mitgliedern des aktuellen Sets signiert) auf den vier Quell-Endpunkten, danach dieselbe Attestierung auf Rand. Rand las während der gesamten Laufzeit von Chain 14 Set 0; Chain 15 beginnt bei Set 1.
  7. Ein Live-Round-Trip pro Quell-Chain mit kleinen Beträgen — Einzahlung über die Bridge, zUSD minten, übertragen, zurück-burnen — und ein Abgleich des locked jeder Deckung (rand_getTokenSupply) mit der Verwahrung des Endpunkts.

Öffentlicher Zustand

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 sind Dilithium2-Public-Keys, je 1.312 Byte als Hex. Ein next_index gibt es nicht mehr: Ein per Bridge übertragener Token wird unter einem durch seine Registrierung festgelegten Index aufgelistet, bevor er eingezahlt werden kann, sodass kein Index mehr vorherzusagen ist. rotation_nonce ist der Wert, den die nächste M_rotate_pq/M_rotate_pause/M_cancel tragen muss; rules_v2 ist null auf einer Chain ohne die Gruppe (Chain 14), und min_inbound_sequence ist null auf einer Chain ohne Replay-Untergrenze (Chain 15 und früher); auf Chain 20 ist es { "2": 3, "3": 3, "4": 3, "5": 7 }. rotation_rules, pending_rotations und fees sind null auf einer Chain ohne diese Gruppen (Chains 14 bis 19). Das obige Beispiel zeigt die Form, nicht die Werte von Chain 20: Chain 20 begann bei guardian_set_index 1 und burn_sequence 13 und liefert "rotation_rules": { "delay_secs": 86400, "needs_possession": true }, "pending_rotations": [] und "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" }. Eine Chain ohne Abschnitt bridge antwortet mit {"enabled": false}.

Keine Guthaben: Per Bridge übertragener Wert besteht aus Notes, und ein Wallet liest mit rand asset-balance [INDEX] aus seinen eigenen Notes, was es hält. Die Bridge-Root schreibt die Emitter, die Guardian-Sets, die verbrauchten Digests, die Burn-Sequenz, das Post-Quanten-Guardian-Set sowie den Pause-Schlüssel, das Pause-Flag und die Governance-Nonces fest (zwei, und rotation_nonce unter Rules v2), dazu die Replay-Untergrenze, wo das Genesis eine setzt, und auf Chain 20 die Rotationsregeln samt jeder ausstehenden Rotation und die Gebührengruppe; sie fließt nur auf einer Chain mit Bridge in die State-Root ein. Das Token-Register samt dem locked jeder Deckung ist separat als tokens_root festgeschrieben. GET /bridge und GET /bridge/assets des Explorers liefern denselben Zustand, Letzteres mit Einzahlungs- und Burn-Summen pro Deckung als Zeichenketten mit 8 Dezimalstellen; GET /tokens und GET /tokens/:id liefern das Register.

Was öffentlich ist und was verborgen bleibt

waswer es sieht
Betrag, Asset-Index und Empfängeradresse einer Einzahlungalle, in dieser einen Transaktion
welche Note ein Empfänger später ausgibtniemand
Betrag, Asset, Coin, Ziel und Relayer-Gebühr eines Burnsalle — die Guardians benötigen sie
Betrag, Asset und Eigentümer einer Bridge-Gebühren-Note, die Treasury (Chain 20)alle: Sie wird aus öffentlichen Feldern rekonstruiert
welche Notes einen Burn finanziert habenniemand
per Bridge übertragene Guthabennur der Inhaber oder ein Inhaber eines Viewing-Keys
Guardian-Sets, Emitter, das Register mit dem locked jeder Deckung, das Burn-Logalle

Bekannte Lücken

Aus der eigenen Liste des Fullnodes: Auf Rand wird kein Relayer bezahlt; der Umschlag einer Einzahlung ist an nichts gebunden, sodass ein feindlicher Relayer Datenmüll versiegeln kann, weshalb das Wallet Einzahlungs-Notes stattdessen aus öffentlichen Feldern rekonstruiert; die gemeinsamen Testvektoren auf Ledger-Ebene wurden auf dem Note-Pool nicht neu aufgebaut; bridge-burn erfährt die eigene Richtlinie des Ziel-Endpunkts weiterhin erst, nachdem ein Beweis bezahlt wurde; Block-Zeitstempel, die dem des Vorgängers entsprechen, sind auf einer per Bridge angebundenen Chain zulässig, sodass zusammenarbeitende Leader ein abgelöstes Guardian-Set innerhalb seines Kulanzfensters weiter gelten lassen könnten; das In-Memory-Burn-Log ist unbegrenzt; ein Light Client oder eine On-Chain-Verifikation des Zustands der Quell-Chain existiert nicht und ist nicht geplant.

Zuletzt geprüft gegen den fullnode-Commit c0878c3f (Chain 21) am 2026-10-12. Wo Code und diese Seite voneinander abweichen, hat der Code recht; sagen Sie uns Bescheid.