Rand Protocol
Dokumentacja
Przykładowe programy

Cztery programy na chain 20

Cztery kompletne poufne programy wdrożone w testnet: anonimowe głosowanie, dowód zobowiązań, rejestr off-chain i prywatna decyzja kredytowa. Zawiera identyfikatory programów, wspólną konfigurację, zmiany, jakie chain 18 wprowadził dla autora programu, oraz instrukcję samodzielnego wywołania każdego z nich.

Te cztery programy to kompletne zastosowania poufnego wywołania: problem, interfejs, gość w Rust, narzędzia po stronie hosta oraz wdrożenie i wywołanie w działającej sieci testnet. Są dłuższe niż samouczki, a każdy opiera się na innej części maszyny. Napisz program w Rust lub C to przewodnik po modelu, z którego korzystają wszystkie. Przeczytaj go najpierw, jeśli read_input, segment publiczny lub osiem slotów wyjściowych są dla Ciebie nowe.

Wszystko tutaj uruchomiono po raz pierwszy w chain 18 z fullnode v0.6.7, 2026-09-30, a każde wyjście na tych stronach pochodzi z tych uruchomień. Programy nie przechodzą przez zmianę łańcucha, więc 2026-10-01 wszystkie cztery wdrożono ponownie, bez zmian, w chain 19, a potem w chain 20 (fullnode v0.6.8), za każdym razem powtarzając wywołania. Identyfikator programu jest skrótem kodu i segmentu publicznego, a nie łańcucha, więc każdy identyfikator jest taki sam; skróty transakcji w zapisach przebiegu pochodzą z chain 18, a poniższa tabela odsyła do transakcji z chain 20. W chain 20 program może też przechowywać stan między wywołaniami (RPL-2); te cztery z tego nie korzystają, a programy, które to robią, opisują Stan programu oraz repozytorium example-programs.

Cztery programy

programco dowodzi wywołanieopiera się nasłowapoziom
Anonimowe głosowaniewywołujący należy do opublikowanej listy, nie mówiąc do której, i nie głosował wcześniejKeccak-256, segment publiczny42712
Dowód zobowiązańprywatne salda klientów sumują się do nie więcej niż opublikowana rezerwa, pod korzeniem, który każdy klient może sprawdzićSHA-256, drzewo budowane wewnątrz programu99414
Księga poza łańcuchempartia przelewów przenosi księgę z jednego stanu do następnego, przy czym każdy nadawca jest uprawniony i nic nie powstaje z niczegoPoseidon2, stan przenoszony między wywołaniami77314
Prywatna decyzja kredytowaopublikowany model zatwierdza lub odrzuca prywatne dane wnioskodawcy, za które poręczył atestatorarytmetyka całkowitoliczbowa, model w segmencie publicznym46212

Wszystkie cztery działają w chain 20. Każde wywołanie w tabeli powtarza, z tymi samymi danymi wejściowymi, wywołanie z chain 18 pokazane na stronie danego programu:

programidentyfikator programuwdrożony w chain 20 wpierwsze wywołanie w chain 20
anonimowe głosowaniead2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970f6ae5528…0f823a304e7a…427b
dowód zobowiązańfce8474dfdf80b9733e04fb72d4d8d19c40ba5ab646b3d867322daf4b9b599c0a8bc281b…17d96116c15f…d62a
księga poza łańcuchemc4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab72cc95f2…3d519c9998bb…66eb
prywatna decyzja kredytowa7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c413e6658…e42bd3ceb306…124a

Eksplorator wyświetla każdy program i jego wywołania pod adresem randscan.org/programs. Łańcuch testnet jest co jakiś czas zastępowany nowym, a programy nie przechodzą dalej: po następnej zmianie tych identyfikatorów już nie będzie i programy trzeba będzie wdrożyć ponownie.

Konfiguracja

Toolchain. Goście budują się za pomocą rand-guest z repozytorium circuits, w commicie, który fullnode v0.6.7 i v0.6.8 oba wendorują:

