Files
SHiNE-server/docs/Archive/07_ARCHIVE_IMPORT_AND_VIEWER.md
T

11 KiB
Raw Blame History

Импорт доверенных 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 и копирует его в локальную таблицу:

solana_user_pda_current.archive_head_tx_id
solana_user_pda_current.archive_head_hash

Для локального состояния importer v23 добавляет туда же:

archive_imported                 BOOLEAN
archive_last_imported_tx_id      TEXT

Это локальные поля сервера, в Solana они не записываются.

Когда обычный Solana Users Sync видит тот же archive head повторно, archive_imported сохраняется как есть.

Когда archive_head_tx_id или archive_head_hash изменился:

archive_imported = false

а archive_last_imported_tx_id сохраняет последнюю успешно обработанную точку и позволяет продолжить после сбоя.

2. Whitelist доверенных publisher-ов

В application.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:

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 уже содержит:

archive_head_tx_id
archive_head_hash
archive_imported
archive_last_imported_tx_id

Пример:

archive_head_tx_id            = TX100
archive_imported              = false
archive_last_imported_tx_id   = TX97

Это означает: Solana уже объявила TX100 текущей головой publisher-а, но локальный сервер успел импортировать только до TX97.

После полной успешной обработки TX100:

archive_imported              = true
archive_last_imported_tx_id   = TX100

При следующем новом PDA head Users Sync сам сбросит archive_imported=false.

6. Догон пропущенных больших блоков

Если сервер был выключен и вместо TX97 сразу увидел TX100, он скачивает и проверяет TX100, читает его FULL reference table и находит TX97.

После этого импортирует только:

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:

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

Таблица:

archive_blockchain_location

содержит для каждой blockchain_name:

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:

GetArchiveBlockchainLocation

Request:

{
  "op": "GetArchiveBlockchainLocation",
  "blockchainName": "alice-001"
}

Response содержит:

blockchainName
publisherLogin
arweaveTxId
archiveHash
bigBlockNumber
chunkOffset
chunkSize
sourceLastBlockNumber

11. UI и ссылка Viewer

В настройках пользователя есть экран Архив блокчейна.

Viewer-файл:

shine-UI/Blockchain-Viewer.html

Основные параметры ссылки:

/Blockchain-Viewer.html
  ?tx=<ARWEAVE_TX_ID>
  &offset=<CHUNK_OFFSET>
  &size=<CHUNK_SIZE>
  &blockchain=<BLOCKCHAIN_NAME>

Дополнительно:

&channel=<CHANNEL_NAME>
&message=<BLOCK_NUMBER>

blockchain используется также для проверки: если загруженный chunk имеет другое имя blockchain, Viewer прекращает обработку.

channel открывает нужный канал, а message прокручивает к указанному сообщению/block number и выделяет его.

12. Как Viewer собирает всю цепочку

Viewer начинает с последнего TX + offset + size:

последний UserBlockchainChunk
        ↓
PreviousBlockchainChunkRef
        ↓
FULL reference table текущего big block
        ↓
TX предыдущего big block
        ↓
Range предыдущего chunk
        ↓
следующий backlink
        ↓
до NO_REFERENCE

Чужие chunks скачивать не требуется.

13. Минимальная настройка принимающего сервера

archive.publish.enabled=false
archive.import.allowedPublishers=server-a,server-b
archive.import.intervalMinutes=60
archive.import.workDir=data/archive-import

Если импорт архивов не нужен:

archive.import.allowedPublishers=

Тогда importer вообще не запускается.

14. Диагностика PostgreSQL

Pending archive heads:

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:

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;