Rand Protocol
Документация
Руководства

Запуск узла

Соберите бинарные файлы, создайте ключ, инициализируйте каталог данных из genesis chain 20, подключитесь к bootstrap-узлам, привяжите RPC, обрежьте историю и проверьте цепочку — в роли наблюдателя или валидатора.

Эта страница описывает запуск собственного rand-node в testnet. Это же рекомендуемый способ приватно пользоваться кошельком: запущенный вами узел ни от кого, кроме вас, не узнаёт, какой лист вы собираетесь потратить. Наблюдатель синхронизируется, заново проверяет каждое доказательство и обслуживает JSON-RPC; валидатор делает то же самое и участвует в консенсусе, как только его ключ попадает в набор эпохи.

Сборка

Требования: Rust 1.98.1 (закреплён в rust-toolchain.toml; rustup установит его сам), компилятор C++ и cmake для RocksDB. У крейта zkVM есть зависимости по путям в соседний чекаут circuits/ репозитория системы доказательств (randprotocol/zkp-circuits). Он должен лежать рядом с fullnode/, на том коммите, из которого был вендорен релиз: это строка circuits: в crates/randprotocol-zkvm/guests-compiled/PROVENANCE.md (aeacf31 для v0.6.8, как и для v0.6.7). Первая сборка занимает от 10 до 20 минут.

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

Запускайте v0.6.8 (git checkout v0.6.8, коммит c9c9bd3c, именно этот релиз запущен на узлах флота; rand_getVersion сообщает sha узла). В его релизе на GitHub лежат rand-node, rand, rand-prover и SHA256SUMS, собранные на одном хосте флота и не подписанные. Цепочке 20 нужен именно он: её genesis содержит поля, которых не читает ни одна более ранняя сборка (testnet, binding_domain, proof_window_blocks, program_state, группы моста rotation и fees, потолки газа и допуск в стейкинг по голосованию), а v0.6.8 вносит изменения консенсуса, которых более старый узел не исполняет. Доказательства по-прежнему строятся по набору ограничений 8, как в цепочках 18 и 19, и каждый бандл по-прежнему несёт доказательство авторизации рядом с доказательством бандла (раздельная авторизация, начиная с цепочки 17), поэтому genesis закрепляет те же два гостя: гость бандла v3 (hc_bundle 60af094a…) и гость авторизации (hc_auth 1e4e347f…). Genesis также содержит раздел gas, hardening_v6 и envelope_bytes. Сборка v0.6.8 отказывается принимать genesis цепочек 14, 15, 16 и 17 и при run, и при verify, по хешу; genesis цепочки 19 она запустить всё же может. Каждый узел работает на сборке флота.

Цепочка 20: жёсткий форк с полной остановкой

Цепочка 20 запущена 2026-10-01 в 06:17 UTC. Она создана из снапшота цепочки 19 и развёрнута по схеме «остановить всё, запустить всё»: каждый узел цепочки 19 остановился на высоте 7 700, и каждый узел стартовал в цепочке 20 с v0.6.8. Это не было поэтапным обновлением, по двум причинам. Цепочка 20 — это новый genesis, так что ни один блок не является общим с цепочкой 19. И v0.6.8 вносит изменения консенсуса из аудита v6, которые ни в коем случае нельзя выкатывать узел за узлом: CON-4 убирает автоматическое снятие блокировки валидатора, а CH-1 добавляет пейсмейкер с сертификатами тайм-аута. Все 26 валидаторов, их ключи, идентификаторы пиров и bootstrap-узлы перенесены; кворум по-прежнему составляет 18 из 26. Цепочки 19 и 18 выведены из эксплуатации.

Поэтому для перевода узла с цепочки 19 на цепочку 20 нужны новый бинарный файл, новый файл genesis и новый каталог данных, как описано ниже. Для отправки транзакций в цепочке 20 кошелькам нужен v0.6.8: её genesis привязывает к своему хешу каждое доказательство и каждое подписанное сообщение (binding_domain: 1), чего более старый кошелёк не делает.

Ключи

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

Файл имеет вид { "seed", "address", "public_key" }, права 0600; секретен только seed. Ключ нужен и наблюдателю: это p2p-идентичность. Ключ узла не является ключом кошелька: для кошелька используйте rand keygen.

