15 KiB
API для разработчиков: 04 — Запись и чтение блока блокчейна
Документ описывает текущий рабочий формат сетевых вызовов:
AddBlock— запись любого блока в блокчейн пользователя;GetBlockchainBlock— публичное чтение одного конкретного блока по имени цепочки и номеру.
GetBlockchainBlock нужен в том числе для межсерверной синхронизации и для открытого чтения публичного блокчейна по одному блоку.
Важный принцип: на уровне JSON API сейчас есть один универсальный метод записи —
AddBlock. Конкретный смысл записи задаётся типом самого бинарного блока (type/subType/versionв заголовке блока).
1. Что делает AddBlock
AddBlock:
- принимает имя блокчейна и base64 бинарного блока;
- проверяет непрерывность цепочки (
blockNumber,prevHash); - проверяет формат и подпись Ed25519;
- валидирует
bodyпо правилам типа блока; - сохраняет блок и обновляет состояние цепочки.
2. JSON формат запроса
op = "AddBlock".
{
"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. Успешный ответ
{
"op": "AddBlock",
"requestId": "req-1001",
"status": 200,
"ok": true,
"payload": {
"reasonCode": null,
"serverLastGlobalNumber": 12,
"serverLastGlobalHash": "9f0e...a1"
}
}
4. Ошибка (единый формат)
При ошибках сервер отдаёт Net_Exception_Response со стандартными полями и дополнительно с состоянием сервера для ресинка:
{
"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_nameblockchain_state_not_foundbad_block_base64,bad_block_format,bad_block_bodybad_block_number,req_global_mismatch,bad_prev_hashbad_signature,signature_verify_failedprev_line_block_not_found,bad_prev_line_hashlimit_exceededchain_resync_in_progress— цепочка временно заблокирована полным resyncrepost_disabled— репосты временно отключены до будущей реализацииentrypoint_edit_forbidden—TEXT_ENTRYPOINTнельзя редактировать черезTEXT_EDIT_POSTstatus_confirmed_target_must_be_status_action—STATUS_CONFIRMEDдолжен ссылаться на статусный блокstatus_action_target_not_allowed— выбранныйSTATUS_ACTIONнельзя ставить на этот тип материалаbad_channel_meta_line,channel_not_found,bad_channel_meta_*,channel_meta_*_too_long— ошибкиTEXT_CHANNEL_METAinternal_error
5. Какие блоки реально можно добавлять через AddBlock
Через AddBlock можно писать поддержанные форматы, кроме явно отключённых временных фич:
-
TECH (type=0)
HEADER_COMPAT (subType=0)TECH_CREATE_CHANNEL (subType=1)
-
TEXT (type=1)
TEXT_POST (10)TEXT_EDIT_POST (11)TEXT_REPLY (20)TEXT_EDIT_REPLY (21)TEXT_RATING (30)— target-based отзыв на конкретный блокTEXT_REPOST (50)— формат зарезервирован, но новые блоки временно отклоняются сrepost_disabledTEXT_CHANNEL_META (90)— скрытый технический снимок профиля каналаTEXT_ENTRYPOINT (100)— входная страница каналаTEXT_EXERCISE (110)— line-based материал упражненияTEXT_SERVICE (120)— line-based материал услуги / процедурыTEXT_COURSE (130)— line-based материал курса
-
REACTION (type=2)
REACTION_LIKE (1)
-
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)
-
USER_PARAM (type=4)
USER_PARAM_TEXT_TEXT (1)
-
STATUS_ACTION (type=5)
STATUS_DONE_ONCE (10)STATUS_LEARNED (20)STATUS_SERVICE_PASSED (30)STATUS_CONFIRMED (100)STATUS_INTERESTED (110)STATUS_STARTED (120)STATUS_IN_STUDY (130)STATUS_ABANDONED (140)STATUS_COMPLETED (150)
6. Практические payload-форматы для каналов и вложений
AddBlock не имеет отдельных JSON-полей для вложений, аватаров или человекочитаемого имени канала. Клиент собирает бинарный блок нужного типа, а новые данные кладёт в текстовые поля тела блока по правилам blockchain-формата.
Вложения в сообщениях
Для TEXT_POST, TEXT_REPLY, TEXT_EDIT_POST и TEXT_EDIT_REPLY вложения записываются в начало текста сообщения одним или несколькими тегами SHiNE:attach v=1 или v=2.
Пример текстового содержимого body:
<SHiNE:attach;v=1;name=photo.jpg;size=248193;sha256=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;ar=BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB>
<SHiNE:attach;v=1;name=report.pdf;size=845221;sha256=cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc;ar=DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD>
Текст сообщения
Пример вложения с отдельным preview-файлом:
<SHiNE:attach;v=2;name=video.mp4;size=5820193;sha256=bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb;ar=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC;previewAr=DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD;previewSha256=eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee>
Сервер хранит это как обычный TEXT-блок. Отображение карусели, картинок, видео и карточек файлов делает клиент. Полная спецификация тега находится в docs/Blockchain/15_TEXT_Attachments.md.
Создание публичного канала с профилем
Для публичного канала начальный профиль пишется одним блоком TECH_CREATE_CHANNEL. Поле channelDescription содержит meta-текст:
<SHiNE:title;v=1;Человекочитаемое имя канала>
<SHiNE:avatar;v=1;size=248193;sha256=3f2c8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;ar=AbCdEfAbCdEfAbCdEfAbCdEfAbCdEfAbCdEfAbCdE>
Описание канала
Ограничение channelDescription — до 2048 UTF-8 байт. Новый клиент не пишет отдельный TEXT_CHANNEL_META сразу после создания канала: создание канала и начальный профиль должны попадать в один TECH_CREATE_CHANNEL.
Изменение профиля канала
Последующие изменения аватара, человекочитаемого имени или описания канала пишутся отдельным скрытым TEXT_CHANNEL_META (subType=90).
Текстовое содержимое body использует тот же формат полного снимка профиля:
<SHiNE:title;v=1;Новое имя канала>
<SHiNE:avatar;v=1;size=248193;sha256=3f2c8aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;ar=AbCdEfAbCdEfAbCdEfAbCdEfAbCdEfAbCdEfAbCdE>
Новое описание канала
Каждый TEXT_CHANNEL_META является полным состоянием профиля на момент записи. Если аватара нет, тег SHiNE:avatar не пишется. Если описания нет, после meta-тегов не добавляется хвостовой текст. Полная спецификация находится в docs/Blockchain/16_TEXT_Channel_Meta.md.
7. Хватает ли функций сейчас
Коротко: для записи событий в блокчейн — хватает, для полноценного клиентского чтения — пока не хватает.
Что есть:
- единый надёжный write-путь
AddBlock; - есть
GetFriendsListsи API поUserParam; - есть унифицированные коды ошибок и поля для ресинхронизации.
Что пока ограничивает продукт:
- нет полноценного read API для каналов/постов/тредов;
- нет API списка подписок с серверными счётчиками непрочитанного;
- нет ленты событий (новые ответы/лайки/подписки) как отдельного RPC.
8. Рекомендации по клиенту при записи блоков
- Перед отправкой держать локальный
lastNumber/lastHash. - При
bad_prev_hashилиbad_block_number:- взять
serverLastGlobalNumber/serverLastGlobalHashиз ошибки, - пересобрать следующий блок на актуальной вершине.
- взять
- Для edit-блоков всегда ссылаться на оригинальный блок, а не на предыдущий edit.
- Для связей/подписок использовать target на root (HEADER или CREATE_CHANNEL), а не на произвольный пост.
9. USER_PARAM для «личных данных»
Да, на текущем API это можно добавить без изменения серверного кода:
- в
UserParamполеparamсейчас не ограничено фиксированным справочником; - сервер хранит пары
param -> valueкак строки (при наличии корректной подписи иtime_ms); - чтение уже есть через
GetUserParamиListUserParams.
Рекомендуемый стартовый набор ключей для профиля (MVP):
namelast_nameaddress_physicaladdress_webphone
Практическая рекомендация: заранее зафиксировать единый словарь ключей в клиенте/документации, чтобы избежать дублей вида lastname vs last_name, site vs address_web и т.д.
Ограничения, которые важно учесть:
- сейчас нет серверной ACL-политики чтения параметров (в MVP их может читать любой клиент, который знает
login); - нет валидации формата значений для конкретных ключей (телефон, URL и т.д. проверяются только на стороне клиента);
- нет отдельного индекса/поиска по этим полям — только точечное чтение и listing по
login.
10. GetBlockchainBlock
Назначение
Публичное чтение одного конкретного блока из цепочки.
Нужно для:
- открытого чтения блокчейна по одному блоку;
- межсерверной синхронизации;
- восстановления/докачки отсутствующего хвоста цепочки.
JSON формат запроса
op = "GetBlockchainBlock".
{
"op": "GetBlockchainBlock",
"requestId": "req-2001",
"payload": {
"blockchainName": "alice-001",
"blockNumber": 12
}
}
Поля payload:
blockchainName— обязательно, форматlogin-NNN.blockNumber— обязательно, номер блока в цепочке,>= 0.
Успешный ответ
{
"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— внутренняя ошибка сервера.