# Карта реализации 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: `.<00001>..tmp.SHiNE-archive` После полной успешной загрузки transaction header + chunks в Arweave: `.<00001>...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`. ## Trusted importer / location index / Viewer Server importer: ```text shine-server-archive/src/main/java/server/archive/ArchiveImportConfig.java shine-server-archive/src/main/java/server/archive/ArchiveImportScheduler.java shine-server-archive/src/main/java/server/archive/ArchiveImportService.java shine-server-archive/src/main/java/server/archive/ShineArchiveReader.java ``` Database: ```text shine-server-db/src/main/java/shine/db/dao/ArchiveImportDAO.java shine-server-db/src/main/java/shine/db/archive/ArchiveBlockchainLocation.java shine-server-db/src/main/java/shine/db/archive/ArchivePublisherHead.java shine-server-db/src/main/resources/postgres/migration_v23.sql shine-server-solana-users-sync/src/main/java/sync/storage/postgres/PostgresStorageRepository.java ``` `PostgresStorageRepository` сохраняет `archive_imported=true` при повторном sync того же head и автоматически сбрасывает флаг в `false`, если `archive_head_tx_id` или `archive_head_hash` изменились. WS API: ```text GetArchiveBlockchainLocation ``` UI: ```text shine-UI/js/pages/blockchain-archive-view.js shine-UI/Blockchain-Viewer.html ``` `Blockchain-Viewer.html` получает `tx + offset + size + blockchain`, идёт назад по `PreviousBlockchainChunkRef` и использует существующий parser каналов старого Viewer-а.