Rand Protocol
Documentation
Guides

Preuves

Comment le wallet produit une preuve de bundle, ce qu'elle coûte en temps et en octets, comment un nœud la vérifie, ce qu'est un ensemble de contraintes et pourquoi en modifier un exige un hard fork - ainsi que l'agrégation de blocs, le changement à venir dans la manière dont les preuves parviennent à un bloc.

Utilisez cette page pour dimensionner une machine de preuve, pour comprendre pourquoi un transfert prend plus d’une minute et pourquoi c’est l’espace de bloc, et non le calcul, qui borne le débit, et pour savoir ce qui casse lorsque le système de preuve change. Tout ce qu’un validator accepte est adossé à une preuve qu’il a lui-même vérifiée.

Ce qu’énonce une preuve

Une seule relation, pour chaque programme que la chaîne exécute :

Ce programme RV32IM, démarré à base_pc, exécuté sur un vecteur d’entrée privé engagé par H_IN et un segment d’entrée public engagé par H_PUB, s’est arrêté en au plus 2ᵗ − 1 cycles, a dépensé au plus GAS_LIMIT de gas et a publié ces huit mots de sortie.

Le programme est identifié par son condensat en circuit hc ; le vérificateur ne voit jamais un mot d’instruction. Un transfert protégé est cette relation appliquée au guest de bundle à actifs cachés, un programme 4 entrées, 4 sorties assemblé à la main, dont le hc est épinglé dans le fichier de genèse sous le nom hc_bundle. La chaîne 20 épingle le guest v3 (guests::bundle_hidden_v3, 60af094a…6e3fce, depuis la chaîne 17). Les chaînes 14 et 15 épinglaient la v1 (83d3a370…cd0ef8) et la chaîne 16 la v2 sans branchement (651043e2…839b). Depuis la chaîne 17, chaque bundle porte aussi une seconde preuve, celle du guest d’authentification (guests::auth, hc_auth de la genèse, 1e4e347f…5639c1) : la preuve du bundle prend la clé de visualisation, et la preuve d’authentification montre que l’expéditeur détient la clé de dépense qui s’y rattache. C’est l’autorisation scindée, et c’est ce qui permet à un prouveur apparié qui ne détient qu’une clé de visualisation de produire la preuve du bundle. Le guest à actifs cachés a remplacé le guest bundle 2 entrées, 2 sorties des chaînes jusqu’à la 13, qui ne subsiste dans le fullnode que pour les tests et qu’aucune chaîne n’épingle. Un appel confidentiel est la même relation appliquée à un programme déployé (Programmes).

Comment le wallet prouve un bundle

