11 KiB
Импорт доверенных 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 сервер проверяет:
- publisher находится в whitelist;
archive_head_tx_id/archive_head_hashуже пришли через обычный User PDA sync;- SHA-256 скачанного файла совпадает с
archive_head_hash; creator_login == closer_login == publisher login;- Ed25519 archive signature проверяется root key publisher-а из User PDA;
- каждый вложенный raw SHiNE block проходит обычную SHiNE-проверку через существующий
AddBlockpath.
Подпись большого архива защищает контейнер и навигацию, а подписи обычных 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:
- берётся lock этой
blockchain_name; - если локального
blockchain_stateнет, identity создаётся по синхронизированному User PDA; - raw records разбираются как обычные
BchBlockEntry; - уже существующий block допускается только при совпадении hash;
- новый block должен идти строго
localLast + 1; - новый block добавляется существующим validator/write path;
- конфликт 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;