Все записи

9 мин чтения

Создавайте на API CardanoWall

Как построить собственный продукт на CardanoWall: расчёт стоимости, загрузка, публикация, чтение, проверка и мониторинг записей Label 309 через тот же API шлюза, на котором работает сам CardanoWall.

Вы можете встроить функцию Proof of Existence в собственный продукт, не отправляя пользователей на сайт CardanoWall. CardanoWall — это пользовательский интерфейс поверх размещённого шлюза, и этот шлюз предоставляет обычный HTTP API: рассчитать стоимость подтверждения, загрузить содержимое, когда нужно хранилище, опубликовать запись Label 309, отслеживать её статус, читать публичные записи, проверять балансы и получать вебхуки. Подтверждение, которое попадает в Cardano, — это стандартные метаданные Label 309, а не закрытый формат, доступный только CardanoWall, поэтому позже любой и с помощью любого инструмента сможет его проверить.

Это и есть главная идея. Взаимодействие с продуктом может быть полностью вашим. А подтверждение остаётся проверяемым независимо. Это тот же API, на котором работают собственное веб-приложение и воркер CardanoWall, — здесь нет ни закрытой двери, ни более быстрого внутреннего пути.

Что на нём можно построить?

API подходит везде, где Proof of Existence уместен в рабочем процессе:

  • SaaS-продукт, проставляющий метки времени для документов клиентов;
  • конвейер CI/CD, закрепляющий манифесты релизов (см. подтверждения в CI/CD);
  • AI-платформа, публикующая записи о происхождении в больших объёмах;
  • комплаенс-архив, фиксирующий ежедневные пакеты свидетельств;
  • юридический инструмент, запечатывающий доказательства для конкретных получателей;
  • система внутреннего аудита, подписывающая срезы состояния контролей;
  • публичный эксплорер или страница профиля, перечисляющая записи публикатора.

API — это не просто удобная обёртка вокруг сайта. Это поверхность для автоматизации, к которой вы обращаетесь, когда в процессе нет человека.

Как выглядит основной процесс публикации?

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

  • POST /api/v1/poe/quote — зафиксировать цену на короткое время.
  • POST /api/v1/poe/uploads — сохранить содержимое и получить контентно-адресуемые URI.
  • POST /api/v1/poe/uploads/sessions и связанные маршруты для чанков — возобновляемый путь для больших файлов.
  • POST /api/v1/poe/publish — отправить готовую запись.
  • POST /api/v1/poe/publish-batch — отправить много записей за один вызов.
  • GET /api/v1/poe/events/{poe_id} — получать обновления статуса через Server-Sent Events.

Точные тела запросов и ответов описаны в документе OpenAPI шлюза, который отдаёт каждое развёртывание, — ориентируйтесь на него, а не на фрагмент из блога. Стабильной остаётся ментальная модель: сначала зафиксируйте цену, затем сохраните любое содержимое и отправьте готовые байты записи в каноническом CBOR вместе с идентификатором расчёта стоимости.

Почему расчёт стоимости идёт первым?

Потому что публикация — это платная операция, а цену заранее знать невозможно.

Шлюз оплачивает реальные расходы за вас: комиссии транзакций Cardano, хранилище Arweave, когда содержимое прикреплено, валютные риски между USD и этими сетями, а также маржу оператора. Расчёт стоимости делает эти расходы явными до того, как вызов публикации потратит что-либо. (О том, почему публикация стоит денег, см. почему у публикации есть цена.)

Ответ с расчётом стоимости содержит quote_id, разбивку цены (составляющие за сеть, за хранение и за сервис), применённую маржу, актуальность снимка обменного курса и метку времени истечения. Цена фиксируется лишь на ограниченное время — примерно пятнадцать минут, — после чего вы запрашиваете новый расчёт.

Это позволяет вашему приложению решить:

  • хватает ли на счёте средств для публикации;
  • достаточно ли свеж снимок обменного курса для вашей политики;
  • требует ли операция подтверждения пользователя;
  • стоит ли разбить большой пакет;
  • стоит ли повторить попытку с новым расчётом, когда прежний истёк.

Не показывайте метку «бесплатно», пока шлюз не подтвердит, что операция ничего не стоит. Запись только с хешем и без хранилища может быть дешёвой, но авторитет в вопросе цены — шлюз, а не ваш интерфейс.

Что делает этап загрузки?

Загрузка обрабатывает байты, которым нужно где-то находиться.

