Вики ZooBC
Приложения и разработка на ZooBC

Приложение на одной HTML-странице

Приложение ZooBC может быть одним HTML-файлом без какого-либо сервера за ним. Страница читает цепочку через HTTP API узла, собирает и подписывает транзакции в браузере ключом пользователя и отправляет подписанные байты. Пользователям не нужно запускать узел, и не нужно запрашивать ключ API. Доказательство — официальный кошелёк: он сам является одним HTML-файлом, который каждый узел отдаёт по адресам / и /wallet (веб-кошелёк).

Один HTML-файл читает через GET, подписывает в браузере и отправляет подписанные байты узлу или шлюзу.
Один HTML-файл читает через GET, подписывает в браузере и отправляет подписанные байты узлу или шлюзу.

API узла

Каждый узел предоставляет API по HTTP/1.1 в открытом виде, по умолчанию на порту 8080, с более чем сотней маршрутов под /api/v1/: блоки, аккаунты, токены, приложения, биржа и многое другое (API узла). Каждый ответ несёт эти заголовки, а на запрос OPTIONS сразу отвечается 200, поэтому страница, размещённая где угодно, может обращаться к любому узлу:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Node-Public-Key, X-Timestamp, X-Signature

У самого узла нет TLS. Публичные домены завершают TLS на шлюзе, который добавляет собственные заголовки CORS. Браузеры блокируют обращения по простому HTTP со страницы, загруженной по HTTPS, поэтому публичная страница общается со шлюзом.

Все маршруты, кроме восьми, — чтение без аутентификации, а отправка транзакции не требует никакого ключа, потому что транзакция несёт собственную подпись. Ограничения действуют для каждого адреса клиента: 300 запросов на запись в минуту, после чего — 429 с заголовком Retry-After; чтение не ограничено, если оператор не задал лимит; а 20 неудачных подписанных аутентификаций за 5 минут блокируют клиента на 300 секунд.

Подписание в браузере

Страница подписывает дайджест, который привязывает транзакцию к одной цепочке:

digest = SHA3-256("ZBC-TX" ‖ genesis hash ‖ unsigned transaction bytes)

Нативный аккаунт подписывает его ключом Ed25519. Страница читает genesis_hash и signing_version (2) из GET /api/v1/node/info, а поскольку хеш генезиса входит в дайджест, подпись, сделанная для одной цепочки, ничего не стоит в другой (подписи, привязанные к цепочке). SDK не требуется: достаточно Ed25519, SHA3-256 и хеша генезиса, а раскладка байтов описана в статье конверт транзакции. Публичные инструменты также предлагают готовые библиотеки, включая JavaScript-файл без зависимостей для тега script (инструменты командной строки). Ключ никогда не покидает страницу.

Отправка и отслеживание

POST /api/v1/transactions               → 202, with "transaction_hash"
GET  /api/v1/transactions/<hash>/status → staging, mempool, confirmed (with block_height),
                                          rejected (with the reason and stage), or 404 not_found

202 означает «принято», а не «выполнено». Узел разобрал транзакцию, провёл первые проверки и взял её в staging; производителю блока ещё предстоит её включить. Пока статус не говорит confirmed, ни один баланс не изменился. При одном блоке каждые 15 секунд опроса каждые пару секунд более чем достаточно.

Если ответ потерян, отправьте те же самые подписанные байты повторно. Хеш транзакции — это SHA3-256 от неподписанных байтов и подписи, поэтому узел, у которого транзакция уже есть, отвечает 202 с "duplicate": true, добавляя "state": "confirmed", когда она уже в блоке, и транзакция исполняется только один раз. Повторное подписание с новой меткой времени создаёт другую транзакцию — именно так и случаются двойные платежи.

По умолчанию блок может включить транзакцию в течение 3 600 секунд после её метки времени; транзакция версии 3 указывает собственное окно, до 86 400 секунд; а метка времени более чем на 3 600 секунд в будущем отклоняется (срок действия и метки времени). Прежде чем отдавать товар в обмен на платёж, дождитесь подтверждения, а затем флага finalized, который транзакция получает, когда её блок охватывает соподписанный spine-блок (финальность и откат). API также сообщает finalized_height, block_seed_proof каждого блока, expiry транзакции и spine-блоки по адресу /spine/blocks/:height.

После 202: staging, mempool, confirmed, finalized; потерянный ответ означает повторную отправку тех же байтов.
После 202: staging, mempool, confirmed, finalized; потерянный ответ означает повторную отправку тех же байтов.

Маршруты для приложений

Маршруты приложений отдаёт сам узел, а не архивный сервис: /apps (фильтры status, limit и category — solo, pvp или party), /apps/open, /apps/pool, /apps/stats, /apps/:id (с урегулированием, если оно есть) и /accounts/:a/apps.

Контрольный список

  • Читайте через шлюз по HTTPS; пишите, отправляя подписанные байты, а ключ держите на странице.
  • Отслеживайте по хешу и повторяйте попытку, отправляя те же байты, а не подписывая заново.
  • Соблюдайте 429 и Retry-After.
  • Показывайте текст из цепочки через textContent (данные цепочки — недоверенный ввод).
  • Считайте «confirmed» получением, а «finalized» — окончательным расчётом.

См. также