rand send <rand1…> <amount> effectue le travail privé en local, et seuls les preuves, les engagements, les nullifiers et les enveloppes scellées parviennent à la chaîne :

  1. Parcourir l’arbre avec la clé de visualisation et choisir les entrées : au plus deux notes RAND couvrant le montant plus les frais dans les emplacements 2–3 et, pour un transfert ou une destruction de jeton, au plus deux notes du jeton dans les emplacements 0–1, les notes RAND couvrant seules les frais. Un emplacement d’entrée inutilisé est un factice : montant 0, détenu par le wallet, son échec d’appartenance masqué en circuit, nullifier tout de même publié. Depuis le guest v2, le guest est sans branchement : le chemin d’un factice est haché sur les 32 niveaux comme celui d’une vraie note.
  2. Récupérer l’ancre de tête (rand_getAnchor) et le témoin de Merkle à 32 niveaux de chaque entrée réelle (rand_getWitness(index)), en vérifiant la racine renvoyée par rapport à l’ancre.
  3. Construire quatre sorties : la note du destinataire et la note de monnaie dans le groupe qui déplace la valeur, présentes même à zéro, et des factices de montant zéro ailleurs, scellés à une clé jetable.
  4. Sceller chaque sortie dans une enveloppe (ML-KEM-768 vers le destinataire, plus la clé de transaction enveloppée sous l’ovk du wallet), assembler la transaction complète avec son action, et en prendre la liaison : blake3("rand-tx-bind-1" || …) sur la transaction dont les preuves sont vidées, huit mots (Transactions). Sur la chaîne 20, dont la genèse fixe binding_domain: 1, c’est blake3("rand-tx-bind-2" || …) avec le condensat de genèse en premier, puis l’identifiant de chaîne, de sorte qu’une preuve produite pour une chaîne n’est valide sur aucune autre, même une chaîne de même identifiant. Un wallet antérieur à la v0.6.8 prend l’ancienne liaison, et la chaîne 20 refuse ses preuves.
  5. Autoriser : tirer un sel frais de 256 bits et prouver le guest d’authentification sur son entrée privée de 16 mots, la clé de dépense et le sel, avec la liaison comme segment d’entrée public, au palier 10. Il dérive nk de la clé de dépense et publie c = H(AUTH, nk ‖ salt), que le bundle porte sous le nom auth_commit.
  6. Prouver le guest de bundle sur son entrée privée de 1 212 mots (la clé de visualisation nk, quatre emplacements d’entrée avec leurs chemins, l’ancre, quatre sorties, fee, burn_a, burn_r, l’actif privé A, time, le sel) avec la même liaison comme segment d’entrée public, au palier 14. La preuve établit la propriété, l’appartenance, que chaque entrée réelle porte l’actif de son emplacement, les quatre nullifiers, le même c, et les deux équilibres in₀ + in₁ = out₀ + out₁ + burn_a et in₂ + in₃ = out₂ + out₃ + fee + burn_r. C’est la preuve qu’un prouveur apparié peut produire à la place du wallet ; la preuve d’authentification est produite sur la machine du wallet lui-même.
  7. Contrôler le condensat publié de la preuve par rapport à celui calculé à partir du texte en clair, puis soumettre Transaction { chain_id, bundle, action: None }.

L’ordre importe depuis la chaîne 14 : les enveloppes et l’action existent avant les preuves, car les preuves s’y engagent. Pour un appel, la preuve d’appel est produite en premier et figure elle aussi dans la liaison. Sur une chaîne hardening_v6, la preuve d’appel est elle-même produite sur une liaison d’appel de la transaction dont les deux preuves sont vidées (rand-call-bind-1 sur les chaînes 16 à 19, rand-call-bind-2 avec le condensat de genèse sur la chaîne 20), qui couvre les nullifiers du bundle de frais ; elle ne peut donc pas être copiée sous un autre bundle.

proving bundle (tier 14; about a minute on a laptop)…
proved in 98.3s: tier 14, 1423170 bytes
submitted transfer 4f2c…  1.5 RAND out, 98.499 RAND change, fee 0.001 RAND, anchored at height 192

La transcription reprend le format de sortie du wallet tel que le décrit le guide du fullnode à la chaîne 14 ; la taille en octets affichée est celle d’une preuve de bundle au profil de production engagée sur la chaîne 14. Depuis la chaîne 17, le wallet, v0.6.8 sur la chaîne 20 comprise, affiche deux lignes avant celles-ci, proving the spend authorisation on this machine (tier 10; seconds)… et authorisation proved in …: tier 10, … bytes, et formule ainsi la ligne du bundle : proving the bundle (tier 14; about a minute and a half on a laptop)….

La même mécanique paie chaque action, toujours avec exactement un bundle : un déploiement, un appel, un enregistrement de jeton ou une émission de jeton s’appuie sur un auto-transfert de RAND à valeur nulle, un bond sur un bundle qui détruit la mise via burn_r, et une destruction de jeton ou une destruction de pont sur un bundle qui détruit l’actif des emplacements 0–1 (burn_a, burn_asset) et paie les frais en RAND depuis les emplacements 2–3. L’actif que déplace un transfert est un mot du témoin et ne figure dans aucun champ public.

Ce que cela coûte

Mesurés sur le portable de développement (Apple Silicon, build release), les chiffres que consignent les propres documents du fullnode :

