Rand Protocol
Dokumentacja
Poradniki

Programy

Model poufnych programów – gość RISC-V dowodzony w zkVM, wdrażany raz i wywoływany z prywatnymi danymi wejściowymi – wraz z poleceniami build, deploy, call, invoke i receipt, stanem programu RPL-2 w chain 20 oraz rzetelną listą tego, co nie jest jeszcze możliwe.

Ta strona opisuje, jak umieścić program w łańcuchu i go wywołać: czym jest program, jak go zbudować, co wywołanie ujawnia, a co ukrywa, jak odczytać potwierdzenie i na jakie limity natrafisz. Każdy program to kod publiczny, a każde wywołanie to dowód. Przykładowe programy zawierają kompletne aplikacje, a randprotocol/example-programs zawiera kod źródłowy programów wdrożonych w chain 20, w tym tych zgodnych z RPL-2 (poniżej).

Czym jest program

Program to kod maszynowy RV32IM dla Rand zkVM: { base_pc: u32, words: Vec<u32> }, co najwyżej tyle słów, ile wynosi limit programu w danym łańcuchu (65 535 słów w chain 20, jak w każdym łańcuchu od chain 13; 4096 w genesis bez max_program_words, takim jak chain 12), przy czym base_pc jest wielokrotnością 4, a każde słowo dekoduje się jako instrukcja (słowo, które się nie dekoduje, jest odrzucane przy wdrożeniu, nigdy w trakcie wykonania). Chain 20, podobnie jak chain 19, odmawia też wdrożenia programu, którego żadne wywołanie nie zdołałoby pomieścić: ponad 8180 słów bez segmentu publicznego, mniej niż tyle z segmentem (poniżej). Program jest adresowany zawartością i niezmienny:

program_id = blake3("rand-program" || base_pc || words)       // the id, with no public input
program_id = blake3("rand-program-2", base_pc || u32_le(len(words)) || words || u32_le(len(public)) || public)   // deployed with --public
code_hash  = hc = Poseidon2 digest of (base_pc, len, words)    // what calls are verified against

Wywołanie to dowód STARK, że program o skrócie hc został wykonany na pewnych prywatnych danych wejściowych, zużył nie więcej niż zadeklarowany limit gazu i opublikował osiem słów wyjścia. Węzeł weryfikuje dowód, zapisuje potwierdzenie i nic więcej się nie dzieje: wyjścia są danymi, nie skutkami. Wywołanie nie może przenieść RAND; wartość przenosi wywołujący w pakiecie, który płaci za wywołanie, a wyjścia programu znaczą to, co program i jego wywołujący uznają, że znaczą. W chain 20 wyjątkiem jest Invoke: jego dowód poświadcza zadeklarowane przejście stanu, które księga następnie stosuje (Stan programu (RPL-2)).

Co może robić program gościa:

grupainstrukcje
górne / skokiLUI AUIPC JAL JALR
rozgałęzieniaBEQ BNE BLT BGE BLTU BGEU
odczyty / zapisyLW SW LB LH LBU LHU SB SH
ALUADDI SLTI SLTIU XORI ORI ANDI SLLI SRLI SRAI ADD SUB SLL SLT SLTU XOR SRL SRA OR AND
RV32MMUL MULH MULHU MULHSU DIV DIVU REM REMU
systemoweECALL; nigdy FENCE, CSR, EBREAK ani rozszerzenia A i C

Wywołania systemowe, według numeru w a7:

#wywołanie systemoweskutek
0HALTkończy wykonanie
1WRITE_OUTPUT slot wordustala publiczne wyjście slot < 8; każdy slot co najwyżej raz, niezapisane sloty mają wartość zero
2READ_INPUT idxzwraca prywatne słowo wejścia idx, związane solonym zobowiązaniem H_IN
3POSEIDON2 ptr nhaszuje w miejscu n ≤ 4096 słów gąbką Poseidon2
4KECCAK ptrjedna permutacja Keccak-f[1600] w miejscu; sprawia, że dowód zawiera tablicę keccak
5SHA256 ptrjedna kompresja SHA-256 w miejscu; sprawia, że dowód zawiera tablicę sha256
6READ_PUBLIC idxzwraca słowo idx segmentu publicznego; w chain 20 segment publiczny jest ustalany przy wdrożeniu (rand program deploy --public); w genesis bez max_program_public_words jest pusty i nic nie odczytuje
7POSEIDON2_LEN ptr nPOSEIDON2 z długością wiadomości związaną w gąbce, tak że [a] i [a, 0] mają różne hasze (zestaw ograniczeń 7, chain 16 i późniejsze). rand-guest check w wersji circuits aeacf31 nadal go odrzuca (następna sekcja)

