ZOOBC / РАЗРАБОТЧИКАМ
Разработчикам
Всё, что нужно программе для чтения и записи в этот блокчейн. Шлюз служит входной дверью по HTTPS: он терминирует TLS, ограничивает частоту запросов и проксирует API блокчейна к узлам, поэтому браузер может работать с ZooBC без собственного узла.
Для какого блокчейна вы разрабатываете?
Примеры ниже работают с публичным шлюзом TestNet. Адреса, балансы и хеши транзакций не переносятся между сетями, поэтому, прежде чем действовать, убедитесь, в какой сети вы находитесь.
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-44 | 883 (зарегистрирован: 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. |
| Типы уровня 1 | 1 SendZBC, 11 TransferToken, 4 ApprovalEscrow. |
| Определение блокчейна | GET /api/v1/node/info возвращает genesis_hash, signing_version: 2 и signing_tag: "ZBC-TX". Считывайте их с того эндпоинта, на который будете отправлять транзакцию. |
Сначала прочитайте эти четыре пункта
Четыре момента, которые влияют на реализацию сильнее всего остального.
| 1 | Nonce нет, и нет эндпоинта для неподписанных транзакций. 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 не изменился. Привязка к блокчейну не затрагивает ни одну длину или смещение ниже.
| # | размер | поле | примечания |
|---|---|---|---|
| 1 | 4 | transaction_type | u32le, см. каталог типов ниже |
| 2 | 1 | version | всегда 0x01; узел отклоняет любое другое значение |
| 3 | 8 | timestamp | u64le, секунды Unix, больше 0 |
| 4 | 36 | sender | 00000000 ‖ pubkey(32) для подписанта ZBC |
| 5 | 4 или 4+n | recipient | 36 байт для ZBC; u32le(type) ‖ payload для остальных; только 02 00 00 00, когда получателя нет |
| 6 | 8 | fee | u64le, атомарные единицы |
| 7 | 4 | body_length | u32le |
| 8 | n | body | структура зависит от типа |
| 9 | 4 или переменный | escrow | без эскроу: 4 байта 02 00 00 00. С эскроу: approver(36) ‖ commission u64le ‖ timeout u64le ‖ instr_len u32le ‖ instruction ‖ multi_party(1) |
| 10 | 4 | message_length | u32le |
| 11 | m | message | обычный текст, не более 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 | название | body | recipient |
|---|---|---|---|
| 1 | SendZBC | amount u64le (8) | обязателен, любой тип |
| 11 | TransferToken | token_id i64le (8) ‖ amount i64le (8) ‖ необязательный fee_in_token u8 | обязателен |
| 4 | ApprovalEscrow | approval 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_KEY | account_index, confirm_on_device | pubkey(32), address (строка из 66 символов). При подтверждении устройство показывает полный адрес, чтобы пользователь сравнил его с адресом на сайте. |
| SIGN_TX | account_index, genesis_hash(32), unsigned_tx_bytes | signature(64), tx_hash(32), или типизированный отказ: SENDER_MISMATCH, UNSUPPORTED_TYPE, MALFORMED, USER_REJECTED, TOO_LARGE, ESCROW_HASH_MISMATCH |
| SIGN_MESSAGE | account_index, message_bytes | signature(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) | подписание | примечание |
|---|---|---|---|
| testnet | https://this-gateway | переход на v0.4.0 | целевая сеть для интеграции после переключения |
| devnet | https://socialconnect.network | v0.4.0 | 11 узлов, кран: /faucet; хеш генезиса меняется при каждом перезапуске |
| mainnet | ещё не запущена | v0.4.0 или новее | при запуске считайте её хеш генезиса из сети |
Чтение направляйте сначала на архивный узел, затем на шлюз, затем на узел. Транзакции отправляйте на шлюз или узел, но никогда не на архивную конечную точку.
Считывайте хеш генезиса во время выполнения, не прописывайте его в коде. У devnet он меняется при каждом перезапуске. Таблица хешей в прошивке нужна лишь для того, чтобы показать название сети на экране, поэтому устаревшая запись приводит к надписи «UNKNOWN CHAIN», а не к сбою подписания. Когда хеш devnet всё же меняется, все подписи, сделанные до этого, становятся недействительными: так и задумано.