Rand Protocol
Documentation
Guides

Exécuter un nœud

Compiler les binaires, créer une clé, initialiser un répertoire de données à partir du genesis de la chain 20, joindre les nœuds bootstrap, lier le RPC, élaguer l'historique et vérifier la chaîne - en tant qu'observateur ou que validator.

Utilisez cette page pour exécuter votre propre rand-node sur le testnet, ce qui est aussi la méthode recommandée pour utiliser un wallet en toute confidentialité : un nœud que vous exploitez n’apprend de personne d’autre que vous quelle feuille vous êtes sur le point de dépenser. Un observateur se synchronise, revérifie chaque preuve et sert le JSON-RPC ; un validateur fait de même et participe au consensus dès que sa clé figure dans l’ensemble d’une époque.

Compilation

Prérequis : Rust 1.98.1 (épinglé dans rust-toolchain.toml ; rustup l’installe), un compilateur C++ et cmake pour RocksDB. Le crate zkVM a des dépendances par chemin vers un checkout voisin circuits/ du dépôt du système de preuve (randprotocol/zkp-circuits), qui doit se trouver à côté de fullnode/, au commit dont la version a été vendorisée : la ligne circuits: de crates/randprotocol-zkvm/guests-compiled/PROVENANCE.md (aeacf31 pour v0.6.8, comme pour v0.6.7). La première compilation prend 10 à 20 minutes.

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

Exécutez v0.6.8 (git checkout v0.6.8, commit c9c9bd3c, est la version qu’exécute la flotte ; rand_getVersion indique le sha d’un nœud). Sa release GitHub contient rand-node, rand, rand-prover et SHA256SUMS, compilés sur un hôte de la flotte et non signés. La chaîne 20 l’exige : son genesis contient des champs qu’aucune build antérieure ne lit (testnet, binding_domain, proof_window_blocks, program_state, les groupes rotation et fees du bridge, les plafonds de gas et l’admission au staking par vote), et v0.6.8 apporte des changements de consensus qu’un nœud plus ancien n’exécute pas. Les preuves sont toujours produites sous le jeu de contraintes 8, comme sur les chaînes 18 et 19, et chaque bundle porte toujours une preuve d’autorisation à côté de sa preuve de bundle (autorisation scindée, depuis la chaîne 17) ; le genesis épingle donc les deux mêmes guests : le guest de bundle v3 (hc_bundle 60af094a…) et le guest d’autorisation (hc_auth 1e4e347f…). Le genesis contient aussi une section gas, hardening_v6 et envelope_bytes. Une build v0.6.8 refuse le genesis des chaînes 14, 15, 16 et 17 à run et à verify, par hash ; elle peut encore exécuter celui de la chaîne 19. Chaque nœud exécute la build de la flotte.

Chaîne 20 : un hard fork à arrêt total

La chaîne 20 est entrée en service le 2026-10-01 à 06:17 UTC. Elle a été découpée à partir d’un snapshot de la chaîne 19 et déployée en arrêt total, redémarrage total : chaque nœud de la chaîne 19 s’est arrêté à la hauteur 7 700, et chaque nœud a démarré sur la chaîne 20 avec v0.6.8. Ce n’était pas une mise à jour progressive, pour deux raisons. La chaîne 20 est un nouveau genesis : aucun bloc n’est partagé avec la chaîne 19. Et v0.6.8 apporte des changements de consensus issus de l’audit v6 qui ne doivent jamais être déployés nœud par nœud : CON-4 supprime la libération automatique du verrou d’un validateur, et CH-1 ajoute un pacemaker à certificats de timeout. Les 26 validateurs, leurs clés, leurs identifiants de pair et leurs bootstraps ont été reportés ; un quorum reste de 18 sur 26. Les chaînes 19 et 18 sont retirées.

Faire passer un nœud de la chaîne 19 à la chaîne 20 demande donc le nouveau binaire, le nouveau fichier genesis et un répertoire de données vierge, comme ci-dessous. Les wallets ont besoin de v0.6.8 pour envoyer sur la chaîne 20 : son genesis lie chaque preuve et chaque message signé à son hash (binding_domain: 1), ce que ne fait pas un wallet plus ancien.