guestmotscyclespalierpreuvetaille de la preuve (production, 80 requêtes)
guest de bundle v3 (chaînes 17 à 20)—4 024 cycles et 273 permutations Poseidon2 de marge dans le budget du palier 1414≈ 100 s, pic de 5,7 GB ; 102,5 à 134,0 s mesurées sur la chaîne 18 (2026-09-30)≈ 1,50 MB ; 1 494 151 à 1 504 260 octets mesurés sur la chaîne 18 (2026-09-30)
guest d’authentification (chaînes 17 à 20)—25010≈ 7 s ; 6,6 à 19,9 s mesurées sur la chaîne 18 (2026-09-30)≈ 1,37 MB ; 1 364 940 à 1 375 371 octets mesurés sur la chaîne 18 (2026-09-30)
bundle à actifs cachés v1, 4 entrées 4 sorties (chaînes 14 et 15)—sous le budget du palier 14, avec environ 2 071 cycles (13 %) de reste14≈ 98–100 s, pic de 5,7 GB≈ 1,43 MB ; 1 422 116 et 1 423 170 octets mesurés sur la chaîne 14
bundle, 2 entrées 2 sorties (retiré ; chaînes jusqu’à la 13)3 811≈ 9 16014≈ 100 s (94,6–97,9 s seul ; 114 s chacun pour deux simultanés)1 321 773 octets mesurés sur la chaîne 8
private_payment (un appel)petit< 1 00010≈ 7 s≈ 1,37 MB (fib au palier 10 : 1 367 688 octets sous l’ensemble de contraintes 7 ; 1 298 729 sous l’ensemble 6)
un appel sans table de hachage (registre hors chaîne, chaîne 18)773—14114,8 s1 498 589 octets
un appel avec la table SHA-256 (preuve de passifs, chaîne 18)994—14122,9 s1 911 152 octets
un appel avec la table Keccak (vote anonyme, chaîne 18)——1229,2 s3 346 865 octets

La vérification est le coût du consensus, payé par chaque nœud : environ 16 ms avec une clé de vérification chaude, environ 230 ms la première fois qu’une clé est construite pour une forme donnée. La clé de vérification ne dépend pas du contenu du programme — elle est indexée par (tier, program_log_height, input_log_height, keccak_log_height, sha256_log_height, public_log_height) — de sorte qu’un nœud en construit une par forme et la réutilise ; pour un programme déployé, la clé est calculée une seule fois en arrière-plan au moment du déploiement (environ 2 s sur un portable, 7 s sur un serveur à 2 vCPU). Le bundle 2 entrées 2 sorties de la chaîne 13 et antérieures déclarait la forme program 12, input 10, keccak 0, sha256 0, public 2, mem 16 ; une preuve de bundle de la chaîne 14 déclare public 4, la hauteur des huit mots de liaison, ne porte aucune table de hachage, et est refusée avant toute construction de clé de vérification si elle déclare un autre palier, l’une ou l’autre table ou une autre hauteur publique. Depuis l’ensemble de contraintes 7, toute table déclarée compte au moins 2⁷ lignes ; une preuve de bundle de la chaîne 20 déclare donc public 7.

Une transaction sur la chaîne 20 porte donc environ 2.87 MB de preuve, la preuve du bundle et la preuve d’authentification, et un appel y ajoute une troisième preuve. Chacune doit tenir dans le max_proof_bytes de la genèse, 4,194,304 (4 MiB) sur la chaîne 20, comme sur les chaînes 17 à 19, contre 8 MiB sur les chaînes 13 à 16.

Le guest à actif caché ne tient dans le palier 14 que parce qu’il lit ses entrées privées dans une boucle et fusionne les lectures des frères de la vérification Merkle. Écrite dans l’ancien style déroulé, la même relation tombe au palier 16, soit environ 400 s et 21–22 GB, ce qui exclurait les portefeuilles de 16 GB.

