Rand Protocol
Documentación
Guías

Ejecutar un nodo

Compile los binarios, cree una clave, inicialice un directorio de datos a partir del genesis de la chain 20, conéctese a los bootstraps, enlace el RPC, pode el historial y verifique la cadena, ya sea como observador o como validador.

Usa esta página para ejecutar tu propio rand-node contra la testnet, que además es la forma recomendada de usar una wallet con privacidad: un nodo que ejecutas tú no sabe de nadie más que de ti qué hoja estás a punto de gastar. Un observador se sincroniza, vuelve a verificar cada prueba y sirve el JSON-RPC; un validador hace lo mismo y participa en el consenso una vez que su clave está en el conjunto de una época.

Compilación

Requisitos: Rust 1.98.1 (fijado en rust-toolchain.toml; rustup lo instala), un compilador de C++ y cmake para RocksDB. El crate del zkVM tiene dependencias por ruta hacia una copia hermana circuits/ del repositorio del sistema de pruebas (randprotocol/zkp-circuits), que debe estar junto a fullnode/, en el commit del que se vendorizó la versión: la línea circuits: de crates/randprotocol-zkvm/guests-compiled/PROVENANCE.md (aeacf31 para v0.6.8, igual que para v0.6.7). La primera compilación tarda de 10 a 20 minutos.

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

Ejecuta v0.6.8 (git checkout v0.6.8, commit c9c9bd3c, es la versión que ejecuta la flota; rand_getVersion informa del sha de un nodo). Su release de GitHub incluye rand-node, rand, rand-prover y SHA256SUMS, compilados en un solo host de la flota y sin firmar. La chain 20 la necesita: su génesis incluye campos que ninguna compilación anterior lee (testnet, binding_domain, proof_window_blocks, program_state, los grupos rotation y fees del bridge, los techos de gas y la admisión al staking por voto), y v0.6.8 incluye cambios de consenso que un nodo más antiguo no ejecuta. Las pruebas se siguen generando con el conjunto de restricciones 8, como en las chains 18 y 19, y cada bundle sigue llevando una prueba de autorización junto a su prueba de bundle (autorización dividida, desde la chain 17), así que el génesis fija los mismos dos guests: el guest de bundle v3 (hc_bundle 60af094a…) y el guest de autorización (hc_auth 1e4e347f…). El génesis incluye además una sección gas, hardening_v6 y envelope_bytes. Una compilación v0.6.8 rechaza el génesis de las chains 14, 15, 16 y 17 en run y en verify, por hash; aún puede ejecutar el de la chain 19. Todos los nodos usan la compilación de la flota.

Chain 20: un hard fork con parada total

La chain 20 entró en funcionamiento el 2026-10-01 a las 06:17 UTC. Se cortó a partir de una instantánea de la chain 19 y se desplegó con parada total y arranque total: todos los nodos de la chain 19 se detuvieron en la altura 7700, y todos los nodos arrancaron en la chain 20 con v0.6.8. No fue una actualización progresiva, por dos razones. La chain 20 es un génesis nuevo, de modo que no comparte ningún bloque con la chain 19. Y v0.6.8 incluye cambios de consenso de la auditoría v6 que nunca deben desplegarse nodo por nodo: CON-4 elimina la liberación automática del bloqueo de un validador, y CH-1 añade un pacemaker con certificados de timeout. Los 26 validadores, sus claves, ids de peer y bootstraps se mantuvieron; un quórum sigue siendo 18 de 26. Las chains 19 y 18 quedan retiradas.

Por tanto, pasar un nodo de la chain 19 a la chain 20 requiere el nuevo binario, el nuevo archivo de génesis y un directorio de datos nuevo, como se indica a continuación. Las wallets necesitan v0.6.8 para enviar en la chain 20: su génesis vincula cada prueba y cada mensaje firmado a su hash (binding_domain: 1), algo que una wallet más antigua no hace.

Claves

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

