Сайт в разработке

Сайт в разработке: мы создаём его к запуску MainNet. Детали будут меняться.

Этот сайт ещё строится

ZooBC развивается открыто. Всё, что вы здесь видите, актуально и честно, но ещё не завершено: воспринимайте это как картину того, где мы находимся сегодня, а не как окончательное заявление.

До запуска MainNet многое изменится: формулировки, структура, изображения и цифры. Некоторые страницы пока содержат только заглушки.

Экосистема ZooBC появляется поэтапно. Кошелёк, обозреватель и каналы сообщества запускаются по мере приближения MainNet, и этот сайт растёт вместе с ними.

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

ZOOBC / РАЗРАБОТЧИКАМ

Разработчикам

Всё, что нужно программе для чтения и записи в этот блокчейн. Шлюз служит входной дверью по HTTPS: он терминирует TLS, ограничивает частоту запросов и проксирует API блокчейна к узлам, поэтому браузер может работать с ZooBC без собственного узла.

Для какого блокчейна вы разрабатываете?

Примеры ниже работают с публичным шлюзом TestNet. Адреса, балансы и хеши транзакций не переносятся между сетями, поэтому, прежде чем действовать, убедитесь, в какой сети вы находитесь.

MainNetzoobc.net
Статус блокчейнаСправочник API

MainNet откроется 14 февраля 2027 года

Один базовый URL

Всё, что касается блокчейна, находится по пути /api/v1/ на этом шлюзе, в том же источнике, что и эта страница: не нужно согласовывать CORS, получать ключ или выбирать тариф.

zoobc.network служит публичным шлюзом ZooBC TestNet, а не MainNet. Примеры ниже работают с тестовой сетью.

curl https://this-gateway/api/v1/blockchain/status

Чтение и запись идут в разные места, и это важнее, чем кажется:

GETпроксируется на архивный узел, который хранит всю историю: блоки, транзакции и аккаунты на любой высоте.
POSTпроксируется напрямую на полный узел, никогда на архивный. Для отправки транзакции нужен мемпул, а архивный узел служит сервисом истории только для чтения; раньше он отвечал на POST кодом 404, из-за чего отправка в сеть через шлюз когда-то не работала.

Маршруты, которые понадобятся в первую очередь

/api/v1/blockchain/statusвысота, подтверждённая высота, последний блок
/api/v1/blocks/latestпоследний блок целиком
/api/v1/accounts/<address>баланс и доступный для расходования баланс. Принимает адрес ZBC_… или необработанный 36-байтный hex
/api/v1/accounts/<address>/tokensвсе токены на этом аккаунте
/api/v1/transactions?limit=25последние подписанные транзакции
/api/v1/movements/latestизменения баланса, которые вносит сам блокчейн: награды, вестинг, возвраты
/api/v1/exchange/marketsоткрытые рынки; …/orderbook?market=<id> для глубины рынка, …/offers для пакетов обмена
/api/v1/tokensвсе токены с предложением и числом знаков после запятой
/api/v1/release/listопубликованные релизы и их хеши, с которыми сверяется /verify
/api/v1/registry/nodesреестр узлов; также /gateways, /relays, /archivals

Некоторые маршруты есть только на архивном узле, а некоторые только на полном. Этот шлюз сначала обращается к архивному узлу и повторяет запрос к API узла, если получил 404 с пустым телом, так что вам редко нужно думать, где какой. Но 404 с телом означает настоящий ответ, а не неверный маршрут.

Отправка транзакции

Соберите и подпишите её с помощью zbc-cli: он принимает параметры в формате JSON через stdin, поэтому закрытый ключ никогда не попадает в ps или историю командной оболочки:

echo '{"sender_privkey":"…","recipient":"ZBC_…","amount":100000000}' \
  | zbc-cli send --json-input --api https://this-gateway

1 ZBC = 108 атомарных единиц. Все суммы в API указаны в атомарных единицах, в формате передачи нигде нет дробных чисел.