La preuve est limitée par le CPU, et le wallet utilise aujourd’hui le backend CPU sur toutes les plateformes. Lors des exécutions sur les grands paliers, le prover était mono-thread : un droplet de 16 vCPU tournait à 99 % d’un cœur, et c’est la mémoire, non les cœurs, qui décidait de l’aboutissement d’une preuve (mémoire de preuve). L’option --cuda de send, bond, call, deploy, bridge-mint et bridge-burn sélectionne un backend NVIDIA qui exige une compilation avec --features cuda. Ses kernels ont tourné pour la première fois sur du matériel réel, un NVIDIA H100, le 2026-09-28 et ont coïncidé avec la référence CPU, mais aucun PTX n’est commité dans le checkout des circuits que la version embarque : la compilation échoue donc avec no PTX for the GPU kernels tant que les kernels ne sont pas compilés. Aucune preuve de production n’a été mesurée sur un nœud GPU de la flotte. Il n’y a volontairement aucun repli sur le CPU. Un appel CUDA ne peut pas non plus sceller une enveloppe d’entrée, car le sel ne quitte jamais le prover. Le pic de mémoire pour une preuve de bundle à actif caché au palier 14 est d’environ 5.7 GB (Production FRI, CPU) ; les classes de mémoire supérieures concernent les preuves de l’interpréteur et de la récursion décrites plus bas (≥ 64 GB et au-delà).

Comment un nœud vérifie

L’admission exécute d’abord les contrôles peu coûteux (Transactions), puis :

  • recalcule le digest du bundle à partir du texte en clair publié (ancre, quatre nullifiers, quatre engagements, fee, burn_a, burn_r, burn_asset, time, l’auth_commit du bundle, et un mot de taint fixé à zéro) et exige qu’il soit égal à ce que la preuve a publié — une preuve dont le témoin a violé la relation publie un digest entaché qui ne correspond à aucun texte en clair. Certains contrôles sont plutôt structurels (le guest fournit lui-même l’expéditeur, l’actif et le temps d’une sortie, de sorte qu’un prover ne peut pas les choisir), et l’actif privé ne figure pas dans le digest ;
  • décode la preuve et vérifie ses hauteurs déclarées par rapport au palier (check_declared_heights), de sorte que la borne que la chaîne impose à la forme d’une preuve est celle du verifier. Avant toute vérification de signature ou de preuve, un filtre de preuve canonique s’exécute sur chaque transaction soumise ou relayée par gossip (et, sous hardening_v6, lors de la validation des blocs) ; depuis fullnode v0.6.8 (audit v6, ZKV-4), ce filtre borne d’abord chaque degré déclaré à 25 bits (MAX_HONEST_DEGREE_BITS, le plafond de la table mémoire plus un), si bien qu’un en-tête à degré énorme ne retient plus un nœud pendant que le calendrier de repli honnête est recalculé pour lui, et qu’une panique dans le filtre fait refuser cette transaction au lieu d’arrêter le nœud ;
  • calcule le binding de la transaction et exécute Machine::verify_public(hc_bundle, &binding, proof) — le verifier par lots plus le contrôle que H_PUB est le digest de ces huit mots, de sorte qu’une preuve copiée sur une transaction portant une autre action, une autre enveloppe ou un autre identifiant de chaîne, ou, sur la chaîne 20, sur la genèse d’une autre chaîne, ne se vérifie pas (jusqu’à la chaîne 13, le segment était vide et rien ne liait une preuve à sa transaction) ;
  • vérifie la preuve d’authentification par rapport à hc_auth et au même binding, et exige que le c qu’elle publie soit égal à auth_commit. Un bundle sans preuve d’authentification est refusé (AuthMissing), tout comme celui dont le c diffère (AuthMismatch) ;
  • exige que le GAS_LIMIT de la preuve du bundle soit égal au bundle_gas_limit de la genèse, 20,479, et celui de la preuve d’authentification à 1,279, les plafonds d’en-tête des deux guests (BundleGasLimit, AuthGasLimit) ;
  • pour un appel, fait de même par rapport au code_hash stocké du programme, avec les mots publics du programme suivis du binding de l’appel comme segment (un Invoke RPL-2 ajoute sa transition comme mots de contexte, Programmes), puis vérifie les frais par rapport à la limite de gas que la preuve déclare et à sa taille en octets (Frais).

Une transaction soumise par RPC ou par gossip est vérifiée sur l’un des quatre workers derrière une file de 64 entrées, hors de la boucle de consensus, et n’est relayée qu’une fois vérifiée ; un token bucket par pair (rafale de 16, recharge de 4 par seconde) limite ce qu’un pair peut faire vérifier à un nœud, et un cache de 8,192 entrées répond gratuitement à la répétition d’une transaction refusée de façon définitive. La vérification des preuves contenues dans un bloc proposé s’exécute encore sur la boucle de consensus lorsque le bloc est appliqué. Un nœud qui se synchronise revérifie chaque certificat et chaque preuve de chaque bloc avant de l’accepter ; rien n’est tenu pour acquis du fait qu’un quorum l’a signé.

