Diese vier Programme sind jeweils eine vollständige Anwendung eines vertraulichen Aufrufs: ein Problem, eine Schnittstelle, ein Rust-Guest, das Host-seitige Tooling sowie ein Deploy und ein Aufruf auf dem Live-testnet. Sie sind länger als die Tutorials, und jedes stützt sich auf einen anderen Teil der Maschine. Write a program in Rust or C ist die Anleitung zum Modell, das alle verwenden. Lesen Sie sie zuerst, wenn read_input, das öffentliche Segment oder die acht Ausgabeslots neu für Sie sind.
Alles hier wurde zuerst auf chain 18 mit fullnode v0.6.7 am 2026-09-30 ausgeführt, und jede Ausgabe auf diesen Seiten stammt aus diesen Läufen. Programme werden über einen Chain-Schnitt nicht übernommen, daher wurden die vier am 2026-10-01 unverändert erneut auf chain 19 und dann auf chain 20 (fullnode v0.6.8) deployt, und die Aufrufe wurden jeweils wiederholt. Eine Programm-ID ist ein Hash aus Code und öffentlichem Segment, nicht aus der Chain, daher ist jede ID dieselbe; die Transaktions-Hashes in den Mitschnitten stammen von chain 18, und die Tabelle unten verlinkt die von chain 20. Auf chain 20 kann ein Programm zwischen Aufrufen auch einen Zustand halten (RPL-2); diese vier tun das nicht, und Program state sowie das example-programs repository behandeln Programme, die es tun.
Die vier Programme
| Programm | was ein Aufruf beweist | stützt sich auf | Wörter | Tier |
|---|---|---|---|---|
| An anonymous vote | der Aufrufer ist Mitglied einer veröffentlichten Liste, ohne zu sagen, welches, und hat noch nicht abgestimmt | Keccak-256, ein öffentliches Segment | 427 | 12 |
| A proof of liabilities | private Kundensalden summieren sich auf nicht mehr als eine veröffentlichte Reserve, unter einer Wurzel, die jeder Kunde prüfen kann | SHA-256, ein im Programm aufgebauter Baum | 994 | 14 |
| An off-chain ledger | ein Stapel von Überweisungen führt ein Ledger von einem Zustand in den nächsten, jeder Sender ist autorisiert und nichts wird erzeugt | Poseidon2, zwischen Aufrufen übertragener Zustand | 773 | 14 |
| A private credit decision | ein veröffentlichtes Modell genehmigt oder lehnt private Antragsdaten ab, für die ein Attestierer gebürgt hat | Ganzzahlarithmetik, ein Modell im öffentlichen Segment | 462 | 12 |
Alle vier sind auf chain 20 live. Jeder Aufruf in der Tabelle wiederholt mit denselben Eingaben den chain-18-Aufruf, der auf der Seite des jeweiligen Programms gezeigt wird:
| Programm | Programm-ID | auf chain 20 deployt in | erster Aufruf auf chain 20 |
|---|---|---|---|
| anonymous vote | ad2f719f12d01c9f3c0962092b9898b75b0914bbb817031d51be13e54c05b970 | f6ae5528…0f82 | 3a304e7a…427b |
| proof of liabilities | fce8474dfdf80b9733e04fb72d4d8d19c40ba5ab646b3d867322daf4b9b599c0 | a8bc281b…17d9 | 6116c15f…d62a |
| off-chain ledger | c4ddd388f2d94299610c7c3409dfdfb4dcb4ae3e0bc9ec78d0c45f51d8231cab | 72cc95f2…3d51 | 9c9998bb…66eb |
| private credit decision | 7bbec14289f4446231b3a9a5ac18cb0613a373ca8a14fe4f046f3fde7ae6c73c | 413e6658…e42b | d3ceb306…124a |
Der Explorer listet jedes Programm und seine Aufrufe unter randscan.org/programs. Eine testnet-Chain wird von Zeit zu Zeit ersetzt, und Programme werden nicht übernommen: Nach dem nächsten Schnitt sind diese IDs verschwunden, und die Programme müssen erneut deployt werden.
Setup
Die Toolchain. Die Guests werden mit rand-guest aus dem circuits-Repository gebaut, und zwar an dem Commit, den fullnode v0.6.7 und v0.6.8 beide mitliefern:
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"
Der Commit ist aus zwei Gründen wichtig. hc ist eine Funktion von Quellcode und Toolchain, daher kann ein an einem anderen Commit von guest-sdk neu gebauter Guest ein anderes hc erhalten. Und die Signaturen des SDK haben sich geändert: Bei aeacf31 nehmen keccak und sha256_compress Array-Referenzen entgegen, wo ältere Commits rohe Zeiger nahmen.
Die Quellen. Alle vier Guests, ihre Host-Skripte und die Demodaten sind ein Download. Entpacken Sie sie innerhalb des Checkouts, denn rand-guest build findet guest-sdk, indem es vom Verzeichnis des Guests aus nach oben sucht:
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
Die Dateien sind auch einzeln unter https://randprotocol.org/examples/programs/ einsehbar, zum Beispiel allowlist/src/main.rs.
Das Wallet. Zum Deployen und Aufrufen benötigen Sie das rand-Wallet aus fullnode v0.6.8 (cargo build --release -p randprotocol-client in einem fullnode-Checkout am Tag v0.6.8, oder das rand aus dem Release v0.6.8), einen Schlüssel und RAND. Ein älteres Wallet kann auf chain 20 keine Transaktionen ausführen: chain 20 bindet jeden Beweis und jede signierte Nachricht an ihren Genesis-Block, und ein v0.6.7-Wallet tut das nicht.
Ein RPC. Der öffentliche Endpunkt https://rpc.randprotocol.org genügt für alles auf diesen Seiten: Programme und Quittungen lesen, ein Wallet synchronisieren, deployen und aufrufen. Die erste Synchronisierung eines Wallets liest jeden Block-Header und jeden Block, der eine Note enthält, bei der Höhe von chain 18 am 2026-09-30 645 Anfragen, und dauerte über den Endpunkt etwa drei Minuten. Der Endpunkt erlaubt jeder Adresse 1.200 Lesezugriffe und 6 Sendevorgänge pro Minute. Ein eigener Node (Running a node) kennt solche Limits nicht.
export RAND_RPC=https://rpc.randprotocol.org
export RAND_KEY=$HOME/wallet.key.json
Auf chain 18 wurde eine der Stimmen, die auf der Seite zur anonymous vote gezählt wurden, fdbcc960…fc2b, auf einem Laptop bewiesen und allein über den öffentlichen Endpunkt gesendet. Auf chain 19 und 20 liefen jedes Mal alle dreizehn Transaktionen, die vier Deploys und neun Aufrufe, auf diesem Weg.
Was chain 18 für ein Programm geändert hat
Ein Aufruf deklariert ein Gaslimit und bezahlt dafür. Constraint set 8 misst jeden Lauf. Ein gewöhnlicher Zyklus kostet 1 Gas. Die Hash-Syscalls sind die Ausnahmen:
| Instruktion | Gas |
|---|---|
jede RV32IM-Instruktion, HALT, WRITE_OUTPUT, READ_INPUT, READ_PUBLIC | 1 |
POSEIDON2 über n Wörter | 3 + 3 × ⌈n / 4⌉ |
SHA256, eine Kompression | 64 |
KECCAK, eine Permutation | 192 |
Der Beweis trägt einen neuen öffentlichen Wert, das deklarierte Limit, und der Schaltkreis erzwingt, dass das Gas des Laufs höchstens so hoch ist. Das genaue Gas bleibt privat. rand call führt das Programm zuerst im Emulator aus und gibt beides aus:
gas: 5636 (declaring 6144, tier 12)
Standardmäßig rundet das Wallet das genaue Gas auf ein Vielfaches von 2^(tier − 2) auf, das sind 1.024 bei Tier 12 und 4.096 bei Tier 14, sodass das deklarierte Limit wenig über den Lauf verrät. --gas-limit max deklariert die Obergrenze des Tiers und verrät nichts über das Tier hinaus. --gas-limit N deklariert einen exakten Wert.
Die Gebühr folgt dem deklarierten Limit und den Bytes.
floor = 0.001 RAND + gas price × declared limit + byte price × ⌈bytes / 1024⌉
Die Preise begannen bei 100 Einheiten pro Gas und 800 Einheiten pro KiB (eine Einheit ist 10⁻⁹ RAND) und bewegen sich mit jedem Block ein wenig auf einen Zielwert zu. rand_getLimits liefert die aktuellen. Das Wallet fügt zwei Preisschritte als Spielraum hinzu, einen Faktor von 1,125², weil ein Aufruf zwei oder drei Blöcke nach der Preisbestimmung landet.
Ein Detail bestimmt den Großteil der Gebühr. Seit chain 18 ist der Beweis eines Aufrufs an seine eigene Transaktion gebunden, daher muss das Wallet die Gebühr festlegen, bevor der Beweis existiert, und es setzt den Beweis mit der Obergrenze der Chain von 4 MiB statt mit seiner tatsächlichen Größe an. Für die anonymous vote ergibt das (1,000,000 + 100 × 6,144 + 800 × 4,097) × 1.125² = 6.191.437 Einheiten, 0,0062 RAND, wovon das Gas etwa ein Achtel ausmacht. Die vier Aufrufe auf diesen Seiten kosteten jeweils zwischen 0,0057 und 0,0075 RAND. --fee überschreibt den Standardwert.
Jedes Bundle besteht aus zwei Beweisen. Chain 18 verwendet geteilte Autorisierung: Das Bundle, das eine Gebühr zahlt, trägt neben dem Tier-14-Bundle-Beweis einen kleinen Tier-10-Autorisierungsbeweis. Das Wallet erstellt beide und gibt für jeden eine Zeile aus. Ein Aufruf besteht damit aus drei Beweisen: dem Aufruf, der Autorisierung und dem Bundle. Auf einem Apple-Silicon-Laptop dauerten die vier Aufrufe hier jeweils zwischen drei und fünfeinhalb Minuten, bei einem Spitzenverbrauch von 6,1 bis 6,6 GB Speicher. Der Bundle-Beweis macht davon jedes Mal etwa zwei Minuten aus. Ein Tier-12-Aufrufbeweis fügt eine halbe Minute hinzu, ein Tier-14-Beweis zwei Minuten.
Die Obergrenzen, in die ein Aufruf passen muss.
| Limit | chain 18 bis 20 |
|---|---|
| Tier | höchstens 14: 16.383 Zyklen, einschließlich der Digest-Zeilen |
| Poseidon2-Permutationen | 2.048 bei Tier 14, einschließlich der Programm-, Eingabe- und öffentlichen Digests |
| Keccak-Permutationen | 128 |
| SHA-256-Kompressionen | 128 |
| Aufrufbeweis | 4.194.304 Bytes (4 MiB) |
| Programm | 8.180 Wörter ohne öffentliches Segment, einige weniger mit einem: chain 20 lehnt, wie chain 18 und 19, ein Deploy ab, das kein Tier-14-Aufruf fassen könnte (ProgramUncallable), unabhängig davon, was max_program_words (65.535) erlaubt |
| öffentliches Segment | 32.768 Wörter, beim Deploy festgelegt |
| privater Input mit versiegeltem Transkript | 16.071 Wörter |
rand-guest run --tier 14 prüft die ersten beiden, bevor Sie deployen. Eine Hash-Tabelle macht einen Beweis größer. Der Tier-14-Beweis des Ledgers ohne Hash-Tabelle umfasst 1,50 MB. Die SHA-256-Tabelle bringt den Liabilities-Beweis auf 1,91 MB, und die Keccak-Tabelle den der Abstimmung auf 3,35 MB, was den größten Teil der 4-MiB-Obergrenze ausmacht.
Conventions the four share
- Slot 0 ist ein Status, und 0 ist nie ein gültiger. Jedes Ergebnis, auch jeder Fehlschlag, hat eine Nummer ab 1, und ein Fehlschlag hält sauber an, sodass er bewiesen werden kann. Ein Receipt, das nur aus Nullen besteht, lässt sich nicht mit einem Ergebnis verwechseln.
- Slot 7 bleibt unberührt. Ein Rust-Guest, der in Panik gerät, schreibt dort
0xdeadbeef(3.735.928.559) hin. Ein Leser prüft zuerst Slot 7. - Die Slots 2 bis 6 tragen einen 160-Bit-Wert: einen Nullifier, eine Wurzel, einen Link, ein Commitment. Ein vollständiger 256-Bit-Hash besteht aus acht Wörtern und ließe keinen Platz für einen Status. Die Kürzung auf fünf Wörter behält 80 Bit Kollisionsresistenz, und jede Seite sagt, was diese schützt.
- Die Byte-Reihenfolge folgt dem Hash. Keccak-Werte sind Little-Endian-Wörter und SHA-256-Werte sind Big-Endian-Wörter, weil jeder Syscall seinen Block so liest. Ein Host-Skript und ein Guest, die sich hierin uneinig sind, erzeugen Digests, die schlicht nie übereinstimmen.
- Alles, was pro Instanz variiert, steht im öffentlichen Segment; alles, was pro Aufruf anfällt, ist private Eingabe. Die Wurzel einer Abstimmung, eine Reservenzahl und die Gewichte eines Modells stehen beim Deploy fest und sind an die Programm-ID gebunden. Ein Aufruf bringt keine eigenen öffentlichen Wörter mit.
- Eingabewörter sind auf der Kommandozeile dezimal. Die Seiten machen aus einer Witness-Datei Flags mit
$(sed 's/[0-9][0-9]*/--input &/g' witness.txt).
Rufen Sie selbst einen auf
Mit entpackten Quellen, gebauter Toolchain und einem aufgeladenen v0.6.8-Wallet sind es vier Befehle, um als eines der Demo-Mitglieder abzustimmen:
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)
Für den Aufruf wird das Image nicht benötigt: rand call holt den Code und das öffentliche Segment des Programms vom Node, prüft, dass sie zur ID gehasht werden, und beweist darüber.
Ein Ergebnis zu lesen erfordert weder ein Wallet noch einen Node. Jedes Receipt ist eine einzige Anfrage an den öffentlichen Endpunkt:
$ 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"}}
Die API des Explorers liefert die Aufrufe eines Programms in einer Anfrage: https://randscan.org/api/v1/programs/<program id> gibt call_count und recent_calls zurück (Explorer API).
Was keines von ihnen tut
Ein Programm auf Rand ist ein Beweis über eine Berechnung, und die Chain handelt nach keiner seiner Ausgaben. Keines dieser Programme bewegt RAND oder einen Token, hält ein Guthaben oder erinnert sich an einen früheren Aufruf. Die Abstimmung zählt, wer die Receipts liest. Die Verbindlichkeiten werden mit Reserven verglichen, die eigene Belege brauchen. Die Salden des Ledgers sind Zahlen in einem Off-Chain-Baum. Die Kreditentscheidung ist ein Bit, dem ein Kreditgeber folgen kann oder nicht. Jede Seite endet mit einer Liste dessen, was ihr Beweis den Nutzern überlässt.