Rand Protocol
Dokumentacja
Podstawowe pojęcia

Konta i adresy

Dwa rodzaje kluczy w łańcuchu, sposób wyprowadzania adresu shielded rand1, informacje ujawniane przez klucz podglądu oraz powód, dla którego krótki adres z chain 11 został wycofany.

Użyj tej strony, aby wygenerować właściwy klucz do danego zadania, sparsować lub zweryfikować adres i wiedzieć, co ujawnia przekazanie komuś klucza. W tym łańcuchu nie ma kont: saldo jest faktem dotyczącym pliku klucza, nigdy łańcucha.

Dwa klucze, dwa rodzaje adresu

klucz węzłaklucz portfela
zapisywany przezrand-node keygenrand keygen
sekret32-bajtowe ziarno256-bitowy klucz wydawania
tożsamość publiczna32-bajtowy adres, base58, od 32 do 44 znakówadres osłonięty, rand1…, 1665 lub 1666 znaków
podpisujebloki, głosy, emisje z faucetu, Unbond, Withdraw, Registration, a w chain 20 głos AdmitValidator; jako uprawnienie emisyjne Key tokena: TokenMint i SetAuthority. W chain 20 każda z tych wiadomości obejmuje także hasz genesis (Transakcje)nic: transfer jest autoryzowany przez swoje dowody
pojawia się w łańcuchu jakoproponent bloku, wiersz rejestru walidatorów, emitent emisji z faucetu, uprawnienie key tokenanigdy w transferze, gdzie jest zapieczętowany w kopertach. Jest publikowany tam, gdzie wartość wchodzi do puli z jawną kwotą: depozyt mostu, emisja tokena oraz, w chain 20, wypłata z wywołania (invoke) wskazują adres odbiorcy; adres wypłat walidatora znajduje się w rejestrze, genesis chain 20 wskazuje skarbiec opłat mostu, a od chain 14 emisja z faucetu i nota genesis alloc publikują pk właściciela, czyli pierwsze 32 bajty adresu
plik{ "seed", "address", "public_key" }, tryb 0600{ "version": 2, "spend_key": "<64 hex>" }, tryb 0600

Te dwa rodzaje nigdy się nie mieszają. Walidatorzy są identyfikowani przez pierwszy rodzaj, a ten sam kształt klucza Dilithium2 służy jako uprawnienie emisyjne Key tokena (rand token create --authority-key-out zapisuje plik w kształcie pliku z rand-node keygen), klucz wstrzymania mostu i jego sześć postkwantowych kluczy strażników. Klucze poświadczeń strażników to secp256k1, z 20-bajtowymi adresami w stylu Ethereum (Most). Wartość jest przechowywana pod drugim rodzajem.

Adres jawny

Klucz węzła to 32-bajtowe ziarno, z którego deterministycznie wyprowadza się parę kluczy Dilithium2: 1312-bajtowy klucz publiczny i 2420-bajtowe podpisy. Adres jest haszem BLAKE3 klucza publicznego z separacją domen, zapisanym w base58:

address = blake3("rand-address" || public_key)      // 32 bytes, shown as base58

Podpisane obiekty (głosy, bloki, emisje, akcje stakingu) niosą pełny klucz publiczny, więc weryfikator sprawdza blake3("rand-address" || public_key) == address, zamiast ufać samemu adresowi. To samo ziarno daje także tożsamość libp2p węzła, klucz ed25519 z blake3("rand-p2p-identity" || seed), który jest identyfikatorem peera po /p2p/ w multiadresie bootstrap.

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

Przykład: EdrEC2QRQrQF64iRXxF9927jNj6RidiHHByzx5xD6F1e to jeden z walidatorów chain 20. Chain 15 do 20 używają ponownie osiemnastu kluczy rotowanych dla chain 14, więc ich adresy i identyfikatory peerów nie zmieniły się, a żaden adres walidatora ani identyfikator peera z chain 13 nie jest już używany; osiem kolejnych walidatorów dołączyło ze stakiem w chain 15, a od chain 16 wszystkich 26 jest w pliku genesis.

Adres osłonięty

Portfel to jeden sekret, 256-bitowy klucz wydawania sk, a wszystko inne jest jego pochodną. Wyprowadzanie opiera się na Poseidon2 z tagiem domeny jako pierwszym wchłanianym słowem (H(domain, msg)), czyli na tej samej funkcji, którą zkVM oblicza w obwodzie:

spend key   sk                          256 bits, in wallet.key.json
   │
   ├─ viewing key   nk = H(NK, sk)      sees the whole history; cannot spend
   │     ├─ pk = H(PK, nk)              the field a note names its owner by
   │     ├─ nullifier(cm) = H(NF, nk, cm)   what a spend publishes
   │     ├─ ovk                         opens envelopes this wallet sent
   │     └─ ML-KEM-768 decapsulation key    opens envelopes sent to this wallet
   └─ address   rand1 + base58(pk || kem_ek)

