Files
SHiNE-server/docs/SHINE_ARCHIVE_PROTOCOL_v1.0_RU_FINAL.md
T
2026-09-12 10:17:23 +03:00

45 KiB
Raw Blame History

SHiNE Archive Protocol v1.0

Полная спецификация серверной архивации SHiNE-блоков в Arweave и фиксации головы архива в Solana

Статус: согласованная спецификация v1.0
Язык: русский
Magic: SHINE-ARCHIVE
Версия протокола: 1.0
Endian: big-endian для всех фиксированных целых чисел


1. Назначение

Этот документ описывает механизм, по которому SHiNE-сервер периодически:

  1. синхронизирует обычные SHiNE-блоки существующим механизмом;
  2. определяет новые локальные блоки, ещё не попавшие в архив;
  3. фиксирует неизменяемую дельту;
  4. группирует все новые записи одной blockchain_name в один блок пользователя внутри большого архивного блока;
  5. добавляет к каждому такому пользовательскому блоку одну ссылку на предыдущий пользовательский блок этой же blockchain_name;
  6. формирует один большой бинарный блок SHINE-ARCHIVE;
  7. загружает его в Arweave;
  8. ждёт закрепления/подтверждения Arweave-транзакции;
  9. обычным обновлением своего существующего User PDA записывает новый archive head в Solana;
  10. только после Solana finalized переводит локальные курсоры на новую точку.

На первом этапе предполагается один архивный Raspberry-сервер, но формат изначально не должен запрещать появление других архиваторов.


2. Основные понятия

2.1. Исходный SHiNE-блок / запись

Это существующие в серверной БД оригинальные block_bytes, относящиеся к конкретной blockchain_name.

Они уже содержат пользовательские данные и существующую криптографию SHiNE.

Архиватор:

  • не изменяет их;
  • не переподписывает их;
  • не переводит их в Base64;
  • не пересобирает их содержимое.

2.2. blockchain_name

В архивном формате основной идентификатор пользовательской цепочки — именно blockchain_name.

Отдельно хранить login для пользовательского блока не требуется.

blockchain_name уже однозначно идентифицирует конкретную пользовательскую blockchain и логически включает пользовательскую идентичность + номер/вариант цепочки.

2.3. Большой архивный блок

Один файл/объект SHINE-ARCHIVE, который сервер формирует за один цикл архивной публикации.

Он содержит:

  • header;
  • список предыдущих больших архивных блоков;
  • по одному UserBlockchainChunk для каждой blockchain_name, у которой есть новые записи;
  • footer;
  • SHA-256 большого блока;
  • подпись сервера, который этот большой блок сформировал и закрыл.

Один большой архивный блок после публикации соответствует одной Arweave-транзакции.

2.4. UserBlockchainChunk

В одном большом архивном блоке для одной blockchain_name существует максимум один такой chunk.

Если за текущую дельту у пользователя/цепочки накопилось 1, 3, 5 или 20 записей, все они помещаются внутрь одного UserBlockchainChunk.

Навигационная ссылка одна на весь chunk.


3. Главная модель пользовательской истории

Допустим alice-001 имела записи:

BigBlock 70:
    AliceChunk = 2 записи

BigBlock 81:
    AliceChunk = 5 записей

BigBlock 95:
    AliceChunk = 3 записи

BigBlock 100:
    AliceChunk = 4 записи

Тогда история alice-001 выглядит:

BigBlock 100
    AliceChunk
        4 записи
        |
        +--> PreviousBlockchainChunkRef
                 |
                 v
BigBlock 95
    AliceChunk
        3 записи
        |
        +--> PreviousBlockchainChunkRef
                 |
                 v
BigBlock 81
    AliceChunk
        5 записей
        |
        v
BigBlock 70

Навигация идёт пачками записей одной blockchain, а не по одной записи за раз.


4. Сервер сам добавляет навигацию

Пользователь не обязан знать:

  • в какой большой архивный блок попадут его записи;
  • какой будет Arweave TX ID;
  • какие будут offsets;
  • где находится предыдущий chunk.

