Перейти к содержанию

Bitcoin для разработчика: узел, кошелёк и сайт

Учебная библиотека продаёт доступ к курсу с номером 17 за 30 000 сатоши. Прежде чем писать кнопку оплаты, определим, кто вправе утверждать, что деньги поступили. Браузер читателя, сервер заказа, кошелёк и Bitcoin-узел располагают разными сведениями. Если смешать их роли, успешный ответ одного компонента легко превратить в ошибочное решение другого.

Результат этого урока — схема доверия, в которой каждое решение имеет собственный источник. Здесь мы описываем устройство проекта. Узел и кошельки пока не запускаются; все дальнейшие состояния в примерах ожидаемые.

Кто проверяет транзакцию

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

Кошелёк решает другую задачу. Он определяет выходы, связанные с известными ему условиями расходования, выбирает входы для отправки, рассчитывает сдачу и, если располагает секретами или подписантом, организует подпись. Наблюдающий кошелёк может показывать поступления, хотя не умеет тратить средства. Следовательно, наличие баланса в ответе кошелька не доказывает наличие права подписи у веб-сервера.

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

Рассмотрим маршрут:

браузер → сервер заказа → таблица счетов
                         ↓
                наблюдающий кошелёк → узел
                         ↓
                 журнал платежей → право доступа

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

Одно событие, четыре значения

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

Компонент Доступное утверждение Чего утверждение не доказывает
Браузер Пользователь запросил отправку Получатель получил требуемую сумму
Узел Транзакция допустима и наблюдается Выполнены условия заказа
Кошелёк Выход относится к известному скрипту Доступ уже выдан ровно один раз
Сервис Счёт соответствует правилу принятия Будущая реорганизация невозможна

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

Ещё один вариант — библиотека получила 30 000 сатоши, но на адрес другого счёта. По кошельку баланс увеличился правильно. По заказу 17 оснований для выдачи нет. Поэтому приложение хранит отдельные адреса и связи, а не ищет совпадение общего баланса с ожидаемой выручкой.

Где находятся секреты

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

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

Отдельно защищается интерфейс управления узлом. RPC Bitcoin Core предназначен для доверенных клиентов. Приём произвольного имени метода от браузера превратит сервер в посредника для команд, которых пользователь не должен выполнять. Вместо универсального прокси зададим небольшой адаптер: узнать текущую цепочку, получить нужную кошельковую транзакцию и выдать новый адрес в контролируемом внутреннем маршруте.

Расположим данные проекта в четырёх логических хранилищах. Узел содержит сетевое состояние; кошелёк — условия наблюдения и, в выбранном режиме, секреты; база приложения — счета и решения; архив выпуска — сами учебные материалы. Резервная копия одного хранилища не заменяет остальных. Сохранённая цепочка не восстанавливает соответствие заказа адресу, а таблица заказов не позволяет подписать расходование потерянного кошелька.

Как описать доверие в проекте

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

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

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

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

Официальное описание границы доверия RPC приведено в документации Bitcoin Core. В следующих уроках разберём, какие именно байты и подписи проверяет сеть. Схема этого урока остаётся основой: ни один проверенный хеш не заменяет корректную логику заказа.

Оглавление серии