# ANS-104 / Arweave transport для пользовательских блоков SHiNE ## Цель Каждый пользовательский блок SHiNE уже на клиенте является самостоятельным подписанным ANS-104 DataItem. Сервер проверяет и хранит **точно эти signed bytes** и может публиковать их одним из двух транспортов: через Turbo по одному DataItem либо через прямую Arweave L1-транзакцию в составе стандартного большого ANS-104 bundle. Способ публикации — локальная политика конкретного сервера. Формат пользовательского блока и импорт от него не зависят. ## User DataItem tags Для тестового контура обязательно: ```text App=test5590 ``` Для блоков конкретного канала дополнительно: ```text c_test5590= ``` Теги входят в ANS-104 подпись пользователя. Старый тестовый тег `c` новым кодом не создаётся и не принимается как channel tag. ## Publisher modes Настройка: ```text arweave.blocks.publish.mode=turbo | arweave | none ``` ### `turbo` ```text blocks.arweave_publish_pending=true ↓ готовый signed user DataItem из blocks.block_bytes ↓ POST в Turbo как application/octet-stream ↓ Turbo bundling / Arweave ``` DataItem **не переподписывается** сервером. Его `data_item_id = SHA-256(user signature)` до и после загрузки должен оставаться тем же. Для Turbo можно задать публичный payer address напрямую: ```text arweave.blocks.publish.turbo.paidByAddress=... ``` либо путь к серверному Arweave JWK: ```text arweave.blocks.publish.turbo.walletJwkPath=/path/to/server-turbo-wallet.json ``` Из JWK локально вычисляется только публичный Arweave address для `x-paid-by`; приватный ключ Turbo upload endpoint не получает. Важно: если upload уже требует оплаты, а signed DataItem принадлежит другому signer, Turbo Credits серверного кошелька используются через Credit Share Approval в пользу signer-адреса. Для маленьких DataItem, попадающих под действующий free tier Turbo, payer может не понадобиться. Код не должен рассчитывать на вечное существование free tier: HTTP `402` считается ошибкой оплаты и блок остаётся pending. ### `arweave` Сохраняется прежний fallback: ```text pending user DataItems ↓ standard ANS-104 binary bundle ↓ server Arweave RSA/JWK signature ↓ Arweave L1 ``` Root transaction содержит только стандартные bundle tags: ```text Bundle-Format=binary Bundle-Version=2.0.0 Content-Type=application/octet-stream ``` Специальный `App=test5590-batch` больше не используется. Важны вложенные user DataItems, у которых уже есть `App=test5590`. ### `none` Сервер принимает и хранит блоки локально, но publisher не отправляет их в Arweave/Turbo. Importer при этом может работать независимо. ## Состояние публикации в БД После успешной публикации: - `arweave_publish_pending=false`; - заполняется `arweave_published_at_ms`. `arweave_root_tx_id` больше не хранится: один и тот же пользовательский DataItem может быть физически упакован разными bundler-ами, а стабильным сетевым идентификатором SHiNE является именно `data_item_id`. ## Importer: только individual DataItems Importer всегда выполняет один discovery-запрос: ```text App=test5590 ``` Он **не ищет root bundles** и не зависит от `publish.mode`. Это одинаково работает для: - DataItem, отправленного через Turbo; - DataItem, находящегося внутри большого direct-Arweave ANS-104 bundle сервера. После того как AR.IO gateway распаковал/indexed bundle, child DataItem присутствует в GraphQL как отдельная сущность со своим `id` и собственными tags. ### Получение полного signed DataItem Обычная выдача DataItem по gateway URL может представлять только payload, а SHiNE для криптографической проверки нужны полные serialized ANS-104 bytes. Поэтому importer: 1. получает `data_item_id` через GraphQL `App=test5590`; 2. запрашивает `GET /ar-io/offsets/{data_item_id}`; 3. получает `rootTxId`, `rootOffset`, `size`; 4. делает range-read `GET /raw/{rootTxId}` ровно по этому диапазону; 5. разбирает полученные bytes как `Ans104DataItem`; 6. проверяет, что `SHA-256(signature) == data_item_id`; 7. проверяет Ed25519 ANS-104 signature; 8. определяет пользователя по `owner`; 9. импортирует через обычную логику `AddBlock` без повторной публикации. Если GraphQL уже увидел DataItem, но gateway ещё не подготовил offsets, checkpoint не продвигается за этот height и DataItem будет повторён в следующем цикле. ## Очередь и порядок блоков `arweave_block_import_queue` хранит: - `data_item_id`; - `block_height`; - полный `raw_data_item`; - status/error/timestamps. `root_tx_id` очереди больше не нужен. Если block N+1 увиден раньше N, он остаётся `PENDING`; после появления предыдущего блока очередь повторно проигрывается. ## Дедупликация `blocks.data_item_id` уникален. Один signed DataItem остаётся одним логическим SHiNE-блоком независимо от того, сколько серверов или bundler-ов физически включили его в Arweave. Импортированный блок записывается через `AddBlock` с отключённой повторной публикацией, поэтому серверы не создают цикл переархивирования. ## Локальное хранение Полный serialized signed DataItem хранится в `blocks.block_bytes` PostgreSQL. Пользовательские `.bch`-файлы не являются источником истины.