Что строится на этих рельсах

Те же типы транзакций, которые вы вызываете здесь, лежат в основе текущих разработок:

ИИ-агентызапланированные переводы, эскроу и мультисиг в роли потолка расходов, который автономный агент не может поднять сам. Работает уже сегодня, с эталонным примером на Python без зависимостей для публичного TestNet.
Децентрализованный ИИнезависимо управляемые специализированные модели, которые собираются на стороне пользователя и рассчитываются открыто, чтобы ни одна компания не стояла между человеком и интеллектом, которым он пользуется. Направление после MainNet.
ProxCellлокальное оптимистичное подтверждение для очных платежей с географическими уровнями расчётов над ним. Рабочий документ.

Полный справочник

Все маршруты, их параметры и формат ответа:

Откройте справочник API →  ·  альтернативный вид

Оба адреса отдают один и тот же документ.

Загрузки

Файлы, которые оператор опубликовал на этом шлюзе, доступны по адресу /dl/<name>. Имя состоит из одного сегмента [A-Za-z0-9._-], и всё отдаётся как вложение и никогда не отображается в браузере: этот источник обслуживает и кошелёк, а страница, отображённая здесь, унаследовала бы источник кошелька.

Пакеты релизов находятся в /releases/<version>/<arch>/, а /releases/latest указывает на текущий. Установщик узла находится по адресу /install.

Работает ли этот шлюз? проверяем…

/gateway/status сообщает, что шлюз знает о себе и о доступных ему узлах: режим, домен, сколько узлов в его списке разрешённых и какие из них отвечают.

/gateway/system-stats сообщает данные о машине: CPU, память, диск, время работы. Именно отсюда берутся показатели на странице шлюза.

curl https://this-gateway/gateway/status
curl https://this-gateway/gateway/system-stats

Ни одному из них не нужен ключ. Ни один не раскрывает ничего о блокчейне: для этого используйте /api/v1/… выше.

Обращение к конкретному узлу

Маршруты блокчейна отвечают от того архивного узла, который выберет шлюз. Чтобы запросить конкретный узел, укажите его IP в имени хоста, заменив точки дефисами; необязательный суффикс -p<port> выбирает порт API:

curl https://ip-192-168-1-100.this-gateway/api/v1/node/info
curl https://ip-192-168-1-100-p8080.this-gateway/api/v1/node/info

Отвечают только узлы из списка разрешённых этого шлюза. Все остальные запросы отклоняются, а не проксируются, поэтому имя хоста нельзя использовать для доступа к произвольным хостам через шлюз.

Ретранслятор

Голос, видео и демонстрация экрана передаются напрямую между участниками; когда брандмауэр блокирует прямое соединение, трафик за плату передаёт ретранслятор. /relay/info сообщает его адрес, тариф и хеш генезиса блокчейна, в котором он проводит расчёты; кошелёк сверяет этот хеш со своим узлом перед оплатой, потому что ретранслятор из другого блокчейна не сможет зачесть полученную оплату.

curl https://this-gateway/relay/info

Панель администратора

Собственные элементы управления шлюза находятся по адресу zoobc.network/admin. Доступ к ним защищён значением admin_api_key из /etc/zoobc/gateway.json, которое установщик генерирует для каждой машины отдельно; ключа по умолчанию нет.

Список разрешённых узлов

Шлюз проксирует запросы только к тем узлам, о которых ему сообщили. Именно этот список не даёт имени хоста в форме ip-… превратить шлюз в открытый прокси.

POST /gateway/allowlist/addдопустить узел, {"ip":"…","port":8080}
POST /gateway/allowlist/removeисключить узел
POST /gateway/allowlist/refreshперечитать реестр и заново проверить работоспособность
GET  /gateway/allowlistчто допущено сейчас

Все три операции записи требуют ключа администратора. В публичном режиме шлюз также находит узлы в собственном реестре блокчейна; в приватном режиме он использует только static_nodes.