Adres to 32 bajty pk po których następuje 1184-bajtowy klucz enkapsulacji ML-KEM-768, w base58 po prefiksie rand1: 1665 lub 1666 znaków (długość base58 zależy od początkowych bajtów; mniej więcej co czternasty adres jest krótszy), około 1,6 KB, bez sumy kontrolnej. Nadawca potrzebuje klucza enkapsulacji, aby zapieczętować kopertę noty, dlatego jest on w adresie. Jest publiczny z założenia: każdy może na niego płacić, a jego posiadanie nic nie mówi o tym, co adres przechowuje.

rand keygen                      # wallet.key.json; refuses to overwrite an existing file
rand address                     # rand1… on stdout, its fingerprint XXXX-XXXX-XXXX-XXXX on stderr

Od v0.5.10 rand address wypisuje na stdout dokładnie adres i znak nowej linii, więc $(rand address) działa w skrypcie. Odcisk trafia na stderr.

Parsowanie, dokładnie tak jak robi to ShieldedAddress::parse: usuń rand1, zdekoduj base58 i wymagaj dokładnie 32 + 1184 = 1216 bajtów; pierwsze 32 to pk jako osiem słów u32 w porządku little-endian, reszta to klucz enkapsulacji. RPC odrzuca każdy parametr dłuższy niż 2000 znaków przed jego sparsowaniem.

Obok pliku klucza portfel przechowuje <key>.key.json.notes.json, pamięć podręczną not, które ten klucz otworzył. Zawiera ona jawne treści not, więc jest zapisywana w trybie 0600 jak klucz; każdy jej wiersz można odtworzyć, skanując ponownie od liścia 0.

Udostępnianie adresu: odcisk i linki randpay:

Adres jest zbyt długi, by czytać go na głos lub porównywać na oko, i nie ma sumy kontrolnej, dlatego od v0.5.10 każdy portfel pokazuje obok niego odcisk. Odcisk służy wyłącznie do wyświetlania: nic w łańcuchu ani w sieci go nie niesie.

fingerprint = first 80 bits of blake3("rand-address-fingerprint-1" || pk_bytes || kem_ek)
            = 16 Crockford base32 characters, shown as XXXX-XXXX-XXXX-XXXX

Hasz jest liczony z 1216 surowych bajtów, które koduje adres. Alfabet to 0123456789ABCDEFGHJKMNPQRSTVWXYZ; parsowanie ignoruje wielkość liter i łączniki oraz odczytuje O jako 0, a I lub L jako 1.

Link randpay: niesie adres i opcjonalnie to, co ma na niego zostać zapłacone:

randpay:<rand1…>[?amount=<decimal>][&asset=<index | 64 hex | rpl1…>][&memo=<percent-encoded UTF-8>]

amount jest podawany w jednostkach wyświetlania aktywa (dziesiętny RAND z dziewięcioma miejscami po przecinku, token z własnym decimals wiersza rejestru) i nie może być zerem. memo to co najwyżej 510 bajtów UTF-8. Schemat jest rozpoznawany bez względu na wielkość liter; nieznany parametr, powtórzony parametr lub błędne kodowanie procentowe są odrzucane.

rand address --uri --amount 1.5 --memo "invoice #42" --qr   # a randpay: link, as a terminal QR
rand contacts add alice rand1q9f…                            # shows the fingerprint, asks add? [y/N]
rand send alice 1.5 --memo "coffee"                          # to a contact, with a memo

rand send <TO> przyjmuje adres rand1…, link randpay: lub nazwę zapisanego kontaktu, sprawdzane w tej kolejności. amount, asset i memo z linku uzupełniają to, czego brakuje w wierszu poleceń, a wartość podana obiema drogami, która się różni, jest odrzucana. Przed wygenerowaniem dowodu send wypisuje to <contact, if any> · fingerprint XXXX-XXXX-XXXX-XXXX · <amount> <asset> i prosi o potwierdzenie; skrypt przekazuje --yes. Kontakty są przechowywane obok klucza w <key>.contacts.json, w trybie 0600. Memo podróżuje wewnątrz koperty noty (Noty).

Nie istnieje rand balance <address> ani RPC salda: łańcuch nie potrafi na to odpowiedzieć nikomu. Portfel oblicza swoje saldo, skanując swoim kluczem podglądu wszystkie koperty w łańcuchu (Noty).

Klucze, które możesz przekazać komuś innemu

Ujawnienie jest własnością kluczy, a łańcuch nie może wymusić żadnego z nich.

kluczotwieraziarnistość
klucz podglądu nk (64 hex)każdą notę, jaką portfel kiedykolwiek otrzymał, a przez ovk każdą notę, jaką kiedykolwiek wysłał, wraz z nullifierem każdego wydaniawszystko albo nic, wstecz i w przód
klucz transakcji TxKey (32 bajty)dokładnie jedną kopertę, którą zapieczętował: notę i, od chain 18, jej memojedna płatność
klucz wywołania K_callzapis prywatnych danych wejściowych jednego poufnego wywołaniajedno wywołanie