Подтверждение только с хешем не загружает ничего — дайджест у вас уже есть. Запись с прикреплённым содержимым, запечатанный шифротекст или материал восстановления требуют контентно-адресуемого хранилища, и конечные точки загрузки сохраняют эти байты, возвращая URI вида ar://.... API поддерживает многочастные загрузки, индивидуальные результаты по каждому файлу, дедупликацию по хешу содержимого в пределах счёта и возобновляемые сессии для файлов, слишком больших, чтобы отправить их одним запросом. CardanoWall не устанавливает фиксированного максимального размера загрузки — содержимое оплачивается побайтово, а загрузки в несколько гигабайт — это норма.

Относитесь к загрузке как к её собственному небольшому жизненному циклу:

  1. Загрузите байты.
  2. Получите контентно-адресуемый URI.
  3. Соберите или финализируйте запись Label 309 с этим URI.
  4. Опубликуйте запись.

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

Что делает публикация и почему она асинхронна?

Публикация отправляет запись и запускает цепочку обработки в блокчейне.

Вызов публикации принимает готовые байты записи в каноническом CBOR плюс quote_id. Шлюз закрепляет запись в Cardano под меткой метаданных 309, списывает рассчитанную сумму и возвращает идентификатор записи шлюза (значение вида poe_...), пока транзакция ещё проходит отправку и подтверждение.

Эта асинхронность важна для вашего проектирования. Когда вызов возвращается, хеша транзакции в блокчейне может ещё не существовать. Показывайте состояние ожидания и слушайте обновления. Статус, о котором сообщает API, проходит через такие значения:

  • submitting — транзакция собирается и рассылается;
  • confirming — она в блокчейне, но ниже порога подтверждения;
  • confirmed — она пересекла этот порог и финализирована;
  • failed — публикация окончательно не удалась и не состоится.

Поток статусов на GET /api/v1/poe/events/{poe_id} существует именно для этого. При окончательном сбое шлюз сам отменяет списание — поэтому ваш продукт может честно говорить пользователям: «плата берётся только за то, что попадает в блокчейн», не ведя собственной сверки. Не стройте на стороне вендора путь возврата средств для сбоев публикации; это привело бы к двойному возврату.

Что должен хранить ваш API-клиент?

Храните ровно столько, чтобы восстановить рабочий процесс, — и не больше. Как минимум:

  • ваш собственный идентификатор пользователя или счёта;
  • poe_id шлюза;
  • идентификатор расчёта стоимости, использованный для публикации;
  • дайджест записи или хеш её байтов;
  • итоговый хеш транзакции Cardano, как только он появится;
  • статус и метки времени;
  • любые идентификаторы попыток загрузки;
  • любые контентно-адресуемые URI;
  • любой локальный материал, который понадобится вам для проверки позже.

Не считайте свою базу данных подтверждением. Считайте её продуктовой read-моделью. Подтверждение — это запись Label 309 в блокчейне плюс содержимое или ключи, необходимые для её проверки, и эта комбинация самодостаточна, независима от ваших серверов.

Как читать и проверять записи?

Набор маршрутов для записей рассчитан на публичное, анонимное чтение:

  • GET /api/v1/records — лента записей из блокчейна с фильтрами и пагинацией.
  • GET /api/v1/records/count — количество записей в этой ленте.
  • GET /api/v1/records/{tx_hash} — одна запись по хешу транзакции.
  • POST /api/v1/records/{tx_hash}/verify — отчёт о проверке на стороне сервера.

Маршруты списка и получения обслуживают анонимных вызывающих — публичным страницам проверки и эксплорерам учётные данные не нужны. Bearer-токен может добавить контекст счёта там, где это уместно, но публичная проверка записей никогда не должна зависеть от приватной сессии CardanoWall.

Используйте эти конечные точки для удобства продукта. Для проверок повышенной надёжности дополнительно запускайте автономный верификатор, который получает данные блокчейна через инфраструктуру Cardano, выбираемую им самим, и формирует независимый отчёт. Это здоровое разделение: API упрощает создание приложений, но подтверждение должно оставаться проверяемым и вне API. Открытые SDK и CLI поставляют именно такой верификатор — о том, как он работает от начала до конца, см. проверку записи Label 309.

Как должна работать аутентификация?

Держите учётные данные узкими, а учётные данные оператора — на своём бэкенде.