Clés

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

Le fichier est { "seed", "address", "public_key" }, en mode 0600 ; seule la seed est secrète. Un observateur a lui aussi besoin d’une clé : c’est l’identité p2p. Une clé de nœud n’est pas une clé de wallet : utilisez rand keygen pour un wallet.

Générez votre propre clé et conservez-la hors du checkout. Le dépôt fullnode ne suit plus aucune clé (audit v3, OPS-1) : les fichiers deploy/node-*.key.json et deploy/payout/*.key.json qui avaient été commités pour les chaînes 8 à 13 sont des seeds publiées et ne doivent pas être utilisés. Les clés de validateur de la chaîne 20 ont été générées hors dépôt et n’y ont jamais figuré : ses 26 validateurs du genesis sont les dix-huit clés générées pour la chaîne 14 et les huit bondées sur la chaîne 15, reportées à chaque découpage depuis.

Depuis v0.6.8, rand-node keygen refuse un fichier --out qui existe déjà ; v0.6.7 l’écrasait sans rien demander. Le fichier est l’unique copie de la seed. Sauvegardez-le avant qu’un bond en dépende.

Initialisation à partir du genesis

Chaque nœud d’une chaîne a besoin du fichier genesis identique à l’octet près ; rand-node init le copie dans le répertoire de données et affiche son hash, qui doit valoir 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135 pour la chaîne 20. Le fichier est deploy/genesis-chain20.json (sha256 0020247a…3a21). Il a été commité sur la branche main du fullnode avec le compte rendu de lancement (commit 74cfaca0), après le tag : un checkout de v0.6.8 ne le contient donc pas, il faut le récupérer depuis main.

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

Cela crée <datadir>/db (RocksDB) avec le bloc 0, les neuf notes du genesis comme premières feuilles de l’arbre (sept notes RAND portant 5 184,243719258 RAND de wallets d’opérateurs issus de la chaîne 19, et deux notes zUSD de 10 et 89 zUSD, les 99 zUSD verrouillés dans la garde du bridge), et le registre des validateurs. Relancer la commande avec le même genesis est sans effet ; un genesis différent est refusé (already initialized with a different genesis signifie que le répertoire de données appartient à une autre chaîne). Nommez le répertoire de données d’après les huit premiers caractères du hash du genesis, comme le fait la flotte (data-<name>-6210cf07), afin qu’il n’entre jamais en collision avec celui de la chaîne 19 (-a3defc93).

La forme du fichier genesis :

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

Chaque champ entre dans le hash du genesis. hc_bundle épingle le guest de bundle contre lequel chaque preuve de bundle est vérifiée, et hc_auth le guest d’autorisation contre lequel chaque preuve d’autorisation est vérifiée ; un nœud dont la build assemble un guest différent refuse de démarrer et indique les deux digests. Les cinq champs max_* sont facultatifs et ne sont intégrés au hash que s’ils sont présents ; la chaîne 20 les définit tous les cinq. max_proof_bytes vaut 4 MiB, là où les chaînes 13 à 16 avaient 8 MiB : depuis la chaîne 17, une transaction peut porter trois preuves (bundle, autorisation, appel), et un genesis avec hc_auth doit avoir un max_block_bytes d’au moins trois fois max_proof_bytes plus 1 MiB. Chaque note d’alloc porte son opening (pk, time, r, et asset pour une note de token) : le propriétaire et le montant d’une note du genesis sont donc publics par conception. La chaîne 20 comporte :

  • une section bridge : l’ensemble de gardiens 1 à l’indice 1 avec huit gardiens, des émetteurs pour les chaînes sources 2, 3, 4 et 5 (ceux de la chaîne 19), huit pq_guardians Dilithium2, une pause_key, burn_sequence 13, un min_inbound_sequence de 3 pour les chaînes sources 2, 3 et 4 et de 7 pour la chaîne source 5, et rules_v2 (un plafond global de mint de 4 000 zUSD par fenêtre de 24 h). Nouveau sur la chaîne 20 : rotation, selon laquelle la rotation d’un PQ-guardian ou de la pause-key exige une preuve de possession par les nouvelles clés et prend effet 86 400 s après son commit ; et fees, selon laquelle la chaîne retient 10 bps de chaque dépôt et 10 bps de chaque burn sous la forme d’une note zUSD versée à une adresse de trésorerie, de sorte qu’un dépôt crédite le brut moins les frais et qu’un burn libère son montant moins les frais ;
  • une section tokens : des frais d’enregistrement de 1 RAND, brûlés plutôt que versés au proposeur ; un mint_cap_per_day de 100 000 tokens par backing à huit décimales ; un plafond de registre de 4 096 tokens ; la borne de valeur de note de 2^63 ; et zUSD listé au genesis avec ses sept backings, 99 zUSD verrouillés (Ethereum USDT 22, BSC USDT 22, Tron USDT 30, Solana USDT 25) face aux deux notes zUSD du genesis ;
  • consensus_domain: 1 : chaque signature de consensus est liée à ce genesis (Consensus) ;
  • une section staking : le faucet limité par liste d’autorisation à 16 wallets et à 18 clés de validateur autorisées à minter, avec un budget de 10 000 RAND par époque, un délai de bond de deux époques, un plafond de poids de 3 333 bps et des enregistrements v2, comme sur la chaîne 19 ; nouveau sur la chaîne 20, admission_by_vote (une nouvelle clé de validateur ne s’enregistre qu’une fois que l’ensemble de validateurs l’a votée) et un budget d’entrée de 2 500 bps du poids actif à chaque frontière d’époque, là où la chaîne 19 avait un montant fixe de 10 000 RAND (Staking). Il n’y a pas de slashing ;
  • une section gas : un appel paie le gas limit que déclare sa preuve et ses octets, à des prix qui partent de 100 unités par gas et 800 unités par KiB et évoluent après chaque bloc selon son remplissage par rapport à une cible de 10 485 760 octets et 262 144 gas, d’au plus 1 250 bps par bloc, jamais en dessous des prix de départ et, nouveau sur la chaîne 20, jamais au-dessus de 10 000 et 80 000. Seuls les octets qui paient un prix à l’octet, la preuve d’un appel et son enveloppe d’entrée, comptent dans la charge du prix à l’octet (byte_load: "paying"). Chaque preuve de bundle déclare la même limite, 20 479 (Frais) ;
  • envelope_bytes: 1860 : chaque enveloppe de note fait exactement 1 860 octets et comporte un champ de mémo chiffré ;
  • hardening_v6: true, l’interrupteur de durcissement v0.6, activé par chaque chaîne depuis la chaîne 16 ;
  • testnet: true, le marqueur qui permet à un faucet de coexister avec une section bridge ;
  • binding_domain: 1 : les preuves de chaque transaction et chaque message d’action signé incluent le hash du genesis, de sorte que rien de prouvé ou de signé pour une chaîne ne se vérifie sur une autre ;
  • proof_window_blocks: 1024 : l’ancre d’un bundle peut être l’une des 1 024 dernières racines de fin de bloc et son time peut avoir jusqu’à 1 024 blocs de retard sur la hauteur (256 sur la chaîne 19), ce qui laisse à un prouveur lent ou délégué une vingtaine de minutes ;
  • program_state, RPL-2 : cellules de programme, coffres de programme et l’action Invoke, avec des frais de cellule de 0,1 RAND par cellule qu’un invoke crée (JSON-RPC donne les limites).

Il n’a pas de section aggregation, et la build refuse de démarrer sur un genesis qui en contient une.

timestamp_ms est l’instant zéro de l’horloge de la chaîne. Lorsque vous produisez un genesis qui vous est propre, fixez-le au plus près de l’heure réelle de lancement, et jamais en avance sur elle. Trop loin dans le passé, l’heure des blocs prend du retard sur l’horloge murale jusqu’à ce que la borne de 60 s par bloc lui permette de rattraper. À plus de 15 secondes dans le futur, le bloc 1 n’est pas du tout validé : chaque vote, y compris celui du leader, est retenu tant que l’heure murale n’a pas atteint l’horodatage du genesis. Vérifiez-le sur l’horloge de la machine qui lance la flotte, et non sur celle qui a produit le fichier.

Exécution

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
argumentdéfautsignification
--datadirobligatoirele répertoire de données initialisé
--keyobligatoirefichier de clé : identité de validateur, identité p2p, destinataire des frais
--listen/ip4/0.0.0.0/tcp/30303multiadresse d’écoute libp2p, répétable
--bootstrapaucunpair à contacter au démarrage, puis toutes les 30 s tant que la connexion est interrompue, répétable
--rpc127.0.0.1:8545l’écouteur JSON-RPC et WebSocket de l’opérateur : toutes les méthodes, et il fait confiance au loopback. Gardez-le sur le loopback, et ne redirigez jamais un point d’accès public vers lui
--public-rpc <ADDR>désactivév0.6.8 : un second écouteur, public, vers lequel pointe un reverse proxy ou une redirection SSH : un ensemble fixe de méthodes sans les méthodes de clé de visualisation, sans rand_mint ni rand_getPeers, sans lots, sans WebSocket, un seul compteur pour tous les appelants (JSON-RPC)
--rpc-viewing-token-file <PATH>désactivév0.6.8 : exige un jeton porteur (la première ligne du fichier, 32 caractères ou plus) sur les méthodes de clé de visualisation de --rpc
--reserved-peer <PEER_ID>aucunv0.6.8, répétable : un pair admis au-delà du plafond de connexions entrantes ; voir Pairs réservés
--strict-gossipdésactivév0.6.8 : n’accepte que le gossip portant une signature d’auteur, un numéro de séquence et une source valides ; il se déploie nœud par nœud
--validatordésactivéce nœud détient une clé de validateur et participe au consensus lorsque sa clé figure dans l’ensemble courant
--no-mdnsdésactivédésactive la découverte sur le réseau local ; recommandé sur les serveurs
--block-interval-ms1000espacement minimal entre les propositions
--view-timeout-ms3000délai de base d’une vue, doublé à chaque expiration consécutive jusqu’à 8×
--verify-chainquickcontrôle d’intégrité au démarrage : off, quick (structure et rejeu du registre), full (également chaque signature et chaque QC)
--keep-raw-proofsdésactivémode archive pour l’agrégation de blocs ; sans effet sur la chaîne 20
--prune-historydésactivé (tout conserver)conserve le registre et seulement cette quantité d’historique de blocs : <n>m, <n>h ou <n>d, au moins 1h. Les validateurs du testnet utilisent 24h, sauf les deux archives ; voir Élagage de l’historique
--min-free-disk-mb1024refuse de démarrer si l’espace libre du système de fichiers du répertoire de données est inférieur à cette valeur ; rand_getHealth répond disk_low sous quatre fois cette valeur. 0 désactive la protection
--prover <ADDR>désactivéhéberge un prouveur délégué à cette adresse, un écouteur distinct et jamais le RPC ; voir ci-dessous
--gas-price, --byte-price100, 800le prix plancher du pool pour un appel sur une chaîne sans section gas. Le genesis de la chaîne 20 fixe ses propres prix, de sorte que le nœud ignore ces deux options et consigne un avertissement

--prover-accept-spend-key est retiré : un nœud v0.6.8 auquel on le passe refuse de démarrer ; supprimez-le donc de tout fichier d’unité repris de la v0.6.7.

Les trois bootstraps ci-dessus sont les validateurs C, D et E, inchangés depuis la chaîne 19. La chaîne 20 fonctionne avec les clés de validateur générées pour la chaîne 14, et leurs identifiants de pair sont donc ceux qu’utilisait la chaîne 14 ; une identité p2p est dérivée de la clé de validateur. La plupart des validateurs ne conservent qu’un jour de blocs : un nœud qui se synchronise depuis le genesis, ou qui a plus d’un jour de retard, a donc aussi besoin d’un pair à historique complet. Ajoutez l’une des deux archives :

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

Omettez --validator pour exécuter un observateur. Démarrage : ouverture du stockage, vérification des guests de cette build par rapport à hc_bundle et hc_auth du genesis, exécution du contrôle d’intégrité (troncature d’une queue endommagée en conservant l’état de sûreté des votes), reprise du consensus à partir de la tête persistée, démarrage du réseau et du RPC, puis synchronisation depuis tout pair en avance. RUST_LOG vaut par défaut info,libp2p=warn,libp2p_mdns=off ; Ctrl-C ou SIGTERM, le signal d’arrêt de systemd, provoque un arrêt propre. Un redémarrage depuis le même --datadir reprend à partir de la tête persistée.

Règles de topologie issues du fullnode : les nœuds situés derrière un NAT contactent les nœuds disposant d’adresses publiques ; ouvrez le port TCP 30303 en entrée sur les nœuds publics ; liez le RPC à 127.0.0.1 sauf s’il est protégé par un pare-feu — il accepte les transactions de quiconque peut l’atteindre et, dès lors qu’une clé de visualisation y a été importée, considérez le port comme porteur de clé. Maintenez --block-interval-ms à 1000 ou plus lent : la preuve d’un bundle prend environ deux minutes et son ancre est valide pendant 1 024 blocs sur la chaîne 20 (256 sur la chaîne 19).

Un validateur a besoin d’une horloge synchronisée. Depuis la chaîne 14, l’heure murale fait partie du consensus (durcissement du bridge B2). Un bloc dont l’horodatage dépasse de plus de 60 s celui de son parent est invalide, et un validateur retient son vote pour un bloc dont l’horodatage a plus de 15 s d’avance sur sa propre horloge. Un validateur dont l’horloge retarde cesse donc de voter sans consigner d’erreur, alors que dix-huit validateurs sur vingt-six doivent voter pour valider un bloc sur la chaîne 20. Exécutez NTP sur chaque validateur et vérifiez-le avant de démarrer le nœud :

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

Pairs réservés

Depuis la v0.6.8, un nœud accepte au plus 256 connexions entrantes établies et 64 semi-ouvertes, et une identité libp2p ne coûte rien à créer : des inconnus pourraient donc occuper tous les emplacements. Trois types de pairs sont réservés, admis au-delà du plafond et servis sur la part du budget de synchronisation réservée aux validateurs : chaque pair --bootstrap, chaque --reserved-peer et chaque identité de validateur apprise à partir d’une liaison de pair signée. Chaque nœud --validator signe son identité libp2p avec sa clé de validateur et la diffuse par gossip ; un nœud qui connaît la clé grâce à un ensemble de validateurs réserve ce pair et conserve l’enregistrement d’un redémarrage à l’autre. Le stockage d’une chaîne fraîchement produite ne contient aucune liaison : tant que les premières annonces ne sont pas arrivées, seuls les deux premiers types sont donc réservés. Lors d’une création de chaîne, transmettez les identifiants de pair des validateurs de la flotte avec --reserved-peer ou gardez-les dans la liste de bootstrap (rand-node address --key <file> affiche l’identifiant de pair d’une clé). rand_status.reserved_peers les compte ; sur un nœud de la flotte, il doit atteindre le nombre de validateurs dans la minute qui suit le démarrage.

Vérification

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

rand_status indique height, view, syncing, sync_target, peer_count, connected_peers, reserved_peers, mempool_size, is_validator, active_validator, faucet, confidential, testnet, fri_profile, programs, notes, nullifiers, tree_root, hc_bundle, hc_auth, binding_domain, gas_prices, l’address et le peer_id du nœud, ainsi que les diagnostics de synchronisation (sync_inflight_age_ms, sync_failures, sync_late_batches), le disque (disk_free_bytes, disk_low) et le seuil de rétention (prune_floor, 0 sur un nœud qui conserve tout ; prune_history_secs, null lorsqu’il n’est pas défini). Sur la chaîne 20, attendez-vous à hc_bundle 60af094a…, hc_auth 1e4e347f…, faucet true, confidential true, testnet true, binding_domain 1, fri_profile "production" et à des gas_prices compris entre 100 et 10 000 par gas et entre 800 et 80 000 par KiB. rand_getGenesisHash doit répondre 6210cf07…5135 et rand_getVersion 0.6.8.

rand_getHealth répond disk_low (avec free_bytes) avant ok, syncing ou behind lorsque l’espace libre est inférieur à quatre fois --min-free-disk-mb.

Élagage de l’historique

Un nœud démarré avec --prune-history 24h conserve le registre (notes, nullifiers, ancres, registre des validateurs, programmes) dans son intégralité et supprime les blocs plus anciens, ainsi que leurs certificats, leurs transactions et leurs reçus. Une passe s’exécute à chaque 16e bloc validé et supprime jusqu’à 512 blocs antérieurs à l’horodatage de la tête moins la fenêtre ; le genesis, la tête et son parent ne sont jamais supprimés, et le seuil ne fait que monter. L’espace disque est restitué après la compaction de RocksDB. Sur la chaîne 20, comme sur la chaîne 19, 24 des 26 validateurs utilisent 24h et conservent environ un jour de blocs. Lorsque l’élagage a été activé pour la chaîne 14 (2026-09-25), les tables de blocs de chaque validateur sont passées de 19 Go à 3,3–4,4 Go une fois l’arriéré résorbé ; les deux autres, obs1 et rand-archive-2, validateurs eux aussi depuis le 2026-09-27, fonctionnent sans l’option et conservent tout. Un nœud mainnet ne passe jamais cette option.

Sur un nœud élagué, une recherche adressée par hauteur sous le seuil (rand_getBlockByHeight, rand_getFinality par hauteur, une plage de rand_getBlocks ou rand_getCompactBlocks qui l’atteint) répond par l’erreur -32010 avec data.floor : adressez-vous à une archive. Une recherche adressée par hash répond null pour un bloc que le nœud ne détient plus, comme pour un bloc qui n’a jamais existé. L’analyse des portefeuilles n’est pas affectée, puisque les notes et les nullifiers ne sont jamais élagués.

La vérification au démarrage d’un nœud élagué est structurelle (le bloc 0 et les blocs conservés, leurs QC, leurs liens de parenté et leurs leaders), et non un rejeu depuis le genesis ; elle ne se répare pas d’elle-même : en cas d’échec, resynchronisez-vous depuis une archive. N’ouvrez pas un répertoire de données élagué avec une build antérieure à la v0.5.7, sauf si elle s’exécute avec --verify-chain off : son contrôle par défaut rejoue depuis le genesis, constate l’absence du bloc 1 et tronque le registre au genesis.

Hébergement d’un prouveur

Sur la chaîne 20, un portefeuille produit deux preuves pour chaque bundle, et la plus volumineuse nécessite environ 6 Go de mémoire. Un portefeuille qui ne peut pas la contenir peut confier cette preuve à un prouveur délégué auquel il est apparié : le binaire distinct rand-prover, ou le même service hébergé par un nœud.

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

pair affiche un lien randprover:. Le portefeuille le prend avec rand prover pair <link> et prouve ensuite par son intermédiaire grâce à l’option globale --prover. Le port d’écoute du prouveur lui est propre, ce n’est jamais une méthode du RPC : une adresse égale à --rpc est refusée. Avec --prover, le nœud refuse de démarrer à moins que <prover-home>/prover.key.json n’existe (par défaut <datadir>/prover) et que 5,74 GB par slot de preuve plus 1 GiB de mémoire ne soient disponibles ; --prover-max-parallel fixe le nombre de slots, un par défaut.

Sur la chain 20, le prouveur reçoit la clé de visualisation du portefeuille et un sel neuf, non la clé de dépense. Le portefeuille produit lui-même la preuve d’autorisation sur la clé de dépense : un prouveur apparié peut donc lire tout l’historique de ce portefeuille, sans pouvoir dépenser. Gardez le port d’écoute sur la boucle locale, sauf si un proxy terminant le TLS le précède, et ne l’hébergez pas sur une machine qui sert un RPC public. Un arrêt attend la preuve en cours : donnez donc à une unité systemd TimeoutStopSec=180.

Vérifier et réparer

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

Arrêtez d’abord le nœud. Code de sortie 0 si la base est cohérente, 2 si un problème a été trouvé et que --repair n’a pas été fourni. Le rejeu revérifie chaque preuve de bundle : un build dont l’ensemble de contraintes diffère de celui qui a produit la chaîne s’arrête dès le premier bundle.

Devenir validateur

Un validateur rejoint une chaîne en cours d’exécution par un bonding, et non par un nouveau genesis : démarrez le nœud avec --validator, imprimez un enregistrement avec rand-node register --key node.key.json --payout <rand1…> --v2 (la chain 20 refuse un enregistrement v1), faites bonder par un portefeuille au moins 1 000 RAND avec rand bond <address> 1000 --registration <hex>, et le nœud propose une fois le bond activé : sur la chain 20, trois frontières d’époque de 1 000 blocs après le bond au plus tôt. Sur la chain 20, le bond vient en second. Son genesis définit staking.admission_by_vote : un bond qui enregistre une nouvelle clé est donc refusé tant que des validateurs détenant plus de deux tiers du poids de vote n’ont pas voté pour admettre la clé. Chacun signe avec rand-node admit sign --candidate <key or registration> --key <validator key> --genesis-hash 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135, et l’un d’eux soumet les votes avec rand-node admit submit (Ligne de commande). rand_getAdmitted liste les clés admises par vote et pas encore enregistrées. Pas plus de 2 500 bps du poids actif ne devient nouveau poids à une même frontière d’époque. Staking le détaille pas à pas. Pour une chaîne de test privée, rand-node genesis en produit une :

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

et scripts/local-testnet.sh fait tourner deux validateurs sur une même machine, avec le RPC sur 8545 et 8546. Sans autres options, le genesis n’a ni hc_auth, ni section gas, ni memo. La chain 20 a été produite avec --hardening-v6 --bundle-guest v3 --auth-guest --gas-price 100 --byte-price 800 --bundle-gas-limit 20479 --gas-dynamic 10485760,262144,1250 --max-gas-price 10000 --max-byte-price 80000 --gas-byte-load paying --envelope-bytes 1860 --testnet --binding-domain 1 --proof-window-blocks 1024 --program-state-cell-fee 10000000 en supplément (les chains 18 et 19 avaient les options jusqu’à --envelope-bytes), et ses sections bridge et staking ainsi que consensus_domain ont été insérées par le script de production. Une nouvelle chaîne qui vous est propre devrait passer --binding-domain 1 : un portefeuille v0.6.8 produit la forme liée au genesis sur tout identifiant de chaîne hors de 14 à 19.

Déploiement sur un serveur

Le répertoire deploy/ du dépôt contient les scripts qu’utilise la flotte : deploy/push-to-vps.sh <ip> <letter> "<bootstraps>" [validator|observer] provisionne un serveur Linux (rsync des sources, build, installation de /usr/local/bin/{rand-node,rand}, initialisation d’un datadir indexé sur le hash du genesis, installation d’un service systemd rand-node), et deploy/rebuild-vps.sh <ip> reconstruit sur un nouveau commit et redémarre en conservant les données. Deux précautions. Le script de provisionnement initialise à partir de deploy/genesis.json, qui n’est pas le fichier de la chain 20 : un nœud pour le testnet se met donc en place à la main à partir de deploy/genesis-chain20.json (sur main, voir ci-dessus). Et les deux scripts font un rsync de /root/fullnode avec --delete : une clé conservée dans cette arborescence est supprimée au prochain rebuild. Gardez-la dans /root/keys, comme le fait la flotte et comme le fait désormais par défaut le script de provisionnement v0.6.8 (celui de la v0.6.7 écrivait la clé sous /root/fullnode/deploy/). La gestion du service passe par systemctl status rand-node et journalctl -u rand-node -f. Le serveur a besoin de build-essential clang cmake pkg-config libssl-dev et d’une chaîne d’outils Rust. Un serveur de 1 vCPU et 2 GB ne peut pas compiler les binaires : compilez ailleurs et copiez-les. Exécuter un validateur et un agrégateur donne les tailles et la procédure pas à pas.

L’explorateur

randscan.org fonctionne à côté du validateur E et indexe la chain 20 ; son API REST et son flux WebSocket sont le moyen le plus simple de lire la chaîne sans nœud (API de l’explorateur). L’explorateur est open source (randprotocol/randscan) et son indexeur n’a besoin que du JSON-RPC rand_* d’un nœud et d’une base PostgreSQL : il peut donc être exécuté contre votre propre nœud.

Dernière vérification par rapport au commit c0878c3f de fullnode (chaîne 21) le 2026-10-12. Lorsque le code et cette page divergent, c'est le code qui a raison ; signalez-le-nous.