Все эти данные сервер добавляет при формировании большого блока.

Исходные подписанные пользовательские block_bytes остаются неизменными.

PreviousBlockchainChunkRef является внешней серверной архивной метаинформацией и защищается hash + подписью всего большого архивного блока.


5. Необходимые ключи архивного сервера

Архивный Raspberry/server хранит:

5.1. SHiNE root private key

Используется:

  • для подписания новой версии User PDA при обычном update_user_pda;
  • в v1.0 — для подписи закрытого большого архивного блока.

5.2. SHiNE client private key

Используется как Solana transaction signer / fee payer в соответствии с текущей логикой shine_users.

5.3. Arweave JWK / private wallet key

Используется для загрузки и оплаты Arweave-транзакции.

5.4. Blockchain private key

Для архивной публикации не требуется.

Архиватор не создаёт новые пользовательские blockchain-блоки.


6. Расписание публикации

Архивная публикация запускается один раз в сутки в заданное локальное время. Она НЕ использует интервал «каждые N минут».

Пример:

archive.publish.enabled=true
archive.publish.time=00:00
archive.publish.zoneId=

Для v1.0 значение по умолчанию:

00:00

То есть новый snapshot и новый большой архивный блок создаются один раз в сутки в полночь.

Если archive.publish.zoneId пуст, используется системная timezone сервера. При необходимости её можно задать явно, например Europe/Warsaw. Это сохраняет публикацию ровно в указанное локальное время даже при переходах летнего/зимнего времени.

Незавершённый archive job после рестарта не ждёт следующей полуночи: сервер продолжает именно его сразу. Новый snapshot при старте вне назначенного времени не создаётся.

6.1. Первая архивная публикация

Если у данного archive publisher ещё нет подтверждённых архивных курсоров, первая публикация берёт ВСЁ локально известное состояние:

для каждой blockchain_name:
source block 0 .. current local head

То есть первый большой архивный блок содержит все SHiNE-блоки, которые сервер успел узнать к моменту первого суточного snapshot. После успешной публикации следующие большие блоки содержат только дельту относительно подтверждённых курсоров.

6.2. Локальная папка и имена файлов

Перед любой сетевой загрузкой большой блок сначала полностью создаётся на локальном диске. По умолчанию каталог:

data/archive/

До получения Arweave TX ID файл имеет временное имя:

<login>.<00001>.<дд.мм.гг>.tmp.SHiNE-archive

Например:

archive01.00001.11.09.26.tmp.SHiNE-archive

Дата — реальная дата создания/freeze snapshot большого блока в timezone archive publisher-а. Номер имеет минимальную ширину 5 цифр. Пять цифр — форматирование, а не лимит: блок 100000 получает шестизначный номер.

После успешной загрузки Arweave возвращает реальный TX ID. Сервер сначала надёжно сохраняет TX ID в БД, затем атомарно переименовывает тот же локальный файл в:

<login>.<00001>.<дд.мм.гг>.<ARWEAVE_TX_ID>.SHiNE-archive

Например:

archive01.00001.11.09.26.Xm32...kP9.SHiNE-archive

Поле <ARWEAVE_TX_ID> — не слово trx, а настоящий Base64URL TX ID загруженного объекта в Arweave. Финальный файл остаётся локально как постоянная копия.

Crash recovery обязан продолжать работу с этим же файлом. Если TX ID уже сохранён, но процесс упал до rename, при следующем запуске сервер вычисляет финальное имя из сохранённого TX ID и завершает переименование без повторной сборки дельты.


7. Состояние, относительно которого считается дельта

Нужно различать:

  1. уже окончательно опубликованное состояние;
  2. текущий замороженный job, который ещё проходит Arweave/Solana.

Нельзя считать дельту от живой головы во время уже запущенной публикации.


8. Таблица archive_chain_cursor

Хранит окончательно подтверждённое архивное состояние для каждой blockchain_name.

