Архивация в Arweave

This commit is contained in:
AidarKC
2026-09-12 10:17:23 +03:00
parent ef068eca81
commit c3253dceac
48 changed files with 6681 additions and 1227 deletions
+120 -54
View File
@@ -168,19 +168,78 @@ BigBlock 70
---
# 6. Периодичность
# 6. Расписание публикации
Частота архивной публикации конфигурируется отдельно от существующей межсерверной синхронизации.
Архивная публикация запускается один раз в сутки в заданное локальное время. Она НЕ использует интервал «каждые N минут».
Пример:
```properties
archive.publish.enabled=true
archive.publish.intervalMinutes=720
archive.publish.initialDelayMinutes=15
archive.publish.time=00:00
archive.publish.zoneId=
```
`720` минут = раз в 12 часов.
Для v1.0 значение по умолчанию:
```text
00:00
```
То есть новый snapshot и новый большой архивный блок создаются один раз в сутки в полночь.
Если `archive.publish.zoneId` пуст, используется системная timezone сервера. При необходимости её можно задать явно, например `Europe/Warsaw`. Это сохраняет публикацию ровно в указанное локальное время даже при переходах летнего/зимнего времени.
Незавершённый archive job после рестарта не ждёт следующей полуночи: сервер продолжает именно его сразу. Новый snapshot при старте вне назначенного времени не создаётся.
## 6.1. Первая архивная публикация
Если у данного archive publisher ещё нет подтверждённых архивных курсоров, первая публикация берёт ВСЁ локально известное состояние:
```text
для каждой blockchain_name:
source block 0 .. current local head
```
То есть первый большой архивный блок содержит все SHiNE-блоки, которые сервер успел узнать к моменту первого суточного snapshot. После успешной публикации следующие большие блоки содержат только дельту относительно подтверждённых курсоров.
## 6.2. Локальная папка и имена файлов
Перед любой сетевой загрузкой большой блок сначала полностью создаётся на локальном диске. По умолчанию каталог:
```text
data/archive/
```
До получения Arweave TX ID файл имеет временное имя:
```text
<login>.<00001>.<дд.мм.гг>.tmp.SHiNE-archive
```
Например:
```text
archive01.00001.11.09.26.tmp.SHiNE-archive
```
Дата — реальная дата создания/freeze snapshot большого блока в timezone archive publisher-а. Номер имеет минимальную ширину 5 цифр. Пять цифр — форматирование, а не лимит: блок `100000` получает шестизначный номер.
После успешной загрузки Arweave возвращает реальный TX ID. Сервер сначала надёжно сохраняет TX ID в БД, затем атомарно переименовывает тот же локальный файл в:
```text
<login>.<00001>.<дд.мм.гг>.<ARWEAVE_TX_ID>.SHiNE-archive
```
Например:
```text
archive01.00001.11.09.26.Xm32...kP9.SHiNE-archive
```
Поле `<ARWEAVE_TX_ID>` — не слово `trx`, а настоящий Base64URL TX ID загруженного объекта в Arweave. Финальный файл остаётся локально как постоянная копия.
Crash recovery обязан продолжать работу с этим же файлом. Если TX ID уже сохранён, но процесс упал до rename, при следующем запуске сервер вычисляет финальное имя из сохранённого TX ID и завершает переименование без повторной сборки дельты.
---
@@ -380,7 +439,7 @@ Offsets и sizes внутри `SHINE-ARCHIVE v1.0` используют `u32`.
archive.maxFileBytes=4000000000
```
Если данных больше, один scheduler-run формирует несколько последовательных больших блоков.
Если собранный frozen job превышает этот лимит, v1.0 останавливает публикацию с явной ошибкой `ArchiveTooLargeException` и не двигает курсоры. Практически лимит очень велик; для такого сервера следует уменьшить объём данных между суточными закрытиями или реализовать деление snapshot на несколько big blocks. Автоматическое деление одного snapshot на несколько big blocks оставлено как совместимое будущее расширение.
---
@@ -480,15 +539,17 @@ bytes[N] creator_login UTF-8
u32
```
Рекомендуемая нумерация:
Рекомендуемая нумерация опубликованных больших блоков:
```text
genesis = 0
next = 1
next = 2
first = 1
next = 2
next = 3
...
```
Первый реально публикуемый большой блок имеет номер `1`, поэтому его локальное имя содержит `00001`. Неудачная незавершённая попытка не становится частью опубликованной archive-цепочки.
---
# 20. `created_at_ms`
@@ -530,9 +591,9 @@ Writer v1.0 MUST использовать `FULL`.
BigBlock #365
References:
#0
#1
#2
#3
...
#364
```
@@ -595,7 +656,7 @@ u32
Индекс непосредственного родителя внутри reference table.
Для genesis:
Для первого большого блока (`#1`), у которого нет родителя:
```text
0xFFFFFFFF
@@ -1061,7 +1122,7 @@ last_chunk_size = new_chunk_size
# 54. Arweave service
Старый `TestFreeAvatarArweaveService` больше не нужен как avatar-specific сервис.
Старый `TestFreeAvatarArweaveService` удаляется из активного протокола; archive publisher использует отдельный `ArweaveArchiveService`.
Его следует переделать/переименовать, например в:
@@ -1307,8 +1368,9 @@ derivePublic(client_private) == UserPDA.client_key
```properties
archive.publish.enabled=false
archive.publish.intervalMinutes=720
archive.publish.initialDelayMinutes=15
archive.publish.time=00:00
archive.publish.zoneId=
archive.workDir=data/archive
archive.maxFileBytes=4000000000
@@ -1321,6 +1383,9 @@ 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.
```
---
@@ -1381,54 +1446,55 @@ Lock не удерживается во время Arweave/Solana ожидани
## Database
- [ ] `archive_chain_cursor`
- [ ] `archive_publish_job`
- [ ] `archive_publish_job_chain`
- [ ] schema migration
- [ ] crash recovery
- [x] `archive_chain_cursor`
- [x] `archive_publish_job`
- [x] `archive_publish_job_chain`
- [x] schema migration
- [x] 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
- [x] magic `SHINE-ARCHIVE`
- [x] major/minor version
- [x] fixed header
- [x] creator login
- [x] FULL previous big block table
- [x] one chunk per `blockchain_name`
- [x] many raw records inside one chunk
- [x] one backlink per chunk
- [x] closer login
- [x] SHA-256
- [x] Ed25519 signature
- [x] max file size < 4 GiB
- [x] local `.tmp.SHiNE-archive -> .<ArweaveTX>.SHiNE-archive` lifecycle in `data/archive`
## Arweave
- [ ] rename/refactor `TestFreeAvatarArweaveService`
- [ ] remove avatar-specific logic
- [ ] large/chunked upload
- [ ] confirmation polling
- [x] rename/refactor `TestFreeAvatarArweaveService`
- [x] remove avatar-specific logic
- [x] large/chunked upload
- [x] 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`
- [x] add PDA block type `100`
- [x] update Rust codec
- [x] update Java codec
- [x] update JS codec/UI writer
- [x] use ordinary `update_user_pda`
- [x] server-side transaction writer
- [x] root signature
- [x] client fee payer
- [x] wait for `finalized`
## Scheduler
- [ ] `archive.publish.enabled`
- [ ] `archive.publish.intervalMinutes`
- [ ] `archive.publish.initialDelayMinutes`
- [ ] no concurrent jobs
- [ ] split files > max size
- [ ] skip when no new blocks
- [x] `archive.publish.enabled`
- [x] `archive.publish.time`
- [x] `archive.publish.zoneId`
- [x] no concurrent jobs
- [ ] future: автоматическое split > max size (v1.0 сейчас безопасно останавливается без cursor commit)
- [x] skip when no new blocks
---
@@ -1506,10 +1572,10 @@ BigBlock #100
| creator = server-A
|
+-- References
| #0 -> BigBlock #0 / hash / TX
| #1 -> BigBlock #1 / hash / TX
| ref[0] -> BigBlock #1 / hash / TX
| ref[1] -> BigBlock #2 / hash / TX
| ...
| #99 -> BigBlock #99 / hash / TX
| ref[98] -> BigBlock #99 / hash / TX
|
+-- alice-001 chunk
| records x4