Pamięć: 32 rejestry, w których x0 ma wartość zero; RAM zaczynający się od zera; każdy adres poniżej 2³⁰; 64 KiB stosu dla skompilowanych programów gościa. Program zbudowany za pomocą rand-guest niesie swoje dane tylko do odczytu i zainicjowane w kontenerze obrazu oraz prolog ładujący, który jest częścią programu i zapisuje te dane, zanim zacznie działać właściwy kod programu (Zestaw instrukcji RISC-V). Zobowiązania do not, unieważniacze i sprawdzenia drzewa Merkle’a nie są wywołaniami systemowymi, lecz procedurami bibliotecznymi opartymi na POSEIDON2.

Od chain 18 (zestaw ograniczeń 8) każde wykonanie jest mierzone w gazie. Zwykła instrukcja oraz wywołania systemowe 0, 1, 2 i 6 kosztują po 1 gaz, POSEIDON2 na n słowach kosztuje 3 + 3 × ⌈n / 4⌉, jedna permutacja KECCAK kosztuje 192, a jedna kompresja SHA256 kosztuje 64. Przykładowe programy zawierają tabelę oraz to, co deklaruje wywołanie.

Budowanie programu

Trzy sposoby, wszystkie dają słowa RV32IM:

Wbudowany asembler. Portfel dostarcza pięć ręcznie asemblowanych programów gościa i asembluje je do pliku JSON { "base_pc", "words" }:

rand program build --guest private_payment --arg 1000 --out pp.json   # prints the program id
rand program build --guest balance_check --arg 1000 --out bc.json
# guests: fib, memcpy, bubble_sort, balance_check, private_payment, public_echo, rpl2_counter (v0.6.8); --arg is repeatable

