# Деплой SHiNE Archive Publisher v1.0 на тестовый сервер Документ рассчитан на человека или автономного coding/deploy агента. Выполнять шаги по порядку. Не включать publisher до проверки Solana-программы и ключей. ## 0. Что именно меняется Нужны изменения одновременно в: 1. серверном Java-коде; 2. PostgreSQL schema v23; 3. Solana-программе `shine_users` (PDA block type `100` + backward-compatible update parser); 4. Java/JS User PDA codecs. **Критично:** новый серверный writer отправляет расширенный обычный `update_user_pda`. Если в целевом кластере работает старая `shine_users`, включать publisher нельзя. --- # 1. Применить пакет к исходникам ZIP из этой поставки содержит только новые/изменённые файлы с путями относительно корня репозитория. Сделать backup текущего проекта, затем распаковать ZIP поверх рабочего дерева. После распаковки удалить legacy-файлы из списка `05_PATCH_CONTENTS_AND_REMOVALS.md`. Проверить: ```bash git status --short ``` или, если это не git checkout, сравнить список файлов с manifest из той же документации. --- # 2. Обязательно обновить `shine_users` в нужном Solana-кластере ## 2.1. Проверить целевой кластер Не деплоить вслепую. Сначала: ```bash solana config get ``` и проверить RPC/кластер, upgrade authority и Program ID. В проекте `shine_users` использует Program ID: ```text SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6 ``` Если тестовый сервер использует mainnet RPC, обновляется именно mainnet-программа. Если тестовый контур использует devnet — сначала убедиться, что программа с нужным ID действительно существует в devnet. ## 2.2. Собрать Solana program ```bash cd shine-solana/shine anchor build ``` Минимальная host-проверка Rust, если Anchor/SBF toolchain временно недоступен: ```bash cargo build -p shine_users ``` Но для реального deploy нужен SBF/Anchor build. ## 2.3. Обновить существующую программу Использовать существующий project deploy/upgrade authority. Типовой вариант: ```bash solana program deploy target/deploy/shine_users.so \ --program-id target/deploy/shine_users-keypair.json \ --upgrade-authority /PATH/TO/UPGRADE_AUTHORITY.json \ --url ``` Если в проекте используется рабочий Anchor deploy workflow, допустимо использовать его вместо прямого `solana program deploy`; главное — сохранить тот же Program ID. После обновления: ```bash solana program show SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6 --url ``` --- # 3. Собрать серверный JAR Из корня репозитория: ```bash ./gradlew clean shadowJar ``` Ожидаемый файл: ```text SHiNE-server/build/libs/shine-server.jar ``` Если Gradle wrapper не может скачать зависимости, сборку выполнять на машине/CI с доступом к Maven/Gradle или с уже заполненным cache. --- # 4. Подготовить PostgreSQL backup Перед первым стартом версии со schema v23 сделать backup тестовой БД. Например: ```bash pg_dump -Fc -d '' -f shine-before-archive-v22.dump ``` Точная команда зависит от текущей схемы доступа PostgreSQL. При старте сервер последовательно применит `migration_v22.sql` и `migration_v23.sql`, если это требуется текущей версии БД. Вручную migrations выполнять обычно не нужно. После старта проверить: ```sql SELECT * FROM db_schema_version WHERE id=1; ``` Ожидается: ```text schema_version = 23 ``` И наличие: ```sql SELECT to_regclass('public.archive_chain_cursor'); SELECT to_regclass('public.archive_publish_job'); SELECT to_regclass('public.archive_publish_job_chain'); ``` --- # 5. Подготовить секреты на тестовом сервере Пример: ```bash sudo -u player mkdir -p /home/player/SHiNE/secrets sudo chmod 700 /home/player/SHiNE/secrets ``` Положить: ```text /home/player/SHiNE/secrets/archive-arweave-wallet.json /home/player/SHiNE/secrets/server-root.key /home/player/SHiNE/secrets/server-client.key ``` Права: ```bash 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-секцию. Минимум: ```properties 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 использует: 1. `solana.users.sync.rpcUrl`, если он задан; 2. иначе `solana.rpcUrl`. Поэтому существующий рабочий RPC не дублировать в archive settings. --- # 7. Убедиться, что server login соответствует ключам `server.SHiNE.login` должен быть тем User PDA, чей archive head будет обновляться. На startup `SolanaArchiveHeadWriter` проверяет: ```text 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 Смотреть лог: ```bash sudo journalctl -u -f ``` Ожидаемые события: 1. DB migration до v22; 2. обычный Solana users sync; 3. обычный server-to-server blockchain sync; 4. archive publisher preflight; 5. строка примерно: ```text 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`); - создаёт локальный файл, например: ```text data/archive/archive01.00001.12.09.26.tmp.SHiNE-archive ``` - загружает его в Arweave; - после успешной загрузки переименовывает тот же файл, например: ```text data/archive/archive01.00001.12.09.26..SHiNE-archive ``` - ждёт confirmations; - обычным `update_user_pda` записывает block type `100`; - ждёт Solana `finalized`; - только затем commit-ит cursors. Если новых данных нет, пустой big block не создаётся. --- # 11. Проверка результата ## Локальные файлы ```bash ls -lah data/archive/ ``` После успешного upload `.tmp.SHiNE-archive` для завершённого job оставаться не должен; должен быть файл с реальным TX ID в имени. ## Job DB ```sql 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; ``` Успех: ```text status = CURSORS_COMMITTED ``` ## Cursors ```sql 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: ```sql SELECT login, record_number, archive_head_tx_id, archive_head_hash FROM solana_user_pda_current WHERE login = ''; ``` `archive_head_tx_id` и `archive_head_hash` должны быть непустыми. ## Arweave TX берётся прямо из имени финального файла. Проверить: ```bash curl -sS 'https://arweave.net/tx//status' ``` --- # 12. Быстрый тест до полуночи Если не хочется ждать 00:00, на тестовом сервере временно установить `archive.publish.time` на ближайшие 5–10 минут в будущем в выбранной `archive.publish.zoneId`, затем перезапустить сервис. После проверки вернуть: ```properties archive.publish.time=00:00 ``` Не использовать интервал в минутах: scheduler специально работает по календарному локальному времени один раз в сутки. --- # 13. Откат / выключение Самый безопасный функциональный rollback: ```properties 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 можно оставить выключенным, но разрешить импорт от конкретного архиватора: ```properties archive.publish.enabled=false archive.import.allowedPublishers= archive.import.intervalMinutes=60 archive.import.workDir=data/archive-import ``` Несколько логинов: ```properties archive.import.allowedPublishers=server-a,server-b ``` Пустая строка означает, что importer не запускается. После старта проверить логи: ```text Archive importer включён. approvedPublishers=... ``` Если сервер подключается к publisher впервые, importer скачает head, прочитает FULL reference table и обработает все ещё не известные big blocks от старых к новым. Проверка БД: ```sql 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 файл должен быть доступен по: ```text https:///Blockchain-Viewer.html ``` В приложении: `Настройки → Архив блокчейна`.