Paliers

palierbudget de cycles (2ᵗ − 1)lignes cpuutilisé par
101,0232¹⁰le guest d’authentification ; private_payment, fib
124,0952¹²
1416,3832¹⁴le guest de bundle à actif caché (et l’ancien guest bundle, retiré) ; le plus grand appel
1665,5352¹⁶la preuve d’appel EVM de la suite de tests ; l’approve ERC-20 traduit (appelé sur la chaîne 13)
18262,1432¹⁸ERC-20 transfer et transferFrom, traduits ou interprétés
201,048,5752²⁰SPL Token, traduit ou sur le guest interpréteur sBPF

Depuis fullnode v0.5.6 (2026-09-25), une preuve d’appel au-dessus du palier 14 est refusée (CallTierTooHigh) avant qu’une clé de verifier soit construite, et un bloc qui en porte une est invalide ; un appel peut en outre comporter au plus 128 permutations Keccak-f et 128 compressions SHA-256. Les paliers 16 à 20 produisent toujours des preuves, mais hors chaîne uniquement : aucune des lignes qui les utilisent ne peut être appelée sur la chaîne 20, et la chaîne 20 refuse le déploiement d’un programme trop grand pour tout appel au palier 14, soit plus de 8,180 mots sans entrée publique (ProgramUncallable) (le budget).

Le prover choisit le plus petit palier dont le budget couvre les cycles plus les lignes du programme et du digest d’entrée ; une exécution qui ne tient pas échoue avant la preuve. Seul le palier est public. Chaque palier multiplie à peu près par quatre le temps de preuve et ajoute un peu à la taille de la preuve. Sur la chaîne 20, les frais d’un appel suivent la limite de gas que sa preuve déclare et sa taille en octets, non le palier (Frais).

Jeux de contraintes et hard forks

Un jeu de contraintes est l’ensemble exact des tables, colonnes, bus, valeurs publiques et paramètres FRI que le verifier contrôle. Une preuve produite sous un jeu ne se vérifie pas sous un autre, dans aucun des deux sens, et le rejeu au démarrage d’un nœud revérifie chaque preuve de sa chaîne — un nœud compilé à partir d’un autre jeu échoue donc à la première preuve et tronque. C’est pourquoi chaque modification du jeu est livrée comme un nouvel identifiant de chaîne, avec tous les nœuds sur un même build, et pourquoi rand_status indique hc_bundle et hc_auth et qu’un nœud dont le build diffère de la genèse refuse de démarrer.

jeudatece qui a changé
22026-09-10cinq correctifs de solidité après la première revue
32026-09-11puce Poseidon2, hc dans le circuit, la table du programme comme témoin, mémoire sous-mot, RV32M, FRI réajusté à 27 requêtes
42026-09-11l’engagement d’entrée salé H_IN avec sa propre table ; le chargeur de binaire plat
52026-09-12la table keccak optionnelle ; hauteur de mémoire déclarée par la preuve ; deux correctifs critiques issus de l’audit zk ; FRI revenu à 80 requêtes ; MAX_PROOF_BYTES porté à 2 MiB
62026-09-14la table public obligatoire et H_PUB ; la table sha256 optionnelle ; les guests interpréteurs EVM et sBPF sous forme de bibliothèques ; 34 valeurs publiques. Ce qu’exécutaient les chaînes 12 à 15.
72026-09-28chaque terminal LogUp masqué par le bus BLIND ; chaque table déclarée plancher à 2⁷ lignes ; contrôles de plage sur 32 bits pour les mots d’entrée, publics et de sel ; le syscall POSEIDON2_LEN (7) ; JALR met à zéro le bit 0 de sa cible et exige funct3 = 0 ; clés de verifier salées à partir de key_derivation_v2. Ce qu’exécutaient les chaînes 16 et 17.
82026-09-29le compteur de gas : la colonne GAS de la table cpu et la valeur publique GAS_LIMIT ; 35 valeurs publiques. Ce qu’exécutent les chaînes 18 à 20.