CREATE TABLE archive_chain_cursor (
    blockchain_name                   TEXT PRIMARY KEY,

    last_archived_source_block_number BIGINT NOT NULL,
    last_archived_source_block_hash   BYTEA NOT NULL,

    last_archive_big_block_number     BIGINT,
    last_archive_big_block_hash       BYTEA,

    last_chunk_offset                 BIGINT,
    last_chunk_size                   BIGINT,

    updated_at_ms                     BIGINT NOT NULL
);

Назначение:

  • last_archived_source_block_number — до какого исходного SHiNE-блока этой blockchain всё успешно опубликовано;
  • last_archived_source_block_hash — hash этого исходного блока;
  • last_archive_big_block_number и last_archive_big_block_hash — в каком большом SHINE-ARCHIVE находится последний chunk этой blockchain;
  • last_chunk_offset и last_chunk_size — точное положение последнего chunk внутри большого блока.

Этого достаточно, чтобы следующая публикация создала один backlink на предыдущий chunk.


9. Отдельный индекс по каждой записи не нужен

Поскольку все новые записи одной blockchain_name агрегируются в один UserBlockchainChunk, не нужно хранить offsets каждой отдельной записи в истории.

Для следующей публикации достаточно помнить:

предыдущий big block
предыдущий big block hash
chunk offset
chunk size

10. Таблица archive_publish_job

Хранит crash-safe состояние одной архивной публикации.

