Użyj tej strony, aby zbudować, wysłać lub zdekodować transakcję: kształt przesyłany przez rand_sendTransaction, sposób obliczania identyfikatora, pełna lista akcji wraz z regułami dla pakietów i opłat oraz kolejność dopuszczania, która decyduje o tym, jaki błąd otrzymasz jako pierwszy.
Transakcja
Transaction { chain_id: u64, bundle: Option<Bundle>, action: Action }
Bundle { // the hidden-asset bundle (since chain 14)
anchor: Word8, nullifiers: [Word8; 4], commitments: [Word8; 4],
fee: u64, burn_a: u64, burn_r: u64, burn_asset: u32, time: u32,
envelopes: [Envelope; 4], proof: Vec<u8>, // postcard(Proof) of the hidden-asset guest
auth_commit: Word8, auth_proof: Vec<u8>, // since chain 17: split authorisation's c and its auth proof
}
Envelope { kem_ct: Vec<u8>, to_receiver: Vec<u8>, to_sender: Vec<u8>, body: Vec<u8> }
Word8 to osiem słów u32 w porządku little-endian, zapisanych jako 64 małe znaki szesnastkowe. Postać przesyłana to bincode(Transaction) z domyślnymi ustawieniami bincode (liczby całkowite o stałej szerokości, 8-bajtowe prefiksy długości, znaczniki enum jako u32), a rand_sendTransaction przyjmuje ją jako hex. Nie ma podpisu pod transakcją ani pola nadawcy: pakiet autoryzuje się własnymi dowodami (dowodem pakietu oraz, od chain 17, dowodem autoryzacji obok niego), a akcja, która jest podpisana, nosi podpis w sobie. Ochronę przed powtórzeniem zapewniają: zbiór nullifierów dla pakietów, nonce każdego walidatora w rejestrze dla podpisanych akcji stakingu, mint_nonce każdego tokena dla TokenMint i SetAuthority oraz pause_nonce i list_nonce mostu dla jego akcji zarządczych.
Transakcja zawiera co najwyżej jeden pakiet, a każda akcja, która nie jest pozbawiona pakietu, zawiera dokładnie jeden. Sloty 0–1 pakietu przenoszą aktywo prywatne, a sloty 2–3 RAND, więc jeden dowód przenosi token i opłaca jego opłatę w RAND; dwupakietowe spalenie mostu z wcześniejszego projektu zniknęło: spalenie to jeden pakiet (Notatki).
Portfel rand buduje to wszystko przez randprotocol_client::wallet::{send, submit}: wybiera wejścia, pobiera kotwicę, oblicza świadectwa z własnej kopii drzewa portfela, buduje wyjścia i zapieczętowuje wszystkie cztery koperty, wyznacza powiązanie transakcji, dowodzi pakiet i dowód autoryzacji, sprawdza opublikowany skrót dowodu pakietu i wysyła.
Powiązanie transakcji
Transakcja nie ma podpisu, a dowód pakietu zobowiązuje się tylko do skrótu samego pakietu: kotwicy, nullifierów, zobowiązań, opłaty, pól spalenia i time. Do chain 13 nic nie wiązało udowodnionego pakietu z akcją, z którą podróżował, ani z jego kopertami, więc każdy, kto zobaczył transakcję, mógł zachować wszystkie bajty dowodu i zmienić resztę: cel BridgeBurn, walidatora w Bond, kopertę. Od chain 14 każdy dowód pakietu jest tworzony względem powiązania transakcji i względem niego weryfikowany:
binding(tx) = blake3("rand-tx-bind-1" || bincode(chain_id, bundle', action')) chains 14 to 19
binding(tx) = blake3("rand-tx-bind-2" || bincode(genesis_hash, chain_id, bundle', action')) chain 20
as 8 LE u32 words
gdzie ' oznacza, że każdy ciąg bajtów dowodu pakietu zastępuje się pustym wektorem: bundle.proof, od chain 17 bundle.auth_proof oraz proof w Aggregate. Wszystko inne jest w środku: auth_commit, wszystkie cztery koperty, każde pole spalenia, cel, walidator, odbiorca, podpisy, poświadczenie strażnika i jego współpodpisy Dilithium2 oraz własny dowód Call. Osiem słów jedzie w segmencie wejścia publicznego dowodu, którego skrót H_PUB obwód już wiąże, więc poprawka nie wymagała zmiany guesta ani obwodu; węzeł uruchamia Machine::verify_public(hc_bundle, binding, proof) i odrzuca dowód pakietu, który deklaruje jakąkolwiek inną wysokość publiczną (public height not the transaction binding's). Transaction::binding to jedyna funkcja, którą portfel wywołuje przed dowodzeniem, a księga przed weryfikacją. Od chain 17 dowód autoryzacji jest tworzony względem tych samych ośmiu słów i względem nich weryfikowany, więc również nie da się go przenieść na inną transakcję.
Chain 20 wiąże skrót genesis. Do chain 19 powiązanie oraz podpisane komunikaty akcji bez pakietu obejmowały sam identyfikator łańcucha, więc dwa łańcuchy o wspólnym identyfikatorze (ponowne wycięcie, prywatna kopia) przyjęłyby nawzajem swoje dowody i podpisy. Genesis chain 20 ustawia binding_domain: 1 (v0.6.8, audyt v6 BIND-1; crates/randprotocol-core/src/types/binding.rs). Skrót genesis otwiera wtedy każdy preimage pod znacznikiem o jedną wersję wyższym: rand-tx-bind-2 i rand-call-bind-2 dla dwóch powiązań, a tak samo dla mintu z faucetu (rand-mint-3), Unbond i Withdraw (rand-unbond-2, rand-withdraw-2), komunikatów mintu i uprawnień RPL (rand-rpl-mint-2, rand-rpl-authority-2), akcji agregatora i komunikatów zarządczych mostu. Rejestracja walidatora w takim łańcuchu jest zawsze komunikatem v2 (genesis, łańcuch, adres, wypłata). Nic udowodnionego ani podpisanego dla jednego łańcucha nie weryfikuje się w innym, niezależnie od jego identyfikatora. Słowa powiązania są wejściem publicznym, więc żaden klucz guesta ani weryfikatora się nie zmienił. Portfel starszy niż v0.6.8 wciąż dowodzi względem postaci z identyfikatorem łańcucha i na chain 20 zostaje odrzucony. rand_getLimits i rand_status zwracają binding_domain (1 na chain 20, 0 bez tego pola).
Przy genesis hardening_v6 (chain 16 do 20) własny dowód Call jest również powiązany, drugim skrótem o tej samej konstrukcji pod własną domeną, z wyzerowanym dowodem wywołania oraz dowodami pakietu:
call_binding(tx) = blake3("rand-call-bind-1" || bincode(chain_id, bundle', action'')) as 8 LE u32 words
Na chain 20 znacznikiem jest rand-call-bind-2, a skrót genesis otwiera preimage, jak wyżej. Dowód Invoke jest wiązany tak samo, z zadeklarowanym przejściem zachowanym w preimage, więc dowód zobowiązuje się dokładnie do zmiany stanu, wypłat i odbiorców, które niesie transakcja.
Dowód wywołania niesie te słowa w segmencie wejścia publicznego, po wejściu publicznym programu z czasu wdrożenia, jeśli je ma, więc jego kopia dołączona do innego pakietu opłaty nie weryfikuje się. Oba powiązania są zagnieżdżone: portfel wybiera notatki pakietu opłaty i buduje transakcję z pustymi dowodami, dowodzi wywołanie względem powiązania wywołania, wstawia ten dowód i dopiero potem wyznacza powiązanie transakcji i dowodzi pakiet.
Wniosek dla klienta: zbuduj całą transakcję — wyjścia, koperty, akcję — przed dowodzeniem. Portfel zbudowany przed forkiem dowodzi względem pustego segmentu publicznego i zostaje odrzucony. Akcje bez pakietu nie mają dowodu do powiązania i opierają się na własnych podpisach. Powiązanie nie jest identyfikatorem transakcji, który pozostaje bez zmian.
Identyfikator transakcji
txid = blake3("rand-txid-3" || bincode(view))
gdzie view to transakcja, w której bajty proof pakietu zastąpiono przez blake3(proof) (proof_hash), a jego bajty auth_proof przez blake3(auth_proof) (auth_proof_hash, po auth_commit, który wchodzi bez zmian), a każde inne pole pozostaje dosłowne. Domeną było rand-txid-2, bez dwóch pól autoryzacji, do chain 16; każdy identyfikator transakcji zmienił się wraz z chain 17. Haszowanie dowodu przez skrót pozwala przyciętemu pakietowi (agregacja bloków, nieaktywna na chain 20) zachować ten sam identyfikator co jego surowa postać; przycięty pakiet zachowuje swój dowód autoryzacji. Transakcja bez pakietu haszuje swoje bajty bez zmian. Ten sam identyfikator służy za klucz w mempoolu, w potwierdzeniach i w każdym zapytaniu RPC.
Każda akcja
Action to enum bincode, znaczniki w kolejności deklaracji:
| tag | akcja | pola | pakiet | minimalna opłata |
|---|---|---|---|---|
| 0 | None | — | wymagany | BUNDLE_BASE = 0.001 RAND |
| 1 | Mint | cm, pk, time, r, envelope, amount, minter: PublicKey, signature | musi być nieobecny | 0 (podpisywany przez walidatora; tylko łańcuchy z faucetem, amount ≤ 100 RAND) |
| 2 | Deploy | base_pc: u32, words: Vec<u32>, public: Vec<u32> | wymagany | opłata podstawowa + 100,000 jednostek za każde słowo programu i słowo publiczne |
| 3 | Call | program, proof, input_envelope: Option<CallEnvelope> | wymagany | w łańcuchu 20: opłata podstawowa + gas_price × zadeklarowany w dowodzie GAS_LIMIT + byte_price za każdy KiB dowodu i koperty wejściowej; w łańcuchu bez sekcji gas: opłata podstawowa + call_fee(tier) |
| 4 | Bond | validator, amount, registration: Option<Registration> | wymagany, oraz burn_r == amount | opłata podstawowa |
| 5 | Unbond | validator, amount, nonce, signature | musi być nieobecny | 0 |
| 6 | Withdraw | validator, amount, nonce, time, r, envelope, signature | musi być nieobecny | 0 w transmisji; opłata podstawowa jest pobierana z amount |
| 7 | BridgeAttest | attestation, recipient: ShieldedAddress, r, time, asset, envelope, pq_signatures: Vec<PqSignature> | wymagany | opłata podstawowa |
| 8 | BridgeBurn | asset, amount, relayer_fee, to_chain: u16, token: [u8; 32], to: [u8; 32] | wymagany, z burn_asset == asset i burn_a == amount | BRIDGE_BURN_FEE = 0.01 RAND |
| 9 | RegisterAggregator | registration | wymagany, burn_r = depozyt | opłata podstawowa |
| 10 | UnbondAggregator | aggregator, nonce, signature | musi być nieobecny | 0 |
| 11 | WithdrawAggregator | aggregator, nonce, time, r, envelope, signature | musi być nieobecny | 0 |
| 12 | SlashAggregator | dwa podpisane nagłówki agregatu | musi być nieobecny | 0 |
| 13 | Aggregate | covers, proof, aggregator, nonce, time, r, envelope, signature | musi być nieobecny | 0 |
| 14 | RegisterToken | name, symbol, decimals: u8, authority: MintAuthority, initial: Option<InitialMint>, salt: [u8; 32], index: u32 | wymagany | opłata podstawowa + registration_fee rejestru (1 RAND w łańcuchu 20, spalany) |
| 15 | TokenMint | asset, amount, recipient: ShieldedAddress, r, time, envelope, nonce, signature | wymagany | opłata podstawowa |
| 16 | SetAuthority | asset, new: Option<PublicKey>, nonce, signature | wymagany | opłata podstawowa |
| 17 | TokenBurn | asset, amount | wymagany, z burn_asset == asset i burn_a == amount | opłata podstawowa |
| 18 | PauseMints | nonce, signature | musi być nieobecny | 0 |
| 19 | UnpauseMints | nonce, pq_signatures | musi być nieobecny | 0 |
| 20 | RegisterBridgedToken | name, symbol, salt, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures | wymagany | opłata podstawowa + registration_fee |
| 21 | ListBacking | token_index, chain: u16, token: [u8; 32], decimals: u8, nonce, pq_signatures | wymagany | opłata podstawowa |
| 22 | RotatePqGuardians | new_pq_guardians: Vec<PublicKey>, nonce, pq_signatures | musi być nieobecny | 0 |
| 23 | RotatePauseKey | new_pause_key: PublicKey, nonce, pq_signatures | musi być nieobecny | 0 |
| 24 | ClaimVested | entry: [u8; 32], amount, nonce, to: ShieldedAddress, time, r, envelope, signature | musi być nieobecny | 0 w transmisji; opłata podstawowa jest pobierana z amount |
| 25 | RevokeVesting | entry: [u8; 32], unvested, nonce, to: ShieldedAddress, time, r, envelope, signatures (od v0.6.8 próg podpisów osób uprawnionych do odwołania wpisu, z wpłatą do skarbca wskazanego we wpisie genesis; wcześniej jeden signature) | musi być nieobecny | 0 w transmisji; opłata podstawowa jest pobierana z unvested |
| 26 | BondVested | entry: [u8; 32], validator, amount, registration: Option<Registration>, nonce, signature | musi być nieobecny | 0 |
| 27 | UnbondVested | entry: [u8; 32], amount, nonce, signature | musi być nieobecny | 0 |
| 28 | AdmitValidator | candidate: PublicKey, signatures: Vec<(PublicKey, Signature)> | musi być nieobecny | 0 |
| 29 | SlashEquivocation | first, second: dwa podpisane nagłówki bloków jednego lidera dla jednego widoku | musi być nieobecny | 0 |
| 30 | RotatePqGuardiansV2 | new_pq_guardians: Vec<PublicKey>, possession: Vec<Vec<u8>>, nonce, pq_signatures | musi być nieobecny | 0 |
| 31 | RotatePauseKeyV2 | new_pause_key: PublicKey, possession: Vec<u8>, nonce, pq_signatures | musi być nieobecny | 0 |
| 32 | CancelRotation | kind: u8, nonce, signature | musi być nieobecny | 0 |
| 33 | Invoke | program, proof, input_envelope: Option<CallEnvelope>, transition: Transition | wymagany; burn_r oraz burn_a dla burn_asset to to, co trafia do programu | minimalna opłata wywołania + cell_fee za każdą utworzoną komórkę |
MintAuthority = None | Key(PublicKey) | Bridge { backings: Vec<Backing> } | Program(Hash)
InitialMint { amount: u64, recipient: ShieldedAddress, r: Word8, time: u32, envelope: Envelope }
PqSignature { index: u8, signature: Vec<u8> } // one guardian's Dilithium2 co-signature, by set index
W łańcuchu 20 dostępne są tagi od 0 do 8, od 14 do 21, 28 oraz od 30 do 33. Jego genesis zawiera sekcję bridge z rules_v2, sekcję tokens, staking.admission_by_vote (tag 28), bridge.rotation (tagi od 30 do 32) i program_state (tag 33). Tagi 22 i 23 są tam odrzucane: przy bridge.rotation.needs_possession rotacja musi mieć formę …V2, która niesie podpis każdego nowego klucza. W łańcuchu 19, który nie miał żadnej z tych sekcji, dostępne były tagi od 0 do 8 i od 14 do 23 (łańcuch 14, bez rules_v2, odrzucał 22 i 23). SlashEquivocation wymaga sekcji staking.slashing, a w łańcuchu 20 jest ona pusta (null). Pięć akcji agregacji wymaga sekcji aggregation, której łańcuch 20 nie ma (kompilacja odmawia uruchomienia na genesis, który ją zawiera, dopóki agregacja nie zostanie ponownie zmierzona). Węzeł odrzuca wszystkie te akcje na bramce, przed jakimkolwiek innym sprawdzeniem, ale nadal zajmują one swoje tagi bincode, więc enkoder, który ma tagi wpisane na stałe, musi je uwzględnić. Agregacja jest opisana jako przyszła funkcja w Dowodach. Cztery akcje genesis-vesting (tagi od 24 do 27, w kompilacji od v0.5.11) wymagają sekcji vesting, której nie miał żaden łańcuch, łącznie z łańcuchem 20, i bez niej są odrzucane przy przyjęciu. Tagi od 28 do 33 są nowe w v0.6.8 i dopisane na końcu, dzięki czemu historia każdego wcześniejszego łańcucha nadal się dekoduje.
Cztery akcje RPL (tagi od 14 do 17) rejestrują token, emitują kolejne jednostki tokenu z autoryzacją Key, przekazują lub zwalniają uprawnienie do emisji i spalają posiadane jednostki; RegisterToken wskazujący uprawnienie Bridge lub Program jest odrzucany. Nie ma akcji transferu tokenu: transfer tokenu to None, nieodróżnialny od płatności w RAND. Zobacz Tokeny RPL. Cztery akcje zarządzania mostem (tagi od 18 do 21) to wstrzymanie emisji, jej wznowienie oraz dopisanie tokenu mostowanego lub kolejnego zabezpieczenia po genesis. PauseMints jest podpisywany kluczem wstrzymania z genesis, który może tylko wstrzymywać; pozostałe trzy wymagają kworum strażników PQ (Dilithium2). BridgeAttest również niesie takie kworum, obok kworum ECDSA. Tagi 22 i 23, w ramach rules_v2 mostu, zastępują zbiór strażników PQ lub klucz wstrzymania, każdy pod kworum PQ obecnego zbioru i z rotation_nonce mostu. Tagi 30 i 31 robią to samo w ramach bridge.rotation i dodatkowo niosą podpis Dilithium2 każdego nowego klucza pod komunikatem rotacji, czyli dowód posiadania klucza; w łańcuchu 20 rotacja jest zapisywana jako oczekująca i wchodzi w życie po delay_secs, czyli 86,400 s. CancelRotation (tag 32), podpisany bieżącym kluczem wstrzymania, anuluje oczekującą rotację, zanim wejdzie w życie. Zobacz Most.
AdmitValidator (tag 28) to głosowanie zbioru walidatorów nad przyjęciem nowego klucza: podpisy walidatorów posiadających ponad dwie trzecie stawki, w rosnącej kolejności adresów. Przy staking.admission_by_vote (łańcuch 20) Bond, który rejestrowałby nowego walidatora, jest odrzucany, jeśli jego adres nie został w ten sposób przyjęty (Staking). Invoke (tag 33, RPL-2) to wywołanie, którego dowód poświadcza jedno zadeklarowane przejście stanu transition programu: odczytane i zapisane komórki, to, co pola spalania pakietu wnoszą do skarbca programu, oraz noty, które skarbiec wypłaca lub które program emituje. Księga stosuje przejście, jeśli każdy zadeklarowany odczyt nadal się zgadza i oba dowody przechodzą weryfikację (Programy).
Deploy i Call są zwykłymi transakcjami osłoniętymi (shielded), których pakiet jest transferem zerowym do samego siebie: istnieje po to, by opłacić minimalną opłatę z własnych not wywołującego, tak aby łańcuch nigdy nie dowiedział się, do kogo należały. RAND opuszcza pulę wyłącznie przez burn_r — stawkę Bond, depozyt RegisterAggregator, wpłatę Invoke do skarbca programu — a token tylko przez burn_a, gdzie burn_asset go wskazuje, w TokenBurn, BridgeBurn lub Invoke. Każda inna akcja niczego nie spala, a pakiet z burn_asset = 0 i niezerowym burn_a jest odrzucany wszędzie.
Kolejność przyjmowania
Ledger::validate_inner sprawdza rzeczy tanie przed kosztownymi, w tej kolejności; mempool wykonuje to samo sprawdzenie przed rozgłoszeniem, więc zła transakcja jest odrzucana raz, na brzegu.
- Limity rozmiaru: cała transakcja ≤
max_block_bytesłańcucha; każda z czterech kopert pakietu i każda koperta notatki niesiona przez akcję ma dokładnieenvelope_bytesz genesis (1 860 w łańcuchu 20; najwyżej 2 048 bajtów w łańcuchu bez tego pola); dowód pakietu, dowód autoryzacji i dowód wywołania każdy ≤max_proof_bytes(domyślnie 2 MiB, 4 MiB w genesis łańcucha 20; w łańcuchach od 13 do 16 było to 8 MiB); program ≤ limit programu w łańcuchu (domyślnie 4 096 słów, 65 535 w genesis łańcucha 20); koperta wywołania ≤max_call_envelope_bytes(domyślnie 18 432 bajty, 65 536 w łańcuchu 20); atestacja ≤ 16 KiB. Dowód pakietu w zapieczętowanej (przyciętej) postaci znacznikowej jest tu odrzucany poza synchronizacją w postaci zapieczętowanej (PrunedFormOutsideSync), przed jakąkolwiek pracą nad dowodem. - Identyfikator łańcucha się zgadza.
- Kształt i dolny próg opłaty: akcja bez pakietu nie niesie żadnego pakietu, a każda inna musi nieść dokładnie jeden;
check_burn_shapeustala, które z trzech pól spalania akcja może ustawić:TokenBurnlubBridgeBurnspala własneassetiamountprzezburn_assetiburn_a,BondlubRegisterAggregatorspala swoją kwotę przezburn_r, a każda inna akcja nie spala niczego;fee ≥ fee_floor(action). W łańcuchu z sekcjągas(łańcuch 20) prógCalllubInvokena tym kroku to reguła gazu przy zadeklarowanym limicie równym 1,BUNDLE_BASE + gas_price + byte_price × ⌈bytes / 1024⌉liczone dla jego dowodu i koperty wejściowej, które są znane przed jakąkolwiek weryfikacją. - Kotwica: jeden z ostatnich 1 024 korzeni końca bloku w łańcuchu 20, którego genesis ustawia
proof_window_blocks: 1024(v0.6.8); jeden z ostatnich 256 w łańcuchu bez tego pola, jak do łańcucha 19 włącznie. - Czas: w przedziale
[height − 1024, height]w łańcuchu 20;[height − 256, height]bez tego pola. Jedna wartość z genesis ustawia oba okna. - Nullifiery i zobowiązania: wszystkie cztery nullifiery są różne i żaden nie jest wydany; wszystkie cztery zobowiązania są różne i żadne nie jest już liściem.
- Kontrole akcji: faucet jest włączony, mint mieści się w limicie i jest podpisany przez walidatora, a w łańcuchu 20 wskazuje odbiorcę z listy dozwolonych
staking.faucet_recipientsz genesis, mieści się wfaucet_budget_per_epochdanej epoki i jest podpisany przez walidatora z listystaking.faucet_mintersz genesis (reguła ważności od łańcucha 16); mint lub depozyt równy 2⁶³ lub większy jest odrzucany w każdym łańcuchu; program daje się zdekodować; program istnieje; reguły stakingu (rejestracja obecna dokładnie wtedy, gdy walidator jest nieznany, nonce, podpis, wystarczający stake); własne reguły mostu dlaBridgeAttestiBridgeBurnoraz dla akcji wstrzymania i wpisania na listę; reguły RPL dla jego czterech akcji, w tym opłata rejestracyjna; w łańcuchu 20 kworum głosowania o dopuszczeniu, a dlaInvokekształt przejścia, zadeklarowane odczyty względem przechowywanych komórek, pokrycie każdej wypłaty przez skarbiec i opłata za komórki. Wszystko to działa przed dowodem pakietu, więc transakcja, której nie można zastosować, nie kosztuje żadnej weryfikacji STARK. Podhardening_v6(łańcuchy od 16 do 20) znajdują się tu dwa dalsze odrzucenia:Deployprogramu, którego żadne wywołanie nie mogłoby przechować (ProgramUncallable), oraz dowód pakietu lub wywołania, którego nagłówek lub transkrypt niesie pole, jakiego uczciwy prover by nie zapisał (non-canonical proof: …), odrzucane przed weryfikacją któregokolwiek z dowodów. Od v0.6.8 dowód, którego nagłówek deklaruje stopień większy niż pułap najwyższej tabeli, jest odrzucany jako pierwszy z tym samym werdyktem (audyt v6 ZKV-4), zanim kontrola go przejdzie. - Skrót pakietu: w łańcuchu z podzieloną autoryzacją (każdy łańcuch od 17) najpierw sprawdzane są pola autoryzacji: pakiet bez dowodu autoryzacji jest odrzucany,
cpublikowane przez dowód autoryzacji (odczytane, jeszcze niezweryfikowane) musi być równeauth_commit, a w łańcuchu 20 dowód autoryzacji musi deklarowaćGAS_LIMITrówny dokładnie 1 279. Następnie księga przelicza skrót opublikowanych pól pakietu, w tymauth_commit, i musi on być równy temu, co opublikował dowód. W łańcuchu 20 dowód pakietu musi też deklarowaćGAS_LIMITrówny dokładniebundle_gas_limitz genesis, czyli 20 479. - Dowód pakietu:
Machine::verify_publicwzględem przypiętego w genesishc_bundlei powiązania transakcji. W łańcuchu z podzieloną autoryzacją następnie weryfikowany jest dowód autoryzacji, względem przypiętego w genesishc_authi tego samego powiązania. - Dowód wywołania i jego opłata za poziom: dla
CalllubInvokenajpierw przypinany jest nagłówek dowodu (poziom najwyżej 14, tabela keccak najwyżej 2¹², tabela sha256 najwyżej 2¹³, wysokość wdrożonego programu, z dolnym progiem 2⁷ wierszy podhardening_v6, a w łańcuchu 20 zadeklarowanyGAS_LIMITnie większy niż pułap nagłówkagas_max), potem STARK wywołania względemcode_hashprogramu i, podhardening_v6, powiązania wywołania transakcji. Opłata jest sprawdzana na końcu, bo można ją poznać dopiero po zdekodowaniu dowodu: w łańcuchu 20fee ≥ BUNDLE_BASE + gas_price × GAS_LIMIT + byte_price × ⌈bytes / 1024⌉przy obowiązujących cenach, pluscell_feeza każdą utworzoną komórkę dlaInvoke(Opłaty); w łańcuchu bez sekcjigasfee ≥ BUNDLE_BASE + call_fee(tier).
Błędy wracają z rand_sendTransaction jako kod -32000 z przyczyną: nullifier already spent, anchor is not one of the last 1024 roots (256 w łańcuchu bez proof_window_blocks), time 12 is outside [476, 1500], fee 1000000 below minimum 2000000, the bundle's digest is not what its proof published, invalid bundle proof: …, unknown program …, already in mempool, conflicts with a pending transaction over <nullifier>, faucet is disabled on this chain, a od łańcucha 14 the bundle burns asset 2 on an action that burns no token, a burn of 5 is not allowed on this action, the bundle burns 399, not the 400 the action declares, wrong mint nonce: expected 1, got 0, wrong token index: expected 3, got 2, od łańcucha 17 this chain requires an auth proof on every bundle (genesis hc_auth) i the auth proof publishes a different commitment than the bundle's auth_commit, a w łańcuchu 20 envelope is 1348 bytes, this chain requires exactly 1860 i bundle proof declares gas limit …, this chain requires exactly 20479. Przyjęcie to nie zatwierdzenie: odpytuj rand_getTransaction, aż zwróci blok.
Blok zawierający jakąkolwiek nieprawidłową transakcję jest nieprawidłowy. Mempool indeksuje każdy oczekujący nullifier i zobowiązanie i odrzuca konflikt z miejsca, więc dwa pakiety wydające tę samą notatkę nigdy nie trafiają do jednego bloku.
Rozmiary
| część | bajty |
|---|---|
Word8 | 32 |
jedna Envelope | 1 892 zakodowane (1 860 szyfrogramu) od łańcucha 18; 1 380 (1 348) do łańcucha 17 |
Bundle bez dwóch dowodów | 7 936 (cztery nullifiery, cztery zobowiązania, cztery koperty, auth_commit) |
| transakcja transferu bez dwóch dowodów | 7 949 |
| transakcja wdrożenia bez dwóch dowodów, program o 100 słowach, bez wejścia publicznego | 8 369 |
| transakcja wywołania bez trzech dowodów, bez koperty wejściowej | 7 990 |
| transakcja mint (bez pakietu) | 5 761 (klucz Dilithium2 o 1 312 bajtach i podpis o 2 420 bajtach) |
| dowód pakietu, poziom 14, profil produkcyjny | 1 497 544 i 1 504 260 odczytane w łańcuchu 18 (wdrożenie na wysokości 97 821 i wywołanie na 98 335, 2026-09-30); 1 422 116 i 1 423 170 zmierzone w łańcuchu 14 (rejestracja zUSD i wpis zabezpieczenia na listę, 2026-09-20) |
| dowód autoryzacji, poziom 10, profil produkcyjny | 1 370 827 i 1 367 303 w tych samych dwóch transakcjach łańcucha 18 |
Pierwszych sześć wierszy to kodowanie od łańcucha 18: pole memo dodało 512 bajtów do każdej koperty, a podzielona autoryzacja dodała 40 bajtów do pakietu przed własnymi bajtami dowodu autoryzacji. Tak więc osłonięty transfer w sieci ma od łańcucha 18, łącznie z łańcuchem 20, około 2,88 MB, w praktyce w całości dowodu, niezależnie od przenoszonego aktywa: około 1,5 MB dowodu pakietu i 1,37 MB dowodu autoryzacji. Do łańcucha 15 było to około 1,43 MB, z jednym dowodem. Bloki są ograniczone do 2 000 transakcji (MAX_BLOCK_TXS) oraz do max_block_bytes łańcucha w bajtach transakcji, obie granice są regułami konsensusu: domyślnie 4 MiB (MAX_BLOCK_BYTES), co pomieściłoby jeden taki transfer, i 20 MiB w genesis łańcucha 20, które mieści siedem. Genesis z hc_auth musi ustawić max_block_bytes na co najmniej trzykrotność max_proof_bytes plus 1 MiB, ponieważ transakcja może nieść trzy dowody (pakietu, autoryzacji, wywołania), więc domyślne 4 MiB jest tam odrzucane. Agregacja na poziomie bloku jest odłożonym w kolejce remedium; zob. Dowody.
Odczyt z powrotem
rand_getTransaction(hash) zwraca null, dopóki transakcja nie zostanie zatwierdzona, a potem jej pozycję w bloku i wyłącznie pola publiczne:
{
"height": 192, "index": 0, "block_hash": "63f6…08",
"tx": {
"hash": "4f2c…e7", "chain_id": 20,
"bundle": {
"anchor": "6b1d…c4",
"nullifiers": ["8c04…d1", "5e77…20", "03aa…6f", "e19b…42"],
"commitments": ["2a9f…07", "b310…88", "77c1…0e", "5d20…b3"],
"fee": "1000000", "burn_a": "0", "burn_r": "0", "burn_asset": 0, "time": 5,
"proof_len": 1497544, "envelope_len": [1860, 1860, 1860, 1860],
"auth_commit": "0bab…e8a1", "auth_proof_bytes": 1370827
},
"action": { "kind": "none" }
}
}
W transferze nie ma pól from, to, nonce, kwoty ani asset, a w zapisanym bloku również ich nie ma: węzeł nie ma już nic więcej do zredagowania. auth_commit i auth_proof_bytes (długość dowodu uwierzytelnienia) występują od chain 17. Pakiet ma zawsze cztery sloty, razem z atrapami, a transfer RAND i transfer dowolnego tokenu RPL mają ten sam wpis "kind": "none". Każda kwota u64 jest ciągiem dziesiętnym od chain 14 (fee, burn_a, burn_r, amount akcji); indeksy, nonce, wysokości i długości pozostają liczbami JSON. Pozostałe rodzaje akcji wyświetlają swoje pola publiczne — mint, deploy, call, bond, unbond, withdraw, bridge_attest, bridge_burn, register_token, token_mint, set_authority, token_burn, pause_mints, unpause_mints, register_bridged_token, list_backing, rotate_pq_guardians, rotate_pause_key, cztery rodzaje vestingu claim_vested, revoke_vesting, bond_vested, unbond_vested oraz od v0.6.8 admit_validator, slash_equivocation, rotate_pq_guardians_v2, rotate_pause_key_v2, cancel_rotation i invoke — zgodnie z opisem w dokumentacji JSON-RPC.