git clone https://github.com/randprotocol/zkp-circuits circuits
cd circuits
git checkout aeacf31                      # branch feat/v067-circuits
rustup toolchain install 1.98.1
rustup target add riscv32im-unknown-none-elf --toolchain 1.98.1
(cd rand-guest && cargo +1.98.1 build --release)
alias rand-guest="$PWD/rand-guest/target/release/rand-guest"

Commit ma znaczenie z dwóch powodów. hc jest funkcją źródła i toolchainu, więc gość zbudowany ponownie w innym commicie guest-sdk może dostać inny hc. Ponadto sygnatury SDK się zmieniły: w aeacf31 funkcje keccak i sha256_compress przyjmują referencje do tablic, tam gdzie starsze commity przyjmowały surowe wskaźniki.

Źródła. Wszyscy czterej goście, ich skrypty hosta i dane demonstracyjne to jedno pobranie. Rozpakuj je wewnątrz checkoutu, ponieważ rand-guest build znajduje guest-sdk, szukając w górę od katalogu gościa:

mkdir -p my-guests
curl -sSf https://randprotocol.org/examples/rand-example-programs.tar.gz | tar -xz --strip-components=1 -C my-guests
ls my-guests
# allowlist  ledger  ledger-host  score  solvency

Pliki można też przeglądać pojedynczo pod adresem https://randprotocol.org/examples/programs/, na przykład allowlist/src/main.rs.

Portfel. Wdrażanie i wywoływanie wymagają portfela rand z fullnode v0.6.8 (cargo build --release -p randprotocol-client w checkoucie fullnode na tagu v0.6.8 albo rand dołączony do wydania v0.6.8), klucza i RAND. Starszy portfel nie może wykonywać transakcji w chain 20: chain 20 wiąże każdy dowód i każdą podpisaną wiadomość ze swoim genesis, a portfel v0.6.7 tego nie robi.

RPC. Publiczny punkt końcowy, https://rpc.randprotocol.org, wystarcza do wszystkiego na tych stronach: czytania programów i potwierdzeń, synchronizacji portfela, wdrażania i wywoływania. Pierwsza synchronizacja portfela czyta każdy nagłówek bloku i każdy blok zawierający note, co przy wysokości chain 18 z 2026-09-30 dawało 645 żądań i trwało około trzech minut przez ten punkt końcowy. Punkt końcowy dopuszcza dla każdego adresu 1,200 odczytów i 6 wysłań na minutę. Własny węzeł (Uruchamianie węzła) nie ma takich limitów.

export RAND_RPC=https://rpc.randprotocol.org
export RAND_KEY=$HOME/wallet.key.json

W chain 18 jeden z głosów policzonych na stronie anonimowego głosowania, fdbcc960…fc2b, został dowiedziony na laptopie i wysłany wyłącznie przez publiczny punkt końcowy. W chain 19 i 20 tą drogą poszło za każdym razem wszystkie trzynaście transakcji: cztery wdrożenia i dziewięć wywołań.

Co chain 18 zmienił dla programu

Wywołanie deklaruje limit gas i za niego płaci. Zestaw ograniczeń 8 mierzy każde uruchomienie. Zwykły cykl to 1 gas. Wyjątkiem są syscalle skrótów:

instrukcjagas
dowolna instrukcja RV32IM, HALT, WRITE_OUTPUT, READ_INPUT, READ_PUBLIC1
POSEIDON2 na n słowach3 + 3 × ⌈n / 4⌉
SHA256, jedna kompresja64
KECCAK, jedna permutacja192

Dowód niesie jedną nową wartość publiczną, zadeklarowany limit, a obwód wymusza, by gas uruchomienia był nie większy od niego. Dokładny gas pozostaje prywatny. rand call najpierw uruchamia program w emulatorze i wypisuje oba:

gas: 5636 (declaring 6144, tier 12)

Domyślnie portfel zaokrągla dokładny gas w górę do wielokrotności 2^(tier − 2), czyli 1,024 na poziomie 12 i 4,096 na poziomie 14, więc zadeklarowany limit niewiele mówi o uruchomieniu. --gas-limit max deklaruje pułap poziomu i nie mówi nic poza samym poziomem. --gas-limit N deklaruje dokładną wartość.

