Le nœud sert JSON-RPC 2.0 sur HTTP à l’adresse --rpc (par défaut 127.0.0.1:8545), et un WebSocket sur le même port pour les abonnements. Depuis la v0.6.8, il peut aussi ouvrir un second point d’écoute, public (--public-rpc, ci-dessous). randprotocol_client::RpcClient (Rust) encapsule chacune des méthodes ci-dessous. Cette page est la liste des méthodes telle que la sert la v0.6.8 (c9c9bd3c), la version qu’exécute chain 20 ; lorsqu’une méthode existe dans le code mais reste inerte sur chain 20, la page le signale.
curl -s http://127.0.0.1:8545 -H 'content-type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"rand_getHead","params":[]}'
Le point d’accès public
https://rpc.randprotocol.org (le même proxy est aussi disponible à https://randprotocol.org/api/rpc) relaie vers un nœud d’archive de chain 20 qui conserve tous les blocs, pour les portefeuilles et les pages qui n’ont pas de nœud propre (rand --rpc https://rpc.randprotocol.org …). Il n’accepte que HTTP POST, avec CORS ouvert et sans WebSocket, et il est plus restreint qu’un nœud :
- Une seule requête par appel. Un lot est refusé avec
-32600. - Une liste d’autorisation de méthodes : depuis le 2026-10-01, le même ensemble que celui du point d’écoute public du nœud (
rpc::PUBLIC_METHODSdans la v0.6.8). Les lectures d’état de programme dontrand program invokea besoin,rand_getAdmitted, les lectures d’agrégation et les lectures de vesting en font donc partie, de même querand_checkTransactionetrand_sendTransaction.rand_mint,rand_importViewingKey,rand_getViewingNotes,rand_removeViewingKeyetrand_getPeersn’en font pas partie et répondent-32601method not allowed heresans atteindre le nœud : un faucet public est une surface d’abus, et une clé de visualisation ne doit être envoyée qu’à un nœud que vous exploitez. - Un corps de requête d’au plus 12 MiB, ce qui suffit pour tout transfert, toute action sur un jeton ou tout déploiement, ainsi que pour un appel dont la preuve atteint environ 3,4 MB (la transaction est encodée en hexadécimal, et ses preuves de bundle et d’authentification occupent environ 2,85 MB avant celle de l’appel). Un appel plus volumineux doit passer par votre propre nœud. Au-delà du plafond : HTTP 413 et
-32600. - Par adresse, 1 200 requêtes par minute, et 6 par minute pour
rand_sendTransaction. Au-delà : HTTP 429 et-32005. Le budget de lecture est dimensionné pour la première synchronisation d’un portefeuille, soit plusieurs centaines de requêtes en une seule rafale (645 à la hauteur 99 000 de chain 18). -32000upstream unavailablelorsque le nœud situé derrière ne répond pas.
Tout le reste de cette page décrit le nœud lui-même.
Les deux points d’écoute du nœud
Depuis la v0.6.8 (audit v6), un nœud dispose de deux points d’écoute JSON-RPC.
--rpc <ADDR>, le point d’écoute de l’opérateur. Toutes les méthodes, les lots et le WebSocket. Il identifie un appelant par son adresse de socket et fait confiance au loopback : les méthodes de clé de visualisation ne répondent qu’au loopback, et le loopback n’est pas comptabilisé. Laissez-le sur127.0.0.1. Un proxy inverse ou une redirection SSH arrive par le loopback ; derrière l’un d’eux, chaque visiteur est donc l’opérateur : ne dirigez jamais un point d’accès public vers ce port.--public-rpc <ADDR>, le point d’écoute public, prévu précisément pour ce saut. Il sert un ensemble fixe de méthodes : les lectures dont un portefeuille, un explorateur ou une page a besoin, ainsi querand_sendTransaction(rpc::PUBLIC_METHODS).rand_importViewingKey,rand_getViewingNotes,rand_removeViewingKey,rand_mintetrand_getPeersn’y sont jamais servies, pas plus que toute méthode ajoutée ultérieurement tant qu’elle n’est pas listée ; un tel appel répond-32000<method> is not served on this node's public listener. Un lot donne-32600, et il n’y a pas de route WebSocket. Un seul compteur couvre l’ensemble des appelants : 600 requêtes en rafale et 150 par seconde ; au-delà, HTTP 429 avec un corps-32000. Les limites par visiteur relèvent du proxy, qui peut voir les visiteurs. Ses appelants occupent au plus la moitié des emplacements de lecture bloquante du nœud, de sorte que le point d’écoute de l’opérateur n’est jamais privé de ressources.
Les deux points d’écoute partagent deux limites de lecture. Une lecture bloquante qui n’a pas rendu son résultat en 30 s reçoit la réponse -32000 this read took longer than 30 s on this node; retry with a smaller range. rand_getBlocks termine sa page dès qu’elle a lu 64 MiB de blocs.
--rpc-viewing-token-file <PATH> place un jeton porteur sur les méthodes de clé de visualisation de --rpc : la première ligne du fichier, d’au moins 32 caractères, envoyée sous la forme Authorization: Bearer <token>. Avec cette option, le loopback seul ne suffit plus, et tout appelant, où qu’il se trouve, qui présente le jeton est servi.
Conventions
- Les adresses de validateur sont des chaînes base58 (32 octets). Les hachages font 64 caractères hexadécimaux, avec ou sans
0x. Les valeurs protégées — engagements, nullifiers, ancres, racines d’arbre, niveaux de témoin — sont desWord8: huit motsu32en little-endian, écrits en 64 caractères hexadécimaux minuscules. - Les adresses protégées sont
rand1+ base58, soit 1 667 caractères. Un paramètre de plus de 2 000 caractères est refusé sur sa longueur avant d’être analysé. - Tout montant
u64est une chaîne décimale ("1500000000"= 1,5 RAND ; 1 RAND = 10⁹ unités) : unités de RAND ou unités de jeton, état de la chaîne ou champ propre d’une transaction décodée. Un nombre JSON n’est pas un entier exact au-delà de 2⁵³, et l’offre d’un jeton à 9 décimales dépasse cette valeur à quelques dizaines de millions d’unités. Voir la note datée ci-dessous pour ce que cela a changé. - Les indices, nonces, hauteurs, indices de feuille, vues, longueurs, décimales et compteurs de jours (
mint_day) ne sont jamais des montants et sont des entiers JSON partout. - Toute requête doit comporter un membre
id. Un objet qui n’en a pas est une notification et est refusé avec-32600; un"id": nullexplicite est une requête normale. - Il n’existe ni méthode de solde ni méthode de compte. Un portefeuille calcule son solde en parcourant
rand_getCommitmentsetrand_getNullifiersavec sa clé de visualisation.rand_getBalance,rand_getAccountetrand_getAssetBalancerépondent-32601.
Changement incompatible, 2026-09-20 (chain 14) : tout montant est une chaîne décimale
Avant chain 14, les montants à l’intérieur d’une transaction décodée étaient des entiers JSON. Cette exception a disparu. Ces champs sont passés de nombres JSON à des chaînes décimales, sans autre changement de sens ni de position :
bundle.fee, dans letxderand_getTransactionet dans lestransactions[]derand_getBlockByHeight/rand_getBlockByHash;action.amountpourkind=mint,bond,unbond,withdraw,bridge_attest(les mêmes trois méthodes) ;action.amountetaction.relayer_feepourkind=bridge_burn(les mêmes trois méthodes) ;lockeddans les lignes derand_getAssetset derand_getBridgeState.assets[].
Un client qui lit l’un de ces champs comme un nombre doit désormais analyser une chaîne. aggregation.subsidy_base de rand_status (chaînes à agrégation uniquement) est devenu une chaîne dans le même changement. Tous les autres montants étaient déjà des chaînes : rand_getSupply, rand_getEmission, rand_getValidators, les notes de visualisation, et chaque champ introduit avec chain 14 — les burn_a et burn_r du bundle, les offres de jetons, le locked de chaque collatéral, mint_cap_per_day et minted_today, registration_fee, ainsi que le amount de token_mint, token_burn et register_token (initial_amount, initial.amount).
Lots
Un corps POST peut être un tableau d’au plus 20 objets de requête, auquel répond un tableau de même longueur, dans l’ordre des requêtes, erreurs comprises. Les requêtes s’exécutent l’une après l’autre. Un tableau vide, un tableau de plus de 20 éléments, ou un corps qui n’est ni un objet ni un tableau donne une unique réponse -32600 avec un id nul. Le corps entier reste borné par la limite de taille de requête du nœud, dimensionnée pour une seule transaction accompagnée de sa preuve : un lot de deux appels rand_sendTransaction avec preuve est refusé avec 413. Groupez des lectures, pas des soumissions avec preuve.
Chaîne et nœud
rand_chainId
Paramètres []. Résultat : l’identifiant de la chaîne (entier) ; 20 sur le testnet.
rand_tokenInfo
Paramètres []. Résultat { "symbol": "RAND", "decimals": 9 }.
rand_getHead
Paramètres []. Résultat { "height": 1998, "hash": "…", "view": 2251 }. view est la vue de consensus courante du nœud, qui devance la hauteur lorsque des vues expirent.
rand_status (alias rand_syncStatus)
Paramètres []. Résultat :
{
"height": 1998, "head_hash": "…", "view": 2251, "high_qc_view": 2250,
"syncing": false, "sync_target": 1998,
"sync_inflight_age_ms": null, "sync_failures": 0, "sync_late_batches": 0,
"peer_count": 26, "connected_peers": 26, "reserved_peers": 25, "ws_clients": 0, "refused_cache": 0,
"verify_queue": 0, "mempool_size": 0,
"is_validator": true, "active_validator": true, "faucet": true, "confidential": true,
"testnet": true, "fri_profile": "production", "programs": 10, "viewing_keys": 0,
"notes": 197, "nullifiers": 160, "tree_root": "72b9…1197", "hc_bundle": "60af…3fce", "hc_auth": "1e4e…39c1",
"binding_domain": 1,
"address": "5TAofR…", "peer_id": "12D3KooW…",
"gas_prices": { "gas_price": "100", "byte_price": "800" }
}
is_validator : ce nœud détient une clé de validateur ; active_validator : cette clé appartient à l’ensemble qui exécute l’époque courante. reserved_peers (v0.6.8) compte les pairs admis au-delà du plafond de connexions entrantes : les bootstraps, tout --reserved-peer, et chaque identité de validateur apprise à partir d’une liaison de pair signée, chacun comptant une fois ; sur un nœud de la flotte, il devrait atteindre le nombre de validateurs dans la minute qui suit le démarrage. viewing_keys est le nombre de clés de visualisation que le nœud garde en mémoire pour l’analyse côté nœud. verify_queue est le nombre de transactions en attente de l’un des quatre workers de vérification de preuve (64 au plus). hc_bundle est le guest de bundle contre lequel les preuves de cette chaîne sont vérifiées, et hc_auth le guest d’authentification que son genesis fixe (autorisation scindée, depuis chain 17), ou null sur une chaîne qui n’en a pas ; un nœud dont la version ne correspond à l’un ou à l’autre refuse de démarrer. testnet et binding_domain sont les champs de genesis de même nom, tels que les sert rand_getLimits. gas_prices désigne les prix du gas courants du registre de tête, actualisés à chaque validation, sous forme de chaînes décimales ; null sur une chaîne sans section gas. Les prix de chain 20 sont dynamiques et évoluent à chaque bloc selon son remplissage, jamais en dessous des planchers de 100 et 800 ni au-dessus des plafonds de 10 000 et 80 000 ; lisez-les donc ici ou dans rand_getLimits et ne les codez pas en dur. L’objet porte aussi un objet aggregation (son subsidy_base est une chaîne décimale, le reste des entiers), qui ne contient que des zéros sur une chaîne sans section aggregation, comme l’est chain 20.
L’objet porte aussi disk_free_bytes (un nombre) et disk_low, ainsi que le couple de rétention : prune_floor, la hauteur la plus basse que ce nœud conserve encore (0 sur une archive), et prune_history_secs, la fenêtre que conserve un nœud démarré avec --prune-history (null lorsqu’il conserve tout). Sur chain 20, comme sur chain 19, 24 des 26 validateurs conservent 24 heures de blocs ; les deux archives les conservent tous. Voir Nœuds élagués.
rand_getPeers
Paramètres []. Résultat : un tableau de { "peer_id": "12D3KooW…", "addrs": ["/ip4/…/tcp/30303"], "connected_secs": 1241 }.
rand_getBlockByHeight / rand_getBlockByHash
Paramètres [height] ou [hash]. Résultat null si inconnu, sinon :
{
"hash": "647b…", "height": 50, "view": 92, "parent": "2d41…",
"proposer": "3v3VBJ…", "timestamp_ms": 1788000123456,
"tx_root": "0000…", "state_root": "a1b2…", "justify_view": 91,
"tx_count": 0, "transactions": [ ]
}
Seuls les blocs validés sont servis. transactions a la même forme que le tx de rand_getTransaction. Un bloc porte aussi sealed, et chacune de ses transactions sealed_by ; ils valent false et null sur une chaîne sans agrégation.
rand_getBlocks
Paramètres [from_height, to_height]. Résultat : des en-têtes de bloc, du plus ancien au plus récent, au plus 1 024 à partir de from_height (128 sur un nœud antérieur au 2026-09-24) : les champs de rand_getBlockByHeight sans transactions. Une plage plus large que le plafond, ou qui dépasse la tête de la chaîne, est tronquée et non refusée, et depuis v0.6.8 une page s’arrête aussi dès qu’elle a lu 64 MiB de blocs ; reprenez à partir de la dernière hauteur renvoyée. -32602 si to_height est inférieur à from_height.
Depuis v0.6.8, chaque en-tête porte aussi public_notes : les transactions bridge_attest, token_mint et register_token du bloc, et, sur une chaîne avec frais de pont, ses transactions bridge_burn, sous la forme [{ "hash", "raw" }] (l’encodage que sert rand_getRawTransaction) ; vide pour tout autre bloc. Une transaction burn est servie avec ses preuves vidées et "proofs_stripped": true ; son hash est le véritable identifiant de la transaction. La première synchronisation d’un portefeuille lit ces pages et n’a besoin de rien d’autre pour reconstruire les notes publiques.
rand_getFinality
Paramètres [height] ou [hash]. Résultat, l’un de { "status": "committed", "height", "hash" }, { "status": "certified", "height", "hash", "qc_view" }, { "status": "proposed", "height", "hash" }, { "status": "unknown" }. Une hauteur n’est jamais vérifiée que par rapport à la chaîne validée ; elle répond donc committed ou unknown ; un hash peut aussi donner certified (dans l’arbre non validé du réplica, avec un certificat de quorum) ou proposed (dans l’arbre sans certificat).
rand_getProposer
Paramètres [view] ou [from_view, to_view], au plus 64 vues. Résultat { "epoch": 3, "proposers": [ { "view": 2251, "proposer": "2nRdFC…" } ] } : le leader de chaque vue selon l’ensemble de validateurs actuel, et non celui qui était en place à cette vue dans l’historique. -32602 pour une plage inversée ou de plus de 64 vues.
rand_getVersion
Paramètres []. Résultat { "version", "git_sha", "chain_id", "hc_bundle", "hc_auth", "fri_profile" }. version est la version du crate du workspace ; git_sha le hash du commit relevé à la compilation, suivi de -dirty si des fichiers suivis différaient, ou "unknown". hc_auth est le guest d’authentification du genesis, comme dans rand_status, ou null. Le point d’accès public au 2026-10-01, sur la chaîne 20 :
{ "version": "0.6.8", "git_sha": "c9c9bd3c1903aa4cc5833b7ab8c62fc3e6942017", "chain_id": 20,
"hc_bundle": "60af094acfe65d85fdb18fb3d06cf9085dcf28c96e59e87f1ee527226e6e3fce",
"hc_auth": "1e4e347f44cf86750b30a9a4bdf9ec9256efe353d4ff8017451eca7d195639c1",
"fri_profile": "production" }
rand_getGenesisHash
Paramètres []. Résultat : le hash du genesis en hexadécimal ; "6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135" sur la chaîne 20 (celui de la chaîne 19 était a3defc93…228a). Un identifiant de chaîne seul ne distingue pas deux coupes d’une chaîne ; ceci le fait. Sur la chaîne 20, il entre aussi dans chaque liaison de transaction et dans chaque message d’action signé (binding_domain: 1, voir rand_getLimits).
rand_getHealth
Paramètres []. Résultat, l’un de { "status": "disk_low", "free_bytes": "3221225472" }, { "status": "ok" }, { "status": "syncing", "behind": 412 }, { "status": "behind", "behind": 30 }. disk_low l’emporte sur tout le reste, lorsque l’espace libre du système de fichiers du répertoire de données est inférieur à quatre fois le --min-free-disk-mb du nœud (1024 par défaut, soit moins de 4 GB) ; free_bytes est une chaîne décimale. ok lorsque sync_target - height vaut au plus 2 et qu’aucune requête de lot de synchronisation n’est en cours ; syncing tant qu’il y en a une ; behind lorsque le nœud ne se synchronise pas et que le retard dépasse encore 2.
rand_getMempoolInfo
Paramètres []. Résultat { "count": 2, "bytes": 2611200, "oldest_ms": 1450, "max_count": 10000 }. bytes additionne la longueur encodée de chaque transaction en pool ; oldest_ms vaut null quand le pool est vide. Depuis v0.6.8, le pool est aussi plafonné en octets, à huit fois le max_block_bytes du genesis (160 MiB sur la chaîne 20) : au-delà, une transaction qui paie davantage au-dessus de son plancher par KiB évince la moins chère, et celle qui paie moins ou autant est refusée avec mempool full. Les actions de gouvernance passent les deux plafonds.
Soumission
rand_sendTransaction
Paramètres [hex], où hex est bincode(Transaction). Résultat : le hash de la transaction. Le nœud valide dans l’ordre d’admission (Transactions), la met en pool et la propage par gossip. Les erreurs sont -32000 avec la raison : nullifier already spent, anchor is not one of the last 1024 roots, time 12 is outside [1500, 2524] (les deux fenêtres valent le proof_window_blocks du genesis, 1 024 sur la chaîne 20 ; 256 sur les chaînes jusqu’à la 19), fee 1000000 below minimum 2000000, the bundle's digest is not what its proof published, invalid bundle proof: …, unknown program …, already in mempool, conflicts with a pending transaction over <nullifier>, faucet is disabled on this chain ; pour un invoke, cell <key> is no longer what this transition read (un verdict d’état : relisez la cellule et prouvez à nouveau) ou the vault holds … of asset … and the transition pays … ; pour un Bond d’enregistrement sous admission par vote, validator … has not been admitted by the validator set's vote (staking.admission_by_vote); ask the validators for an AdmitValidator first ; pour un bundle qui ne convient pas au genesis de la chaîne 20, envelope is 1348 bytes, this chain requires exactly 1860, this chain requires an auth proof on every bundle (genesis hc_auth), the auth proof publishes a different commitment than the bundle's auth_commit, auth proof: …, bundle proof declares gas limit …, this chain requires exactly 20479, non-canonical proof: … — re-prove with an up-to-date wallet ; pour les actions de pont bridge: attestation already consumed, the attestation names a different recipient, the attestation deposits under asset 2, and the transaction names 1, the bundle burns 399, not the 400 the action declares ; pour un bundle qui porte un burn que son action ne peut pas porter, the bundle burns asset 2 on an action that burns no token, a burn of 5 is not allowed on this action ou RAND burned through burn_a (5); a RAND burn goes through burn_r ; pour l’état du pont, bridge minting is paused, mint cap … per backing per day, already paused, not paused, wrong pause nonce, wrong list nonce, chain N has no registered emitter ; pour le registre de tokens, wrong mint nonce et wrong token index. L’acceptation n’est pas la validation : interrogez rand_getTransaction ou rand_getTransactionStatus.
Une transaction plus grande que le plafond de bloc de la chaîne (max_block_bytes de rand_getLimits) est refusée avec -32000, qui nomme les deux tailles, avant d’atteindre le mempool. Le corps de la requête est plafonné à 2 × (2 × max_proof_bytes + 4 × 2048 + max_call_envelope_bytes + 16,384 + 64 KiB) + 256 KiB, calculé à partir du genesis au démarrage : 8 867 840 octets sur une chaîne par défaut et 17 350 656 aux limites de la chaîne 20, comme pour les chaînes 17 à 19 (34 127 872 sur les chaînes 13 à 15, dont le plafond de preuve était de 8 MiB). 4 × 2048 correspond aux quatre enveloppes du bundle au plafond d’enveloppe. Un corps au-delà est -32600, avec le nom de la limite.
Seuls -32000 (l’admission l’a refusée) et -32602 (elle n’a pu être décodée) constituent un verdict sur la transaction. Toute autre défaillance, -32603 comprise, laisse son sort inconnu : le nœud met en pool et propage une transaction avant de répondre, si bien qu’un nœud qui s’arrête dans cet intervalle peut avoir admis ce dont il n’a jamais répondu. randprotocol_client ne traite un envoi comme refusé que sur ces deux codes.
rand_getTransactionStatus
Paramètres [[hash, …]], de 1 à 64 hashes. Résultat : une entrée par hash, dans l’ordre :
[ { "hash": "…", "status": "committed", "height": 1998, "index": 0 },
{ "hash": "…", "status": "pending" },
{ "hash": "…", "status": "rejected", "reason": "bad mint signature" },
{ "hash": "…", "status": "unknown" } ]
Ordre de recherche pour chaque hash : validée (stockage), en attente (le mempool), rejetée (le cache des refus que compte le refused_cache de rand_status), sinon unknown. rejected ne couvre que les refus portant sur les octets propres de la transaction, qu’aucun bloc ultérieur ne peut changer : une preuve invalide, un digest de bundle qui n’est pas celui que sa preuve a publié, une mauvaise signature de mint, un mauvais identifiant de chaîne, une partie surdimensionnée, un nullifier ou un engagement répété à l’intérieur de la transaction, une enveloppe de mauvaise taille, une preuve d’authentification absente ou discordante, un bundle ou une preuve d’authentification qui déclare une mauvaise limite de gaz. Un refus qui dépend de l’état du nœud à cet instant — un nullifier dépensé, une ancre ou un time hors de la fenêtre, des frais sous le plancher (qui, sur la chaîne 20, suit les prix du gaz), un programme inconnu, un nonce de registre, une lecture invoke périmée — n’est pas mémorisé et donne unknown. -32602 pour une liste vide ou de plus de 64 hashes.
rand_mint (faucet testnet)
Paramètres [address] ou [address, amount] ; address est une adresse rand1…, amount une chaîne d’unités d’au plus 100000000000 (100 RAND, la valeur par défaut). Résultat : le hash de la transaction de mint. Uniquement sur une chaîne dont le genesis porte "faucet": true ; sinon -32000 faucet is disabled on this chain. Un observateur répond faucet mints are signed by validators; ask a validator node.
Sur une chaîne dont le genesis définit staking.faucet_recipients, comme celui de la chaîne 20 (16 portefeuilles), seuls les portefeuilles listés peuvent être payés : un mint vers toute autre adresse est refusé avec the faucet may not mint to this recipient (not in staking.faucet_recipients). Le genesis de la chaîne 20 définit aussi staking.faucet_minters (18 clés de validateur) : seule une clé listée peut signer un mint. Le faucet a également un budget par époque, staking.faucet_budget_per_epoch (10 000 RAND par époque de 1 000 blocs sur la chaîne 20) ; un mint au-delà est refusé avec FaucetBudgetExhausted, un verdict d’état qui donne unknown une fois sorti du pool.
La faucet est limitée en débit par processus de nœud : huit mints à la suite, rechargés à raison d’un par seconde. Au-delà, l’appel répond faucet is rate limited on this node (8 mints back to back, refilling at 1/s); try again shortly. Une rafale de requêtes à la faucet ne peut donc pas remplir le pool avant un PauseMints du bridge. Celui-ci, comme les trois autres actions de gouvernance du bridge, échappe aussi au refus mempool full du pool et est ordonné en premier dans un bloc.
Depuis la chaîne 14, un mint est ouvert : l’action Mint porte le pk, le time et le r de la note à côté de cm et amount, l’admission recalcule l’engagement et refuse un cm qui ne s’ouvre pas sur ces valeurs (the mint's commitment does not open to its published note and amount), et le minter signe les sept champs sous le domaine rand-mint-2 ; sur la chaîne 20 (binding_domain: 1), sous rand-mint-3, avec le hash de genèse en premier. Le propriétaire et le montant d’une note de la faucet sont donc publics, comme l’est le paiement d’un retrait. Les paramètres de rand_mint sont inchangés.
rand_estimateFee
Params [spec], l’un de {"kind":"bundle"}, {"kind":"deploy","words":n,"public_words":m}, {"kind":"call","tier":t,"bytes":b,"gas":g} avec t parmi 10, 12, 14, 16, 18, 20. Résultat : le frais minimal en unités, sous forme de chaîne. {"kind":"bundle"} vaut "1000000". public_words (facultatif, 0 par défaut) se paie par mot comme le code, de sorte que le frais est celui d’un déploiement de n + m mots. bytes (facultatif, 0 par défaut) est la longueur de la preuve de l’appel plus celle de son enveloppe d’entrée.
La tarification d’un appel dépend du gas_metering de rand_getLimits :
"circuit", une chaîne dotée de sa propre sectiongas, comme l’est la chaîne 20.gasest la limite de gas que la preuve de l’appel déclare, et elle est obligatoire : sans elle, l’appel répond-32602under the gas section a call estimate needs its gas. La réponse est1,000,000 + gas_price × gas + byte_price × ⌈bytes / 1024⌉aux prix courants de la tête de chaîne, ceux que sertrand_getLimits. Aux prix plancher de la chaîne 20, 100 et 800, un appel qui déclare 20 479 gas avec 1 300 000 octets de preuve et d’enveloppe vaut"4063900". Les prix évoluent à chaque bloc : estimez au plus près de l’envoi."header", une politique de gas au niveau du nœud sur une chaîne sans la section. La spécification d’appel acceptekeccak_log_heightetsha256_log_height(facultatifs, 0 par défaut, chacun de 0 à 40, sinon-32602), les hauteurs de table de hachage que l’en-tête de la preuve déclare, et la réponse est le plus grand du plancher de palier du registre et du plancher de gas de cet en-tête. Sous une sectiongas, les deux hauteurs sont acceptées et inutilisées.null, ni l’un ni l’autre. Le barème de paliers du registre seul : jusqu’à la franchise gratuite incluse (2 097 152 + 18 432 octets), un appel coûte le frais de son palier ; chaque KiB au-delà, un KiB entamé comptant pour un entier, ajoute 1 000 unités.
Depuis v0.6.8, {"kind":"invoke", …, "created_cells": c} tarifie un invoke RPL-2 : tous les champs d’une spécification d’appel, plus created_cells (facultatif, 0 par défaut, au plus max_writes, sinon -32602), les cellules que la transition créerait. Chacune ajoute le cell_fee de la chaîne (rand_getLimits.program_state). C’est à l’appelant de les compter : une écriture d’une valeur non nulle dans une cellule que rand_getProgramCell lit comme des zéros. Sur la chaîne 20, l’appel ci-dessus avec deux cellules créées vaut "24063900". Sur une chaîne sans section program_state, ce kind donne -32602.
Frais donne le barème.
Le pool
rand_getTreeInfo
Params []. Résultat { "next_index": 41, "root": "6b1d…c4", "nullifiers": 12 } : le nombre de feuilles, la racine courante, le nombre de notes dépensées.
rand_getCommitments
Params [from_index] ou [from_index, limit]. Résultat : une page de feuilles à partir de from_index, les plus anciennes d’abord, au plus 1 000 lignes quelle que soit la valeur de limit.
[ { "index": 40, "cm": "2a9f…07", "height": 37,
"envelope": { "kem_ct": "b41c…", "to_receiver": "77e0…", "to_sender": "0c31…", "body": "9dd2…" } } ]
rand_getNullifiers
Params [from_height] ou [from_height, limit]. Résultat : tous les nullifiers publiés à partir de cette hauteur, avec le même plafond de 1 000 lignes. Une page peut s’arrêter au milieu d’une hauteur ; remontez à la plus haute hauteur vue.
[ { "height": 37, "nullifier": "8c04…d1" }, { "height": 41, "nullifier": "12be…9a" } ]
rand_getCompactBlocks
Params [from_height, to_height], tous deux obligatoires. Résultat : une ligne par bloc, les plus anciens d’abord, avec ce dont un wallet léger a besoin et rien de plus :
[ { "height": 192, "hash": "63f6…08", "timestamp_ms": 1788000123456,
"commitments": [],
"transactions": [
{ "hash": "4f2c…e7",
"commitments": [ { "index": 40, "cm": "2a9f…07", "envelope": { "kem_ct": "…", "to_receiver": "…", "to_sender": "…", "body": "…" } } ],
"nullifiers": ["8c04…d1", "5e77…20", "03aa…6f", "e19b…42"] } ] } ]
Chaque transaction porteuse d’un bundle possède ici quatre notes et quatre nullifiers : ses quatre emplacements, y compris les emplacements factices, qu’aucun lecteur ne distingue des vrais (seule la première ligne d’engagement de l’exemple est montrée). Depuis v0.6.8, certaines possèdent davantage de notes, après ces quatre : un invoke possède une note par paiement, pays puis mints, chacune avec l’enveloppe propre au paiement, de sorte qu’un bénéficiaire la trouve par déchiffrement d’essai ; sur une chaîne avec frais de bridge, comme l’est la chaîne 20, un bridge_burn possède sa note de frais de trésorerie, et un bridge_attest possède sa note de frais après son dépôt. L’enveloppe d’une note de frais est vide, scellée pour personne : le wallet de la trésorerie reconstruit la note à partir des champs publics de la transaction.
Au plus 128 blocs par appel (une plage plus large est tronquée) et, passé le premier bloc — toujours servi en entier —, 1 000 notes ; reprenez au dernier height renvoyé plus un. Le tableau commitments au niveau du bloc contient les dépôts de genèse à la hauteur 0 et est vide ailleurs ; les notes dérivées d’un Withdraw et d’un BridgeAttest apparaissent sous leur transaction. -32602 pour une plage inversée ou une borne manquante.
rand_getAnchor
Params [] pour la tête, ou [height]. Résultat { "height": 192, "root": "6b1d…c4" }, ou -32001 pour une hauteur sans ancre enregistrée. Seules les racines de fin de bloc sont des ancres. Le nœud peut répondre pour une hauteur ancienne, mais l’ancre d’un bundle n’est admissible que si elle figure parmi les proof_window_blocks dernières racines : 1 024 sur la chaîne 20, 256 sur les chaînes jusqu’à la 19. Construisez contre la tête.
rand_getWitness
Params [index]. Résultat null au-delà de la fin de l’arbre, sinon le chemin de Merkle de cette feuille, de la feuille vers la racine, exactement 32 niveaux, avec la racine courante de l’arbre :
{ "index": 40, "root": "6b1d…c4", "path": ["0000…00", "f2a1…3b", "…"] }
C’est la lecture la plus coûteuse que sert le nœud (il reconstruit l’arbre complet de profondeur 32) et la seule requête qui révèle quelque chose sur l’appelant. Un nœud exécute au plus deux reconstructions d’arbre à la fois, rand_getWitness et rand_getWitnesses confondus ; une requête qui attend plus de 10 s un emplacement est refusée avec -32000 witness builds are busy on this node; retry shortly.
rand_getWitnesses
Params [[index, …]], de 1 à 32 indices de feuilles. Résultat { "root": "…", "witnesses": [ { "index": 7, "path": ["…"] }, { "index": 9, "path": null } ] } : rand_getWitness appliqué à de nombreuses feuilles en une seule construction d’arbre, de sorte qu’un wallet qui prouve plusieurs notes ne paie la reconstruction qu’une fois. Un indice au-delà de la fin de l’arbre donne path: null pour cette entrée. -32602 pour une liste vide ou plus de 32 indices.
Clés de visualisation et divulgation
rand_importViewingKey
Params [viewing_key] ou [viewing_key, rescan_from_height], la clé sous forme de 64 caractères hexadécimaux. Résultat { "imported": true, "rescan_from_height": 0, "viewing_keys": 3 }. Le nœud conserve la clé en mémoire uniquement : 64 au plus par nœud, effacées au redémarrage ; une clé déjà détenue est une opération sans effet ("imported": false). L’import ne lance jamais d’analyse ; l’analyse est pilotée par la méthode suivante. -32602 pour une clé mal formée ; -32000 à la 65e clé.
Les trois méthodes de clé de visualisation ne répondent qu’à un appelant en loopback et refusent tout autre avec -32000, sauf si le nœud tourne avec --rpc-viewing-open derrière un dispositif qui authentifie. rand_removeViewingKey ([viewing_key] → { "removed": bool, "viewing_keys": n }) libère un emplacement et met la clé à zéro. Le registre est indexé par un identifiant à sens unique de la clé, blake3("rand-viewing-registry-id-1" ‖ nk), jamais par la clé elle-même : l’import est donc la seule copie durable et la suppression l’efface. Le texte JSON de la requête elle-même et les copies transitoires que fait le déchiffrement d’essai ne sont pas effacés.
rand_getViewingNotes
Params [viewing_key] ou [viewing_key, from_index, limit] (limit vaut 1 000 par défaut et au maximum). Chaque appel fait avancer l’analyse de la clé d’au plus 10 000 feuilles, puis sert la page :
{ "scanned_index": 10041, "next_index": 10041, "complete": true,
"notes": [
{ "index": 40, "cm": "2a9f…07", "height": 37, "role": "received",
"note": { "pk": "…", "from": "…", "amount": "1500000000", "asset": 0, "time": 5, "memo": "coffee" },
"nullifier": "8c04…d1", "spent": false },
{ "index": 43, "cm": "b310…88", "height": 39, "role": "sent",
"note": { "pk": "…", "from": "…", "amount": "25000000", "asset": 0, "time": 9, "memo": null },
"nullifier": null, "spent": null } ] }
role vaut received (l’enveloppe s’est ouverte côté destinataire et la note désigne le pk de la clé) ou sent (ouverte par la clé de visualisation sortante). Le memo de la note est le mémo chiffré de l’expéditeur, du texte UTF-8 d’au plus 510 octets, ou null lorsqu’il n’y en a pas ou que son champ s’est ouvert mal formé ; un mémo mal formé ne coûte jamais la note au bénéficiaire. Un mémo est le texte de n’importe qui : affichez-le comme non fiable. -32001 pour une clé que ce nœud n’a pas importée.
rand_checkTransaction
Params [hash, key], key étant une TxKey par transaction sous forme de 64 caractères hexadécimaux. Résultat null pour un hash inconnu, sinon ce que la clé divulgue :
{ "tx": "4f2c…e7", "height": 192,
"disclosed": [ { "output": "bundle:0", "cm": "2a9f…07", "index": 40,
"note": { "pk": "…", "from": "…", "amount": "1500000000", "asset": 0, "time": 5, "memo": "invoice 7" } } ] }
Sans état : la clé est abandonnée avec l’appel. output vaut bundle:0 … bundle:3 pour les quatre emplacements de sortie du bundle (emplacements 0–1 : sorties de l’actif privé ; emplacements 2–3 : sorties RAND, factices comprises), deposit pour l’enveloppe de dépôt d’une attestation de bridge, token_mint pour la note d’un TokenMint, initial_mint pour le mint initial d’un RegisterToken, mint:0 pour l’unique enveloppe d’un mint de faucet, ou (v0.6.8) payout:0 … payout:3 pour les notes versées par un invoke, numérotées selon leur place dans la transition, pays puis mints ; asset_bundle:* a disparu avec le second bundle. Sur une chaîne avec frais de bridge, deposit ouvre le dépôt net, soit le montant brut moins les frais. La note divulguée porte son asset : cet appel, avec la clé sous laquelle l’expéditeur a scellé, est le seul endroit où le RPC révèle quel actif un transfert a déplacé. Elle porte aussi le memo, comme le fait rand_getViewingNotes : la clé qui ouvre la note ouvre le mémo, issu du même corps scellé. Une mauvaise clé renvoie { "disclosed": [] }, indiscernable d’une transaction qui ne divulgue rien. Les enveloppes d’un withdraw et d’une allocation de genèse ne sont pas essayées : elles sont scellées à l’intérieur du nœud sous des clés abandonnées aussitôt, de sorte qu’aucune TxKey n’existe pour elles. Celle d’un mint de faucet est essayée : son destinataire retrouve la clé grâce à la moitié KEM de l’enveloppe (rand tx-key).
Transactions
rand_getTransaction
Paramètres [hash]. Résultat null tant que la transaction n’est pas validée, puis :
{
"height": 192, "index": 0, "block_hash": "63f6…08",
"tx": {
"hash": "4f2c…e7", "chain_id": 20,
"bundle": { "anchor": "6b1d…c4",
"nullifiers": ["8c04…d1", "5e77…20", "03aa…6f", "e19b…42"],
"commitments": ["2a9f…07", "b310…88", "77c1…0e", "5d20…b3"],
"fee": "1000000", "burn_a": "0", "burn_r": "0", "burn_asset": 0, "time": 5,
"proof_len": 1490435, "envelope_len": [1860, 1860, 1860, 1860],
"auth_commit": "9217…ee9e", "auth_proof_bytes": 1361161 },
"action": { "kind": "none" }
}
}
bundle vaut null pour les actions sans bundle. Les preuves et les enveloppes sont indiquées par leur seule longueur. envelope_len vaut 1,860 pour chaque emplacement sur la chaîne 20, dont la genèse fixe l’enveloppe de note à cette taille (envelope_bytes dans rand_getLimits) ; elle valait 1,348 sur les chaînes 14 à 17. auth_commit et auth_proof_bytes relèvent de l’autorisation scindée (depuis la chaîne 17) : l’engagement public c = H(AUTH, nk, salt) que la preuve du bundle et la preuve d’autorisation publient toutes deux, et la longueur de la preuve d’autorisation. Sur une chaîne sans hc_auth, ils valent des zéros et 0.
bundle est le bundle à actif caché (depuis la chaîne 14) : quatre emplacements d’entrée et quatre emplacements de sortie, donc toujours quatre nullifiers, quatre commitments et quatre envelope_len, factices comprises. Chaque transaction en porte au plus un ; il n’existe pas de second bundle. Il n’a aucun champ asset : les emplacements 0–1 portent un actif privé et les emplacements 2–3 du RAND, et rien de public n’indique quel actif les emplacements 0–1 ont déplacé. Un transfert de RAND et un transfert de n’importe quel jeton RPL sont tous deux "kind": "none" avec burn_a, burn_r et burn_asset tous à 0 : la même forme, champ pour champ. Il n’existe pas de type token_transfer. Les trois champs de burn sont la seule déclaration publique du bundle sur la valeur qui quitte le pool :
burn_a/burn_asset— un montant de l’actif privé brûlé, et de quel actif il s’agit. Non nul sur untoken_burnou unbridge_burn, où ils égalent l’amountet l’assetde l’action, ainsi que sur uninvokedont l’inflowestdepositouburn.burn_r— RAND brûlé. Non nul sur unbond(égal à sonamount), unregister_aggregator, et uninvokequi dépose du RAND dans le coffre du programme.fee— les frais en RAND, toujours prélevés sur les emplacements 2–3.
Représentation des actions (chaque amount, relayer_fee et initial_amount est une chaîne décimale ; les montants d’un jeton sont dans ses propres unités, non en RAND) :
| kind | fields |
|---|---|
none | — |
mint | cm, amount, minter (validateur en base58) |
deploy | program, words, public_words_len |
call | program, proof_len, input_envelope_len (null lorsque l’appel n’en porte aucune) |
bond | validator, amount, registered |
unbond | validator, amount, nonce |
withdraw | validator, amount, nonce, time |
bridge_attest | attestation_len, recipient, asset, asset_index, amount, time, r, commitment, pq_signers (les indices, dans pq_guardians, des gardiens cosignataires ; les signatures de 2,420 octets ne sont pas représentées). asset_index, amount et commitment valent null pour une rotation de l’ensemble des gardiens. r est dérivé : blake3("rand-deposit-r-1" ‖ mu) sur le condensat que les gardiens ont signé. Depuis v0.6.8, également deposit_amount (la note du déposant : amount, le brut signé par les gardiens, moins les frais de bridge) et fee_note ({ amount, asset, time, r, commitment }, la note de la trésorerie sans son propriétaire, ou null sans frais de bridge) ; commitment est la feuille du dépôt net |
bridge_burn | asset, amount, relayer_fee, to_chain, token (la garantie rachetée), to. Un seul bundle porte tout le burn : son burn_asset et son burn_a sont l’asset et l’amount de l’action. Depuis v0.6.8, également release_amount (ce que libère le corps signé, amount moins les frais de bridge) et fee_note, comme sur une attestation |
register_token | name, symbol, decimals, authority ("none", "key", "bridge" ou "program"), index, initial_amount, initial ({ amount, recipient, time, r } ou null) |
token_mint | asset, amount, recipient, time, r, nonce |
set_authority | asset, nonce, new_authority (base58, ou null pour une renonciation) |
token_burn | asset, amount ; le burn_asset / burn_a du bundle les reprennent |
pause_mints | nonce ; sans bundle et sans frais, signé par la clé de pause de la genèse |
unpause_mints | nonce, pq_signers ; sans bundle et sans frais |
register_bridged_token | name, symbol, salt, chain, token, decimals (les décimales source de la première garantie), nonce, asset_id, pq_signers |
list_backing | token_index, chain, token, decimals, nonce, pq_signers |
rotate_pq_guardians | new_pq_guardians, nonce (le rotation_nonce consommé), pq_signers (indices dans l’ensemble PQ avant la rotation) ; sans bundle et sans frais, chaînes de bridge rules_v2 uniquement |
rotate_pause_key | new_pause_key, nonce, pq_signers ; idem |
rotate_pq_guardians_v2, rotate_pause_key_v2 | v0.6.8 : les champs de la v1, plus possession_signatures (un décompte) sur la forme PQ. Sans bundle et sans frais ; requis sur une chaîne dont le bridge.rotation de la genèse définit needs_possession, comme celui de la chaîne 20. La rotation prend effet delay_secs plus tard (86,400 s sur la chaîne 20) |
cancel_rotation | v0.6.8 : rotation_kind ("pq_guardians" ou "pause_key"), nonce ; signé par la clé de pause actuelle, abandonne une rotation en attente |
admit_validator | v0.6.8 : candidate (adresse), candidate_key (hex), voters (adresses). Sans bundle et sans frais : le vote de l’ensemble de validateurs pour autoriser l’enregistrement d’une nouvelle clé, sur une chaîne avec staking.admission_by_vote, comme la chaîne 20 |
slash_equivocation | v0.6.8 : offender, view, first et second ({ hash, height }) ; chaînes avec staking.slashing uniquement, ce que n’est pas la chaîne 20 |
invoke | v0.6.8, RPL-2 : les champs d’un appel (program, proof_len, input_envelope_len) et sa transition (ci-dessous). Chaînes avec une section program_state uniquement, comme la chaîne 20 |
register_aggregator, unbond_aggregator, withdraw_aggregator, slash_aggregator, aggregate | leurs champs publics ; chaînes à agrégation uniquement |
claim_vested, revoke_vesting, bond_vested, unbond_vested | entry (hex), amount (unvested sur une révocation), nonce ; time sur une réclamation ou une révocation ; validator et registers sur un bond ; depuis v0.6.8, signers sur une révocation (les positions de ses signataires dans les revokers de l’entrée). Sans bundle ; chaînes avec une section vesting dans la genèse uniquement, ce que n’est pas la chaîne 20 |
L’enregistrement et les mints d’un jeton sont publics, comme l’est un dépôt de bridge : chaque mot d’une note mintée figure dans l’action (son from est le MINT_FROM fixe de la chaîne), si bien qu’un destinataire reconstruit la note à partir de ces champs sans rien déchiffrer. Les chaînes de type bridge_attest, token_mint, register_token et invoke sont celles sur lesquelles s’appuie l’analyse du portefeuille, et un test les verrouille.
Un invoke à la hauteur 3,079 de la chaîne 20, qui a lu une cellule, l’a supprimée et a versé deux notes depuis le coffre :
{ "kind": "invoke", "program": "aaaf…4b21", "proof_len": 1433874, "input_envelope_len": null,
"transition": {
"reads": [{ "key": "0100…fa98", "value": "0000000004000000…00000000" }],
"writes": [{ "key": "0100…fa98", "value": "0000000000000000000000000000000000000000000000000000000000000000" }],
"inflow": "none",
"pays": [{ "asset": 0, "amount": "1000000000", "recipient": "rand1…", "time": 3027, "r": "87e6…9a67", "cm": "2555…6487" },
{ "asset": 4, "amount": "2000000000", "recipient": "rand1…", "time": 3027, "r": "b833…f390", "cm": "2b70…f204" }],
"mints": [] } }
Tout cela est public par conception. Les reads sont les cellules que le programme a lues, avec les valeurs lues : la preuve a été établie à partir d’elles, et le registre refuse la transaction si elles ne tiennent plus. Les writes sont les cellules qu’il a écrites ; une valeur écrite de 64 zéros supprime la cellule. Une clé ou une valeur de cellule est un Word8. Ce qui entre est du fait du bundle : burn_r est le RAND déposé dans le coffre du programme, et burn_a de burn_asset est le jeton que nomme inflow, deposit dans le coffre ou burn (le jeton propre du programme), none lorsque burn_a vaut 0. Ce qui sort est une note calculée par la chaîne pour chaque versement, pays hors du coffre, puis mints d’un jeton dont l’autorité de mint est ce programme. Chaque mot de chaque note figure ici sous les noms de champs d’un token_mint ; time est celui du bundle, cm la feuille que la chaîne a ajoutée, et le from de la note est le PROGRAM_FROM fixe de la chaîne. Les huit mots de sortie du programme figurent sur le reçu, comme ceux d’un appel.
Aucune réponse de transfert ne porte d’expéditeur, de destinataire, de nonce, de montant ni d’actif : un tel champ n’existe pas dans un transfert stocké.
rand_getRawTransaction
Paramètres [hash]. Résultat : la transaction validée complète en hex bincode, preuves comprises — ce dont un agrégateur a besoin et que rand_getTransaction ne représente jamais.
Programmes et reçus
rand_getProgram
Paramètres [program_id]. Résultat null ou { "id", "base_pc", "words_len", "code_hash", "deployed_at", "public_words_len", "public_digest" }. Pas de champ deployer : un déploiement est payé par un bundle. public_words_len est la longueur de l’entrée publique du programme fixée au déploiement (0 sans entrée) ; public_digest en est le condensat, la valeur contre laquelle la preuve de chaque appel est vérifiée, ou null sans entrée.
rand_getProgramCode
Paramètres [program_id]. Résultat null ou { "base_pc": 0, "words": [ … ] }.
rand_getProgramPublic
Paramètres [program_id]. Résultat : les mots publics du programme fixés au déploiement, sous la forme d’une seule chaîne hexadécimale, chaque mot occupant ses 4 octets en little-endian (8 chiffres hexadécimaux par mot) : [1, 2, 0xdeadbeef] donne "0100000002000000efbeadde". "" pour un programme déployé sans entrée publique, null pour un identifiant qu’aucun programme ne porte. Un wallet qui prouve un appel transmet ces mots au prover ; le registre vérifie l’engagement de la preuve par rapport au public_digest du programme.
rand_getProgramCell
v0.6.8, RPL-2. Paramètres [program_id, key], la clé étant un Word8 sous forme de 64 chiffres hexadécimaux (avec ou sans 0x). Résultat { "key": "<64 hex>", "value": "<64 hex>" }. Une cellule que le programme n’a jamais écrite, ou dans laquelle il a écrit des zéros (ce qui la supprime), se lit comme 64 zéros, l’unique encodage de « absente » ; il en va de même pour toute clé d’un programme que personne n’a déployé, car l’existence du programme n’est pas vérifiée. { "enabled": false } sur une chaîne sans section program_state. Une cellule est publique par conception : c’est ce qu’un wallet lit pour construire les reads d’un invoke.
rand_getProgramCells
v0.6.8. Paramètres [program_id, { "after": "<64 hex>" | null, "limit": n }], l’objet étant facultatif. Résultat { "cells": [{ "key", "value" }, …], "next": "<64 hex>" | null } : les cellules du programme dans l’ordre des clés (comparaison non signée des huit mots), à partir de after exclu, au plus limit d’entre elles (borné entre 1 et 1 000). next est la dernière clé servie lorsqu’il en reste ; renvoyez-la comme after. Il vaut null sur la dernière page. Un programme sans cellule répond { "cells": [], "next": null } ; { "enabled": false } sans la section.
rand_getProgramVault
v0.6.8. Paramètres [program_id]. Résultat [{ "asset": 0, "amount": "123" }, …], par indice d’actif croissant (0 est RAND, tout autre indice est celui du registre des jetons), les montants étant des chaînes décimales dans les unités propres à l’actif. Une ligne à zéro n’existe pas : un coffre vide, tout comme un programme que personne n’a déployé, donne donc []. { "enabled": false } sans la section. La valeur entre dans un coffre par le bundle d’un invoke (burn_r, et burn_a de burn_asset lorsque inflow vaut deposit) et en sort sous forme des notes que désignent les pays de la transition.
rand_getLimits
Paramètres []. Résultat : ce dont un wallet a besoin, à partir du genesis de la chaîne, pour construire une transaction : les cinq limites d’appel, la taille de l’enveloppe de note, l’interrupteur v0.6, le guest d’authentification, les conditions de gas et, depuis v0.6.8, les champs genesis de l’audit-v6 et les limites de l’état des programmes. Sur la chaîne 20 :
{ "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_metering": "circuit", "gas_price": "100", "byte_price": "800",
"bundle_gas_limit": 20479, "adjust_bps": 1250,
"max_gas_price": "10000", "max_byte_price": "80000", "byte_load": "paying",
"admission_by_vote": true, "testnet": true, "slashing": null, "binding_domain": 1,
"proof_window_blocks": 1024,
"program_state": { "cell_fee": "10000000", "max_reads": 8, "max_writes": 8, "max_payouts": 4 } }
Un genesis sans les cinq champs de limite (chaîne 12) reçoit 4096, 2097152, 4194304, 18432 et 0. max_proof_bytes valait 8 388 608 sur les chaînes 13 à 15.
| field | meaning |
|---|---|
envelope_bytes | 1860 lorsque le genesis fixe chaque enveloppe créatrice de note à exactement cette longueur, le format qui porte un mémo ; null sur un genesis sans ce champ (chaînes 14 à 17), où un wallet scelle l’enveloppe de 1 348 octets sans mémo. Aucune autre valeur n’existe |
hardening_v6 | true lorsque le genesis applique les règles de validité v0.6. Un wallet prouve alors un appel sur l’entrée publique du programme suivie de la liaison d’appel de la transaction, et non sur l’entrée publique seule : les notes du bundle de frais sont choisies d’abord, l’appel est prouvé ensuite, le bundle en dernier. Une chaîne avec ce drapeau refuse l’ancienne preuve, une chaîne sans lui refuse la nouvelle |
hc_auth | le guest d’authentification que fixe le genesis, en hexadécimal, ou null. S’il est défini, hc_bundle est le guest de bundle v3 et chaque bundle porte auth_commit et une preuve d’authentification : le wallet donne au guest de bundle nk et un sel neuf, et prouve le guest d’authentification sur la clé de dépense |
gas_metering | "circuit" sur une chaîne dont le genesis comporte une section gas (le compteur dans le circuit) ; "header" sur une chaîne qui n’en a pas lorsque le nœud applique sa propre politique de gas (--gas-price, --byte-price) ; null dans les autres cas |
gas_price, byte_price | unités par gas, et unités par KiB de preuve et d’enveloppe d’entrée d’un appel, sous forme de chaînes décimales. Sous une section gas, ce sont des données de consensus, identiques sur chaque nœud : avec gas.dynamic, comme sur la chaîne 20, les prix courants de la tête de chaîne, qui varient à chaque bloc selon son remplissage, ne descendent jamais sous les planchers du genesis (100 et 800) et ne dépassent jamais les plafonds ; sans lui, les prix fixes de la section. Sans section, ce sont la politique propre à ce nœud, et deux nœuds peuvent répondre différemment |
bundle_gas_limit | le gas forfaitaire que toute preuve de bundle doit déclarer, tiré de la section gas du genesis ; null sans section |
adjust_bps | la plus forte variation de prix en un bloc du contrôleur dynamique, en points de base ; null sur une chaîne sans gas.dynamic |
max_gas_price, max_byte_price | v0.6.8 : les plafonds au-dessus desquels le contrôleur ne relève jamais un prix, chaînes décimales ; "10000" et "80000" sur la chaîne 20, null lorsque le genesis n’en fixe aucun (chaînes 18 et 19) |
byte_load | v0.6.8 : "paying" lorsque seuls la preuve et l’enveloppe d’entrée d’un appel font bouger byte_price, de sorte qu’un bloc de transferts n’en déplace aucun, comme sur la chaîne 20 ; null lorsque les octets de chaque transaction comptent (chaînes 18 et 19) |
admission_by_vote | v0.6.8 : indique si un bond qui enregistre une nouvelle clé de validateur exige d’abord le vote de l’ensemble des validateurs (rand_getAdmitted) ; true sur la chaîne 20 |
testnet | v0.6.8 : le marqueur testnet du genesis, qui permet à un faucet de coexister avec une section bridge ; true sur la chaîne 20, false sur toutes les chaînes antérieures |
slashing | v0.6.8 : { "equivocation_bps", "jail_epochs" }, ce que coûte une équivocation du leader, ou null ; null sur la chaîne 20 |
binding_domain | v0.6.8 : 0 lorsque la liaison d’une transaction et chaque message d’action signé ne lient que l’identifiant de chaîne (chaînes 14 à 19), 1 lorsqu’ils portent aussi le hash du genesis, de sorte qu’une preuve ou une signature produite pour une chaîne n’est valide sur aucune autre (chaîne 20). Un wallet décide de la forme à prouver d’après l’identifiant de chaîne, jamais d’après cette réponse : le wallet rand v0.6.8 applique la forme à identifiant de chaîne aux chaînes 14 à 19 et la forme liée au genesis à tout autre identifiant, et ne lit ce champ que pour refuser tôt. Un wallet antérieur à v0.6.8 ne peut pas envoyer sur la chaîne 20 |
proof_window_blocks | v0.6.8 : l’ancienneté maximale, en blocs, de l’ancre d’un bundle et le retard maximal de son time par rapport à la hauteur, une seule fenêtre pour les deux ; 1024 sur la chaîne 20, null (256) sur les chaînes jusqu’à 19. Un wallet dont le prover est lent ou délégué y lit combien de temps une preuve peut prendre |
program_state | v0.6.8, RPL-2 : la section program_state du genesis et ses limites d’invoke, ou null, auquel cas tout invoke est refusé et aucun jeton de programme ne peut être enregistré. cell_fee est le nombre d’unités de RAND (chaîne décimale) dont le plancher de frais d’un invoke augmente par cellule qu’il crée, c’est-à-dire l’écriture d’une valeur non nulle dans une cellule qui se lit comme des zéros ; réécrire, supprimer ou écrire des zéros ne coûte rien. max_reads, max_writes et max_payouts (pays et mints ensemble) sont des plafonds stricts. En pratique, c’est la règle de segment qui limite en premier : les mots de contexte de la transition (11 + 16 par lecture ou écriture + 3 par paiement) doivent tenir à côté de l’entrée publique du programme et des 8 mots de liaison sans agrandir la table publique du zkVM ; pour un programme sans entrée publique, cette table compte 128 lignes, ce qui laisse 119 mots de contexte |
Un nœud antérieur à un champ répond sans lui. Rien dans cette réponse n’est authentifié, si bien qu’un wallet ne retient pas envelope_bytes sur la seule parole du nœud : le wallet rand scelle l’enveloppe héritée sur les identifiants de chaîne 14 à 17, quelle que soit la réponse du nœud, et les autres clients devraient faire de même.
rand_getReceipt
Paramètres [tx_hash]. Résultat null tant que l’appel n’est pas validé, puis { "tx", "program", "tier": 14, "outputs": [1, 0, 25, 0, 0, 0, 0, 0], "height": 17, "index": 0, "h_in": "9c0e…7f", "h_pub": null }. Pas de champ effect. h_pub est le condensat de l’entrée publique du programme fixée au déploiement, celle contre laquelle la preuve de l’appel a été vérifiée, null pour un programme sans entrée publique. Un invoke (v0.6.8) a le reçu d’un appel, et rand_getReceipts ainsi que le topic receipts le transmettent tel quel.
rand_getReceipts
Paramètres [program_id, from_height, to_height, limit?]. Résultat { "receipts": [ … ], "next_height": 2051 } : les reçus de program_id avec from_height <= height <= to_height, par hauteur puis par indice, chacun exactement l’objet de rand_getReceipt. limit vaut 256 par défaut, ne peut pas dépasser 256 et ne coupe jamais une hauteur : dès qu’une page contient limit reçus, elle sert encore le reste de cette hauteur. next_height est la première hauteur non servie, ou null lorsque to_height a terminé la page. -32602 si to_height est inférieur à from_height.
rand_getCallEnvelope
Paramètres [tx_hash]. Résultat null, ou { "tx", "h_in", "kem_ct", "to_sender", "to_auditor", "body" } en hexadécimal — la transcription d’entrée scellée de l’appel, servie telle quelle ; le nœud ne détient aucune clé qui l’ouvre. null lorsque l’appel n’a rien publié (--no-envelope), que la transaction n’est pas un appel, ou qu’il n’existe aucun reçu.
Staking et offre
rand_getValidators
Paramètres []. Résultat : une ligne par entrée du registre, par ordre d’adresse, les montants étant des chaînes décimales :
[{ "address": "…", "stake": "1000000000000",
"pending": [{ "release_epoch": 41, "amount": "5000000000" }],
"rewards": "4000000", "payout": "rand1…", "nonce": 3, "active": true, "jailed_until": null }]
jailed_until (v0.6.8) est la première époque à partir de laquelle une clé sanctionnée par slashing peut siéger de nouveau dans un ensemble (18446744073709551615 pour toujours), null lorsqu’elle n’est pas emprisonnée. La chaîne 20 n’a pas de staking.slashing, elle y vaut donc toujours null.
rand_getAdmitted
v0.6.8. Paramètres []. Résultat { "admission_by_vote": true, "max": 256, "admitted": ["<address>", …] } : les clés que l’ensemble de validateurs a admises par un vote AdmitValidator et qui ne se sont pas encore enregistrées, dans l’ordre des adresses. Sur une chaîne dont le genesis définit staking.admission_by_vote, comme celui de la chaîne 20, un Bond qui enregistre une nouvelle clé doit figurer parmi elles. max est le nombre que le registre conserve à la fois. Sur une chaîne sans ce drapeau, admission_by_vote vaut false, la liste est toujours vide et un enregistrement ne requiert aucun vote. La chaîne 20 a répondu "admitted": [] le 2026-10-01.
rand_getEpoch
Paramètres []. Résultat { "epoch": 41, "epoch_blocks": 1000, "next_set": ["…", "…"] } ; next_set est une projection.
rand_getSupply
Paramètres []. Résultat :
{ "height": 1998,
"genesis_deposited": "…", "genesis_staked": "…", "faucet_minted": "…",
"faucet_epoch": "…", "faucet_minted_in_epoch": "…",
"withdraw_deposited": "…", "fees_paid": "…", "burned": "…",
"subsidised": "…", "sealed_blocks": "…", "aggregator_bonds": "…", "slashed": "…",
"registration_fees_burned": "…",
"vesting_issued": "…", "vesting_released": "…", "vesting_in_register": "…", "vesting_locked": "…",
"program_rand_out": "…", "program_rand_held": "…",
"pool_value": "…", "register_total": "…", "total_supply": "…", "invariant_holds": true }
subsidised, sealed_blocks, aggregator_bonds et slashed ne bougent que sur une chaîne à agrégation. faucet_epoch et faucet_minted_in_epoch sont le compteur par époque du faucet, comparé à staking.faucet_budget_per_epoch (état de consensus sur la chaîne 20). Les quatre champs vesting_* sont les compteurs du vesting au genesis, tous à "0" sur une chaîne sans section vesting, ce qui est le cas de la chaîne 20. program_rand_out et program_rand_held (v0.6.8, RPL-2 ; "0" sans section program_state) sont les RAND que les invocations ont versés hors des coffres de programme sous forme de notes (côté pool, inclus dans pool_value) et ce que les coffres détiennent encore (côté registre, inclus dans total_supply) ; ils sont entrés par le burn_r d’une invocation, ils figurent donc dans burned côté pool. registration_fees_burned est la somme Σ des frais d’enregistrement de tokens brûlés en vertu du tokens.burn_registration_fee du genesis, que la chaîne 20 active ; invariant_holds le soustrait, avec slashed, de ce que la chaîne a émis. Supply explique l’identité.
rand_getEmission
Paramètres []. Résultat { "inflation": "0", "subsidy": null, "faucet": true }. inflation vaut toujours "0". subsidy est le barème de subvention de l’agrégation de blocs (current, base, halving_blocks, sealed_blocks, next_halving_at) et vaut null sur une chaîne sans section aggregation, ce qui est le cas de la chaîne 20. faucet reprend la valeur de rand_status.
Bridge
Les quatre répondent sur toute chaîne ; sur une chaîne sans section bridge, l’état est { "enabled": false } et le registre est []. Le genesis de la chaîne 20 en comporte une : l’ensemble de gardiens 1 (guardian_set_index 1) avec huit gardiens, huit gardiens PQ, une clé de pause, burn_sequence 13 au genesis, un plancher anti-rejeu (min_inbound_sequence 3 pour les chaînes sources 2, 3 et 4, et 7 pour la chaîne source 5), des rules_v2 du bridge avec un plafond global de mint de 4 000 zUSD par fenêtre de 24 heures, tous backings confondus, et deux groupes nouveaux de la v0.6.8 : rotation (la rotation des gardiens PQ ou de la clé de pause exige une preuve de possession par les nouvelles clés et prend effet 86 400 s après son engagement) et fees (10 bps de chaque dépôt et 10 bps de chaque burn, conservés sur Rand sous forme de note zUSD au profit d’une adresse de trésorerie). La chaîne 19 avait burn_sequence 8, des planchers de 1 et 4, et aucun des deux groupes. Sa section tokens comporte un registration_fee de 1 RAND (brûlé), un mint_cap_per_day par backing de 10000000000000 (100 000 zUSD à huit décimales) et liste zUSD lui-même : repris de la chaîne 14 au genesis, même identifiant d’actif, indice 1, sept backings.
rand_getBridgeState
Paramètres []. Résultat { "enabled": false }, ou :
{
"enabled": true,
"emitter": "01…",
"emitters": { "2": "02…" },
"guardian_set_index": 0,
"guardians": ["aabb…"],
"pq_guardians": ["…"],
"mint_paused": false,
"pause_nonce": 0,
"list_nonce": 0,
"pause_key": "…",
"registration_fee": "1000000000",
"burn_sequence": 1,
"rotation_nonce": 0,
"rules_v2": { "global_mint_cap_per_window": "…", "cap_window_secs": 86400,
"global_minted_in_window": "…", "global_mint_headroom": "…" },
"min_inbound_sequence": { "2": 2, "3": 2, "4": 2, "5": 2 },
"rotation_rules": { "delay_secs": 86400, "needs_possession": true },
"pending_rotations": [],
"fees": { "mint_bps": 10, "burn_bps": 10, "recipient": "rand1…" },
"assets": [ … ]
}
| champ | signification |
|---|---|
emitter | l’adresse de l’émetteur sortant de cette chaîne, 32 octets en hex |
emitters | identifiant de la chaîne source → l’adresse de l’émetteur reconnu pour elle |
guardians | les adresses de 20 octets de l’ensemble courant, en hex |
pq_guardians | l’ensemble PQ : clés publiques Dilithium2 (1 312 octets), en hex, alignées par indice sur l’ensemble de gardiens courant (l’ensemble 1 sur la chaîne 20). Chaque BridgeAttest porte un quorum de cosignatures de cet ensemble. Une rotation de l’ensemble de gardiens ne le déplace jamais ; seule une rotate_pq_guardians le fait, sur une chaîne rules_v2 |
mint_paused | tant que la valeur est true, toute attestation de transfert est refusée avec bridge minting is paused ; les burns et les rotations restent ouverts |
pause_nonce | ce que doit porter la prochaine pause ou reprise ; les deux l’incrémentent |
list_nonce | ce que doit porter le prochain register_bridged_token ou list_backing ; les deux l’incrémentent |
pause_key | l’unique clé Dilithium2 habilitée à suspendre le mint, en hex. Elle ne peut que suspendre : une reprise exige le quorum des gardiens PQ |
registration_fee | ce que doit un RegisterBridgedToken au-delà de la base du lot, en unités RAND, chaîne décimale |
burn_sequence | nombre de messages sortants émis jusqu’ici |
rotation_nonce | ce que doit porter la prochaine rotate_pq_guardians / rotate_pause_key |
rules_v2 | le plafond global de mint et sa fenêtre glissante, ou null sur une chaîne sans règles v2 du bridge. Sous ces règles, le plafond par backing est lui aussi une fenêtre glissante, et un référencement est refusé tant que le mint est suspendu. Depuis la v0.6.8, il porte aussi global_minted_in_window et global_mint_headroom, chaînes décimales |
min_inbound_sequence | le plancher anti-rejeu du genesis : identifiant de la chaîne source → la plus basse séquence que peut porter un transfert depuis cette chaîne, nombres simples ; null sur une chaîne qui n’en a pas (chaîne 15 et antérieures). Une bridge_attest en dessous est refusée avec BelowReplayFloor, un verdict définitif. Un relayeur le lit pour écarter un verrouillage que la chaîne dont celle-ci est issue a déjà minté |
rotation_rules | v0.6.8 : { "delay_secs", "needs_possession" } issu du bridge.rotation du genesis, ou null. Sous ces règles, une rotation est l’action _v2, qui porte les signatures de possession des nouvelles clés, et attend delay_secs avant de prendre effet |
pending_rotations | v0.6.8 : les rotations engagées et pas encore en vigueur, chacune { "kind": "pq_guardians", "new_pq_guardians", "effective_at_secs" } ou { "kind": "pause_key", "new_pause_key", "effective_at_secs" } ; null sans rotation_rules. Une cancel_rotation signée par la clé de pause courante en annule une |
fees | v0.6.8 : { "mint_bps", "burn_bps", "recipient" } issu du bridge.fees du genesis, ou null (chaînes 14 à 19). La chaîne conserve cette part de chaque dépôt et de chaque burn, arrondie à l’unité de libération entière inférieure du backing, sous forme de note zUSD au profit de recipient. Un dépôt crédite le brut moins les frais ; le corps signé d’un burn libère son montant moins les frais. Un portefeuille le lit pour évaluer son propre dépôt et, quand il est recipient, pour reconstruire ses notes de frais |
assets | les lignes de rand_getAssets |
Aucun solde : la valeur bridgée est faite de notes. Il n’y a plus de next_index : un token bridgé est référencé, sous un indice que son enregistrement a déjà fixé, avant de pouvoir être déposé ; il n’y a donc plus d’indice à prédire. Lisez l’indice d’un token référencé dans assets ou rand_getTokens.
rand_getAssets
Paramètres []. Résultat : le registre d’actifs du bridge, par indice croissant, ou [] :
[{ "index": 1, "chain": 2, "token": "aaaa…", "asset_id": "…", "decimals": 6, "locked": "600",
"mint_cap_per_day": "10000000000000", "minted_today": "1000", "mint_day": 20350,
"minted_in_window": "1000", "mint_window_secs": 86400, "mint_headroom": "390000000000" }]
Depuis la v0.6.8, chaque ligne de backing, ici et partout où les backings sont servis (rand_getBridgeState.assets, les backings de rand_getToken, rand_getTokens et rand_getTokenSupply), porte minted_in_window (une chaîne décimale) et mint_window_secs, tous deux null sur une chaîne à compteur journalier, ainsi que mint_headroom : le plus grand dépôt vers ce backing que les plafonds admettent à l’heure de bloc de la tête, soit le plafond par backing moins ce qu’il a déjà minté et, sous les règles v2, pas plus que ce qu’il reste à la fenêtre de l’ensemble du registre. Lisez mint_headroom, et non mint_cap_per_day − minted_today, qui ignore le plafond global. Sous les règles v2, comme sur la chaîne 20, minted_today est le décompte de la fenêtre glissante, le chiffre auquel le plafond par backing est comparé ; la description du compteur journalier ci-dessous est celle de la chaîne 14.
Une ligne par backing (coin source), de sorte qu’un token à sept backings occupe sept lignes sous un même index. index est le mot asset que porte une note de cet actif ; l’indice 0 est RAND et n’est jamais dans le registre. decimals est le nombre de décimales source du backing. locked, mint_cap_per_day et minted_today sont des chaînes décimales dans les unités à huit décimales du token lui-même, jamais celles de RAND ; mint_day est un entier simple, un numéro de jour UTC. mint_cap_per_day est le tokens.mint_cap_per_day du genesis, le maximum qu’un backing peut minter par jour UTC de l’heure de bloc. minted_today est ce que le backing a minté le mint_day, le jour UTC (timestamp_ms / 86,400,000) du bloc de tête, de sorte qu’un compteur hérité d’un jour antérieur vaut "0" dès que la tête passe minuit. Un dépôt au-delà du plafond est refusé avec MintCapExceeded et redevient admissible le lendemain. chain et token sont l’identité sur le réseau sur laquelle les gardiens signent ; asset_id est le blake3 des deux, ce que calcule rand_bridgeAssetId.
rand_bridgeAssetId
Paramètres [token_chain, token_address] (entier, 32 octets en hex). Résultat : l’identifiant de l’actif, 64 caractères hex. Pure arithmétique.
rand_getBridgeBurn
Paramètres [sequence]. Résultat null ou { "sequence": 0, "body_hex": "…", "digest": "…", "tx": "…", "height": 2, "amount": "…", "release_amount": "…", "fee": "…" }.
Les trois derniers sont de la v0.6.8, sur toute chaîne, en chaînes décimales dans les unités à huit décimales du token. release_amount est le montant que porte le corps signé, lu dans body_hex lui-même : ce que le contrat source libère, et sur quoi il prélève les frais du relayeur du corps. fee est les frais de bridge que la chaîne a conservés sous forme de note de trésorerie, et amount ce que le lot a brûlé, soit release_amount + fee. Sans bridge.fees (chaînes 14 à 19), fee vaut "0". Séquence 13 sur la chaîne 20, un burn de 0,999 zUSD : "amount": "99900000", "fee": "99900", "release_amount": "99800100".
Tokens (RPL)
Le registre de tokens RPL : tous les tokens, bridgés et natifs. Tokens explique le standard.
rand_getTokens
Paramètres [from_index, limit], tous deux facultatifs (0 et 1000 ; limit est borné à 1 000). Résultat : le registre par indice croissant à partir de from_index, lu par plage à partir de cet indice :
{ "enabled": true, "registration_fee": "1000000000", "next_index": 3, "max_tokens": 4096,
"burn_registration_fee": true, "bound_note_value": true,
"tokens": [
{ "index": 2, "id": "<64 hex>", "id_text": "rpl1…", "name": "Test Coin", "symbol": "TST",
"decimals": 6,
"authority": { "kind": "key", "key": "<Dilithium2 key, hex>", "address": "<base58>" },
"mint_nonce": 1, "total_supply": "5700", "registered_at": 3 }
] }
Sur une chaîne sans section tokens : { "enabled": false, "tokens": [] }. index est le mot asset que porte une note du token (0 est RAND et n’est jamais listé). id est l’identifiant d’actif du token et id_text sa forme textuelle avec somme de contrôle : bech32m, HRP rpl, sur les 32 octets de l’identifiant, soit 62 caractères. authority est l’une des valeurs suivantes :
kind | autres champs |
|---|---|
none | — (offre fixe, ou autorité abandonnée) |
key | key, address |
bridge | backings : [{ "chain", "token", "decimals", "locked", "mint_cap_per_day", "minted_today", "mint_day" }], les champs de rand_getAssets, une ligne par collatéral |
program | program |
Les offres, locked, mint_cap_per_day, minted_today et registration_fee sont des chaînes décimales (en unités de RAND pour registration_fee, en unités du token pour les autres) ; next_index, mint_day et max_tokens sont des nombres. max_tokens est le plafond de genèse du registre (4 096 sur la chaîne 20 ; null s’il n’y en a pas), au-delà duquel un enregistrement est refusé avec RegistryFull. burn_registration_fee indique si les frais d’un enregistrement sont brûlés plutôt que versés au proposant ; bound_note_value indique si un mint ou un dépôt égal ou supérieur à 2⁶³ relève d’une règle de validité (l’admission en refuse un sur toutes les chaînes, quoi qu’il arrive). Les trois sont définis sur la chaîne 20. Une page plus courte que limit est la dernière.
Une autorité program ({ "kind": "program", "program": "<id>" }) ne peut être enregistrée que sur une chaîne dotée d’une section program_state, comme l’est la chaîne 20 (v0.6.8) : le token démarre avec une offre nulle, et seuls les invokes de ce programme peuvent le mint ou le burn.
Un wallet résout un identifiant de token via cette liste, jamais via rand_getToken : l’actif d’un transfert est privé sur la chaîne, et lire l’ensemble du registre coûte la même chose quel que soit le token visé.
rand_getToken
Paramètres [token] : un index du registre (un nombre ou une chaîne décimale), l’identifiant en 64 caractères hexadécimaux (avec ou sans 0x, quelle que soit la casse) ou la forme textuelle rpl1… (tout en minuscules ou tout en majuscules). Résultat : la ligne de ce token dans rand_getTokens, ou null. Un identifiant mal formé — somme de contrôle erronée, autre HRP, longueur incorrecte, casse mixte, non hexadécimal — donne -32602, de sorte qu’une faute de frappe ne désigne jamais un autre token. Une recherche par token indique au nœud quel token intéresse l’appelant ; elle est réservée aux explorateurs et aux lectures ponctuelles.
rand_getTokenSupply
Paramètres [token], mêmes formes. Résultat null, ou :
{ "total_supply": "600",
"backings": [{ "chain": 2, "token": "<32 bytes hex>", "decimals": 6, "locked": "600",
"mint_cap_per_day": "10000000000000", "minted_today": "1000", "mint_day": 20350 }] }
backings est vide pour un token natif ; pour un token ponté, les montants locked totalisent total_supply. La même remarque de confidentialité que pour rand_getToken s’applique.
Agrégation de blocs (inactive sur la chaîne 20)
Ces méthodes existent dans le nœud pour les chaînes dont la genèse comporte une section aggregation. Sur la chaîne 20, le registre est vide et rien n’est jamais descellé.
| méthode | paramètres | résultat |
|---|---|---|
rand_getAggregate | [hash] | les champs covers, aggregator, subsidy, proving_share, n, height de l’agrégat de scellement ; null pour toute autre transaction |
rand_getAggregators | [] | le registre des agrégateurs : address, bond, payout, nonce, unbonding par ligne |
rand_getUnsealed | [from, limit] | { "bundles": [{ "hash", "height", "excess" }], "next_from" }, les bundles qu’un agrégateur peut encore couvrir |
Un Aggregate est soumis via rand_sendTransaction, comme tout le reste.
Acquisition progressive de la genèse (inactive sur la chaîne 20)
Ces méthodes existent dans le nœud pour les chaînes dont la genèse comporte une section vesting. Aucune chaîne n’en a encore : sur la chaîne 20, chacune répond { "enabled": false }. Le point d’accès public les relaie depuis le 2026-10-01.
| méthode | paramètres | résultat |
|---|---|---|
rand_getVesting | [id] ou [id, at_ms], l’identifiant de l’entrée en 64 caractères hexadécimaux | l’entrée : sa classe, son bénéficiaire, son calendrier (amount, start_ms, cliff_ms, linear_ms, step_ms), ce qui a été réclamé, révoqué et immobilisé en bond, ainsi que vested_now, claimable_now, unvested_now à as_of_ms (par défaut l’heure du bloc de tête) ; null pour un identifiant inconnu. Depuis v0.6.8, une entrée révocable liste revokers, le threshold de signataires dont une révocation a besoin et le treasury que paie une révocation (ils remplacent le revoker unique), et l’entrée porte revoke_nonce et locked_now |
rand_getVestingSummary | [] | { "enabled": true, "height", "as_of_ms", "entries", "issued", "released", "locked", "classes": [ … ] } : totaux par classe, sans clé ni propriétaire |
rand_getVestingSchedule | [from_ms, to_ms, step_ms], au plus 1 000 points | { "enabled": true, "issued", "points": [{ "t_ms", "locked" }] } : le total verrouillé du registre dans le temps |
Les montants sont des chaînes décimales. Les quatre actions de vesting sont soumises via rand_sendTransaction.
Abonnements (WebSocket)
ws://127.0.0.1:8545/ ou /ws. Le socket ne sert que rand_subscribe et rand_unsubscribe et répond -32601 à tout le reste. Trois sujets, des flux indépendants sans promesse d’ordre entre eux :
newHeads— la forme derand_getHead, envoyée une fois par bloc validé, dans l’ordre, y compris pendant la synchronisation.viewdésigne ici la vue propre au bloc.receiptsoureceipts <program_id>— une notification par bloc validé qui porte au moins un reçu d’appel correspondant :{ "height", "hash", "receipts": [ … ] }, chaque reçu ayant la forme derand_getReceipt.transaction <hash>— une notification, puis le nœud supprime l’abonnement :{ "status": "committed", "height", "index" }, ou{ "status": "rejected", "reason" }pour un refus portant sur les octets propres de la transaction (la limite que connaîtrand_getTransactionStatus). Un refus dépendant de l’état n’est jamais annoncé ; associez donc un délai d’expiration.
{ "jsonrpc": "2.0", "id": 1, "method": "rand_subscribe", "params": ["newHeads"] }
{ "jsonrpc": "2.0", "id": 2, "method": "rand_unsubscribe", "params": ["1"] }
{ "jsonrpc": "2.0", "id": 3, "method": "rand_subscribe", "params": ["receipts", "<program_id>"] }
{ "jsonrpc": "2.0", "id": 4, "method": "rand_subscribe", "params": ["transaction", "<hash>"] }
{ "jsonrpc": "2.0", "id": 1, "result": "1" }
{ "jsonrpc": "2.0", "id": 2, "result": true }
{ "jsonrpc": "2.0", "method": "rand_subscription",
"params": { "subscription": "1", "result": { "height": 1998, "hash": "…", "view": 2251 } } }
Limites : 64 connexions par nœud (la 65e est refusée à la mise à niveau avec 503), 8 abonnements par connexion, 64 KiB par trame client, un ping toutes les 30 s auquel il faut répondre sous 5 s, et 256 entrées de retard par flux — un abonné trop en retard sur un flux qu’il écoute est fermé avec le code 1008, sans mise en tampon ; reconnectez-vous et comblez l’écart avec rand_getCompactBlocks (têtes), rand_getReceipts (reçus) ou rand_getTransactionStatus (une transaction). Un simple GET / sans en-têtes de mise à niveau reçoit 400.
Erreurs
| code | signification |
|---|---|
-32601 | méthode inconnue (y compris les méthodes de compte retirées) |
-32602 | paramètre invalide ou manquant |
-32000 | rejet : une transaction que le mempool a refusée, un appel de faucet soumis à une limite de débit, une construction de témoin qui a attendu plus de 10 s, une lecture de plus de 30 s, une méthode que l’écouteur public ne sert pas, ou un abonnement au-delà du plafond |
-32001 | objet référencé introuvable |
-32010 | élagué : une hauteur inférieure au seuil de rétention de ce nœud ; data.floor le nomme (voir ci-dessous) |
-32603 | erreur interne |
-32600 | requête invalide : corps dépassant la limite de taille, non JSON, lot mal formé, ou notification |
{ "jsonrpc": "2.0", "id": 1, "error": { "code": -32000, "message": "…" } }
Pour rand_sendTransaction, seuls -32000 et -32602 constituent un verdict sur la transaction ; après tout autre code, son sort est inconnu (voir rand_sendTransaction).
Nœuds élagués : erreur -32010
Un nœud démarré avec --prune-history <window> conserve tout le registre (notes, nullifiers, ancres, le registre) mais seulement les <window> derniers de blocs, de transactions et de reçus ; rand_status.prune_floor est la plus basse hauteur qu’il détient encore. Sur la chaîne 20, 24 des 26 validateurs exécutent --prune-history 24h ; les deux archives fonctionnent sans cet indicateur et conservent chaque bloc, et le point d’accès public relaie vers l’une d’elles.
- Par hauteur, sous le seuil (genèse exceptée) :
rand_getBlockByHeight,rand_getFinalitypar hauteur, et une plage derand_getBlocks,rand_getCompactBlocksourand_getReceiptsdescendant sous ce seuil répondent-32010pruned: height h is below this node's retention floor favecdata: { "floor": f }. Interrogez une archive.[0, 0]sert toujours la genèse. - Par hash :
rand_getBlockByHash,rand_getReceipt,rand_getRawTransaction,rand_getCallEnvelopeetrand_getAggregaterépondentnullpour un hash élagué, comme pour un hash qui n’a jamais existé ; seule une archive peut distinguer les deux.rand_getTransactionetrand_checkTransactionrépondent aussinull, et-32010uniquement lorsqu’une ligne de localisation a survécu en nommant une hauteur inférieure au seuil.rand_getTransactionStatusajoutefloorà chaque entréeunknownsur un nœud élagué. - L’analyse par les wallets n’est pas affectée :
rand_getCommitments,rand_getNullifiers,rand_getAnchoret les méthodes de témoin lisent des familles qui ne sont jamais élaguées.
La transaction sur le réseau
Transaction { chain_id: u64, bundle: Option<Bundle>, action: Action }
Bundle { // the hidden-asset bundle (since chain 14)
anchor: Word8, nullifiers: [Word8; 4], commitments: [Word8; 4],
fee: u64, burn_a: u64, burn_r: u64, burn_asset: u32, time: u32,
envelopes: [Envelope; 4], proof: Vec<u8>,
auth_commit: Word8, auth_proof: Vec<u8>, // since chain 17 (v0.6.3): split authorisation's c and
} // its proof; zeros and empty without hc_auth
Envelope { kem_ct: Vec<u8>, to_receiver: Vec<u8>, to_sender: Vec<u8>, body: Vec<u8> }
Action::None
Action::Mint { cm: Word8, pk: Word8, time: u32, r: Word8, envelope: Envelope, amount: u64,
minter: PublicKey, signature: Signature }
Action::Deploy { base_pc: u32, words: Vec<u32>, public: Vec<u32> }
Action::Call { program: Hash, proof: Vec<u8>, input_envelope: Option<CallEnvelope> }
Action::Bond { validator: Address, amount: u64, registration: Option<Registration> }
Action::Unbond { validator: Address, amount: u64, nonce: u64, signature: Signature }
Action::Withdraw { validator: Address, amount: u64, nonce: u64, time: u32, r: Word8,
envelope: Envelope, signature: Signature }
Action::BridgeAttest { attestation: Vec<u8>, recipient: ShieldedAddress, r: Word8, time: u32,
asset: u32, envelope: Envelope, pq_signatures: Vec<PqSignature> }
Action::BridgeBurn { asset: u32, amount: u64, relayer_fee: u64, to_chain: u16,
token: [u8; 32], to: [u8; 32] }
// the five aggregation variants sit here in the enum's declared order
Action::RegisterToken { name: String, symbol: String, decimals: u8, authority: MintAuthority,
initial: Option<InitialMint>, salt: [u8; 32], index: u32 }
Action::TokenMint { asset: u32, amount: u64, recipient: ShieldedAddress, r: Word8, time: u32,
envelope: Envelope, nonce: u64, signature: Signature }
Action::SetAuthority { asset: u32, new: Option<PublicKey>, nonce: u64, signature: Signature }
Action::TokenBurn { asset: u32, amount: u64 }
Action::PauseMints { nonce: u64, signature: Signature } // bundle-less, fee-less
Action::UnpauseMints { nonce: u64, pq_signatures: Vec<PqSignature> } // bundle-less
Action::RegisterBridgedToken { name: String, symbol: String, salt: [u8; 32], chain: u16,
token: [u8; 32], decimals: u8, nonce: u64,
pq_signatures: Vec<PqSignature> }
Action::ListBacking { token_index: u32, chain: u16, token: [u8; 32], decimals: u8, nonce: u64,
pq_signatures: Vec<PqSignature> }
Action::RotatePqGuardians { new_pq_guardians: Vec<PublicKey>, nonce: u64,
pq_signatures: Vec<PqSignature> } // bundle-less, fee-less
Action::RotatePauseKey { new_pause_key: PublicKey, nonce: u64,
pq_signatures: Vec<PqSignature> } // bundle-less, fee-less
// the four vesting variants (ClaimVested, RevokeVesting, BondVested, UnbondVested) follow, tags 24 to 27;
// since v0.6.8 RevokeVesting carries `signatures: Vec<RevokerSignature>` in place of one `signature`
Action::AdmitValidator { candidate: PublicKey, signatures: Vec<(PublicKey, Signature)> } // tag 28, v0.6.8
Action::SlashEquivocation { first: Box<SignedHeader>, second: Box<SignedHeader> } // tag 29
Action::RotatePqGuardiansV2 { new_pq_guardians: Vec<PublicKey>, possession: Vec<Vec<u8>>,
nonce: u64, pq_signatures: Vec<PqSignature> } // tag 30
Action::RotatePauseKeyV2 { new_pause_key: PublicKey, possession: Vec<u8>, nonce: u64,
pq_signatures: Vec<PqSignature> } // tag 31
Action::CancelRotation { kind: u8, nonce: u64, signature: Signature } // tag 32
Action::Invoke { program: Hash, proof: Vec<u8>, input_envelope: Option<CallEnvelope>,
transition: Transition } // tag 33, RPL-2
MintAuthority = None | Key(PublicKey) | Bridge { backings: Vec<Backing> } | Program(Hash)
InitialMint { amount: u64, recipient: ShieldedAddress, r: Word8, time: u32, envelope: Envelope }
Transition { reads: Vec<Cell>, writes: Vec<Cell>, inflow: Inflow, pays: Vec<Payout>, mints: Vec<Payout> }
Cell { key: Word8, value: Word8 } // keys strictly ascending in each list
Inflow = None | Deposit | Burn // what the bundle's burn_a of burn_asset is to the program
Payout { asset: u32, amount: u64, recipient: ShieldedAddress, r: Word8, envelope: Envelope }
Registration { public_key: PublicKey, payout: ShieldedAddress, signature: Signature }
CallEnvelope { kem_ct: Vec<u8>, to_sender: Vec<u8>, to_auditor: Vec<u8>, body: Vec<u8> }
PqSignature { index: u8, signature: Vec<u8> } // one guardian's Dilithium2 co-signature, by set index
Configuration par défaut de bincode : entiers de largeur fixe, préfixes de longueur de 8 octets, balises d’énumération u32 dans l’ordre de déclaration. Les cinq variantes d’agrégation suivent BridgeBurn et précèdent RegisterToken dans l’énumération (Transactions) ; la chaîne 20 ne les admet jamais, pas plus que les quatre variantes de vesting ni SlashEquivocation, mais un encodeur qui code en dur les numéros de balise doit tout de même les compter.
Les deux derniers champs du bundle sont arrivés avec v0.6.3 et la chaîne 17. bincode est positionnel : un encodeur écrit pour la chaîne 16 ou antérieure ne produit donc pas une transaction que cette chaîne décode, et chaque identifiant de transaction a changé avec eux : le domaine de l’identifiant est désormais rand-txid-3, qui lie auth_commit et le condensé de la preuve d’autorisation. Un client qui recalcule les identifiants localement doit suivre. Sur la chaîne 20, chaque bundle porte une preuve d’autorisation, qu’elle soit auto-prouvée ou déléguée. v0.6.8 ne change rien à la disposition de Transaction ou de Bundle ; elle ajoute les actions 28 à 33.
Chaque transaction porte au plus un bundle. Un Bond porte un bundle dont burn_r est égal à son amount, avec burn_a et burn_asset à 0. Un BridgeBurn ou un TokenBurn brûle via le bundle : burn_asset doit être l’asset de l’action (jamais 0), burn_a l’amount de l’action, et burn_r 0, tandis que fee est payé en RAND depuis les emplacements 2 à 3. Toute autre action ne brûle aucun token, et RAND n’est jamais brûlé que par burn_r : un bundle avec burn_asset à 0 et burn_a non nul est refusé partout. Le r d’un BridgeAttest est dérivé, non choisi : l’admission exige blake3("rand-deposit-r-1" ‖ mu) calculé sur le condensat que les guardians ont signé, et refuse toute autre valeur (WrongDepositBlinding, un refus définitif). Un token ponté est listé — à la genèse, ou par un RegisterBridgedToken / ListBacking — avant que toute attestation le concernant puisse être admise ; l’attestation d’une pièce non listée est refusée avec UnlistedToken.
Chaque preuve de bundle est produite sur la transaction dans laquelle elle voyage, et vérifiée par rapport à elle : la transaction entière, preuves mises à blanc, liée à l’identifiant de chaîne et, sur la chaîne 20 (binding_domain: 1), au hash de genèse, sous de nouvelles étiquettes. Sur les chaînes 14 à 19, la liaison se faisait par l’identifiant de chaîne seul ; une preuve produite pour la chaîne 19 ne se vérifie donc pas sur la chaîne 20, et un wallet antérieur à la v0.6.8 ne peut pas y envoyer. Sur les chaînes 18 à 20, un bundle sans ses deux preuves s’encode en 7 936 octets et une transaction de transfert sans elles en 7 949 (5 848 et 5 861 sur les chaînes 14 à 16, avec des enveloppes de 1 348 octets et sans champs d’authentification). Un transfert sur le réseau pèse environ 2,85 Mo au profil de production, presque entièrement de la preuve : une preuve de bundle d’environ 1,49 Mo et une preuve d’authentification d’environ 1,36 Mo. Il pesait environ 1,43 Mo avant l’autorisation scindée.