Сгенерируйте собственный ключ и храните его вне чекаута. Репозиторий fullnode больше не отслеживает ни одного ключа (аудит v3, OPS-1): файлы deploy/node-*.key.json и deploy/payout/*.key.json, которые были закоммичены для цепочек 8–13, — это опубликованные seed-значения, и использовать их нельзя. Ключи валидаторов цепочки 20 были сгенерированы вне репозитория и никогда в нём не находились: её 26 genesis-валидаторов — это восемнадцать ключей, сгенерированных для цепочки 14, и восемь, застейканных в цепочке 15, перенесённые через все последующие переходы.

Начиная с v0.6.8 rand-node keygen отказывается записывать в уже существующий файл --out; v0.6.7 перезаписывал его без вопросов. Файл — единственная копия seed. Сделайте резервную копию до того, как от ключа начнёт зависеть bond.

Инициализация из genesis

Каждому узлу цепочки нужен побайтово идентичный файл genesis; rand-node init копирует его в каталог данных и печатает его хеш, который для цепочки 20 должен быть 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135. Файл — deploy/genesis-chain20.json (sha256 0020247a…3a21). Он был закоммичен в main репозитория fullnode вместе с записью о запуске (коммит 74cfaca0), уже после тега, поэтому в чекауте v0.6.8 его нет: возьмите его из main.

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

Это создаёт <datadir>/db (RocksDB) с блоком 0, девятью genesis-нотами в качестве первых листьев дерева (семь нот RAND, содержащих 5 184,243719258 RAND операторских кошельков из цепочки 19, и две ноты zUSD на 10 и 89 zUSD, то есть 99 zUSD, заблокированных в хранилище моста) и реестром валидаторов. Повторный запуск с тем же genesis ничего не меняет; другой genesis отвергается (already initialized with a different genesis означает, что каталог данных принадлежит другой цепочке). Называйте каталог данных по первым восьми символам хеша genesis, как это делает флот (data-<name>-6210cf07), чтобы он никогда не совпал с каталогом цепочки 19 (-a3defc93).

Структура файла genesis:

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

Каждое поле входит в хеш genesis. hc_bundle закрепляет гостя бандла, по которому проверяется каждое доказательство бандла, а hc_auth — гостя авторизации, по которому проверяется каждое доказательство авторизации; узел, чья сборка собирает другого гостя, отказывается запускаться и называет оба дайджеста. Пять полей max_* необязательны и включаются в хеш, только если присутствуют; цепочка 20 задаёт все пять. max_proof_bytes равно 4 MiB, тогда как в цепочках 13–16 было 8 MiB: начиная с цепочки 17 транзакция может нести три доказательства (бандла, авторизации, вызова), а genesis с hc_auth должен задавать max_block_bytes не меньше чем втрое больше max_proof_bytes плюс 1 MiB. Каждая нота в alloc несёт свой opening (pk, time, r и asset для ноты токена), поэтому владелец и сумма genesis-ноты публичны по замыслу. В цепочке 20 есть:

  • раздел bridge: набор гардианов 1 с индексом 1 из восьми гардианов, эмитенты для исходных цепочек 2, 3, 4 и 5 (как в цепочке 19), восемь гардианов Dilithium2 pq_guardians, pause_key, burn_sequence 13, min_inbound_sequence, равный 3 для исходных цепочек 2, 3 и 4 и 7 для исходной цепочки 5, и rules_v2 (глобальный лимит эмиссии 4 000 zUSD на окно в 24 h). Нового в цепочке 20: rotation, по которой ротация PQ-гардиана или ключа паузы требует доказательства владения новыми ключами и вступает в силу через 86 400 s после фиксации; и fees, по которой цепочка удерживает 10 bps от каждого депозита и 10 bps от каждого сжигания в виде ноты zUSD на адрес казны, так что депозит зачисляет сумму брутто за вычетом комиссии, а сжигание высвобождает свою сумму за вычетом комиссии;
  • раздел tokens: регистрационный сбор 1 RAND, который сжигается, а не выплачивается proposer’у; mint_cap_per_day в 100 000 токенов на каждое обеспечение с восемью десятичными знаками; предел реестра в 4 096 токенов; граница значения ноты 2^63; и zUSD, внесённый в список в genesis с семью обеспечениями, 99 zUSD заблокированы (Ethereum USDT 22, BSC USDT 22, Tron USDT 30, Solana USDT 25) против двух genesis-нот zUSD;
  • consensus_domain: 1: каждая подпись консенсуса привязана к этому genesis (Консенсус);
  • раздел staking: faucet в списке допущенных из 16 кошельков и 18 эмитирующих ключей валидаторов, с бюджетом 10 000 RAND на эпоху, задержкой bond в две эпохи, ограничением веса 3 333 bps и регистрациями v2, как в цепочке 19; нового в цепочке 20 — admission_by_vote (новый ключ валидатора регистрируется только после того, как набор валидаторов проголосует за его допуск) и бюджет входа 2 500 bps активного веса на границу эпохи, тогда как в цепочке 19 было фиксированные 10 000 RAND (Стейкинг). Раздела slashing нет;
  • раздел gas: вызов оплачивает лимит газа, который объявляет его доказательство, и свои байты по ценам, которые начинаются со 100 единиц за газ и 800 единиц за KiB и после каждого блока смещаются в зависимости от его заполненности относительно цели в 10 485 760 байт и 262 144 газа, не более чем на 1 250 bps за блок, никогда не ниже начальных цен и, что нового в цепочке 20, никогда не выше 10 000 и 80 000. В нагрузку на цену за байт засчитываются только те байты, за которые взимается цена за байт, — доказательство вызова и конверт входных данных (byte_load: "paying"). Каждое доказательство бандла объявляет один и тот же лимит, 20 479 (Комиссии);
  • envelope_bytes: 1860: каждый конверт ноты занимает ровно 1 860 байт и несёт зашифрованное поле memo;
  • hardening_v6: true, переключатель усиления v0.6, установленный в каждой цепочке начиная с цепочки 16;
  • testnet: true, маркер, позволяющий faucet соседствовать с разделом bridge;
  • binding_domain: 1: доказательства каждой транзакции и каждое подписанное сообщение действия включают хеш genesis, поэтому ничто, доказанное или подписанное для одной цепочки, не проходит проверку в другой;
  • proof_window_blocks: 1024: якорем бандла может быть любой из последних 1 024 корней на конец блока, а его time может отставать от высоты до чем на 1 024 блока (256 в цепочке 19), что даёт медленному или делегированному prover’у около двадцати минут;
  • program_state, RPL-2: ячейки программ, хранилища программ и действие Invoke, с платой за ячейку 0,1 RAND за каждую ячейку, которую создаёт invoke (JSON-RPC описывает лимиты).

Раздела aggregation в нём нет, и сборка отказывается запускаться на genesis, который его содержит.

timestamp_ms — точка отсчёта часов цепи. Когда вы создаёте собственный genesis, задайте это значение как можно ближе к фактическому времени запуска и никогда не позже него. Если оно слишком далеко в прошлом, время блока отстаёт от настенных часов, пока ограничение в 60 s на шаг между блоками не позволит его догнать. Если оно более чем на 15 секунд в будущем, блок 1 не фиксируется вовсе: все голоса, включая голос лидера, удерживаются, пока настенное время не достигнет временной метки genesis. Сверяйте её с часами машины, которая запускает парк узлов, а не той, что создала файл.

Запуск

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
аргументпо умолчаниюзначение
--datadirобязателенинициализированный каталог данных
--keyобязателенфайл ключа: идентичность валидатора, p2p-идентичность, получатель комиссий
--listen/ip4/0.0.0.0/tcp/30303multiaddr для прослушивания libp2p, можно повторять
--bootstrapнетпир, к которому нужно подключиться при старте и затем каждые 30 s, пока соединения нет; можно повторять
--rpc127.0.0.1:8545операторский слушатель JSON-RPC и WebSocket: все методы, и он доверяет loopback. Держите его на loopback и никогда не пробрасывайте к нему публичную конечную точку
--public-rpc <ADDR>выкл.v0.6.8: второй, публичный слушатель, на который могут указывать обратный прокси или SSH-проброс: фиксированный набор методов без методов viewing key, rand_mint и rand_getPeers, без пакетных запросов, без WebSocket, один общий счётчик лимита для всех вызывающих (JSON-RPC)
--rpc-viewing-token-file <PATH>выкл.v0.6.8: требовать bearer-токен (первая строка файла, 32 символа или больше) в методах viewing key на --rpc
--reserved-peer <PEER_ID>нетv0.6.8, можно повторять: пир, допускаемый сверх лимита входящих соединений; см. Зарезервированные пиры
--strict-gossipвыкл.v0.6.8: принимать только gossip с корректной подписью автора, порядковым номером и источником; включается узел за узлом
--validatorвыкл.этот узел хранит ключ валидатора и участвует в консенсусе, когда его ключ входит в текущий набор
--no-mdnsвыкл.отключить обнаружение в локальной сети; рекомендуется на серверах
--block-interval-ms1000минимальный интервал между предложениями блоков
--view-timeout-ms3000базовый тайм-аут вида, удваивается при каждом последующем тайм-ауте до 8×
--verify-chainquickпроверка целостности при запуске: off, quick (структура и воспроизведение реестра), full (дополнительно каждая подпись и QC)
--keep-raw-proofsвыкл.архивный режим для агрегации блоков; на chain 20 не действует
--prune-historyвыкл. (хранить всё)хранить реестр и только такой объём истории блоков: <n>m, <n>h или <n>d, не меньше 1h. Валидаторы testnet работают с 24h, кроме двух архивных; см. Прореживание истории
--min-free-disk-mb1024отказаться запускаться, если на файловой системе каталога данных свободного места меньше этого значения; rand_getHealth отвечает disk_low, когда его меньше чем вчетверо больше порога. 0 отключает защиту
--prover <ADDR>выкл.разместить делегированного доказывающего на этом адресе, отдельном слушателе, а не на RPC; см. ниже
--gas-price, --byte-price100, 800нижняя граница цены в пуле для вызова в цепи без секции gas. Genesis chain 20 задаёт собственные цены, поэтому узел игнорирует оба флага и записывает в журнал предупреждение

--prover-accept-spend-key выведен из употребления: узел v0.6.8, которому его передали, отказывается запускаться, поэтому удалите его из любого unit-файла, перенесённого с v0.6.7.

Три указанных выше bootstrap-узла — валидаторы C, D и E, без изменений по сравнению с chain 19. Chain 20 работает на ключах валидаторов, созданных для chain 14, поэтому их peer id — те же, что использовала chain 14; p2p-идентичность выводится из ключа валидатора. Большинство валидаторов хранят блоки лишь за сутки, поэтому узлу, синхронизирующемуся с genesis или отстающему более чем на сутки, нужен ещё и пир с полной историей. Добавьте один из двух архивных:

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

Опустите --validator, чтобы запустить наблюдателя. Запуск проходит так: открыть хранилище, сверить гостевые программы этой сборки с hc_bundle и hc_auth из genesis, выполнить проверку целостности (обрезав повреждённый хвост и сохранив состояние безопасности голосования), возобновить консенсус с сохранённой головы, запустить сеть и RPC, затем синхронизироваться с любого пира, который впереди. RUST_LOG по умолчанию равен info,libp2p=warn,libp2p_mdns=off; Ctrl-C или SIGTERM, сигнал остановки systemd, корректно завершает работу. Перезапуск с тем же --datadir возобновляет работу с сохранённой головы.

Правила топологии из fullnode: узлы за NAT подключаются к узлам с публичными адресами; на публичных узлах откройте входящий TCP 30303; привязывайте RPC к 127.0.0.1, если он не защищён межсетевым экраном — он принимает транзакции от любого, кто до него дотянется, а после импорта в него viewing key порт следует считать хранящим ключ. Держите --block-interval-ms равным 1000 или больше: доказательство пакета занимает около двух минут, а его якорь действителен 1 024 блока на chain 20 (256 на chain 19).

Валидатору нужны синхронизированные часы. Начиная с chain 14 настенное время — часть консенсуса (усиление моста B2). Блок, временная метка которого более чем на 60 s позже временной метки родителя, недействителен, а валидатор удерживает голос за блок, временная метка которого более чем на 15 s опережает его собственные часы. Поэтому валидатор с отстающими часами перестаёт голосовать, не записывая в журнал никакой ошибки, а для фиксации на chain 20 должны проголосовать восемнадцать из двадцати шести. Запустите NTP на каждом валидаторе и проверьте его до запуска узла:

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

Зарезервированные пиры

Начиная с v0.6.8 узел принимает не более 256 установленных и 64 полуоткрытых входящих соединений, а libp2p-идентичность создаётся бесплатно, так что посторонние могли бы занять все слоты. Резервируются три вида пиров: они допускаются сверх лимита и обслуживаются из доли бюджета синхронизации, отведённой валидаторам. Это каждый пир из --bootstrap, каждый --reserved-peer и каждая идентичность валидатора, полученная из подписанной привязки пира. Каждый узел с --validator подписывает свою libp2p-идентичность ключом валидатора и распространяет её по gossip; узел, знающий этот ключ из набора валидаторов, резервирует этого пира и сохраняет запись между перезапусками. В хранилище только что созданной цепи привязок нет, поэтому до прихода первых объявлений резервируются только первые два вида: при создании цепи передайте peer id валидаторов парка через --reserved-peer или оставьте их в списке bootstrap (rand-node address --key <file> выводит peer id ключа). rand_status.reserved_peers их подсчитывает; на узле парка он должен достичь числа валидаторов в течение минуты после запуска.

Проверка

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

rand_status сообщает 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, address и peer_id узла, диагностику синхронизации (sync_inflight_age_ms, sync_failures, sync_late_batches), диск (disk_free_bytes, disk_low) и нижнюю границу хранения (prune_floor, 0 на узле, который хранит всё; prune_history_secs, null, если не задан). На chain 20 ожидайте hc_bundle 60af094a…, hc_auth 1e4e347f…, faucet true, confidential true, testnet true, binding_domain 1, fri_profile "production" и gas_prices в пределах от 100 до 10 000 за gas и от 800 до 80 000 за KiB. rand_getGenesisHash должен отвечать 6210cf07…5135, а rand_getVersion — 0.6.8.

rand_getHealth отвечает disk_low (с free_bytes) раньше, чем ok, syncing или behind, когда свободного места меньше, чем вчетверо больше --min-free-disk-mb.

Прореживание истории

Узел, запущенный с --prune-history 24h, хранит реестр (заметки, нуллификаторы, якоря, реестр валидаторов, программы) целиком и удаляет более старые блоки, их сертификаты, транзакции и квитанции. Проход выполняется на каждом 16-м зафиксированном блоке и удаляет до 512 блоков старше временной метки головы за вычетом окна; genesis, голова и её родитель не удаляются никогда, а нижняя граница только растёт. Место на диске возвращается после компактации RocksDB. На chain 20, как и на chain 19, 24 из 26 валидаторов работают с 24h и хранят блоки примерно за сутки. Когда прореживание включили для chain 14 (2026-09-25), таблицы блоков каждого валидатора уменьшились с 19 GB до 3,3–4,4 GB после того, как накопившийся объём был обработан; два других, obs1 и rand-archive-2, с 2026-09-27 тоже валидаторы, работают без этого флага и хранят всё. Узел mainnet никогда не передаёт этот флаг.

На прореженном узле поиск по высоте ниже нижней границы (rand_getBlockByHeight, rand_getFinality по высоте, диапазон rand_getBlocks или rand_getCompactBlocks, достигающий её) возвращает ошибку -32010 с data.floor: обратитесь к архивному узлу. Поиск по хешу возвращает null для блока, которого у узла больше нет, как и для блока, которого никогда не существовало. Сканирование кошельком это не затрагивает, поскольку заметки и нуллификаторы не прореживаются никогда.

Проверка при запуске прореженного узла структурная (блок 0 и сохранённые блоки, их QC, связи с родителями и лидеры), а не воспроизведение с genesis, и сама она не восстанавливается: если проверка не проходит, выполните повторную синхронизацию с архивного узла. Не открывайте каталог данных прореженного узла сборкой старше v0.5.7, если она не запущена с --verify-chain off: её проверка по умолчанию воспроизводит цепь с genesis, обнаруживает отсутствие блока 1 и обрезает реестр до genesis.

Размещение доказывающего узла

На chain 20 кошелёк строит два доказательства для каждого пакета, и для большего нужно около 6 GB памяти. Кошелёк, который не может столько выделить, вправе передать это доказательство делегированному доказывающему, с которым он сопряжён: отдельному бинарному файлу rand-prover или той же службе, размещённой на узле.

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 выводит ссылку randprover:. Кошелёк принимает её командой rand prover pair <link>, а затем строит доказательства через неё с помощью глобальной опции --prover. Слушатель prover’а у него собственный и никогда не является методом RPC: адрес, совпадающий с --rpc, отклоняется. С --prover узел отказывается запускаться, если нет файла <prover-home>/prover.key.json (по умолчанию <datadir>/prover) или если не доступно 5,74 GB на каждый слот доказательства плюс 1 GiB памяти; --prover-max-parallel задаёт число слотов, по умолчанию один.

В chain 20 prover получает от кошелька ключ просмотра и свежую соль, но не ключ расходования. Доказательство авторизации над ключом расходования кошелёк строит сам, поэтому подключённый prover может читать всю историю этого кошелька, но потратить средства не может. Держите слушатель на loopback, если перед ним не стоит прокси с терминированием TLS, и не размещайте его на машине, которая обслуживает публичный RPC. Остановка дожидается доказательства, которое строится в данный момент, поэтому для юнита systemd задайте TimeoutStopSec=180.

Проверка и восстановление

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

Сначала остановите узел. Код возврата 0, если данные согласованы, и 2, если найдена проблема, а --repair не указан. Воспроизведение заново проверяет доказательство каждого пакета, поэтому сборка с набором ограничений, отличным от того, на котором создана цепочка, останавливается на первом же пакете.

Как стать валидатором

Валидатор присоединяется к работающей цепочке через бондинг, а не через новый genesis: запустите узел с --validator, выведите регистрацию командой rand-node register --key node.key.json --payout <rand1…> --v2 (chain 20 отклоняет регистрацию v1), затем с кошелька внесите бонд не менее 1 000 RAND командой rand bond <address> 1000 --registration <hex>; узел начнёт предлагать блоки, когда бонд активируется: в chain 20 не раньше чем через три границы эпох по 1 000 блоков после бонда. В chain 20 бонд делается вторым шагом. Её genesis задаёт staking.admission_by_vote, поэтому бонд, регистрирующий новый ключ, отклоняется, пока за этот ключ не проголосуют валидаторы, обладающие более чем двумя третями веса голосов: каждый подписывает командой rand-node admit sign --candidate <key or registration> --key <validator key> --genesis-hash 6210cf071a390d7ac61d8cbea5dd9d139d1a493a862b54a45a498f36af2d5135, а один из них отправляет голоса командой rand-node admit submit (Командная строка). rand_getAdmitted перечисляет ключи, за которые проголосовали, но которые ещё не зарегистрированы. На одной границе эпохи новым весом может стать не более 2 500 bps активного веса. Подробный разбор в разделе Стейкинг. Частную тестовую цепочку создаёт rand-node genesis:

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

а scripts/local-testnet.sh запускает двух валидаторов на одной машине с RPC на 8545 и 8546. Без дополнительных флагов в genesis нет hc_auth, секции gas и memo. Chain 20 была создана с флагами --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 сверх перечисленных (у chains 18 и 19 флаги были до --envelope-bytes включительно), а её секции bridge и staking и consensus_domain были добавлены скриптом создания. Для новой собственной цепочки передавайте --binding-domain 1: кошелёк v0.6.8 строит доказательство в форме, привязанной к genesis, для любого идентификатора цепочки вне диапазона с 14 по 19.

Развёртывание на сервере

В каталоге deploy/ репозитория лежат скрипты, которыми пользуется парк серверов: deploy/push-to-vps.sh <ip> <letter> "<bootstraps>" [validator|observer] разворачивает Linux-сервер (rsync исходного кода, сборка, установка /usr/local/bin/{rand-node,rand}, инициализация datadir по хешу genesis, установка systemd-сервиса rand-node), а deploy/rebuild-vps.sh <ip> пересобирает по новому коммиту и перезапускает узел, сохраняя данные. Две оговорки. Скрипт развёртывания инициализирует узел из deploy/genesis.json, а это не файл chain 20, поэтому узел для testnet настраивается вручную из deploy/genesis-chain20.json (в main, см. выше). И оба скрипта выполняют rsync /root/fullnode с --delete, так что ключ, лежащий внутри этого дерева, будет удалён при следующей пересборке: храните его в /root/keys, как это делает парк серверов и как теперь по умолчанию делает скрипт развёртывания v0.6.8 (скрипт v0.6.7 записывал ключ в /root/fullnode/deploy/). Управление сервисом: systemctl status rand-node, journalctl -u rand-node -f. На сервере нужны build-essential clang cmake pkg-config libssl-dev и набор инструментов Rust. Сервер с 1 vCPU и 2 GB не может собрать бинарные файлы; соберите их в другом месте и скопируйте. Размеры и пошаговую настройку описывает раздел Запуск валидатора и агрегатора.

Обозреватель

randscan.org работает рядом с валидатором E и индексирует chain 20; его REST API и поток WebSocket — самый простой способ читать цепочку без узла (API обозревателя). Обозреватель имеет открытый исходный код (randprotocol/randscan), а его индексатору нужны только JSON-RPC rand_* узла и база данных PostgreSQL, поэтому его можно запустить поверх собственного узла.

Последняя проверка по коммиту fullnode c0878c3f (chain 21) от 2026-10-12. Если код и эта страница расходятся, прав код; сообщите нам.