Local validator дає ізольоване середовище з повним контролем над станом леджера, слотами та набором програм. Це незамінний інструмент, коли потрібно перевірити поведінку смарт-контрактів із конкретними акаунтами, змоделювати граничні стани та відтворити баги без ризику для реальних коштів. Нижче — покрокова конфігурація solana-test-validator із фокусом на production-подібне тестування.

Запуск solana-test-validator з потрібними параметрами

Завантаження акаунтів та програм

Базовий запуск без параметрів створює порожній леджер. Для реалістичного тестування зазвичай потрібні конкретні програми та акаунти. Ключові прапорці:

  • --bpf-program <ADDRESS> <PATH> — завантажує скомпільовану програму (.so) за вказаною адресою. Корисно, коли потрібно тестувати з конкретною версією програми, яка ще не розгорнута на devnet.
  • --account <ADDRESS> <FILENAME> — завантажує стан акаунта з JSON-файлу, який можна експортувати через solana account.
  • --account-dir <PATH> — масово завантажує всі акаунти з каталогу. Зручно, коли є набір підготовлених станів для тест-сьютів.
  • --warp-slot <SLOT> — перемотує леджер до вказаного слота. Допомагає перевірити логіку, що залежить від епох чи конкретних часових точок.
  • --rpc-port та --faucet-port — дозволяють уникнути конфліктів портів, якщо запущено кілька інстансів.

Приклад команди для запуску з власною програмою та кількома акаунтами:

solana-test-validator --bpf-program 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU ./target/deploy/your_program.so --account TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb ./accounts/token_2022.json --account-dir ./test-accounts/ --rpc-port 8899 --warp-slot 250000000

Очікуваний результат: валідатор стартує, у логах підтверджується завантаження програми та акаунтів, RPC доступний на вказаному порту. Перевірити можна через solana cluster-version або виклик getHealth.

Типова помилка: адреса у прапорці --bpf-program не збігається з адресою, за якою програма скомпільована у Anchor.toml. Причина — розбіжність між ключами у target/deploy/ та тим, що передано в CLI. Рішення — брати адресу з файлу target/deploy/your_program-keypair.json.

Клонування стану з mainnet-beta

Прапорець --clone <ADDRESS> дозволяє завантажити акаунт безпосередньо з mainnet-beta через публічний RPC. Це критично, коли програма взаємодіє зі складними станами, які неможливо відтворити локально вручну — наприклад, великі пули ліквідності або токен-акаунти з нестандартними налаштуваннями.

Приклад клонування Token-2022 програми та конкретного токен-акаунта:

solana-test-validator --clone TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --clone 7f2YyE2qGo8ZMqjLpXzZ6DkCw9Th8QRk1vqXFYvXxMj7

Обмеження: клонування завантажує лише сам акаунт, не його залежності. Якщо акаунт посилається на інші акаунти (власник, мінт, авторитет), їх також треба клонувати окремо. Інакше транзакції, що залежать від ланцюжка акаунтів, завершаться помилкою AccountNotFound.

Ризик: при масовому клонуванні через публічний RPC можливі rate limits. Для стабільного робочого процесу варто налаштувати власний RPC-вузол або використовувати кешовані знімки.

План відкату: якщо клонований стан призводить до непередбачуваної поведінки, видаліть каталог леджера (за замовчуванням ./test-ledger) та перезапустіть валідатор без прапорця --clone.

Налаштування знімків для швидкого перезапуску

За замовчуванням solana-test-validator створює знімки леджера періодично (кожні 100 слотів). При перезапуску з тим самим шляхом --ledger валідатор автоматично відновлює стан з останнього знімка, що значно скорочує час підготовки середовища.

Параметри керування знімками:

  • --snapshot-interval-slots <N> — частота створення знімків. Зменшення значення прискорює відновлення при перезапуску, але збільшує дискове навантаження під час роботи.
  • --incremental-snapshot-interval-slots <N> — частота інкрементальних знімків. Дозволяє зменшити розмір кожного окремого знімка ціною складнішої логіки відновлення.

Практична схема для CI/CD та частих перезапусків:

  1. Перший запуск — з повним набором --clone та --bpf-program. Валідатор працює, створює початковий знімок.
  2. Усі наступні запуски в межах тієї ж сесії — з тим самим --ledger без повторного клонування. Валідатор піднімається з локального знімка за секунди.
  3. Очищення — видалення каталогу леджера, коли потрібен свіжий стан.

