# API для разработчиков: 04 — Запись и чтение блока блокчейна Документ описывает **текущий рабочий формат** сетевых вызовов: - `AddBlock` — запись любого блока в блокчейн пользователя; - `GetBlockchainBlock` — публичное чтение одного конкретного блока по имени цепочки и номеру. `GetBlockchainBlock` нужен в том числе для межсерверной синхронизации и для открытого чтения публичного блокчейна по одному блоку. > Важный принцип: на уровне JSON API сейчас есть **один универсальный метод** записи — `AddBlock`. > Конкретный смысл записи задаётся типом самого бинарного блока (`type/subType/version` в заголовке блока). ## 1. Что делает `AddBlock` `AddBlock`: - принимает имя блокчейна и base64 бинарного блока; - проверяет непрерывность цепочки (`blockNumber`, `prevHash`); - проверяет формат и подпись Ed25519; - валидирует `body` по правилам типа блока; - сохраняет блок и обновляет состояние цепочки. ## 2. JSON формат запроса `op = "AddBlock"`. ```json { "op": "AddBlock", "requestId": "req-1001", "payload": { "blockchainName": "alice-001", "blockNumber": 12, "prevBlockHash": "ab12...ff", "blockBytesB64": "AAAB..." } } ``` Поля `payload`: - `blockchainName` — обязательно, формат `login-NNN`. - `blockNumber` — обязательно (временное legacy-поле для совместимости; должно совпасть с номером внутри бинарного блока). - `prevBlockHash` — legacy-поле, сейчас сервер использует `prevHash` из бинарного блока и состояние цепочки. - `blockBytesB64` — обязательно: **полный бинарный блок** (`preimage + sigMarker + signature`) в Base64. ## 3. Успешный ответ ```json { "op": "AddBlock", "requestId": "req-1001", "status": 200, "ok": true, "payload": { "reasonCode": null, "serverLastGlobalNumber": 12, "serverLastGlobalHash": "9f0e...a1" } } ``` ## 4. Ошибка (единый формат) При ошибках сервер отдаёт `Net_Exception_Response` со стандартными полями и дополнительно с состоянием сервера для ресинка: ```json { "op": "AddBlock", "requestId": "req-1001", "status": 400, "ok": false, "error": "bad_prev_hash", "message": "Некорректный prevHash (цепочка не совпадает)", "payload": { "serverLastGlobalNumber": 11, "serverLastGlobalHash": "c3d4...98" } } ``` ### Основные `reasonCode` - `empty_blockchain_name`, `bad_blockchain_name` - `blockchain_state_not_found` - `bad_block_base64`, `bad_block_format`, `bad_block_body` - `bad_block_number`, `req_global_mismatch`, `bad_prev_hash` - `bad_signature`, `signature_verify_failed` - `prev_line_block_not_found`, `bad_prev_line_hash` - `limit_exceeded` - `chain_resync_in_progress` — цепочка временно заблокирована полным resync - `repost_disabled` — репосты временно отключены до будущей реализации - `internal_error` ## 5. Какие блоки реально можно добавлять через `AddBlock` Через `AddBlock` можно писать поддержанные форматы, кроме явно отключённых временных фич: 1. **TECH (type=0)** - `HEADER_COMPAT (subType=0)` - `TECH_CREATE_CHANNEL (subType=1)` 2. **TEXT (type=1)** - `TEXT_POST (10)` - `TEXT_EDIT_POST (11)` - `TEXT_REPLY (20)` - `TEXT_EDIT_REPLY (21)` - `TEXT_REPOST (30)` — формат зарезервирован, но новые блоки временно отклоняются с `repost_disabled` 3. **REACTION (type=2)** - `REACTION_LIKE (1)` 4. **CONNECTION (type=3)** - `CONNECTION_FRIEND (10)` - `CONNECTION_UNFRIEND (11)` - `CONNECTION_CONTACT (20)` - `CONNECTION_UNCONTACT (21)` - `CONNECTION_FOLLOW (30)` - `CONNECTION_UNFOLLOW (31)` - `CONNECTION_SPOUSE (40)` - `CONNECTION_UNSPOUSE (41)` - `CONNECTION_PARENT (50)` - `CONNECTION_UNPARENT (51)` - `CONNECTION_CHILD (52)` - `CONNECTION_UNCHILD (53)` - `CONNECTION_SIBLING (54)` - `CONNECTION_UNSIBLING (55)` - `CONNECTION_KNOWN_PERSON (60)` - `CONNECTION_UNKNOWN_PERSON (61)` - `CONNECTION_SHINE_CONFIRMED (70)` - `CONNECTION_SHINE_UNCONFIRMED (71)` - `CONNECTION_SHINE_SEEN (74)` - `CONNECTION_SHINE_UNSEEN (75)` 5. **USER_PARAM (type=4)** - `USER_PARAM_TEXT_TEXT (1)` ## 6. Хватает ли функций сейчас Коротко: **для записи событий в блокчейн — хватает**, для полноценного клиентского чтения — **пока не хватает**. Что есть: - единый надёжный write-путь `AddBlock`; - есть `GetFriendsLists` и API по `UserParam`; - есть унифицированные коды ошибок и поля для ресинхронизации. Что пока ограничивает продукт: - нет полноценного read API для каналов/постов/тредов; - нет API списка подписок с серверными счётчиками непрочитанного; - нет ленты событий (новые ответы/лайки/подписки) как отдельного RPC. ## 7. Рекомендации по клиенту при записи блоков 1. Перед отправкой держать локальный `lastNumber/lastHash`. 2. При `bad_prev_hash` или `bad_block_number`: - взять `serverLastGlobalNumber/serverLastGlobalHash` из ошибки, - пересобрать следующий блок на актуальной вершине. 3. Для edit-блоков всегда ссылаться на **оригинальный** блок, а не на предыдущий edit. 4. Для связей/подписок использовать target на **root** (HEADER или CREATE_CHANNEL), а не на произвольный пост. ## 8. USER_PARAM для «личных данных» Да, на текущем API это можно добавить **без изменения серверного кода**: - в `UserParam` поле `param` сейчас не ограничено фиксированным справочником; - сервер хранит пары `param -> value` как строки (при наличии корректной подписи и `time_ms`); - чтение уже есть через `GetUserParam` и `ListUserParams`. Рекомендуемый стартовый набор ключей для профиля (MVP): - `name` - `last_name` - `address_physical` - `address_web` - `phone` Практическая рекомендация: заранее зафиксировать единый словарь ключей в клиенте/документации, чтобы избежать дублей вида `lastname` vs `last_name`, `site` vs `address_web` и т.д. Ограничения, которые важно учесть: - сейчас нет серверной ACL-политики чтения параметров (в MVP их может читать любой клиент, который знает `login`); - нет валидации формата значений для конкретных ключей (телефон, URL и т.д. проверяются только на стороне клиента); - нет отдельного индекса/поиска по этим полям — только точечное чтение и listing по `login`. --- ## 9. `GetBlockchainBlock` ### Назначение Публичное чтение одного конкретного блока из цепочки. Нужно для: - открытого чтения блокчейна по одному блоку; - межсерверной синхронизации; - восстановления/докачки отсутствующего хвоста цепочки. ### JSON формат запроса `op = "GetBlockchainBlock"`. ```json { "op": "GetBlockchainBlock", "requestId": "req-2001", "payload": { "blockchainName": "alice-001", "blockNumber": 12 } } ``` Поля `payload`: - `blockchainName` — обязательно, формат `login-NNN`. - `blockNumber` — обязательно, номер блока в цепочке, `>= 0`. ### Успешный ответ ```json { "op": "GetBlockchainBlock", "requestId": "req-2001", "status": 200, "ok": true, "payload": { "blockchainName": "alice-001", "blockNumber": 12, "blockHash": "9f0eaabbccddeeff00112233445566778899aabbccddeeff0011223344556677", "blockBytesB64": "AAAB..." } } ``` ### Ошибки - `400 / BAD_FIELDS` — некорректные `blockchainName` или `blockNumber`. - `404 / BLOCK_NOT_FOUND` — такого блока нет. - `500 / INTERNAL_ERROR` — внутренняя ошибка сервера.