Tej strony używaj, żeby dobrać właściwą bibliotekę przy współpracy z łańcuchem, sprawdzić parametr względem tego, co wymusza węzeł, i samodzielnie przeliczyć identyfikator lub zobowiązanie. Każda podana tu wartość to ta, na której działa kod w łańcuchu 20 (fullnode v0.6.8).
Prymitywy i miejsca ich użycia
| prymityw | parametry | zastosowanie |
|---|---|---|
| Dilithium2 | klucz publiczny 1 312 bajtów, podpis 2 420 bajtów; pary kluczy wyprowadzane z 32-bajtowego ziarna | głosy walidatorów i propozycje bloków; emisje z faucetu; Unbond, Withdraw i Registration walidatora; głosy AdmitValidator (łańcuch 20); akcje agregatora; uprawnienie emisyjne Key tokena RPL (TokenMint, SetAuthority); postkwantowe współpodpisy opiekunów mostu, jego klucz wstrzymania oraz, w łańcuchu 20, dowody posiadania nowych kluczy przy rotacji (niżej) |
| ML-KEM-768 | klucz enkapsulacji 1 184 bajty, szyfrogram 1 088 bajtów | zapieczętowanie klucza pojedynczej transakcji w kopercie notatki dla adresu odbiorcy; zapieczętowanie koperty wejścia wywołania dla audytora; zapieczętowanie zadania dowodzenia dla sparowanego provera |
| ChaCha20-Poly1305 | klucz 32 bajty, losowy 12-bajtowy nonce dołączany na początku, tag 16 bajtów | każda część koperty: treści notatek, opakowane klucze transakcji, transkrypty wejść wywołań; zadania i odpowiedzi sparowanego provera |
| BLAKE3 | wyjście 32 bajty, zawsze z poprzedzającym ciągiem domeny | adresy, identyfikatory transakcji, wiązanie transakcji i wiązanie wywołania, identyfikatory programów, identyfikatory aktywów tokenów, korzeń stanu i jego drzewa Merkle’a, tożsamość p2p, komunikaty podpisywane, identyfikatory aktywów mostu, zaślepienie depozytu mostu i zaślepienia notatek opłat mostu |
| Poseidon2 nad Goldilocks | szerokość 8, rate 4, capacity 4, gąbka z nadpisywaniem bez dopełniania; elementy komunikatu to słowa 32-bitowe; 86 stałych rund to dosłowna tabela w kodzie źródłowym węzła (poseidon2_constants.rs) od cięcia w łańcuchu 15, te same wartości, które dawało wcześniej losowanie z ziarna | wszystko wewnątrz dowodu: klucze, zobowiązania do notatek, nullifiery, drzewo zobowiązań, skrót programu hc, zobowiązania wejściowe H_IN i H_PUB, skrót pakietu, zobowiązanie autoryzacji |
| secp256k1 ECDSA + keccak256 | podpisy z niskim s, 20-bajtowe adresy opiekunów w stylu Ethereum, mu = keccak256(keccak256(body)) | atestacje opiekunów mostu, dzięki czemu ten sam klucz działa w kontraktach EVM, w programie Solana i w Rand; w Rand każda atestacja wymaga ponadto kworum Dilithium2 opisanego niżej |
| bech32m (BIP-350) | część czytelna dla człowieka rpl, ładunek 32 bajty, 52 znaki danych + 6 znaków sumy kontrolnej = 62 | zapis tekstowy rpl1… identyfikatora aktywa tokena RPL |
| Plonky3 batch STARK | Goldilocks p = 2⁶⁴ − 2³² + 1, hiding FRI, blowup 8, arność składania 8, 80 zapytań, 20 bitów proof-of-work | każdy dowód pakietu, dowód autoryzacji i dowód wywołania; dowód pakietu i jego dowód autoryzacji niosą osiem słów wiążących swojej transakcji jako segment wejścia publicznego |
Docelowe parametry whitepapera w niektórych miejscach różnią się od tych, na których działa implementacja (na przykład 384-bitowy skrót transkryptu w artykule wobec transkryptu Poseidon2 w zkVM). Te strony opisują kod.
Postkwantowe współpodpisy w moście
Atestacje opiekunów mostu to secp256k1 ECDSA, który przeciwnik kwantowy mógłby podrobić. Od łańcucha 14 każde BridgeAttest — zarówno depozyt, jak i rotacja zestawu opiekunów — musi też zawierać kworum podpisów Dilithium2 tych samych opiekunów pod tym samym skrótem:
M = "rand-bridge-pq-cosign-1" || rand_chain_id (u64 BE) || mu // 63 bytes, deterministic signing
gdzie mu = keccak256(keccak256(body)) to wartość, którą podpisuje kworum ECDSA. Identyfikator łańcucha Rand w M sprawia, że współpodpis z testnetu nie przejdzie weryfikacji w innym łańcuchu Rand, nawet jeśli klucze opiekunów zostałyby użyte ponownie. M wiąże wyłącznie identyfikator łańcucha, także w łańcuchu 20: to jedyny podpisywany komunikat, którego binding_domain nie przesunęło za skrót genesis, ponieważ demony opiekunów, które go podpisują, żyją w innym repozytorium. Kopia łańcucha o tym samym identyfikatorze i z tym samym zestawem opiekunów przyjęłaby współpodpisy swoich depozytów; indeks zestawu opiekunów nie wchodzi do M, więc współpodpis przetrwa rotację zestawu ECDSA. Pole pq_signatures akcji to lista PqSignature { index: u8, signature }, sprawdzana od najtańszych kontroli do najdroższych: quorum(n) ≤ len ≤ n (PqNoQuorum), indeksy ściśle rosnące (PqIndexOrder), każdy indeks poniżej n (PqIndexOutOfRange), każdy podpis dokładnie 2 420 bajtów (PqBadSignatureLength) i na końcu weryfikacja każdego podpisu pod pq_guardians[index] (PqBadSignature). Podpisujący PQ nie muszą być podpisującymi ECDSA; każde kworum jest liczone osobno. Pole znajduje się wewnątrz wiązania transakcji, więc nie można go usunąć ani podmienić w kopii.
Zestaw PQ to genesis bridge.pq_guardians — osiem kluczy po 1 312 bajtów w łańcuchu 20, tak jak w łańcuchach od 15 do 19, wyrównanych indeksami z genesisowym zestawem ECDSA, zestaw opiekunów 1 (łańcuch 14 miał sześć, wyrównanych z zestawem 0). Rotacja zestawu opiekunów ECDSA nigdy go nie rusza. W łańcuchu 14 był stały przez cały czas życia łańcucha; od łańcucha 15 genesis zawiera grupę rules_v2 mostu, w której kworum PQ może wymienić cały zestaw PQ (RotatePqGuardians) lub klucz wstrzymania (RotatePauseKey), każde pod rotation_nonce mostu. Genesis łańcucha 20 dodaje bridge.rotation: rotacja musi być akcją V2, niosącą własny podpis Dilithium2 każdego nowego klucza pod tym samym komunikatem (dowód posiadania), i wchodzi w życie dobę po zapisaniu, chyba że obecny klucz wstrzymania ją anuluje (Most). rand_getBridgeState zwraca klucze, a zdekodowane bridge_attest wymienia pq_signers, czyli indeksy, które podpisały. To samo kworum PQ autoryzuje komunikaty zarządcze mostu, każdy o stałym układzie big-endian niosącym identyfikator łańcucha i nonce. W łańcuchu 20 (genesis binding_domain: 1) cyfra wersji każdej domeny to 2, a 32-bajtowy skrót genesis leży między domeną a identyfikatorem łańcucha; łańcuchy od 14 do 19 używają form z -1:
| komunikat | domena (łańcuchy 14–19 / łańcuch 20) | podpisuje |
|---|---|---|
M_pause | rand-bridge-pause-1 / -2 | jeden genesisowy klucz wstrzymania (Dilithium2), który może wstrzymać, ale nigdy wznowić |
M_unpause | rand-bridge-pq-unpause-1 / -2 | kworum opiekunów PQ |
M_list | rand-bridge-pq-list-1 / -2 | kworum opiekunów PQ (ListBacking) |
M_register | rand-bridge-pq-register-1 / -2 | kworum opiekunów PQ (RegisterBridgedToken) |
M_rotate_pq | rand-bridge-pq-rotate-pq-1 / -2 | kworum obecnego zestawu PQ (RotatePqGuardians, łańcuchy rules_v2); w łańcuchu 20 także każdy nowy klucz, jako dowód posiadania |
M_rotate_pause | rand-bridge-pq-rotate-pause-1 / -2 | kworum obecnego zestawu PQ (RotatePauseKey, łańcuchy rules_v2); w łańcuchu 20 także nowy klucz |
M_cancel | rand-bridge-cancel-rotation-1 / -2, z końcowym bajtem kind | obecny klucz wstrzymania (CancelRotation, łańcuchy z bridge.rotation: łańcuch 20) |
STARK w produkcji
zkVM działa na zestawie ograniczeń 8: dziewięć obowiązkowych tabel (program, cpu, memory, alu, range, nibble, poseidon2, input, public) oraz tabele keccak i sha256, które dowód zawiera tylko wtedy, gdy jego gość wywołał odpowiedni syscall, połączone siedemnastoma szynami i dowodzone jako jeden wsadowy STARK. Od zestawu ograniczeń 7 każda zadeklarowana tabela ma co najmniej 2⁷ wierszy, a terminal LogUp każdej tabeli jest zaślepiony przez siedemnastą szynę, BLIND. Publiczny interfejs dowodu to 35 elementów ciała (pv::NUM): wejściowy pc, poziom, osiem słów wyjściowych, skrót programu hc (8 słów), solone zobowiązanie do wejścia prywatnego H_IN (8), niesolony skrót segmentu publicznego H_PUB (8) oraz, od zestawu ograniczeń 8, zadeklarowany limit gazu GAS_LIMIT.
| parametr | produkcja | uwaga |
|---|---|---|
| zapytania FRI | 80 | zmniejszenie do 27 zapytań w zestawach ograniczeń 3 i 4 cofnięto po audycie z 2026-09-12: zostawiało ~42 bity udowodnione |
| blowup | 8 (log_blowup = 3) | maksymalny stopień ograniczeń każdej tabeli jest przypięty poniżej limitu 8 |
| arność składania | 8 | |
| bity proof-of-work | 20 | |
| bezpieczeństwo | 3·80 + 20 = 260 bitów przypuszczalnych (ethSTARK); około 86 bitów udowodnionych przy lukach bliskości | |
| zero-knowledge | statystyczne: LDE i wielomian wsadowy FRI są randomizowane, więc dwa dowody tego samego przebiegu różnią się | od zestawu ograniczeń 7 każda zadeklarowana tabela ma co najmniej 2⁷ wierszy, więcej niż 82 punkty otwierane przez dowód, a terminale LogUp są zaślepione |
| poziomy | 10, 12, 14, 16, 18, 20 | dowód ujawnia poziom, nigdy liczby cykli; od fullnode v0.5.6 dowód wywołania powyżej poziomu 14 jest odrzucany (CallTierTooHigh), dowód pakietu jest przypięty do poziomu 14, a dowód autoryzacji do poziomu 10 |
| limit dowodu | genesisowe max_proof_bytes: 4 MiB (4 194 304) w łańcuchach od 17 do 20; 8 MiB w łańcuchach od 13 do 16; 2 MiB (MAX_PROOF_BYTES) w genesis bez tego pola, jak łańcuch 12 | 2 MiB jest powyżej każdego dowodu bez tabel haszujących, jaki mogą wytworzyć wdrożone programy-goście, i poniżej dowodu z keccakiem; 4 MiB dopuszcza oba |
| gaz | 1 za wiersz cpu; 3 za wiersz absorpcji Poseidon2; 64 za wiersz SHA256; 192 za wiersz KECCAK | kumulowany w kolumnie GAS tabeli cpu i dowodzony najwyżej GAS_LIMIT; weryfikator odrzuca limit powyżej gas_max(t, k, s) = (2ᵗ − 1) + 2ᵗ⁻² + 191·(2ᵏ/32) + 63·(2ˢ/64), czyli 1 279 dla nagłówka poziomu 10 bez haszowania i 20 479 dla nagłówka poziomu 14 |
Zmierzone rozmiary przy zestawie ograniczeń 6 (Apple Silicon, build release): produkcyjny dowód bez keccaka miał 1,298,729 bajtów na poziomie 10 i 1,359,978 na poziomie 12; dowód z keccakiem na poziomie 10 miał 3,198,430 bajtów, czyli powyżej domyślnych 2 MiB i poniżej 8 MiB łańcuchów od 13 do 15. Dowód na poziomie 14 wycofanego guesta pakietu 2-in-2-out zmierzono na 1,321,773 bajtów w łańcuchu 8 oraz 1,418,406 do 1,420,423 bajtów w łańcuchu 13. Guest pakietu z ukrytymi aktywami 4-in-4-out z łańcucha 14 pozostaje na poziomie 14 bez żadnej z tablic skrótów, a jego dowody zmierzono na 1,422,116 i 1,423,170 bajtów w łańcuchu 14 (2026-09-20); łańcuch 15 przypiął tego samego guesta (hc_bundle bez zmian). Dowód pakietu deklarował tam public_log_height równe 4, czyli wysokość ośmiu słów wiążących, a węzeł odrzucał każdą inną wartość, zanim zbudował klucz weryfikacyjny. Dowód wywołania ERC-20 approve z keccakiem, wykonany w łańcuchu 13 dnia 2026-09-19 (poziom 16), ma 3,412,405 bajtów; każdy łańcuch od fullnode v0.5.6 odrzuca to wywołanie na poziomie 16.
Kolumny maskujące i dolne progi tablic zestawu ograniczeń 7 dodają około 5% do dowodu bez haszowania: według pomiaru fullnode z 2026-09-28 fib ma 1,367,688 bajtów na poziomie 10 i 1,424,299 na poziomie 12, dowód z keccakiem na poziomie 10 ma 3,283,898, a dowód pakietu z ukrytymi aktywami na poziomie 14 ma 1,498,821 (guest v1) i 1,497,156 (guest v2). Od tamtej pory dowód pakietu deklaruje public_log_height równe 7. Zestaw ograniczeń 8 dodaje poniżej 1%. Pomiar w łańcuchu 18 z 2026-09-30 (Apple Silicon): dowody uwierzytelnienia od 1,364,940 do 1,375,371 bajtów (poziom 10), dowody pakietu od 1,494,151 do 1,504,260 bajtów (poziom 14, guest v3) oraz dowody wywołań: 1,498,589 bajtów na poziomie 14 bez tablicy skrótów, 1,911,152 na poziomie 14 z tablicą SHA-256 i 3,346,865 na poziomie 12 z tablicą Keccak. Wszystkie mieszczą się w limicie 4 MiB łańcuchów od 18 do 20.
Weryfikacja trwa około 16 ms przy ciepłym kluczu weryfikacyjnym i około 230 ms przy pierwszym zbudowaniu klucza dla danej pary (tier, heights). Profil FRI test (16 zapytań) istnieje na potrzeby zestawu testów i jest niebezpieczny; genesis łańcucha 20 przypina production. Fullnode v0.6.8 nie zmienia żadnego ograniczenia: łańcuch 20 przypina te same hc_bundle i hc_auth co łańcuchy od 17 do 19.
Konstrukcje skrótów, które można odtworzyć
BLAKE3 z domeną: blake3(domain || data), czyli ciąg domeny bez separatora.
Domena wiążąca. Genesis łańcucha 20 ustawia binding_domain: 1 (fullnode v0.6.8, audyt v6 BIND-1). W takim łańcuchu każde powiązanie dowodu i każda podpisana wiadomość akcji, które wiązały dotąd samo id łańcucha, wiążą teraz także hasz genesis: numer wersji domeny rośnie o jeden, a 32-bajtowy hasz genesis trafia na początek preimage’u, przed id łańcucha. Dowód lub podpis wykonany dla jednego łańcucha nie weryfikuje się wtedy w żadnym innym, nawet takim, który używa ponownie id łańcucha i kluczy, jak dzieje się przy ponownym wycięciu łańcucha. Słowa wiążące są publicznym wejściem dowodów, więc żaden guest ani klucz weryfikacyjny się nie zmienił. Tabela podaje obie postacie; łańcuchy od 14 do 19 używają pierwszej, bajt w bajt. Już wcześniej powiązane z genesis i bez zmian: głosy i propozycje (od consensus_domain: 1), Registration v2 oraz głos AdmitValidator. Nieprzeniesione: współpodpis mintu mostu (powyżej). Portfel potrzebuje v0.6.8, aby wysyłać w łańcuchu 20, i wybiera postać według id łańcucha, a nie według tego, co twierdzi węzeł; rand_getLimits.binding_domain ją raportuje.
| wartość | konstrukcja |
|---|---|
| adres jawny | blake3("rand-address" || dilithium2_public_key), base58 |
| id transakcji | blake3("rand-txid-3" || bincode(view)), gdzie view to transakcja, w której bajty dowodu pakietu zastąpiono blake3(proof), a jej dowód uwierzytelnienia blake3(auth_proof); auth_commit wchodzi bez zmian. Do łańcucha 16 domeną było rand-txid-2, bez pól uwierzytelnienia |
| powiązanie transakcji | blake3("rand-tx-bind-1" || bincode(chain_id, bundle', action')) w łańcuchach od 14 do 19; blake3("rand-tx-bind-2" || bincode(genesis_hash, chain_id, bundle', action')) w łańcuchu 20. Osiem u32 w porządku little-endian, gdzie ' zastępuje bundle.proof, bundle.auth_proof oraz proof w Aggregate pustym wektorem; bincode ze stałej szerokości liczbami całkowitymi little-endian. Każdy dowód pakietu i dowód uwierzytelnienia jest tworzony na tych słowach i weryfikowany względem nich |
| powiązanie wywołania | blake3("rand-call-bind-1" || …), albo rand-call-bind-2 z haszem genesis na początku w łańcuchu 20: konstrukcja powiązania transakcji, w której także własny proof obiektu Call (lub Invoke) jest wyzerowany. W genesis z hardening_v6, takim jak łańcuch 20, segment publiczny dowodu wywołania to publiczne słowa programu z czasu wdrożenia, po których następuje tych osiem słów, a segment Invoke dodaje za nimi swoje przejście jako słowa kontekstu |
| id programu | blake3("rand-program" || base_pc as 4 LE bytes || words as 4 LE bytes each) |
| tożsamość p2p | klucz ed25519 z blake3("rand-p2p-identity" || seed) |
| id aktywa mostu (dla każdego zabezpieczenia) | blake3("rand-bridge-asset" || token_chain as 2 BE bytes || token_address (32 bytes)) — rand_bridgeAssetId je oblicza, a rand_getAssets wypisuje je w każdym wierszu zabezpieczenia; identyfikuje monetę źródłową w sieci, a nie token |
| id aktywa tokena RPL, natywnego | blake3("rand-rpl-asset" || bincode(name, symbol, decimals, authority, initial: Option<InitialMint>, salt)), salt to 32 bajty wybrane przez twórcę; pokazywane jako hex i jako rpl1… |
| id aktywa tokena RPL, mostowanego | blake3("rand-rpl-asset" || bincode(name, symbol, BRIDGE_DECIMALS, salt)) — nie dla pary (chain, token), ponieważ jeden token mostowany obejmuje kilka zabezpieczeń |
| maskowanie depozytu mostu | r = blake3("rand-deposit-r-1" || mu), jedyne r, które admisja przyjmuje dla BridgeAttest (w przeciwnym razie WrongDepositBlinding) |
| maskowania notatek opłat mostu (łańcuch 20) | notatka opłaty depozytu: r = blake3("rand-bridge-fee-r-1" || mu); notatka spalenia: r = blake3("rand-bridge-burn-fee-r-1" || txid); oba odczytywane jako osiem słów little-endian. Notatki nie mają koperty: każde pole jest jawne, a właścicielem jest bridge.fees.recipient z genesis |
| odbiorca mostu | blake3("rand-shielded-recipient" || pk_bytes || kem_ek) obliczane z pełnego adresu shielded |
wiadomość Mint kranu | blake3("rand-mint-2" || bincode(chain_id, cm, pk, time, r, envelope, amount)); w łańcuchu 20 blake3("rand-mint-3" || bincode(genesis_hash, chain_id, cm, pk, time, r, envelope, amount)) |
wiadomości TokenMint, SetAuthority | blake3("rand-rpl-mint-1" or "rand-rpl-authority-1", chain_id || asset_id || nonce || body); body mintu zawiera blake3(bincode(envelope)). W łańcuchu 20 rand-rpl-mint-2 i rand-rpl-authority-2, z haszem genesis na początku |
wiadomość Registration | blake3("rand-register-2" || bincode(genesis_hash, chain_id, validator, payout)) w genesis z staking.registration_v2 lub binding_domain: 1, takim jak łańcuch 20 (rejestracja v1 jest tam odrzucana); blake3("rand-register" || bincode(chain_id, payout)) w pozostałych |
głos AdmitValidator (łańcuch 20) | blake3("rand-admit-validator-1" || genesis_hash || candidate_address), podpisywany przez każdego głosującego walidatora |
wiadomość Unbond | blake3("rand-unbond" || bincode(chain_id, validator, amount, nonce)); w łańcuchu 20 blake3("rand-unbond-2" || bincode(genesis_hash, chain_id, validator, amount, nonce)) |
wiadomość Withdraw | blake3("rand-withdraw" || bincode(chain_id, validator, amount, nonce, time, r, envelope)); w łańcuchu 20 rand-withdraw-2, z haszem genesis na początku |
| węzeł Merkle’a (drzewa stanu) | blake3("rand-merkle-node" || left || right) |
| liść rejestru tokenów | blake3("rand-token-leaf-1" || bincode(TokenInfo)), w kolejności indeksów, pod tokens_root |
| korzeń stanu | w łańcuchu 20 blake3("rand-state-admitted-1" || inner || admitted_root) wokół inner = blake3("rand-state-8" || tree_root || nullifier_root || validators_root || programs_root || bridge_root || tokens_root || faucet_epoch || faucet_minted_in_epoch || bond_queue_root || gas_price || byte_price || program_state_root), gdzie program_state_root = blake3("rand-program-state-1" || cells_root || vaults_root) (RPL-2), dwa liczniki kranu i dwie bieżące ceny mają po 8 bajtów big-endian, a opakowanie zatwierdza zbiór dopuszczonych (staking.admission_by_vote). Korzeń łańcucha 19 miał postać blake3("rand-state-7" || …) nad tymi samymi polami do byte_price (sekcja genesis staking dopisuje trzy pola po tokens_root pod rand-state-5, jak w łańcuchach od 15 do 17; sekcja genesis gas.dynamic dopisuje dwie ceny i przenosi domenę na rand-state-7); rand-state-4 nad pierwszymi sześcioma w łańcuchu 14; rand-state-2 nad pierwszymi czterema w łańcuchu bez sekcji bridge, tokens ani aggregation (Konsensus) |
Poseidon2 z tagiem domeny: H(domain, msg) = sponge_hash([domain, msg...]), przy czym tag jest pierwszym absorbowanym słowem, każde słowo jest elementem Goldilocks, a 4-elementowy digest jest podzielony na osiem słów u32 (najpierw dolna, potem górna połowa każdego elementu). Tagi domen:
| tag | wartość | konstrukcja |
|---|---|---|
NK | 1 | klucz podglądu nk = H(1, sk) |
PK | 2 | wartość publiczna notatki pk = H(2, nk) |
NF | 3 | nullifier H(3, nk || cm) |
CM | 4 | zobowiązanie do notatki H(4, pk(8) || from(8) || amount_lo || amount_hi || asset || time || r(8)), 28 słów |
OVK, KEM_SEED | 5, 6 | wychodzący klucz podglądu i ziarno ML-KEM, przez szeroki hasz w trybie licznika [domain, nk, counter] |
NODE | 7 | węzeł drzewa H(7, left || right); pusty liść to same zera |
HC | 8 | skrót programu; znacznik, base_pc i len leżą w liniach pojemności pierwszej permutacji, po czym słowa są absorbowane po cztery na blok |
OUT | 9 | skrót wyjściowy gościa transferu |
IN | 10 | H_IN: nagłówek pojemności [10, n_in, 0], następnie 4-słowowa sól, potem dane wejściowe |
BUNDLE | 11 | skrót wycofanej paczki 2-wejścia-2-wyjścia dla 47 słów publicznych; żaden łańcuch nie przypina już tego gościa |
| hidden bundle | 64 | skrót paczki aktywów ukrytych dla 81 słów w łańcuchach od 14 do 16: kotwica, cztery nullifiery, cztery zobowiązania, fee, burn_a, burn_r (po dwa słowa), burn_asset, time i na końcu słowo skażenia. W łańcuchach od 17 do 20 (gość paczki v3) jest to 89 słów: osiem słów zobowiązania autoryzacji c stoi bezpośrednio przed słowem skażenia. Aktywa prywatnego A w nim nie ma. Znacznik jest lokalny dla węzła i leży z dala od wbudowanych znaczników od 1 do 15 |
AUTH | 65 | zobowiązanie autoryzacji c = H(65, nk || salt), 16 słów, ze świeżą 256-bitową solą dla każdej paczki; publikowane przez dowód paczki w jej skrócie oraz przez dowód autoryzacji jako jego osiem słów wyjściowych, a w paczce przenoszone jako auth_commit (łańcuchy od 17 do 20). Lokalne dla węzła, jak 64 |
PUB | 15 | H_PUB: nagłówek [15, n_pub, 0], bez soli; dla programu, w łańcuchu 20, słowa są ustalane przy wdrożeniu (co najwyżej 32 768), segmentem każdego wywołania są te słowa, po których następuje osiem słów powiązania wywołania, a wywołanie jest sprawdzane względem skrótu tego segmentu; dla dowodu paczki i dowodu autoryzacji segmentem jest osiem słów powiązania transakcji; genesis bez max_program_public_words dopuszcza tylko segment pusty |
hc, H_IN i H_PUB nie są zwykłymi gąbkami z prefiksem wiadomości: nagłówek trafia do pojemności, dzięki czemu wiersze skrótu w tabeli cpu absorbują dokładnie cztery słowa na permutację. Odtwarzaj je za pomocą dołączonego crate’a (randprotocol_zkvm::hash::{input_digest, public_digest}, isa::Program::digest), a nie ręcznie.
Zwykła gąbka nie dopełnia, więc w obrębie pierwszego bloku [a] i [a, 0] dają ten sam skrót. Każda powyższa konstrukcja haszuje wiadomość o stałej długości pod własnym znacznikiem. Dla danych o zmiennej długości zestaw ograniczeń 7 dodał gąbkę wiążącą długość (hash::sponge_hash_len, syscall POSEIDON2_LEN): startuje ze stanu [0, 0, 0, 0, n, 0, 0, 0], gdzie n to liczba słów, i wykonuje co najmniej jedną permutację. Jest to inny skrót tych samych słów i żadna notatka, nullifier, korzeń ani skrót w łańcuchu go nie używa.
Warto zapamiętać dwie właściwości: hc jest wiążący, ale nie ukrywający (brak soli, więc zgadnięty program można potwierdzić, a dwa wdrożenia jednego programu da się ze sobą powiązać), natomiast H_IN jest zarówno ukrywający, jak i wiążący (świeża 128-bitowa sól dla każdego dowodu), więc opublikowanie H_IN nie ujawnia niczego o danych wejściowych, dopóki posiadacz soli go nie otworzy.
Wyprowadzanie kluczy
| klucz | z czego |
|---|---|
| para kluczy Dilithium2 (węzeł) | deterministycznie z 32-bajtowego ziarna |
| libp2p ed25519 (węzeł) | blake3("rand-p2p-identity" || seed) |
klucz podglądu, pk, ovk, para kluczy ML-KEM (portfel) | wyprowadzenia Poseidon2 z klucza wydatkowania, jak w tabeli powyżej; para ML-KEM to MlKem768::from_seed(kem_seed) |
Artykuły na stronie artykułów zawierają argumenty dotyczące bezpieczeństwa: wybory postkwantowe, rachunek poprawności FRI i powód, dla którego zachowano 80 zapytań, oraz ograniczenie długości śladu.