Ta strona wyjaśnia maszynę stojącą za każdym dowodem w Rand: co dowodzi, jak przebieg programu staje się czymś, co walidator może sprawdzić w milisekundach, i skąd bierze się prywatność. Ile kosztuje dowód i jak portfel go tworzy, opisuje strona Dowody. Parametry i konstrukcje funkcji skrótu opisuje strona Kryptografia. Zestaw instrukcji, względem którego pisze się programy, opisuje strona Architektura RISC-V.
Jedno twierdzenie, które wykazuje każdy dowód
zkVM dowodzi jednego rodzaju twierdzenia, dla każdego programu uruchamianego przez łańcuch:
Program, którego skrót to
hc, uruchomiony odbase_pc, na prywatnych danych wejściowych zobowiązanych przezH_INi publicznym segmencie wejścia zobowiązanym przezH_PUB, zakończył działanie w ciągu2ᵗ − 1cykli, zużył co najwyżejGAS_LIMITgazu i zapisał te osiem słów wyjściowych.
Transfer shielded to takie twierdzenie o jednym ustalonym programie, gościu pakietu ukrytych aktywów (hidden-asset bundle guest), którego hc jest przypięty w pliku genesis jako hc_bundle. Od łańcucha 17 twierdzenie to wykazuje się dwukrotnie na transfer: drugi, mały dowód o gościu uwierzytelniającym (auth guest, hc_auth w pliku genesis) pokazuje, że nadawca posiada klucz wydatkowania (Dowody). Poufne wywołanie to to samo twierdzenie o wdrożonym programie. Jest jedna maszyna, jeden weryfikator i jeden zestaw ograniczeń dla wszystkich. Wdrożenie programu jest więc operacją na danych (zapisanie jego słów i hc), a nie kryptograficzną: nie ma obwodu dla każdego programu ani zaufanej konfiguracji.
System dowodzenia to STARK zbudowany na Plonky3. Potrzebuje wyłącznie funkcji skrótu: bez ceremonii konfiguracji i bez krzywych eliptycznych, więc nie ma w nim niczego, co mógłby złamać algorytm Shora.
Potok
secret input ──► run the program ──► execution trace ──► polynomials ──► commit ──► FRI ──► proof
(keys, (RV32IM emulator) (nine tables, (one per (8× code, (80 queries,
notes, paths) one row per step) column) Merkle) 20-bit grinding)
every validator: recompute the challenges from the Merkle roots, open 80 random positions,
check the rules there → ≈ 16 ms
Górny wiersz wykonuje się raz, na maszynie nadawcy, w około 100 sekund dla pakietu i około 7 sekund dla towarzyszącego mu dowodu autoryzacji. Dolny wiersz wykonuje się na każdym walidatorze, wyłącznie na podstawie pól publicznych i dowodu.
1. Uruchom program i zapisz wszystko
Maszyna to 32-bitowy procesor RISC-V: RV32IM bez FENCE, instrukcji CSR i EBREAK (Architektura RISC-V). Prover najpierw uruchamia program na emulatorze i zapisuje jeden wiersz na każdy takt zegara. Ten zapis to ślad wykonania (execution trace). Każdy, kto go ma, mógłby odtworzyć przebieg krok po kroku; reszta schematu istnieje po to, by weryfikator mógł nabrać pewności, że ślad jest poprawny, bez posiadania go.
Program komunikuje się ze światem zewnętrznym przez osiem wywołań systemowych i niczym więcej:
| wywołanie systemowe | nr | co robi |
|---|---|---|
HALT | 0 | zatrzymuje; osiem słów wyjściowych jest ostatecznych |
WRITE_OUTPUT | 1 | ustawia jedno z ośmiu publicznych słów wyjściowych |
READ_INPUT | 2 | odczytuje słowo prywatnych danych wejściowych |
POSEIDON2 | 3 | haszuje bufor w pamięci w miejscu |
KECCAK | 4 | jedna permutacja Keccak-f[1600] |
SHA256 | 5 | jedna kompresja SHA-256 |
READ_PUBLIC | 6 | odczytuje słowo publicznego segmentu wejścia |
POSEIDON2_LEN | 7 | POSEIDON2 z długością wiadomości powiązaną ze skrótem (zestaw ograniczeń 7) |
Zobowiązania do notatek, nullifiery i ścieżki Merkle nie są wbudowane w maszynę. To zwykły kod gościa, który umieszcza w pamięci wiadomość oznaczoną domeną i wywołuje POSEIDON2. To wywołanie nie dopełnia danych, więc [a] i [a, 0] dają ten sam skrót. Dostarczeni goście wywołują je wyłącznie na wiadomościach o stałej długości. Nowy program, który haszuje dane o zmiennej długości, używa POSEIDON2_LEN (Architektura RISC-V).
Nie ma przerwania
Procesor nie ma instrukcji, która odmawia zakończenia, więc program nie może się zatrzymać, gdy kontrola zawiedzie. Gość pakietu zapisuje zamiast tego niepowodzenie: każda nieudana kontrola dodaje operacją OR jedynkę do rejestru o nazwie bad, a bad jest ostatnim słowem haszowanym do skrótu wyjściowego:
D = H(64; anchor, nf₀..₃, cm₀..₃, fee, burn_a, burn_r, burn_asset, time, c, bad)
c to zobowiązanie autoryzacyjne, które publikuje także dowód uwierzytelniający (od łańcucha 17; skrót z łańcuchów 14–16 nie miał c).
Łańcuch nigdy nie przyjmuje przeobrazu D od nadawcy. Odbudowuje go z publicznych pól transakcji, sam ustawia bad = 0, haszuje i porównuje. Oszust może wytworzyć w pełni poprawny dowód przebiegu, w którym kontrola zawiodła, ale ten przebieg wypisał H(…, 1), a łańcuch szuka H(…, 0). Dopasowanie ich wymaga kolizji funkcji skrótu.
2. Podziel ślad na dziewięć tabel
Jedna tabela opisująca cały procesor byłaby zbyt szeroka, więc ślad dzieli się na wyspecjalizowane tabele, dowodzone razem jako jedna partia:
| tabela | wiersze na poziomie t | co zapisuje |
|---|---|---|
program | deklarowane, ≥ 2⁷ | słowa instrukcji programu, dekodowane w obwodzie; określa, co CPU może pobrać |
cpu | 2ᵗ | jeden wiersz na cykl, poprzedzony wierszami haszującymi program, prywatne wejście i segment publiczny; zawiera 35 wartości publicznych |
memory | deklarowane, ≥ 2ᵗ⁺² | rejestry i RAM, posortowane według (przestrzeń, adres, czas) |
alu | 2ᵗ⁺¹ | każda operacja arytmetyczna, logiczna, przesunięcia, porównania, mnożenia i dzielenia, na limbach bajtowych |
range, nibble | po 256 | stałe tablice wyszukiwania: bajty, potęgi dwójki, 4-bitowe AND/OR/XOR |
poseidon2 | 2ᵗ⁺² | permutacja funkcji skrótu, jeden wiersz na rundę |
input | deklarowane, ≥ 2⁷ | zobowiązane słowa prywatnego wejścia |
public | deklarowane, ≥ 2⁷ | słowa segmentu publicznego (dla pakietu: powiązanie z transakcją) |
Dwie dodatkowe tabele, keccak i sha256, występują tylko w dowodzie programu, który je wywołał, każda z co najmniej 2⁷ wierszami, gdy jest obecna; gość pakietu nie używa żadnej z nich. Dolna granica 2⁷ wierszy dla każdej deklarowanej tabeli pojawiła się wraz z zestawem ograniczeń 7 (sekcja 9).
Poziom (tier) t to jedna z wartości 10, 12, 14, 16, 18 lub 20 i określa rozmiar tabel. Każdy pakiet jest dowodzony na poziomie 14: tabela CPU o 16 384 wierszach, z których gość wykorzystuje około 12 400. Gość uwierzytelniający wykonuje 250 cykli na poziomie 10. Reszta to dopełnienie, więc dowód ujawnia, jakiego poziomu wymagał program, a nigdy ile kroków wykonał w jego obrębie (Poziomy).
Tabela pamięci sprawia, że RAM można sprawdzić. Ponieważ jest posortowana według adresu, a potem czasu, reguła „odczyt zwraca ostatnią wartość zapisaną pod tym adresem” staje się regułą między dwoma sąsiednimi wierszami.
3. Połącz tabele szynami
Skąd weryfikator wie, że suma użyta przez CPU jest sumą obliczoną przez ALU? Dzięki szynom (buses). Gdy CPU potrzebuje dodawania, wysyła wiadomość (op, a, b, c); ALU odbiera jedną wiadomość dla każdego wiersza, który oblicza. Maszyna ma siedemnaście szyn:
| szyna | między |
|---|---|
PROGRAM, PROGRAM_WORD | cpu ← program: pobrania instrukcji oraz słowa, które pochłania skrót programu |
MEMORY | cpu ↔ memory: każdy odczyt i zapis |
ALU | cpu → alu |
RANGE8, POW2, AND4, OR4, XOR4 | kontrole zakresu bajtów, przesunięcia i operacje bitowe względem stałych tabel |
POSEIDON2 | cpu → poseidon2: jedna wiadomość na permutację |
INPUT_DIGEST, INPUT_READ | cpu ← input: słowa, które pochłania skrót wejścia, oraz odczyty programu |
PUBLIC_DIGEST, PUBLIC_READ | cpu ← public: ta sama para dla segmentu publicznego |
KECCAK, SHA256 | cpu → opcjonalne tabele skrótów |
BLIND | każda tabela → następna tabela w stałym cyklu: po jednej losowej wartości, w pierwszym wierszu (zestaw ograniczeń 7) |
Szyny sprawdza się argumentem pochodnej logarytmicznej (LogUp). Po zobowiązaniu tabel losowana jest wartość α, a każda tabela prowadzi bieżącą sumę ±1/(α − m) po wiadomościach, które wysyła lub odbiera. Sumy znoszą się dokładnie wtedy i tylko wtedy, gdy każda wysłana wiadomość została odebrana, czyli ten sam multizbiór po obu stronach. Jedna sfałszowana wiadomość zostawia resztę, którą losowe α z przytłaczającym prawdopodobieństwem ujawnia. Szyna pamięci używa bliskiego krewnego, kontroli permutacji multizbioru.
Każda tabela publikuje końcową wartość swojej bieżącej sumy, swój terminal, a weryfikator sprawdza, czy terminale sumują się do zera. Do zestawu ograniczeń 6 włącznie terminal był sprawdzalną funkcją zawartości swojej tabeli. Od zestawu ograniczeń 7 każda tabela ma pięć dodatkowych kolumn i w swoim pierwszym wierszu wysyła jedną losową wartość oraz odbiera wartość sąsiada na szynie BLIND. Każdy terminal przesuwa się o jednostajnie losowy element ciała wyzwań, a suma pozostaje zerem.
Pary szyn skrótu i odczytu są celowo rozdzielone. LogUp bilansuje według wiadomości, a nie według odbiorcy, więc jedna wspólna szyna pozwoliłaby proverowi pominąć słowo w skrócie, podczas gdy odczyt tego słowa nadal by się udał.
4. Zakotwicz ślad w świecie zewnętrznym
Ze śladu wychodzi trzydzieści pięć wartości publicznych, wszystkie w tabeli CPU:
| wartości | czym są |
|---|---|
| 1 | punkt wejścia pc |
| 1 | poziom (tier) |
| 8 | słowa wyjściowe (dla pakietu: skrót D) |
| 8 | hc, skrót programu, obliczany wewnątrz śladu ze słów tablicy programu |
| 8 | H_IN, skrót prywatnych danych wejściowych, liczony po danych z najpierw dodaną świeżą 128-bitową solą |
| 8 | H_PUB, skrót segmentu publicznego, bez soli |
| 1 | GAS_LIMIT, górny limit gazu deklarowany przez dowodzącego (zbiór ograniczeń 8) |
Ponieważ hc jest obliczany w obwodzie, dowód dotyczący innego programu nie może uchodzić za dowód dotyczący gościa pakietu (bundle guest). Weryfikator nigdy nie widzi żadnego słowa instrukcji; porównuje hc ze skrótem, którego oczekuje. Ponieważ H_IN jest solony, wiąże prywatne dane wejściowe bez ich ujawniania, nawet wobec kogoś, kto potrafiłby je odgadnąć. Ponieważ H_PUB nie jest solony, weryfikator może go przeliczyć ze słów, które już ma. W przypadku pakietu tymi słowami jest powiązanie transakcji (transaction binding): blake3("rand-tx-bind-1" ‖ chain_id ‖ tx), liczone z transakcji z wyzerowanymi bajtami dowodu, więc dowód skopiowany na transakcję z innym adresatem, memo, kopertą lub identyfikatorem łańcucha nie przechodzi weryfikacji. W chain 20 (genesis binding_domain: 1) jest to blake3("rand-tx-bind-2" ‖ genesis_hash ‖ chain_id ‖ tx), więc dowód nie przechodzi weryfikacji w innym łańcuchu, nawet przy tym samym identyfikatorze łańcucha; słowa są wejściem publicznym, więc w obwodzie nic się nie zmieniło. Dowód uwierzytelnienia jest tworzony nad tymi samymi ośmioma słowami. Dowód wywołania w chain 20 jest związany w ten sam sposób: jego segment to publiczne słowa programu z czasu wdrożenia, a po nich osiem słów powiązania wywołania (rand-call-bind-2, przed chain 20 rand-call-bind-1), więc dowód wywołania skopiowany pod inny pakiet opłat nie przechodzi weryfikacji.
Gaz (gas) to miara ciężaru przebiegu w maszynie, w obwodzie od zbioru ograniczeń 8. Tablica CPU zawiera kolumnę GAS, która zaczyna od 1 i dodaje 1 za każdy wiersz, 3 za wiersz absorpcji Poseidon2, 64 za wiersz SHA256 i 192 za wiersz KECCAK. W wierszu HALT obwód dowodzi, że GAS ≤ GAS_LIMIT, za pomocą czterech kończyn (limbs) różnicy ze sprawdzaniem zakresu, które nigdy nie są ujawniane: limit jest publiczny, a gaz zużyty przez przebieg nie. Weryfikator odrzuca, zanim zbuduje klucz, limit wyższy niż ten, którego mógłby potrzebować nagłówek samego dowodu:
gas_max(t, k, s) = (2ᵗ − 1) + 2ᵗ⁻² + 191·(2ᵏ / 32) + 63·(2ˢ / 64)
gdzie k i s to zadeklarowane logarytmiczne wysokości tablic keccak i sha256, a każdy składnik jest zerem dla nieobecnej tablicy. Pułap nagłówka poziomu 10 bez hashowania to 1 279, a poziomu 14 to 20 479. Łańcuch wycenia wywołanie według jego zadeklarowanego limitu (Opłaty).
5. Zamiana reguł na tożsamości wielomianowe
Każda kolumna jest listą liczb w ciele Goldilocks (Goldilocks field), czyli liczb całkowitych modulo p = 2⁶⁴ − 2³² + 1. Lista n liczb jest tym samym co wielomian stopnia mniejszego niż n: ten, który przechodzi przez te liczby, gdy rozłoży się je na podgrupie {1, ω, ω², …} ciała. Goldilocks ma takie podgrupy każdego rozmiaru będącego potęgą dwójki aż do 2³², więc przekształcenie to szybka transformata Fouriera.
Reguła maszyny staje się wówczas równaniem między wielomianami kolumn. „Licznik programu rośnie o cztery, chyba że instrukcja wykonuje skok” staje się, schematycznie:
is_jump(x)·(pc(ωx) − target(x)) + (1 − is_jump(x))·(pc(ωx) − pc(x) − 4) = 0
dla każdego x w podgrupie, gdzie pc(ωx) oznacza „następny wiersz”. Pełny zbiór takich reguł tablicy to jej AIR (algebraic intermediate representation, algebraiczna reprezentacja pośrednia). Reguły CPU i ALU osiągają stopień 8; pozostałe mieszczą się między 2 a 4.
Wielomian, który zeruje się w każdym punkcie podgrupy, jest podzielny przez Z(x) = xⁿ − 1. Dowodzący łączy wszystkie reguły, z losowymi wagami, w jeden wielomian C(x) i dzieli go przez Z(x). Jeśli każda reguła zachodzi w każdym wierszu, iloraz jest prawdziwym wielomianem niskiego stopnia. Jeśli jedna reguła zawodzi w jednym wierszu, nie jest. Pytanie „czy każdy krok 16 000-cyklowego przebiegu był poprawny?” zamieniło się w „czy ta funkcja jest bliska wielomianowi niskiego stopnia?”, a na to pytanie można odpowiedzieć, patrząc na kilka losowo wybranych wartości.
6. Zobowiązanie, zanim poznane zostaną pytania
Dowodzący musi ustalić swoje wielomiany, zanim zobaczy losowe pytania weryfikatora, bo inaczej mógłby dopasować do nich odpowiedzi. Oblicza każdą kolumnę na dziedzinie ośmiokrotnie większej niż tablica (blow-up 8, współczynnik kodu 1/8) i publikuje korzeń Merkle’a nad tymi wartościami. Ta nadmiarowość jest kodem Reeda–Solomona: wielomian niskiego stopnia jest wyznaczony przez dowolną ósmą część swoich wartości, więc oszust, który zmieni choćby jeden wiersz śladu, musi zmienić większość rozszerzonego słowa kodowego, by pozostać spójnym, a losowe wyrywkowe kontrole trafią na uszkodzenie.
Drzewa Merkle’a używają Poseidon2 nad Goldilocks: szerokość 8, S-box x⁷, 4 pełne rundy, 22 rundy częściowe i 4 pełne rundy, skróty złożone z czterech elementów ciała (około 256 bitów). Każdy liść jest hashowany z czterema elementami świeżej losowości, więc korzeń nie ujawnia niczego o leżących pod nim wartościach. Ta sama permutacja jest zobowiązaniem do noty (note commitment) i hashem nullifiera wewnątrz maszyny. Jej 86 stałych rundowych to dosłowna tablica w kodzie źródłowym węzła (poseidon2_constants.rs), więc żadna aktualizacja biblioteki nie może ich zmienić.
7. Wyrywkowa kontrola z FRI
Weryfikator ma teraz korzenie Merkle’a. Najpierw wiąże iloraz ze śladem: wybiera losowy punkt z spoza dziedziny tablicy, prosi o wartość każdej kolumny i ilorazu w tym punkcie oraz sprawdza bezpośrednio, że C(z) = Q(z)·Z(z). Pozostaje potwierdzić, że zatwierdzone funkcje naprawdę są wielomianami niskiego stopnia, i właśnie to testuje FRI.
FRI działa przez składanie (folding). Podziel wielomian na część parzystą i nieparzystą, f(x) = fₑ(x²) + x·fₒ(x²). Dla losowego β od weryfikatora f′(y) = fₑ(y) + β·fₒ(y) ma o połowę niższy stopień na dziedzinie o połowę mniejszej. Dowodzący zobowiązuje się do f′, dostaje nowe β i składa ponownie (Rand składa do ośmiu naraz w każdej rundzie), aż zostanie stała, którą wysyła jawnie. Zapytanie (query) wybiera wtedy jedną losową pozycję w pierwszej warstwie i śledzi ją w dół, sprawdzając w każdej warstwie, że wartości otwarte wyżej składają się na tę z warstwy niższej. Funkcja, która była daleko od wielomianu niskiego stopnia, nie zdoła utrzymać spójności wszystkich tych obietnic.
Rand wykonuje 80 zapytań. Zgodnie z udowodnionym twierdzeniem o przerwach bliskości (proximity gaps) każde zapytanie zostawia oszustowi co najwyżej 9/16 szans, więc 80 zapytań daje (9/16)⁸⁰ ≈ 2⁻⁶⁶·⁴. Według hipotezy ethSTARK, na której opierają się wdrożone systemy STARK, każde zapytanie zostawia 1/8, co jest wartością znacznie mniejszą.
8. Usunięcie rozmowy
Wszystko powyżej opisano jako dialog, ale transakcja musi być jedną wiadomością, którą każdy walidator może sprawdzić samodzielnie. Przekształcenie Fiata–Shamira usuwa weryfikatora: dowodzący oblicza każde „losowe” wyzwanie jako hash wszystkiego, do czego dotąd się zobowiązał, za pomocą gąbki dupleksowej Poseidon2. Zmiana któregokolwiek zobowiązania zmienia każde następne wyzwanie, więc dowodzący nie może dobrać zobowiązań do wyzwań.
Dwa dalsze środki zamykają luki, które to zostawia:
- Rozszerzenie ciała. Ciało o
2⁶⁴elementach jest zbyt małe, by losować z niego wyzwania: oszust potrzebuje, by wyzwanie trafiło w jedną z nielicznych złych wartości wielomianu, i odnosi sukces z prawdopodobieństwem okołoN/2⁶⁴, niezależnie od liczby zapytań. Dlatego ślad pozostaje w Goldilocks, ale każde wyzwanie jest losowane z jego rozszerzenia kwadratowego o około2¹²⁸elementach. - Grinding. Zanim zostaną wyznaczone pozycje zapytań, dowodzący musi znaleźć nonce, którego hash z transkryptem zaczyna się od 20 zerowych bitów, czyli około miliona hashy. Uczciwy dowodzący płaci to raz. Oszust, który przelosowuje pozycje zapytań, aż ominą jego fałszerstwo, płaci to przy każdej próbie. To jedyny proof of work w całym Rand i nie ma on nic wspólnego z konsensusem.
Podsumowując: 66,4 bitu z zapytań plus 20 z grindingu daje 86 bitów udowodnionych i około 100 bitów przy hipotezie, gdzie ograniczeniem jest składnik rozszerzenia ciała.
9. Skąd bierze się wiedza zerowa
STARK sam w sobie nie jest zero-knowledge: wartości otwarte na pozycjach zapytań są wartościami wielomianów śladu i bez należytej staranności przeciekają. Konfiguracja Rand dodaje cztery warstwy:
- każdy zatwierdzony wielomian jest zrandomizowany (ukrywający FRI z Plonky3 domieszkowuje cztery losowe słowa kodowe), więc 80 otwartych wartości może zostać odtworzonych, co do rozkładu, przez kogoś, kto nigdy nie widział śladu;
- każdy liść Merkle’a jest solony, więc korzenie i ścieżki uwierzytelniające nie ujawniają niczego;
- losowość dowodzącego jest pobierana świeżo z systemu operacyjnego dla każdego dowodu, więc dwa dowody tej samej płatności wyglądają na niepowiązane;
- każda zadeklarowana tablica ma co najmniej
2⁷wierszy, a każdy końcowy składnik LogUp jest zaślepiony (zbiór ograniczeń 7). Dowód otwiera każdą tablicę w 80 punktach zapytań i dwóch punktach poza dziedziną, a ukrywające zobowiązanie domieszkowuje tyle losowych wierszy, ile tablica ma wierszy, więc tablica jest ukryta tylko wtedy, gdy ma co najmniej 82 wiersze. 128 jest najmniejszą potęgą dwójki, która to spełnia.
Warstwa 4 to korekta. Przegląd z 2026-09-27 odzyskał z produkcyjnych dowodów prywatne wejścia wywołań o 31 lub mniej słowach wejściowych, odczytał liczby pobrań programu z jego krótkiej tabeli programu oraz odróżnił rzeczywiste sloty wejściowe pakietu od atrap po jego nieoślepionych terminalach. Chodzi o dowody wytworzone w chain 15 i wcześniejszych. Chain 16 (zbiór ograniczeń 7, z bezgałęziowym guestem pakietu) zamknął wszystkie trzy luki, a chain 18 do 20 zachowują te poprawki.
Gwarancją jest statystyczna wiedza zerowa w modelu losowej wyroczni. To, czego dowód nie ukrywa, jest ściśle określone: jego poziom (tier), zadeklarowane wysokości tabel oraz jego wartości publiczne, w tym limit gazu. Zadeklarowana wysokość keccak lub sha256 równa 0 oznacza, że wykonanie nigdy nie użyło tego skrótu, więc nagłówek dowodu wywołania pokazuje, czy wywołanie haszowało, a powyżej progu, jak często, z dokładnością do czynnika dwa. W przypadku pakietu wartościami i polami publicznymi są kotwica, cztery nullifiery, cztery zobowiązania, opłata, spalenia, aktywo spalenia, czas, zobowiązanie autoryzacji i powiązanie transakcji. Aktywo przenoszone przez transfer jest prywatnym wejściem, więc płatność w zUSD i płatność w RAND wyglądają w łańcuchu tak samo. hc jest deterministyczne, więc każdy może stwierdzić, że dowód dotyczy guesta pakietu (dotyczy go każdy dowód przenoszący wartość) albo, w przypadku wywołania, który wdrożony program został uruchomiony.
10. Co sprawdza walidator
Machine::verify_public(hc, words, proof) wykonuje, zaczynając od najtańszych kroków:
- zadeklarowany w dowodzie poziom (tier) i wysokości tabel są ograniczone (
check_declared_heights), zanim cokolwiek zostanie na ich podstawie zwymiarowane, więc wadliwy nagłówek kosztuje kilka porównań, a nie wielosekundowe budowanie klucza; pakiet musi deklarować tier 14 bez żadnej z tabel haszujących, a dowód autoryzacji tier 10; - jest 35 wartości publicznych, każda będąca kanonicznym elementem ciała, przy czym
pv[HC] = hc,pv[TIER]jest równe tierowi dowodu, każde słowo wyjściowe jest mniejsze niż2³², aGAS_LIMITnie przekraczagas_maxz nagłówka; - zbiorczy STARK jest weryfikowany względem klucza weryfikatora buforowanego według kształtu
(tier, program, input, keccak, sha256, public heights), a nie według programu; H_PUBjest równe skrótowiwords, który dla pakietu jest powiązaniem transakcji obliczonym przez sam węzeł.
Przed tym wszystkim węzeł sprawdza każdy przesłany lub rozgłoszony dowód pod kątem kanonicznego kodowania: jego harmonogram składania FRI musi być tym, który uczciwy prover tworzy dla stopni deklarowanych w nagłówku. Od fullnode v0.6.8 (audyt v6, ZKV-4) to sprawdzenie najpierw odrzuca każdy zadeklarowany stopień powyżej 25 bitów (MAX_HONEST_DEGREE_BITS, czyli pułap 2²⁴ tabeli pamięci plus podwojenie wynikające z wiedzy zerowej, największy stopień, jaki niesie przyjęty dowód). Przed wprowadzeniem tej granicy nagłówek deklarujący ogromny stopień zmuszał węzeł do ponownego wyprowadzania olbrzymiego harmonogramu składania w jego pętli zdarzeń, blokując ją tak długo, jak chciał nadawca; weryfikator i tak odrzuciłby taki dowód, więc granica zmienia jedynie koszt odmowy. Panika wewnątrz tego sprawdzenia odrzuca teraz tę jedną transakcję, zamiast zatrzymywać węzeł.
Następnie księga przelicza skrót pakietu D z bad = 0 i porównuje go z ośmioma słowami wyjściowymi dowodu. W chain 20 wymaga ona również, aby GAS_LIMIT dowodu pakietu było dokładnie równe genesisowemu bundle_gas_limit, czyli 20 479, a dowodu autoryzacji 1279, tak by każdy transfer deklarował te same wartości. Przy rozgrzanym kluczu całe sprawdzenie trwa około 16 ms.
Parametry i powody ich wyboru
Każda liczba poniżej należy do konsensusu: weryfikator ją sprawdza, dowód wytworzony z jakąkolwiek inną wartością nie przejdzie weryfikacji, a zmiana którejkolwiek oznacza nowy zbiór ograniczeń i hard fork (Zbiory ograniczeń). Profil FRI ustala plik genesis (fri_profile: production w chain 20), nigdy dowód.
| parametr | wartość | dlaczego ta wartość |
|---|---|---|
| ciało bazowe | Goldilocks, p = 2⁶⁴ − 2³² + 1 | 64-bitowa liczba pierwsza, której arytmetyka to kilka instrukcji maszynowych, z podgrupami multiplikatywnymi każdego rozmiaru będącego potęgą dwójki aż do 2³², więc zamiana kolumny w wielomian to szybka transformata Fouriera. Ślad znajduje się tutaj, ponieważ jest to tanie ciało. |
| ciało wyzwań | rozszerzenie kwadratowe F_p² (≈ 2¹²⁸) | Wyzwania losowane z samego Goldilocks zostawiłyby dolną granicę solidności rzędu N/2⁶⁴, której nie obniży żadna liczba zapytań. Rozszerzenie spycha ten składnik do co najwyżej 2⁻¹⁰⁰ dla śladów do 2²⁵ wierszy. Za większe ciało płacą tylko wyzwania; ślad nie. Rozszerzenie sześcienne (≈ 2¹⁹²) to wskazany zapas na wypadek, gdyby kiedykolwiek wymagane było 128 bitów. |
| współczynnik rozszerzenia | 8 (stopa ρ = 1/8, log_blowup = 3) | Dwa powody naraz. Tabele CPU i ALU mają reguły stopnia 8, a przy fladze zero-knowledge iloraz ma stopień 9, czyli najwięcej, ile może unieść współczynnik 8; reguła wyższego stopnia wymusiłaby współczynnik 16. Ponadto zdolność każdego zapytania do wykrycia oszusta zależy od stopy: (1 + ρ)/2 = 9/16 na zapytanie w wersji udowodnionej, ρ = 1/8 w wersji przypuszczalnej. Większy współczynnik pozwoliłby użyć mniej zapytań, kosztem proporcjonalnie większego rozszerzenia niskiego stopnia, które prover musi obliczyć i przechowywać w pamięci. |
| zapytania FRI | 80 | Liczba, która utrzymuje udowodnioną solidność na poziomie około 86 bitów. Raz obniżono ją do 27 (zestawy ograniczeń 3 i 4), co spełniało przypuszczalny cel 100 bitów (3·27 + 20 = 101), ale zostawiało tylko około 42 udowodnione bity; audyt zk z 2026-09-12 (ustalenie ZM1) przywrócił 80. Zapytania to większość rozmiaru dowodu, dlatego dowód pakietu ma ≈ 1,50 MB. Udowodnione 100 bitów wymagałoby 97 zapytań. |
| grinding | 20 bitów, w fazie zapytań | Zanim zostaną wylosowane pozycje zapytań, prover musi znaleźć nonce, którego skrót zaczyna się od 20 zerowych bitów: około miliona skrótów, płaconych raz przez uczciwego provera i przy każdej ponownej próbie przez oszusta losującego pozycje od nowa. Tanio dodaje to 20 bitów; więcej grindingu (34 bity dałyby też udowodnione 100) spowalnia każdy uczciwy dowód. Faza zobowiązania nie ma grindingu. |
| arność składania | do 8 na rundę (max_log_arity = 3) | Składanie przez osiem zamiast dwóch daje trzy razy mniej warstw FRI, więc każde zapytanie otwiera mniej ścieżek Merkle’a, a dowód jest mniejszy, kosztem nieco większej pracy na warstwę. |
| wielomian końcowy | stała (log_final_poly_len = 0) | FRI składa się aż do końca i wysyła jedną wartość jawnie, co daje weryfikatorowi najprostszą kontrolę końcową. |
| hasz (w układzie, Merkle, transkrypt) | Poseidon2, szerokość 8, S-box x⁷, rundy 4 + 22 + 4, stopa 4, pojemność 4 | Hasz zbudowany tak, by był tani jako arytmetyka ciała, więc haszowanie wewnątrz dowodu (zobowiązania, nullifiery, ścieżki Merkle’a, skrót programu) kosztuje wiersze, a nie tysiące operacji bitowych. Wykładnik S-boxa nie może mieć wspólnego czynnika z p − 1 = 2³²·3·5·17·257·65537, a 7 jest najmniejszym, który to spełnia. Szerokość 8 przy stopie 4 daje skrót o czterech elementach, około 256 bitów. 86 stałych rund to literalna tabela w węźle (poseidon2_constants.rs). |
| szerokość skrótu | 4 elementy ciała (≈ 256 bitów) | Wystarcza dla ról opartych na przeciwobrazie (nullifiery, zobowiązania: 128 bitów wobec algorytmu Grovera). Nie wystarcza dla 128-bitowej odporności kwantowej na kolizje w transkrypcie (2²⁵⁶/³ ≈ 2⁸⁵); projekt z whitepaper wymaga 384-bitowego skrótu transkryptu, a działający łańcuch go nie przyjął. |
| ukrywanie | 4 losowe słowa kodowe w FRI; 4 elementy soli na liść Merkle’a; świeża losowość systemu operacyjnego przy każdym dowodzie | Minimum, dzięki któremu 80 otwartych pozycji i ścieżki Merkle’a można zasymulować wyłącznie z wartości publicznych, więc dowód jest statystycznie zero-knowledge. Dowody tej samej płatności wykonanej dwa razy wyglądają na niepowiązane. |
| minimalny rozmiar tabeli | 2⁷ wierszy dla każdej zadeklarowanej tabeli (zestaw ograniczeń 7) | Dowód otwiera 80 punktów zapytań i 2 punkty poza dziedziną każdej tabeli, łącznie 82, a tabela o h wierszach jest maskowana przez h losowych wierszy. 64 wiersze to za mało, a 128 to następna potęga dwójki. Liczba zapytań powyżej 126 wymagałaby 2⁸. |
| zaślepka końcowa | jedna wartość z F_p² na tabelę, na magistrali BLIND (zestaw ograniczeń 7) | Wartość końcowa LogUp jest liczona z surowego śladu, więc bez zaślepki jest niesolonym zobowiązaniem do swojej tabeli. Zaślepka jest elementem ciała rozszerzonego, ponieważ jedna współrzędna z ciała bazowego ograniczyłaby przesunięcie do wąskiego zbioru, względem którego można by testować kandydujący świadek. |
| wagi gazu | 1 za wiersz; 3 za wiersz absorpcji Poseidon2; 64 za wiersz SHA256; 192 za wiersz KECCAK (zestaw ograniczeń 8) | Każda waga liczy wiersze, które dany wiersz wciąga do innych tabel: wiersz absorpcji jedną 32-wierszową permutację, wiersz SHA256 64 wiersze po 466 kolumn, wiersz KECCAK 32 wiersze po 2 612 kolumn i 100 wierszy pamięci. |
| maksymalny stopień ograniczeń | 8 | Wyznaczony przez powyższy współczynnik rozszerzenia; stopień każdej tabeli jest przypięty testem, aby nowa reguła nie mogła go po cichu przekroczyć. |
| poziomy | 10, 12, 14, 16, 18, 20 | Tabele mają rozmiar w sześciu stałych krokach, każdy czterokrotnie większy od poprzedniego, a dowód ujawnia tylko swój poziom: zgrubny, dopełniony przedział długości przebiegu, a nie liczbę cykli. Każdy pakiet ma poziom 14, a każdy dowód auth poziom 10; wywołania są ograniczone do poziomu 14 od fullnode v0.5.6. |
| maksymalny ślad | 2²⁴ wierszy (2²⁵ z bitem zero-knowledge) | Rozmiar, dla którego udowodniono składnik ciała rozszerzonego; dłuższy ślad jest odrzucany, zamiast być dowodzony przy twierdzeniu o solidności, które nie zachodzi. |
| wartości publiczne | 35 elementów ciała | Wejściowy pc, poziom, osiem słów wyjścia, trzy ośmiosłowowe skróty hc, H_IN, H_PUB oraz zadeklarowany limit gazu: najmniejsze twierdzenie, które wskazuje program, wejście prywatne, wejście publiczne i ograniczenie pracy. Do zestawu ograniczeń 7 włącznie było ich 34. |
| limit rozmiaru dowodu | domyślnie 2 MiB; 4 MiB na chain 20 (max_proof_bytes) | 2 MiB to więcej niż każdy dowód, który wytwarzają wdrożone programy gościa bez tabeli haszującej, i mniej niż dowód z keccakiem (≈ 3,3 MB); genesis chain 20 ustawia 4 194 304, by dopuścić oba, tak jak chain 17–19. Chain 13–16 ustawiały 8 MiB. Limit jest sprawdzany przed zdekodowaniem dowodu. |
Co te liczby dają w sumie: 86 bitów solidności udowodnionej, około 100 przy przypuszczeniu ethSTARK (gdzie granicą jest ciało rozszerzone, a nie zapytania) i około 85 bitów wobec przeciwnika kwantowego, wyznaczone przez 256-bitowy skrót transkryptu. Whitepaper celowo podaje obie wartości solidności obok siebie: każdy wdrożony FRI-STARK opiera swoje ostatnie bity na przypuszczeniu, a czytelnik dobierający wdrożenie powinien wiedzieć, które założenie niesie które bity.
Dowody o dowodach
Przy około 1,50 MB dowód pakietu jest ciężki, obok niego jedzie 1,37-megabajtowy dowód auth, a każdy węzeł przechowuje je wszystkie. Zbudowanym remedium jest rekursja: dowód, którego twierdzenie brzmi „zweryfikowałem te dowody”. Fullnode zawiera w tym celu drugą, mniejszą maszynę, maszynę rekursji, której natywnym słowem jest element Goldilocks, a instrukcjami są własne operacje weryfikatora (arytmetyka ciała, Poseidon2, kroki Merkle’a i FRI). Jej program agregujący odtwarza pełną weryfikację N dowodów pakietów i publikuje skrót ich wartości publicznych, związany z łańcuchem, agregatorem i nonce. Weryfikacja jednego dowodu pakietu wewnątrz niej kosztowała 1 968 619 wierszy w zestawie ograniczeń 6 i 2 044 506 w zestawie 7; w zestawie 8 nie zmierzono jej ponownie. Jest zaimplementowana i wyłączona w genesis na każdym dotychczasowym łańcuchu, chain 20 włącznie. Program agregujący weryfikuje wyłącznie dowody pakietów, więc w łańcuchu z podzieloną autoryzacją dowód auth pozostałby. Jej produkcyjne dowody wymagają też więcej pamięci, niż planowano: zmierzono w zestawie ograniczeń 7 co najmniej 133 GB (Wkrótce: agregacja bloków).
Czego schemat nie twierdzi
- Margines kwantowy wynosi około 85 bitów, a nie 128. Kwantowe wyszukiwanie kolizji dla
n-bitowego skrótu kosztuje około2ⁿ/³. Projekt z whitepaper przewiduje 384-bitowy skrót transkryptu; działający łańcuch używa 256-bitowego skrótu Poseidon2. Jego poszerzenie oznacza nowy zbiór ograniczeń i hard fork. - 100 bitów to hipoteza; 86 jest udowodnione. Podniesienie udowodnionej wartości do 100 kosztowałoby 97 zapytań, czyli około jedną piątą więcej danych zapytań na dowód.
- Stałe Poseidon2 pochodzą z losowania z ziarna, a nie z opublikowanej instancjacji. Nie jest znany żaden atak na tej podstawie; przyjęcie standardowego zestawu zmieniłoby każdy skrót w łańcuchu.
- Niektóre twierdzenia są sprawdzane obok dowodu, a nie w nim. Weryfikator porównuje publiczną wartość
TIERz kształtem dowodu, ale obwód jej nie ogranicza (audyt ZL1), a dyscyplina taint w gueście bundle opiera się na argumentacji strukturalnej, harnessie osłabionego guesta i fuzzingu mutacyjnym, a nie na weryfikacji maszynowej. - Nagłówek dowodu nie jest ukryty. Tier i wysokości dwóch tabel haszujących są jawne, więc dowód wywołania pokazuje, czy wywołanie użyło
KECCAK, czySHA256. Istnieje opcja provera, która dopełnia obie tabele, ale żaden portfel jej nie ustawia: zwiększa ona dowód tier-10 z około 1,3 MB do około 3,6 MB. - Syscall
POSEIDON2nie jest iniektywny dla danych wejściowych o zmiennej długości. Pozostaje taki, jaki jest, ponieważ zależą od niego każde zobowiązanie, nullifier, korzeń i skrót programu. Żaden dostarczany guest nie używa jeszczePOSEIDON2_LEN. - Dowodzenie jest wolne i wykonuje je nadawca lub prover, z którym nadawca się łączy w parę: około 100 s i 5,7 GB na procesorze laptopa dla bundle. Jądra GPU po raz pierwszy uruchomiono na GPU (NVIDIA H100) 2026-09-28 i zgadzały się z referencją CPU; portfel nadal dowodzi na CPU.
Gdzie czytać dalej
- Sekcja whitepaper „The Zero-Knowledge Scheme: How a Rand Proof Works” (
randprotocol.tex) omawia te same siedem składników wraz z twierdzeniami o poprawności. - Artykuł implementacyjny (
randprotocol_implementation.tex) określa zestaw instrukcji i syscalle, guest bundle, sponge i skróty, każdą tabelę i szynę, konfigurację maszyny oraz maszynę wirtualną rekurencji. - Kod:
crates/randprotocol-zkvm/src/w fullnode —emulator.rs,tables/,machine.rs,gas.rs,guests.rs,hash.rs,poseidon2_constants.rs.