Run a Rand node
The reference full node and its wallet are written in Rust and build from source. Two binaries come out: rand-node, the node and operator tools, and rand, a wallet that holds a shielded spend key, talks to any node over JSON-RPC and runs the zkVM prover locally for every transfer and every confidential call. An experimental testnet, chain 21, is live on 26 validators on DigitalOcean droplets. It is not audited and carries no real value. Linux x86_64 binaries of both are attached to the v0.8.0 release, the build the fleet was rolled to.
# 1. Rust (rustup picks up the pinned 1.98.1 toolchain) and RocksDB's build deps
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
sudo apt install build-essential clang cmake pkg-config libssl-dev # Linux
# 2. Clone and build (10–20 min the first time)
git clone https://github.com/randprotocol/fullnode.git
cd fullnode
cargo build --release # target/release/rand-node, target/release/rand
# 3. A two-validator chain on this machine (RPC on 8545 and 8546)
scripts/local-testnet.sh
target/release/rand --rpc http://127.0.0.1:8545 status
# 4. Tests; release because STARK proving is slow in debug (678 tests, about 43 minutes as measured on 2026-09-14)
cargo test --workspace --release
# Release v0.8.0 (c0878c3f) runs chain 21, and no earlier release does: chain 21 runs constraint set 9, whose verifier keys and proof encoding nothing older carries. Build it from the tag, or on Linux x86_64
# take the release binaries and check them against the SHA256SUMS published with it
git checkout v0.8.0 && cargo build --release
curl -LO https://github.com/randprotocol/fullnode/releases/download/v0.8.0/rand-node -LO https://github.com/randprotocol/fullnode/releases/download/v0.8.0/rand \
-LO https://github.com/randprotocol/fullnode/releases/download/v0.8.0/msauth-vk-production.bin# the pinned multisig verifier key: rand-node loads it from beside its binary and refuses to start without it
sha256sum rand-node rand msauth-vk-production.bin # must print the release's values (SHA256SUMS, signed as SHA256SUMS.sig):
# 324a4ab1029d39e67fc2de36b036341b0f88f13089997d7fe24c596363a58278 rand-node
# e3a38e74ee28743a960fc1c3eb1ac6c538e18dc259411e10afff7f54e268bbf4 rand
# 68facb06c7f321aaf1d4d541acf6b5c0e9d7f17788ed1c9add6c5a8a8f7acd30 msauth-vk-production.bin
# Your node identity: Dilithium2 validator key and p2p peer id in one file. Generate your own
# and keep it outside the checkout; the repository tracks no key any more
rand-node keygen --out node.key.json
# The genesis ships in the repository; every node must hold the identical file.
# init prints its hash, which must match the one below
rand-node init --datadir ./data --genesis deploy/genesis-chain21.json
# Dial the public validators (chain 21 kept chain 14's keys, so its peer ids and IPs).
# Omit --validator: your key is not in the validator set. A validator also needs NTP
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
# A wallet is a separate, shielded key. Its rand1… address is about 1.6 KB of text
rand keygen && rand address
# A faucet mint is signed by a validator, so it needs a validator's RPC: it works on the
# local two-validator chain as-is; an observer answers "ask a validator node". On chain 21
# the faucet mints only to the 16 wallets its genesis allowlists, and only from the validators it names
rand faucet # 100 RAND into a note only you can open
rand balance # scans the tree with your key; nobody else can
rand send <rand1 address> 1.5 # asks to confirm, proves (~2 min), waits for the commit
# Assemble a built-in guest: pay the surplus over 1000 if four private balances cover it
rand program build --guest private_payment --arg 1000 --out pp.json
rand program deploy pp.json # prints the program id (its content hash)
# Prove locally; only the proof, eight output words and a sealed input transcript go on chain
rand call <program id> --input 400 --input 250 --input 300 --input 75
rand receipt <tx> # outputs [1, 0, 25, 0, ...]: the surplus, published as data
# Later, open the transcript and check it against the H_IN the proof published
rand open-call <tx> # inputs: [400, 250, 300, 75] — verdict: faithful
Requirements
- OS
- Linux x86_64 or macOS arm64
- Rust
- 1.98.1, pinned by rust-toolchain.toml; rustup installs it
- Build deps
- C++ compiler, cmake, pkg-config, OpenSSL headers (RocksDB)
- First build
- 10 to 20 minutes: RocksDB and Plonky3 from source
- Ports
- TCP 30303 inbound on public nodes; RPC on 127.0.0.1:8545
- Server
- A 1 to 2 vCPU cloud instance runs a testnet validator
- Clock
- NTP (chrony) on a validator: it withholds its vote from a block more than 15 s ahead of its own clock, so a slow clock silently stops voting
- Proving
- CPU only today: a shielded transfer takes about two minutes on a laptop (a 7 s authorisation proof and a bundle proof of about 110 s) and about 6 GB of memory; a small call adds about 30 s
Choose a role
- Observer open to anyone
The same binary without --validator. Syncs the chain, verifies every certificate and every bundle and call proof, re-executes every block, compares receipts, and serves JSON-RPC. This is what a new key runs on the public testnet.
- Validator bonded
Votes and proposes under chained HotStuff. On chain 21, as on chain 20, a new validator is admitted by vote first: validators holding more than two thirds of stake sign an AdmitValidator action for its key. Then it registers the node key (rand-node register --v2), bonds at least 1,000 RAND from a wallet, and enters the set at the third 1,000-block epoch boundary after the bond (chain 18's two-epoch bond delay). Chain 18 started from 26 validators at 1,000 RAND each; more than two thirds of stake, 18 of 26, must be online and voting to commit. Generate your own key: no validator key is kept in the repository any more, and the key files committed for earlier chains are published seeds.
- Wallet and prover local
The rand client holds a 256-bit spend key and signs nothing: two proofs are the authorization, a small one made with the spend key and the 4-in-4-out bundle proof. It scans the tree for its own notes, proves both on your machine, the same shape for RAND and for any RPL token, and for a confidential call proves the program too. Only proofs, commitments, nullifiers and sealed envelopes reach the chain.
Not running a node? The same wallet ships as an app for iOS, Android, Chrome and Firefox: Wallets.
Every node checks everything
A node that syncs re-verifies every certificate, re-executes every block including its proofs, and compares receipts before accepting. At startup it checks its own database, truncates anything inconsistent while keeping its vote-safety state, and refetches the gap from peers.
# Node stopped: hashes, QCs, signatures, proofs, replay
rand-node verify --datadir ./data --mode full
rand-node verify --datadir ./data --repair
Fault tests run on the live testnet
- ›Stop one of four validators: the chain keeps committing and the node catches up in seconds when it returns.
- ›Stop two of four: the chain halts without quorum and resumes without a fork when they return.
- ›Corrupt a node's database: detected at startup, truncated, resynced from peers, byte-identical afterwards.
- ›Transfers through different nodes: all committed, balances identical everywhere, supply unchanged.
- ›A dropped peer link is re-established by the redial logic within about 30 seconds.
Not yet implemented
Listed so the gap between the paper and the node is legible rather than inferred.
- ›Persistent per-program state and cross-program calls
- ›A compiler in the wallet itself: a program is a built-in assembled guest or an image built with the separate rand-guest toolchain
- ›Slashing and jailing, block rewards, fee markets, the hash-sortition leader beacon
- ›A nullifier accumulator, proof pruning after finality, proof batching
- ›A tier-20 proof of the SPL Token transfer, and the GPU prover market of the paper