Gunakan halaman ini untuk berintegrasi dengan bridge: membangun deposit di chain sumber, mengirimkan atestasi ke Rand, membakar aset bridged kembali ke luar, dan membaca state publik bridge. Chain 20, testnet saat ini (aktif sejak 2026-10-01 06:17 UTC, fullnode v0.6.8), dibuat dengan bagian bridge: delapan guardian yang dimulai dari indeks guardian set 1, delapan guardian pasca-kuantum, sebuah pause key, emitor untuk Ethereum, BSC, Tron, dan Solana (tiga yang pertama adalah endpoint yang di-deploy ulang pada 2026-09-30), grup rules-v2 yang batas mint globalnya sebesar 4.000 zUSD per 24 jam bergulir, sebuah batas bawah replay masuk, serta dua grup baru di v0.6.8: biaya sebesar 10 bps pada setiap mint dan setiap burn, diambil dalam zUSD, dan aturan rotasi yang mewajibkan rotasi set pasca-kuantum atau pause key membuktikan kepemilikan atas kunci baru dan menunggu satu hari. Berbeda dengan chain 14, genesis-nya mencantumkan zUSD dengan ketujuh backing dan membawa serta kustodi yang diturunkan dari chain 14 melalui setiap cut sesudahnya (Dibawa dari chain 14). rand_getAssets dan /bridge/assets milik explorer memberikan locked terkini untuk setiap backing.
Alurnya
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 │
└────────────────────────────────┘
Masuk: nilai dikunci di chain sumber, guardian memberikan atestasi, siapa pun mengirimkan atestasi itu ke Rand, dan ledger menambahkan satu catatan deposit untuk penerima, yaitu catatan yang kata asset-nya adalah indeks registry token bridged. Keluar: transaksi Rand membakar catatan aset tersebut dan memancarkan sebuah pesan; guardian memberikan atestasi atas pesan itu; kontrak di chain sumber melepaskan dana. Rand tidak pernah memverifikasi header atau bukti state chain sumber: tanda tangan komite guardian adalah seluruh model kepercayaannya.
Model kepercayaan dan id chain
Id chain lokal bridge, yang dipakai bersama dengan kontrak (bridge-codec):
| chain | id |
|---|---|
| Rand | 1 |
| Ethereum | 2 |
| BSC | 3 |
| Tron | 4 |
| Solana | 5 |
Guardian adalah komite kunci secp256k1; identitas seorang guardian adalah 20 byte terakhir dari keccak256(uncompressed_pubkey[1..]), yakni derivasi alamat Ethereum, sehingga satu kunci berlaku di kontrak EVM, program Solana, dan Rand. Kuorum untuk set berukuran n adalah n·2/3 + 1. Setiap tanda tangan harus low-s (s ≤ n/2), sehingga menutup malleability. Digest yang ditandatangani guardian adalah mu = keccak256(keccak256(body_bytes)). Rotasi guardian set adalah atestasi dari emitor tata kelola (chain 1, keccak256("rand-bridge-governance")), harus membawa new_index == current + 1, dan harus ditandatangani oleh set saat ini, bukan set yang sudah digantikan; set yang keluar mempertahankan jendela tenggang 86.400 detik untuk transfer yang sedang berjalan. (Itu adalah set ECDSA. Set pasca-kuantum dan pause key dirotasi melalui aksinya masing-masing, yang di chain 20 menunggu tundaan satu hari: di bawah.) Jika n·2/3 + 1 guardian berkolusi, mereka dapat mencetak nilai sembarang; Pengerasan bridge membatasi seberapa banyak dan seberapa cepat, dan tidak menghilangkan risiko itu.
Pengikatan transaksi. Transaksi Rand tidak membawa tanda tangan: sebuah burn diotorisasi oleh bukti STARK dari bundle-nya. Sejak chain 14, setiap bukti bundle dibuat atas, dan diverifikasi terhadap, Transaction::binding, yaitu hash dari seluruh transaksi (id chain, satu bundle beserta empat envelope-nya, aksi) dengan hanya byte bukti yang dikosongkan. Sejak chain 17 sebuah bundle membawa dua bukti, bukti bundle dan bukti otorisasi pembelanjaan: binding mengosongkan keduanya, mempertahankan auth_commit milik bundle, dan keduanya diverifikasi terhadapnya. Salinan burn yang tertunda dengan to, relayer_fee, atau (to_chain, token) yang diubah, atau attest dengan time atau envelope yang diubah, gagal pada pemeriksaan bukti (TxError::InvalidBundleProof). Di chain 20 binding juga membawa hash genesis (genesis binding_domain: 1: rand-tx-bind-2 atas hash genesis, id chain, bundle, dan aksi), sehingga bukti yang dibuat untuk chain lain yang memakai id chain yang sama juga tidak lolos verifikasi. Wallet dan relayer harus menjalankan binary yang dibangun untuk chain tujuan pengiriman: v0.6.8 di chain 20 (v0.6.7 di chain 18 dan 19); rand yang lebih lama menghasilkan bukti dan tanda tangan yang ditolak chain 20.
Daemon guardian dan relayer bukan bagian dari full node; full node membawa codec bersama dan separuh sisi penerima.
Format wire
Setiap bilangan bulat berformat big-endian; versi wire adalah 1.
Envelope atestasi
| offset | len | field |
|---|---|---|
| 0 | 1 | version = 1 |
| 1 | 4 | guardian_set_index |
| 5 | 1 | n_sigs |
| 6 | 66·n_sigs | tanda tangan: index (1), r (32), s (32), v (1) |
6 + 66·n_sigs | … | body |
Header body: 51 byte, lalu payload
| offset | len | field |
|---|---|---|
| 0 | 4 | timestamp |
| 4 | 4 | nonce |
| 8 | 2 | emitter_chain |
| 10 | 32 | emitter_address |
| 42 | 8 | sequence |
| 50 | 1 | consistency_level |
| 51 | … | payload |
Payload Transfer: id 1, 133 byte
| offset | len | field |
|---|---|---|
| 0 | 1 | id = 1 |
| 1 | 32 | amount (u256; Rand memakai 128 bit terendah, dan sebuah deposit juga harus muat dalam u64 milik catatan) |
| 33 | 32 | token_address |
| 65 | 2 | token_chain |
| 67 | 32 | to: hash penerima, lihat di bawah |
| 99 | 2 | to_chain |
| 101 | 32 | fee (u256): bagian amount untuk relayer |
Payload GuardianSetUpgrade: id 2: id (1), new_index (4), n (1), lalu n kunci berukuran 20 byte.
Alamat Ethereum, BSC, dan Tron berukuran 20 byte yang diberi padding nol 12 byte di kiri dalam slot 32 byte; alamat Solana mengisi penuh slot tersebut. Sebuah Transfer harus berasal dari emitor terdaftar milik chain untuk emitter_chain, dan token_chain harus sama dengan chain pengirim. Sebuah atestasi berukuran paling besar 16 KiB. Tidak ada satu pun dari pengerasan bridge yang mengubah format wire, codec, atau endpoint chain sumber yang telah di-deploy: ko-tanda tangan pasca-kuantum dan pesan tata kelola dibawa dalam transaksi Rand, bukan dalam atestasi.
Penerima 32 byte
Alamat terlindung berukuran 1.216 byte, sedangkan wire hanya memiliki ruang 32. Field to membawa hash darinya:
to = blake3("rand-shielded-recipient" || pk_bytes || kem_ek) // ShieldedAddress::recipient_hash
Penyetor menyebutkan hash itu di chain sumber, guardian menandatanganinya, pengirim memasukkan alamat lengkap ke dalam aksi BridgeAttest, dan ledger menghitung ulang hash tersebut lalu menolak jika tidak cocok; jika tidak, pengirim dapat memilih siapa yang menerima deposit orang lain. Front end chain sumber harus menerima bentuk ini; wallet mencetaknya. Chain 11 sempat menjadikan slot itu sebagai id penerima secara langsung; bentuk alamat tersebut dikembalikan pada chain 12 (Akun dan alamat).
Satu token, banyak backing: zUSD
Registry aset tidak lagi menjadi milik bridge. Registry itu dipindahkan ke registry token RPL tingkat ledger, yang dipakai bersama oleh token native dan token bridged, dan state root chain mengomitmennya sebagai tokens_root. Token bridged adalah entri registry yang otoritas mint-nya Bridge { backings }, dan ia tidak lagi sesuai dengan satu pasangan (chain, token). Yang pertama, zUSD, didukung oleh USDT dan USDC yang dikunci di empat chain: Ethereum, Binance Smart Chain, dan Solana membawa kedua koin, sedangkan Tron hanya USDT (Circle menghentikan USDC di Tron):
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
Di chain 20 zUSD adalah indeks 1, rpl1xtj6k2x8strx8c2d5fjs50ltztck5ykms4ve7nmzmstf6fhn0v0spelqtx, bernama “Shielded USD”, 8 desimal, tercantum dalam file genesis, seperti pada chain 15 sampai 19, dengan nama, simbol, dan salt yang sama seperti di chain 14, sehingga id asetnya (32e5ab28c782c663e14da2650a3feb12f16a12db85599f4f62dc169d26f37b1f) dan id rpl1…-nya terbawa tanpa berubah (explorer: GET /tokens/1). Di chain 14 ia didaftarkan pada tinggi 256 oleh transaksi register_bridged_token.
Backing { chain: u16, token: [u8; 32], decimals: u8, locked: u64, minted_today: u64, mint_day: u32 }
Sebuah pasangan (chain, token) mendukung paling banyak satu token di seluruh registry (TokenError::BackingTaken); sebuah token memiliki 1 sampai 32 backing; decimals adalah jumlah desimal milik koin sumber itu sendiri, 0 sampai 18, bukan 8 milik token bridged. Backing yang dipool adalah risiko yang dinyatakan: ketujuh koin dapat saling ditukar dengan zUSD pada paritas, sehingga depeg pada salah satunya mengencerkan setiap pemegang zUSD, dan tidak ada batas di sisi Rand untuk porsi satu backing.
zUSD adalah sebuah keranjang (basket). Pemegang menanggung risiko dari setiap backing yang terdaftar, bukan hanya dari koin yang ia setorkan: satu zUSD adalah klaim atas pool, pada backing mana pun yang memiliki locked yang cukup. Tidak ada mekanisme yang menetapkan harga sebuah backing. Penguncian 1 unit dari koin terdaftar mana pun mencetak 1 zUSD, dan sebuah burn boleh menyebut backing mana pun yang locked-nya mencukupi, sehingga koin yang telah kehilangan peg dapat disetor pada nilai par dan zUSD-nya dibakar ke koin yang tetap mempertahankan peg, sampai backing yang sehat terkuras. Kerugian dibatasi oleh jendela pencetakan bergulir (rolling mint window) dan oleh kustodi yang benar-benar terkunci: pada chain 20 paling banyak 4.000 zUSD untuk seluruh backing, dan 100.000 per backing, yang dapat dicetak dalam 24 jam (rules v2), dan tidak ada yang dapat keluar melalui backing yang sehat melebihi yang dimiliki backing tersebut. Jendela ini membatasi lajunya, bukan totalnya: jendela ini memberi waktu untuk menjeda pencetakan, yang menghentikan setoran baru dari koin yang lemah sementara burn tetap terbuka. Tidak ada batas penebusan per backing dan tidak ada jeda per backing. Pada chain 14, sebelum rules v2, pauser milik endpoint sumber sendiri dan batas laju per token adalah satu-satunya pembatas.
Invarian: total_supply == Σ backings.locked. TokenRegistry::lock (sebuah BridgeAttest) menambahkan jumlah yang diatestasi ke locked milik backing dan ke total_supply milik token dalam satu pemanggilan; TokenRegistry::release (sebuah BridgeBurn) mengurangi kedua field tersebut sebesar jumlah yang dirilis. Tidak ada hal lain yang menulis kedua field itu, dan leaf token mengikat locked setiap backing ke dalam tokens_root, sehingga dua node yang berbeda pendapat tentang salah satunya akan menyimpang pada state root. Endpoint sumber tidak memungut biaya pada saat lock dan mengurangi seluruh jumlah yang diatestasi pada saat release, sehingga locked setiap backing sama dengan kustodi endpoint tersebut, kecuali untuk pesan yang sedang dalam perjalanan. Biaya pada chain 20 tidak melanggar hal ini: biaya adalah sebuah note zUSD yang tetap diterbitkan dan di-back, sehingga setoran tetap mengunci jumlah bruto dan burn hanya merilis amount − fee. Pada chain 20, potongan 10 bps milik endpoint sendiri atas USDT pada saat release telah diatur ke 0 pada saat cut; biaya kini dipungut di Rand.
Jumlah dinyatakan dalam unit 8 desimal pada wire, berapa pun desimal koin sumbernya: setoran 1 USDT dari Ethereum (6 desimal) terkunci sebagai 100_000_000 unit. Hanya indeks 0, RAND, yang memiliki sembilan desimal milik chain ini.
Sebuah token harus terdaftar sebelum setoran pertamanya. Tidak ada lagi pendaftaran pada kemunculan pertama: atestasi yang menyebut (chain, token) yang belum didaftarkan siapa pun ditolak dengan BridgeError::UnlistedToken (token … of chain … is not listed on this chain). Dana tetap berada dalam kustodi endpoint sumber sampai kuorum guardian pasca-kuantum mendaftarkan koin tersebut (Listing after genesis).
asset_id = blake3("rand-bridge-asset" || token_chain (2 bytes BE) || token_address (32 bytes))
rand_bridgeAssetId(token_chain, token_address) tetap menghitung nilai ini pada chain mana pun, dan inilah yang menjadi tujuan resolusi (token_chain, token_address) dalam payload Transfer, tetapi nilai ini bukan lagi kunci registry. Yang dibawa oleh sebuah note dan sebuah wallet adalah indeks token, sebuah u32 padat: indeks 0 adalah RAND dan tidak pernah ada dalam registry. rand_getAssets memiliki satu baris per backing, dengan setiap baris dari satu token berada di bawah indeks yang sama:
[{ "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 dan minted_today adalah string desimal dalam unit 8 desimal, seperti setiap jumlah u64 yang disajikan RPC sejak chain 14; index, chain, decimals dan mint_day tetap berupa angka. Metode tingkat token — rand_getTokens, rand_getToken, rand_getTokenSupply — mengembalikan token yang dijembatani sebagai satu baris yang memuat semua backing-nya (RPL tokens). GET /bridge/tokens milik explorer adalah allowlist statis dari koin yang diterima bridge.
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
Dibawa oleh transaksi shielded biasa yang satu bundle aset tersembunyinya hanya membayar basis 0,001 RAND dan tidak lebih: relayer-nya membayar untuk penyetor yang belum memegang RAND. Chain, bukan pengirim, yang menghitung note setoran { pk: recipient.pk, from: 0, amount, asset: <index>, time, r } dari jumlah yang ditandatangani guardian, sehingga relayer tidak dapat menggelembungkan atau mengalihkan pencetakan; komitmennya adalah satu-satunya leaf yang tidak dibawa transaksi pada wire. Pada chain 20, amount pada note adalah jumlah bruto dikurangi biaya pencetakan, dan chain menambahkan note kedua, yaitu biaya, ke treasury (Fees on mint and burn).
Blinding diturunkan, bukan dipilih. r harus
r = blake3("rand-deposit-r-1" || mu) // read as eight little-endian words; mu = keccak256(keccak256(body))
yaitu digest yang ditandatangani kuorum guardian; selain itu adalah BridgeError::WrongDepositBlinding, ditolak sebelum pekerjaan tanda tangan apa pun. Field ini tetap ada pada wire — rebuild field publik milik wallet membacanya dari sana — field ini hanya dibatasi, dan aturan ini mencakup attest dari sebuah rotasi juga. Sebelum aturan ini, siapa pun yang melihat transaksi tertunda milik relayer dapat mengirimkan atestasi yang sama dengan blinding miliknya sendiri: penerima tetap mendapat note, tetapi transaksi relayer gugur setelah ia membayar sebuah proof. Dengan r yang tetap, dua pengirim atestasi yang sama pada time yang sama menamai satu note yang sama dan pool mengonflikkan keduanya pada komitmen.
time dan asset adalah dua prediksi penyetor, dibuat agar envelope dapat disegel sebelum transaksi ada. time dibatasi pada jendela yang sama dengan milik sebuah bundle (1.024 blok pada chain 20, dari proof_window_blocks genesis; 256 sebelumnya), dan sengaja tidak diturunkan: tidak ada aturan yang memberi time yang disepakati setiap pengirim, sehingga front-runner masih dapat memilih time lain dalam jendela — note lain dengan penerima, jumlah dan aset yang sama, yang ditemukan wallet penerima dengan cara yang sama — dan kedua pengiriman itu bertabrakan pada digest atestasi sebagai gantinya. asset harus sama dengan indeks yang telah diberikan registry kepada (chain, token), jika tidak maka TxError::AttestAssetMismatch.
fee pada payload Transfer dibawa dan dibayarkan kepada siapa pun tidak: tidak ada identitas pengirim untuk dibayar, sehingga setoran adalah jumlah bruto, dikurangi hanya biaya pencetakan milik chain sendiri bila genesis menetapkannya (chain 20). Perlindungan replay dilakukan melalui digest: setiap mu yang telah dikonsumsi masuk ke himpunan spent milik bridge, dan mempool menolak relayer kedua yang berpacu pada atestasi yang sama. Setoran juga dibatasi oleh batas pencetakan backing, pada chain 20 juga oleh batas global, dan ditolak selama pencetakan dijeda (B1, rules v2). Kedua batas menghitung jumlah bruto. Pada chain yang genesis-nya menetapkan replay floor, termasuk chain 20, transfer yang sequence-nya di bawah floor chain sumbernya ditolak dengan BelowReplayFloor sebelum tanda tangan apa pun diperiksa (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 wajib: kuorum tanda tangan bersama Dilithium2 milik guardian sebagai [{"index":0,"signature":"<4840 hex>"},…]. Sebelum proving apa pun, wallet mendekode atestasi, memeriksa kuorum terhadap pq_guardians milik node dan id chain, memeriksa bahwa penerima di-hash menjadi penerima yang diatestasi, dan membaca indeks yang terdaftar; kemudian wallet menurunkan r, menyegel envelope penerima (untuk jumlah neto pada chain dengan biaya) dan membayar dengan satu bundle RAND miliknya sendiri. bridge-rotate mengirimkan rotasi set guardian (payload 2), yang tidak menyetorkan apa pun, dan menolak apa pun yang tidak menaikkan set Rand sebanyak satu. Karena setiap field dari note setoran bersifat publik dalam satu transaksi itu, wallet penerima membangun ulang note setoran yang dialamatkan kepadanya dengan menelusuri transaksi bridge_attest yang telah di-commit, tanpa membuka envelope — relayer yang jahat dapat menyegel sampah tetapi tidak dapat menyembunyikan note tersebut.
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 }
}
Sejak bundle aset tersembunyi chain 14, sebuah burn adalah satu bundle (sebelumnya dua): slot 0–1 membuktikan bahwa pembakar memiliki note dari aset tersebut yang berjumlah sedikitnya amount, dengan burn_a sebagai nilai yang keluar dari pool, dan slot 2–3 membayar biaya dalam RAND. Biayanya adalah BRIDGE_BURN_FEE, 0,01 RAND, yang mencakup basis bundle. relayer_fee adalah bagian dari amount (fee ≤ amount) yang dipotong pada chain tujuan oleh kontrak release, bukan biaya tambahan, sehingga burn_a tepat sebesar amount. Pada chain 20 chain juga menyimpan biaya burn sebesar 10 bps dari amount sebagai note zUSD kepada treasury, dan release adalah amount − fee; bagian relayer harus muat di dalam release tersebut (Fees on mint and burn).
Sebuah burn menyebut koinnya. (to_chain, token) harus berupa backing dari aset yang dibakar (TokenError::NotABacking jika tidak), dan dua aturan diperiksa pada langkah action, sebelum proof bundle:
- Unit release. Backing dengan desimal sumber
d < 8merilis dalam unit10^(8−d)(100 untuk koin 6 desimal; 1 untuk koin 18 desimal). Jumlah yang dirilis danrelayer_feemasing-masing harus merupakan kelipatan bulat, jika tidakTokenError::NotReleasable(… is not a multiple of the release unit …): pecahan di bawah unit terkecil koin sumber akan terdampar dalam kustodi. - Kustodi backing itu sendiri. Jumlah yang dirilis
≤ lockeduntuk backing tersebut, jika tidakTokenError::InsufficientBacking(only … is locked in that backing, not the … asked for). Total supply token mungkin mencukupi release; yang harus mencukupi adalah kustodi satu backing ini.
Jumlah yang dirilis adalah amount pada chain tanpa biaya dan amount − fee pada chain 20.
to harus berbentuk benar (20 byte berpadding nol pada chain 2, 3 dan 4; tidak pernah seluruhnya nol). Ledger mencatat sebuah BridgeBurnRecord di bawah burn_sequence yang naik secara ketat, dengan hash transaksi pada slot pengirim, dan guardian membaca log burn:
→ {"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 adalah pesan keluar persis seperti yang harus di-hash dan ditandatangani oleh guardian; jumlah Transfer-nya adalah release_amount. Catatan di atas adalah burn pertama pada chain 20: 0,999 zUSD dibakar menuju USDT BSC pada ketinggian 2.899, yang 0,000999 di antaranya masuk ke treasury dan 0,998001 dirilis.
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
Wallet memeriksa terlebih dahulu — dari satu kali pembacaan status bridge dan registri token, sebelum pembuktian apa pun — bahwa chain memiliki bridge, bahwa ASSET terdaftar dalam registri, bahwa TOKEN pada TO_CHAIN menjadi penopangnya dan memiliki sedikitnya AMOUNT yang terkunci, serta bahwa AMOUNT dan biaya relayer merupakan satuan rilis utuh. Pada chain yang menerapkan biaya, wallet kemudian menampilkan biaya bridge dan jumlah yang akan dirilis, lalu meminta konfirmasi sebelum pembuktian (--yes melewati pertanyaan tersebut). Satu burn kemudian membuktikan satu bundel: sekitar 100 detik pada laptop dan sekitar 1,5 MB bukti, dan pada chain 20, seperti untuk setiap bundel sejak chain 17, disertai bukti otorisasi pembelanjaan (beberapa detik, sekitar 1,37 MB).
Biaya pada mint dan burn
Baru pada fullnode v0.6.8 dan ditetapkan pada chain 20 oleh grup genesis
"bridge": { …, "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" } }
chain mengenakan biaya pada setiap mint yang dijembatani dan setiap burn yang dijembatani, dalam token yang dijembatani, di Rand, sebagai note shielded kepada alamat treasury recipient. Setiap tarif paling tinggi 100 bps (1 %); pada chain 20 keduanya 10 bps, 0,1 %. Biayanya adalah ⌊amount · bps / 10⁴⌋ yang dibulatkan ke bawah menjadi satuan rilis utuh dari penopangnya (10^(8 − source decimals): 100 satuan untuk USDT dengan 6 desimal, 1 untuk koin dengan 18 desimal), sehingga biaya dan sisanya sama-sama merupakan satuan utuh, dan biaya yang dibulatkan menjadi nol tidak menghasilkan note (ledger/bridge_notes.rs, bridge_fee).
deposit (BridgeAttest) sebesar bruto g | burn (BridgeBurn) sebesar amount | |
|---|---|---|
| biaya | ⌊g · mint_bps / 10⁴⌋, dibulatkan ke bawah ke satuan rilis | ⌊amount · burn_bps / 10⁴⌋, dibulatkan ke bawah ke satuan rilis |
| sisi pengguna | note deposit sebesar g − fee | amount dibakar (burn_a == amount, tanpa kembalian guest); isi pesan keluar mengatestasi release_amount = amount − fee, dan endpoint merilis tepat jumlah itu |
| note treasury | fee, blinding blake3("rand-bridge-fee-r-1" ‖ mu), pada time dan aset deposit | fee, blinding blake3("rand-bridge-burn-fee-r-1" ‖ txid), pada time bundel |
locked dan total_supply | naik sebesar g | turun sebesar release_amount |
| diperiksa terhadap | batas mint per penopang dan global, pada g | InsufficientBacking dan NotReleasable pada release_amount; porsi relayer harus muat di dalamnya (FeeExceedsAmount) |
Kedua note diterbitkan dan ditopang, sehingga total_supply == Σ locked == custody tetap berlaku: note biaya adalah zUSD seperti yang lain, dan nilainya tetap terkunci di chain sumber hingga ada yang membakarnya. BRIDGE_BURN_FEE, yakni 0,01 RAND yang dibayar sebuah burn pada slot RAND-nya, tidak berubah dan terpisah.
Perjalanan pulang-pergi pertama chain 20, dibaca dari node: deposit 1 zUSD dari BSC pada ketinggian 1.664 (4f6e6c54…1a64) memberi penyetor 0,999 zUSD dan treasury 0,001; burn 0,999 zUSD kembali ke USDT BSC pada ketinggian 2.899 (urutan 13) menyisakan 0,000999 zUSD dan merilis 0,998001. Setelahnya (dibaca 2026-10-01) penopang USDT BSC menahan 22,001999 zUSD, dan suplai token, 99,001999 zUSD, sama dengan jumlah seluruh penopangnya.
Bagaimana treasury menemukan note-nya. Note biaya tidak memiliki amplop: setiap katanya bersifat publik (jumlah dari transaksi dan tarif genesis, aset dan time dari transaksi, blinding diturunkan, pemilik adalah recipient genesis). Wallet yang alamatnya adalah recipient membangun ulang note biaya dalam lintasan publik yang sama dengan yang membangun ulang note deposit, dengan membaca rand_getBridgeState.fees dan desimal para penopang sekali per sinkronisasi. Node yang berbohong tentang biaya hanya dapat membuat sebuah note tidak ditemukan, tidak pernah dapat mengkreditkannya. Wallet penyetor menilai depositnya sendiri dengan cara yang sama, bruto dikurangi biaya. Hanya wallet v0.6.8 yang melakukan ini; wallet yang lebih lama bagaimanapun tidak dapat mengirim pada chain 20.
Tanpa grup tersebut, chain 14 hingga 19, tidak ada biaya: deposit mengkreditkan jumlah bruto dan burn merilis amount. Hingga chain 20, satu-satunya biaya bridge adalah pungutan 10 bps USDT oleh endpoint sumber sendiri saat rilis, di chain host; pungutan itu ditetapkan menjadi 0 pada pemotongan chain 20. Grup tersebut di-commit ke hash genesis dan dilebur ke dalam akar bridge di bawah rand-bridge-fees-1. docs/bridge.md §25 pada fullnode memuat penyandiannya.
Penguatan bridge
locked per penopang dan InsufficientBacking menjaga penebusan. Keduanya tidak membatasi pencetakan: atestasi dengan kuorum guardian yang valid mencetak sebesar yang tertulis di dalamnya. Tiga lapis menutup celah itu dan lapis keempat memungkinkan sebuah token didaftarkan tanpa pemotongan chain. Tidak satu pun mengubah endpoint chain sumber, format kawat atestasi, atau bridge-codec.
B1: batas mint per penopang, dan jeda mint
tokens.mint_cap_per_day pada genesis berlaku per penopang: chain 20 menetapkan 10000000000000, yaitu 100.000 zUSD per penopang per hari, seperti setiap chain sejak 14. Tanpa rules v2 (chain 14), satu hari adalah timestamp_ms / 86_400_000 dari stempel waktu blok; di bawah rules v2 (chain 15 hingga 20) batas yang sama diukur pada jendela bergulir sepanjang cap_window_secs waktu blok, dan batas global seregistri — 4.000 zUSD per 86.400 detik pada chain 20 — adalah batas yang lebih ketat (rules v2). Deposit yang melewati batas ditolak dengan TokenError::MintCapExceeded sebelum pekerjaan tanda tangan guardian, dan dapat diterima kembali setelah jendela kembali memiliki ruang.
bridge.pause_key pada genesis adalah satu kunci Dilithium2, terpisah dari setiap guardian pascakuantum. Dua aksi tanpa bundel dan tanpa biaya:
PauseMints { nonce, signature }— ditandatangani oleh kunci jeda saja. Kunci ini hanya dapat menjeda.UnpauseMints { nonce, pq_signatures }— memerlukan kuorum guardian pascakuantum, tidak pernah kunci jeda.
Keduanya membawa dan menaikkan pause_nonce, sehingga tidak ada yang dapat diputar ulang. Selama dijeda, setiap BridgeAttest transfer ditolak dengan BridgeError::MintsPaused; burn dan rotasi set guardian tetap terbuka, sehingga jeda tidak pernah menjebak penebusan. Di bawah rules v2 (chain 15 hingga 20), pendaftaran juga ditolak selama jeda. Kunci jeda dimaksudkan untuk disimpan pada mesin yang berbeda dari kunci guardian.
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]
Cakupan jeda: daemon guardian yang diperdaya atau diretas untuk ikut menandatangani mint palsu, sementara pemegang kuorum pascakuantum tidak. Jeda bukan pertahanan terhadap pencurian kuorum tersebut, yang dapat membuka jeda, mendaftarkan hingga 32 penopang pada satu token, masing-masing dengan batas hariannya sendiri, dan di bawah rules v2 mengganti set pascakuantum serta kunci jeda.
B2: stempel waktu blok yang dibatasi
Stempel waktu blok pada chain yang dijembatani menentukan kedaluwarsa set guardian dan hari batas mint, sehingga leader tidak boleh dapat melompatkannya. Pada chain dengan bridge:
- Aturan validitas, yang diperiksa juga saat replay:
timestamp_ms ≤ parent.timestamp_ms + 60_000, jika tidakBlockError::TimestampLeap. Bersama aturan mundur yang sudah ada (BlockError::TimestampRewind), waktu blok hanya bergerak maju, paling banyak satu menit per blok. - Aturan suara, bukan aturan replay: validator menahan suaranya dari blok yang lebih dari 15.000 ms mendahului jamnya sendiri.
Karena itu setiap validator pada chain yang dijembatani memerlukan NTP: selisih lebih dari 15 detik berarti leader yang salah dan pemberi suara yang salah. Saat chain mengejar ketertinggalan setelah berhenti, waktu blok dapat maju hingga 60 detik per blok, sehingga hari batas mint dapat berganti lebih cepat daripada hari sebenarnya — paling banyak satu hari tambahan batas per hari penuh keterlambatan yang menumpuk.
B3: tanda tangan bersama Dilithium2 pada setiap mint
Setiap BridgeAttest, baik deposit maupun rotasi, harus membawa di samping kuorum ECDSA-nya sebuah kuorum tanda tangan Dilithium2 atas
M = "rand-bridge-pq-cosign-1" || rand_chain_id (u64 BE) || mu // 63 bytes
mu adalah digest yang ditandatangani kuorum ECDSA; id chain mencegah tanda tangan bersama testnet diverifikasi pada chain Rand lain. Tanda tangan dibawa dalam pq_signatures milik aksi, di dalam pengikatan transaksi, sehingga tidak dapat dilucuti atau ditukar pada salinan. Diperiksa yang murah sebelum yang mahal: quorum(n) ≤ len ≤ n (PqNoQuorum), indeks naik secara ketat (PqIndexOrder), setiap indeks < n (PqIndexOutOfRange), setiap tanda tangan tepat 2.420 byte (PqBadSignatureLength), dan terakhir bahwa masing-masing terverifikasi di bawah pq_guardians[index] (PqBadSignature). Kedua kuorum dihitung secara terpisah. bridge.pq_guardians pada genesis sejajar indeks dengan set ECDSA genesis (set 1 pada chain 20). Rotasi payload-2 hanya memindahkan set ECDSA; tanpa rules v2 set pascakuantum tetap selama hidup chain, dan di bawah rules v2 hanya RotatePqGuardians yang menggantinya (pada chain 20 bentuk V2-nya, dengan bukti kepemilikan dan penundaan satu hari). Chain 20 memiliki delapan, delapan kunci yang sama seperti chain 15 hingga 19; sebuah mint memerlukan enam di antaranya (chain 14 memiliki enam dan memerlukan lima).
Co-signature ini masih hanya mengikat chain id, termasuk pada chain 20: berbeda dengan pesan tata kelola di bawah, co-signature ini tidak dipindahkan ke balik genesis hash, karena daemon guardian yang membuatnya berada di repositori lain dan belum berubah. Salinan dengan id yang sama dari sebuah chain yang juga menyalin set guardian akan menerima co-signature deposit yang dibuat untuk chain aslinya; fee bundle dari transaksi deposit itu sendiri terikat pada genesis di chain 20, sedangkan attestation di dalamnya tidak.
B4: mendaftarkan token bridged setelah genesis
Alamat biasa tidak pernah dapat membuat token berotoritas Bridge. Ada dua aksi, masing-masing dengan fee bundle RAND biasa dan masing-masing di bawah kuorum pasca-kuantum:
RegisterBridgedToken { name, symbol, salt, chain, token, decimals, nonce, pq_signatures }— token bridged baru pada indeks berikutnya, dengan 8 desimal di Rand, di bawah batas mint genesis, beserta backing pertamanya. Fee: base bundle ditambahregistration_feedari registry (1 RAND di chain 20, sehingga 1,001 RAND; chain 20 membakar registration fee alih-alih membayarkannya kepada proposer).ListBacking { token_index, chain, token, decimals, nonce, pq_signatures }— backing tambahan pada token bridged yang sudah terdaftar, dimulai darilocked = 0. Fee: base bundle.
Keduanya membawa dan menaikkan list_nonce (jika tidak, BadListNonce { expected, got }) dan menjalankan pemeriksaan yang sama: chain tersebut memiliki emitter terdaftar (BridgeError::NoEmitter), (chain, token) belum menjadi backing apa pun, kurang dari 32 backing, 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>
Dua aturan urutan. Para guardian menandatangani pendaftaran terlebih dahulu, menunggu hingga ter-commit, membaca kembali indeks yang ditetapkan, dan baru kemudian menandatangani listing yang menyebutkan indeks itu — mendaftarkan token native bersifat permissionless, sehingga listing yang ditandatangani lebih awal dapat mendapati indeksnya sudah diambil. Dan sebuah koin didaftarkan di Rand sebelum setToken mengaktifkannya pada endpoint sumber: jika dibalik, sebuah deposit dapat terkunci sebelum Rand memiliki backing untuk dikreditkan, dan attestation-nya akan ditolak dengan UnlistedToken sampai Rand menyusul.
Pesan tata kelola
Byte tetap, tidak pernah bincode; semua bilangan bulat 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 membawa pause_nonce, M_list/M_register membawa list_nonce, dan M_rotate_pq/M_rotate_pause membawa rotation_nonce. count adalah jumlah kunci, bukan panjang dalam byte; setiap kunci adalah 1.312 byte mentah dari kunci publik Dilithium2, dalam urutan yang akan dimiliki set baru.
Di chain 20 setiap pesan terikat pada genesis hash (genesis binding_domain: 1): digit versi pada tag menjadi 2 dan genesis hash sepanjang 32 byte berada di antara tag dan chain id, sedangkan bagian ekornya tidak berubah — M'_pause = "rand-bridge-pause-2" || genesis [32] || chain_id u64 || nonce u64, dan demikian pula rand-bridge-pq-unpause-2, rand-bridge-pq-list-2, rand-bridge-pq-register-2, rand-bridge-pq-rotate-pq-2 dan rand-bridge-pq-rotate-pause-2 (bridge/gov.rs). Kunci pause atau kuorum yang menandatangani untuk satu chain tidak akan menandatangani untuk chain lain mana pun yang memakai chain id dan kunci yang sama. Berkas yang ditandatangani dalam bentuk …-1 ditolak di chain 20 (BadPauseSignature, PqBadSignature). Chain 14 hingga 19 memakai tata letak …-1 di atas, byte demi byte.
Rules v2: rotasi, batas bergulir, tanpa listing saat dijeda
Grup opsional di dalam bagian bridge pada genesis. Chain 14 tidak memilikinya; setiap chain sejak chain 15 memilikinya, dan chain 20 dengan nilai yang sama:
"bridge": { …, "rules_v2": { "global_mint_cap_per_window": 400000000000, "cap_window_secs": 86400 } }
- Dua rotasi di bawah kuorum pasca-kuantum.
RotatePqGuardians { new_pq_guardians, nonce, pq_signatures }mengganti seluruh set pasca-kuantum;RotatePauseKey { new_pause_key, nonce, pq_signatures }mengganti kunci pause. Keduanya tanpa bundle dan tanpa fee sepertiPauseMints, keduanya ditandatangani oleh set pasca-kuantum saat ini, dan keduanya membawa serta menaikkanrotation_nonce, sehingga satu kuorum yang ditandatangani hanya mengotorisasi tepat satu rotasi. Set baru harus memiliki panjang yang sama dengan set ECDSA saat ini, kunci sepanjang 1.312 byte, tanpa duplikat, dan bukan kunci pause. Tidak ada masa tenggang: set atau kunci yang digantikan tidak menandatangani apa pun lagi. Di chain 20 aksi v1 ini ditolak, dan sebuah rotasi harus membuktikan kepemilikan atas kunci-kunci baru serta menunggu satu hari (di bawah). - Batas bergulir.
mint_cap_per_dayper backing diukur pada jendela bergulircap_window_secs, bukan hari kalender, dan batas kedua yang berlaku untuk seluruh registry,global_mint_cap_per_window, mencakup semua backing dari semua token sekaligus (TokenError::GlobalMintCapExceeded). Di chain 20 nilainya 400.000.000.000 unit, 4.000 zUSD per 86.400 s. Pada aturan hari kalender, batas pada pukul 23:59:59 dan lagi pada pukul 00:00:01 berarti dua kali batas dalam dua detik; pada jendela bergulir, detik kedua ditolak. - Tanpa listing saat dijeda.
RegisterBridgedTokendanListBackingditolak denganMintsPausedselamamint_paused. Burn dan kedua rotasi tetap terbuka.
Rotasi: kepemilikan dan penundaan satu hari
Rules v2 meninggalkan tiga hal yang belum terjawab: tidak ada yang membuktikan bahwa kunci baru dipegang oleh siapa pun (salah ketik, atau kunci yang tidak dipegang siapa pun, akan melumpuhkan kuorum pasca-kuantum untuk selamanya), sebuah rotasi berlaku pada blok tempat ia masuk, dan tidak ada yang dapat menghentikan rotasi yang bermusuhan. Grup opsional kedua di samping rules_v2 (yang disyaratkannya), baru di v0.6.8 (audit v6, BRG-14), menutup ketiganya. Chain 20 mengaktifkannya:
"bridge": { …, "rotation": { "delay_secs": 86400, "needs_possession": true } }
- Kepemilikan. Sebuah rotasi harus berupa
RotatePqGuardiansV2 { new_pq_guardians, possession, nonce, pq_signatures }atauRotatePauseKeyV2 { new_pause_key, possession, nonce, pq_signatures }:possession[i]adalah tanda tangan Dilithium2 milik kunci baru ke-iatas pesan yang persis sama dengan yang ditandatangani kuorum (M_rotate_pqatauM_rotate_pause, dalam bentuk…-2di chain 20), satu untuk setiap kunci menurut urutan kunci, masing-masing 2.420 byte. Aksi v1 ditolak denganPossessionRequired. Jumlah atau panjang yang salah ditolak sebelum kuorum diverifikasi; tanda tangan kepemilikan yang buruk (BadPossession { index }) setelahnya. - Penundaan. Rotasi yang diterima dicatat sebagai tertunda, dengan
effective_at_secs = block time + delay_secs, dan berlaku pada akhir blok pertama yang timestamp-nya mencapai nilai itu. Di chain 20 itu 86.400 s, satu hari. Sampai saat itu set lama ikut menandatangani setiap mint dan kunci pause lama yang menjeda. Menerima rotasi menghabiskan saturotation_noncedan mengaktifkannya menghabiskan satu lagi, sehingga attestation mint yang dikumpulkan di bawah set lama keluar dari pool ketika set berubah. Satu rotasi dari setiap jenis dapat tertunda pada satu waktu (RotationPending { effective_at_secs }). - Pembatalan.
CancelRotation { kind, nonce, signature }(tanpa bundle, tanpa fee) menggugurkan rotasi tertunda dengankindtersebut (0 = set pasca-kuantum, 1 = kunci pause). Aksi ini ditandatangani oleh kunci pause saat ini, satu-satunya kunci yang dipegang terpisah dari kuorum, atas"rand-bridge-cancel-rotation-2" || genesis [32] || chain_id u64 || rotation_nonce u64 || kind u8di chain 20, dan menghabiskan nonce. Dengan demikian kuorum yang direbut tidak dapat merotasi set dalam waktu kurang dari sehari, dan pemegang kunci pause dapat menghentikannya dalam hari itu; kunci pause yang akan masuk tidak dapat membatalkan kedatangannya sendiri.
Penandatangannya adalah rand-node bridge-gov (rotate-pq, rotate-pause, cancel-rotation, masing-masing dengan langkah message, sign dan submit; submit memverifikasi ulang setiap baris kepemilikan sebelum mengirim). rand_getBridgeState menyajikan rotation_rules dan pending_rotations; keduanya null pada chain tanpa grup tersebut (chain 14 hingga 19). Rotasi set guardian ECDSA (payload 2) tidak terpengaruh: ia mempertahankan aturan dan masa tenggangnya sendiri. Belum ada rotasi yang dijalankan di chain 20.
Dibawa dari chain 14
Chain 15 dipotong dari posisi bridge chain 14 (emitter dan endpoint sumber yang sama), dan chain 16, 17 dan 18 masing-masing dipotong dari posisi pendahulunya dengan cara yang sama. Chain 19 dipotong dari posisi chain 18 dengan satu perubahan: emitter Ethereum, BSC dan Tron-nya adalah endpoint yang di-deploy ulang pada 2026-09-30 (0x7af6b17047c1db6cb54347fdea45cf9179075bfa untuk chain 2 dan 3, 0x6410797df959987a5baf65b5fab97edeb34d5163 untuk chain 4). Chain 18 tidak pernah memercayainya, sehingga tidak ada yang di-mint dari keduanya di sana. Chain 20 dipotong dari snapshot chain 19 pada tinggi 7.258, dengan bridge dihentikan terlebih dahulu, dan mempertahankan emitter, guardian, kunci pasca-kuantum, serta kunci pause chain 19 tanpa perubahan; yang ditambahkannya adalah grup fee dan rotasi. Emitter Solana dan emitter sisi Rand tidak berubah sejak chain 14. Apa yang dibawa genesis chain 20:
- Kustodian. 99 zUSD, berupa dua note zUSD genesis (10 dan 89 zUSD), dengan jaminan 22 USDT terkunci di Ethereum, 22 USDT di BSC, 30 USDT di Tron, dan 25 USDT di Solana, serta 0 pada setiap jaminan USDC: itulah
lockedchain 19 pada saat snapshot, yang sesuai dengan kustodian endpoint.total_supply == Σ lockedsama dengan kustodian endpoint pada blok 0. Riwayat: 10 zUSD yang tersisa di chain 14 merupakan satu note genesis pada chain 15 sampai 19; genesis chain 18 menyimpannya sebagai 9 USDT terkunci di Tron dan 1 di Solana, dan genesis chain 19 sebagai 10 di Solana setelah penyeimbangan ulang pada chain 18. Sisa 89 adalah setoran bersih chain 19, dikurangi pembakarannya. - Guardian set indeks 1, tempat endpoint sumber sudah berada; rotasi ECDSA berikutnya harus membawa indeks 2. Delapan kunci pascakuantum dan kunci jeda adalah milik chain 15, tidak berubah.
burn_sequence13: pembakaran chain 14 berakhir di 6, genesis chain 15 sampai 18 dimulai dari 7, chain 18 melakukan satu pembakaran (7) dan chain 19 dimulai dari 8. Chain 19 melakukan lima pembakaran, urutan 8 sampai 12, sehingga chain 20 dimulai dari 13, dan tidak ada endpoint maupun daemon yang berkunci pada urutan akan melihat satu urutan dua kali. Pembakaran pertama chain 20 mengambil urutan 13.- Replay. Mint dari chain sebelumnya masih akan lolos kuorum ECDSA, tetapi ko-tanda tangan pascakuantumnya mengikat id chain tersebut, sehingga ditolak di chain 20. Genesis chain 15 tidak memiliki batas bawah replay masuk (
min_inbound_sequencebernilainull), sehingga di sana perlindungan bertumpu pada ko-tanda tangan dan pada guardian yang tidak pernah ikut menandatangani lock lama untuk id chain yang baru. Sejak chain 16, genesis menetapkannya: per chain sumber,sequenceterendah yang boleh dibawa oleh atestasi transfer dari emitter chain tersebut. Milik chain 20 adalah{ "2": 3, "3": 3, "4": 3, "5": 7 }: urutan keluar berikutnya pada setiap endpoint ketika snapshot chain 19 diambil, dengan setiap lock sebelumnya telah dicetak di chain 19. (Milik chain 18 adalah{ "2": 2, "3": 2, "4": 2, "5": 2 }; milik chain 19 adalah{ "2": 1, "3": 1, "4": 1, "5": 4 }, dengan urutan 0 pada setiap endpoint yang dideploy ulang merupakan lock milik operator sendiri, yang tidak boleh dicetak.) Transfer di bawah batas bawah ditolak denganBelowReplayFloor, apa pun kuorum dan ko-tanda tangan yang dibawanya. Rotasi guardian set tidak dinilai olehnya. Batas bawah ini ditetapkan saat genesis, dikomit ke hash genesis, dan dibungkus ke dalam root bridge.
Urutan peluncuran di chain 14 (riwayat)
Cara bridge dijalankan di chain 14, sebuah testnet sebelumnya, pada September 2026:
- Membuat chain dengan bagian
bridge(termasukpq_guardiansdanpause_key) dan bagiantokensyang menetapkanregistration_feedanmint_cap_per_day, tanpa mendaftarkan token apa pun. Selesai 2026-09-20. - Mengaktifkan faucet agar dompet deployer dapat memperoleh RAND. Selesai.
- Deployer mendaftarkan zUSD dengan jaminan pertamanya di bawah tanda tangan kuorum atas
M_registerpadalist_nonce = 0. Selesai pada tinggi 256. - Para guardian membaca kembali indeksnya dan menandatangani enam pesan
ListBackingyang tersisa, dikirim satu per satu. Selesai:list_nonceterbaca 7. - Untuk setiap jaminan: daftarkan di Rand sebelum
setTokenpada endpoint sumber. - Rotasi guardian set (set 0 → set 1, ditandatangani 5 dari 6 oleh set saat ini) pada keempat endpoint sumber, lalu atestasi yang sama di Rand. Rand terbaca set 0 selama masa hidup chain 14; chain 15 dimulai pada set 1.
- Satu putaran langsung per chain sumber dengan jumlah kecil — bridge masuk, cetak zUSD, transfer, bakar kembali — dan rekonsiliasi
lockedsetiap jaminan (rand_getTokenSupply) terhadap kustodian endpoint.
Status publik
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 adalah kunci publik Dilithium2, masing-masing 1.312 byte dalam heksadesimal. Tidak ada lagi next_index: token yang di-bridge didaftarkan, dengan indeks yang sudah ditetapkan oleh registrasinya, sebelum dapat disetor, sehingga tidak ada indeks yang perlu diprediksi. rotation_nonce adalah nilai yang harus dibawa oleh M_rotate_pq/M_rotate_pause/M_cancel berikutnya; rules_v2 bernilai null pada chain tanpa grup tersebut (chain 14), dan min_inbound_sequence bernilai null pada chain tanpa batas bawah replay (chain 15 dan sebelumnya); pada chain 20 nilainya { "2": 3, "3": 3, "4": 3, "5": 7 }. rotation_rules, pending_rotations, dan fees bernilai null pada chain tanpa grup tersebut (chain 14 sampai 19). Contoh di atas menunjukkan bentuknya, bukan nilai chain 20: chain 20 dimulai pada guardian_set_index 1 dan burn_sequence 13, dan menyajikan "rotation_rules": { "delay_secs": 86400, "needs_possession": true }, "pending_rotations": [], dan "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" }. Chain tanpa bagian bridge menjawab {"enabled": false}.
Tidak ada saldo: nilai yang di-bridge berupa note, dan dompet membaca apa yang dipegangnya dengan rand asset-balance [INDEX] dari note miliknya sendiri. Root bridge mengomit para emitter, guardian set, digest yang telah dibelanjakan, urutan pembakaran, guardian set pascakuantum, serta kunci jeda, bendera jeda, dan nonce tata kelola (dua, dan rotation_nonce di bawah rules v2), batas bawah replay bila genesis menetapkannya, dan pada chain 20 aturan rotasi beserta rotasi yang tertunda dan grup biaya; root ini hanya dilebur ke dalam root state pada chain yang memiliki bridge. Registri token, dengan locked setiap jaminan, dikomit secara terpisah sebagai tokens_root. GET /bridge dan GET /bridge/assets milik explorer menyajikan state yang sama, yang kedua dengan total setoran dan pembakaran per jaminan sebagai string 8 desimal; GET /tokens dan GET /tokens/:id menyajikan registri.
Apa yang publik dan apa yang tersembunyi
| apa | siapa yang melihat |
|---|---|
| jumlah setoran, indeks aset, dan alamat penerima | semua orang, dalam satu transaksi itu |
| note mana yang kemudian dibelanjakan penerima | tidak seorang pun |
| jumlah, aset, koin, tujuan, dan biaya relayer sebuah pembakaran | semua orang — para guardian membutuhkannya |
| jumlah, aset, dan pemilik note biaya bridge, yaitu treasury (chain 20) | semua orang: note ini dibangun ulang dari bidang publik |
| note mana yang mendanai sebuah pembakaran | tidak seorang pun |
| saldo yang di-bridge | hanya pemegangnya, atau pemegang viewing key |
guardian set, emitter, registri dengan locked setiap jaminan, log pembakaran | semua orang |
Celah yang diketahui
Dari daftar milik fullnode sendiri: tidak ada relayer yang dibayar di Rand; envelope setoran tidak terikat pada apa pun, sehingga relayer yang bermusuhan dapat menyegel data sampah, itulah sebabnya dompet membangun ulang note setoran dari bidang publik; vektor uji bersama tingkat ledger belum dibangun ulang di atas pool note; bridge-burn baru mengetahui kebijakan endpoint tujuan setelah membayar sebuah bukti; timestamp blok yang sama dengan induknya diperbolehkan pada chain yang di-bridge, sehingga leader yang berkolusi dapat mempertahankan guardian set yang sudah digantikan di dalam masa tenggangnya; log pembakaran di memori tidak dibatasi; tidak ada light client atau verifikasi on-chain atas state chain sumber, dan hal itu pun tidak direncanakan.