Les chaînes 14 et 15 n’ont modifié aucune contrainte. Le nœud de la chaîne 15 détient les 86 constantes de tour de Poseidon2 sous forme de table littérale au lieu d’un tirage à partir d’une graine (audit ZKV-2) — les mêmes valeurs, de sorte qu’aucun hash, digest ou preuve n’a bougé. Chaîne 14 : le bundle à actif caché est un nouveau guest (un nouveau hc_bundle) et le binding de la transaction utilise le segment public que le jeu 6 lie déjà dans le circuit. C’est malgré tout un hard fork — le format de transmission du bundle, le guest épinglé et chaque identifiant de transaction ont changé, une transaction de la chaîne 13 portant un bundle est refusée par le nouveau build, et un wallet compilé avant le fork produit des preuves à segment vide que la chaîne refuse.

Les trois chaînes suivantes ont chacune changé ce qu’est une preuve. La chaîne 16 (fullnode v0.6.1, 2026-09-28) est passée au jeu de contraintes 7, a épinglé le programme invité de lot sans branchement v2 et a activé hardening_v6, après que la revue du 2026-09-27 a montré que des tables courtes et des terminaux LogUp non masqués laissaient fuiter des données privées (Comment fonctionne le zkVM). Toutes les clés de vérification ont changé. La chaîne 17 (v0.6.3, 2026-09-29) a conservé le jeu 7 et ajouté l’autorisation scindée : programme invité de lot v3, programme invité d’autorisation, deux nouveaux champs dans le format de transmission du lot et le domaine d’identifiant de transaction rand-txid-3. La chaîne 18 (v0.6.7-rc1, 2026-09-29) est passée au jeu de contraintes 8 pour le compteur de gaz. Son coût mesuré en taille de preuve est inférieur à 1 %. La chaîne 19 (2026-10-01) est une nouvelle genèse de la chaîne 18 sur le même build v0.6.7 et n’a rien changé aux preuves. La chaîne 20 (fullnode v0.6.8, 2026-10-01) conserve le jeu de contraintes 8, les mêmes programmes invités et les mêmes clés de vérification, et change ce à quoi une preuve est liée : les liaisons de transaction et d’appel portent le hash de genèse (binding_domain: 1), une entrée publique que le wallet et le registre calculent, de sorte qu’aucun circuit n’a bougé. C’est tout de même un hard fork, puisque les preuves d’un wallet v0.6.7 n’y sont pas vérifiées. Chaque build refuse les chaînes qui le précèdent d’après le hash de genèse.

Deux notes d’audit figurent dans le code et méritent d’être connues : la valeur publique TIER est vérifiée par le vérifieur par rapport à l’en-tête de la preuve et aux degree bits du lot, mais elle n’est pas contrainte par le circuit lui-même (audit ZL1, signalé pour le jeu suivant plutôt que corrigé, car la retirer décalerait la disposition des valeurs publiques) ; et H_IN est un engagement visible du programme invité que le vérifieur ne compare pas à une valeur fournie par l’appelant, par conception.

À venir : l’agrégation de blocs

