SHA256
Архивация в Arweave
This commit is contained in:
@@ -14,8 +14,6 @@
|
||||
| --- | --- | --- |
|
||||
| `GetUser` | `01_User_Registration_API.md` | чтение/проверка пользователя + server-состояние его блокчейна |
|
||||
| `SearchUsers` | `01_User_Registration_API.md` | поиск логинов по префиксу |
|
||||
| `TestGetFreeAvatarQuota` | `14_Test_Free_Avatar_Upload_API.md` | временный тестовый просмотр остатка бесплатных загрузок аватара |
|
||||
| `TestUploadFreeAvatar` | `14_Test_Free_Avatar_Upload_API.md` | временная тестовая бесплатная загрузка маленького аватара в Arweave |
|
||||
| `ResolveLoginForAuth` | `02_Authentication_API.md` | проверка login перед входом: LOCAL / REMOTE / NOT_FOUND / NO_ACCESS_SERVER + URL правильного access server |
|
||||
| `AuthChallenge` | `02_Authentication_API.md` | challenge для создания новой сессии |
|
||||
| `CreateAuthSession` | `02_Authentication_API.md` | создание новой авторизованной сессии |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,98 @@
|
||||
# Карта реализации SHiNE Archive Publisher v1.0
|
||||
|
||||
Этот документ связывает протокол с конкретными файлами проекта. Он нужен, чтобы другой агент мог быстро понять, где искать каждую часть реализации.
|
||||
|
||||
## 1. Новый Java-модуль `shine-server-archive`
|
||||
|
||||
Путь: `SHiNE-server/shine-server-archive/`.
|
||||
|
||||
Основные классы:
|
||||
|
||||
- `ArchivePublisherScheduler` — включает publisher только при `archive.publish.enabled=true`, сразу продолжает незавершённый job после рестарта и планирует новый snapshot один раз в сутки в `archive.publish.time`. Следующая дата вычисляется по `ZoneId`, а не как `+24h`, поэтому DST не сдвигает локальную полночь.
|
||||
- `ArchivePublisherService` — state machine: snapshot → локальный файл → Arweave → confirmations → User PDA → Solana finalized → cursor commit.
|
||||
- `ArchivePublisherConfig` — читает настройки. Для Solana RPC отдельного archive URL нет: используется `solana.users.sync.rpcUrl`, затем fallback на `solana.rpcUrl`.
|
||||
- `ShineArchiveWriter` — сериализует бинарный big block `SHINE-ARCHIVE v1.0`, FULL reference table, по одному chunk на `blockchain_name`, footer/hash/signature.
|
||||
- `ArchiveFileNames` — временное и финальное локальные имена.
|
||||
- `ArweaveArchiveService` + `ArweaveMerkle` — Arweave v2 transaction + chunk upload + polling confirmations.
|
||||
- `SolanaArchiveHeadWriter` — обычный `update_user_pda`: root signature + текущая last-block signature + client key fee payer, затем ожидание `finalized`.
|
||||
- `ArchiveKeyLoader` — читает Ed25519 seed/keypair из raw 32/64 bytes, Solana JSON 32/64 или Base64/PKCS8.
|
||||
|
||||
## 2. Локальное состояние PostgreSQL
|
||||
|
||||
Миграция: `SHiNE-server/shine-server-db/src/main/resources/postgres/migration_v22.sql`.
|
||||
|
||||
Таблицы:
|
||||
|
||||
### `archive_chain_cursor`
|
||||
Одна строка на `blockchain_name`. Хранит только **последнее окончательно опубликованное** состояние:
|
||||
|
||||
- последний source block number/hash;
|
||||
- big block number/hash, где находится последний chunk;
|
||||
- offset/size последнего chunk.
|
||||
|
||||
Если blockchain не попала в новый big block, эта строка не меняется.
|
||||
|
||||
### `archive_publish_job`
|
||||
Crash-safe state одной большой публикации и путь к локальному файлу.
|
||||
|
||||
Основные состояния:
|
||||
`SNAPSHOT_CREATED → FILE_BUILT → ARWEAVE_UPLOADED → ARWEAVE_CONFIRMED → SOLANA_SUBMITTED → SOLANA_FINALIZED → CURSORS_COMMITTED`.
|
||||
|
||||
### `archive_publish_job_chain`
|
||||
Frozen range каждой blockchain текущего job + старые и новые координаты chunk. Пока job не finalized, `archive_chain_cursor` не двигается.
|
||||
|
||||
`DatabaseInitializer` автоматически применяет migration v22 при старте существующей БД. Новая БД создаётся уже со схемой v22.
|
||||
|
||||
## 3. Как считается первая дельта
|
||||
|
||||
`ArchivePublisherService.createFrozenJob()` проходит по `BlockchainStateDAO.listAll()`.
|
||||
|
||||
- Если cursor для `blockchain_name` отсутствует: `from = 0`, поэтому первый архив содержит всё локально известное состояние `0..head`.
|
||||
- Если cursor существует: `from = last_archived + 1`.
|
||||
- Перед продолжением проверяется hash cursor-блока.
|
||||
|
||||
Папка `data/archive` сама по себе **не является источником истины** о том, был ли первый архив. Источник истины — БД cursor/job. Поэтому удаление локального файла не приводит к ошибочной повторной полной публикации.
|
||||
|
||||
## 4. Локальный lifecycle файла
|
||||
|
||||
До появления Arweave TX ID:
|
||||
|
||||
`<login>.<00001>.<dd.MM.yy>.tmp.SHiNE-archive`
|
||||
|
||||
После полной успешной загрузки transaction header + chunks в Arweave:
|
||||
|
||||
`<login>.<00001>.<dd.MM.yy>.<ARWEAVE_TX_ID>.SHiNE-archive`
|
||||
|
||||
Дата — реальная дата freeze snapshot в timezone archive publisher-а. Номер начинается с `00001`. Пять цифр — минимальная ширина, а не лимит.
|
||||
|
||||
После rename файл остаётся локально. При crash после сохранения TX ID, но до rename, recovery переименует тот же файл и не загрузит его повторно.
|
||||
|
||||
## 5. User PDA block type `100`
|
||||
|
||||
Содержимое:
|
||||
|
||||
```text
|
||||
u8 block_type = 100
|
||||
u8 block_version = 0
|
||||
bytes[32] archive_tx_id
|
||||
bytes[32] archive_hash
|
||||
```
|
||||
|
||||
Используется существующий `update_user_pda`; отдельной instruction нет.
|
||||
|
||||
Совместимость:
|
||||
- legacy update без archive extension должен сохранить старый archive head;
|
||||
- новый update может заменить/очистить block `100`;
|
||||
- Java/JS codecs и PostgreSQL Solana sync умеют читать новый блок.
|
||||
|
||||
Ключевой Rust-файл: `shine-solana/shine/programs/shine_users/src/lib.rs`.
|
||||
|
||||
## 6. Startup сервера
|
||||
|
||||
`WsServer` после текущего Solana/users sync и inter-server blockchain sync вызывает `ArchivePublisherScheduler.startOrLog()`.
|
||||
|
||||
При `archive.publish.enabled=false` scheduler пишет лог о выключенной функции и больше ничего не делает. Ключи/Arweave wallet на обычном сервере тогда не требуются.
|
||||
|
||||
## 7. Legacy TestFreeAvatar
|
||||
|
||||
Старый временный `TestFreeAvatarArweaveService` больше не является частью активного WS-протокола. Registry/API документация убраны. При наложении changed-files ZIP поверх старого дерева старые исходники физически останутся, поэтому их список для удаления находится в `05_PATCH_CONTENTS_AND_REMOVALS.md`.
|
||||
@@ -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` действительно обновлена этой версией.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Проверка и эксплуатация Archive Publisher
|
||||
|
||||
## Перед ночным тестом
|
||||
|
||||
- [ ] Новый `shine_users` уже развёрнут на том Solana-кластере, который использует сервер.
|
||||
- [ ] Серверный JAR собран из этого пакета.
|
||||
- [ ] БД забэкаплена.
|
||||
- [ ] `archive.publish.enabled=true`.
|
||||
- [ ] `archive.publish.time=00:00`.
|
||||
- [ ] `archive.publish.zoneId` соответствует желаемой локальной полуночи.
|
||||
- [ ] `server.SHiNE.login` соответствует root/client keys.
|
||||
- [ ] Arweave JWK читается пользователем процесса.
|
||||
- [ ] На Arweave wallet достаточно AR.
|
||||
- [ ] `data/archive` доступна на запись.
|
||||
- [ ] В логе есть `Archive publisher включён` и точное время следующего запуска.
|
||||
|
||||
## Во время job
|
||||
|
||||
Нормальная последовательность логов/статусов:
|
||||
|
||||
```text
|
||||
SNAPSHOT_CREATED
|
||||
FILE_BUILT
|
||||
ARWEAVE_UPLOADED
|
||||
ARWEAVE_CONFIRMED
|
||||
SOLANA_SUBMITTED
|
||||
SOLANA_FINALIZED
|
||||
CURSORS_COMMITTED
|
||||
```
|
||||
|
||||
После `FILE_BUILT` существует `.tmp.SHiNE-archive`.
|
||||
После полного Arweave upload имя уже содержит настоящий TX ID.
|
||||
|
||||
## После успешного первого job
|
||||
|
||||
Проверить:
|
||||
|
||||
```bash
|
||||
find data/archive -maxdepth 1 -type f -name '*.SHiNE-archive' -ls
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT big_block_number, status, local_archive_path, arweave_confirmations
|
||||
FROM archive_publish_job ORDER BY id DESC LIMIT 1;
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT count(*) AS archived_blockchains FROM archive_chain_cursor;
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT login, archive_head_tx_id, archive_head_hash
|
||||
FROM solana_user_pda_current
|
||||
WHERE login='<SERVER_LOGIN>';
|
||||
```
|
||||
|
||||
## Проверка второй публикации
|
||||
|
||||
До следующей полуночи добавить несколько новых SHiNE records только в часть blockchain. После следующего job:
|
||||
|
||||
- в новый big block должны попасть только изменившиеся blockchain;
|
||||
- одна blockchain в новом big block должна иметь один chunk независимо от числа новых records;
|
||||
- cursor blockchain, которая не изменилась, должен остаться на старом big block/chunk;
|
||||
- backlink изменившегося chunk должен указывать на предыдущий chunk этой же blockchain;
|
||||
- FULL reference table нового big block должна содержать все предыдущие finalized big blocks.
|
||||
|
||||
## Crash/restart сценарии
|
||||
|
||||
### Restart после FILE_BUILT
|
||||
Должен использоваться тот же frozen job и тот же локальный файл.
|
||||
|
||||
### Restart после ARWEAVE_UPLOADED
|
||||
Не должно быть повторной оплаты/upload. Если TX сохранён, но rename не успел произойти, recovery переименует `.tmp` в имя с TX ID.
|
||||
|
||||
### Restart после SOLANA_FINALIZED
|
||||
При совпадении PDA head с job сервер должен только commit cursors.
|
||||
|
||||
## Обычный сервер без публикации
|
||||
|
||||
Проверить отдельно:
|
||||
|
||||
```properties
|
||||
archive.publish.enabled=false
|
||||
```
|
||||
|
||||
Сервер должен запускаться без Arweave/root/client archive key files и не создавать `archive_publish_job`.
|
||||
@@ -0,0 +1,60 @@
|
||||
# Состав patch-пакета и удаление legacy-файлов
|
||||
|
||||
ZIP `SHINE_archive_changed_files_with_docs.zip` предназначен для распаковки **поверх исходного дерева той версии сервера, из которой он был сделан**. Внутри находятся только новые и изменённые файлы, пути сохранены относительно корня репозитория.
|
||||
|
||||
## Важное ограничение ZIP-overlay
|
||||
|
||||
Распаковка ZIP может добавить/заменить файлы, но не удалит старые. Поэтому после распаковки нужно удалить legacy test-free-avatar исходники ниже. Они больше не зарегистрированы в `JsonHandlerRegistry`, однако физическое удаление сохраняет дерево в точном состоянии новой версии и не оставляет старый тестовый Arweave-код рядом с production archive publisher.
|
||||
|
||||
## Удалить после распаковки
|
||||
|
||||
```text
|
||||
SHiNE-server/shine-server-db/src/main/java/shine/db/dao/TestFreeAvatarUploadsDAO.java
|
||||
SHiNE-server/shine-server-db/src/main/java/shine/db/entities/TestFreeAvatarUploadEntry.java
|
||||
SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_TestGetFreeAvatarQuota_Handler.java
|
||||
SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_TestUploadFreeAvatar_Handler.java
|
||||
SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/TestFreeAvatarArweaveService.java
|
||||
SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestGetFreeAvatarQuota_Request.java
|
||||
SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestGetFreeAvatarQuota_Response.java
|
||||
SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestUploadFreeAvatar_Request.java
|
||||
SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestUploadFreeAvatar_Response.java
|
||||
docs/API/14_Test_Free_Avatar_Upload_API.md
|
||||
```
|
||||
|
||||
Linux-команда из корня репозитория:
|
||||
|
||||
```bash
|
||||
rm -f \
|
||||
'SHiNE-server/shine-server-db/src/main/java/shine/db/dao/TestFreeAvatarUploadsDAO.java' \
|
||||
'SHiNE-server/shine-server-db/src/main/java/shine/db/entities/TestFreeAvatarUploadEntry.java' \
|
||||
'SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_TestGetFreeAvatarQuota_Handler.java' \
|
||||
'SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_TestUploadFreeAvatar_Handler.java' \
|
||||
'SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/TestFreeAvatarArweaveService.java' \
|
||||
'SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestGetFreeAvatarQuota_Request.java' \
|
||||
'SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestGetFreeAvatarQuota_Response.java' \
|
||||
'SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestUploadFreeAvatar_Request.java' \
|
||||
'SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestUploadFreeAvatar_Response.java' \
|
||||
'docs/API/14_Test_Free_Avatar_Upload_API.md'
|
||||
```
|
||||
|
||||
## Что пакет принципиально добавляет
|
||||
|
||||
- новый Gradle module `shine-server-archive`;
|
||||
- migration/schema v22;
|
||||
- archive state DAO/entities;
|
||||
- server startup scheduler;
|
||||
- Arweave chunk uploader;
|
||||
- Solana User PDA writer;
|
||||
- PDA block type `100` в Rust/Java/JS;
|
||||
- обновлённую документацию формата User PDA;
|
||||
- отдельную папку `docs/Archive/` с полным protocol/deploy/runbook.
|
||||
|
||||
## После overlay + удаления
|
||||
|
||||
Минимально проверить:
|
||||
|
||||
```bash
|
||||
./gradlew shadowJar
|
||||
```
|
||||
|
||||
И отдельно собрать/задеплоить изменённую Solana `shine_users` согласно `03_DEPLOY_TEST_SERVER.md`.
|
||||
@@ -0,0 +1,73 @@
|
||||
# Manifest changed/new files
|
||||
|
||||
Основа сравнения: последний исходный ZIP пользователя, на который рассчитан этот пакет.
|
||||
|
||||
- Изменённых файлов: 20
|
||||
- Новых файлов до добавления этого manifest: 25
|
||||
- Удаляемых legacy-файлов: 10
|
||||
|
||||
## Изменённые файлы
|
||||
|
||||
- `SHiNE-server/shine-server-config/src/main/java/utils/config/AppConfig.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/DatabaseInitializer.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/resources/postgres/schema_v1.sql`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/JsonHandlerRegistry.java`
|
||||
- `SHiNE-server/shine-server-solana-users-sync/src/main/java/sync/codec/ShineUsersCodec.java`
|
||||
- `SHiNE-server/shine-server-solana-users-sync/src/main/java/sync/storage/postgres/PostgresStorageRepository.java`
|
||||
- `SHiNE-server/src/main/java/server/ws/WsServer.java`
|
||||
- `SHiNE-server/src/main/resources/application.properties`
|
||||
- `build.gradle`
|
||||
- `docs/API/09_Operations_Index.md`
|
||||
- `docs/SHINE_ARCHIVE_PROTOCOL_v1.0_RU.md`
|
||||
- `docs/SHINE_ARCHIVE_PROTOCOL_v1.0_RU_FINAL.md`
|
||||
- `docs/Solana/user_pda/README.md`
|
||||
- `docs/Solana_Architecture/details/shine_users.md`
|
||||
- `settings.gradle`
|
||||
- `shine-UI/js/services/auth-service.js`
|
||||
- `shine-UI/js/services/shine-user-pda-service.js`
|
||||
- `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md`
|
||||
- `shine-solana/shine/doc/programs/shine_users.md`
|
||||
- `shine-solana/shine/programs/shine_users/src/lib.rs`
|
||||
|
||||
## Новые файлы
|
||||
|
||||
- `SHiNE-server/shine-server-archive/build.gradle`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchiveFileNames.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchiveKeyLoader.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchivePublisherConfig.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchivePublisherScheduler.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchivePublisherService.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArweaveArchiveService.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArweaveMerkle.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ShineArchiveWriter.java`
|
||||
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/SolanaArchiveHeadWriter.java`
|
||||
- `SHiNE-server/shine-server-archive/src/test/java/server/archive/ArchiveFileNamesTest.java`
|
||||
- `SHiNE-server/shine-server-archive/src/test/java/server/archive/ShineArchiveWriterTest.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/archive/ArchiveBigBlockRef.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/archive/ArchiveChainCursor.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/archive/ArchivePublishJob.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/archive/ArchivePublishJobChain.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/dao/ArchivePublicationDAO.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/resources/postgres/migration_v22.sql`
|
||||
- `docs/Archive/01_PROTOCOL_v1.0.md`
|
||||
- `docs/Archive/02_IMPLEMENTATION_MAP.md`
|
||||
- `docs/Archive/03_DEPLOY_TEST_SERVER.md`
|
||||
- `docs/Archive/04_TEST_AND_OPERATIONS.md`
|
||||
- `docs/Archive/05_PATCH_CONTENTS_AND_REMOVALS.md`
|
||||
- `docs/Archive/README.md`
|
||||
- `docs/Archive/archive-publisher.example.properties`
|
||||
|
||||
## Legacy-файлы, которые ZIP сам не удаляет
|
||||
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/dao/TestFreeAvatarUploadsDAO.java`
|
||||
- `SHiNE-server/shine-server-db/src/main/java/shine/db/entities/TestFreeAvatarUploadEntry.java`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_TestGetFreeAvatarQuota_Handler.java`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/Net_TestUploadFreeAvatar_Handler.java`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/TestFreeAvatarArweaveService.java`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestGetFreeAvatarQuota_Request.java`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestGetFreeAvatarQuota_Response.java`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestUploadFreeAvatar_Request.java`
|
||||
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/tempToTest/entyties/Net_TestUploadFreeAvatar_Response.java`
|
||||
- `docs/API/14_Test_Free_Avatar_Upload_API.md`
|
||||
|
||||
Подробная команда удаления находится в `05_PATCH_CONTENTS_AND_REMOVALS.md`.
|
||||
@@ -0,0 +1,28 @@
|
||||
# SHiNE Archive Publisher — документация
|
||||
|
||||
Эта папка — **актуальная точка входа** для механизма серверной архивации SHiNE в Arweave с фиксацией archive head в Solana User PDA.
|
||||
|
||||
Если задачу выполняет другая нейронка/агент, читать документы нужно в таком порядке:
|
||||
|
||||
1. `01_PROTOCOL_v1.0.md` — бинарный формат `SHINE-ARCHIVE`, big-block references, `UserBlockchainChunk`, подписи, PDA block `100`, crash-safety.
|
||||
2. `02_IMPLEMENTATION_MAP.md` — как спецификация разложена по Java/Rust/JS/SQL файлам текущего проекта.
|
||||
3. `03_DEPLOY_TEST_SERVER.md` — полный порядок установки на тестовый сервер, включая обязательный апгрейд `shine_users`, конфиг, ключи, сборку и запуск.
|
||||
4. `04_TEST_AND_OPERATIONS.md` — что проверять до полуночи, после полуночи и при сбоях.
|
||||
5. `05_PATCH_CONTENTS_AND_REMOVALS.md` — какие файлы содержит пакет и какие legacy test-free-avatar файлы нужно удалить при наложении ZIP поверх старого исходника.
|
||||
6. `archive-publisher.example.properties` — минимальный конфиг архиватора.
|
||||
|
||||
## Коротко
|
||||
|
||||
- Архиватор **по умолчанию выключен**: `archive.publish.enabled=false`.
|
||||
- При включении создаёт новый snapshot **один раз в сутки в заданное локальное время**, по умолчанию `00:00`.
|
||||
- При первом успешном запуске, когда архивных курсоров ещё нет, в первый big block попадает **всё локально известное состояние всех blockchain, начиная с source block 0**.
|
||||
- Далее публикуется только дельта.
|
||||
- Один `blockchain_name` в одном big block представлен максимум одним `UserBlockchainChunk`; внутри него лежат все новые raw SHiNE records этой цепочки.
|
||||
- В конце chunk одна ссылка на предыдущий chunk этой же blockchain. Если blockchain в текущем big block отсутствует, её cursor/head не меняется.
|
||||
- Готовый файл сначала существует локально как `<login>.<00001>.<dd.MM.yy>.tmp.SHiNE-archive`. После успешной загрузки в Arweave он переименовывается в `<login>.<00001>.<dd.MM.yy>.<REAL_ARWEAVE_TX_ID>.SHiNE-archive` и остаётся локально.
|
||||
- После Arweave confirmations обычным `update_user_pda` обновляется PDA block type `100`: `archive_tx_id[32] + archive_hash[32]`.
|
||||
- Cursor commit выполняется только после Solana `finalized`.
|
||||
|
||||
## Важно перед тестом
|
||||
|
||||
Изменён формат/парсер `shine_users`. **Нельзя просто заменить серверный JAR и включить archive publisher, если целевая Solana-программа `shine_users` ещё не обновлена кодом из этого пакета.** Сначала обновить программу на нужном кластере, затем сервер.
|
||||
@@ -0,0 +1,23 @@
|
||||
# Минимальный пример для archive-capable сервера.
|
||||
# Добавлять во внешний application.properties; существующие DB/Solana/server настройки не удалять.
|
||||
|
||||
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 RPC НЕ задаётся.
|
||||
# Используется solana.users.sync.rpcUrl, иначе solana.rpcUrl.
|
||||
archive.solana.rootKeyPath=/home/player/SHiNE/secrets/server-root.key
|
||||
archive.solana.clientKeyPath=/home/player/SHiNE/secrets/server-client.key
|
||||
# Эти файлы могут содержать Base58 seed 32 bytes или Base58 Solana secret key 64 bytes.
|
||||
archive.solana.confirmPollSeconds=5
|
||||
archive.solana.confirmTimeoutMinutes=30
|
||||
archive.solana.commitment=finalized
|
||||
+1207
-940
File diff suppressed because it is too large
Load Diff
@@ -168,19 +168,78 @@ BigBlock 70
|
||||
|
||||
---
|
||||
|
||||
# 6. Периодичность
|
||||
# 6. Расписание публикации
|
||||
|
||||
Частота архивной публикации конфигурируется отдельно от существующей межсерверной синхронизации.
|
||||
Архивная публикация запускается один раз в сутки в заданное локальное время. Она НЕ использует интервал «каждые N минут».
|
||||
|
||||
Пример:
|
||||
|
||||
```properties
|
||||
archive.publish.enabled=true
|
||||
archive.publish.intervalMinutes=720
|
||||
archive.publish.initialDelayMinutes=15
|
||||
archive.publish.time=00:00
|
||||
archive.publish.zoneId=
|
||||
```
|
||||
|
||||
`720` минут = раз в 12 часов.
|
||||
Для v1.0 значение по умолчанию:
|
||||
|
||||
```text
|
||||
00:00
|
||||
```
|
||||
|
||||
То есть новый snapshot и новый большой архивный блок создаются один раз в сутки в полночь.
|
||||
|
||||
Если `archive.publish.zoneId` пуст, используется системная timezone сервера. При необходимости её можно задать явно, например `Europe/Warsaw`. Это сохраняет публикацию ровно в указанное локальное время даже при переходах летнего/зимнего времени.
|
||||
|
||||
Незавершённый archive job после рестарта не ждёт следующей полуночи: сервер продолжает именно его сразу. Новый snapshot при старте вне назначенного времени не создаётся.
|
||||
|
||||
## 6.1. Первая архивная публикация
|
||||
|
||||
Если у данного archive publisher ещё нет подтверждённых архивных курсоров, первая публикация берёт ВСЁ локально известное состояние:
|
||||
|
||||
```text
|
||||
для каждой blockchain_name:
|
||||
source block 0 .. current local head
|
||||
```
|
||||
|
||||
То есть первый большой архивный блок содержит все SHiNE-блоки, которые сервер успел узнать к моменту первого суточного snapshot. После успешной публикации следующие большие блоки содержат только дельту относительно подтверждённых курсоров.
|
||||
|
||||
## 6.2. Локальная папка и имена файлов
|
||||
|
||||
Перед любой сетевой загрузкой большой блок сначала полностью создаётся на локальном диске. По умолчанию каталог:
|
||||
|
||||
```text
|
||||
data/archive/
|
||||
```
|
||||
|
||||
До получения Arweave TX ID файл имеет временное имя:
|
||||
|
||||
```text
|
||||
<login>.<00001>.<дд.мм.гг>.tmp.SHiNE-archive
|
||||
```
|
||||
|
||||
Например:
|
||||
|
||||
```text
|
||||
archive01.00001.11.09.26.tmp.SHiNE-archive
|
||||
```
|
||||
|
||||
Дата — реальная дата создания/freeze snapshot большого блока в timezone archive publisher-а. Номер имеет минимальную ширину 5 цифр. Пять цифр — форматирование, а не лимит: блок `100000` получает шестизначный номер.
|
||||
|
||||
После успешной загрузки Arweave возвращает реальный TX ID. Сервер сначала надёжно сохраняет TX ID в БД, затем атомарно переименовывает тот же локальный файл в:
|
||||
|
||||
```text
|
||||
<login>.<00001>.<дд.мм.гг>.<ARWEAVE_TX_ID>.SHiNE-archive
|
||||
```
|
||||
|
||||
Например:
|
||||
|
||||
```text
|
||||
archive01.00001.11.09.26.Xm32...kP9.SHiNE-archive
|
||||
```
|
||||
|
||||
Поле `<ARWEAVE_TX_ID>` — не слово `trx`, а настоящий Base64URL TX ID загруженного объекта в Arweave. Финальный файл остаётся локально как постоянная копия.
|
||||
|
||||
Crash recovery обязан продолжать работу с этим же файлом. Если TX ID уже сохранён, но процесс упал до rename, при следующем запуске сервер вычисляет финальное имя из сохранённого TX ID и завершает переименование без повторной сборки дельты.
|
||||
|
||||
---
|
||||
|
||||
@@ -380,7 +439,7 @@ Offsets и sizes внутри `SHINE-ARCHIVE v1.0` используют `u32`.
|
||||
archive.maxFileBytes=4000000000
|
||||
```
|
||||
|
||||
Если данных больше, один scheduler-run формирует несколько последовательных больших блоков.
|
||||
Если собранный frozen job превышает этот лимит, v1.0 останавливает публикацию с явной ошибкой `ArchiveTooLargeException` и не двигает курсоры. Практически лимит очень велик; для такого сервера следует уменьшить объём данных между суточными закрытиями или реализовать деление snapshot на несколько big blocks. Автоматическое деление одного snapshot на несколько big blocks оставлено как совместимое будущее расширение.
|
||||
|
||||
---
|
||||
|
||||
@@ -480,15 +539,17 @@ bytes[N] creator_login UTF-8
|
||||
u32
|
||||
```
|
||||
|
||||
Рекомендуемая нумерация:
|
||||
Рекомендуемая нумерация опубликованных больших блоков:
|
||||
|
||||
```text
|
||||
genesis = 0
|
||||
next = 1
|
||||
next = 2
|
||||
first = 1
|
||||
next = 2
|
||||
next = 3
|
||||
...
|
||||
```
|
||||
|
||||
Первый реально публикуемый большой блок имеет номер `1`, поэтому его локальное имя содержит `00001`. Неудачная незавершённая попытка не становится частью опубликованной archive-цепочки.
|
||||
|
||||
---
|
||||
|
||||
# 20. `created_at_ms`
|
||||
@@ -530,9 +591,9 @@ Writer v1.0 MUST использовать `FULL`.
|
||||
BigBlock #365
|
||||
|
||||
References:
|
||||
#0
|
||||
#1
|
||||
#2
|
||||
#3
|
||||
...
|
||||
#364
|
||||
```
|
||||
@@ -595,7 +656,7 @@ u32
|
||||
|
||||
Индекс непосредственного родителя внутри reference table.
|
||||
|
||||
Для genesis:
|
||||
Для первого большого блока (`#1`), у которого нет родителя:
|
||||
|
||||
```text
|
||||
0xFFFFFFFF
|
||||
@@ -1061,7 +1122,7 @@ last_chunk_size = new_chunk_size
|
||||
|
||||
# 54. Arweave service
|
||||
|
||||
Старый `TestFreeAvatarArweaveService` больше не нужен как avatar-specific сервис.
|
||||
Старый `TestFreeAvatarArweaveService` удаляется из активного протокола; archive publisher использует отдельный `ArweaveArchiveService`.
|
||||
|
||||
Его следует переделать/переименовать, например в:
|
||||
|
||||
@@ -1307,8 +1368,9 @@ derivePublic(client_private) == UserPDA.client_key
|
||||
|
||||
```properties
|
||||
archive.publish.enabled=false
|
||||
archive.publish.intervalMinutes=720
|
||||
archive.publish.initialDelayMinutes=15
|
||||
archive.publish.time=00:00
|
||||
archive.publish.zoneId=
|
||||
archive.workDir=data/archive
|
||||
|
||||
archive.maxFileBytes=4000000000
|
||||
|
||||
@@ -1321,6 +1383,9 @@ archive.arweave.confirmTimeoutMinutes=180
|
||||
archive.solana.rootKeyPath=/opt/shine/secrets/root.key
|
||||
archive.solana.clientKeyPath=/opt/shine/secrets/client.key
|
||||
archive.solana.commitment=finalized
|
||||
|
||||
# Отдельного archive.solana.rpcUrl нет.
|
||||
# Используется solana.users.sync.rpcUrl, а если он пуст — обычный solana.rpcUrl.
|
||||
```
|
||||
|
||||
---
|
||||
@@ -1381,54 +1446,55 @@ Lock не удерживается во время Arweave/Solana ожидани
|
||||
|
||||
## Database
|
||||
|
||||
- [ ] `archive_chain_cursor`
|
||||
- [ ] `archive_publish_job`
|
||||
- [ ] `archive_publish_job_chain`
|
||||
- [ ] schema migration
|
||||
- [ ] crash recovery
|
||||
- [x] `archive_chain_cursor`
|
||||
- [x] `archive_publish_job`
|
||||
- [x] `archive_publish_job_chain`
|
||||
- [x] schema migration
|
||||
- [x] crash recovery
|
||||
|
||||
## Archive writer
|
||||
|
||||
- [ ] magic `SHINE-ARCHIVE`
|
||||
- [ ] major/minor version
|
||||
- [ ] fixed header
|
||||
- [ ] creator login
|
||||
- [ ] FULL previous big block table
|
||||
- [ ] one chunk per `blockchain_name`
|
||||
- [ ] many raw records inside one chunk
|
||||
- [ ] one backlink per chunk
|
||||
- [ ] closer login
|
||||
- [ ] SHA-256
|
||||
- [ ] Ed25519 signature
|
||||
- [ ] max file size < 4 GiB
|
||||
- [x] magic `SHINE-ARCHIVE`
|
||||
- [x] major/minor version
|
||||
- [x] fixed header
|
||||
- [x] creator login
|
||||
- [x] FULL previous big block table
|
||||
- [x] one chunk per `blockchain_name`
|
||||
- [x] many raw records inside one chunk
|
||||
- [x] one backlink per chunk
|
||||
- [x] closer login
|
||||
- [x] SHA-256
|
||||
- [x] Ed25519 signature
|
||||
- [x] max file size < 4 GiB
|
||||
- [x] local `.tmp.SHiNE-archive -> .<ArweaveTX>.SHiNE-archive` lifecycle in `data/archive`
|
||||
|
||||
## Arweave
|
||||
|
||||
- [ ] rename/refactor `TestFreeAvatarArweaveService`
|
||||
- [ ] remove avatar-specific logic
|
||||
- [ ] large/chunked upload
|
||||
- [ ] confirmation polling
|
||||
- [x] rename/refactor `TestFreeAvatarArweaveService`
|
||||
- [x] remove avatar-specific logic
|
||||
- [x] large/chunked upload
|
||||
- [x] confirmation polling
|
||||
|
||||
## Solana
|
||||
|
||||
- [ ] add PDA block type `100`
|
||||
- [ ] update Rust codec
|
||||
- [ ] update Java codec
|
||||
- [ ] update JS codec/UI writer
|
||||
- [ ] use ordinary `update_user_pda`
|
||||
- [ ] server-side transaction writer
|
||||
- [ ] root signature
|
||||
- [ ] client fee payer
|
||||
- [ ] wait for `finalized`
|
||||
- [x] add PDA block type `100`
|
||||
- [x] update Rust codec
|
||||
- [x] update Java codec
|
||||
- [x] update JS codec/UI writer
|
||||
- [x] use ordinary `update_user_pda`
|
||||
- [x] server-side transaction writer
|
||||
- [x] root signature
|
||||
- [x] client fee payer
|
||||
- [x] wait for `finalized`
|
||||
|
||||
## Scheduler
|
||||
|
||||
- [ ] `archive.publish.enabled`
|
||||
- [ ] `archive.publish.intervalMinutes`
|
||||
- [ ] `archive.publish.initialDelayMinutes`
|
||||
- [ ] no concurrent jobs
|
||||
- [ ] split files > max size
|
||||
- [ ] skip when no new blocks
|
||||
- [x] `archive.publish.enabled`
|
||||
- [x] `archive.publish.time`
|
||||
- [x] `archive.publish.zoneId`
|
||||
- [x] no concurrent jobs
|
||||
- [ ] future: автоматическое split > max size (v1.0 сейчас безопасно останавливается без cursor commit)
|
||||
- [x] skip when no new blocks
|
||||
|
||||
---
|
||||
|
||||
@@ -1506,10 +1572,10 @@ BigBlock #100
|
||||
| creator = server-A
|
||||
|
|
||||
+-- References
|
||||
| #0 -> BigBlock #0 / hash / TX
|
||||
| #1 -> BigBlock #1 / hash / TX
|
||||
| ref[0] -> BigBlock #1 / hash / TX
|
||||
| ref[1] -> BigBlock #2 / hash / TX
|
||||
| ...
|
||||
| #99 -> BigBlock #99 / hash / TX
|
||||
| ref[98] -> BigBlock #99 / hash / TX
|
||||
|
|
||||
+-- alice-001 chunk
|
||||
| records x4
|
||||
|
||||
@@ -94,16 +94,17 @@ UserPdaRecordV1
|
||||
| `40` | `AccessServersBlock` | Серверы доступа/relay. |
|
||||
| `50` | `SessionsBlock` | Опубликованные пользовательские сессии и homeserver-ы. |
|
||||
| `70` | `TrustedStateBlock` | Счетчик trusted-связей. |
|
||||
| `100` | `ArchiveHeadBlock` | Текущая голова серверного SHINE-ARCHIVE: Arweave TX ID + SHA-256 архива. |
|
||||
| `255` | `ReservedBlock` | Зарезервировано, пока не используется. |
|
||||
|
||||
Правила:
|
||||
|
||||
- неизвестный `block_type` в `format_major = 1` считается ошибкой;
|
||||
- обязательные блоки: `RecoveryKeyBlock`, `RootKeyBlock`, `ClientKeyBlock`, `BlockchainRegistryBlock`;
|
||||
- необязательные блоки: `ServerProfileBlock`, `AccessServersBlock`, `SessionsBlock`, `TrustedStateBlock`;
|
||||
- необязательные блоки: `ServerProfileBlock`, `AccessServersBlock`, `SessionsBlock`, `TrustedStateBlock`, `ArchiveHeadBlock`;
|
||||
- каждый обязательный блок должен встречаться ровно один раз;
|
||||
- порядок блоков в записи фиксируется для простоты проверки:
|
||||
`RecoveryKey`, `RootKey`, `ClientKey`, `BlockchainRegistry`, `ServerProfile`, `AccessServers`, `Sessions`, `TrustedState`.
|
||||
`RecoveryKey`, `RootKey`, `ClientKey`, `BlockchainRegistry`, `ServerProfile`, `AccessServers`, `Sessions`, `TrustedState`, `ArchiveHead`.
|
||||
|
||||
## 6. RecoveryKeyBlock
|
||||
|
||||
@@ -359,6 +360,28 @@ TrustedStateBlock
|
||||
|
||||
Пока блок с доверенными лицами не реализуется, потому что полный формат trusted-логики еще не составлен. В будущем trusted-связи, очереди, таймеры и подтверждения должны быть вынесены в отдельный формат.
|
||||
|
||||
## 15.1. ArchiveHeadBlock
|
||||
|
||||
Необязательный блок текущей головы серверного архива. Он используется archive-capable сервером и хранится в том же User PDA.
|
||||
|
||||
```text
|
||||
ArchiveHeadBlock
|
||||
- block_type: u8 = 100
|
||||
- block_version: u8 = 0
|
||||
- archive_tx_id: [u8; 32]
|
||||
- archive_hash: [u8; 32]
|
||||
```
|
||||
|
||||
Семантика:
|
||||
|
||||
- `archive_tx_id` — raw 32-byte Arweave transaction id последнего опубликованного большого `SHINE-ARCHIVE`; текстовая Base64URL-форма получается вне PDA;
|
||||
- `archive_hash` — SHA-256 большого archive block по правилам `docs/Archive/01_PROTOCOL_v1.0.md`;
|
||||
- отсутствие block `100` означает, что аккаунт ещё не объявлял archive head;
|
||||
- обычный legacy `update_user_pda`, в instruction которого archive extension отсутствует, **обязан сохранить существующий ArchiveHeadBlock без изменений**;
|
||||
- расширенный `update_user_pda` может заменить archive head или явно очистить его; отдельной Solana instruction для архива нет.
|
||||
|
||||
`ArchiveHeadBlock` входит в unsigned bytes User PDA и тем самым покрывается обычной root-подписью записи.
|
||||
|
||||
## 16. Подпись user_pda
|
||||
|
||||
Подписывается не вся PDA целиком, а unsigned-часть записи:
|
||||
@@ -392,6 +415,7 @@ Solana-программа проверяет подпись через встр
|
||||
- обязательные блоки присутствуют;
|
||||
- создается минимум один `BlockchainRecord`;
|
||||
- новый `SessionsBlock` может присутствовать, но при обычной регистрации сейчас записывается пустой список с `sessions_mode = 1`;
|
||||
- `ArchiveHeadBlock` при регистрации не обязателен; обычный пользователь/сервер может начать публиковать архив позже;
|
||||
- стартовый `paid_limit_bytes` равен стартовому бонусу плюс оплаченный дополнительный лимит;
|
||||
- `used_bytes <= paid_limit_bytes`;
|
||||
- пользователь платит регистрационную комиссию;
|
||||
@@ -408,6 +432,7 @@ Solana-программа проверяет подпись через встр
|
||||
- `prev_record_hash` равен хэшу unsigned-части предыдущей записи;
|
||||
- `updated_at_ms` обновляется;
|
||||
- unsigned-часть новой записи подписана `root_key`;
|
||||
- если archive extension в instruction отсутствует (legacy client), старый `ArchiveHeadBlock` сохраняется; если extension присутствует, применяется переданное `archive_head_update`;
|
||||
- лимиты блокчейнов могут только увеличиваться;
|
||||
- занятый размер и номер последнего блока не могут уменьшаться;
|
||||
- при увеличении оплаченного лимита пользователь доплачивает комиссию;
|
||||
|
||||
@@ -135,3 +135,23 @@
|
||||
- economy-настройки меняет DAO-authority;
|
||||
- upgrade-authority программы после проверки передается DAO;
|
||||
- пользовательские операции `create_user_pda` и `update_user_pda` остаются доступными обычным пользователям при корректных подписях и оплате.
|
||||
|
||||
## ArchiveHeadBlock и серверный SHINE-ARCHIVE
|
||||
|
||||
Формат User PDA поддерживает необязательный `ArchiveHeadBlock` (`block_type = 100`, `block_version = 0`):
|
||||
|
||||
```text
|
||||
archive_tx_id [32]
|
||||
archive_hash [32]
|
||||
```
|
||||
|
||||
Он хранит текущую голову архива конкретного SHiNE-аккаунта: raw Arweave TX ID и SHA-256 соответствующего большого `SHINE-ARCHIVE`. Подробный бинарный формат и серверный workflow находятся в `docs/Archive/01_PROTOCOL_v1.0.md`.
|
||||
|
||||
Отдельной инструкции программы для архива нет. Используется существующий `update_user_pda`. Парсер update instruction обратно совместим:
|
||||
|
||||
- legacy payload без archive extension сохраняет старый block `100`;
|
||||
- новый payload может заменить/очистить archive head;
|
||||
- итоговая полная User PDA запись, включая block `100`, покрывается обычной root-подписью.
|
||||
|
||||
Это позволяет обычным старым клиентским обновлениям профиля не стирать archive head серверного publisher-а.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user