Staging-середовище для Solana-програм — це локальний або ізольований тестовий валідатор із реальними даними з mainnet-beta, окремим індексатором та повним циклом розгортання. Його мета — дати впевненість, що оновлення програми не зламає існуючі акаунти, CPI-виклики та бізнес-логіку перед деплоєм у production. Нижче — покрокова інструкція з конкретними командами, межами застосування та планом відкату.
Клонування стану з mainnet-beta
Локальний тестовий валідатор (solana-test-validator) дозволяє завантажити стан конкретних акаунтів безпосередньо з mainnet-beta. Це базова передумова для staging: без реальних даних тестування CPI та міграцій стану не має сенсу.
Підготовка та передумови
- Solana CLI — встановлена та оновлена до актуальної версії (перевірте командою solana --version); версія CLI має збігатися з версією target-кластера, інакше поведінка бінкодів може відрізнятися.
- Доступ до mainnet-beta RPC — публічний або приватний ендпойнт із достатнім лімітом запитів (клонування великих акаунтів генерує багато getAccountInfo-викликів).
- Список акаунтів для клонування — програмний акаунт, ключові PDA (пули, скарбниці, конфігурації), а також акаунти, з якими ваша програма взаємодіє через CPI.
Клонування акаунтів
Базовий спосіб — прапорець --clone, який завантажує акаунт із вказаного RPC:
solana-test-validator --url https://api.mainnet-beta.solana.com --clone <ACCOUNT_PUBKEY> --clone <ANOTHER_PUBKEY>
Для програм additionally потрібен прапорець --bpf-program, який розгортає скомпільований .so-файл за фіксованим адресом:
solana-test-validator --url https://api.mainnet-beta.solana.com --bpf-program <PROGRAM_ID> target/deploy/your_program.so --clone <PROGRAM_ID> --clone <IMPORTANT_PDA_1> --clone <IMPORTANT_PDA_2>
Обмеження клонування
- Не клонуються всі акаунти кластера. Тільки ті, що ви вкажете явно. Якщо ваша програма читає стан стороннього акаунта, який ви не клонували — отримаєте AccountNotFound.
- Sysvar-акаунти (Clock, Rent, EpochSchedule) ініціалізуються тестовим валідатором із нульовими значеннями. Якщо ваша логіка залежить від конкретного слоту чи епохи — результати будуть відрізнятися від mainnet.
- Розмір акаунта. Якщо акаунт перевищує ліміт getAccountInfo на вашому RPC-провайдері, клонування завершиться помилкою. У такому разі потрібен RPC із підвищеними лімітами або завантаження через snapshot.
- Snapshot-клонування (--snapshot <PATH>) дає повніший стан, але snapshot mainnet-beta важкий (терабайти) і містить усі програми кластера, що ускладнює ізоляцію вашого сценарію.
Перевірка та відкат
Після старту валідатора виконайте:
solana account <CLONED_PUBKEY> --url http://127.0.0.1:8899
Очікуваний результат — дані акаунта збігаються з mainnet-beta (порівняйте з тим самим запитом до mainnet RPC). Якщо дані порожні або відмінні — перевірте логи валідатора на наявність помилок завантаження.
План відкату: зупиніть валідатор (Ctrl+C), видаліть локальний тестовий каталог (test-ledger за замовчуванням) і перезапустіть із правильним списком клонованих акаунтів. Стан mainnet-beta при цьому не змінюється.
Налаштування окремого індексатора
Staging без індексатора дозволяє тестувати транзакції, але не дозволяє тестувати ті шари вашого застосунку, які читають дані через індексатор (дашборди, бекофіс, агрегації). Окремий індексатор для staging має бути повністю ізольований від production-бази даних.
Архітектурні рішення
- Тип індексатора. Якщо в production ви використовуєте Helius Custom Indexer, Metaplex Digital Asset API або самописний індексатор на базі geyser-plugin — у staging має працювати той самий тип. Інакше ви тестуєте не свій стек.
- База даних. Окрема інстанція PostgreSQL (або іншої СУБД, яку використовує ваш індексатор). Ніколи не підключайте staging-індексатор до production-бази, навіть у режимі читання — це створює ризик випадкових записів.
- Geyser-plugin підключення. Якщо індексатор працює через geyser-plugin, у конфігурації тестового валідатора додайте секцію --geyser-plugin-config <PATH>, де конфіг вказує на staging-базу даних.
Кроки налаштування
- Розгорніть окрему базу даних та застосуйте ті самі міграції схеми, що й у production. Схема має бути ідентичною — розбіжності в колонках чи типах призведуть до помилок, які не репрезентують реальні проблеми.
- Скопіюйте конфігураційний файл індексатора та змініть у ньому: RPC-ендпойнт (на http://127.0.0.1:8899), рядок підключення до бази даних (на staging), програмні ID (якщо вони відрізняються).
- Запустіть індексатор. Оскільки тестовий валідатор починає працювати з клонованим станом, індексатор має проіндексувати початковий стан і далі підхоплювати оновлення в реальному часі.
Типові проблеми
- Індексатор не підхоплює оновлення. Причина: geyser-plugin не підключено до валідатора, або конфігурація geyser вказує на неправильний порт. Перевірте логи валідатора на наявність повідомлень про geyser-plugin.
- Помилки десеріалізації. Причина: програма в staging скомпільована з іншою версією бінкодів, ніж дані, які були клоновані з mainnet. Це очікувана поведінка при тестуванні міграцій — але якщо ви не планували міграцію, це сигнал про невідповідність версій.
План відкату: зупиніть індексатор, скиньте staging-базу даних (DROP та повторне застосування міграцій), виправте конфігурацію та перезапустіть.
Тестування повного циклу в staging
«Повний цикл» означає: клієнтський запит → RPC → транзакція → виконання програми (включно з CPI) → оновлення стану → індексація → читання через індексатор. Кожен етап має бути перевірений окремо.
Перевірка транзакцій
- Симуляція. Перед відправкою виконайте solana simulate-transaction або використовуйте RPC-метод simulateTransaction. Перевірте, що логи не містять помилок і споживання compute units відповідає очікуваному.
- Відправка та підтвердження. Відправте транзакцію з skipPreflight: false. Перевірте статус через getSignatureStatuses — має бути confirmationStatus: "finalized".
- Перевірка стану. Після фіналізації прочитайте оновлені акаунти через RPC та переконайтеся, що дані змінилися коректно.
Тестування CPI-викликів
Якщо ваша програма викликає інші програми через Cross-Program Invocation, переконайтеся, що ці програми також присутні в staging. Для стандартних програм (System Program, Token Program, Associated Token Account Program) тестовий валідатор надає вбудовані реалізації. Для сторонніх програм (оракли, DEX-и, lending-протоколи) їх треба додати через --bpf-program або --clone їхніх акаунтів.
Типова помилка: CPI до програми, яка відсутня в staging, призводить до InstructionError::InvalidAccountData або AccountNotFound. Фактична причина — не брак привілеїв, а саме відсутність цільової програми в тестовому кластері.
Тестування через індексатор
Після виконання транзакції перевірте, що індексатор оновив відповідні записи в базі даних. Зробіть запит до staging-API індексатора та порівняйте результат із прямим читанням через RPC. Розбіжності вказують на проблеми десеріалізації, фільтрації або логіки індексатора.
Межі staging-тестування
- Конкурентність та MEV. Локальний валідатор не відтворює реальну чергу транзакцій, конкуренцію за слоти та MEV-стратегії. Тестування захисту від фронтранінгу вимагає devnet або окремого тестового кластера з кількома валідаторами.
- Масштаб. Один локальний валідатор не відтворює навантаження mainnet. Тестування продуктивності індексатора під високим TPS вимагає окремого інструментарію (наприклад, load-генераторів).
- Часові залежності. Оскільки Clock sysvar у staging не відповідає реальному часу mainnet, логіка, що залежить від епох, слотів чи timestamp, потребує окремої уваги та ручної налаштування.
Синхронізація змін між staging та production
Різниця між staging та production має зводитися виключно до конфігурації (RPC-ендпойнти, програмні ID, ключі). Код програми, схема бази даних та логіка індексатора мають бути ідентичними.
Управління програмними ID
У staging програма розгортається за тим самим адресом, що й у production (через --bpf-program <PRODUCTION_ID>). Це критично для коректності клонованого стану: якщо програмний ID відрізняється, усі PDA, похідні від нього, будуть іншими, і клоновані акаунти стануть недоступні.
Якщо ви тестуєте нову версію програми, яка ще не задеплоєна в production, розгортайте її за production-ID у staging. Це дозволить перевірити міграцію стану на реальних даних. Зверніть увагу: після такого тестування в production доведеться виконати фактичну міграцію — про підходи до цього дивіться у розділі Як мігрувати дані між версіями програми.
Конфігурація як єдине джерело відмінностей
Використовуйте змінні середовища або конфігураційні файли для параметрів, що відрізняються:
| Параметр | Staging | Production |
|---|---|---|
| RPC URL | http://127.0.0.1:8899 | https://api.mainnet-beta.solana.com (або приватний RPC) |
| Індексатор DB | staging_db | production_db |
| Ключі підписання | Тестові (не мають доступу до реальних коштів) | Production-ключі (HSM, KMS) |
| WebSocket endpoint | ws://127.0.0.1:8900 | Виробничий WS-ендпойнт |
Репродуктивність збірки
Перед переносом між середовищами переконайтеся, що збірка детермінована. Використовуйте фіксовані залежності (lock-файл Cargo.lock), однакову версію Rust toolchain та однакові features. Збережіть хеш коміту та вивід solana program deploy --print-id для порівняння: програмний ID має збігатися між staging та production збірками.
Процес перенесення
- Збірка в ізольованому середовищі. Скомпілюйте програму один раз, перевірте хеш .so-файлу.
- Розгортання в staging. розгортання, тестування повного циклу, перевірка через індексатор.
- Фіксація артефакту. Збережіть конкретний .so-файл, який пройшов staging-тестування. Не перезбирайте перед production-деплоєм.
- Production-розгортання. Використовуйте той самий артефакт із належними production-ключами.
Типова помилка: перезбірка програми між staging та production призводить до іншого бінкоду (навіть якщо вихідний код не змінювався — через різні версії компілятора, залежностей чи features). Фактична причина невідповідності — відсутність фіксації артефакту, а не логічна помилка в коді.
План відкату в production: якщо після розгортання виникають проблеми, використовуйте збережений .so-файл попередньої версії для відкату через solana program deploy --program-id <ID> <OLD_SO_FILE>. Відкат можливий, оскільки програмний ID не змінюється. Переконайтеся, що попередня версія сумісна з поточним станом акаунтів — інакше відкат може посилити проблему.