À environ 1,43 MB par preuve de lot à actif masqué sur les chaînes 14 et 15, un bloc de 4 MiB (la valeur par défaut, et celle de la chaîne 12) contenait deux transferts shielded ; il en contenait trois avec les ~1,3 MB du lot 2-in-2-out. Les chaînes 13, 14 et 15 ont porté le plafond de bloc à 20 MiB (max_block_bytes), de la place pour quatorze. La chaîne 20 conserve 20 MiB, et un transfert y porte environ 2,87 MB de preuve, de sorte qu’un bloc en contient environ sept. Le remède retenu par le fullnode (2026-09-12), de préférence à l’élagage et à un bloc plus grand, est l’agrégation au niveau du bloc : une preuve récursive par fenêtre scellée qui vérifie N preuves de lot, si bien que l’historique reste porteur de preuves de bout en bout tandis que les preuves brutes peuvent être abandonnées. Elle est construite et fusionnée mais pas activée, et depuis la chaîne 14 elle doit être re-mesurée avant de pouvoir l’être : la forme de lot qu’elle admet a été mesurée avec le segment public vide (public 2), et un lot lié à la transaction déclare public 4, de sorte qu’aucun lot prouvé sur la chaîne 14 n’y correspond. Les jeux de contraintes 7 et 8 ont de nouveau déplacé la forme : une preuve de lot déclare désormais public 7 et publie 35 valeurs, si bien que l’interface de l’agrégat est de 35 mots par preuve. Un build de nœud refuse de démarrer sur toute genèse qui porte une section aggregation, et les chaînes 14 à 20 ont été créées sans. Une genèse avec des prix de gaz dynamiques, comme celle de la chaîne 20, est également refusée à côté d’une section aggregation. Depuis le fullnode v0.5.8, la preuve d’agrégat lie aussi son aggrégateur (audit AGG-2 : le programme absorbe aggregate_binding(chain_id, aggregator, nonce)), de sorte que le condensat de programme de la forme admise doit être mesuré sur un build qui porte ce changement. L’activation attend aussi un exercice de bout en bout d’agrégat forgé contre le vérifieur de récursion corrigé, qui n’a pas été mené, ainsi qu’une décision sur la preuve d’autorisation : le programme d’agrégation ne vérifie que des preuves de lot, de sorte que, sur une chaîne à autorisation scindée, sceller une fenêtre ferait passer un transfert d’environ 2,87 MB à environ 1,37 MB, et non à rien.

Ce qui est construit :

  • une seconde machine native Goldilocks (la machine de récursion, randprotocol-rvm) dont le programme vérifieur contrôle une preuve RV32 en environ 1,97 million de lignes au niveau 21 (2 044 506 sous le jeu de contraintes 7), et un programme d’agrégation générique en N sur une fenêtre de preuves de lot ;
  • deux rôles : les validators restent peu coûteux en CPU et vérifient un agrégat par bloc scellé ; les aggrégateurs (un registre sans permission, avec caution, doté d’une adresse de paiement shielded) prouvent les agrégats sur des machines de classe GPU et sont rémunérés de la part de preuve des lots couverts, plus une subvention de bloc en nouveaux RAND selon un calendrier de réduction de moitié ;
  • cinq actions (RegisterAggregator, UnbondAggregator, WithdrawAggregator, SlashAggregator, Aggregate ; SlashAggregator reste dans le format de transmission et est refusée, car un agrégat retenté re-signe le même nonce et ne constitue pas une équivocation), une admission en neuf étapes, le scellement et l’élagage des lots couverts jusqu’à environ 3 KB de champs publics chacun, une synchronisation en forme scellée qui coûte à un nouveau nœud une vérification de récursion par fenêtre, les méthodes RPC rand_getAggregate, rand_getAggregators, rand_getUnsealed, rand_getRawTransaction, et le démon rand-node aggregate --watch.

Mesuré sur le profil FRI de test : un agrégat N=1 prouvé en environ 26 minutes sur un portable chargé (327 035 bytes) ; un nouveau nœud a appliqué un lot scellé de sept blocs en moins d’une seconde avec une seule vérification de récursion. Classes de machines telles que bornées au départ : N=1 exige ≥ 64 GB de mémoire hôte, N=2 ≥ 128 GB, N=3 ≥ 160 GB ; la production avec N ≥ 2 relève du backend CUDA, qui requiert un nœud GPU qui n’existe pas encore. Mesuré sur une machine de 256 GB sous le jeu de contraintes 7 (2026-09-28 et 29), l’agrégat N=2 a pris 133 GB, N=3 a pris 221 GB, et la preuve de sortie de production (le programme vérifieur sur une preuve interne) a dépassé 256 GB. Rien de cela n’a été re-mesuré sous le jeu de contraintes 8. Tant que l’agrégation n’est pas activée, aucun agrégat ne tient lieu de preuve sur la chaîne 20 : ses deux nœuds d’archive conservent chaque preuve brute, et ses validators élaguent l’historique de plus de 24 heures.

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.