Инструменты для релизов

Инструмент хеширования файлов вычисляет SHA-256 в браузере для всего, что вы хотите проверить вручную, а /verify сверяет загруженный пакет с хешами, опубликованными в блокчейне.

Два документа

Всё, что нужно команде аппаратного кошелька, описано здесь, а полностью в двух PDF ниже. Оба документа проверены на узле ZooBC v0.4.0 (zoobc-main на коммите 7ad161a4) и на векторах транзакций, которые принял работающий узел v0.4.0.

Версия 2.1 отличается от черновика 1 в одном существенном отношении: подписи транзакций теперь привязаны к блокчейну. Два изменения ломают совместимость для всех, кто разрабатывал по черновику 1: дайджест подписания и тело ApprovalEscrow. Старую конструкцию v0.3.2 больше ничто не реализует, поэтому разрабатывайте только по текущему документу.

Сводка на одной странице

Схема подписиEd25519 (RFC 8032, чистый вариант, без предварительного хеширования, без контекстной строки). Подпись 64 байта, открытый ключ 32 байта.
Что подписываетсяEd25519.sign(sk, SHA3-256("ZBC-TX" ‖ genesis_hash(32) ‖ unsigned_bytes)). 32-байтный дайджест и есть сообщение Ed25519.
Привязка к блокчейнуДайджест включает хеш генезис-блока блокчейна, поэтому подпись, созданную для одного блокчейна, нельзя воспроизвести в другом. Формат передачи не изменился; хеш генезиса не является полем транзакции.
Тег подписания"ZBC-TX", шесть байтов ASCII 5a 42 43 2d 54 58. Без завершающего символа и без префикса длины.
Хеш-функцияSHA3-256 (FIPS 202). Не Keccak-256 и не SHA-256.
Вывод ключейМнемоника BIP-39, затем seed (PBKDF2-HMAC-SHA512, 2048 раундов), затем SLIP-0010 Ed25519, путь m/44'/883'/account'. Три усиленных (hardened) уровня, без уровней change и index.
Тип монеты SLIP-44883 (зарегистрирован: 883 | ZBC | ZooBC).
Аккаунт (в формате передачи)36 байт: u32le(0) ‖ pubkey(32). Именно hex-форму принимает JSON API.
Адрес (для отображения)ZBC_ плюс 56 символов base32 в 7 группах по 8. 3-байтная контрольная сумма = SHA3-256(pubkey ‖ "ZBC")[0..3].
Байт версии0x01, без изменений. Его не увеличивали ради привязки к блокчейну, и он не служит различителем версий.
Базовая единица1 ZBC = 100 000 000 атомарных единиц (8 знаков после запятой). Суммы и комиссии задаются в атомарных единицах как u64 little-endian.
Порядок байтовВсё в формате передачи записывается в порядке little-endian, кроме дочерних индексов SLIP-0010 (big-endian, согласно стандарту).
Хеш транзакцииtx_hash = SHA3-256(unsigned_bytes ‖ signature); tx_id = int64le(tx_hash[0..8]). Хеш генезиса входит только в дайджест подписания.
Защита от повторовПривязка к блокчейну, плюс уникальность хеша, плюс окно включения 3600 с. Nonce нет.
Минимальная комиссияБаза 0,025 ZBC (2 500 000 атомарных единиц), умноженная на сетевой коэффициент комиссий, плюс плата за размер. Узнайте у узла: GET /api/v1/blockchain/estimate-fee.
Типы уровня 11 SendZBC, 11 TransferToken, 4 ApprovalEscrow.
Определение блокчейнаGET /api/v1/node/info возвращает genesis_hash, signing_version: 2 и signing_tag: "ZBC-TX". Считывайте их с того эндпоинта, на который будете отправлять транзакцию.

Сначала прочитайте эти четыре пункта

Четыре момента, которые влияют на реализацию сильнее всего остального.

