SHA256
Архивация в Arweave
This commit is contained in:
@@ -0,0 +1,415 @@
|
||||
# Деплой SHiNE Archive Publisher v1.0 на тестовый сервер
|
||||
|
||||
Документ рассчитан на человека или автономного coding/deploy агента. Выполнять шаги по порядку. Не включать publisher до проверки Solana-программы и ключей.
|
||||
|
||||
## 0. Что именно меняется
|
||||
|
||||
Нужны изменения одновременно в:
|
||||
|
||||
1. серверном Java-коде;
|
||||
2. PostgreSQL schema v22;
|
||||
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 <TARGET_RPC_URL>
|
||||
```
|
||||
|
||||
Если в проекте используется рабочий Anchor deploy workflow, допустимо использовать его вместо прямого `solana program deploy`; главное — сохранить тот же Program ID.
|
||||
|
||||
После обновления:
|
||||
|
||||
```bash
|
||||
solana program show SHiNEPr1APdAgNBteUyBXcNovaHctpSjUu8oH2ZJdN6 --url <TARGET_RPC_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 v22 сделать backup тестовой БД. Например:
|
||||
|
||||
```bash
|
||||
pg_dump -Fc -d '<DATABASE_URL_OR_NAME>' -f shine-before-archive-v22.dump
|
||||
```
|
||||
|
||||
Точная команда зависит от текущей схемы доступа PostgreSQL.
|
||||
|
||||
При старте сервер сам применит `migration_v22.sql`, если `db_schema_version < 22`. Вручную migration выполнять обычно не нужно.
|
||||
|
||||
После старта проверить:
|
||||
|
||||
```sql
|
||||
SELECT * FROM db_schema_version WHERE id=1;
|
||||
```
|
||||
|
||||
Ожидается:
|
||||
|
||||
```text
|
||||
schema_version = 22
|
||||
```
|
||||
|
||||
И наличие:
|
||||
|
||||
```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 <SERVICE_NAME> -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.<REAL_TX_ID>.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 = '<SERVER_LOGIN>';
|
||||
```
|
||||
|
||||
`archive_head_tx_id` и `archive_head_hash` должны быть непустыми.
|
||||
|
||||
## Arweave
|
||||
|
||||
TX берётся прямо из имени финального файла. Проверить:
|
||||
|
||||
```bash
|
||||
curl -sS 'https://arweave.net/tx/<TX_ID>/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` действительно обновлена этой версией.
|
||||
Reference in New Issue
Block a user