38 KiB
SHiNE Archive Protocol v1.0
Полная русская спецификация серверной фиксации больших блоков, Arweave, Solana User PDA и навигации по предыдущим записям пользователя
Статус: рабочая спецификация v1.0
Формат: бинарный
Magic: SHINE-ARCHIVE
Версия: два отдельных байта major / minor
1. Назначение
Протокол предназначен для периодической долговременной фиксации новых SHiNE-блоков.
Стартовая схема:
Основной SHiNE-сервер
|
| существующая межсерверная синхронизация
v
Raspberry / архивный SHiNE-сервер
|
+--> видит все новые локальные SHiNE-блоки
+--> фиксирует стабильную дельту
+--> собирает большой SHINE-ARCHIVE блок
+--> загружает его в Arweave
+--> ждёт закрепления Arweave-транзакции
+--> обычным update своего User PDA записывает новый archive head
Существующую межсерверную синхронизацию в v1 менять не требуется.
Архивный сервер не создаёт и не переподписывает пользовательские сообщения. Он берёт уже существующие пользовательские записи и добавляет только серверную навигационную метаинформацию большого блока.
2. Основные принципы
- Публикуется только новая дельта.
- Дельта замораживается до начала загрузки.
- Новые данные, пришедшие во время публикации, попадают в следующий большой блок.
- После загрузки сервер ждёт закрепления Arweave-транзакции.
- Только после закрепления Arweave сервер обновляет свой User PDA в Solana.
- Только после
finalizedв Solana локальные курсоры считаются окончательно сдвинутыми. - Большой блок имеет собственный SHA-256 и подпись составившего его SHiNE-аккаунта.
- Каждый большой блок содержит список предыдущих больших блоков с номером, hash и Arweave TX ID.
- В v1 в начало каждого нового большого блока записывается полный список всех известных предыдущих больших блоков данной ветки.
- Для каждого пользователя сервер строит обратную цепочку не на одну предыдущую запись, а на предыдущий большой блок, где были записи этого пользователя, плюс список всех его записей внутри того блока.
3. Ключи архивного сервера
Для первой реализации серверу требуются:
root private keySHiNE-аккаунта;client private keySHiNE-аккаунта;- Arweave JWK/private wallet key.
blockchain private key для самой архивной публикации не требуется: пользовательские записи уже существуют и уже подписаны.
В v1 большой блок может подписываться root key того SHiNE-аккаунта, который его сформировал.
4. Локальное состояние дельты
Нужно различать:
- уже окончательно опубликованное состояние;
- текущую замороженную дельту, которая ещё проходит Arweave/Solana.
Рекомендуются три таблицы.
4.1. archive_chain_cursor
CREATE TABLE archive_chain_cursor (
blockchain_name TEXT PRIMARY KEY,
last_archived_block_number BIGINT NOT NULL,
last_archived_block_hash BYTEA NOT NULL,
updated_at_ms BIGINT NOT NULL
);
Она хранит точку, до которой конкретная локальная blockchain уже окончательно вошла в опубликованный и зафиксированный archive head.
4.2. archive_publish_job
CREATE TABLE archive_publish_job (
id BIGSERIAL PRIMARY KEY,
archive_number BIGINT NOT NULL,
status TEXT NOT NULL,
parent_arweave_tx_id BYTEA,
parent_archive_hash BYTEA,
created_at_ms BIGINT NOT NULL,
local_archive_path TEXT,
archive_hash BYTEA,
arweave_tx_id BYTEA,
arweave_confirmations INTEGER,
solana_signature TEXT,
error_text TEXT,
updated_at_ms BIGINT NOT NULL
);
Рекомендуемые состояния:
SNAPSHOT_CREATED
FILE_BUILT
ARWEAVE_UPLOADED
ARWEAVE_CONFIRMED
SOLANA_SUBMITTED
SOLANA_FINALIZED
CURSORS_COMMITTED
FAILED
4.3. archive_publish_job_chain
CREATE TABLE archive_publish_job_chain (
job_id BIGINT NOT NULL,
blockchain_name TEXT NOT NULL,
from_block_number BIGINT NOT NULL,
to_block_number BIGINT NOT NULL,
previous_block_hash BYTEA NOT NULL,
last_block_hash BYTEA NOT NULL,
PRIMARY KEY (job_id, blockchain_name)
);
Сырые блоки в staging-таблицы копировать не нужно. В job фиксируются только точные диапазоны и крайние hash.
5. Заморозка дельты
Пример:
локально сейчас:
Alice = 180
Bob = 94
уже опубликовано:
Alice = 160
Bob = 90
Новый job фиксирует:
Alice: 161..180
Bob: 91..94
Если во время Arweave upload локальное состояние стало:
Alice = 185
Bob = 97
текущий job НЕ меняется.
Следующая публикация начнётся с:
Alice: 181..185
Bob: 95..97
6. Общая структура большого блока
Большой опубликованный файл одновременно является:
- архивной дельтой;
- индексом предыдущих больших блоков;
- контейнером пользовательских записей;
- навигационным узлом для истории каждого пользователя;
- подписанным объектом того SHiNE-аккаунта, который его сформировал.
Структура:
+------------------------------------------------+
| PREFIX / HEADER |
| magic = SHINE-ARCHIVE |
| version major/minor |
| размеры/счётчики |
| номер большого блока |
| время |
| LOGIN составителя |
| режим таблицы предыдущих блоков |
| число предыдущих блоков |
+------------------------------------------------+
| PREVIOUS BLOCK REFERENCES TABLE |
| |
| block_number + block_hash + arweave_tx_id |
| block_number + block_hash + arweave_tx_id |
| ... |
+------------------------------------------------+
| USER RECORDS / SERVER NAVIGATION METADATA |
| ... |
+------------------------------------------------+
| FOOTER |
| LOGIN закрывшего блок |
| block_hash |
| signature |
+------------------------------------------------+
Логин в header и footer должен быть одинаковым.
Дублирование логина намеренное:
- header позволяет сразу при скачивании начала файла увидеть, кто составил блок;
- footer делает закрытие блока человекочитаемым рядом с hash и подписью.
7. Magic и версия
Magic:
SHINE-ARCHIVE
Это ровно 13 ASCII-байт:
53 48 49 4E 45 2D 41 52 43 48 49 56 45
Версия:
u8 version_major
u8 version_minor
Для этой спецификации:
1
0
то есть v1.0.
8. Порядок байтов
Все fixed-size integer-поля v1 хранятся в:
big-endian
9. Header большого блока v1.0
Header должен быть устроен так, чтобы клиент мог сначала скачать только небольшой префикс файла, узнать размер всей начальной индексной области и затем докачать только её.
Формат:
bytes[13] magic = "SHINE-ARCHIVE"
u8 version_major
u8 version_minor
u32 header_size
u32 block_number
u64 created_at_ms
u8 creator_login_length
bytes[N] creator_login UTF-8
u8 references_mode
u32 references_count
u16 reference_entry_size
u32 records_count
9.1. header_size
header_size — абсолютный byte offset от начала файла до первого байта области пользовательских записей.
То есть включает:
header prefix
+ creator_login
+ всю таблицу PreviousBlockReference
Клиент может:
- скачать первые условные 64–128 байт;
- прочитать
header_size; - докачать
0 .. header_size-1; - уже иметь все адреса предыдущих больших блоков;
- после этого скачивать только нужные user records.
9.2. block_number
u32
Номер большого итогового блока данной ветки.
Диапазон:
0 .. 4 294 967 295
Для предполагаемой частоты закрытия это более чем достаточный запас.
9.3. created_at_ms
u64
Unix Epoch UTC в миллисекундах.
Это время фиксации snapshot текущего большого блока.
9.4. creator_login
Логин SHiNE-пользователя/сервера, который сформировал этот большой блок.
Пример:
raspberry-archive
Это поле находится именно в header, поэтому клиент видит автора уже после скачивания начала блока.
creator_login входит в hash большого блока и тем самым криптографически привязан к его подписи.
9.5. references_mode
u8
Зарезервированные значения:
0 = FULL_HISTORY
1 = PARTIAL_HISTORY
Для v1 publisher MUST использовать:
FULL_HISTORY
То есть каждый новый большой блок содержит ссылки на все известные предыдущие большие блоки своей ветки.
PARTIAL_HISTORY резервируется на будущее, если когда-нибудь полный список станет слишком большим.
9.6. references_count
u32
Количество элементов в таблице предыдущих больших блоков.
9.7. reference_entry_size
u16
Для v1:
68
Поле оставлено явно, чтобы будущая minor/major версия могла расширить PreviousBlockReference, а клиент всё ещё мог корректно пропускать неизвестные дополнительные байты.
9.8. records_count
u32
Количество пользовательских record-контейнеров внутри большого блока.
10. Таблица всех предыдущих больших блоков
Сразу после header идёт:
PreviousBlockReference[references_count]
В v1 это полная история предыдущих больших блоков данной ветки.
При одном закрытии в сутки даже через год это около 365 элементов, поэтому простота полного списка важнее преждевременной оптимизации.
11. PreviousBlockReference v1
Формат одного элемента:
u32 block_number
bytes[32] block_hash
bytes[32] arweave_tx_id
Размер:
4 + 32 + 32 = 68 байт
Смысл:
block_number
-> логический номер большого блока
block_hash
-> SHA-256 / канонический hash точного содержимого большого блока
arweave_tx_id
-> raw 32-byte TX ID, по которому этот большой блок можно скачать из Arweave
То есть:
HASH = идентичность содержимого
TXID = место хранения
Оба поля обязательны.
Внешнее строковое Base64URL-представление TX ID внутрь бинарного файла не записывается.
Таблица v1 SHOULD быть отсортирована детерминированно по block_number, затем по block_hash.
12. Почему в v1 записываются все предыдущие блоки
Если закрывать примерно один большой блок в сутки:
365 × 68 ≈ 24.8 KB в год
Даже через много лет это остаётся небольшим overhead по сравнению с полезными данными.
Главное преимущество: любой новый большой блок сразу является индексом всей предыдущей ветки.
Клиент не обязан идти:
365 -> 364 -> 363 -> ... -> 95
Чтобы получить адрес блока 95.
Он получает его из начала текущего блока.
13. Пользовательская обратная навигация
Навигация пользователя строится не на одну предыдущую запись.
Правило v1:
Для каждого пользователя сервер находит предыдущий большой блок, в котором существовала хотя бы одна запись этого пользователя, и сохраняет ссылку на этот большой блок вместе со списком
offset + sizeвсех записей этого пользователя в том предыдущем большом блоке.
Например:
Большой block 100:
Alice сегодня имеет 2 записи.
Предыдущий большой блок, где Alice присутствовала:
block 95.
В block 95 у Alice было 3 записи:
offset 8123 / size 417
offset 9001 / size 351
offset 14020 / size 692
Тогда server-generated ссылка Alice из block 100 содержит все три диапазона.
14. PreviousUserRecordsRef v1
Формат:
u32 previous_block_ref
u32 previous_records_count
repeat previous_records_count times:
u32 previous_record_offset
u32 previous_record_size
Минимальный размер:
8 байт
Плюс:
8 байт на каждую предыдущую запись пользователя
Пример для 3 сообщений:
previous_block_ref = 94
previous_records_count = 3
8123 417
9001 351
14020 692
Если PreviousBlockReference[94] описывает big block 95, клиент получает:
block number = 95
block hash = ...
Arweave TXID = ...
и может HTTP Range-запросами забрать только три нужных диапазона.
15. previous_block_ref
previous_block_ref — индекс в таблице PreviousBlockReference текущего большого блока.
То есть пользовательская ссылка не повторяет 32-byte TX ID и 32-byte hash.
Она хранит только компактный индекс.
Специальное значение:
0xFFFFFFFF
означает:
у пользователя ещё не было записей в предыдущих больших блоках
В этом случае:
previous_records_count = 0
16. Смещение и размер записи
previous_record_offset:
u32
Абсолютный offset от первого байта соответствующего большого SHINE-ARCHIVE файла до первого байта нужной архивированной user-record.
previous_record_size:
u32
Полный размер этой архивированной user-record в байтах.
Таким образом, зная:
TX ID
block hash
offset
size
клиент может скачать ровно нужную пользовательскую запись.
17. Как сервер добавляет пользовательскую ссылку
PreviousUserRecordsRef создаёт сервер, который формирует большой блок, а не пользовательский клиент.
Оригинальные подписанные пользователем bytes изменять нельзя.
Поэтому server navigation metadata хранится как часть контейнера большого блока вокруг/после оригинальных user-record bytes и покрывается:
block_hashбольшого блока;- подписью сервера/пользователя, который закрыл большой блок.
Для каждого пользователя в текущем большом блоке достаточно одного PreviousUserRecordsRef, связанного с последней записью этого пользователя в текущем блоке.
Если у пользователя сегодня в текущем большом блоке две записи, сервер после их сериализации добавляет один navigation tail, который указывает на предыдущий большой блок пользователя и перечисляет все его записи там.
При чтении предыдущего большого блока та же схема даёт следующий переход назад.
Получается:
Alice in block 100
|
| PreviousUserRecordsRef
| -> block 95
| -> offsets of ALL Alice records in block 95
v
Alice records in block 95
|
| server navigation metadata из block 95
| -> block 81
| -> offsets of ALL Alice records in block 81
v
...
18. Рекомендуемый контейнер архивированной пользовательской записи
Чтобы сервер мог добавлять свою навигационную метаинформацию, не изменяя исходную пользовательскую подпись, v1 SHOULD использовать серверный envelope.
Рекомендуемая модель:
u32 archived_record_size
u32 raw_user_record_size
bytes[N] raw_user_record
u8 navigation_flags
[optional server navigation data]
Где:
navigation_flags bit 0 = PreviousUserRecordsRef присутствует
У большинства записей:
navigation_flags = 0
У последней записи конкретного пользователя в текущем большом блоке:
navigation_flags bit0 = 1
и далее сериализован PreviousUserRecordsRef.
archived_record_size позволяет Range-reader получить и полностью декодировать конкретный archived record.
Исходный raw_user_record остаётся байт-в-байт неизменным.
19. Пример пользовательской цепочки
BIG BLOCK 100
Alice record A
Alice record B + PreviousUserRecordsRef:
previous_block_ref -> block 95
previous_records_count = 3
[8123,417]
[9001,351]
[14020,692]
|
v
BIG BLOCK 95
Alice old record #1
Alice old record #2
Alice old record #3 + PreviousUserRecordsRef:
previous_block_ref -> block 81
previous_records_count = 1
[4412,380]
|
v
BIG BLOCK 81
...
Так клиент получает всю историю пользователя назад блок за блоком, но скачивает только конкретные нужные byte ranges.
20. Hash большого блока
В footer записывается:
bytes[32] block_hash
block_hash вычисляется как:
SHA256(
все байты большого блока
от magic
через header
через creator_login
через PreviousBlockReference table
через все user records и server navigation metadata
через footer closer_login_length + closer_login
)
Само поле block_hash и closer_signature в hash НЕ входят.
21. Footer: кто закрыл блок, hash и подпись
В конце каждого большого блока MUST быть:
u8 closer_login_length
bytes[N] closer_login UTF-8
bytes[32] block_hash
bytes[64] closer_signature
Логические значения:
1. кто сформировал/закрыл блок;
2. hash точных bytes блока;
3. подпись закрывающего аккаунта.
closer_login MUST точно совпадать с creator_login из header.
Если значения отличаются — блок невалиден.
22. Подпись большого блока
Алгоритм v1:
Ed25519
Подписываемый payload:
ASCII("SHINE-ARCHIVE-V1") || block_hash
То есть:
closer_signature =
Ed25519Sign(
closer_private_key,
"SHINE-ARCHIVE-V1" || block_hash
)
Для первой реализации closer_private_key может быть root private key SHiNE-аккаунта creator_login / closer_login.
Public key для проверки берётся из соответствующего User PDA в Solana.
23. Archive head в Solana User PDA
Отдельный PDA создавать не требуется.
Используется тот же существующий User PDA.
Новый block type:
100
Формат:
u8 block_type = 100
u8 block_version = 0
bytes[32] archive_tx_id
bytes[32] archive_hash
Размер:
66 байт
Solana тем самым хранит:
где находится текущая голова -> Arweave TX ID
какие exact bytes являются правильными -> SHA-256
24. Обновление Solana — обычный User PDA update
Отдельная Solana instruction для archive head не требуется.
Архивный сервер использует существующий обычный механизм обновления собственного User PDA:
- читает текущий PDA;
- сохраняет все существующие поля;
- меняет/добавляет только block type
100; - пересобирает новую PDA-запись;
- выполняет существующую root-signature проверку;
client keyподписывает/оплачивает Solana transaction;- ждёт
finalized.
Важно: все новые Java/JS/Rust сериализаторы обязаны понимать block type 100 и сохранять его при обычных обновлениях PDA.
25. TestFreeAvatarArweaveService
Старый сервис больше не нужен как avatar test service.
Его следует переименовать, например, в:
ArweaveArchiveService
и переделать только под серверную фиксацию.
Нужно оставить/переиспользовать:
- JWK parsing;
- wallet address;
- reward calculation;
- balance check;
- RSA-PSS signing;
- Arweave HTTP.
Удалить avatar-specific части:
- PNG/JPEG/WebP проверки;
- avatar quota;
- лимит 128 KiB;
- avatar DAO;
- avatar-specific API.
Новый сервис должен уметь:
publishArchive(...)
waitForConfirmation(...)
и корректно работать с большими/chunked uploads.
26. Ожидание закрепления Arweave
После получения TX ID сервер НЕ обновляет Solana сразу.
Он ждёт заданное число подтверждений.
Рекомендуемые настройки:
archive.arweave.minConfirmations=1
archive.arweave.confirmPollSeconds=30
archive.arweave.confirmTimeoutMinutes=180
Только после ARWEAVE_CONFIRMED разрешён update User PDA.
27. Порядок публикации
Строго:
1. Синхронизировать/иметь актуальные локальные блоки.
2. Прочитать archive_chain_cursor.
3. Зафиксировать snapshot новых диапазонов.
4. Создать archive_publish_job.
5. Собрать большой SHINE-ARCHIVE v1.0.
6. В header записать creator_login.
7. В начало блока записать FULL_HISTORY список всех предыдущих больших блоков:
block_number + hash + Arweave TX ID.
8. Для каждого пользователя сформировать server navigation metadata:
previous big block + список всех offset/size его записей там.
9. Посчитать block_hash.
10. В footer записать closer_login, block_hash, signature.
11. Загрузить файл в Arweave.
12. Сохранить TX ID в publish job.
13. Дождаться закрепления Arweave.
14. Обычным User PDA update записать block type 100:
archive_tx_id + archive_hash.
15. Дождаться Solana finalized.
16. В одной локальной DB transaction сдвинуть archive_chain_cursor.
17. Отметить job CURSORS_COMMITTED.
Если новых данных нет — пустой большой блок создавать не нужно.
28. Crash recovery
Crash после snapshot
Продолжить с теми же frozen ranges.
Crash после построения файла
Использовать тот же файл и тот же hash.
Crash после Arweave upload
Не загружать второй раз. Использовать сохранённый TX ID.
Crash после Arweave confirmation
Продолжить с User PDA update.
Crash после Solana submit
Сначала проверить текущий PDA/transaction.
Crash после Solana finalized, но до cursor commit
Если PDA уже содержит ожидаемые:
archive_tx_id
archive_hash
то безопасно выполнить локальный cursor commit.
29. Настройки scheduler
archive.publish.enabled=false
archive.publish.intervalMinutes=720
archive.publish.initialDelayMinutes=15
archive.arweave.gateway=https://arweave.net
archive.arweave.walletJwkPath=/opt/shine/secrets/archive-wallet.json
archive.arweave.minConfirmations=1
archive.arweave.confirmPollSeconds=30
archive.arweave.confirmTimeoutMinutes=180
archive.solana.rootKeyPath=/opt/shine/secrets/root.key
archive.solana.clientKeyPath=/opt/shine/secrets/client.key
archive.solana.commitment=finalized
Для первой версии разумно начать с:
720 минут = раз в 12 часов
Частота обычной межсерверной синхронизации меняется независимо.
30. Проверка большого блока клиентом
Клиент SHOULD выполнять:
1. Скачать начало файла.
2. Проверить magic.
3. Прочитать major/minor.
4. Прочитать header_size.
5. Прочитать creator_login.
6. Докачать весь header + PreviousBlockReference table.
7. При необходимости получить нужные previous block TXID/hash.
8. Скачать нужные user record ranges.
9. При полной проверке файла пересчитать block_hash.
10. Проверить closer_login == creator_login.
11. Получить public key автора из User PDA.
12. Проверить Ed25519 signature.
31. Пример навигации Alice
Допустим клиент уже имеет актуальную запись Alice из block 100.
Server navigation metadata говорит:
previous_block_ref = 94
previous_records_count = 3
[8123, 417]
[9001, 351]
[14020, 692]
Из начала block 100:
PreviousBlockReference[94]:
block_number = 95
block_hash = H95
arweave_tx_id = TX95
Клиент:
1. идёт в TX95;
2. проверяет H95;
3. делает Range на 8123/417;
4. Range на 9001/351;
5. Range на 14020/692;
6. получает все три Alice records из block 95;
7. из server navigation metadata последней Alice record получает следующий переход, например на block 81;
8. повторяет.
То есть история читается:
100 -> 95 -> 81 -> 63 -> ...
но на каждом шаге скачиваются только конкретные записи данного пользователя.
32. Почему hash и TX ID нужны одновременно
Нельзя оставить только TX ID.
Правильная модель:
block_number
-> логический номер
block_hash
-> криптографическая идентичность
arweave_tx_id
-> текущее физическое расположение
Если в будущем большой блок будет перенесён из Arweave в другое хранилище, его hash останется тем же.
33. Будущее PARTIAL_HISTORY
В первой реализации используется только:
references_mode = FULL_HISTORY
На будущее уже зарезервирован:
PARTIAL_HISTORY
Будущая версия сможет, например, хранить:
- последние N больших блоков;
- checkpoint-блоки;
- только реально используемые ссылки;
- многоуровневый индекс.
Но это НЕ требуется для v1.
34. Итоговые бинарные структуры v1.0
Header
bytes[13] magic = "SHINE-ARCHIVE"
u8 version_major
u8 version_minor
u32 header_size
u32 block_number
u64 created_at_ms
u8 creator_login_length
bytes[N] creator_login
u8 references_mode
u32 references_count
u16 reference_entry_size
u32 records_count
PreviousBlockReference
u32 block_number
bytes[32] block_hash
bytes[32] arweave_tx_id
Размер v1:
68 bytes
PreviousUserRecordsRef
u32 previous_block_ref
u32 previous_records_count
repeat previous_records_count:
u32 previous_record_offset
u32 previous_record_size
User archived envelope — рекомендуемая форма
u32 archived_record_size
u32 raw_user_record_size
bytes[N] raw_user_record
u8 navigation_flags
[optional PreviousUserRecordsRef]
Footer
u8 closer_login_length
bytes[N] closer_login
bytes[32] block_hash
bytes[64] closer_signature
Solana User PDA block 100
u8 block_type = 100
u8 block_version = 0
bytes[32] archive_tx_id
bytes[32] archive_hash
35. Обязательные проверки v1
Publisher MUST:
- не изменять frozen job после snapshot;
- проверять cursor hash;
- не двигать cursor до Solana finalized;
- не публиковать пустую дельту;
- записывать полный список предыдущих больших блоков;
- хранить hash и TX ID каждого previous block;
- записывать creator login в header;
- повторять тот же login в footer;
- подписывать hash большого блока;
- добавлять для пользователя ссылку на предыдущий большой блок и offsets/sizes всех его записей там.
Reader MUST:
- проверять magic/version;
- проверять границы offsets;
- проверять hash скачанного большого блока;
- проверять
closer_login == creator_login; - проверять подпись;
- проверять, что скачанные user records действительно принадлежат ожидаемому пользователю;
- не считать Arweave TX ID самостоятельным доказательством корректности содержимого.
36. Реализационный checklist
Solana
- Добавить User PDA block type
100. - Добавить
archive_tx_id [32]. - Добавить
archive_hash [32]. - Научить обычный User PDA update читать и сохранять block 100.
- Серверу дать возможность обычным update менять block 100.
- Ждать
finalized.
Database
archive_chain_cursor.archive_publish_job.archive_publish_job_chain.
Arweave
- Переименовать
TestFreeAvatarArweaveServiceвArweaveArchiveService. - Удалить avatar-specific код.
- Реализовать большие/chunked uploads.
- Реализовать ожидание confirmations.
- Сохранять TX ID до ожидания подтверждений.
Большой блок
- Magic
SHINE-ARCHIVE. version_major u8.version_minor u8.header_size.block_number u32.created_at_ms u64.creator_loginв header.FULL_HISTORYprevious block table.block_number + block_hash + txidдля каждого previous block.- Server-side
PreviousUserRecordsRef. - Список всех
offset + sizeпользовательских записей из предыдущего big block. closer_login + block_hash + signatureв footer.
Scheduler / recovery
- Настраиваемый interval.
- Initial delay.
- Один active job.
- Resume после restart.
- Cursor commit только после Solana finalized.
37. Краткая итоговая модель
SOLANA USER PDA
|
+--> block 100
archive TX ID
archive SHA-256
|
v
SHINE-ARCHIVE block N
|
| HEADER:
| version
| block number
| time
| CREATOR LOGIN
|
| PREVIOUS BLOCK TABLE:
| #0 number/hash/TXID
| #1 number/hash/TXID
| ... все предыдущие в v1
|
| USER DATA:
| original signed user records
| server navigation metadata
| previous block ref
| ALL previous user record offsets/sizes
|
| FOOTER:
| same creator/closer login
| hash
| Ed25519 signature
v
SHINE-ARCHIVE block N-1
|
v
...
Это завершает согласованную спецификацию SHiNE Archive Protocol v1.0.