1Nonce нет, и нет эндпоинта для неподписанных транзакций. ZooBC основан на аккаунтах, но здесь из этого не следует ни то, ни другое. Хост сам сериализует транзакцию; структура ниже полная и закреплена тестовыми векторами.
2Подпись покрывает префикс с тегом, "ZBC-TX" плюс хеш генезиса блокчейна, а не только байты транзакции. Ошибётесь здесь, и каждая подпись будет отвергаться с общей ошибкой.
3Эндпоинт отправки не принимает подписанный blob транзакции. Он принимает байты тела транзакции плюс конверт в виде именованных ключей JSON и сам собирает конверт заново.
4История возвращается от новых записей к старым и ограничена limit. Полную картину дают два эндпоинта вместе, а сумма зависит от типа и не является полем конверта.

Вывод ключей и адреса

Эталон: zoobc-signer/extension/lib/zoobc-crypto.js, побайтно идентичен эталонному кошельку; эквиваленты на стороне узла находятся в src/crypto/slip10.cpp.

seed = PBKDF2-HMAC-SHA512(NFKD(mnemonic), "mnemonic" + passphrase, 2048, 64)
I    = HMAC-SHA512("ed25519 seed", seed);  k = I[0..32], c = I[32..64]

for index in [44, 883, account]:                       # all hardened
    data = 0x00 || k || u32be(index + 0x80000000)
    I    = HMAC-SHA512(c, data);  k = I[0..32], c = I[32..64]

pubkey = Ed25519.publicKeyFromSeed(k)                  # k is the 32-byte seed

Аккаунту 0 соответствует m/44'/883'/0', аккаунту 1 соответствует m/44'/883'/1' и так далее. Привязка к блокчейну не влияет на вывод ключей: один набор ключей работает во всех блокчейнах ZooBC, различается только подпись.

Адрес для отображения строится из открытого ключа, а не из 36-байтного аккаунта:

buf[35] = pubkey(32) || "ZBC"
h       = SHA3-256(buf);  buf[32..35] = h[0..3]        # 3-byte checksum
s       = base32(buf)                                   # RFC 4648 A-Z2-7, no padding, 56 chars
address = "ZBC_" + s[0..8] + "_" + s[8..16] + ...       # 7 groups, canonical length 66

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

Формат передачи неподписанной транзакции

Источник истины: src/util/transaction_util.cpp:144-270. Формат передачи в v0.4.0 не изменился. Привязка к блокчейну не затрагивает ни одну длину или смещение ниже.

#размерполепримечания
14transaction_typeu32le, см. каталог типов ниже
21versionвсегда 0x01; узел отклоняет любое другое значение
38timestampu64le, секунды Unix, больше 0
436sender00000000 ‖ pubkey(32) для подписанта ZBC
54 или 4+nrecipient36 байт для ZBC; u32le(type) ‖ payload для остальных; только 02 00 00 00, когда получателя нет
68feeu64le, атомарные единицы
74body_lengthu32le
8nbodyструктура зависит от типа
94 или переменныйescrowбез эскроу: 4 байта 02 00 00 00. С эскроу: approver(36) ‖ commission u64le ‖ timeout u64le ‖ instr_len u32le ‖ instruction ‖ multi_party(1)
104message_lengthu32le
11mmessageобычный текст, не более 256 байт для обычных транзакций

Завершающий байт multi_party в блоке эскроу обязателен. Если его опустить или записать маркер отсутствия эскроу с другим значением, узел выдаст только «Invalid transaction signature» без какой-либо дополнительной диагностики.

Разобранный пример, проверенный в блокчейне

SendZBC на 1 ZBC самому себе, комиссия 0,05 ZBC, timestamp 1700000000, без эскроу, без сообщения. Это вектор 0 из семи в спецификации.