Opłata zależy od zadeklarowanego limitu i od bajtów.

floor = 0.001 RAND  +  gas price × declared limit  +  byte price × ⌈bytes / 1024⌉

Ceny zaczynały od 100 jednostek za gas i 800 jednostek za KiB (jednostka to 10⁻⁹ RAND) i z każdym blokiem przesuwają się nieco w stronę wartości docelowej. rand_getLimits podaje bieżące. Portfel dodaje zapas w wysokości dwóch kroków ceny, czyli współczynnik 1.125², ponieważ wywołanie trafia do łańcucha dwa lub trzy bloki po wycenie.

Jeden szczegół rozstrzyga o większości opłaty. Od chain 18 dowód wywołania jest związany z własną transakcją, więc portfel musi ustalić opłatę, zanim dowód powstanie, i wycenia dowód według limitu łańcucha wynoszącego 4 MiB zamiast jego rzeczywistego rozmiaru. Dla anonimowego głosowania daje to (1,000,000 + 100 × 6,144 + 800 × 4,097) × 1.125² = 6,191,437 jednostek, czyli 0.0062 RAND, z czego gas stanowi około jednej ósmej. Cztery wywołania na tych stronach kosztowały od 0.0057 do 0.0075 RAND każde. --fee nadpisuje wartość domyślną.

Każdy pakiet to dwa dowody. Chain 18 stosuje podzieloną autoryzację: pakiet, który płaci opłatę, niesie mały dowód autoryzacji poziomu 10 obok dowodu pakietu poziomu 14. Portfel tworzy oba i wypisuje po jednym wierszu dla każdego. Wywołanie to zatem trzy dowody: wywołania, autoryzacji i pakietu. Na laptopie z Apple Silicon cztery wywołania tutaj zajęły od trzech do pięciu i pół minuty każde, przy szczytowym zużyciu pamięci od 6.1 do 6.6 GB. Dowód pakietu to za każdym razem około dwóch minut z tego czasu. Dowód wywołania poziomu 12 dodaje pół minuty, a poziomu 14 dwie minuty.

Limity, w które musi się zmieścić wywołanie.

limitchain 18 do 20
poziomco najwyżej 14: 16,383 cykli, wraz z wierszami skrótów
permutacje Poseidon22,048 na poziomie 14, wraz ze skrótami programu, danych wejściowych i danych publicznych
permutacje Keccak128
kompresje SHA-256128
dowód wywołania4,194,304 bajtów (4 MiB)
program8,180 słów bez segmentu publicznego, kilka mniej z nim: chain 20, tak jak chain 18 i 19, odrzuca wdrożenie, którego nie zmieściłoby żadne wywołanie poziomu 14 (ProgramUncallable), niezależnie od tego, na co pozwala max_program_words (65,535)
segment publiczny32,768 słów, ustalone przy wdrożeniu
dane prywatne z zapieczętowanym transkryptem16,071 słów

rand-guest run --tier 14 sprawdza dwa pierwsze limity przed wdrożeniem. Tablica skrótów powiększa dowód. Dowód księgi poziomu 14, bez tablicy skrótów, ma 1.50 MB. Tablica SHA-256 podnosi dowód zobowiązań do 1.91 MB, a tablica Keccak podnosi dowód głosowania do 3.35 MB, czyli prawie do limitu 4 MiB.

