Добавить импорт архивов с доверенных серверов

This commit is contained in:
AidarKC
2026-09-12 16:59:35 +03:00
parent 2df0a78eb2
commit 1be1d56599
34 changed files with 4942 additions and 114 deletions
+28
View File
@@ -1617,3 +1617,31 @@ BigBlock #100
- PDA обновляется обычным `update_user_pda`;
- Arweave используется как долговременное хранилище;
- публикация считается завершённой только после Arweave confirmation + Solana finalized + cursor commit.
---
## Trusted archive reader / importer
Бинарный формат v1.0 не меняется. Обычный сервер MAY использовать archive head из User PDA для восстановления недостающих SHiNE-блоков, но SHOULD принимать архивы только от явно разрешённых server logins.
Рекомендуемая политика v1:
```text
publisher login ∈ local whitelist
AND UserPDA.is_server = true
AND SHA256(file) == UserPDA.archive_head_hash / reference hash
AND creator_login == closer_login == publisher login
AND Ed25519 archive signature valid for publisher root key
AND every raw SHiNE block passes normal AddBlock validation
```
FULL reference table head-блока позволяет поздно подключившемуся серверу импортировать всю неизвестную историю publisher-а от старых big blocks к новым.
Для быстрого пользовательского доступа сервер SHOULD поддерживать локальный индекс:
```text
blockchain_name -> arweave_tx_id + chunk_offset + chunk_size
```
Он указывает на последний известный `UserBlockchainChunk`. Backlink внутри chunk делает эту одну ссылку достаточной для обхода всей архивной истории конкретной blockchain.
+39
View File
@@ -96,3 +96,42 @@ bytes[32] archive_hash
## 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-а.
+55 -4
View File
@@ -7,7 +7,7 @@
Нужны изменения одновременно в:
1. серверном Java-коде;
2. PostgreSQL schema v22;
2. PostgreSQL schema v23;
3. Solana-программе `shine_users` (PDA block type `100` + backward-compatible update parser);
4. Java/JS User PDA codecs.
@@ -107,7 +107,7 @@ SHiNE-server/build/libs/shine-server.jar
# 4. Подготовить PostgreSQL backup
Перед первым стартом версии со schema v22 сделать backup тестовой БД. Например:
Перед первым стартом версии со schema v23 сделать backup тестовой БД. Например:
```bash
pg_dump -Fc -d '<DATABASE_URL_OR_NAME>' -f shine-before-archive-v22.dump
@@ -115,7 +115,7 @@ pg_dump -Fc -d '<DATABASE_URL_OR_NAME>' -f shine-before-archive-v22.dump
Точная команда зависит от текущей схемы доступа PostgreSQL.
При старте сервер сам применит `migration_v22.sql`, если `db_schema_version < 22`. Вручную migration выполнять обычно не нужно.
При старте сервер последовательно применит `migration_v22.sql` и `migration_v23.sql`, если это требуется текущей версии БД. Вручную migrations выполнять обычно не нужно.
После старта проверить:
@@ -126,7 +126,7 @@ SELECT * FROM db_schema_version WHERE id=1;
Ожидается:
```text
schema_version = 22
schema_version = 23
```
И наличие:
@@ -413,3 +413,54 @@ archive.publish.enabled=false
### Старый `shine_users`
Если новый update payload отклоняется программой, проверить, что целевая Solana `shine_users` действительно обновлена этой версией.
## Trusted archive importer
На обычном тестовом сервере publisher можно оставить выключенным, но разрешить импорт от конкретного архиватора:
```properties
archive.publish.enabled=false
archive.import.allowedPublishers=<LOGIN_ARCHIVE_SERVER>
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://<UI_HOST>/Blockchain-Viewer.html
```
В приложении: `Настройки → Архив блокчейна`.
+27
View File
@@ -84,3 +84,30 @@ archive.publish.enabled=false
```
Сервер должен запускаться без Arweave/root/client archive key files и не создавать `archive_publish_job`.
## Проверка trusted importer
1. На принимающем сервере указать только тестовый publisher:
```properties
archive.import.allowedPublishers=<publisher-login>
```
2. Перезапустить сервер.
3. Дождаться Solana PDA sync и цикла importer-а.
4. Проверить, что у publisher в `solana_user_pda_current` после успешного цикла `archive_imported=true`, а `archive_last_imported_tx_id=archive_head_tx_id`.
5. Проверить `archive_blockchain_location`.
6. Для blockchain, которой локально не хватало блоков, убедиться, что `blockchain_state.last_block_number` вырос.
7. Для уже существующих блоков importer должен пропускать совпадающий hash, а не создавать дубликат.
8. Временно удалить publisher из whitelist и убедиться, что новые archive heads больше не скачиваются.
### Проверка Viewer
Открыть `Настройки → Архив блокчейна`, получить ссылку и проверить:
- `tx`, `offset`, `size`, `blockchain` присутствуют;
- Viewer собирает несколько chunks по backlink;
- неправильный `blockchain` в URL приводит к ошибке проверки;
- `channel` открывает нужный канал;
- `message` прокручивает к нужному block number.
+23 -45
View File
@@ -1,60 +1,38 @@
# Состав patch-пакета и удаление legacy-файлов
# Состав текущего patch-пакета
ZIP `SHINE_archive_changed_files_with_docs.zip` предназначен для распаковки **поверх исходного дерева той версии сервера, из которой он был сделан**. Внутри находятся только новые и изменённые файлы, пути сохранены относительно корня репозитория.
Этот этап рассчитан **поверх последнего рабочего ZIP**, присланного после успешного запуска archive publisher.
## Важное ограничение ZIP-overlay
Пакет этого этапа добавляет:
Распаковка ZIP может добавить/заменить файлы, но не удалит старые. Поэтому после распаковки нужно удалить legacy test-free-avatar исходники ниже. Они больше не зарегистрированы в `JsonHandlerRegistry`, однако физическое удаление сохраняет дерево в точном состоянии новой версии и не оставляет старый тестовый Arweave-код рядом с production archive publisher.
- trusted archive importer;
- whitelist publisher-ов через настройки сервера;
- строгую проверку больших `SHINE-ARCHIVE`;
- импорт недостающих raw SHiNE blocks через обычный validator `AddBlock`;
- локальные поля состояния импорта в `solana_user_pda_current` и таблицу `archive_blockchain_location`;
- schema migration v23;
- WS API `GetArchiveBlockchainLocation`;
- экран `Настройки → Архив блокчейна`;
- `shine-UI/Blockchain-Viewer.html`;
- документацию importer/viewer.
## Удалить после распаковки
## Удаления
```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-команда из корня репозитория:
Старые test/free-avatar исходники, если они всё ещё физически присутствуют в рабочем дереве, этим patch-пакетом не затрагиваются. Они не относятся к trusted archive importer и не должны удаляться автоматически при наложении этого обновления.
```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'
```
## Как накладывать ZIP changed-files
## Что пакет принципиально добавляет
ZIP содержит только новые/изменённые файлы с путями от корня репозитория.
- новый 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`.
Если Gradle wrapper ещё не установлен локально, сначала обеспечить доступ к уже используемой версии Gradle/кэшу.
При старте сервер сам должен поднять schema с v22 до v23.
+28 -51
View File
@@ -1,73 +1,50 @@
# Manifest changed/new files
Основа сравнения: последний исходный ZIP пользователя, на который рассчитан этот пакет.
Основа сравнения: последний присланный рабочий ZIP `3d14e34c-4249-4e11-8042-3f3349c8e9fd.zip`.
- Изменённых файлов: 20
- Новых файлов до добавления этого manifest: 25
- Удаляемых legacy-файлов: 10
- Изменённых файлов: 19
- Новых файлов: 14
- Удаляемых файлов: 0
## Изменённые файлы
- `SHiNE-server/shine-server-config/src/main/java/utils/config/AppConfig.java`
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchivePublisherService.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-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.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/06_FILE_MANIFEST.md`
- `docs/Archive/README.md`
- `docs/Archive/archive-publisher.example.properties`
- `shine-UI/js/app.js`
- `shine-UI/js/pages/settings-view.js`
- `shine-UI/js/services/auth-service.js`
## 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`
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchiveImportConfig.java`
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchiveImportScheduler.java`
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ArchiveImportService.java`
- `SHiNE-server/shine-server-archive/src/main/java/server/archive/ShineArchiveReader.java`
- `SHiNE-server/shine-server-db/src/main/java/shine/db/archive/ArchiveBlockchainLocation.java`
- `SHiNE-server/shine-server-db/src/main/java/shine/db/archive/ArchivePublisherHead.java`
- `SHiNE-server/shine-server-db/src/main/java/shine/db/dao/ArchiveImportDAO.java`
- `SHiNE-server/shine-server-db/src/main/resources/postgres/migration_v23.sql`
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_GetArchiveBlockchainLocation_Handler.java`
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/entyties/Net_GetArchiveBlockchainLocation_Request.java`
- `SHiNE-server/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/entyties/Net_GetArchiveBlockchainLocation_Response.java`
- `docs/Archive/07_ARCHIVE_IMPORT_AND_VIEWER.md`
- `shine-UI/Blockchain-Viewer.html`
- `shine-UI/js/pages/blockchain-archive-view.js`
Подробная команда удаления находится в `05_PATCH_CONTENTS_AND_REMOVALS.md`.
## Удаления
На этом этапе файлов для удаления нет.
@@ -0,0 +1,303 @@
# Импорт доверенных SHINE-ARCHIVE и Blockchain Viewer
Этот документ описывает вторую половину архивной системы: как обычный SHiNE-сервер узнаёт о новых archive head других серверов, кому доверяет, как импортирует недостающие SHiNE-блоки и как UI получает ссылку на историю конкретной `blockchain_name`.
## 1. Источник archive head
Archive importer **не делает отдельные Solana RPC-запросы**.
Уже существующий Solana Users Sync разбирает User PDA block type `100` и копирует его в локальную таблицу:
```text
solana_user_pda_current.archive_head_tx_id
solana_user_pda_current.archive_head_hash
```
Для локального состояния importer v23 добавляет туда же:
```text
archive_imported BOOLEAN
archive_last_imported_tx_id TEXT
```
Это локальные поля сервера, в Solana они не записываются.
Когда обычный Solana Users Sync видит тот же archive head повторно, `archive_imported` сохраняется как есть.
Когда `archive_head_tx_id` или `archive_head_hash` изменился:
```text
archive_imported = false
```
а `archive_last_imported_tx_id` сохраняет последнюю успешно обработанную точку и позволяет продолжить после сбоя.
## 2. Whitelist доверенных publisher-ов
В `application.properties` задаётся список логинов серверов, архивы которых разрешено принимать:
```properties
archive.import.allowedPublishers=archive-server-1,archive-server-2
archive.import.intervalMinutes=60
archive.import.workDir=data/archive-import
```
Правила:
- логины разделяются запятыми;
- сравнение без учёта регистра;
- пустое `archive.import.allowedPublishers=` полностью выключает importer;
- принимаются только строки `solana_user_pda_current` с `is_server=true`;
- whitelist является только первым фильтром, криптографические проверки всё равно обязательны.
## 3. Периодическая проверка
После запуска сервера importer делает первую проверку примерно через 10 секунд, затем по умолчанию раз в 60 минут.
Каждый цикл — дешёвый запрос только к локальной PostgreSQL:
```text
approved publisher
AND is_server=true
AND archive_head_tx_id != ''
AND archive_imported=false
```
Если таких строк нет, Arweave не вызывается.
## 4. Проверки archive block
Для каждого pending publisher сервер проверяет:
1. publisher находится в whitelist;
2. `archive_head_tx_id/archive_head_hash` уже пришли через обычный User PDA sync;
3. SHA-256 скачанного файла совпадает с `archive_head_hash`;
4. `creator_login == closer_login == publisher login`;
5. Ed25519 archive signature проверяется root key publisher-а из User PDA;
6. каждый вложенный raw SHiNE block проходит обычную SHiNE-проверку через существующий `AddBlock` path.
Подпись большого архива защищает контейнер и навигацию, а подписи обычных SHiNE blocks защищают сами пользовательские данные.
## 5. Как определяется, что head новый
Отдельная таблица обработанных TX для основной логики не нужна.
Текущий User PDA snapshot уже содержит:
```text
archive_head_tx_id
archive_head_hash
archive_imported
archive_last_imported_tx_id
```
Пример:
```text
archive_head_tx_id = TX100
archive_imported = false
archive_last_imported_tx_id = TX97
```
Это означает: Solana уже объявила `TX100` текущей головой publisher-а, но локальный сервер успел импортировать только до `TX97`.
После полной успешной обработки `TX100`:
```text
archive_imported = true
archive_last_imported_tx_id = TX100
```
При следующем новом PDA head Users Sync сам сбросит `archive_imported=false`.
## 6. Догон пропущенных больших блоков
Если сервер был выключен и вместо `TX97` сразу увидел `TX100`, он скачивает и проверяет `TX100`, читает его FULL reference table и находит `TX97`.
После этого импортирует только:
```text
TX98
TX99
TX100
```
После каждого полностью импортированного большого блока `archive_last_imported_tx_id` сдвигается вперёд.
Если сервер впервые видит publisher и `archive_last_imported_tx_id` пустой, импортируются все previous refs от старых к новым, затем текущий head.
Если непустой `archive_last_imported_tx_id` отсутствует в FULL history текущего head, importer останавливается: это рассматривается как возможная смена/fork archive chain, а не как повод молча забыть старый cursor.
## 7. Crash recovery
Если процесс упал после `TX98`, но до `TX100`:
```text
archive_imported = false
archive_last_imported_tx_id = TX98
```
Следующий часовой цикл продолжит с `TX99`.
Если процесс успел импортировать head и записать `archive_last_imported_tx_id = TX100`, но упал до установки `archive_imported=true`, следующий цикл просто завершит отметку без повторной загрузки всей цепочки.
Все cursor updates выполняются условно по ожидаемому `archive_head_tx_id`. Если обычный Solana Users Sync успел заменить head во время импорта, старый процесс не сможет пометить новый head импортированным.
## 8. Импорт `UserBlockchainChunk`
Для каждого chunk:
1. берётся lock этой `blockchain_name`;
2. если локального `blockchain_state` нет, identity создаётся по синхронизированному User PDA;
3. raw records разбираются как обычные `BchBlockEntry`;
4. уже существующий block допускается только при совпадении hash;
5. новый block должен идти строго `localLast + 1`;
6. новый block добавляется существующим validator/write path;
7. конфликт hash или gap останавливает импорт этой archive chain.
## 9. Индекс последнего archive chunk
Таблица:
```text
archive_blockchain_location
```
содержит для каждой `blockchain_name`:
```text
blockchain_name
publisher_login
arweave_tx_id
archive_hash
big_block_number
chunk_offset
chunk_size
source_last_block_number
updated_at_ms
```
Если blockchain встретилась в новом archive block, её location обновляется. Если не встретилась — старая ссылка остаётся.
Эту таблицу заполняют как trusted importer, так и локальный archive publisher.
## 10. API для UI
WS operation:
```text
GetArchiveBlockchainLocation
```
Request:
```json
{
"op": "GetArchiveBlockchainLocation",
"blockchainName": "alice-001"
}
```
Response содержит:
```text
blockchainName
publisherLogin
arweaveTxId
archiveHash
bigBlockNumber
chunkOffset
chunkSize
sourceLastBlockNumber
```
## 11. UI и ссылка Viewer
В настройках пользователя есть экран `Архив блокчейна`.
Viewer-файл:
```text
shine-UI/Blockchain-Viewer.html
```
Основные параметры ссылки:
```text
/Blockchain-Viewer.html
?tx=<ARWEAVE_TX_ID>
&offset=<CHUNK_OFFSET>
&size=<CHUNK_SIZE>
&blockchain=<BLOCKCHAIN_NAME>
```
Дополнительно:
```text
&channel=<CHANNEL_NAME>
&message=<BLOCK_NUMBER>
```
`blockchain` используется также для проверки: если загруженный chunk имеет другое имя blockchain, Viewer прекращает обработку.
`channel` открывает нужный канал, а `message` прокручивает к указанному сообщению/block number и выделяет его.
## 12. Как Viewer собирает всю цепочку
Viewer начинает с последнего `TX + offset + size`:
```text
последний UserBlockchainChunk
PreviousBlockchainChunkRef
FULL reference table текущего big block
TX предыдущего big block
Range предыдущего chunk
следующий backlink
до NO_REFERENCE
```
Чужие chunks скачивать не требуется.
## 13. Минимальная настройка принимающего сервера
```properties
archive.publish.enabled=false
archive.import.allowedPublishers=server-a,server-b
archive.import.intervalMinutes=60
archive.import.workDir=data/archive-import
```
Если импорт архивов не нужен:
```properties
archive.import.allowedPublishers=
```
Тогда importer вообще не запускается.
## 14. Диагностика PostgreSQL
Pending archive heads:
```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;
```
Последние известные пользовательские chunks:
```sql
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;
```
+15 -1
View File
@@ -9,7 +9,8 @@
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` — минимальный конфиг архиватора.
6. `07_ARCHIVE_IMPORT_AND_VIEWER.md` — whitelist доверенных publisher-ов, импорт archive chain, индекс последнего chunk, UI и `Blockchain-Viewer.html`.
7. `archive-publisher.example.properties` — пример конфигурации publisher + importer.
## Коротко
@@ -26,3 +27,16 @@
## Важно перед тестом
Изменён формат/парсер `shine_users`. **Нельзя просто заменить серверный JAR и включить archive publisher, если целевая Solana-программа `shine_users` ещё не обновлена кодом из этого пакета.** Сначала обновить программу на нужном кластере, затем сервер.
## Импорт архивов других серверов
Импорт по умолчанию также выключен. Настройка:
```properties
archive.import.allowedPublishers=
```
Пустой список означает: не доверять архивам ни одного внешнего сервера. Для разрешения перечислить логины через запятую. Подробности — `07_ARCHIVE_IMPORT_AND_VIEWER.md`.
Текущая схема БД: **v23**. `v22` добавила publisher, `v23` добавляет локальные `archive_imported/archive_last_imported_tx_id`, trusted importer и универсальный `archive_blockchain_location`.
@@ -21,3 +21,13 @@ archive.solana.clientKeyPath=/home/player/SHiNE/secrets/server-client.key
archive.solana.confirmPollSeconds=5
archive.solana.confirmTimeoutMinutes=30
archive.solana.commitment=finalized
# =============================================================
# Trusted archive import (independent from publisher)
# Empty = do not import archives from any external server.
# Comma-separated SHiNE server logins, case-insensitive.
# =============================================================
archive.import.allowedPublishers=
archive.import.intervalMinutes=60
archive.import.workDir=data/archive-import