Klucz podglądu nie może wydawać. Od chain 17 (podzielona autoryzacja) sam dowód pakietu jest tworzony z nk, a każdy pakiet niesie też drugi, mały dowód autoryzacji, który może wytworzyć tylko klucz wydawania i który jest związany z jedną transakcją (Noty). Dzięki temu delegowanemu proverowi można przekazać nk, a więc całą historię portfela, bez możliwości wydawania; portfel zawsze tworzy dowód autoryzacji na własnej maszynie. Adresy, klucze i istniejące noty nie zmieniają się przez ten podział. Portfel wyprowadza nk przy każdym uruchomieniu i nigdy go nie przechowuje; każdy TxKey jest losowany od nowa dla każdej koperty i porzucany po zapieczętowaniu. Węzłowi można przekazać klucz podglądu przez rand_importViewingKey (w pamięci, najwyżej 64 na węzeł, znika po restarcie), aby skanował w imieniu posiadacza — to ścieżka eksploratora — a rand_checkTransaction(hash, key) sprawdza jedną płatność względem TxKey w jednym bezstanowym wywołaniu. rand viewing-key wypisuje nk, a rand tx-key <hash> wypisuje klucz każdego wyjścia transakcji, które ten portfel potrafi otworzyć.

Identyfikatory tokenów nie są adresami

Token RPL ma trzeci rodzaj tekstowego identyfikatora, który nazywa token, nigdy posiadacza. 32-bajtowy identyfikator aktywa tokena jest pokazywany jako 64 znaki hex oraz jako rpl1…: bech32m (BIP-350) z częścią czytelną dla człowieka rpl nad 32 bajtami identyfikatora, 52 znaki danych plus 6-znakowa suma kontrolna, 62 znaki. Parsowanie jest ścisłe: błędna suma kontrolna, inna część czytelna dla człowieka, ładunek o długości innej niż dokładnie 32 bajty lub mieszana wielkość liter są odrzucane, więc literówka nigdy nie wskaże innego tokena. Nota nie niesie identyfikatora; jej słowo asset to gęsty indeks tokena w rejestrze (Tokeny RPL).

Odbiorca w moście

Format przesyłania mostu ma 32-bajtowe pole odbiorcy, a adres osłonięty się w nim nie mieści. Obecnie to pole niesie hasz adresu:

recipient_hash = blake3("rand-shielded-recipient" || pk_bytes || kem_ek)

Depozytor w łańcuchu źródłowym wskazuje hasz, strażnicy go podpisują, a transakcja BridgeAttest niesie pełny adres, aby księga mogła go z nim porównać (Most).

Krótki adres osłonięty: wypróbowany w chain 11, wycofany

Chain 11 (tag v0.2 w fullnode, 2026-09-17) zastąpił długi adres identyfikatorem odbiorcy (ang. receiver id) o długości 53–55 znaków, rand1 + base58(blake3(receiver signing key) ‖ checksum), a klucz enkapsulacji ML-KEM przeniósł do podpisanego rekordu odbiorcy, który nadawca pobierał z rejestru eksploratora albo otrzymywał w treści żądania płatności. Działał przez dobę i tego samego dnia został wycofany (fullnode 17db41d); chain 12 wrócił do długiego adresu, dokładnie takiego, jak opisano powyżej, i każdy kolejny chain, w tym chain 20, go zachowuje.

Dlaczego go wycofano. Identyfikator odbiorcy jest skrótem, a nadawca nie może zaszyfrować noty dla skrótu. Pierwsza płatność do portfela, który nigdy się nie zarejestrował, wymagała, by odbiorca przekazał żądanie płatności lub plik rekordu, a sama rejestracja była opłacana transferem własnym, którego nowy, pusty portfel wykonać nie może. Obie ścieżki dostarczenia ujawniały też rejestrowi, kto za chwilę otrzyma płatność. Projekt, który zachowuje krótki adres, bezwarunkową możliwość wysyłania i kryptograficzną prywatność, to adres hybrydowy: 32-bajtowy klucz X25519 w treści adresu (około 93 znaków), klucz postkwantowy pobierany z rejestru, przy czym pierwsza płatność do niezarejestrowanego portfela jest poufna wobec przeciwników klasycznych, a każda kolejna jest w pełni postkwantowa. Jest to opisane w następnej części; do czasu jej wdrożenia adresem pozostaje adres długi.

Specyfikacja chain 11 i jej implementacja pozostają w historii fullnode (docs/superpowers/specs/2026-09-17-short-shielded-address.md, tag v0.2) ze względu na elementy, które projekt hybrydowy wykorzystuje ponownie: pochodny klucz podpisu odbiorcy, podpisany rekord wraz z jego weryfikatorem oraz rejestr jako stan łańcucha.

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.