Konwencje wspólne dla wszystkich czterech

  • Slot 0 to status, a 0 nigdy nie jest jego prawidłową wartością. Każdy wynik, także porażka, ma numer od 1 w górę, a porażka kończy działanie czysto, tak aby dało się ją udowodnić. Wyzerowanego w całości receipt nie da się pomylić z wynikiem.
  • Slot 7 pozostaje nietknięty. Guest napisany w Rust, który wpada w panikę, zapisuje tam 0xdeadbeef (3 735 928 559). Czytelnik sprawdza najpierw slot 7.
  • Sloty od 2 do 6 niosą 160-bitową wartość: nullifier, korzeń, powiązanie, zobowiązanie. Pełny 256-bitowy skrót to osiem słów i nie zostawiłby miejsca na status. Obcięcie do pięciu słów zachowuje 80 bitów odporności na kolizje, a każda strona mówi, co ta odporność chroni.
  • Kolejność bajtów wynika z funkcji skrótu. Wartości Keccak to słowa little-endian, a wartości SHA-256 to słowa big-endian, ponieważ tak każdy syscall odczytuje swój blok. Skrypt hosta i guest, które różnią się w tej kwestii, dają skróty, które po prostu nigdy się nie zgadzają.
  • Wszystko, co zmienia się między instancjami, jest w segmencie publicznym; wszystko, co zmienia się między wywołaniami, jest wejściem prywatnym. Korzeń głosowania, kwota rezerw i wagi modelu są ustalone przy wdrożeniu i związane z identyfikatorem programu. Wywołanie nie niesie własnych słów publicznych.
  • Słowa wejściowe w wierszu poleceń są dziesiętne. Strony zamieniają plik świadka we flagi za pomocą $(sed 's/[0-9][0-9]*/--input &/g' witness.txt).

Wywołaj program samodzielnie

Gdy źródła są rozpakowane, toolchain zbudowany, a portfel v0.6.8 zasilony, głosowanie jako jeden z członków demonstracyjnych to cztery polecenia:

cd my-guests/allowlist
python3 allowlist.py witness 10 1 demo-secrets.txt 6 1 > witness.txt    # member 6, choice 1
rand-guest build . --max-words 65535      # only needed to run it in the emulator first
rand call ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 \
    $(sed 's/[0-9][0-9]*/--input &/g' witness.txt)

Wywołanie nie wymaga obrazu: rand call pobiera z węzła kod programu i jego segment publiczny, sprawdza, czy ich skrót odpowiada identyfikatorowi, i generuje nad nimi dowód.

Odczyt wyniku nie wymaga ani portfela, ani węzła. Każdy receipt to jedno żądanie do publicznego punktu końcowego:

$ curl -s https://rpc.randprotocol.org -H 'content-type: application/json' \
    -d '{"jsonrpc":"2.0","id":1,"method":"rand_getReceipt","params":["8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666"]}'
{"id":1,"jsonrpc":"2.0","result":{"h_in":"ea71e7701e62d49cac37b239bef132178f481835027d62b34da45fd6cf2667c6","h_pub":"2296cfca8db9d38bd0b7579056c9dea3c8d8005df7b42e2b1e954a75fed67510","height":98335,"index":0,"outputs":[1,2,392974773,873526743,1921224601,3950868247,2248722827,0],"program":"ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970","tier":12,"tx":"8aa13d1f76d28f63a46c30bb09ef44b1ba35083ca80ba7301997f98e5f783666"}}

API eksploratora podaje wywołania programu w jednym żądaniu: https://randscan.org/api/v1/programs/<program id> zwraca call_count i recent_calls (Explorer API).

Czego żaden z nich nie robi

Program w Rand to dowód dotyczący obliczenia, a łańcuch nie działa na podstawie żadnego z jego wyników. Żaden z tych programów nie przenosi RAND ani tokena, nie przechowuje salda i nie pamięta poprzedniego wywołania. Głosy zlicza ten, kto czyta receipty. Zobowiązania są porównywane z rezerwami, które wymagają własnych dowodów. Salda księgi to liczby w drzewie poza łańcuchem. Decyzja kredytowa to bit, który pożyczkodawca sam wybiera, czy uszanować. Każda strona kończy się listą tego, co jej dowód pozostawia osobom z niego korzystającym.

Ostatnio zweryfikowano względem commitu fullnode 48dc04b (łańcuch 18) dnia 2026-09-30, a także circuits `aeacf31` (`guest-sdk/src/lib.rs`, `rand-guest`), the commit fullnode v0.6.7 vendors; `docs/fees.md`, `docs/zkvm.md`, `docs/cli.md` and `crates/randprotocol-client/src/main.rs` at fullnode `48dc04b`; the four deploys and the four calls were made on chain 18 on 2026-09-30, and every receipt was read back from `https://rpc.randprotocol.org`. Jeśli kod i ta strona się różnią, rację ma kod; prosimy o zgłoszenie.