Шлюз разделяет две плоскости. Ваши пользователи действуют через плоскость данных (/api/v1/*) с недолговечным токеном счёта или API-ключом, который ваш бэкенд выпускает на каждую сессию. Только ваш бэкенд держит учётные данные оператора для плоскости управления (/control/v1/*) — создание счетов, начисление кредитов, когда ваш биллинг собирает деньги, установку маржи и выпуск тех самых токенов счёта.

Доступ к плоскости данных ограничен по областям. Области, которые вы будете использовать:

  • poe:create — расчёт стоимости, загрузка, публикация и пакетная публикация;
  • poe:read — чтение записей, маршрут проверки и поток статусов при вызове с bearer-токеном;
  • account:read — чтение баланса и реестра;
  • webhooks:read и webhooks:write — управление вебхуками в пределах счёта;
  • billing:read — зарезервировано для биллинговых интерфейсов вендора.

Странице публикации нужен poe:create; виджету баланса нужен account:read; ни тому, ни другому не нужно большего. Учётные данные оператора никогда не должны попадать в браузерный бандл, мобильное приложение, скрипт партнёра или CI-задание, которому нужно лишь публиковать подтверждения, — такие задания вместо этого выпускают собственный токен счёта с ограниченными областями. Утёкший токен счёта должен оставаться проблемой на час, а не инцидентом.

Как организовать повторные попытки и идемпотентность?

Спроектируйте повторные попытки до выхода в продакшен, потому что системы публикации отказывают скучными способами: сбои сети, истёкшие расчёты стоимости, нехватка баланса, ограничения частоты, всё ещё незавершённая загрузка или оборвавшийся поток статусов.

Надёжная интеграция должна:

  • использовать идемпотентность там, где её поддерживает API, с ключом по стабильному исходному идентификатору;
  • считать истечение расчёта стоимости нормой — получить новый расчёт и продолжить;
  • после неопределённого сбоя опрашивать авторитетную конечную точку попытки или записи, а не отправлять заново вслепую;
  • избегать повторной загрузки больших файлов;
  • избегать двойного начисления баланса (привязывайте каждое начисление к собственному идентификатору платежа);
  • логировать идентификаторы запросов и идентификаторы шлюза для поддержки;
  • различать в отчётах публикацию со статусом failed и запись со статусом unverifiable.

Публикация дедуплицируется по байтам записи, а пакеты загрузок поддерживают идемпотентное воспроизведение. Опирайтесь на эту семантику вместо цикла «попробуем ещё раз и понадеемся».

Что должны делать вебхуки?

Вебхуки — это то, как вы строите read-модели: не опросом каждую секунду и уж точно не чтением таблиц базы данных шлюза, которые являются внутренними для движка и меняются без предупреждения.

Зарегистрируйте подписку на вебхук, и шлюз будет присылать события жизненного цикла: poe_status_changed, balance_changed, намерения возврата средств, сбои загрузки. Операторы могут подписаться на полный поток событий в пределах всего инстанса; на плоскости данных есть и маршруты вебхуков в пределах счёта. Представление «Отправленные», где каждый пользователь видит опубликованные им записи с актуальным статусом, — канонический сценарий: потребляйте poe_status_changed, проецируйте его в собственную таблицу и отображайте оттуда.

Ваш приёмник должен проверять подпись каждой доставки, принимать доставку «не менее одного раза», привязывать проекции к идентификатору события или доставки и обрабатывать повторную доставку как пустую операцию. Read-модель принадлежит вашему приложению. Каноническое состояние трат и публикации принадлежит шлюзу — кешируйте его для отображения, если нужно, но для любого решения, которое допускает трату, читайте шлюз.

Что никогда не должно покидать клиент?

Приватный материал идентичности. Ваше приложение вызывает API, чтобы публиковать записи; оно никогда не должно передавать шлюзу Identity Seed (сид идентичности) пользователя, приватный ключ подписи или приватный ключ получателя.

  • Для подписанных записей подписывайте локально или используйте подпись вне хоста — SDK предоставляют сценарий, в котором приватный ключ живёт в KMS, HSM или на машине с воздушным зазором, а через сеть проходят только публичный ключ и подпись.
  • Для запечатанных записей шифруйте локально перед загрузкой, когда сценарию нужна сквозная конфиденциальность.
  • Для проверки получателем расшифровывайте локально.

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

С чего начать

Самый быстрый путь — открытые SDK и CLI на github.com/cardanowall: SDK на TypeScript (@cardanowall/sdk-ts), SDK на Python (cardanowall-sdk), SDK на Rust (cardanowall) и CLI cardanowall. Все они не зависят от конкретного шлюза — вы задаёте базовый URL и непрозрачный API-ключ — и все поставляют один и тот же структурный валидатор. Если вы предпочитаете сначала поработать с API вручную, опубликуйте своё первое подтверждение проводит через одну запись от начала до конца, а использование CLI в автоматизации охватывает скриптовый путь. Если же вы хотите запускать бэкенд самостоятельно, а не использовать размещённый, шлюз тоже открыт — см. запустите собственный шлюз.

Что почитать дальше

apidevelopersgateway