El archivo es { "seed", "address", "public_key" }, con modo 0600; solo la semilla es secreta. Un observador también necesita una clave: es la identidad p2p. Una clave de nodo no es una clave de wallet: usa rand keygen para una wallet.

Genera tu propia clave y guárdala fuera del checkout. El repositorio del fullnode ya no rastrea ninguna clave (auditoría v3, OPS-1): los archivos deploy/node-*.key.json y deploy/payout/*.key.json que se confirmaron para las chains 8 a 13 son semillas publicadas y no deben usarse. Las claves de validador de la chain 20 se generaron fuera del repositorio y nunca han estado en él: sus 26 validadores de génesis son las dieciocho claves generadas para la chain 14 y las ocho vinculadas en la chain 15, trasladadas en cada corte desde entonces.

Desde v0.6.8, rand-node keygen rechaza un archivo --out que ya existe; v0.6.7 lo sobrescribía sin preguntar. El archivo es la única copia de la semilla. Haz una copia de seguridad antes de que un bond dependa de él.

Inicialización desde el génesis

Todos los nodos de una chain necesitan el archivo de génesis idéntico byte a byte; rand-node init lo copia en el directorio de datos e imprime su hash, que para la chain 20 debe ser 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135. El archivo es deploy/genesis-chain20.json (sha256 0020247a…3a21). Se confirmó en main del fullnode junto con el registro de lanzamiento (commit 74cfaca0), después de la etiqueta, por lo que un checkout de v0.6.8 no lo tiene: obtenlo de main.

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

Esto crea <datadir>/db (RocksDB) con el bloque 0, las nueve notas de génesis como primeras hojas del árbol (siete notas RAND que suman 5184,243719258 RAND de wallets de operadores de la chain 19, y dos notas zUSD de 10 y 89 zUSD, los 99 zUSD bloqueados en la custodia del bridge) y el registro de validadores. Volver a ejecutarlo con el mismo génesis no hace nada; un génesis distinto se rechaza (already initialized with a different genesis significa que el datadir pertenece a otra chain). Nombra el directorio de datos con los primeros ocho caracteres del hash del génesis, como hace la flota (data-<name>-6210cf07), para que nunca colisione con el de la chain 19 (-a3defc93).

La forma del archivo de génesis:

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

Todos los campos entran en el hash del génesis. hc_bundle fija el guest de bundle contra el que se comprueba cada prueba de bundle, y hc_auth el guest de autorización contra el que se comprueba cada prueba de autorización; un nodo cuya compilación ensambla un guest distinto se niega a arrancar y nombra ambos digests. Los cinco campos max_* son opcionales y solo se incorporan al hash cuando están presentes; la chain 20 establece los cinco. max_proof_bytes es 4 MiB, donde las chains 13 a 16 tenían 8 MiB: desde la chain 17 una transacción puede llevar tres pruebas (bundle, autorización, llamada), y un génesis con hc_auth debe tener un max_block_bytes de al menos tres veces max_proof_bytes más 1 MiB. Cada nota de alloc lleva su opening (pk, time, r y asset para una nota de token), por lo que el propietario y el importe de una nota de génesis son públicos por diseño. La chain 20 tiene:

  • una sección bridge: el conjunto de guardianes 1 en el índice 1 con ocho guardianes, emisores para las chains de origen 2, 3, 4 y 5 (los de la chain 19), ocho pq_guardians Dilithium2, una pause_key, burn_sequence 13, un min_inbound_sequence de 3 para las chains de origen 2, 3 y 4 y de 7 para la chain de origen 5, y rules_v2 (un tope global de emisión de 4000 zUSD por ventana de 24 h). Novedad de la chain 20: rotation, según la cual la rotación de un PQ-guardian o de la pause-key exige prueba de posesión de las nuevas claves y surte efecto 86.400 s después de confirmarse; y fees, según la cual la chain retiene 10 bps de cada depósito y 10 bps de cada burn como una nota zUSD a una dirección de tesorería, de modo que un depósito acredita el importe bruto menos la comisión y un burn libera su importe menos la comisión;
  • una sección tokens: una comisión de registro de 1 RAND, quemada en lugar de pagada al proponente; un mint_cap_per_day de 100.000 tokens por respaldo con ocho decimales; un tope de registro de 4096 tokens; el límite de valor de nota de 2^63; y zUSD listado en el génesis con sus siete respaldos, 99 zUSD bloqueados (Ethereum USDT 22, BSC USDT 22, Tron USDT 30, Solana USDT 25) frente a las dos notas zUSD de génesis;
  • consensus_domain: 1: cada firma de consenso queda vinculada a este génesis (Consenso);
  • una sección staking: el faucet incluido en la lista de permitidos para 16 wallets y 18 claves de validador acuñadoras, con un presupuesto de 10.000 RAND por época, un retraso de bond de dos épocas, un tope de peso de 3333 bps y registros v2, como en la chain 19; novedad de la chain 20, admission_by_vote (una nueva clave de validador solo se registra después de que el conjunto de validadores la vote) y un presupuesto de entrada de 2500 bps del peso activo por frontera de época, donde la chain 19 tenía 10.000 RAND fijos (Staking). No hay slashing;
  • una sección gas: una llamada paga el límite de gas que declara su prueba y sus bytes, a precios que parten de 100 unidades por gas y 800 unidades por KiB y se mueven tras cada bloque según su ocupación frente a un objetivo de 10.485.760 bytes y 262.144 de gas, como máximo 1250 bps por bloque, nunca por debajo de los precios iniciales y, novedad de la chain 20, nunca por encima de 10.000 y 80.000. Solo cuentan para la carga del precio por byte los bytes que pagan un precio por byte, la prueba de una llamada y su sobre de entrada (byte_load: "paying"). Cada prueba de bundle declara el mismo límite, 20.479 (Comisiones);
  • envelope_bytes: 1860: cada sobre de nota mide exactamente 1860 bytes y lleva un campo de memo cifrado;
  • hardening_v6: true, el interruptor de endurecimiento v0.6, activado por todas las chains desde la chain 16;
  • testnet: true, la marca que permite que un faucet conviva con una sección bridge;
  • binding_domain: 1: las pruebas de cada transacción y cada mensaje de acción firmado incluyen el hash del génesis, de modo que nada probado o firmado para una chain se verifica en otra;
  • proof_window_blocks: 1024: el ancla de un bundle puede ser una de las últimas 1024 raíces de fin de bloque y su time puede ir hasta 1024 bloques por detrás de la altura (256 en la chain 19), lo que da a un probador lento o delegado unos veinte minutos;
  • program_state, RPL-2: celdas de programa, bóvedas de programa y la acción Invoke, con una comisión de celda de 0,1 RAND por cada celda que crea una invocación (JSON-RPC recoge los límites).

No tiene sección aggregation, y la compilación se niega a arrancar con un génesis que la incluya.

timestamp_ms es el punto cero del reloj de la cadena. Cuando genere un genesis propio, fije el valor cerca de la hora real de lanzamiento y nunca por delante de ella. Si queda demasiado en el pasado, el tiempo de bloque va por detrás del reloj de pared hasta que el límite de 60 s por paso de bloque le permite alcanzarlo. Si queda más de 15 segundos en el futuro, el bloque 1 no se confirma en absoluto: todos los votos, incluido el del líder, se retienen hasta que la hora de pared alcanza la marca de tiempo del genesis. Compárelo con el reloj de la máquina que lanza la flota, no con el de la que generó el archivo.

Ejecución

rand-node run --datadir ./data --key node.key.json \
    --listen /ip4/0.0.0.0/tcp/30303 --rpc 127.0.0.1:8545 --no-mdns \
    --bootstrap /ip4/164.90.239.200/tcp/30303/p2p/12D3KooWEQEbUwZRgqhDPXhW7vNBmcRcnhUDGUADe81VFdv3ALYe \
    --bootstrap /ip4/165.245.173.74/tcp/30303/p2p/12D3KooWJyc5oDHggAr89e9QrRFBy9Tg8TxXh18yzrb1SM6KKLYw \
    --bootstrap /ip4/188.166.235.187/tcp/30303/p2p/12D3KooWSyX84P1DAbWByLHpRBMLRWD53r3Jj4aqVt6tHoF8B2ZQ
argumentovalor por defectosignificado
--datadirobligatorioel directorio de datos inicializado
--keyobligatorioarchivo de clave: identidad de validator, identidad p2p, destinatario de comisiones
--listen/ip4/0.0.0.0/tcp/30303multiaddr de escucha de libp2p, repetible
--bootstrapningunopeer al que conectarse al iniciar y cada 30 s mientras esté desconectado, repetible
--rpc127.0.0.1:8545el listener JSON-RPC y WebSocket del operador: todos los métodos, y confía en loopback. Manténgalo en loopback y nunca redirija un endpoint público hacia él
--public-rpc <ADDR>desactivadov0.6.8: un segundo listener, público, al que apuntar desde un proxy inverso o un reenvío SSH: un conjunto fijo de métodos sin los métodos de clave de visualización, rand_mint ni rand_getPeers, sin lotes, sin WebSocket, un único medidor para todos los llamantes (JSON-RPC)
--rpc-viewing-token-file <PATH>desactivadov0.6.8: exige un bearer token (la primera línea del archivo, de 32 o más caracteres) en los métodos de clave de visualización de --rpc
--reserved-peer <PEER_ID>ningunov0.6.8, repetible: un peer admitido por encima del límite de conexiones entrantes; véase Peers reservados
--strict-gossipdesactivadov0.6.8: acepta solo gossip con una firma de autor, un número de secuencia y un origen válidos; se despliega nodo por nodo
--validatordesactivadoeste nodo posee una clave de validator y participa en el consenso cuando su clave está en el conjunto actual
--no-mdnsdesactivadodesactiva el descubrimiento en LAN; recomendado en servidores
--block-interval-ms1000espaciado mínimo entre propuestas
--view-timeout-ms3000tiempo de espera base de la vista, que se duplica con cada tiempo de espera consecutivo hasta 8×
--verify-chainquickcomprobación de integridad al inicio: off, quick (estructura y reproducción del ledger), full (también cada firma y cada QC)
--keep-raw-proofsdesactivadomodo de archivo para la agregación de bloques; sin efecto en la chain 20
--prune-historydesactivado (conserva todo)conserva el ledger y solo esta cantidad de historial de bloques: <n>m, <n>h o <n>d, como mínimo 1h. Los validators de la testnet usan 24h, salvo los dos archivos; véase Poda del historial
--min-free-disk-mb1024se niega a iniciar con menos espacio libre en el sistema de archivos del directorio de datos; rand_getHealth responde disk_low por debajo de cuatro veces ese valor. 0 desactiva la protección
--prover <ADDR>desactivadoaloja un prover delegado en esta dirección, un listener propio y nunca el RPC; véase más abajo
--gas-price, --byte-price100, 800el precio mínimo del pool para una llamada en una cadena sin sección gas. El genesis de la chain 20 fija sus propios precios, así que el nodo ignora ambos indicadores allí y registra una advertencia

--prover-accept-spend-key está retirado: un nodo v0.6.8 que lo reciba se niega a iniciar, así que elimínelo de cualquier archivo de unidad heredado de v0.6.7.

Los tres bootstraps anteriores son los validators C, D y E, sin cambios respecto a la chain 19. La chain 20 funciona con las claves de validator generadas para la chain 14, por lo que sus peer ids son los que usó la chain 14; una identidad p2p se deriva de la clave de validator. La mayoría de los validators conservan solo un día de bloques, así que un nodo que sincroniza desde el genesis, o uno con más de un día de retraso, necesita además un peer con historial completo. Añada uno de los dos archivos:

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

Omita --validator para ejecutar un observador. Inicio: abrir el almacenamiento, comprobar los guests de esta compilación con hc_bundle y hc_auth del genesis, ejecutar la comprobación de integridad (truncando una cola dañada y conservando el estado de seguridad de voto), reanudar el consenso desde la cabeza persistida, iniciar la red y el RPC, y después sincronizar desde cualquier peer que esté por delante. RUST_LOG toma por defecto info,libp2p=warn,libp2p_mdns=off; Ctrl-C o SIGTERM, la señal de parada de systemd, apaga el nodo de forma limpia. Reiniciar con el mismo --datadir reanuda desde la cabeza persistida.

Reglas de topología del fullnode: los nodos tras NAT se conectan hacia nodos con direcciones públicas; abra el TCP 30303 de entrada en los nodos públicos; enlace el RPC a 127.0.0.1 salvo que esté protegido por un cortafuegos: acepta transacciones de cualquiera que pueda alcanzarlo y, una vez importada en él una clave de visualización, trate el puerto como portador de claves. Mantenga --block-interval-ms en 1000 o más lento: una prueba de bundle tarda unos dos minutos y su ancla es válida durante 1,024 bloques en la chain 20 (256 en la chain 19).

Un validator necesita un reloj sincronizado. Desde la chain 14, la hora de pared forma parte del consenso (refuerzo del bridge B2). Un bloque cuya marca de tiempo supere en más de 60 s la de su padre no es válido, y un validator retiene su voto de un bloque cuya marca de tiempo esté más de 15 s por delante de su propio reloj. Por tanto, un validator cuyo reloj va lento deja de votar sin registrar ningún fallo, y en la chain 20 deben votar dieciocho de veintiséis para confirmar. Ejecute NTP en cada validator y compruébelo antes de iniciar el nodo:

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

Peers reservados

Desde v0.6.8 un nodo acepta como máximo 256 conexiones entrantes establecidas y 64 semiabiertas, y una identidad libp2p no cuesta nada de crear, de modo que unos extraños podrían ocupar todas las plazas. Se reservan tres clases de peer, admitidos por encima del límite y atendidos con la parte del presupuesto de sincronización que corresponde a los validators: cada peer de --bootstrap, cada --reserved-peer y cada identidad de validator conocida a partir de un vínculo de peer firmado. Cada nodo --validator firma su identidad libp2p con su clave de validator y la difunde por gossip; un nodo que conoce la clave a partir de un conjunto de validators reserva ese peer y conserva el registro entre reinicios. El almacén de una cadena recién generada no contiene vínculos, así que, hasta que lleguen los primeros anuncios, solo se reservan las dos primeras clases: en un corte, pase los peer ids de los validators de la flota con --reserved-peer o manténgalos en la lista de bootstrap (rand-node address --key <file> imprime el peer id de una clave). rand_status.reserved_peers los cuenta; en un nodo de la flota debería alcanzar el número de validators en el plazo de un minuto desde el inicio.

Comprobación

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

rand_status informa de height, view, syncing, sync_target, peer_count, connected_peers, reserved_peers, mempool_size, is_validator, active_validator, faucet, confidential, testnet, fri_profile, programs, notes, nullifiers, tree_root, hc_bundle, hc_auth, binding_domain, gas_prices, el address y el peer_id del nodo, los diagnósticos de sincronización (sync_inflight_age_ms, sync_failures, sync_late_batches), el disco (disk_free_bytes, disk_low) y el límite de retención (prune_floor, 0 en un nodo que lo conserva todo; prune_history_secs, null si no está definido). En la chain 20 cabe esperar hc_bundle 60af094a…, hc_auth 1e4e347f…, faucet true, confidential true, testnet true, binding_domain 1, fri_profile "production" y gas_prices entre 100 y 10,000 por gas y entre 800 y 80,000 por KiB. rand_getGenesisHash debe responder 6210cf07…5135 y rand_getVersion 0.6.8.

rand_getHealth responde disk_low (con free_bytes) antes que ok, syncing o behind cuando el espacio libre es inferior a cuatro veces --min-free-disk-mb.

Poda del historial

Un nodo iniciado con --prune-history 24h conserva el ledger (notes, nullifiers, anclas, el registro de validators, programas) completo y elimina los bloques más antiguos, sus certificados, transacciones y recibos. Una pasada se ejecuta cada 16.º bloque confirmado y elimina hasta 512 bloques anteriores a la marca de tiempo de la cabeza menos la ventana; el genesis, la cabeza y su padre nunca se eliminan, y el límite solo sube. El disco se recupera después de que RocksDB compacte. En la chain 20, como en la chain 19, 24 de los 26 validators usan 24h y conservan aproximadamente un día de bloques. Cuando se activó la poda en la chain 14 (2026-09-25), las tablas de bloques de cada validator pasaron de 19 GB a 3.3–4.4 GB una vez que se vació el trabajo pendiente; los otros dos, obs1 y rand-archive-2, validators también desde 2026-09-27, funcionan sin el indicador y lo conservan todo. Un nodo de mainnet nunca usa el indicador.

En un nodo podado, una consulta por altura por debajo del límite (rand_getBlockByHeight, rand_getFinality por altura, un rango de rand_getBlocks o rand_getCompactBlocks que lo alcance) responde con el error -32010 y data.floor: consulte a un archivo. Una consulta por hash responde null para un bloque que el nodo ya no conserva, igual que para uno que nunca existió. El escaneo de la wallet no se ve afectado, ya que las notes y los nullifiers nunca se podan.

La comprobación de inicio de un nodo podado es estructural (el bloque 0 y los bloques retenidos, sus QC, enlaces con el padre y líderes), no una reproducción desde el genesis, y no se repara por sí misma: si falla, vuelva a sincronizar desde un archivo. No abra un directorio de datos podado con una compilación anterior a v0.5.7 a menos que se ejecute con --verify-chain off: su comprobación por defecto reproduce desde el genesis, no encuentra el bloque 1 y trunca el ledger hasta el genesis.

Alojar un prover

En la chain 20 una wallet genera dos pruebas por cada bundle, y la mayor necesita unos 6 GB de memoria. Una wallet que no pueda soportarlo puede entregar esa prueba a un prover delegado con el que esté emparejada: el binario independiente rand-prover, o el mismo servicio alojado por un nodo.

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

pair imprime un enlace randprover:. La wallet lo recibe con rand prover pair <link> y después prueba a través de él con la opción global --prover. El listener del probador es propio y nunca es un método del RPC: una dirección igual a --rpc se rechaza. Con --prover, el nodo se niega a arrancar a menos que exista <prover-home>/prover.key.json (por defecto <datadir>/prover) y haya disponibles 5.74 GB por cada slot de prueba más 1 GiB de memoria; --prover-max-parallel fija los slots, uno por defecto.

En la chain 20 el probador recibe la clave de visualización de la wallet y una sal nueva, no la clave de gasto. La wallet genera por sí misma la prueba de autorización sobre la clave de gasto, de modo que un probador emparejado puede leer todo el historial de esa wallet y no puede gastar. Mantén el listener en loopback salvo que lo anteponga un proxy que termine TLS, y no lo alojes en una máquina que sirva un RPC público. Una parada espera a la prueba en curso, así que asigna TimeoutStopSec=180 a una unidad de systemd.

Verificar y reparar

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

Detén primero el nodo. El código de salida es 0 si todo es consistente y 2 si se encontró un problema y no se indicó --repair. La repetición vuelve a verificar la prueba de cada bundle, de modo que una compilación cuyo conjunto de restricciones difiere del que produjo la cadena se detiene en el primer bundle.

Convertirse en validator

Un validator se une a una cadena en funcionamiento mediante un bond, no mediante un nuevo génesis: arranca el nodo con --validator, imprime un registro con rand-node register --key node.key.json --payout <rand1…> --v2 (la chain 20 rechaza un registro v1), haz que una wallet vincule al menos 1,000 RAND con rand bond <address> 1000 --registration <hex>, y el nodo propone en cuanto el bond se activa: en la chain 20, como pronto tres límites de época de 1,000 bloques después del bond. En la chain 20 el bond viene en segundo lugar. Su génesis fija staking.admission_by_vote, por lo que un bond que registra una clave nueva se rechaza hasta que los validators que poseen más de dos tercios del peso de voto hayan votado a favor de admitir la clave: cada uno firma con rand-node admit sign --candidate <key or registration> --key <validator key> --genesis-hash 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135, y uno de ellos envía los votos con rand-node admit submit (Línea de comandos). rand_getAdmitted lista las claves admitidas por voto que aún no están registradas. No más de 2,500 bps del peso activo se convierte en peso nuevo en un mismo límite de época. Staking lo explica paso a paso. Para una cadena de pruebas privada, rand-node genesis genera una:

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

y scripts/local-testnet.sh ejecuta dos validators en una máquina con RPC en 8545 y 8546. Sin más indicadores, el génesis no tiene hc_auth, ni sección gas, ni memo. La chain 20 se generó con --hardening-v6 --bundle-guest v3 --auth-guest --gas-price 100 --byte-price 800 --bundle-gas-limit 20479 --gas-dynamic 10485760,262144,1250 --max-gas-price 10000 --max-byte-price 80000 --gas-byte-load paying --envelope-bytes 1860 --testnet --binding-domain 1 --proof-window-blocks 1024 --program-state-cell-fee 10000000 encima (las chains 18 y 19 llevaban los indicadores hasta --envelope-bytes), y sus secciones bridge y staking y el consensus_domain los insertó el script de generación. Una cadena nueva propia debería pasar --binding-domain 1: una wallet v0.6.8 genera la prueba en su forma ligada al génesis en todo chain id fuera del rango 14 a 19.

Despliegue en un servidor

El directorio deploy/ del repositorio contiene los scripts que usa la flota: deploy/push-to-vps.sh <ip> <letter> "<bootstraps>" [validator|observer] aprovisiona un servidor Linux (sincroniza el código fuente con rsync, compila, instala /usr/local/bin/{rand-node,rand}, inicializa un directorio de datos identificado por el hash del génesis e instala un servicio systemd rand-node), y deploy/rebuild-vps.sh <ip> recompila con un commit nuevo y reinicia conservando los datos. Dos advertencias. El script de aprovisionamiento se inicializa desde deploy/genesis.json, que no es el archivo de la chain 20, por lo que un nodo para la testnet se configura a mano desde deploy/genesis-chain20.json (en main, véase más arriba). Y ambos scripts sincronizan /root/fullnode con rsync usando --delete, así que una clave guardada dentro de ese árbol la borra la siguiente recompilación: guárdala en /root/keys, como hace la flota y como hace ahora por defecto el script de aprovisionamiento de la v0.6.8 (el de la v0.6.7 escribía la clave en /root/fullnode/deploy/). La gestión del servicio se hace con systemctl status rand-node y journalctl -u rand-node -f. El servidor necesita build-essential clang cmake pkg-config libssl-dev y una toolchain de Rust. Un servidor de 1 vCPU y 2 GB no puede compilar los binarios; compílalos en otro lugar y cópialos. Ejecutar un validator y un aggregator contiene los tamaños y la configuración paso a paso.

El explorador

randscan.org se ejecuta junto al validator E e indexa la chain 20; su API REST y su feed WebSocket son la forma más sencilla de leer la cadena sin un nodo (API del explorador). El explorador es de código abierto (randprotocol/randscan) y su indexador solo necesita el JSON-RPC rand_* de un nodo y una base de datos PostgreSQL, por lo que puede ejecutarse contra tu propio nodo.

Verificado por última vez con el commit c0878c3f del fullnode (cadena 21) el 2026-10-12. Cuando el código y esta página no coinciden, el código tiene razón; avísenos.