Programy kompilowane, za pomocą rand-guest. Narzędzie rand-guest z repozytorium circuits buduje crate Rust no_std (riscv32im-unknown-none-elf, Rust 1.98.1) lub katalog C (clang 23.1.1) do kontenera obrazu, sprawdza każde słowo względem dekodera maszyny i wypisuje hc oraz identyfikator programu. Budowanie jest hermetyczne, więc ten sam kod źródłowy daje to samo hc na każdej maszynie. Dla chain 18 do 20 zbuduj toolchain z commitu circuits aeacf31 (gałąź feat/v067-circuits), czyli commitu przypiętego przez fullnode v0.6.7 i v0.6.8. W tym commicie rand-guest check wymienia tylko wywołania systemowe od 0 do 6, więc program gościa wywołujący guest_sdk::poseidon2_len (wywołanie systemowe 7) jest odrzucany przy budowaniu, choć maszyna go wykonuje (zkp-circuits issue #1). Krok po kroku: Wdrożenie programu w Rust i Wdrożenie programu RISC-V.

Programy tłumaczone. sbpf2rv tłumaczy plik ELF sBPF z Solana, a evm2rv tłumaczy bajtkod uruchomieniowy EVM na programy, których wyjścia słowo w słowo równają się wyjściom programów gościa będących interpreterami Rand: Transpiler Solana i Transpiler Ethereum.

Sam portfel nie ma kompilatora. rand program deploy przyjmuje plik .json, surowy plik .bin ze słowami w porządku little-endian albo kontener obrazu zapisywany przez rand-guest (fullnode v0.3 i nowsze).

Wdrażanie

rand program deploy pp.json          # a bundle pays the floor; waits for the commit; prints the id
rand program show <id>               # id, base_pc, words_len, code_hash, deployed_at, public_words_len, public_digest

Testnet to chain 20, a wdrażanie, wywoływanie i Invoke w nim wymagają portfela rand z fullnode v0.6.8 lub nowszego. Starszy portfel w ogóle nie może tam wysyłać: genesis chain 20 ustawia binding_domain: 1, więc każde powiązanie dowodu i każda podpisana wiadomość zawiera hasz genesis, a powiązania portfela v0.6.7 niosą sam identyfikator łańcucha. Publiczny punkt końcowy https://rpc.randprotocol.org obsługuje synchronizację portfela, wdrożenia i wywołania (konfiguracja).

Portfel najpierw wypisuje identyfikator programu, liczbę słów i hc, po czym pyta węzeł o opłatę przez rand_estimateFee, który odrzuca program przekraczający limit łańcucha, zanim cokolwiek zostanie udowodnione. Następnie buduje pakiet samoprzelewu o zerowej wartości (jedyny pakiet ukrytych aktywów 4-wejścia-4-wyjścia, który niesie każda transakcja; opłata jest pobierana z jego slotów RAND), płacący 0.001 RAND + 100,000 units × words z własnych not (słowa segmentu publicznego liczą się jako słowa kodu), dołącza Action::Deploy { base_pc, words, public }, dowodzi pakiet względem powiązania transakcji i wysyła. Od chain 17 są to dwa dowody, a portfel wypisuje po linii dla każdego: autoryzację wydatku tier-10 (sekundy, około 1,37 MB) oraz pakiet tier-14 (około dwóch minut na laptopie, około 1,5 MB). Przed chain 17 był to sam dowód pakietu, około 100 s i około 1,42 MB w chain 15. Opłata za wdrożenie nie ma składnika gazu ani bajtów: gaz wycenia wyłącznie wywołania. Węzeł sprawdza, czy każde słowo się dekoduje, oblicza hc, zapisuje rekord i w tle zaczyna budować klucz weryfikatora. Kod jest publiczny; to, kto za niego zapłacił, już nie — rand_getProgram nie ma pola deployer. Wdrożenie tych samych słów dwukrotnie jest idempotentne.

→ {"method":"rand_getProgram","params":["675adeea…0065"]}
← { "id": "675adeea…0065", "base_pc": 0, "words_len": 42, "code_hash": "…", "deployed_at": 10,
    "public_words_len": 0, "public_digest": null }
→ {"method":"rand_getProgramCode","params":["675adeea…0065"]}
← { "base_pc": 0, "words": [ … ] }

Wywoływanie

rand call <program id> --input 400 --input 250 --input 300 --input 75
rand receipt <tx hash>

Portfel pobiera z węzła kod i segment publiczny i sprawdza, czy haszują się do identyfikatora. Uruchamia wywołanie w emulatorze i wypisuje gaz oraz opłatę, zanim cokolwiek udowodni:

gas: 5636 (declaring 6144, tier 12)
fee 0.006191437 RAND (incl. two price steps of headroom)

Następnie pieczętuje transkrypt wejścia (poniżej), dowodzi wywołanie lokalnie w profilu FRI łańcucha, z danymi wejściowymi jako wektorem prywatnym, dowodzi pakiet płacący opłatę, wysyła, czeka i wypisuje potwierdzenie. W chain 20 dowód wywołania jest związany z własną transakcją (powiązaniem zawierającym hasz genesis), a pakiet to dwa dowody, więc wywołanie to trzy dowody: wywołanie, autoryzacja wydatku tier-10 i pakiet tier-14. Dwie powyższe linie pochodzą z wywołania anonimowego głosowania w chain 18 z 2026-09-30 (Anonimowe głosowanie).

Opłata. W chain 20 dolna granica wynosi BUNDLE_BASE + gas_price × declared gas limit + byte_price × ⌈bytes / 1024⌉, gdzie BUNDLE_BASE to 0,001 RAND, bajty to dowód wywołania wraz z kopertą wejść, a ceny zaczynały od 100 i 800 jednostek (jednostka to 10⁻⁹ RAND) i zmieniają się wraz z zapełnieniem bloków. Chain 20 ogranicza je do 10 000 i 80 000 jednostek, a do jego obciążenia wliczają się tylko bajty, które płacą cenę za bajt (dowody wywołań i koperty) (byte_load: "paying"); w chain 18 i 19 ceny nie miały górnej granicy, a bajty każdej transakcji przesuwały cenę za bajt. Płaski harmonogram księgi 0.001 RAND + call_fee(tier), obowiązujący w chain 15, tam nie obowiązuje. Portfel ustala opłatę, zanim powstanie dowód, więc wycenia dowód według limitu 4 MiB dla tego chain i dolicza zapas w postaci dwóch kroków ceny. Cztery zmierzone wywołania w chain 18 kosztowały od 0,0057 do 0,0075 RAND każde (rachunek). rand fee call <tier> --gas <limit> --bytes <B> pyta węzeł o samą dolną granicę. private_payment odczytuje cztery salda prywatne i, jeśli ich suma wynosi co najmniej jego próg, publikuje nadwyżkę: wyjścia [1, 0, 25, 0, 0, 0, 0, 0]. Łańcuch widzi dowód, poziom i te osiem słów; nigdy nie widzi sald.

Flagi: --tier T wymusza poziom; --gas-limit <N|max> deklaruje dokładny limit gazu lub górną granicę poziomu zamiast domyślnej wartości, czyli dokładnego gazu zaokrąglonego w górę do wielokrotności 2^(tier − 2); --fee <RAND> ustawia opłatę; --expect-public <FILE> odmawia przed wygenerowaniem dowodu, chyba że segment publiczny programu składa się ze słów z tego pliku; --auditor <rand1…> zapieczętowuje zapis również dla wskazanego audytora; --print-call-key wypisuje klucz wywołania; --no-envelope nie publikuje zapisu; --cuda wybiera backend GPU (nieprzetestowany na sprzęcie i niezgodny z pieczętowaniem koperty). Chain 20 odrzuca --cuda przy wywołaniu bez wyjątku: dowód wywołania powiązany ze swoją transakcją powstaje na backendzie CPU.

Pokwitowania

Pokwitowanie jest przechowywane dla każdego zatwierdzonego wywołania i przeliczane, gdy węzeł synchronizuje lub weryfikuje swój łańcuch:

→ {"method":"rand_getReceipt","params":["b082…09b0"]}
← { "tx": "b082…09b0", "program": "675adeea…0065", "tier": 14,
    "outputs": [1, 0, 25, 0, 0, 0, 0, 0], "height": 105, "index": 0, "h_in": "9c0e…7f", "h_pub": null }

h_in to solone zobowiązanie dowodu do prywatnych wejść; samo w sobie niczego nie ujawnia. h_pub to skrót segmentu publicznego programu, ustalonego przy wdrożeniu, czyli public_digest programu, lub null dla programu wdrożonego bez niego. Nie ma pola effect, a zadeklarowany limit gazu nie znajduje się w pokwitowaniu. Eksplorator pokazuje ten sam obiekt jako receipt w GET /transactions/:hash dla call, wypisuje wywołania programu w GET /programs/:id i filtruje GET /transactions?kind=call&program=<id>.

Koperty wejść: ujawnienie wywołania później

Domyślnie rand call publikuje zapis (salt, inputs) wywołania zapieczętowany w kopercie wejść wywołania, aby wejścia można było pokazać później bez drugiego dowodu:

częśćbajtyczym jest
body12 + 16 + 4·n + 16salt ‖ inputs pod świeżym kluczem wywołania K_call
to_sender60K_call opakowany kluczem podglądu wychodzącego wywołującego
kem_ct1,088 lub 0enkapsulacja ML-KEM-768 dla audytora, pusta bez audytora
to_auditor60 lub 0K_call opakowany pod sekretem tej enkapsulacji

Dane powiązane treści to H_IN, więc koperta przeniesiona na inne wywołanie nie otworzy się dla nikogo. Łańcuch sprawdza tylko rozmiar (co najwyżej max_call_envelope_bytes: 65 536 bajtów w chain 20, 18 432 w genezie bez tego pola) i zwraca ją bez zmian przez rand_getCallEnvelope(tx). Otwierają ją trzy klucze i żaden więcej: klucz podglądu wywołującego (każde wywołanie wykonane przez ten portfel), klucz wywołania (to jedno wywołanie), klucz podglądu audytora (to jedno wywołanie).

rand open-call <txhash>                    # as the caller
rand open-call <txhash> --as-auditor       # as the auditor
rand open-call <txhash> --call-key <hex>   # with the per-call key alone

open-call otwiera zapis, sprawdza, że input_digest(salt, inputs) == H_IN, ponownie uruchamia program na odzyskanych wejściach w emulatorze, porównuje wyjścia z wyjściami z pokwitowania — i kończy się kodem niezerowym, jeśli któraś z kontroli zawiedzie, aby skrypt nie wziął ostrzeżenia za potwierdzenie. --no-envelope jest nieodwracalne: gdy sól zniknie, nikt, łącznie z wywołującym, nie może otworzyć tego wywołania.

Od v0.6.8 ponowne uruchomienie odbywa się na segmencie publicznym, na którym powstał dowód: słowach publicznych programu ustalonych przy wdrożeniu i, przy hardening_v6, następujących po nich ośmiu słowach powiązania wywołania (zgłoszenie #116 w fullnode). W v0.6.7 open-call działało tylko dla programu wdrożonego bez segmentu publicznego; dla programu z segmentem wypisywało wejścia, po czym zatrzymywało się z komunikatem re-running the program on these inputs failed: PublicIndex(0), kończąc się kodem niezerowym bez wykonania którejkolwiek kontroli.

Stan programu (RPL-2)

Chain 20 jest pierwszym chain z RPL-2, włączonym przez sekcję program_state w jego genezie. Program otrzymuje trzy rzeczy, których wcześniej nie miał (fullnode docs/program-state.md, crates/randprotocol-core/src/ledger/program_state.rs w v0.6.8):

komórkimapa z 8-słowowego klucza na 8-słowową wartość, osobna dla każdego programu. Nieobecna komórka daje odczyt ośmiu zer; zapis ośmiu zer ją usuwa. Zatwierdzone w korzeniu stanu
skarbiecpubliczne saldo dla każdego aktywa (0 to RAND). Wartość wchodzi przez pakiet invoke, a wychodzi jako noty obliczane przez łańcuch
własny tokentoken zarejestrowany przez rand token create --program <id> (uprawnienie do emisji Program(id), bez podaży początkowej) jest emitowany i spalany tylko przez wywołania invoke tego programu

Komórki i salda skarbca są publiczne, tak jak podaż tokena. Żadne z nich nie jest notą.

Invoke (Action 33) to wywołanie, które dodatkowo deklaruje przejście, a jego dowód wywołania pokazuje, że program akceptuje dokładnie to przejście:

Invoke     { program, proof, input_envelope, transition }
Transition { reads:  [{ key, value }]     // cells read, with the values read; keys strictly ascending
             writes: [{ key, value }]     // cells written; keys strictly ascending
             inflow: none | deposit | burn
             pays:   [{ asset, amount, recipient, r, envelope }]   // notes out of the vault
             mints:  [{ asset, amount, recipient, r, envelope }] } // new units of the program's own token

To, co wchodzi, to to, co pakiet spala: burn_r to RAND wpłacony do skarbca; burn_a jednostek burn_asset to token wpłacony (napływ deposit) lub zniszczony (napływ burn, tylko własny token programu). To, co wychodzi, to jedna obliczona przez łańcuch nota na każdą wypłatę, najpierw wypłaty, potem emisje, z kwotą i pk odbiorcy jawnymi, tak jak w przypadku emisji. Księga stosuje przejście tylko wtedy, gdy program istnieje, kształt jest dozwolony, każda komórka w reads nadal ma zadeklarowaną wartość, skarbiec (po wpłacie z tej samej transakcji) pokrywa każdą wypłatę, każda emisja dotyczy własnego tokena programu, a dowody się weryfikują. Odczyt, który już się nie zgadza, jest odrzucany jako StaleRead; portfel odczytuje ponownie i generuje dowód od nowa.

Czym invoke różni się od call.

Call (RPL-1)Invoke (RPL-2)
segment publiczny, na którym powstaje dowódpublic ‖ call_bindingpublic ‖ call_binding ‖ context, przejście jako słowa
co robi łańcuch po sukcesiezapisuje pokwitowanie; wyjścia są danymizapisuje pokwitowanie i stosuje przejście: zapisuje komórki, przenosi wartość w skarbcu, dopisuje noty wypłat
wartośćtylko własny transfer pakietuwpłaty do skarbca przez spalenia pakietu; wypłaty i emisje z niego
zależność od stanuniedowód nie zależy (segment jest budowany z transakcji), ale kontrola odczytu tak, przy przyjęciu i w puli
opłatadolna granica wywołaniadolna granica wywołania plus cell_fee za każdą utworzoną komórkę

Program odczytuje słowo kontekstu i przez READ_PUBLIC(public_len + 8 + i). Słowo 0 to wersja (1), słowa od 1 do 4 to liczby odczytów, zapisów, wypłat i emisji, następnie burn_r, rodzaj napływu, burn_asset i burn_a, potem każdy odczyt i zapis jako klucz i wartość (po 8 słów), potem każda wypłata jako aktywo i kwota. Odbiorców nie ma w kontekście: program decyduje o kwotach, a powiązanie ustala, komu się płaci. Program musi ograniczać każde słowo kontekstu, które mu pokazano, i nie może mieć ścieżki kończącej się paniką, ponieważ procedura obsługi paniki w guest-sdk zatrzymuje wykonanie, a zatrzymane wykonanie da się udowodnić.

Limity chain 20 (rand_getLimits.program_state):

limitchain 20
cell_fee10 000 000 jednostek, 0,01 RAND, za każdą utworzoną komórkę (komórkę zapisaną wartością niezerową tam, gdzie nic nie było zapisane); ponowny zapis lub usunięcie komórki jest darmowe
max_reads8 komórek
max_writes8 komórek
max_payouts4 noty, wypłaty i emisje łącznie

Reguła segmentu jest zwykle ostrzejsza: cały segment musi się zmieścić w tablicy publicznej, na której dowodzi się wywołanie tego samego programu, czyli w 127 słowach dla programu bez wejścia publicznego. Zostaje więc 119 słów kontekstu, na przykład trzy odczyty, trzy zapisy i cztery wypłaty.

Polecenia i RPC (portfel v0.6.8):

rand program build --guest rpl2_counter --out counter.json   # one cell, incremented by one per invoke
rand program invoke <id> --transition step.json [--input N | --inputs-file F.json]
rand program state <id> [--cell <64 hex>]                   # rand_getProgramCell / rand_getProgramCells
rand program vault <id>                                      # rand_getProgramVault

program invoke sprawdza każdy wpis reads względem aktywnej komórki przed wygenerowaniem dowodu i kończy się kodem 3 przy nieaktualnym odczycie, przed dowodzeniem lub przy włączeniu do bloku, więc skrypt odczytuje ponownie i ponawia próbę. rand_estimateFee z "kind": "invoke" wycenia opłatę. Format pliku przejścia opisano w docs/cli.md fullnode. Kompletne programy RPL-2 wdrożone w chain 20 (amm, stableswap, orderbook, stablecoin, lending, perp, crowdfund, stoploss) oraz programy RPL-1 (sealed-auction, ballot, eligibility, private-join, credit-score) znajdują się w randprotocol/example-programs.

Co jest jawne, a co ukryte

publiczneukryte
identyfikator programu i jego kod; poziom (tier); zadeklarowany limit gazu; osiem wyjść; H_IN; opłata pakietu i jego pola publiczne; rozmiar koperty; to, czy wywołanie użyło Keccak czy SHA-256, i mniej więcej jak często (nagłówek dowodu deklaruje wysokość każdej tablicy skrótów)prywatne dane wejściowe; rejestry, pamięć, rozgałęzienia; rzeczywista liczba cykli i dokładna ilość gazu; sól H_IN; kto wywołał program i czym zapłacił

Dwa dowody tego samego przebiegu to różne bajty. Na chain 15 i wcześniejszych to nie wystarczało: wywołanie z 31 lub mniejszą liczbą prywatnych słów dowodziło tablicy zbyt małej, by cokolwiek ukryć, i jego dane wejściowe dawały się odzyskać z dowodu. Od v0.6 prover ustawia dla tych tablic dolny próg 128 wierszy, a zestaw ograniczeń 7 (chain 16 i późniejsze) zaślepia sumy dla poszczególnych tablic, które nadal przeciekały. hc jest wiążące, ale nie ukrywające: każdy, kto potrafi wyliczyć programy-kandydaty, może potwierdzić, który z nich został wdrożony.

Czego jeszcze nie da się zrobić

W brzmieniu, w jakim podaje to fullnode:

  • Brak wywołań między programami i brak stanu poza RPL-2. Call to jeden dowód z jednego przebiegu; z wywołania do wywołania nie przechodzi nic poza tym, co wywołujący sam poda ponownie. Na chain 20 Invoke odczytuje i zapisuje własne komórki i skarbiec programu (powyżej); żaden chain przed 20 nie ma stanu programu, a invoke nie może sięgnąć do komórek innego programu.
  • Brak transferów inicjowanych przez program poza skarbcem. Efekt rodzaju 1, transfer opłacany przez program na konto, został usunięty razem z kontami, a wyjścia Call są oświadczeniami. Na chain 20 Invoke wypłaca z własnego skarbca programu, najwyżej cztery noty, a kwoty i odbiorcy są jawni.
  • Brak kompilatora w portfelu. Programy to wbudowane, wcześniej złożone gościnne obrazy albo obrazy zbudowane za pomocą rand-guest w repozytorium circuits.
  • Wejście publiczne jest ustalane przy wdrożeniu, a nie przy każdym wywołaniu. Zestaw ograniczeń 6 dodał READ_PUBLIC i H_PUB. Na chain 20 wdrożenie może zawierać do 32 768 słów publicznych, identyfikator programu je wiąże, a dowód każdego wywołania jest sprawdzany względem ich skrótu. Nie ma słów publicznych przypisanych do pojedynczego wywołania.
  • Syscalle skrótów dają duże dowody. Dowód zawierający keccak ma około 3,2 MB, czyli wprost przekracza domyślny limit dowodu 2 MiB; dowód zawierający sha256 dodaje około 400 KB. Limit chain 20 wynosi 4 194 304 bajtów (4 MiB, tak jak na chain 17–19; na chain 13–16 było to 8 MiB), co mieści oba przypadki: na chain 18 wywołanie tier 12 z tablicą Keccak miało 3,35 MB, a wywołanie tier 14 z tablicą SHA-256 miało 1,91 MB (Przykładowe programy). Wywołanie ma też limit 128 permutacji Keccak-f i 128 kompresji SHA-256 (poniżej), a każdy KiB dowodu jest rozliczany według ceny za bajt.
  • Programy EVM i Solana nie wdrażają się na chain 20, a ich wywołania przekraczają limit poziomu wywołania. Interpretery gościnne (evm.bin, 18 009 słów; sbpf.bin, 8 317) i przetłumaczone obrazy (ERC-20, 11 686 słów; SPL Token, 65 096) przekraczają domyślny limit 4096 słów i mieszczą się w limicie 65 535 ustawionym przez chain 13 i utrzymywanym przez każdy kolejny. Oba przetłumaczone obrazy wdrożono na chain 13 dnia 2026-09-19, SPL Token z jego ELF jako wejściem publicznym. Chain 14 i 15 uruchomiono od zera i żaden z obrazów nie został na nich wdrożony ponownie. Chain 20, podobnie jak chain 19, odmawia wdrożenia któregokolwiek z tych czterech: zgodnie z regułą hardening_v6 program, którego nie pomieściłoby żadne wywołanie tier 14, czyli mający więcej niż 8 180 słów bez segmentu publicznego, a z nim mniej, jest odrzucany przy wdrożeniu (ProgramUncallable), aby nie płacić opłaty za wdrożenie programu, którego nikt nie może wywołać. Wywołanie ERC-20 dowodzi się na tier 16 lub 18: approve na tier 16 wywołano na chain 13 dnia 2026-09-19 z produkcyjnym dowodem o rozmiarze 3 412 405 bajtów (Transpiler Ethereum), a chain 20 odrzuca każde wywołanie powyżej tier 14 (następny punkt), więc dziś żadne wywołanie ERC-20 nie jest tam dopuszczalne. Wywołania SPL Token to tier 20, który wymaga około 330 GB; nie istnieje żaden dowód wywołania SPL (pamięć dowodzenia). Opkody kontekstu bloku EVM kończą się pułapką, a stanu EVM w łańcuchu nie ma.
  • Wywołanie dowodzi się na tier 14 lub niższym. Od fullnode v0.5.6 (2026-09-25) dowód wywołania powyżej tier 14 jest odrzucany (CallTierTooHigh), podobnie jak dowód z więcej niż 128 permutacjami Keccak-f lub 128 kompresjami SHA-256. approve ERC-20 na tier 16 z chain 13 zostałby na chain 20 odrzucony, tak jak zostałby odrzucony na chain 15, 18 i 19, i tak samo każde wywołanie ERC-20 na tier 18 lub SPL Token na tier 20; takie wywołania nadal da się udowodnić poza łańcuchem (budżet).
  • Dowodzenie na GPU nigdy nie zostało uruchomione na sprzęcie. --cuda wymaga kompilacji z --features cuda i zatwierdzonego PTX; żadne z nich nie istnieje we flocie. Na chain 20 dowód wywołania powstaje na backendzie CPU niezależnie od kompilacji.
  • hc nie jest ukrywające, jak wyżej, a żądanie świadka nadal informuje węzeł, który liść wydaje portfel.

Ostatnio zweryfikowano względem commitu fullnode c0878c3f (łańcuch 21) dnia 2026-10-12. Jeśli kod i ta strona się różnią, rację ma kod; prosimy o zgłoszenie.