16 KiB
Деплой SHiNE Archive Publisher v1.0 на тестовый сервер
Документ рассчитан на человека или автономного coding/deploy агента. Выполнять шаги по порядку. Не включать publisher до проверки Solana-программы и ключей.
0. Что именно меняется
Нужны изменения одновременно в:
- серверном Java-коде;
- PostgreSQL schema v23;
- Solana-программе
shine_users(PDA block type100+ backward-compatible update parser); - Java/JS User PDA codecs.
Критично: новый серверный writer отправляет расширенный обычный update_user_pda. Если в целевом кластере работает старая shine_users, включать publisher нельзя.
1. Применить пакет к исходникам
ZIP из этой поставки содержит только новые/изменённые файлы с путями относительно корня репозитория.
Сделать backup текущего проекта, затем распаковать ZIP поверх рабочего дерева.
После распаковки удалить legacy-файлы из списка 05_PATCH_CONTENTS_AND_REMOVALS.md.
Проверить:
git status --short
или, если это не git checkout, сравнить список файлов с manifest из той же документации.
2. Обязательно обновить shine_users в нужном Solana-кластере
2.1. Проверить целевой кластер
Не деплоить вслепую. Сначала:
solana config get
и проверить RPC/кластер, upgrade authority и Program ID. В проекте shine_users использует Program ID:
SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6
Если тестовый сервер использует mainnet RPC, обновляется именно mainnet-программа. Если тестовый контур использует devnet — сначала убедиться, что программа с нужным ID действительно существует в devnet.
2.2. Собрать Solana program
cd shine-solana/shine
anchor build
Минимальная host-проверка Rust, если Anchor/SBF toolchain временно недоступен:
cargo build -p shine_users
Но для реального deploy нужен SBF/Anchor build.
2.3. Обновить существующую программу
Использовать существующий project deploy/upgrade authority. Типовой вариант:
solana program deploy target/deploy/shine_users.so \
--program-id target/deploy/shine_users-keypair.json \
--upgrade-authority /PATH/TO/UPGRADE_AUTHORITY.json \
--url <TARGET_RPC_URL>
Если в проекте используется рабочий Anchor deploy workflow, допустимо использовать его вместо прямого solana program deploy; главное — сохранить тот же Program ID.
После обновления:
solana program show SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6 --url <TARGET_RPC_URL>
3. Собрать серверный JAR
Из корня репозитория:
./gradlew clean shadowJar
Ожидаемый файл:
SHiNE-server/build/libs/shine-server.jar
Если Gradle wrapper не может скачать зависимости, сборку выполнять на машине/CI с доступом к Maven/Gradle или с уже заполненным cache.
4. Подготовить PostgreSQL backup
Перед первым стартом версии со schema v23 сделать backup тестовой БД. Например:
pg_dump -Fc -d '<DATABASE_URL_OR_NAME>' -f shine-before-archive-v22.dump
Точная команда зависит от текущей схемы доступа PostgreSQL.
При старте сервер последовательно применит migration_v22.sql и migration_v23.sql, если это требуется текущей версии БД. Вручную migrations выполнять обычно не нужно.
После старта проверить:
SELECT * FROM db_schema_version WHERE id=1;
Ожидается:
schema_version = 23
И наличие:
SELECT to_regclass('public.archive_chain_cursor');
SELECT to_regclass('public.archive_publish_job');
SELECT to_regclass('public.archive_publish_job_chain');
5. Подготовить секреты на тестовом сервере
Пример:
sudo -u player mkdir -p /home/player/SHiNE/secrets
sudo chmod 700 /home/player/SHiNE/secrets
Положить:
/home/player/SHiNE/secrets/archive-arweave-wallet.json
/home/player/SHiNE/secrets/server-root.key
/home/player/SHiNE/secrets/server-client.key
Права:
sudo chown player:player /home/player/SHiNE/secrets/*
sudo chmod 600 /home/player/SHiNE/secrets/*
Форматы root/client key
Поддерживаются:
- raw seed 32 bytes;
- raw keypair 64 bytes;
- Solana JSON array на 32/64 байта;
- Base58 seed на 32 байта или Solana secret key на 64 байта;
- Base64 raw/PKCS8, где seed извлекается из последних 32 bytes.
Arweave wallet
Ожидается RSA JWK с полями n,e,d,p,q,dp,dq,qi. Кошелёк должен иметь достаточно AR для размера первого полного архива.
6. Настроить внешний application.properties
Сервер читает внешний application.properties из WorkingDirectory процесса и накладывает его поверх встроенного конфига. Сохранять существующие DB/Solana/server параметры и добавить archive-секцию.
Минимум:
archive.publish.enabled=true
archive.publish.time=00:00
archive.publish.zoneId=Europe/Warsaw
archive.workDir=data/archive
archive.maxFileBytes=4000000000
archive.arweave.gateway=https://arweave.net
archive.arweave.walletJwkPath=/home/player/SHiNE/secrets/archive-arweave-wallet.json
archive.arweave.minConfirmations=1
archive.arweave.confirmPollSeconds=30
archive.arweave.confirmTimeoutMinutes=180
archive.solana.rootKeyPath=/home/player/SHiNE/secrets/server-root.key
archive.solana.clientKeyPath=/home/player/SHiNE/secrets/server-client.key
archive.solana.confirmPollSeconds=5
archive.solana.confirmTimeoutMinutes=30
archive.solana.commitment=finalized
archive.publish.zoneId выбрать осознанно. Если оставить пустым, используется timezone JVM/машины. Для ежедневного запуска ровно в нужную локальную полночь лучше задать ZoneId явно.
Solana RPC
Отдельного archive RPC нет. Writer использует:
solana.users.sync.rpcUrl, если он задан;- иначе
solana.rpcUrl.
Поэтому существующий рабочий RPC не дублировать в archive settings.
7. Убедиться, что server login соответствует ключам
server.SHiNE.login должен быть тем User PDA, чей archive head будет обновляться.
На startup SolanaArchiveHeadWriter проверяет:
derive(root private) == UserPDA.root_key
derive(client private) == UserPDA.client_key
При несовпадении archive publisher не стартует.
8. Развернуть JAR
Можно использовать существующий deploy/scripts/deploy_server.sh. Он:
- собирает
shadowJar; - копирует JAR;
- создаёт/обновляет systemd unit;
- перезапускает сервис.
Типовой запуск задаётся уже существующими переменными проекта. Либо вручную заменить shine-server.jar в рабочей директории и перезапустить systemd service.
После deploy убедиться, что WorkingDirectory содержит внешний application.properties.
9. Первый startup
Смотреть лог:
sudo journalctl -u <SERVICE_NAME> -f
Ожидаемые события:
- DB migration до v22;
- обычный Solana users sync;
- обычный server-to-server blockchain sync;
- archive publisher preflight;
- строка примерно:
Archive publisher включён: login=... dir=data/archive dailyAt=00:00 zone=...
Следующая архивная публикация запланирована на ...
Если остался незавершённый job, он будет продолжен сразу после старта, не ожидая полуночи. Новый snapshot создаётся только по расписанию.
10. Что произойдёт в первую полночь
Если archive_chain_cursor пуст:
- сервер проходит все локально известные
blockchain_name; - для каждой берёт range
0..current_head; - создаёт первый big block
#1; - для каждой blockchain создаёт максимум один
UserBlockchainChunk; - внутри chunk лежат все её raw records из frozen range;
- backlink первого chunk пустой (
previous_big_block_ref = 0xFFFFFFFF); - создаёт локальный файл, например:
data/archive/archive01.00001.12.09.26.tmp.SHiNE-archive
- загружает его в Arweave;
- после успешной загрузки переименовывает тот же файл, например:
data/archive/archive01.00001.12.09.26.<REAL_TX_ID>.SHiNE-archive
- ждёт confirmations;
- обычным
update_user_pdaзаписывает block type100; - ждёт Solana
finalized; - только затем commit-ит cursors.
Если новых данных нет, пустой big block не создаётся.
11. Проверка результата
Локальные файлы
ls -lah data/archive/
После успешного upload .tmp.SHiNE-archive для завершённого job оставаться не должен; должен быть файл с реальным TX ID в имени.
Job DB
SELECT id, big_block_number, status, created_at_ms, local_archive_path,
arweave_confirmations, solana_signature, error_text
FROM archive_publish_job
ORDER BY id DESC
LIMIT 10;
Успех:
status = CURSORS_COMMITTED
Cursors
SELECT blockchain_name,
last_archived_source_block_number,
last_archive_big_block_number,
last_chunk_offset,
last_chunk_size
FROM archive_chain_cursor
ORDER BY blockchain_name;
Локальная проекция User PDA
После очередной Solana sync:
SELECT login, record_number, archive_head_tx_id, archive_head_hash
FROM solana_user_pda_current
WHERE login = '<SERVER_LOGIN>';
archive_head_tx_id и archive_head_hash должны быть непустыми.
Arweave
TX берётся прямо из имени финального файла. Проверить:
curl -sS 'https://arweave.net/tx/<TX_ID>/status'
12. Быстрый тест до полуночи
Если не хочется ждать 00:00, на тестовом сервере временно установить archive.publish.time на ближайшие 5–10 минут в будущем в выбранной archive.publish.zoneId, затем перезапустить сервис.
После проверки вернуть:
archive.publish.time=00:00
Не использовать интервал в минутах: scheduler специально работает по календарному локальному времени один раз в сутки.
13. Откат / выключение
Самый безопасный функциональный rollback:
archive.publish.enabled=false
и рестарт сервера. Тогда обычная серверная работа продолжается, archive scheduler ничего не публикует.
Не удалять archive_chain_cursor/job таблицы без причины: они нужны, чтобы после повторного включения publisher продолжил дельту, а не загрузил всю историю заново.
Уже опубликованные Arweave данные являются постоянными и обычным rollback сервера не удаляются.
14. Наиболее вероятные ошибки
Root key archive publisher-а не совпадает с User PDA
Положен неправильный root key или неверный server.SHiNE.login.
Client key archive publisher-а не совпадает с User PDA
Неверный client key.
Недостаточно AR
Пополнить Arweave wallet. Первый архив может быть существенно больше ежедневных дельт.
Arweave upload прошёл, Solana update не прошёл
Не удалять локальный файл/job. После исправления RPC/program/key причины restart продолжит незавершённый job.
archive cursor hash не совпадает
Локальная blockchain изменилась относительно уже зафиксированного cursor. Не форсировать публикацию; сначала разобраться с resync/fork.
Старый shine_users
Если новый update payload отклоняется программой, проверить, что целевая Solana shine_users действительно обновлена этой версией.
Trusted archive importer
На обычном тестовом сервере publisher можно оставить выключенным, но разрешить импорт от конкретного архиватора:
archive.publish.enabled=false
archive.import.allowedPublishers=<LOGIN_ARCHIVE_SERVER>
archive.import.intervalMinutes=60
archive.import.workDir=data/archive-import
Несколько логинов:
archive.import.allowedPublishers=server-a,server-b
Пустая строка означает, что importer не запускается.
После старта проверить логи:
Archive importer включён. approvedPublishers=...
Если сервер подключается к publisher впервые, importer скачает head, прочитает FULL reference table и обработает все ещё не известные big blocks от старых к новым.
Проверка БД:
SELECT login, archive_head_tx_id, archive_imported, archive_last_imported_tx_id
FROM solana_user_pda_current
WHERE is_server=TRUE AND archive_head_tx_id<>''
ORDER BY login;
SELECT blockchain_name, publisher_login, arweave_tx_id,
big_block_number, chunk_offset, chunk_size, source_last_block_number
FROM archive_blockchain_location
ORDER BY updated_at_ms DESC
LIMIT 20;
После деплоя UI файл должен быть доступен по:
https://<UI_HOST>/Blockchain-Viewer.html
В приложении: Настройки → Архив блокчейна.