Перевірка: після перезапуску виконайте solana slot — значення має відповідати слоту, на якому було створено останній знімок, а не нулю.

Типова помилка: зміна набору програм або акаунтів у CLI без видалення старого леджера. Причина — валідатор відновлює стан із кешованого знімка, ігноруючи нові прапорці. Рішення — перед зміною конфігурації обов'язково видалити каталог леджера.

Обмеження local validator порівняно з mainnet

Розуміння цих обмежень запобігає хибним висновкам під час тестування:

Аспект Local validator Mainnet-beta
Консенсус Відсутній — єдиний вузол, транзакції підтверджуються миттєво Підтвердження через Tower BFT, затримки між слотами
Час між слотами Мінімальний, обмежений лише обчисленнями ~400 мс цільовий час
Мережева затримка Відсутня — все на localhost Реальні затримки між валідаторами
MEV Не моделюється Активне середовище з фронтранінгом та арбітражем
Економіка Симуляційна, SOL з faucet без реальної вартості Реальні комісії, інфляція, стейкінг
Доступні програми Лише завантажені явно або клоновані Усі розгорнуті програми
Розмір леджера Обмежений пам'яттю машини, прапорець --limit-ledger-size Тербайти історичних даних
Транзакційна пропускна здатність Обмежена одним потоком обробки Паралельна обробка через Sealevel

Наслідки для тестування:

  • Транзакції, що проходять локально, можуть не пройти на devnet/mainnet через відсутність потрібних програм у ланцюжку акаунтів. Перевіряйте, що всі залежності завантажені.
  • Таймінг-залежна логіка (наприклад, перевірка clock.sysvar) поводиться інакше. Використовуйте --warp-slot для контролю.
  • Конкурентні транзакції, що модифікують спільний стан, локально обробляються послідовно. На mainnet порядок не гарантований.
  • Комісії за обчислення (compute units) локально розраховуються коректно, але реальний вплив на пріоритезацію транзакцій не моделюється.

Інтеграція з Anchor test framework

Anchor автоматично керує життєвим циклом solana-test-validator у тестах. Конфігурація валідатора задається у Anchor.toml у секції [test.validator].

Базова конфігурація:

[test]
startup_wait = 10000

[test.validator]
rpc_port = 8899
warp_slot = 250000000

Завантаження програм та клонування акаунтів через Anchor:

[[test.validator.clone]]
address = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"

[[test.validator.account]]
address = "7f2YyE2qGo8ZMqjLpXzZ6DkCw9Th8QRk1vqXFYvXxMj7"
filename = "tests/fixtures/pool_account.json"

Критичний нюанс: Anchor завантажує програму з проєкту автоматично за адресою, вказаною в Anchor.toml у полі program_id. Не потрібно додатково вказувати її через bpf_program — це призведе до конфлікту адрес.

Параметр startup_wait: час у мілісекундах, який тестовий ранер чекає після запуску валідатора перед виконанням тестів. При клонуванні великих акаунтів або кількох програм це значення треба збільшувати. Недостатній startup_wait — найчастіша причина помилок Connection refused на початку тест-сьюту.

Знімки в контексті Anchor: Anchor за замовчуванням створює тимчасовий каталог леджера для кожного запуску anchor test і видаляє його після завершення. Це означає, що автоматичного відновлення зі знімків між запусками не відбувається. Для збереження знімків між запусками можна вказати фіксований шлях через ledger у секції [test.validator], але тоді треба самостійно керувати очищенням.

Перевірка інтеграції: додайте на початок тест-сьюту логування підключення до RPC та запит поточного слота. Якщо слот відповідає warp_slot або ненульовий при використанні знімків — валідатор піднявся коректно.

Типова помилка: тести проходять окремо, але падають при запуску всього сьюту разом. Причина — спільний стан валідатора між тестами: один тест змінює акаунт, інший очікує початковий стан. Рішення — або ізолювати тести в окремі файли з окремими конфігураціями валідатора, або явно скидавати стан у beforeEach.

Наступний крок: після налаштування стабільного локального середовища варто перейти до перевірки транзакцій через simulation — це дозволить оцінити споживання compute units та поведінку за межами локального валідатора.

Джерела