SHA256
99 lines
6.5 KiB
Markdown
99 lines
6.5 KiB
Markdown
# Карта реализации 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`.
|