Rand Protocol
Dokumentacja
Poradniki

Uruchamianie węzła

Zbuduj pliki binarne, utwórz klucz, zainicjuj katalog danych z genesis chain 20, połącz się z węzłami bootstrap, powiąż RPC, przytnij historię i zweryfikuj łańcuch - jako obserwator lub validator.

Użyj tej strony, aby uruchomić własny rand-node w sieci testnet. To także zalecany sposób prywatnego korzystania z portfela: węzeł, który sam prowadzisz, nie dowiaduje się od nikogo poza tobą, który liść zamierzasz wydać. Obserwator synchronizuje się, ponownie weryfikuje każdy dowód i obsługuje JSON-RPC; walidator robi to samo i uczestniczy w konsensusie, gdy jego klucz znajdzie się w zbiorze danej epoki.

Budowanie

Wymagania: Rust 1.98.1 (przypięty w rust-toolchain.toml; instaluje go rustup), kompilator C++ oraz cmake dla RocksDB. Crate zkVM ma zależności ścieżkowe do sąsiedniego checkoutu circuits/ repozytorium systemu dowodowego (randprotocol/zkp-circuits), który musi leżeć obok fullnode/, na commicie, z którego zwendorowano wydanie: w wierszu circuits: pliku crates/randprotocol-zkvm/guests-compiled/PROVENANCE.md (aeacf31 dla v0.6.8, tak jak dla v0.6.7). Pierwsze budowanie trwa od 10 do 20 minut.

git clone https://github.com/randprotocol/fullnode
cargo build --release          # target/release/rand-node, target/release/rand, target/release/rand-prover

Uruchom v0.6.8 (git checkout v0.6.8, commit c9c9bd3c, to wydanie, na którym działa flota; rand_getVersion zwraca sha węzła). Jego wydanie na GitHub zawiera rand-node, rand, rand-prover i SHA256SUMS, zbudowane na jednym hoście floty i niepodpisane. Chain 20 go wymaga: jego genesis zawiera pola, których nie czyta żaden wcześniejszy build (testnet, binding_domain, proof_window_blocks, program_state, grupy rotation i fees mostu, limity gazu oraz przyjmowanie do stakingu w drodze głosowania), a v0.6.8 wprowadza zmiany konsensusu, których starszy węzeł nie wykonuje. Dowody nadal powstają w zestawie ograniczeń 8, jak w chainach 18 i 19, a każdy pakiet nadal niesie obok dowodu pakietu dowód autoryzacji (podzielona autoryzacja, od chaina 17), więc genesis przypina te same dwa guesty: guest pakietu v3 (hc_bundle 60af094a…) i guest autoryzacji (hc_auth 1e4e347f…). Genesis zawiera też sekcję gas, hardening_v6 i envelope_bytes. Build v0.6.8 odrzuca genesis chainów 14, 15, 16 i 17 przy run i przy verify, na podstawie skrótu; genesis chaina 19 nadal potrafi uruchomić. Każdy węzeł używa buildu floty.

Chain 20: hard fork z całkowitym zatrzymaniem

Chain 20 ruszył 2026-10-01 o 06:17 UTC. Został wycięty ze snapshotu chaina 19 i wdrożony metodą all-stop, all-start: każdy węzeł chaina 19 zatrzymał się na wysokości 7700, a każdy węzeł wystartował na chainie 20 z v0.6.8. Nie była to aktualizacja kroczącą, z dwóch powodów. Chain 20 to nowy genesis, więc nie ma żadnego bloku wspólnego z chainem 19. A v0.6.8 wprowadza zmiany konsensusu z audytu v6, które nie mogą być wdrażane węzeł po węźle: CON-4 usuwa automatyczne zwalnianie blokady walidatora, a CH-1 dodaje pacemaker oparty na certyfikatach timeoutu. Wszystkich 26 walidatorów, ich klucze, identyfikatory peerów i bootstrapy zostało przeniesionych; kworum nadal wynosi 18 z 26. Chainy 19 i 18 są wycofane.

Przeniesienie węzła z chaina 19 na chain 20 wymaga więc nowego pliku binarnego, nowego pliku genesis i świeżego katalogu danych, jak poniżej. Portfele potrzebują v0.6.8, aby wysyłać na chainie 20: jego genesis wiąże każdy dowód i każdą podpisaną wiadomość ze swoim skrótem (binding_domain: 1), czego starszy portfel nie robi.

Klucze

rand-node keygen --out node.key.json      # prints the address
rand-node address --key node.key.json     # address, public key, libp2p peer id