CREATE TABLE archive_publish_job (
    id                       BIGSERIAL PRIMARY KEY,

    big_block_number         BIGINT NOT NULL,
    status                   TEXT NOT NULL,

    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

Завершённые rows этой таблицы одновременно дают локальную историю больших блоков:

big_block_number
archive_hash
arweave_tx_id

По ним строится FULL-список предыдущих больших блоков следующей публикации.


11. Таблица archive_publish_job_chain

Хранит frozen range каждой blockchain в текущем job и координаты создаваемого chunk.

CREATE TABLE archive_publish_job_chain (
    job_id                     BIGINT NOT NULL,
    blockchain_name            TEXT NOT NULL,

    from_source_block_number   BIGINT NOT NULL,
    to_source_block_number     BIGINT NOT NULL,

    previous_source_block_hash BYTEA NOT NULL,
    last_source_block_hash     BYTEA NOT NULL,

    previous_archive_big_block_number BIGINT,
    previous_archive_big_block_hash   BYTEA,
    previous_chunk_offset              BIGINT,
    previous_chunk_size                BIGINT,

    new_chunk_offset           BIGINT,
    new_chunk_size             BIGINT,

    PRIMARY KEY (job_id, blockchain_name)
);

12. Заморозка дельты

Пример:

локально:
alice-001 = source block 180
bob-001   = source block 94

опубликовано:
alice-001 = 160
bob-001   = 90

Новый job фиксирует:

alice-001: 161..180
bob-001:    91..94

Если во время загрузки появятся:

alice-001 181..185
bob-001   95..97

они попадут только в следующий job.


13. Проверка курсора перед публикацией

Перед созданием frozen range сервер проверяет:

local hash(last_archived_source_block_number)
==
archive_chain_cursor.last_archived_source_block_hash

При несовпадении публикация этой chain останавливается до resync/fork handling.


14. Ограничение размера большого блока

Offsets и sizes внутри SHINE-ARCHIVE v1.0 используют u32.

Поэтому один большой archive-файл MUST быть меньше 4 GiB.

Рекомендуемый запас:

archive.maxFileBytes=4000000000

Если собранный frozen job превышает этот лимит, v1.0 останавливает публикацию с явной ошибкой ArchiveTooLargeException и не двигает курсоры. Практически лимит очень велик; для такого сервера следует уменьшить объём данных между суточными закрытиями или реализовать деление snapshot на несколько big blocks. Автоматическое деление одного snapshot на несколько big blocks оставлено как совместимое будущее расширение.


15. Magic и версия

Файл начинается ASCII:

SHINE-ARCHIVE

Размер: 13 байт.

Далее:

u8 version_major
u8 version_minor

Для v1.0:

1
0

16. Общий layout большого блока

+------------------------------------------+
| FIXED HEADER                             |
+------------------------------------------+
| creator_login                            |
+------------------------------------------+
| PREVIOUS BIG BLOCK REFERENCES            |
| ref[0]                                   |
| ref[1]                                   |
| ...                                      |
+------------------------------------------+
| UserBlockchainChunk #1                   |
| UserBlockchainChunk #2                   |
| ...                                      |
+------------------------------------------+
| FOOTER: closer_login                     |
| block_hash                               |
| signature                                |
+------------------------------------------+

17. Fixed Header v1.0

Все integers — big-endian.

Поле Тип Размер
magic bytes[13] 13
version_major u8 1
version_minor u8 1
header_size u32 4
big_block_number u32 4
created_at_ms u64 8
creator_login_length u8 1
references_mode u8 1
references_count u32 4
reference_entry_size u16 2
parent_reference_index u32 4
blockchain_chunks_count u32 4
total_user_records_count u32 4

После fixed header немедленно идут creator_login bytes.

header_size — byte offset первого PreviousBigBlockReference.


18. creator_login

SHiNE login пользователя/сервера, который сформировал большой блок.

u8 creator_login_length
bytes[N] creator_login UTF-8

Этот login находится в начале файла, поэтому его можно узнать без скачивания payload.


19. big_block_number

u32

Рекомендуемая нумерация опубликованных больших блоков:

first = 1
next  = 2
next  = 3
...

Первый реально публикуемый большой блок имеет номер 1, поэтому его локальное имя содержит 00001. Неудачная незавершённая попытка не становится частью опубликованной archive-цепочки.


20. created_at_ms

u64

Unix Epoch UTC milliseconds.

Это время заморозки содержимого big block.


21. Режим ссылок на предыдущие большие блоки

references_mode u8

Значения:

0 = FULL
1 = PARTIAL, зарезервировано на будущее

Writer v1.0 MUST использовать FULL.


22. FULL previous-block table

Новый big block содержит ссылки на ВСЕ предыдущие большие блоки своей ветки.

Пример:

BigBlock #365

References:
#1
#2
#3
...
#364

При одном big block в день через год таблица занимает примерно 24.8 KiB.


23. PreviousBigBlockReference

Фиксированный размер: 68 байт.

Поле Тип Размер
big_block_number u32 4
big_block_hash bytes[32] 32
arweave_tx_id bytes[32] 32

Семантика:

big_block_number = логический номер
big_block_hash   = криптографическая идентичность
arweave_tx_id    = где скачать блок

24. references_count

Количество entries в таблице ссылок.

u32

25. reference_entry_size

u16

Для v1.0:

68

Поле оставлено для будущего расширения entry.


26. parent_reference_index

u32

Индекс непосредственного родителя внутри reference table.

Для первого большого блока (#1), у которого нет родителя:

0xFFFFFFFF

В простой FULL-линейной ветке обычно это последний entry.


27. Быстрое скачивание начала блока

Клиент сначала скачивает fixed header + creator login.

Из него узнаёт:

header_size
references_count
reference_entry_size

Затем скачивает только reference table и уже имеет адреса/hash всех предыдущих больших блоков.

Payload пользователей можно пока не скачивать.


28. blockchain_chunks_count

u32

Количество UserBlockchainChunk в текущем big block.

Одна blockchain_name встречается максимум один раз.


29. total_user_records_count

u32

Общее количество исходных SHiNE-записей во всех chunks.


30. Порядок chunks и records

Для детерминированной сериализации chunks SHOULD сортироваться по blockchain_name ASC.

Внутри chunk исходные записи MUST идти по возрастанию номера исходного SHiNE-блока.


31. Формат UserBlockchainChunk

u32       chunk_size

u8        blockchain_name_length
bytes[N]  blockchain_name UTF-8

u32       records_count

repeat records_count:
    u32       record_size
    bytes[M]  raw_record_bytes

u32 previous_big_block_ref
u32 previous_chunk_offset
u32 previous_chunk_size

32. chunk_size

u32

Полный размер chunk от первого байта chunk_size до последнего байта previous_chunk_size включительно.

Это позволяет:

  • быстро пропустить chunk;
  • скачать его Range-запросом;
  • сохранить его точные координаты для следующего backlink.

33. blockchain_name внутри chunk

Хранится только blockchain_name.

Отдельный login не нужен.


34. records_count

u32

Если за период у alice-001 появилось 7 записей:

records_count = 7

Все 7 находятся в одном chunk.


35. Raw records

Каждая исходная запись сериализуется:

u32 record_size
bytes[record_size] raw_record_bytes

raw_record_bytes — оригинальные данные SHiNE из БД.

Сервер не меняет их содержимое.


36. Один backlink на весь chunk

После всех raw records находится ровно один PreviousBlockchainChunkRef.

Он относится ко всей пачке записей blockchain.

Он не повторяется у отдельных записей.


37. PreviousBlockchainChunkRef

Поле Тип Размер
previous_big_block_ref u32 4
previous_chunk_offset u32 4
previous_chunk_size u32 4

Итого 12 байт на весь chunk.


38. previous_big_block_ref

Индекс в PreviousBigBlockReferences[] текущего big block.

Он указывает на тот предыдущий big block, где находится предыдущий chunk этой же blockchain_name.

Это именно reference index, а не номер блока.


39. previous_chunk_offset

u32

Byte offset от начала предыдущего SHINE-ARCHIVE до первого байта предыдущего chunk этой blockchain.

Первый байт chunk — поле chunk_size.


40. previous_chunk_size

u32

Полный размер предыдущего chunk.

Поэтому клиент может Range-запросом забрать сразу всю предыдущую пачку записей пользователя.


41. Первая публикация blockchain

Если у данной blockchain_name нет предыдущего архивного chunk:

previous_big_block_ref = 0xFFFFFFFF
previous_chunk_offset  = 0
previous_chunk_size    = 0

42. Пример backlink-цепочки

В BigBlock #100 есть alice-001 chunk из 4 записей.

Его backlink:

previous_big_block_ref = 94
previous_chunk_offset  = 8123
previous_chunk_size    = 1770

ref[94] содержит:

big_block_number = 95
big_block_hash   = ...
arweave_tx_id    = ...

Клиент:

  1. получает TX big block #95;
  2. проверяет hash;
  3. делает Range на 8123 .. 8123+1770;
  4. получает весь предыдущий alice-001 chunk;
  5. читает его записи;
  6. берёт следующий backlink.

43. Почему TX ID не хранится в каждом chunk

TX ID и hash предыдущих больших блоков уже один раз записаны в общей reference table.

Поэтому в chunk достаточно компактного u32 previous_big_block_ref.


44. Footer большого блока

После последнего chunk:

u8        closer_login_length
bytes[N]  closer_login UTF-8

bytes[32] block_hash
bytes[64] closer_signature

45. closer_login

Login пользователя/сервера, который сформировал и закрыл big block.

В v1.0:

closer_login MUST == creator_login

Login повторяется намеренно:

  • header даёт автора сразу;
  • footer явно фиксирует того, кто закрыл блок;
  • hash покрывает footer login;
  • verifier проверяет совпадение.

46. block_hash

bytes[32]
SHA-256

Хэшируется всё от первого байта magic до последнего байта closer_login включительно.

Не входят:

block_hash
closer_signature

Псевдокод:

hash_input =
    fixed_header
    + creator_login
    + references
    + all UserBlockchainChunks
    + closer_login_length
    + closer_login

block_hash = SHA256(hash_input)

47. closer_signature

bytes[64]
Ed25519

Подписывается:

ASCII("SHINE-ARCHIVE-V1") || block_hash

Для v1.0 используется root private key аккаунта creator_login.


48. Проверка подписи

Verifier:

  1. читает creator_login;
  2. проверяет creator_login == closer_login;
  3. получает root public key аккаунта из User PDA / исторического состояния Solana;
  4. пересчитывает block_hash;
  5. проверяет Ed25519 signature.

Старые архивы должны оставаться проверяемыми после ротации root key, поэтому исторические PDA-состояния нужно сохранять/уметь получать.


49. Собственный Arweave TX ID текущего блока

Текущий файл не может заранее знать свой будущий TX ID.

Поэтому собственный TX ID:

  • не входит в сам текущий файл;
  • сохраняется после upload;
  • записывается в Solana PDA;
  • появляется как previous-block reference уже в следующем big block.

50. User PDA block type 100

Archive head хранится прямо в существующем User PDA publisher-а.

Формат:

u8        block_type = 100
u8        block_version = 0
bytes[32] archive_tx_id
bytes[32] archive_hash

Итого 66 байт.

archive_hash = block_hash последнего успешно опубликованного big block.


51. Обновление Solana

Отдельная новая Solana instruction не требуется.

Используется существующий update_user_pda.

Нужно расширить существующие serializer/parser/codec так, чтобы block type 100:

  • читался;
  • записывался;
  • сохранялся обычными обновлениями;
  • мог быть изменён архивным сервером обычным update User PDA.

52. Полный порядок публикации

1. Существующая server-to-server sync независимо обновляет локальные blockchains.

2. Scheduler запускает archive job.

3. Проверяется отсутствие другого активного archive job.

4. Читаются archive_chain_cursor.

5. Читаются текущие локальные blockchain heads.

6. Для каждой blockchain вычисляется frozen delta.

7. Создаётся archive_publish_job.

8. Создаются archive_publish_job_chain rows.

9. Загружается история всех успешно finalized previous big blocks.

10. Формируется fixed header.

11. Записывается creator_login.

12. Записывается FULL PreviousBigBlockReferences.

13. Для каждой blockchain_name:
      - читается frozen range;
      - создаётся ровно один UserBlockchainChunk;
      - внутрь кладутся все новые records;
      - добавляется один PreviousBlockchainChunkRef;
      - сохраняются new_chunk_offset/new_chunk_size.

14. Записывается closer_login.

15. Вычисляется SHA-256 block_hash.

16. Создаётся Ed25519 closer_signature.

17. Файл закрывается.

18. Файл загружается в Arweave.

19. TX ID сохраняется в archive_publish_job.

20. Сервер ждёт требуемое число Arweave confirmations.

21. Обычным update_user_pda обновляется block type 100:
      archive_tx_id
      archive_hash

22. Сервер ждёт Solana finalized.

23. В одной DB transaction обновляются archive_chain_cursor.

24. Job получает CURSORS_COMMITTED.

53. Cursor commit

Для каждой blockchain из job:

last_archived_source_block_number = to_source_block_number
last_archived_source_block_hash   = last_source_block_hash

last_archive_big_block_number     = current big_block_number
last_archive_big_block_hash       = current block_hash

last_chunk_offset                 = new_chunk_offset
last_chunk_size                   = new_chunk_size

Следующая публикация сразу знает, куда должен вести backlink.


54. Arweave service

Старый TestFreeAvatarArweaveService удаляется из активного протокола; archive publisher использует отдельный ArweaveArchiveService.

Его следует переделать/переименовать, например в:

ArweaveArchiveService

Он должен:

  • использовать Arweave JWK;
  • подписывать и оплачивать upload;
  • поддерживать большие/chunked uploads;
  • возвращать TX ID;
  • ждать подтверждения;
  • опрашивать confirmations.

Рекомендуемые настройки:

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

55. Solana finalization

После обычного update_user_pda сервер должен дождаться:

finalized

Только после этого разрешается сдвинуть локальные курсоры.


56. Crash recovery

После SNAPSHOT_CREATED

Возобновить тот же frozen job.

После FILE_BUILT

Использовать тот же локальный файл.

После ARWEAVE_UPLOADED

Не загружать второй раз. Продолжить по сохранённому TX ID.

После ARWEAVE_CONFIRMED

Продолжить Solana update.

После SOLANA_SUBMITTED

Сначала проверить transaction / текущий PDA.

После SOLANA_FINALIZED, но до cursor commit

Проверить:

PDA.archive_tx_id == expected_tx
PDA.archive_hash  == expected_hash

После этого безопасно выполнить cursor commit.


57. Нет новых данных

Если новых записей нет ни в одной blockchain:

не создавать пустой SHINE-ARCHIVE
не загружать Arweave
не обновлять Solana

58. FULL references как стартовая стратегия

v1.0 сознательно хранит ВСЕ previous big blocks в каждом новом big block.

Плюсы:

  • актуальный блок сразу даёт карту всей предыдущей истории;
  • не нужно идти parent-by-parent;
  • backlink может использовать компактный ref index;
  • reader очень простой.

При одном big block в сутки:

1 год:  ~24.8 KiB
5 лет:  ~121 KiB
20 лет: ~485 KiB

59. PARTIAL references

references_mode = 1 зарезервирован на будущее.

v1.0 writer его не реализует.

В будущем PARTIAL может означать:

  • только реально используемые previous blocks;
  • последние N blocks;
  • checkpoint references;
  • смешанную стратегию.

60. Чтение истории одной blockchain

Имея актуальный chunk alice-001, клиент:

  1. читает все records chunk;
  2. читает PreviousBlockchainChunkRef;
  3. если ref = 0xFFFFFFFF, история закончилась;
  4. иначе берёт соответствующий PreviousBigBlockReference;
  5. получает TX ID + hash previous big block;
  6. Range-запросом читает previous_chunk_offset/previous_chunk_size;
  7. получает сразу всю предыдущую пачку записей alice-001;
  8. повторяет.

Так можно читать историю конкретной blockchain, не скачивая чужие chunks.


61. Дубликаты

Если исходная SHiNE-запись уже есть у получателя, она пропускается/дедуплицируется стандартной логикой.


62. Конфликты исходной blockchain

Архивная ветка сама по себе не является пользовательским конфликтом.

Конфликт возникает, когда одна blockchain_name имеет два несовместимых валидно подписанных продолжения одной предыдущей точки.

Для v1 безопасно:

сохранить обе ветки
пометить blockchain CONFLICTED
остановить автоматическое продолжение

Механизм разрешения fork можно добавить позже.


63. Авторство большого блока

Автор указан дважды:

Header

creator_login
closer_login
block_hash
closer_signature

Для v1.0:

creator_login == closer_login

Это означает: именно этот SHiNE-аккаунт сформировал и криптографически закрыл big block.


64. Подпись сервера не заменяет подписи пользователей

closer_signature доказывает авторство большого archive block.

Каждая пользовательская raw record всё равно проверяется существующей криптографией SHiNE.


65. Независимость от Arweave

Arweave TX ID — location.

SHA-256 — identity.

Поэтому архивы можно перенести на другой storage и всё равно проверять.


66. Block type 100 в Solana — текущая голова

User PDA publisher-а хранит только:

current archive TX ID
current archive SHA-256

Детальная история находится внутри big blocks.


67. Startup preflight

Перед запуском archive publisher сервер SHOULD проверить:

derivePublic(root_private) == UserPDA.root_public_key

и:

derivePublic(client_private) == UserPDA.client_key

При несовпадении archive publisher не запускается.


68. Рекомендуемые настройки

archive.publish.enabled=false
archive.publish.time=00:00
archive.publish.zoneId=
archive.workDir=data/archive

archive.maxFileBytes=4000000000

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

# Отдельного archive.solana.rpcUrl нет.
# Используется solana.users.sync.rpcUrl, а если он пуст — обычный solana.rpcUrl.

69. Требование к AppConfig

Archive settings должны читаться из:

  • Java system properties;
  • environment variables;
  • application.properties.

getInt() и getBoolean() должны использовать общую логику getParam().


70. Что переиспользуется из текущего сервера

Существующие механизмы:

BlockchainStateDAO.listAll()
BlocksDAO.listRangeByNumber(...)

дают необходимые heads и raw block ranges.

Используются существующие:

block_bytes
block_hash
block_signature
blockchain_name
block_number

Server-to-server sync в v1.0 не меняется.


71. Блокировки

Можно использовать существующий blockchain lock только на коротком этапе:

проверить cursor hash
прочитать frozen range
проверить конечный hash
сохранить/сериализовать frozen data

Lock не удерживается во время Arweave/Solana ожиданий.


72. Реализационный checklist

Database

  • archive_chain_cursor
  • archive_publish_job
  • archive_publish_job_chain
  • schema migration
  • crash recovery

Archive writer

  • magic SHINE-ARCHIVE
  • major/minor version
  • fixed header
  • creator login
  • FULL previous big block table
  • one chunk per blockchain_name
  • many raw records inside one chunk
  • one backlink per chunk
  • closer login
  • SHA-256
  • Ed25519 signature
  • max file size < 4 GiB
  • local .tmp.SHiNE-archive -> .<ArweaveTX>.SHiNE-archive lifecycle in data/archive

Arweave

  • rename/refactor TestFreeAvatarArweaveService
  • remove avatar-specific logic
  • large/chunked upload
  • confirmation polling

Solana

  • add PDA block type 100
  • update Rust codec
  • update Java codec
  • update JS codec/UI writer
  • use ordinary update_user_pda
  • server-side transaction writer
  • root signature
  • client fee payer
  • wait for finalized

Scheduler

  • archive.publish.enabled
  • archive.publish.time
  • archive.publish.zoneId
  • no concurrent jobs
  • future: автоматическое split > max size (v1.0 сейчас безопасно останавливается без cursor commit)
  • skip when no new blocks

73. Итоговая бинарная схема v1.0

SHINE-ARCHIVE
version_major u8
version_minor u8

header_size u32
big_block_number u32
created_at_ms u64

creator_login_length u8

references_mode u8
references_count u32
reference_entry_size u16
parent_reference_index u32

blockchain_chunks_count u32
total_user_records_count u32

creator_login bytes


PreviousBigBlockReference[references_count]:

    big_block_number u32
    big_block_hash [32]
    arweave_tx_id [32]


UserBlockchainChunk[blockchain_chunks_count]:

    chunk_size u32

    blockchain_name_length u8
    blockchain_name bytes

    records_count u32

    repeat records_count:
        record_size u32
        raw_record_bytes

    previous_big_block_ref u32
    previous_chunk_offset u32
    previous_chunk_size u32


FOOTER:

    closer_login_length u8
    closer_login bytes

    block_hash [32]
    closer_signature [64]

74. Основная архитектура одним рисунком

SOLANA USER PDA
    |
    | block type 100
    | TX + HASH
    v
BigBlock #100
    |
    +-- Header
    |     creator = server-A
    |
    +-- References
    |     ref[0]  -> BigBlock #1  / hash / TX
    |     ref[1]  -> BigBlock #2  / hash / TX
    |     ...
    |     ref[98] -> BigBlock #99 / hash / TX
    |
    +-- alice-001 chunk
    |     records x4
    |     |
    |     +--> BigBlock #95 / AliceChunk
    |
    +-- bob-001 chunk
    |     records x2
    |     |
    |     +--> BigBlock #98 / BobChunk
    |
    +-- kate-002 chunk
    |     records x10
    |     |
    |     +--> BigBlock #74 / KateChunk
    |
    +-- Footer
          closer = server-A
          SHA-256
          Ed25519 signature

75. Итоговые решения v1.0

  • один большой блок = одна архивная публикация;
  • в начале лежит полный список всех предыдущих больших блоков;
  • каждый previous block reference содержит number + SHA-256 + Arweave TX ID;
  • одна blockchain_name встречается в большом блоке максимум одним chunk;
  • все новые записи этой blockchain объединяются в этот chunk;
  • chunk имеет одну ссылку на предыдущий chunk этой же blockchain;
  • backlink состоит из previous big block ref + chunk offset + chunk size;
  • сервер сам создаёт backlink;
  • пользовательские raw records не меняются;
  • login сервера есть в header и footer;
  • большой блок закрывается SHA-256 + Ed25519 signature сервера;
  • Solana User PDA block 100 хранит текущий TX ID + archive hash;
  • PDA обновляется обычным update_user_pda;
  • Arweave используется как долговременное хранилище;
  • публикация считается завершённой только после Arweave confirmation + Solana finalized + cursor commit.