01000000                 type = 1 (SendZBC)
01                       version
00f1536500000000         timestamp 1700000000
00000000 5e8eb28d...2152 sender    (type 0 + pubkey)
00000000 5e8eb28d...2152 recipient (type 0 + pubkey)
404b4c0000000000         fee 5 000 000
08000000                 body length 8
00e1f50500000000         body: amount 100 000 000
02000000                 no-escrow marker
00000000                 message length 0

Это 113 байт. Прообраз для подписания добавляет в начало ещё 38:

5a42432d5458              tag "ZBC-TX"          6 bytes
090ab3c7...a61878         genesis hash         32 bytes
0100000001...00000000     the 113 bytes above 113 bytes
                                              ---------
                                              151 bytes

digest = SHA3-256(preimage) = f37e5340...4644fc, подпись 9a3b044d...03bb07, tx_hash = f00a6692...ce78b0. Работающий узел v0.4.0 вернул HTTP 202 и именно этот хеш.

Процедура подписания на стороне устройства

input : account_index, genesis_hash(32), unsigned_bytes

1. k      = SLIP10(m/44'/883'/account_index')
2. pub    = Ed25519.pub(k);  self = 00000000 || pub
3. parse unsigned_bytes; every length must land exactly at the end
4. require version == 1 and sender == self
5. decode body per type; render the approval screen; wait for the user
6. digest = SHA3-256("ZBC-TX" || genesis_hash || unsigned_bytes)
7. sig    = Ed25519.sign(k, digest)          # the digest IS the message
8. return sig(64), and tx_hash = SHA3-256(unsigned_bytes || sig) for the host to cross-check

Используйте потоковый SHA3, чтобы на шаге 6 не держать всю транзакцию в RAM. Подайте 6 байтов тега, затем 32 байта генезиса, затем транзакцию.

Тег остаётся самым вероятным местом ошибки реализации во всей спецификации. "ZBC-TX" состоит из шести байтов ASCII, 5a 42 43 2d 54 58, без завершающего NUL и без префикса длины. Прошивка, которая добавляет к строке завершающий символ или записывает семь байтов, выдаёт подпись, которая выглядит корректной, но отвергается в блокчейне с общей ошибкой и без диагностики. Прежде всего проверьте это на векторе 0.

Что привязка к блокчейну делает и чего не делает

32 байта генезиса передаёт хост; устройство их не ищет. Поэтому подписание не зависит от блокчейна, и один путь кода обслуживает TestNet, devnet, MainNet и любой приватный или параллельный блокчейн ZooBC.

ДелаетПредотвращает воспроизведение подписи в других блокчейнах. Подпись, созданная для блокчейна A, не пройдёт проверку в блокчейне B. Именно для этого привязка и создана, и с этой задачей она справляется полностью.
Не делаетНе мешает скомпрометированному хосту указать не тот блокчейн. Устройство подписывает для любого хеша, который ему передали.

Поэтому таблица известных хешей генезиса в прошивке по-прежнему нужна, но только для одной цели: показать на экране надёжное имя сети, а если хеша нет в таблице, показать «UNKNOWN CHAIN» и первые 8 символов hex.

Не отклоняйте нераспознанные блокчейны по умолчанию. Хеш devnet меняется при каждом перезапуске, а приватные и параллельные блокчейны для ZooBC обычное дело, поэтому жёсткий отказ делает устройство бесполезным для интеграции и законных развёртываний. Если производитель хочет такую блокировку, сделайте её явной пользовательской настройкой, выключенной по умолчанию.

Типы транзакций: что должна поддерживать стандартная прошивка

Id типа = group + 256 × subtype. Всего существует пятьдесят шесть типов. Эти три покрывают оплату в ZBC, оплату любым токеном и завершение эскроу, а их тела небольшие и фиксированные.

idназваниеbodyrecipient
1SendZBCamount u64le (8)обязателен, любой тип
11TransferTokentoken_id i64le (8) ‖ amount i64le (8) ‖ необязательный fee_in_token u8обязателен
4ApprovalEscrowapproval u32le ‖ escrowed_transaction_hash (32) = 36 байтпустой маркер; отправитель является утверждающим

token_id представляет собой знаковое 64-битное значение, полученное из хеша, поэтому может быть отрицательным. Монеты генезиса имеют id от 1 до 14; сам ZBC имеет id 0 и никогда не встречается в TransferToken. У каждого токена свой decimals (от 0 до 8), который устройство не может проверить.

ApprovalEscrow изменился в v0.4.0. Раньше тело состояло из approval и 8-байтного id (12 байт); теперь это approval и 32-байтный хеш транзакции в эскроу (36 байт), и парсер отклоняет любую другую длину. Причина напрямую связана с аппаратными кошельками: подписант, видевший только 8-байтный id, не мог проверить, что он высвобождает, а 8-байтный id можно подобрать перебором 2^64 вариантов для поддельного эскроу, показанного устройству. С полным хешем устройство может проверить то, что одобряет.

Что проверяет устройство

СтруктураРазберите 11 полей. Каждый префикс длины должен быть согласован, а буфер должен заканчиваться ровно после сообщения. В любом другом случае откажите.
ВерсияДолжна быть равна 1.
ОтправительДолжен совпадать с аккаунтом, который устройство само выводит для запрошенного пути. Устройство никогда не доверяет переданным ему байтам отправителя: оно заново выводит аккаунт и сравнивает все 36 байт. В этом и состоит вся гарантия ответа на вопрос «откуда это».
ТипДолжен входить в поддерживаемый набор, либо должен быть включён экспертный режим.
Длина тела8 для SendZBC, 16 или 17 для TransferToken, 36 для ApprovalEscrow. При лишних байтах откажите. Узел принял бы более длинное тело SendZBC и проигнорировал бы хвост, и именно так мог бы проскочить скрытый груз.
Таймаут эскроуДолжен быть больше метки времени транзакции.
Хеш генезисаДолжен занимать ровно 32 байта. Устройство не сверяет его со списком, но неверная длина означает некорректный запрос.
НикогдаНе принимайте для подписи заранее вычисленный дайджест. Устройство само хеширует байты, включая тег и генезис.
Ограничения размераРекомендуется: неподписанная транзакция не более 2 КБ, сообщение 256 Б, инструкция эскроу 512 Б; всё, что больше, отклоняйте с TOO_LARGE. Транзакции уровня 1 занимают меньше 300 байт, но ApprovalEscrow приходит со второй транзакцией, приложенной для проверки, поэтому ограничение должно покрывать оба буфера. Более крупным данным место в программных кошельках.

Что пользователь подтверждает на экране

По экранам, в таком порядке, по образцу эталонного подписанта, проверенного в блокчейне.

СетьИмя из таблицы прошивки для переданного хеша генезиса; «UNKNOWN CHAIN» и первые 8 символов hex, если хеша нет в таблице.
Тип«Send ZBC», «Send token», «Approve escrow».
Суммаamount / 1e8 с символом ZBC.
ПолучательПолный адрес, без сокращений. Адрес, сокращённый посередине, легко подделать ключом-двойником, подобранным vanity-генератором.
Комиссияатомарные единицы / 1e8 ZBC.
Всего списываетсясумма плюс комиссия одним числом, только для сумм в ZBC. Сумма в токене и комиссия в ZBC выражены в разных единицах, складывать их нельзя.
СообщениеПоказывается, если оно печатаемое, иначе «N bytes (binary)» и первые 8 символов hex его SHA3-256.
ЭскроуУтверждающий полностью, комиссия, таймаут в виде даты и времени, текст инструкции.
Токен (тип 11)Id токена (десятичное число со знаком) и сумма в атомарных единицах. Если хост передаёт decimals или symbol как подсказки для отображения, покажите масштабированную сумму с пометкой «as reported by the app», потому что устройство не может их проверить.
Одобрение эскроу«APPROVE» или «REJECT» крупным шрифтом, затем проверенные детали эскроу, затем комиссия.

Метки времени показывать не обязательно. Для пользователя они не несут смысла, а окно контролирует узел.

Протокол между хостом и устройством

Транспортом служит собственный уровень USB/HID производителя с уже существующим разбиением на фрагменты. Важно содержание.

командазапросответ
GET_APP_VERSIONнетверсия прошивки, набор поддерживаемых типов, максимальный размер транзакции
GET_PUBLIC_KEYaccount_index, confirm_on_devicepubkey(32), address (строка из 66 символов). При подтверждении устройство показывает полный адрес, чтобы пользователь сравнил его с адресом на сайте.
SIGN_TXaccount_index, genesis_hash(32), unsigned_tx_bytessignature(64), tx_hash(32), или типизированный отказ: SENDER_MISMATCH, UNSUPPORTED_TYPE, MALFORMED, USER_REJECTED, TOO_LARGE, ESCROW_HASH_MISMATCH
SIGN_MESSAGEaccount_index, message_bytessignature(64), необработанная подпись Ed25519, см. правило ниже

genesis_hash несёт реальную нагрузку, а не служит подсказкой для отображения. В черновике 1 параметр сети мог быть перечислением, потому что он лишь выбирал метку. Теперь он входит в дайджест, поэтому должен содержать реальные 32 байта, а неверное значение даёт подпись, которую не примет ни один блокчейн.

Правило необработанной подписи. Команда необработанного подписания никогда не должна подписывать входные данные длиной ровно 32 байта и должна подписывать только то, что может показать в виде текста. 32-байтный необработанный ввод может оказаться дайджестом SHA3 транзакции, которую пользователь никогда не видел, и тогда команда для сообщений превращается в слепое подписание транзакций. Отказ при длине 32 закрывает эту дыру без изменений в блокчейне.

Полная последовательность

Website (page)         Companion / host           device                    Gateway / node
   |-- connect ---------->|                            |                          |
   |                      |-- GET_PUBLIC_KEY(0) ----->|  (shows ZBC_... address) |
   |<-- address ----------|<-- pubkey, address -------|                          |
   |-- GET /api/v1/node/info  (genesis_hash, signing_version, height) ---------->|
   |-- GET /api/v1/accounts/<addr>  (balances) --------------------------------->|
   |-- GET /api/v1/accounts/<addr>/tokens, /api/v1/tokens/<id> (decimals) ------>|
   |   user fills the form                             |                          |
   |-- GET /api/v1/blockchain/estimate-fee?type=1&body_length=8 ---------------->|
   |<-- {minimum_fee, timestamp} ------------------------------------------------|
   |-- fields {type, recipient, body, fee, message} -->|                          |
   |                      |  serialize the 11 fields   |                          |
   |                      |-- SIGN_TX(0, genesis, -->|  parse, verify, display,  |
   |                      |            bytes)         |  user approves            |
   |                      |<-- signature, tx_hash ---|                          |
   |<-- signature --------|                            |                          |
   |-- POST /api/v1/transactions {JSON} ---------------------------------------->|
   |<-- 202 {status:"success", transaction_hash} --------------------------------|
   |-- GET /api/v1/transactions/<hash>/status  (staging, mempool, confirmed) --->|

Хост сравнивает transaction_hash узла с tx_hash устройства. Они должны совпадать, иначе хост изменил байты после подписания.

Считывайте genesis_hash и signing_version с того эндпоинта, на который будете отправлять транзакцию, потому что именно этот блокчейн оценивает подпись.

Идентификация блокчейна

Единственный сетевой идентификатор в ZooBC: хеш генезис-блока. Узлы сравнивают его перед установлением связи; байта network-id нет. GET /api/v1/node/info возвращает его вместе с двумя полями, появившимися в v0.4.0:

"genesis_hash": "090ab3c7...", "signing_version": 2, "signing_tag": "ZBC-TX"

signing_version отсутствует: значит, узел старше v0.4.0 и применяет устаревший непривязанный дайджест. Хост, поддерживающий только актуальные блокчейны, может считать отсутствие поля сигналом «здесь не подписывать». Узел выводит hex в нижнем регистре, а архивный API в верхнем, поэтому декодируйте без учёта регистра и удаляйте начальный 0x, если он есть. Байты в любом случае одни и те же.

Откуда его считывать. Шлюз отвечает на /api/v1/node/info из архивного сервиса, а не из узла. Архивный сервис передаёт два поля подписания от узла, который он зеркалирует, и выдаёт их ровно тогда, когда их сообщает узел, начиная со сборки 7ad161a4. Считывайте signing_version с эндпоинта узла или со шлюза на этой сборке или новее, а отсутствие поля считайте сигналом «спросить узел», а не ответом.

Отправка в сеть

POST /api/v1/transactions. В v0.4.0 без изменений, новых полей нет. transaction_body_bytes содержит только тело: узел собирает конверт из именованных полей, добавляет в начало собственный хеш генезиса и тег и пересчитывает дайджест, поэтому каждое поле должно совпадать с подписанным.

{
  "version": 1,
  "timestamp": 1700000000,
  "transaction_type": 1,
  "fee": 5000000,
  "sender_account_address":    "00000000 5e8eb28d ... 2152",
  "recipient_account_address": "00000000 5e8eb28d ... 2152",
  "transaction_body_bytes": "00e1f50500000000",
  "signature": "9a3b044d ... 03bb07",
  "message_hex": "...optional...",
  "escrow": { "approver_address": "...", "commission": 0, "timeout": 1700003600 }
}

Успех: HTTP 202 с {"status":"success","duplicate":false,"transaction_hash":"..."}. duplicate:true означает, что узел уже содержит эту транзакцию, поэтому не отправляйте её повторно. Ответ 200 тоже считайте успехом. Ошибка: 400 с телом ошибки или 503 под нагрузкой.

Полезный интеграционный тест: промежуточный пул сначала проверяет подпись, поэтому транзакция, дошедшая до более поздней ошибки вроде «Sender account does not exist», уже прошла проверку подписи. Свой путь подписания можно проверить на ключе без средств.

Когда подпись отклонена

Сегодня несовпадение блокчейна нельзя отличить от любой другой ошибки подписи. Устаревший дайджест и дайджест для чужого блокчейна возвращают одинаковое тело:

{"success":false,"error":"Transaction validation failed: ValidationError: Invalid transaction signature","code":400}

Поэтому проверяйте причины в таком порядке, начиная с наиболее вероятной:

1Тег пропущен, завершён NUL-байтом или записан семью байтами.
2Вместо SHA3-256 использован Keccak-256.
3Хеш генезиса не совпадает с сетью, в которую отправляется транзакция.
4Пропущен завершающий байт multi_party эскроу.
5Тело ApprovalEscrow по-прежнему занимает 12 байт, а не 36.

Конечные точки для интеграции

сетьшлюз (HTTPS)подписаниепримечание
testnethttps://this-gatewayпереход на v0.4.0целевая сеть для интеграции после переключения
devnethttps://socialconnect.networkv0.4.011 узлов, кран: /faucet; хеш генезиса меняется при каждом перезапуске
mainnetещё не запущенаv0.4.0 или новеепри запуске считайте её хеш генезиса из сети

Чтение направляйте сначала на архивный узел, затем на шлюз, затем на узел. Транзакции отправляйте на шлюз или узел, но никогда не на архивную конечную точку.

Считывайте хеш генезиса во время выполнения, не прописывайте его в коде. У devnet он меняется при каждом перезапуске. Таблица хешей в прошивке нужна лишь для того, чтобы показать название сети на экране, поэтому устаревшая запись приводит к надписи «UNKNOWN CHAIN», а не к сбою подписания. Когда хеш devnet всё же меняется, все подписи, сделанные до этого, становятся недействительными: так и задумано.