Plik ma postać { "seed", "address", "public_key" }, z uprawnieniami 0600; tajny jest tylko seed. Obserwator również potrzebuje klucza — to tożsamość p2p. Klucz węzła nie jest kluczem portfela: do portfela użyj rand keygen.

Wygeneruj własny klucz i trzymaj go poza checkoutem. Repozytorium fullnode nie śledzi już żadnego klucza (audyt v3, OPS-1): pliki deploy/node-*.key.json i deploy/payout/*.key.json, które zostały zacommitowane dla chainów 8–13, to opublikowane seedy i nie wolno ich używać. Klucze walidatorów chaina 20 wygenerowano poza repozytorium i nigdy w nim nie były: jego 26 walidatorów genesis to osiemnaście kluczy wygenerowanych dla chaina 14 i osiem związanych na chainie 15, przenoszone przez każde kolejne cięcie.

Od v0.6.8 rand-node keygen odmawia zapisu do pliku --out, który już istnieje; v0.6.7 nadpisywał go bez pytania. Plik to jedyna kopia seeda. Zrób jego kopię zapasową, zanim od niego zacznie zależeć jakiekolwiek wiązanie (bond).

Inicjalizacja z genesis

Każdy węzeł w danym chainie potrzebuje identycznego co do bajta pliku genesis; rand-node init kopiuje go do katalogu danych i wypisuje jego skrót, który dla chaina 20 musi brzmieć 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135. Plik to deploy/genesis-chain20.json (sha256 0020247a…3a21). Został zacommitowany do gałęzi main fullnode wraz z zapisem uruchomienia (commit 74cfaca0), po tagu, więc checkout v0.6.8 go nie zawiera: pobierz go z main.

git -C fullnode show origin/main:deploy/genesis-chain20.json > genesis-chain20.json
rand-node init --datadir ./data-6210cf07 --genesis genesis-chain20.json

Powstaje <datadir>/db (RocksDB) z blokiem 0, dziewięcioma notatkami genesis jako pierwszymi liśćmi drzewa (siedem notatek RAND niosących 5 184,243719258 RAND z portfeli operatorów z chaina 19 oraz dwie notatki zUSD po 10 i 89 zUSD, czyli 99 zUSD zablokowane w depozycie mostu) i rejestrem walidatorów. Ponowne uruchomienie z tym samym genesis nic nie zmienia; inny genesis zostanie odrzucony (already initialized with a different genesis oznacza, że katalog danych należy do innego chaina). Nazwij katalog danych od pierwszych ośmiu znaków skrótu genesis, jak robi to flota (data-<name>-6210cf07), aby nigdy nie kolidował z katalogiem chaina 19 (-a3defc93).

Struktura pliku genesis:

{
  "chain_id": 20,
  "timestamp_ms": 1790835251661,
  "validators": [ { "public_key": "<hex>", "stake": 1000000000000, "payout": "rand1…" } ],
  "alloc": [ { "cm": "<64 hex>", "envelope": { "kem_ct": "…", "to_receiver": "…", "to_sender": "…", "body": "…" }, "amount": 1000000000000,
               "opening": { "pk": "<64 hex>", "time": 0, "r": "<64 hex>" } },
             { "cm": "…", "envelope": { … }, "amount": 1000000000,
               "opening": { "pk": "<64 hex>", "time": 0, "r": "<64 hex>", "asset": 1 } } ],
  "faucet": true,
  "confidential": true,
  "fri_profile": "production",
  "hc_bundle": "60af094acfe65d85fdb18fb3d06cf9085dcf28c96e59e87f1ee527226e6e3fce",
  "bridge": { "emitter": "<64 hex>", "guardians": [ "<40 hex>" ], "emitters": { "2": "<64 hex>", "3": "…", "4": "…", "5": "…" },
              "pq_guardians": [ "<hex>" ], "pause_key": "<hex>",
              "rules_v2": { "global_mint_cap_per_window": 400000000000, "cap_window_secs": 86400 },
              "guardian_set_index": 1, "burn_sequence": 13,
              "min_inbound_sequence": { "2": 3, "3": 3, "4": 3, "5": 7 },
              "rotation": { "delay_secs": 86400, "needs_possession": true },
              "fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" } },
  "tokens": { "registration_fee": 1000000000, "mint_cap_per_day": 10000000000000,
              "tokens": [ { "name": "Shielded USD", "symbol": "zUSD", "salt": "<64 hex>",
                            "backings": [ { "chain": 2, "token": "<64 hex>", "decimals": 6 }, … ] } ],
              "max_tokens": 4096, "burn_registration_fee": true, "bound_note_value": true },
  "consensus_domain": 1,
  "staking": { "faucet_budget_per_epoch": "10000000000000", "bond_activation_epochs": 2,
               "max_weight_bps": 3333, "registration_v2": true,
               "faucet_recipients": [ "rand1…" ], "faucet_minters": [ "<validator address>" ],
               "max_stake_entry_bps_per_epoch": 2500, "admission_by_vote": true },
  "epoch_blocks": 1000,
  "max_program_words": 65535,
  "max_proof_bytes": 4194304,
  "max_block_bytes": 20971520,
  "max_call_envelope_bytes": 65536,
  "max_program_public_words": 32768,
  "envelope_bytes": 1860,
  "hardening_v6": true,
  "hc_auth": "1e4e347f44cf86750b30a9a4bdf9ec9256efe353d4ff8017451eca7d195639c1",
  "gas": { "gas_price": "100", "byte_price": "800", "bundle_gas_limit": 20479, "metering": "circuit",
           "dynamic": { "target_block_bytes": 10485760, "target_block_gas": 262144,
                        "adjust_bps": 1250, "min_gas_price": "100", "min_byte_price": "800",
                        "max_gas_price": "10000", "max_byte_price": "80000", "byte_load": "paying" } },
  "testnet": true,
  "binding_domain": 1,
  "proof_window_blocks": 1024,
  "program_state": { "cell_fee": 10000000 }
}

Każde pole wchodzi do skrótu genesis. hc_bundle przypina guesta pakietu, względem którego sprawdzany jest każdy dowód pakietu, a hc_auth guesta autoryzacji, względem którego sprawdzany jest każdy dowód autoryzacji; węzeł, którego build składa inny guest, odmawia startu i podaje oba skróty. Pięć pól max_* jest opcjonalnych i wchodzi do skrótu tylko wtedy, gdy występuje; chain 20 ustawia wszystkie pięć. max_proof_bytes wynosi 4 MiB, podczas gdy chainy 13–16 miały 8 MiB: od chaina 17 transakcja może nieść trzy dowody (pakietu, autoryzacji, wywołania), a genesis z hc_auth musi mieć max_block_bytes równe co najmniej trzykrotności max_proof_bytes plus 1 MiB. Każda notatka alloc niesie swoje opening (pk, time, r oraz asset dla notatki tokenu), więc właściciel i kwota notatki genesis są jawne z założenia. Chain 20 ma:

  • sekcję bridge: zbiór strażników 1 pod indeksem 1 z ośmioma strażnikami, emitentów dla łańcuchów źródłowych 2, 3, 4 i 5 (jak w chainie 19), osiem kluczy Dilithium2 pq_guardians, pause_key, burn_sequence 13, min_inbound_sequence równe 3 dla łańcuchów źródłowych 2, 3 i 4 oraz 7 dla łańcucha źródłowego 5, a także rules_v2 (globalny limit emisji 4000 zUSD na okno 24 h). Nowość w chainie 20: rotation, wedle której rotacja kluczy PQ-guardian lub pause key wymaga dowodu posiadania przez nowe klucze i wchodzi w życie 86 400 s po zatwierdzeniu; oraz fees, wedle których chain zatrzymuje 10 bps każdego depozytu i 10 bps każdego spalenia jako notatkę zUSD na adres skarbca, więc depozyt uznaje kwotę brutto pomniejszoną o opłatę, a spalenie uwalnia swoją kwotę pomniejszoną o opłatę;
  • sekcję tokens: opłatę rejestracyjną 1 RAND, spalaną, a nie wypłacaną proposerowi; mint_cap_per_day równe 100 000 tokenów na zabezpieczenie (backing) przy ośmiu miejscach dziesiętnych; limit rejestru 4096 tokenów; ograniczenie wartości notatki do 2^63; oraz zUSD wpisany w genesis z siedmioma zabezpieczeniami, 99 zUSD zablokowanymi (Ethereum USDT 22, BSC USDT 22, Tron USDT 30, Solana USDT 25) wobec dwóch notatek zUSD genesis;
  • consensus_domain: 1: każdy podpis konsensusu jest związany z tym genesis (Konsensus);
  • sekcję staking: faucet z listą dozwolonych obejmującą 16 portfeli i 18 kluczy walidatorów emitujących, z budżetem 10 000 RAND na epokę, opóźnieniem wiązania wynoszącym dwie epoki, limitem wagi 3333 bps i rejestracjami v2, jak w chainie 19; nowość w chainie 20: admission_by_vote (nowy klucz walidatora rejestruje się dopiero po tym, jak zbiór walidatorów go przegłosuje) oraz budżet wejść równy 2500 bps aktywnej wagi na granicę epoki, podczas gdy chain 19 miał stałe 10 000 RAND (Staking). Nie ma slashing;
  • sekcję gas: wywołanie płaci za limit gazu, który deklaruje jego dowód, oraz za swoje bajty, według cen, które zaczynają się od 100 jednostek za gaz i 800 jednostek za KiB i zmieniają się po każdym bloku zależnie od jego zapełnienia względem celu 10 485 760 bajtów i 262 144 gazu, o najwyżej 1250 bps na blok, nigdy poniżej cen początkowych i, nowość w chainie 20, nigdy powyżej 10 000 i 80 000. Do obciążenia ceny bajtu wliczają się tylko bajty, za które płaci się cenę bajtu, czyli dowód wywołania i jego koperta wejściowa (byte_load: "paying"). Każdy dowód pakietu deklaruje ten sam limit, 20 479 (Opłaty);
  • envelope_bytes: 1860: każda koperta notatki ma dokładnie 1860 bajtów i zawiera zaszyfrowane pole memo;
  • hardening_v6: true, przełącznik utwardzenia v0.6, ustawiany przez każdy chain od chaina 16;
  • testnet: true, znacznik, który pozwala faucetowi współistnieć z sekcją mostu;
  • binding_domain: 1: dowody każdej transakcji i każda podpisana wiadomość akcji zawierają skrót genesis, więc nic, co zostało udowodnione lub podpisane dla jednego chaina, nie weryfikuje się na innym;
  • proof_window_blocks: 1024: kotwica pakietu może być jednym z ostatnich 1024 korzeni końca bloku, a jego time może pozostawać w tyle za wysokością do 1024 bloków (256 w chainie 19), co daje wolnemu lub delegowanemu proverowi około dwudziestu minut;
  • program_state, RPL-2: komórki programów, skarbce programów i akcję Invoke, z opłatą za komórkę wynoszącą 0,1 RAND za każdą komórkę tworzoną przez invoke (JSON-RPC podaje limity).

Nie ma sekcji aggregation, a build odmawia startu na genesis, który ją zawiera.

timestamp_ms to punkt zerowy zegara łańcucha. Gdy tworzysz własny genesis, ustaw go blisko rzeczywistego momentu uruchomienia i nigdy przed nim. Zbyt odległa przeszłość sprawia, że czas bloku pozostaje w tyle za czasem zegarowym, dopóki limit 60 s na krok bloku nie pozwoli mu nadrobić dystansu. Przy wartości większej niż 15 sekund w przyszłości blok 1 w ogóle nie zostaje zatwierdzony: każdy głos, także lidera, jest wstrzymywany, dopóki czas zegarowy nie osiągnie znacznika czasu genesis. Sprawdź to względem zegara maszyny, która uruchamia flotę, a nie tej, która utworzyła plik.

Uruchomienie

rand-node run --datadir ./data --key node.key.json \
    --listen /ip4/0.0.0.0/tcp/30303 --rpc 127.0.0.1:8545 --no-mdns \
    --bootstrap /ip4/164.90.239.200/tcp/30303/p2p/12D3KooWEQEbUwZRgqhDPXhW7vNBmcRcnhUDGUADe81VFdv3ALYe \
    --bootstrap /ip4/165.245.173.74/tcp/30303/p2p/12D3KooWJyc5oDHggAr89e9QrRFBy9Tg8TxXh18yzrb1SM6KKLYw \
    --bootstrap /ip4/188.166.235.187/tcp/30303/p2p/12D3KooWSyX84P1DAbWByLHpRBMLRWD53r3Jj4aqVt6tHoF8B2ZQ
argumentdomyślnieznaczenie
--datadirwymaganyzainicjalizowany katalog danych
--keywymaganyplik klucza: tożsamość walidatora, tożsamość p2p, odbiorca opłat
--listen/ip4/0.0.0.0/tcp/30303multiadres nasłuchu libp2p, można powtarzać
--bootstrapbrakpeer, z którym nawiązywane jest połączenie przy starcie i co 30 s po utracie połączenia, można powtarzać
--rpc127.0.0.1:8545nasłuch JSON-RPC i WebSocket operatora: każda metoda, a pętla zwrotna jest zaufana. Trzymaj go na pętli zwrotnej i nigdy nie przekierowuj do niego publicznego punktu końcowego
--public-rpc <ADDR>wyłączonyv0.6.8: drugi, publiczny nasłuch, na który może wskazywać reverse proxy lub przekierowanie SSH: stały zestaw metod bez metod klucza podglądu, bez rand_mint i rand_getPeers, bez batchy, bez WebSocket, jeden licznik dla wszystkich wywołujących (JSON-RPC)
--rpc-viewing-token-file <PATH>wyłączonyv0.6.8: wymagaj tokena bearer (pierwszy wiersz pliku, co najmniej 32 znaki) w metodach klucza podglądu na --rpc
--reserved-peer <PEER_ID>brakv0.6.8, można powtarzać: peer przyjmowany ponad limitem połączeń przychodzących; zob. Peery zarezerwowane
--strict-gossipwyłączonyv0.6.8: przyjmuj tylko gossip z prawidłowym podpisem autora, numerem sekwencyjnym i źródłem; włącza się go węzeł po węźle
--validatorwyłączonyten węzeł posiada klucz walidatora i uczestniczy w konsensusie, gdy jego klucz należy do bieżącego zbioru
--no-mdnswyłączonywyłącza wykrywanie w sieci LAN; zalecane na serwerach
--block-interval-ms1000minimalny odstęp między propozycjami
--view-timeout-ms3000bazowy limit czasu widoku, podwajany przy każdym kolejnym przekroczeniu, do 8×
--verify-chainquickkontrola integralności przy starcie: off, quick (struktura i odtworzenie księgi), full (także każdy podpis i QC)
--keep-raw-proofswyłączonytryb archiwalny agregacji bloków; bez wpływu na chain 20
--prune-historywyłączony (zachowaj wszystko)zachowaj księgę i tylko tyle historii bloków: <n>m, <n>h lub <n>d, co najmniej 1h. Walidatory testnet działają z 24h, z wyjątkiem dwóch archiwów; zob. Przycinanie historii
--min-free-disk-mb1024odmawia startu przy mniejszej ilości wolnego miejsca w systemie plików katalogu danych; rand_getHealth odpowiada disk_low poniżej czterokrotności tej wartości. 0 wyłącza zabezpieczenie
--prover <ADDR>wyłączonyhostuje delegowanego provera pod tym adresem, na własnym nasłuchu, a nigdy na RPC; zob. niżej
--gas-price, --byte-price100, 800dolny próg ceny w puli dla wywołania w łańcuchu bez sekcji gas. Genesis chain 20 ustala własne ceny, więc węzeł ignoruje tam obie flagi i zapisuje ostrzeżenie w logu

--prover-accept-spend-key zostało wycofane: węzeł v0.6.8, któremu je przekazano, odmawia startu, więc usuń je z każdego pliku unit przeniesionego z v0.6.7.

Trzy powyższe węzły bootstrap to walidatory C, D i E, bez zmian względem chain 19. Chain 20 działa na kluczach walidatorów wygenerowanych dla chain 14, więc ich identyfikatory peerów są takie, jakich używał chain 14; tożsamość p2p jest wyprowadzana z klucza walidatora. Większość walidatorów przechowuje tylko dobę bloków, więc węzeł synchronizujący się od genesis lub opóźniony o więcej niż dobę potrzebuje także peera z pełną historią. Dodaj jedno z dwóch archiwów:

    --bootstrap /ip4/168.144.46.203/tcp/30303/p2p/12D3KooWS5usdMPvsbaWSaT2xtKndHvDYurwmueoNvaRRarb2wRf    # obs1
    --bootstrap /ip4/206.81.29.236/tcp/30303/p2p/12D3KooWPocyZGcNB9Vp68VSfgVvNXhSzBBoJa4h9R1yMkp71XHh    # rand-archive-2

Pomiń --validator, aby uruchomić obserwatora. Start przebiega tak: otwarcie magazynu, sprawdzenie guestów tego builda względem hc_bundle i hc_auth z genesis, kontrola integralności (obcięcie uszkodzonego końca i zachowanie stanu bezpieczeństwa głosowania), wznowienie konsensusu od zapisanej głowy, uruchomienie sieci i RPC, a następnie synchronizacja z dowolnego peera, który jest dalej. RUST_LOG domyślnie ma wartość info,libp2p=warn,libp2p_mdns=off; Ctrl-C lub SIGTERM, sygnał zatrzymania systemd, kończy pracę w sposób czysty. Ponowne uruchomienie z tego samego --datadir wznawia pracę od zapisanej głowy.

Zasady topologii z fullnode: węzły za NAT łączą się z węzłami o adresach publicznych; otwórz TCP 30303 dla ruchu przychodzącego na węzłach publicznych; wiąż RPC z 127.0.0.1, chyba że jest chronione zaporą: przyjmuje transakcje od każdego, kto może się z nim połączyć, a gdy zaimportowano do niego klucz podglądu, traktuj ten port jako przechowujący klucze. Utrzymuj --block-interval-ms na poziomie 1000 lub wolniej: dowód paczki trwa około dwóch minut, a jego kotwica jest ważna przez 1024 bloki na chain 20 (256 na chain 19).

Walidator potrzebuje zsynchronizowanego zegara. Od chain 14 czas zegarowy jest częścią konsensusu (utwardzenie mostu B2). Blok ze znacznikiem czasu późniejszym o więcej niż 60 s od jego rodzica jest nieprawidłowy, a walidator wstrzymuje głos na blok, którego znacznik czasu wyprzedza jego własny zegar o więcej niż 15 s. Walidator ze spóźnionym zegarem przestaje więc głosować bez zapisania błędu w logu, a do zatwierdzenia na chain 20 musi zagłosować osiemnastu z dwudziestu sześciu. Uruchom NTP na każdym walidatorze i sprawdź go przed startem węzła:

timedatectl                     # want "System clock synchronized: yes" and "NTP service: active"
chronyc tracking
apt-get install -y chrony && systemctl enable --now chrony    # if it is not synchronised

Peery zarezerwowane

Od v0.6.8 węzeł przyjmuje co najwyżej 256 nawiązanych i 64 półotwarte połączenia przychodzące, a tożsamość libp2p nic nie kosztuje, więc obcy mogliby zająć każdy slot. Zarezerwowane są trzy rodzaje peerów, przyjmowane ponad limitem i obsługiwane z puli synchronizacji przeznaczonej dla walidatorów: każdy peer --bootstrap, każdy --reserved-peer i każda tożsamość walidatora poznana z podpisanego powiązania peera. Każdy węzeł --validator podpisuje swoją tożsamość libp2p kluczem walidatora i rozsyła ją gossipem; węzeł, który zna klucz ze zbioru walidatorów, rezerwuje tego peera i zachowuje wpis po restartach. Magazyn świeżo utworzonego łańcucha nie zawiera powiązań, więc do czasu nadejścia pierwszych ogłoszeń zarezerwowane są tylko pierwsze dwa rodzaje: przy tworzeniu łańcucha przekaż identyfikatory peerów walidatorów floty przez --reserved-peer lub zachowaj je na liście bootstrap (rand-node address --key <file> wypisuje identyfikator peera klucza). rand_status.reserved_peers je zlicza; na węźle floty powinno to osiągnąć liczbę walidatorów w ciągu minuty od startu.

Sprawdzenie

rand-node status                # rand_status verbatim
rand --rpc http://127.0.0.1:8545 status
rand peers

rand_status zwraca height, view, syncing, sync_target, peer_count, connected_peers, reserved_peers, mempool_size, is_validator, active_validator, faucet, confidential, testnet, fri_profile, programs, notes, nullifiers, tree_root, hc_bundle, hc_auth, binding_domain, gas_prices, address i peer_id węzła, diagnostykę synchronizacji (sync_inflight_age_ms, sync_failures, sync_late_batches), dysk (disk_free_bytes, disk_low) oraz próg retencji (prune_floor, 0 na węźle, który zachowuje wszystko; prune_history_secs, null, gdy nie ustawiono). Na chain 20 spodziewaj się hc_bundle 60af094a…, hc_auth 1e4e347f…, faucet true, confidential true, testnet true, binding_domain 1, fri_profile "production" oraz gas_prices między 100 a 10 000 za gaz i między 800 a 80 000 za KiB. rand_getGenesisHash musi zwrócić 6210cf07…5135, a rand_getVersion 0.6.8.

rand_getHealth zwraca disk_low (wraz z free_bytes) przed ok, syncing lub behind, gdy wolne miejsce jest mniejsze niż czterokrotność --min-free-disk-mb.

Przycinanie historii

Węzeł uruchomiony z --prune-history 24h zachowuje księgę (notatki, nullifiery, kotwice, rejestr walidatorów, programy) w całości i usuwa starsze bloki, ich certyfikaty, transakcje i potwierdzenia. Przebieg odbywa się co 16. zatwierdzony blok i usuwa do 512 bloków starszych niż znacznik czasu głowy pomniejszony o okno; genesis, głowa i jej rodzic nigdy nie są usuwane, a próg tylko rośnie. Miejsce na dysku wraca po kompaktowaniu RocksDB. Na chain 20, tak jak na chain 19, 24 z 26 walidatorów działa z 24h i przechowuje około doby bloków. Gdy przycinanie włączono dla chain 14 (2026-09-25), tabele bloków każdego walidatora zmalały z 19 GB do 3,3–4,4 GB po opróżnieniu zaległości; pozostałe dwa, obs1 i rand-archive-2, od 2026-09-27 także walidatory, działają bez tej flagi i zachowują wszystko. Węzeł mainnet-beta nigdy nie przekazuje tej flagi.

Na węźle z przycinaniem wyszukiwanie po wysokości poniżej progu (rand_getBlockByHeight, rand_getFinality po wysokości, zakres rand_getBlocks lub rand_getCompactBlocks sięgający progu) zwraca błąd -32010 z data.floor: zapytaj archiwum. Wyszukiwanie po haszu zwraca null dla bloku, którego węzeł już nie przechowuje, tak jak dla bloku, który nigdy nie istniał. Skanowanie portfela nie jest dotknięte, ponieważ notatki i nullifiery nigdy nie są przycinane.

Kontrola startowa węzła z przycinaniem jest strukturalna (blok 0 i zachowane bloki, ich QC, powiązania z rodzicami i liderzy), a nie odtworzeniem od genesis, i nie naprawia się sama: jeśli się nie powiedzie, zsynchronizuj ponownie z archiwum. Nie otwieraj katalogu danych z przycinaniem buildem starszym niż v0.5.7, chyba że działa z --verify-chain off: jego domyślna kontrola odtwarza od genesis, stwierdza brak bloku 1 i obcina księgę do genesis.

Hostowanie provera

Na chain 20 portfel tworzy dwa dowody dla każdej paczki, a większy z nich wymaga około 6 GB pamięci. Portfel, który tego nie pomieści, może przekazać ten dowód delegowanemu proverowi, z którym jest sparowany: osobnemu plikowi binarnemu rand-prover albo tej samej usłudze hostowanej przez węzeł.

rand-prover --home /root/prover keygen
rand-prover --home /root/prover pair --name laptop --url https://prover.example.net
rand-node run --datadir ./data --key node.key.json --prover 127.0.0.1:8600 --prover-home /root/prover

pair wypisuje link randprover:. Portfel przyjmuje go poleceniem rand prover pair <link>, a następnie generuje dowody za jego pośrednictwem, używając globalnej opcji --prover. Nasłuch provera jest osobny i nigdy nie jest metodą RPC: adres równy --rpc zostaje odrzucony. Z opcją --prover węzeł nie uruchomi się, jeśli nie istnieje <prover-home>/prover.key.json (domyślnie <datadir>/prover) oraz jeśli nie jest dostępne 5.74 GB na każdy slot dowodzenia plus 1 GiB pamięci; --prover-max-parallel ustawia liczbę slotów, domyślnie jeden.

W chain 20 prover otrzymuje klucz podglądu portfela i świeży salt, a nie klucz wydawania. Dowód autoryzacji nad kluczem wydawania portfel wykonuje samodzielnie, więc sparowany prover może odczytać całą historię tego portfela, ale nie może wydawać środków. Nasłuch należy trzymać na pętli zwrotnej (loopback), chyba że przed nim stoi proxy kończące TLS, i nie hostować go na maszynie, która udostępnia publiczne RPC. Zatrzymanie czeka na dowód w toku, dlatego jednostce systemd należy nadać TimeoutStopSec=180.

Weryfikacja i naprawa

rand-node verify --datadir ./data                  # full: hashes, QCs, signatures, proofs, ledger replay, the supply counters
rand-node verify --datadir ./data --mode quick     # structure and ledger replay
rand-node verify --datadir ./data --repair         # truncate a damaged tail; peers resync the rest

Najpierw zatrzymaj węzeł. Kod wyjścia 0 oznacza spójność, 2 oznacza wykrycie problemu przy braku --repair. Odtworzenie ponownie weryfikuje dowód każdego bundle, więc build, którego zbiór ograniczeń różni się od tego, który utworzył łańcuch, zatrzymuje się na pierwszym bundle.

Zostanie walidatorem

Walidator dołącza do działającego łańcucha przez bonding, a nie przez nowy genesis: uruchom węzeł z --validator, wypisz rejestrację poleceniem rand-node register --key node.key.json --payout <rand1…> --v2 (chain 20 odrzuca rejestrację v1), poproś portfel o zabondowanie co najmniej 1,000 RAND poleceniem rand bond <address> 1000 --registration <hex>, a węzeł zacznie proponować bloki, gdy bond się aktywuje: w chain 20 najwcześniej po trzech granicach epok liczących po 1,000 bloków od bondu. W chain 20 bond następuje jako drugi. Jego genesis ustawia staking.admission_by_vote, więc bond rejestrujący nowy klucz jest odrzucany, dopóki walidatorzy dysponujący ponad dwiema trzecimi wagi głosów nie zagłosują za przyjęciem klucza: każdy z nich podpisuje poleceniem rand-node admit sign --candidate <key or registration> --key <validator key> --genesis-hash 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135, a jeden z nich przesyła głosy poleceniem rand-node admit submit (Command line). rand_getAdmitted wyświetla klucze, za którymi zagłosowano i które nie są jeszcze zarejestrowane. Nie więcej niż 2,500 bps aktywnej wagi staje się nową wagą na jednej granicy epoki. Staking przeprowadza przez cały proces. Prywatny łańcuch testowy tworzy rand-node genesis:

rand-node genesis --chain-id 88 \
    --validator a.key.json,1000,rand1<a's payout address> \
    --validator <hex public key of b>,1000,rand1<b's payout address> \
    --alloc rand1<address>=1000 \
    --epoch-blocks 1000 --faucet --fri-profile production \
    --out genesis.json

a scripts/local-testnet.sh uruchamia dwóch walidatorów na jednej maszynie z RPC na 8545 i 8546. Bez dodatkowych flag genesis nie ma hc_auth, sekcji gas ani memo. Chain 20 został utworzony z dodatkowymi flagami --hardening-v6 --bundle-guest v3 --auth-guest --gas-price 100 --byte-price 800 --bundle-gas-limit 20479 --gas-dynamic 10485760,262144,1250 --max-gas-price 10000 --max-byte-price 80000 --gas-byte-load paying --envelope-bytes 1860 --testnet --binding-domain 1 --proof-window-blocks 1024 --program-state-cell-fee 10000000 (chain 18 i 19 miały flagi do --envelope-bytes włącznie), a jego sekcje bridge i staking oraz consensus_domain zostały wstawione przez skrypt cięcia. Nowy własny łańcuch powinien przekazywać --binding-domain 1: portfel v0.6.8 generuje dowód w formie związanej z genesis na każdym identyfikatorze łańcucha spoza przedziału od 14 do 19.

Wdrażanie na serwerze

Katalog deploy/ repozytorium zawiera skrypty używane przez flotę: deploy/push-to-vps.sh <ip> <letter> "<bootstraps>" [validator|observer] konfiguruje serwer z Linuksem (rsync źródeł, build, instalacja /usr/local/bin/{rand-node,rand}, inicjalizacja datadir z kluczem opartym na hashu genesis, instalacja usługi systemd rand-node), a deploy/rebuild-vps.sh <ip> przebudowuje na nowym commicie i restartuje z zachowaniem danych. Dwa ostrzeżenia. Skrypt konfiguracji inicjalizuje z deploy/genesis.json, który nie jest plikiem chain 20, więc węzeł dla testnet konfiguruje się ręcznie z deploy/genesis-chain20.json (na main, zob. wyżej). Ponadto oba skrypty wykonują rsync /root/fullnode z opcją --delete, więc klucz trzymany wewnątrz tego drzewa zostanie usunięty przy następnej przebudowie: trzymaj go w /root/keys, jak robi to flota i jak robi to teraz domyślnie skrypt konfiguracji v0.6.8 (skrypt v0.6.7 zapisywał klucz w /root/fullnode/deploy/). Zarządzanie usługą to systemctl status rand-node i journalctl -u rand-node -f. Serwer wymaga build-essential clang cmake pkg-config libssl-dev oraz toolchainu Rust. Serwer z 1 vCPU i 2 GB nie zbuduje binariów; zbuduj je gdzie indziej i skopiuj. Running a validator and an aggregator zawiera rozmiary i konfigurację krok po kroku.

Eksplorator

randscan.org działa obok walidatora E i indeksuje chain 20; jego REST API i kanał WebSocket to najprostszy sposób odczytu łańcucha bez węzła (Explorer API). Eksplorator jest open source (randprotocol/randscan), a jego indeksator potrzebuje jedynie JSON-RPC rand_* węzła i bazy PostgreSQL, więc można go uruchomić na własnym węźle.

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.