Three binaries come out of cargo build --release: rand-node (the node and the operator commands), rand (the shielded wallet) and rand-prover (the delegated prover). Every command accepts -h/--help. This page describes the v0.8.0 release (c0878c3f), the build chain 21 runs since 2026-10-12; where v0.6.8 (chain 20’s build) behaved differently, it says so, and what v0.8.0 adds is gathered at the end, What chain 21 and v0.8.0 add. A rand wallet older than v0.8.0 cannot send on chain 21: constraint set 9 changed every proof’s encoding and every verifier key, and the genesis binding every chain since 20 carries refuses a proof made for another chain in any case. The first two hold different kinds of key: a node key is a Dilithium2 key pair that signs blocks; a wallet key is a shielded spend key that signs nothing on chain.
rand-node
rand-node <COMMAND>
keygen Generate a new Dilithium2 key file (refuses to overwrite an existing one)
address Print the address, public key, and libp2p peer id of a key file
genesis Write a genesis.json: every validator key is staked, each --alloc becomes a deposit note
alloc-note Print one genesis alloc note as JSON, RAND or a listed token, for splicing into a genesis
init Initialise a data directory from a genesis file
run Run the node
verify Verify the chain in a data directory without running the node
db Database maintenance on a stopped node's data directory
safety status | clear-halt | release-lock: a stopped validator's consensus safety state
status Show node status
register Print this node's Registration, for the wallet that bonds it in
admit sign | submit: the validator set's vote to admit a new key (admission by vote)
unbond Move bonded stake into unbonding
withdraw Pay released stake and rewards into a note at the payout address
bridge-gov rotate-pq | rotate-pause | cancel-rotation: the bridge's post-quantum rotations
vesting status | claim | revoke | bond | unbond: genesis vesting (chains with a vesting section)
multisig id | status | pay | rotate: the signers' side of a multisig account (chains with a multisig section)
price status | sign | submit: the validator set's RAND/USD price vote (chains with fees.usd_subsidy)
aggregator register | unbond | withdraw — the aggregator role (aggregation chains only)
aggregate The aggregate daemon (aggregation chains only)
safety, admit and bridge-gov are new in v0.6.8; multisig and price in v0.8.0. There is no balance and no transfer: a transfer needs a shielded spend key, which only the wallet holds.
One global flag, --allow-test-fri-profile (or RAND_ALLOW_TEST_FRI_PROFILE=1), lets init, run and verify accept a genesis whose fri_profile is test. That profile is about 17 bits of soundness, and whoever writes a genesis file can name it, so since v0.6.8 a node refuses such a genesis without the flag. The test suites set it; nothing else should.
keygen, address
| command | argument | default |
|---|---|---|
keygen | --out <OUT> | node.key.json |
address | --key <KEY> | required |
keygen writes { "seed": <32-byte hex>, "address": <base58>, "public_key": <hex> } with mode 0600. The file is the only copy of the seed. Since v0.6.8 keygen refuses an --out that already exists (… already exists or cannot be created; refusing to overwrite a key file) and leaves it untouched; there is no --force, so move an old file aside to replace it. v0.6.7 and earlier overwrote it without asking. address prints address, public_key (1,312 bytes hex) and peer_id.
genesis
| argument | default | meaning |
|---|---|---|
--chain-id <CHAIN_ID> | 1 | chain id; transactions and gossip topics are bound to it |
--validator <KEY,STAKE,PAYOUT> | required, repeatable | one register entry: a key file path or hex public key, the stake in RAND (at least 1,000), the rand1… payout address |
--epoch-blocks <N> | 1000 | blocks per epoch |
--alloc <ALLOCS> | none, repeatable | a deposit note: rand1<address>=<amount in RAND> |
--out <OUT> | genesis.json | output path |
--faucet | off | testnet only: enable Mint. Beside a bridge section it needs --testnet |
--testnet | off | v0.6.8: write "testnet": true, the marker that lets a faucet sit beside a bridge section. Part of the genesis hash when given |
--no-confidential | off | disable Deploy and Call |
--fri-profile <production|test> | production | test is insecure and for the test suite; init, run and verify refuse such a file without --allow-test-fri-profile |
--max-program-words <N> | none (4096) | the largest program a deploy may carry, 1..=65535. Written only when given; part of the genesis hash whenever present, so --max-program-words 4096 is a different chain from omitting it. Every node needs a v0.4 binary before init on such a file |
--max-proof-bytes <N> | none (2,097,152) | the largest proof a transaction may carry, 1048576..=33554432. Every proof cap follows it |
--max-block-bytes <N> | none (4,194,304) | the largest block, and so the largest transaction, 4194304..=67108864, and at least 2 × max_proof_bytes + 1 MiB, or 3 × max_proof_bytes + 1 MiB with --auth-guest, where a call carries three proofs (bundle, auth, call) |
--max-call-envelope-bytes <N> | none (18,432) | the largest call input envelope, 18432..=1048576 |
--max-program-public-words <N> | none (0) | the largest public input a deploy may fix, 0..=65535; 0 admits none |
--tokens <TOKENS.JSON> | none | the RPL tokens section, as a JSON file {"registration_fee": …, "mint_cap_per_day": …, "tokens": [...]}. Omitted, the file has no such section and hashes as before. A bridge section needs one with a non-zero mint_cap_per_day (per backing, per UTC day, in the token’s eight-decimal units); a listed token needs a bridge section |
--envelope-bytes <N> | none (the legacy 1,348-byte envelope) | the exact size of every note envelope: a bundle output, a faucet mint, a withdraw, a bridge deposit, a genesis alloc. Only 1860 is accepted, the size that lets every envelope carry an encrypted memo. Part of the genesis hash when given, and set before this command’s own --alloc notes are sealed |
--vesting <VESTING.JSON> | none | the vesting section: the timelocked allocations, {"entries": [{"id", "class", "beneficiary", "amount", "start_ms", "cliff_ms", "linear_ms", "step_ms"?, …}]}. Since v0.6.8 a revocable entry lists revokers (1 to 5 keys), a threshold and the treasury a revoke pays, and a file that leaves one out is refused |
--staking <STAKING.JSON> | none | v0.6.8: the staking section as a JSON file (faucet_budget_per_epoch, bond_activation_epochs, max_weight_bps, max_stake_entry_per_epoch or max_stake_entry_bps_per_epoch, registration_v2, faucet_recipients, faucet_minters, admission_by_vote, slashing), validated by the build |
--consensus-domain <0|1> | none (0) | v0.6.8: 1 binds every vote, new-view and proposal to this genesis |
--bundle-guest <v1|v2|v3> | v1 | the bundle guest pinned as hc_bundle: v1 (the hidden-asset guest chains 14 and 15 ran), v2 (the branch-free guest of chain 16) or v3 (split authorisation: the guest takes nk and a salt instead of the spend key). v3 needs --auth-guest |
--auth-guest | off | pin this build’s auth guest as hc_auth: every bundle then carries an auth proof over the spend key, bound to its transaction, whose output equals the bundle’s auth_commit. Required with --bundle-guest v3 and refused with v1 or v2, before any file is written. Absent, the file has no hc_auth field |
--hardening-v6 | off | turn the v0.6 rules into validity rules (hardening_v6): the pc window, uncallable deploys, canonical proof shapes and proof-of-work words, the call binding and the program-table floor. Absent, a node still refuses those at its pool, as policy |
--gas-price <UNITS> | none (no gas section) | units (10⁻⁹ RAND) per gas the chain itself charges a call. This flag is what writes the gas section (metering: "circuit"); the three flags below are refused without it. Part of the genesis hash |
--byte-price <UNITS> | 800, only with --gas-price | units per KiB, or part of one, of call proof plus input envelope, from byte 0 |
--bundle-gas-limit <N> | 20479, only with --gas-price | the exact GAS_LIMIT every bundle proof must declare; a bundle proof declaring any other value is refused. The genesis accepts only 20479, gas_max(14, 0, 0) for the tier-14 bundle guest |
--gas-dynamic <TARGET_BYTES,TARGET_GAS,ADJUST_BPS> | none (fixed prices) | turn on the per-block price controller, floored at the section’s own starting gas_price and byte_price. Refused beside --aggregation |
--max-gas-price <UNITS>, --max-byte-price <UNITS> | none (no ceiling) | v0.6.8: the ceilings the controller never lifts a price over, each at least the starting price. Refused without --gas-dynamic |
--gas-byte-load paying | none (every transaction’s bytes) | v0.6.8: only a call’s proof and input envelope move byte_price, so a block of transfers moves none. Refused without --gas-dynamic; paying is the only value |
--binding-domain <0|1> | none (no field) | v0.6.8: 1 binds the genesis hash into every transaction binding and every signed action message (a faucet mint, unbond, withdraw, the RPL token messages, the aggregator actions, the bridge governance messages), so a proof or signature for this chain verifies on no other. Omitted, the command warns: a v0.6.8 wallet proves the genesis-bound form on every chain id outside 14 to 19, so every chain from 20 on is cut with 1 |
--proof-window-blocks <N> | none (256) | v0.6.8: one window, 256 to 4,096 blocks, for how old a bundle’s anchor and its time may be. Served as rand_getLimits.proof_window_blocks |
--program-state-cell-fee <UNITS> | none (no program_state section) | v0.6.8, RPL-2: turn on program cells, program vaults and the Invoke action, with this cell fee in RAND units (at most 1,000 RAND) added to an invoke’s fee floor per cell it creates. Needs --tokens, --gas-price, --hardening-v6 and --auth-guest, each refused by name before any file is written |
--aggregation <BOND,MAX_COVERS,SUBSIDY,HALVING,WINDOW>, --admitted-shape <…> | none | the aggregation section; the v0.6.8 build refuses to start on a genesis that has one |
Prints one alloc line per --alloc, then the genesis hash, the validator and note counts, the epoch length, the five limits, hc_bundle and hc_auth (none without --auth-guest), then the gas line: gas: price P/gas, B/KiB, bundle limit N, dynamic: target … B / … gas, adjust … bps, floor …/…, ceiling …/…, byte load all|paying, or gas: none without --gas-price, and with --program-state-cell-fee a program_state: cell fee N RAND line. init prints the same gas line. The five limit flags behave alike: omitted, the file has no such field and the chain runs the default; given, the field is part of the genesis hash even at its default value. Chain 20 was cut with these flags (deploy/cut-chain20-genesis.sh); chain 21 (deploy/cut-chain21-genesis.sh, v0.8.0) with the same ones except --max-proof-bytes 6291456 --max-block-bytes 19922944 --gas-dynamic 9961472,262144,1250, plus --fees fees.json (burn_base and burn_floor), --perps … (RPL-3, one BTC-PERP market on zUSD), --multisig MULTISIG.JSON (the foundation treasury) and --auth-multisig-guest (the private-multisig guest, hc_auth_multisig):
--max-program-words 65535 --max-proof-bytes 4194304 --max-block-bytes 20971520 \
--max-call-envelope-bytes 65536 --max-program-public-words 32768 \
--tokens tokens.json \
--bundle-guest v3 --auth-guest --hardening-v6 \
--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 \
--epoch-blocks 1000 --faucet --fri-profile production
Chains 18 and 19 were cut with the same flags up to --envelope-bytes 1860 and none of the v0.6.8 ones. Chain 20’s proof cap was 4 MiB, down from the 8 MiB of chains 13 to 16, and chain 21’s is 6 MiB with a 19 MiB block, room for the tier-18 multisig auth proof: a split-authorisation transaction carries up to three proofs, and 3 × 4 MiB + 1 MiB fits the 20 MiB block. tokens.json holds a registration_fee of 1 RAND, a mint_cap_per_day of 10000000000000, max_tokens 4096, burn_registration_fee and bound_note_value, and zUSD listed with its seven backings. There is no --bridge flag, and the cut script still splices the bridge, staking and consensus_domain sections into the file afterwards (on chains 20 and 21: the bridge at guardian set 1 with eight PQ guardian keys, the pause key, rules_v2, rotation and fees; the staking section with the faucet allowlist, admission_by_vote and a 2,500 bps entry budget; consensus_domain: 1; and zUSD’s genesis notes, made with alloc-note --asset 1 --envelope-bytes 1860). The genesis hash that counts is the one init prints on the finished file.
Each --alloc builds one deposit note with fresh commitment randomness, so cutting twice yields two different hashes. The command always writes the note’s opening beside cm and amount, which a chain with a tokens section requires:
"alloc": [
{ "cm": "<64 hex>",
"envelope": { "kem_ct": "<hex>", "to_receiver": "<hex>", "to_sender": "<hex>", "body": "<hex>" },
"amount": 1000000000000,
"opening": { "pk": "<64 hex>", "time": 0, "r": "<64 hex>" } }
]
A genesis note’s owner and amount are therefore public by design once the file is published; only when and into what it is later spent stays private. Cut a genesis once and keep the file.
alloc-note
--to <rand1…> (required), --amount <N> (required), --asset <INDEX> (default 0), --envelope-bytes <N> (default: the legacy format). Prints one alloc note as JSON, sealed to --to as genesis --alloc seals one. A genesis that sets envelope_bytes refuses a note sealed without the matching --envelope-bytes. --amount is in whole units of the asset: nine decimals for RAND, eight for a bridged token (10 at asset 1 is 10 zUSD). A note of a listed token needs that token’s backings to carry locked amounts summing to exactly its notes.
init
| argument | meaning |
|---|---|
--datadir <DATADIR> | data directory to create |
--genesis <GENESIS> | genesis file; copied to <datadir>/genesis.json |
Prints the genesis hash and the same gas: line as genesis.
run
| argument | default | meaning |
|---|---|---|
--datadir <DATADIR> | required | initialised data directory |
--key <KEY> | required | key file: validator identity, p2p identity, fee recipient |
--listen <LISTEN> | /ip4/0.0.0.0/tcp/30303 | libp2p listen multiaddr, repeatable |
--bootstrap <BOOTSTRAP> | none, repeatable | /ip4/<ip>/tcp/<port>/p2p/<peer-id>, dialled at start and every 30 s while disconnected. Since v0.6.8 its peer id is reserved |
--reserved-peer <PEER_ID> | none, repeatable | v0.6.8: a peer admitted past the inbound connection cap and served from the validators’ share of the sync budget. Bootstraps and every validator whose signed peer binding this node has seen are reserved without it; list the fleet’s validators here on a freshly cut chain, whose store holds no bindings yet |
--strict-gossip | off | v0.6.8: gossipsub’s strict validation, accepting only messages with a valid author signature, sequence number and source. Strict and permissive nodes interoperate, so it rolls node by node |
--rpc <RPC> | 127.0.0.1:8545 | the operator’s JSON-RPC listener: every method, and it trusts loopback. Keep it on loopback and never forward a public endpoint to it |
--public-rpc <ADDR> | off | v0.6.8: a second, public listener with a fixed method set (no viewing-key methods, rand_mint or rand_getPeers), no batches, no WebSocket and one meter for all callers; what a reverse proxy or an SSH forward should point at (JSON-RPC) |
--rpc-viewing-token-file <PATH> | off | v0.6.8: require Authorization: Bearer <first line of the file> (32 or more characters) on the viewing-key methods of --rpc |
--validator | off | take part in consensus with this key |
--no-mdns | off | disable LAN discovery |
--block-interval-ms <MS> | 1000 | minimum spacing between proposals |
--view-timeout-ms <MS> | 3000 | base view timeout; doubles per consecutive timeout up to 8× |
--verify-chain <MODE> | quick | off, quick, full |
--keep-raw-proofs | off | archive mode on an aggregation chain |
--prune-history <WINDOW> | none (keep everything) | keep only this much block history, <n>m, <n>h or <n>d, at least 1h: older blocks lose their block, QC, transactions and receipts; the ledger stays. On chain 21, as on chain 20, 24 of the 26 validators run 24h and two nodes are archives; an archive and a mainnet node never set it. Height lookups below the floor answer -32010 (JSON-RPC) |
--min-free-disk-mb <MB> | 1024 | refuse to start with less free on the data directory’s filesystem; rand_getHealth says disk_low under four times it. 0 disables the guard |
--rpc-viewing-open | off | let non-loopback callers use the viewing-key methods; only behind something that authenticates |
--gas-price <UNITS> | 100 | units (10⁻⁹ RAND) per gas this node’s pool demands of a call, over gas_max of its proof header. Admission policy, not a chain rule: a block carrying a cheaper call is still valid. 0 with --byte-price 0 runs no policy. On a chain whose genesis has a gas section, as chain 20’s did and chain 21’s does, the chain’s own prices are the validity rule |
--byte-price <UNITS> | 800 | units per KiB, or part of one, of a call’s proof and input envelope, from byte 0, under the same policy |
--prover <ADDR> | off | host the delegated prover (the prover_* JSON-RPC) on this address: a listener of its own, which must differ from --rpc. The node refuses to start without <prover-home>/prover.key.json, made by rand-prover keygen |
--prover-home <DIR> | <datadir>/prover | the prover’s prover.key.json and pairings.json |
--prover-accept-spend-key | retired | refused at startup since v0.6.8: run given this flag exits before the node key is read, naming the flag. No prover accepts a spend-key witness any more; take it out of the unit file. On v0.6.7 it accepted spend-key witnesses for bundle guest v1 or v2 chains |
--prover-max-parallel <N> | 1 | proofs the hosted prover runs at once; the start is refused unless 5.74 GB × N + 1 GiB of memory is available |
--prover-max-queue <N> | 8 | jobs queued beyond those running |
--prover-cuda | off | prove on the CUDA backend (a build with --features cuda); no CPU fallback |
--prover-skip-memory-check | off | skip the free-memory gate |
--prover-fee <RAND>, --prover-fee-address <rand1…> | none | the fee every job sent to the hosted prover must pay, in RAND with up to nine decimals: one RAND output to the address, inside the bundle proved. Each flag needs the other |
--prover-allow-origin <ORIGIN> | browser extensions and loopback pages | a browser origin whose pages may read the prover’s replies, repeatable; given once or more, the values are the whole list. * allows every website and prints a warning |
Environment: RUST_LOG (default info,libp2p=warn,libp2p_mdns=off). Ctrl-C or SIGTERM shuts the node down cleanly.
The public endpoint https://rpc.randprotocol.org is an allowlist proxy in front of an archive node; since 2026-10-01 its allowlist is the public listener’s method set.
safety
A stopped validator’s consensus safety state (v0.6.8). Each command opens the data directory itself, so none runs beside a live node.
rand-node safety status --datadir <dir>
rand-node safety clear-halt --datadir <dir> --i-have-compared-this-node-with-the-fleet
rand-node safety release-lock --datadir <dir> --i-give-up-this-validators-lock
status prints the committed head, the view and last voted view, the high QC, whether the validator is locked above its head, and any recorded safety halt. A node that sees a certified three-chain try to commit a block that does not descend from its own committed head stops and records it (audit v6, CON-5); run then exits with the record until clear-halt. Compare this node’s head with two other validators’ and re-sync from an archive if it differs before clearing. Since v0.6.8 (CON-4) no quorum of “not held” answers releases a lock any more: a validator locked on a block it cannot obtain stays silent until a proposal whose justify outranks the lock arrives or the block is fetched. release-lock lowers the persisted lock to the committed head’s certificate; use it on one validator at a time, and only when the block is lost for good. Without its acknowledgement flag each writing command prints what it would do and changes nothing.
verify
| argument | default | meaning |
|---|---|---|
--datadir <DATADIR> | required | the node must not be running |
--mode <MODE> | full | quick or full |
--repair | off | truncate the damaged tail; safety state is kept |
Exit code 0 if consistent, 2 if a problem was found without --repair.
db
db drop-receipts-index --datadir <DATADIR>, with the node stopped: drop the receipts_by_program index before rolling back to a pre-v0.3 build, which cannot open a database that has it. A later start rebuilds the index from the receipts.
status
--rpc <URL> (default http://127.0.0.1:8545). Prints rand_status verbatim.
register, unbond, withdraw
| command | arguments | meaning |
|---|---|---|
register | --key, --payout <rand1…>, --rpc, --v2 | print a hex Registration signed by this node’s key; the RPC is read for the chain id, and with --v2 for the genesis hash too. --v2 is required on a chain whose genesis sets staking.registration_v2, as chains 20 and 21 do; a v1 registration is refused there, a v2 one everywhere else |
unbond <amount RAND> | --key, --rpc, --no-wait | move stake into unbonding; free; withdrawable two epochs later |
withdraw <amount RAND> | --key, --rpc, --no-wait | pay released stake and rewards into a note at the payout address, less the 0.001 RAND base |
admit sign|submit
Admission by vote (v0.6.8). On a chain whose genesis sets staking.admission_by_vote, as chains 20 and 21 do, a new validator key registers only after validators holding strictly more than two thirds of the voting weight have signed it in.
| command | arguments | meaning |
|---|---|---|
admit sign | --candidate <key hex, registration hex, or a file holding either>, --key <validator key>, --genesis-hash <hex> | print this validator’s vote as one line, <voter key hex>:<signature hex>, over the genesis hash and the candidate’s address, so it is good on exactly one chain. Offline: reads no node |
admit submit | --candidate …, --signature <line> (repeatable), --rpc, --no-wait | check every vote against the node’s genesis hash (a vote for another genesis or candidate, or a voter twice, is named), order them and submit the fee-less AdmitValidator |
The candidate is the key the registration will carry; rand-node address --key prints it, and the hex register printed holds it. After the admission commits, the bond is as before (rand bond … --registration) and consumes it. rand_getAdmitted lists what is admitted and waiting.
bridge-gov
The bridge’s post-quantum rotations (v0.6.8), for guardians and the pause-key holder. Every step reads the chain id, its binding domain and rand_getBridgeState.rotation_nonce from --rpc (default http://127.0.0.1:8545).
| command | does |
|---|---|
rotate-pq message --new-key K… | print the hex rotation message for the current nonce, to compare on every signer |
rotate-pq sign --new-key K… --key F --index I [--possession] | print I:signature: a current PQ guardian’s quorum line (I its index in the current set) or, with --possession, a new key holder’s line (I its index in the new set); refuses a key file that is not the key at I |
rotate-pq submit --new-key K… --quorum @F [--possession @F] [--no-wait] | assemble RotatePqGuardians, or the V2 action with possession lines, and send it; refuses possession on a chain that does not take it and its absence on one that requires it, as chains 20 and 21 do |
rotate-pause message|sign|submit --new-key K | the same for the pause key (possession index 0) |
cancel-rotation sign --kind pq|pause --key PAUSE_KEY | print 0:signature over the cancel message, checked against the chain’s current pause key |
cancel-rotation submit --kind pq|pause --signature @F | send CancelRotation, dropping a pending rotation before its delay ends |
--new-key is a hex Dilithium2 public key, @file holding one, or @key-file (its public key). Signature lines are index:hex, separated by whitespace or newlines, # comments allowed.
vesting status|claim|revoke|bond|unbond
The holder’s and the revoker’s side of a timelocked genesis allocation, on a chain whose genesis carries a vesting section. Chain 20’s had none and chain 21’s has none. The key is a rand-node keygen file.
| command | arguments | meaning |
|---|---|---|
vesting status <ENTRY> | --rpc | the entry as rand_getVesting serves it: its terms, what has vested, what is claimable now |
vesting claim | --entry <id>, --to <rand1…>, --amount <RAND> or --all, --key, --rpc, --no-wait | pay what has unlocked into a note at --to, less the 0.001 RAND base; signed by the beneficiary key |
vesting revoke prepare | --entry, --margin-secs (default 600), --out (default revoke.json), --rpc | v0.6.8, no key: write the revoke every revoker signs, a note of the part still unvested --margin-secs past the head, paid to the entry’s genesis treasury (there is no --to) |
vesting revoke sign | --proposal <file>, --key, --index? | one revoker key, offline: print <index>:<signature hex> |
vesting revoke submit | --proposal <file>, --signature <index:hex> (repeatable), --rpc, --no-wait | send the revoke with at least the entry’s threshold of signatures, within 256 blocks of prepare. v0.6.7’s single-key vesting revoke --to is gone |
vesting bond <amount RAND> | --entry, --validator <address>, --registration <hex>, --key, --rpc, --no-wait | irrevocable entries: bond locked RAND as that validator’s stake; a validator not yet in the register needs what register printed |
vesting unbond <amount RAND> | --entry, --key, --rpc, --no-wait | take bonded RAND back into the lock; claimable again after the unbonding epochs |
aggregator register|unbond|withdraw, aggregate
The aggregator role’s commands for a chain with an aggregation section: aggregator register --key --bond <RAND> --payout <rand1…> --rpc prints a signed AggregatorRegistration; aggregator unbond and aggregator withdraw mirror the validator pair. aggregate --key <KEY> [--rpc <URL>] [--watch] [--interval-secs 15] [--no-wait] is the daemon that polls rand_getUnsealed, proves one aggregate and submits it. Inert on chain 21, as on chain 20: neither genesis has an aggregation section. Since v0.8.0 a failed pass (an RPC failure, a nonce that moved, a refused aggregate) is logged and retried on the interval rather than stopping the daemon, up to --watch-max-failures (20) in a row.
multisig id|status|pay|rotate
v0.8.0, the signers’ side of a multisig account (multisig accounts), on a chain whose genesis carries a multisig section, as chain 21’s does. Creating and funding an account are the wallet’s (rand multisig create / deposit, below); the key here is a rand-node keygen file, one per signer.
| command | flags | does |
|---|---|---|
multisig id | --chain-id, --salt <64 hex>, --threshold, --signer <pubkey hex> (repeat, in order) | derive an account id offline, refusing a set the chain would (more than 10 signers, a threshold of 0 or above the count) |
multisig status <id> | --rpc | the account as rand_getMultisig serves it: signers, threshold, nonce and vault |
multisig pay prepare | --account <id>, --to <rand1…> (repeat), --amount <A> (one per --to, in the asset’s display units), --asset <N> (omitted: RAND), --out (pay.json), --rpc | no key: write the payment every signer signs — each payout’s note sealed to its recipient, the account’s nonce, the head height as time; the account also pays the 0.001 RAND base from its own RAND row |
multisig pay sign | --proposal <file>, --key, --index | one signer, offline: print <index>:<signature hex> |
multisig pay submit | --proposal <file>, --signature <index:hex> (repeat), --rpc, --no-wait | send the payment with at least the account’s threshold of signatures, within proof_window_blocks (1,024 on chain 21) of prepare |
multisig rotate prepare / sign / submit | as pay, with --signer <pubkey hex> (repeat, the new order) and --threshold | the new signer set, signed by at least the current threshold of the current signers; fee-less |
The chain-21 treasury, d5fd1883…1958, is such an account: 2 of 3 signers, seeded with 100,000 RAND at the cut.
price status|sign|submit
v0.8.0, the validator set’s RAND/USD price vote for the dollar-indexed sealing subsidy (genesis fees.usd_subsidy), in admit’s shape with a nonce: price status --rpc prints the voted price, the height that set it, whether it is fresh for the next block and the next nonce; price sign --price <dollars> --nonce <N> --key <validator key> --genesis-hash <hex> prints one validator’s vote, offline; price submit --price … --nonce <N> --signature <line or file> … --rpc checks every vote against the node’s genesis hash, price and nonce and sends the fee-less SetRandPrice. Inert on chain 21, whose genesis has no usd_subsidy: the action is refused there.
run: proving, the verify checkpoint and the apply thread (v0.8.0)
Three run flags are new. --verify-chain quick, the default startup check, is skipped when this same clean build already verified the store through its head (the startup checkpoint, written as the node runs); --verify-chain replay forces the walk, and rand-node verify always walks. --apply-proposals-inline restores the pre-v0.8.0 behaviour of applying an incoming proposal on the consensus loop; by default a follower applies it on a blocking thread (at most four at once) while the prechecks and the insert stay on the loop. --msauth-vk <PATH> (default $RAND_MSAUTH_VK, else msauth-vk-<profile>.bin beside the binary) names the pinned multisig verifier key: on a chain naming hc_auth_multisig, as chain 21 does, run and verify load it at start and refuse to start without it, so every chain-21 node ships msauth-vk-production.bin next to rand-node (Running a node).
rand (wallet)
Global options, before or after the subcommand:
| option | env | default |
|---|---|---|
--rpc <RPC> | RAND_RPC | http://127.0.0.1:8545; http or https (the public endpoint is https://rpc.randprotocol.org). A reply over 64 MiB is refused |
--key <KEY> | RAND_KEY | wallet.key.json; the note store lives beside it at <key>.notes.json. The store is bound to the chain it was scanned against (the node’s rand_getGenesisHash): pointed at another chain, such as a wallet kept across the cut from chain 19 to 20, it is emptied and rescanned from leaf 0 with a warning. Since v0.6.8 every command refuses a key file that group or other can read, naming the fix (chmod 600 <key>) |
--prover | off. Prove the bundle on the prover paired with this wallet (rand prover pair) instead of on this machine; the proof is checked here (its digest, its size and a local verify) before it goes into a transaction. Applies to every command that proves a bundle; for call and program invoke, only the paying bundle goes to the prover and the call proof stays local. Refused without a pairing, and together with --cuda except on call. On a split-authorisation chain such as chains 20 and 21 the prover is sent the viewing key nk and a salt, never the spend key: it can read this wallet’s whole history and cannot spend. The auth proof is always made on this machine. On a chain whose bundle guest is v1 or v2, v0.6.8 refuses --prover before the prover is asked anything | |
--max-prover-fee <RAND> | 1. The most a paired prover may charge per bundle. A quote above it is refused before any bundle is built; 0 refuses any fee. The fee is one more RAND output in the same bundle, shown as prover fee: <amount> RAND to <fingerprint> | |
--threads <N> | every core (v0.7.1). CPU threads a proof made on this machine uses; RAYON_NUM_THREADS when set and no flag; 0 refused. A wallet proves one bundle with a person waiting, so the default is every core (8 threads 17.1 s, 16 threads 13.9 s for a bundle on an M4 Max, fullnode docs/node-hardware.md §6) | |
--cpu | off (v0.7.1). Prove on the CPU even when a GPU is visible to a build with the CUDA backend; without --cpu or --cuda such a build takes the GPU and says so once on stderr |
Amounts are decimal RAND with up to nine decimals (1, 1.5, .25, 0.000000001). A token amount on send, token mint and token burn, and in a randpay: link, is in the token’s display units, at its registry row’s own decimals (10 of zUSD is 10 zUSD). bridge-burn, token create --fixed-supply and --initial take plain integers in the token’s smallest unit, and asset-balance prints them. Wherever a command takes a token, it takes the registry index, the rpl1… id or the 64-hex id; an id is resolved to its index from the node’s whole registry listing (rand_getTokens, every page), never by a per-token lookup, which would tell the node which token is about to move. A numeric index never reaches the node at all.
| command | arguments | behaviour |
|---|---|---|
keygen | write a new spend-key file at --key, mode 0600; refuses to overwrite | |
address | --uri [--amount A] [--asset X] [--memo M], --qr, --qr-png <FILE> | print this wallet’s rand1… address on stdout, exactly the address and a newline, so $(rand address) works in a script, and its fingerprint XXXX-XXXX-XXXX-XXXX on stderr: 16 Crockford base32 characters, a human check of the address that is never sent on the wire. --uri also prints a randpay: link (the address plus an optional amount, asset and memo). --qr renders that link as a QR code in the terminal; --qr-png writes it as a PNG |
contacts add <NAME> <TO> / list / show <NAME> [--qr] / remove <NAME> | --yes (add) | named addresses this wallet can send to by name. TO is a rand1… address or a randpay: link, of which only the address is kept. add prints the fingerprint and asks add? [y/N] unless --yes; a stdin that is not a terminal is refused without it. Stored at <key>.contacts.json, mode 0600. A name is 1 to 64 characters, never starts with rand1 or randpay:, and is unique |
prover pair <LINK> / prover pair --trusted / show / forget | --name <NAME>, --yes (pair) | pair this wallet with one delegated prover. pair takes the randprover: link rand-prover pair printed, refuses a URL that is not https:// (plain http:// only to localhost, 127.0.0.1 or [::1]), then states what the prover will receive (this prover will receive this wallet's viewing key: it can read the wallet's whole history; it cannot spend) and asks pair? [y/N]; --yes skips the question, and a stdin that is not a terminal is refused without it. Then it asks the prover’s prover_info, refuses unless its key fingerprint is the one the link names, and writes <key>.prover.json, mode 0600, replacing any earlier pairing. --trusted (v0.6.8) takes no link: it pairs with the validators’ prover pool, https://prover.randprotocol.org, from the link the build carries; its operators can read this wallet’s history and cannot spend. show prints name, URL, fingerprint, own and the prover’s key, never the token; forget deletes the file. v0.6.7 did not ask before pairing |
viewing-key | print this wallet’s viewing key nk, 64 hex: the parameter rand_importViewingKey takes. It reads every note the wallet sent or received and spends none | |
balance | scan the tree, save the store, print spendable value and the unspent note count | |
sync | --rescan | scan without printing a balance. A first sync reads every block header (1,024 a call) before the leaves. --rescan forgets every note, spent mark and cursor (keeping the chain binding) and rescans from leaf 0: the way back when a node reported this wallet’s notes as spent wrongly. Best run against a node you trust |
notes | --memo | every note this wallet has opened: index, asset, amount, height, spent, pending, and the memo cut to 24 characters, or whole with --memo |
asset-balance [INDEX] | scan, then print what this wallet holds in one asset, or a row per asset held; amounts in the asset’s own smallest unit | |
history | --memo | every note this wallet created for someone else, with the same memo column as notes |
send <TO> [AMOUNT] | --asset <INDEX|rand|rpl1…|hex> (default 0, RAND), --memo <M>, --fee <RAND> (default 0.001), --no-wait, --yes, --cuda | TO is a rand1… address, a randpay: link or a saved contact’s name, tried in that order. A link’s own amount, asset and memo fill in what the command line leaves out; AMOUNT is optional only when the link carries one, and a value given both ways that disagrees is refused. The command prints the confirmation, to <contact, if any> · fingerprint XXXX-XXXX-XXXX-XXXX · <amount> <asset> and, when there is a memo, a memo: "<text>" line of its own, then asks before proving unless --yes. A script must pass --yes: a stdin that is not a terminal is refused with stdin is not a terminal: pass --yes to send without confirmation. Then it selects at most two notes of the asset and at most two RAND notes for the fee, seals the memo into the payment output only, proves one four-slot hidden-asset bundle and, on a split-authorisation chain, the spend authorisation locally, submits and waits for the commit. A non-empty memo is refused before proving on a chain whose genesis has no envelope_bytes. AMOUNT is in the asset’s display units: decimal RAND for RAND, and a token at its registry row’s own decimals; only 0 or rand reads RAND, whatever index the node answered. A token id is decoded before the node is asked, and a listing row that answers it with index 0 or with disagreeing id fields is refused. The confirmation shows a token amount in display units and in base units, 10.00000000 zUSD (1000000000 units), so a node lying about a token’s decimals is visible before anything is sent. The asset index and fee are printed before proving. On chain a token transfer is a plain bundle, indistinguishable from a RAND payment; its fee is RAND, and a wallet without spendable RAND is refused before proving |
bond <VALIDATOR> <AMOUNT> | --registration <hex>, --fee, --no-wait, --cuda | stake onto a validator: the bundle burns the amount |
faucet [ADDRESS] | --amount <RAND> (default and maximum 100) | testnet only: ask a validator node to mint. On chain 21, as on chain 20, the faucet pays only the 16 wallets on its genesis allowlist |
program build | --guest <fib|memcpy|bubble_sort|balance_check|private_payment|public_echo|rpl2_counter>, --arg N (repeatable), --out <file> (default program.json) | assemble a built-in guest; prints the program id. rpl2_counter (v0.6.8) is the RPL-2 demonstration program |
program deploy <FILE> | .json, a raw .bin, or the image container rand-guest build writes; --public <FILE>, --input <u32> (repeatable) or --check-call, --cuda | print the program id, hc and word count; refuse a program over the chain’s cap (via rand_estimateFee) before proving; pay the deploy floor through a bundle, wait. --public stores a public input with the program (whitespace-separated u32 words, or an ELF .so), within the chain’s max_program_public_words; the program id then binds it. With --input or --check-call the wallet first dry-runs a call over those private inputs in the emulator and refuses unless it fits the call tier cap (14); nothing is proved for the check |
program show <ID> | deployed program metadata | |
program invoke <PROGRAM-ID> | --transition <FILE.json> (required), --input N (repeatable, private), --inputs-file <FILE.json> (a JSON array of u32, appended), --fee <RAND>, --gas-limit <N|max>, --tier T, --no-wait | v0.6.8, RPL-2: declare a state transition of the program and have the chain apply it. Before anything is proved, it checks that the chain has a program_state section, that the program exists, that the transition’s context fits the segment rule, that each reads entry matches the live cell (rand_getProgramCell; a mismatch names the stale cell and exits 3), that the vault can pay every pays entry plus what this transition deposits, and that this wallet holds the token and RAND the deposit and fee need. It counts the cells the writes create, dry-runs the program over the real context words, proves the call bound to the transaction, then the bundle (the fee, plus deposit.rand burned through burn_r and deposit.amount of deposit.asset through burn_a) and the auth proof. The default fee is the call’s floor for the declared gas plus the cell fee per created cell, with the same two price steps of headroom as call. Prints each payout and, unless --no-wait, the receipt’s eight outputs. A stale read at inclusion also exits 3, so a calling tool re-reads and retries. Publishes no input transcript. The transition file, every field optional: {"reads": [{"key", "value"}], "writes": [...], "deposit": {"rand": "<units>", "asset": N, "amount": "<units>", "kind": "none"|"deposit"|"burn"}, "pays": [{"asset": N, "amount": "<units>", "to": "rand1…"}], "mints": [...]}; keys and values are 64-hex Word8s, amounts decimal strings in the asset’s own units, to defaults to this wallet |
program state <ID> | --cell <64 hex> | v0.6.8: every cell of the program in key order (rand_getProgramCells, paged to the end), or one cell with --cell, 64 zeros when absent. Refused on a chain without a program_state section |
program vault <ID> | v0.6.8: the program’s vault (rand_getProgramVault), one row per asset it holds, amounts in units. Refused on a chain without the section | |
multisig create | --signer <pubkey hex> (repeat, in order), --threshold N, --salt <64 hex>, --fund <RAND>, --fund-token <asset> <amount>, --fee <RAND>, --no-wait, --cuda | v0.8.0, multisig accounts (a chain with a multisig section, as chain 21): create an M-of-N account of rand-node keygen keys; prints the derived id before sending. One bundle from this wallet burns the 1 RAND create_fee and any first deposit through burn_r (or burn_a for a token); at most 10 signers |
multisig deposit <ID> <AMOUNT> | --asset <index|id>, --fee, --no-wait, --cuda | v0.8.0: credit an existing account from this wallet’s burn; anyone may deposit, nothing is signed. Paying out and rotating are the signers’ rand-node multisig pay|rotate |
perp keygen / deposit / order / cancel / withdraw / oracle / prove / state / account / inputs / genesis-root / image-hc | --trading-key <FILE> on the account commands; deposit --amount <A> (collateral: zUSD on chain 21, in display units), order --market <ID> --side buy|sell --size <lots> --price <ticks> [--kind limit|market] [--tif gtc|ioc|post] [--reduce-only], cancel --target <NONCE>, withdraw --amount <UNITS>, oracle --node-key <FILE> --price <MARKET>=<UNITS>[,…] [--every <SECS>], prove --image <FILE> --job <FILE> --tier <N>; --nonce <N> (default: one past the account’s nonce_high), --no-wait | v0.8.0, RPL-3 perpetual futures (a chain with a perps section, as chain 21: one market, BTC-PERP, lot 100,000, tick 100,000,000 = one dollar). A trading key is a Dilithium2 key file in rand-node keygen’s shape and the account it owns is its address’s eight words. A deposit is the one action that rides a bundle (this wallet’s --key burns the collateral and pays the RAND fee); orders, cancels, withdrawal requests and a validator’s oracle prices are signed, bundle-less and fee-less. withdraw carries the note the chain will pay to this wallet when a state proof settles it. prove runs the pinned engine over a perp-prover job and submits the PerpStateProof (unsigned, so anyone may prove; about 22 GB a proof). state, account and inputs are rand_getPerps, rand_getPerpAccount and rand_getPerpInputs verbatim |
call <PROGRAM-ID> | --input N (repeatable), --expect-public <FILE>, --tier T, --gas-limit <N|max>, --fee, --auditor <rand1…>, --no-envelope, --print-call-key, --cuda | run the call in the emulator first and print gas: <exact> (declaring <limit>, tier <t>): the exact gas is witness and never leaves the wallet, the declared limit is the proof’s public GAS_LIMIT. On a chain with a gas section the default limit is the exact gas rounded up to a multiple of 2^(t−2); max declares the header’s ceiling gas_max, which leaks nothing the tier does not; N is refused under the exact gas or over the ceiling. Without a section, or with --cuda, the default is max. Then prove the call locally over the program’s public input, seal its input transcript, pay through a bundle, print the receipt. The default fee is the floor for the declared limit, BUNDLE_BASE + gas_price·GAS_LIMIT + byte_price·KiB at the prices rand_getLimits serves, plus two price steps of headroom where prices move, printed as fee … RAND (incl. two price steps of headroom). On a hardening_v6 chain the fee is fixed before the proof exists, so the default prices the chain’s proof cap in the proof’s place, which can only overpay; --fee pays an exact amount. --expect-public refuses before proving unless the program’s public input is that file’s words |
open-call <TXHASH> | --call-key <hex>, --as-auditor | open a call’s transcript, check it against H_IN, re-run the program; exits non-zero on a mismatch |
receipt <TX> | the receipt of a committed call | |
bridge-mint <ATTESTATION> | hex or @path, --pq <JSON or @path> (required), --to <rand1…> (default: this wallet), --fee (default: the action’s floor), --no-wait, --cuda | deposit a guardian-signed attestation as a note. --pq is the guardians’ Dilithium2 co-signature quorum, [{"index":0,"signature":"<4840 hex>"},…]. Before any proving: decode the attestation, check the quorum against the node’s pq_guardians and chain id, check the recipient and the asset id, read the listed index (rand_getAssets). The deposit note’s blinding is derived from the attestation’s digest, r = blake3("rand-deposit-r-1" || mu), the only r the chain admits. The fee bundle is proved against the whole transaction, the quorum included. Prints the note’s owner, time and r |
bridge-rotate <ROTATION> | hex or @path, --pq <JSON or @path> (required), --fee (default: the action’s floor), --no-wait, --cuda | submit a guardian-set rotation with the current PQ set’s co-signature quorum, as a BridgeAttest that deposits nothing. Refuses, before proving, anything but a rotation that steps Rand’s set by one. Prints the guardian_set_index afterwards |
bridge-pause | --sig <HEX or @path> (required), --no-wait | pause bridge minting: a bundle-less, fee-less PauseMints, so no key file and no RAND are needed. The file is rand-bridge-gov pause’s output, the pause key’s 2,420-byte signature as hex, made for the bridge’s current pause_nonce; checked against the node’s pause_key and chain id before it is sent. Burns and rotations stay open while paused |
bridge-unpause | --pq <JSON or @path> (required), --no-wait | lift a pause with a PQ guardian quorum (rand-bridge-gov pq-unpause’s file) made for the current pause_nonce. Bundle-less and fee-less; the pause key alone can never unpause |
bridge-burn <ASSET> <AMOUNT> <TO_CHAIN> <TOKEN> <TO> | --relayer-fee N, --fee (default 0.01), --no-wait, --cuda, --yes | burn a bridged asset outbound: check that the coin TOKEN on TO_CHAIN backs ASSET and has at least AMOUNT locked, and that AMOUNT and the relayer fee are whole release units; then prove one bundle that spends the asset in slots 0–1, burns exactly AMOUNT of it (burn_a, burn_asset) and pays the RAND fee from slots 2–3. --relayer-fee is a portion of AMOUNT, not an extra charge. On a chain with a bridge fee (rand_getBridgeState.fees), as chains 20 and 21 are, the wallet (since v0.6.8) first prints the fee the chain keeps (in the asset) and the amount that will be released, and asks to confirm; --yes skips the question, and a stdin that is not a terminal is refused without it. The locked and relayer-fee checks are then on the released amount |
token create | --name, --symbol, --decimals <0..=9>, --salt <hex32> (random if not given), then either --fixed-supply <N> --to <rand1…>, --authority-key-out <FILE> [--initial <N> --to <rand1…>] or (v0.6.8) --program <PROGRAM-ID>, a token whose mint authority is that program, which only its invokes mint or burn, starting at zero supply and needing the chain’s program_state section, --fee (default: the bundle base plus the registry’s registration_fee; refused when the node reports a registration fee above 10 RAND, so pass --fee to pay a larger one deliberately), --no-wait, --cuda | register a token at the registry’s next index: fixed supply (mints once, authority none) or Key-authorised (a fresh Dilithium2 key file, written 0600, refusing to overwrite). The authority key sits at <FILE>.pending until the chain accepts the registration. If another registration takes the index first, prints another token took index N first — re-run to register at N+1; on success prints the index and the id, hex and rpl1… |
token mint | --asset, --to <rand1…>, --amount <N> (the token’s display units), --authority-key <FILE>, --fee (default 0.001), --no-wait, --cuda | mint more of a Key-authorised token; refused before any proof if the token is not Key-authorised or the key is not its authority |
token set-authority | --asset, --authority-key <FILE>, then either --new-key <FILE> or --renounce, --fee (default 0.001), --no-wait, --cuda | hand a Key-authorised token to another key, or renounce minting for good |
token burn <ASSET> <AMOUNT> | --fee (default 0.001), --no-wait, --cuda | destroy AMOUNT, in the token’s display units, of an RPL token this wallet holds; its public supply drops by exactly that. A bridged token is refused before proving: it leaves through bridge-burn |
token info <INDEX|rpl1…|hex> | one token’s full rand_getTokens row: name, symbol, decimals, authority, mint_nonce, total supply, registered_at, id (hex and id_text) and, if bridged, each backing (locked, mint_cap_per_day, minted_today, mint_day) | |
token list | --from <index> (default 0), --limit <N> (default 1000) | one page of rand_getTokens: enabled, registration_fee, next_index, tokens |
token register-bridged | --name, --symbol, --salt <hex32>, --chain, --token <hex32>, --decimals (the first backing’s source decimals), --pq <JSON or @path> (required), --fee (default: the bundle base plus the node’s registration_fee, with the same 10 RAND refusal as token create), --no-wait, --cuda | register a bridged token after genesis with its first backing, authorised by a PQ guardian quorum (rand-bridge-gov pq-register’s file) made for the node’s list_nonce. List on Rand first, setToken on the endpoint second |
token list-backing | --asset <index>, --chain, --token <hex32>, --decimals, --pq <JSON or @path> (required), --fee (default: the bundle base), --no-wait, --cuda | add a backing to a bridged token, authorised by a PQ guardian quorum (rand-bridge-gov pq-list’s file) at the node’s list_nonce |
bridge | the bridge’s public state: guardians, emitters, the asset registry, the burn sequence | |
bridge-message <SEQUENCE> | one outbound burn message, verbatim | |
fee bundle / fee deploy <words> / fee call <tier> | --public-words M (deploy); --gas N, --bytes B, --keccak-log-height K, --sha256-log-height S (call) | the minimum fee from the node’s schedule. On a chain with a gas section, fee call <tier> --gas N prices the declared limit rand call prints, at the tip’s prices and with no headroom: the floor itself. Without --gas it prices the ceiling gas_max(tier, K, S), the most such a call can cost. --bytes is the call’s proof plus input-envelope bytes |
tx <HASH> | committed transaction with its height and index | |
tx-key <HASH> | one row per output of that transaction this wallet sent, received or kept as change: output (bundle:0 … bundle:3, the one bundle’s four slots, or mint:0), role, amount (in RAND, or N (asset I) for a token), the memo it carried (- for none) and the per-transaction key it was sealed under. Hand a sent row’s key to a payee or auditor and rand_checkTransaction <HASH> <KEY> discloses that one output, memo included. A dummy slot opens to nobody and has no row | |
block <ID> | block by height or hash | |
head | { height, hash, view } | |
status | rand_status | |
peers | connected peers | |
validators | the validator register |
--cuda requires a build with --features cuda and has no fallback; no PTX has been built yet. It cannot seal a call-input envelope: rand call --cuda needs --no-envelope. It declares the gas ceiling only, and refuses --gas-limit N.
Every memo and contact name the CLI prints (send’s confirmation, notes, history, tx-key) goes through one display rule first: control characters, Unicode format characters (bidi controls, zero-width characters, the soft hyphen) and U+2028/U+2029 are shown as U+FFFD, and a run of space separators as one space.
multisig-private: a T-of-N account that looks like any wallet
v0.8.0, on a chain whose rand_status names hc_auth_multisig, as chain 21 does. A private multisig account is a T-of-N policy of Dilithium2 signer keys whose address and spends look like any wallet’s: the account has a viewing key and no spend key; a spend is a proposal that T signers check and sign offline, and the signatures are hidden inside the auth proof, so the chain learns only that the bundle was authorised. The policy file holds the account’s secret msk (its viewing power over every note) and a proposal holds the witness: both are written mode 0600, never over an existing file, and never printed. Signers need the policy file (handed to them out of band) and a rand-node keygen key of their own.
| command | flags | does |
|---|---|---|
multisig-private new-policy | --signer <pubkey hex> (repeat, in order: the order is part of the address), --threshold <T>, --out (policy.json), --public-account <ID> (repeat) | make a policy with a fresh msk; prints the address and its fingerprint. Refused where the prover could never spend it: T above 3, more than 16 signers, a repeated key. Each --public-account is read from the node and a warning printed for every signer that also signs it, since anyone who knows both policies can link the two |
multisig-private address | --policy | the address, its fingerprint (every signer compares it out of band), the owner fingerprint and the policy hash |
multisig-private balance | --policy, --store | scan the chain for the account’s notes; prints the balance |
multisig-private prepare | --policy, --to <rand1…> --amount <RAND> or --sweep-to <rand1…>, --memo, --fee <RAND>, --out (proposal.json) | write the proposal every signer checks: a payment, or every RAND note of the account (at most two) moved to a new address less the fee, which is how a policy is rotated. Prints the summary and the height before which it must be proved |
multisig-private check / sign | --proposal, --policy, --key (sign), --expect-to <rand1…[:AMOUNT]> (repeat) | the checks a signer makes, offline; sign prints <index>:<hex>. Name the payee: with --expect-to every output that moves value and is not the account’s change must pay one of the named addresses (and each must be paid; ADDR:AMOUNT pins the total), or nothing is signed |
multisig-private prove | --proposal, --policy, --signature <index:hex> (repeat, at least T), --out (proposal.tx), --cuda | verify the shares and prove. The auth proof is proved on this machine at tier 18 — minutes, and a peak footprint of about 76 GB measured at the Test profile, so a machine of 96 GB or more; the bundle proof here or on the paired prover (--prover). Refused at or past 16 blocks before the expiry |
multisig-private submit | --tx, --no-wait | send the proved transaction and wait for the commit |
A proposal expires proof_window_blocks after it is prepared (1,024 blocks on chain 21): the shares must be gathered and the transaction proved before then, or prepare again. No chain-21 transaction has carried a private-multisig spend yet.
A first shielded transfer
rand keygen # wallet.key.json
rand address # rand1… on stdout, its fingerprint XXXX-XXXX-XXXX-XXXX on stderr
rand faucet # testnet, allowlisted wallets only: 100 RAND into a note only you can open
rand balance # balance: 100 RAND
rand send rand1q9f… 1.5 # asks to confirm, then a minute and a half to two minutes of local proving, then the commit
rand notes # the spent note, and the change note
Sharing an address, and a memo
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" # the memo is sealed into the payment output
rand history --memo # alice's row shows the whole memo
rand tx-key <hash> # hand the sent row's key to alice or an auditor
The memo sits inside the same sealed body as the note, so whoever can open one can open the other: the payee through notes, and anyone holding that output’s per-transaction key through rand_checkTransaction <hash> <key>. Chain 18 is the first chain whose genesis sets envelope_bytes; on chains 14 to 17 --memo was refused before anything proved.
A confidential call you can open again later
rand program build --guest balance_check --arg 1000 --out bc.json
rand program deploy bc.json # prints the program id
rand call <id> --input 100 --input 200 --input 300 --input 400
rand open-call <txhash> # inputs: [100, 200, 300, 400] — verdict: faithful (exit 0)
A stateful program: the RPL-2 counter
On a chain with a program_state section, as chains 20 and 21 are. The counter accepts exactly the transitions that read one cell and write it back with its first word plus one; everything else in the transition (what the bundle deposits, what the vault pays, what the program mints) rides along unchecked, which makes it a demonstration and not a program to put value behind.
rand program build --guest rpl2_counter --out counter.json
rand program deploy counter.json # prints the program id
rand token create --name "Counter Share" --symbol CTR --decimals 0 --program <id> # say index 2
rand program state <id> --cell 0100000000000000000000000000000000000000000000000000000000000000 # 64 zeros: absent
cat > step.json <<'JSON'
{ "reads": [{ "key": "0100…00", "value": "0000…00" }],
"writes": [{ "key": "0100…00", "value": "0100…00" }],
"deposit": { "rand": "500000000", "kind": "none" },
"pays": [{ "asset": 0, "amount": "200000000", "to": "rand1…" }],
"mints": [{ "asset": 2, "amount": "1" }] }
JSON
rand program invoke <id> --transition step.json # exit 3 if the cell moved since; re-read and retry
rand program vault <id> # asset 0: 300000000 units (0.5 RAND in, 0.2 out)
The … in the file stand for the full 64 hex characters, which the wallet requires.
What a deploy and a call print
On a split-authorisation chain every bundle is two proofs made on this machine: the tier-10 spend authorisation and the tier-14 bundle. A program deploy with rand v0.6.7 on chain 18, 2026-09-30:
proving the spend authorisation on this machine (tier 10; seconds)…
authorisation proved in 6.8s: tier 10, 1368908 bytes
proving the bundle (tier 14; about a minute and a half on a laptop)…
proved in 116.7s: tier 14, 1500710 bytes
submitted deploy <hash>
0 RAND out, 59.8545 RAND change, fee 0.1007 RAND, anchored at height 97831, proved in 116.7s (bundle) + 6.8s (auth)
A call prints its gas and its fee before the call proof, then proves the paying bundle the same way:
proving the call locally (90 inputs stay private, over the program's 11-word public input)…
gas: 5636 (declaring 6144, tier 12)
fee 0.006191437 RAND (incl. two price steps of headroom)
proved in 29.2s: tier 12, 3346865 bytes, outputs [...]
Here 6,144 is 5,636 rounded up to a multiple of 2^10, and rand fee call 12 --gas 6144 --bytes <B> prices that limit at the tip’s prices with no headroom.
rand-prover
The delegated prover as a binary of its own: it makes a wallet’s tier-14 bundle proof on another machine. rand-node run --prover hosts the same service inside a node. Every subcommand takes --home <DIR> (env RAND_PROVER_HOME, default ~/.rand-prover), the directory that holds prover.key.json and pairings.json.
| command | arguments | behaviour |
|---|---|---|
keygen | write prover.key.json, mode 0600, and print the key’s fingerprint; refuses to overwrite | |
pair | --name <LABEL> (required), --url <URL> (default http://127.0.0.1:8600), --own, --qr | mint a token, store only its hash under the label and print the randprover: pairing link that rand prover pair takes. The token is shown once, inside the link. --url is where the wallet reaches this prover; --own adds own=1; --qr also prints the link as a terminal QR code |
unpair | --name <LABEL> | remove a pairing; a running prover reads the file at start, so it takes effect on restart |
pairings | one line per pairing: label, own or -, creation date | |
run | --listen <ADDR> (default 127.0.0.1:8600), --max-parallel <N> (1), --max-queue <N> (8), --per-token <N> (2), --cuda, --skip-memory-check, --allow-origin <ORIGIN> (repeatable), --fee <RAND> with --fee-address <rand1…>, --accept-spend-key | serve the prover_* JSON-RPC until Ctrl-C or SIGTERM. The start is refused unless 5.74 GB × --max-parallel + 1 GiB of memory is available. --per-token is the number of jobs one pairing may have queued or proving at once. --accept-spend-key is retired in v0.6.8: run refuses to start with it, and a prover accepts only viewing-key witnesses. On v0.6.7 it admitted spend-key witnesses, which only a chain on bundle guest v1 or v2 sends |
A prover on chain 21, as on chain 20, receives the wallet’s viewing key nk and a fresh salt. It cannot spend, and it can read everything that wallet ever received or sent: pair with a prover you would show your history to. The wallet checks every proof it gets back.
Key file formats
Node key:
{ "seed": "hex of 32 bytes", "address": "base58 of blake3(\"rand-address\" || public key)", "public_key": "hex of 1312-byte Dilithium2 public key" }
Wallet key, version 2:
{ "version": 2, "spend_key": "hex of 8 little-endian u32 words (64 characters)" }
rand keygen writes it with mode 0600, and since v0.6.8 every rand command refuses a wallet key file that group or other can read: a key copied or restored by hand must be set back to 0600 (chmod 600 <key>). Losing a wallet key loses every note it could open; there is no recovery phrase in this release.
What chain 21 and v0.8.0 add
Chain 21 went live on 2026-10-12 on fullnode v0.8.0 (c0878c3f). Against the v0.6.8 command set, as this page gave it for chain 20:
rand-node:multisig id|status|pay|rotateandprice status|sign|submit;run --verify-chain replay,--apply-proposals-inlineand--msauth-vk;verify --mode;aggregate --watch-max-failures;genesis --fees,--perps,--multisig,--auth-multisig-guestand--aggregation-tree(the last refused on every chain so far). Every chain-21 node needsmsauth-vk-production.binbesiderand-node.rand:multisig create|deposit,perp …,multisig-private …,--threadsand--cpu(v0.7.1), and a scan that pages envelope heads (rand_getScanLeaves) and falls back to full pages on an older node. A v0.8.0 wallet sends on chain 21 only; a wallet older than v0.8.0 sends on no chain that still runs.rand-prover: the same--threads/--cpumodule (cores − 1 by default, capped at 8), a proving timeout (v0.7.0, audit v7), and the pool unit confined to loopback with a syscall filter.
The fullnode’s own docs/cli.md at c0878c3f is what this page is read against.