UTXO в Bitcoin: журнал неизрасходованных выходов
Банковский интерфейс обычно показывает одно число баланса. В Bitcoin расходование строится из отдельных выходов предыдущих транзакций. Чтобы понять поступление на адрес библиотеки и последующую сдачу, научимся вести небольшой журнал UTXO. Он поможет объяснить, почему нельзя потратить половину выхода, оставив вторую половину на месте.
UTXO означает неизрасходованный выход транзакции. В нашем примере читатель располагает выходом на 80 000 сатоши. Библиотека ожидает 30 000 за курс 17. Значения относятся к собственному учебному расчёту; в сети ничего не отправляется.
Идентичность выхода
Каждый выход имеет сумму и условие расходования. Его однозначно определяет пара: идентификатор транзакции и номер выхода внутри неё. Такая пара называется outpoint. Нумерация начинается с нуля. Если одна транзакция создаёт два выхода по одинаковой сумме на одинаковый адрес, это всё равно два разных объекта.
Запишем исходное состояние:
{
"outpoint": {"txid": "FUNDING_TXID", "vout": 0},
"amountSats": "80000",
"ownerLabel": "reader",
"spent": false
}
FUNDING_TXID — обозначение для будущего реального идентификатора лаборатории. Поле ownerLabel существует только в объясняющей модели. Протокол не хранит имя читателя: он хранит условие, которому расходование должно удовлетворять. Даже если приложение подписало выход своим удобным названием, право траты определяется скриптом и необходимыми данными.
Баланс в этой модели является суммой подходящих доступных выходов. Однако слово «доступный» требует уточнения. Выход может находиться в неподтверждённой транзакции, требовать ещё недоступную подпись или быть ограничен временем. Поэтому кошелёк способен показывать несколько категорий баланса, а приложение не должно сводить их к одному без определения.
Расходование целого объекта
Читатель не изменяет сумму исходного выхода с 80 000 на 50 000. Новая транзакция указывает исходный outpoint как вход, расходует его целиком и создаёт новые выходы. В выбранном расчёте библиотека получает 30 000, читатель — 49 300 сдачи, а разница 700 становится комиссией.
| Изменение набора | Сумма | Смысл |
|---|---|---|
| Удаление исходного UTXO | 80 000 | Предыдущий выход израсходован |
| Создание выхода библиотеке | 30 000 | Новый объект получения |
| Создание выхода сдачи | 49 300 | Новый объект читателя |
Комиссия не является третьим обычным выходом с адресом «майнер». Она определяется разностью сумм входов и выходов. Майнер учитывает разрешённые комиссии в своей coinbase-транзакции по правилам блока. Если забыть выход сдачи, разница может стать гораздо больше предполагаемых 700 сатоши.
Для состояния после принятия транзакции нашей моделью получим один UTXO читателя на 49 300 и один UTXO библиотеки на 30 000. Исходный объект хранится в историческом журнале, но больше не входит в текущий набор неизрасходованных выходов. История и состояние — разные представления одних событий.
Несколько входов и выходов
Поменяем исходное условие. У читателя нет выхода на 80 000, зато есть два: на 20 000 и 15 000 сатоши. Оба могут стать входами. При переводе 30 000 и комиссии 700 сдача составит 4 300. Это корректная арифметическая модель, хотя фактическая комиссия может измениться из-за большего числа входов и другого веса транзакции.
Теперь библиотека получает два перевода на один счёт: 12 000 и 18 000. Приложение суммирует подходящие выходы, сохраняя их собственные outpoint. Общая сумма равна цене, но каждый выход может иметь собственное состояние подтверждения. Если второй перевод исчезает из наблюдаемой цепочки, нельзя продолжать считать итог без пересчёта.
Важно учитывать именно выходы получения. Один txid может содержать несколько выходов библиотеке. Запись только идентификатора транзакции потеряет различие между ними. Запись только адреса и суммы тоже не обеспечивает уникальность: два разных платежа могут совпадать по этим полям. Для журналирования используем сеть плюс txid плюс vout.
Нельзя расходовать дважды
Две конкурирующие транзакции могут указать один исходный выход. Обе могут иметь математически корректные подписи и правильную арифметику. Однако в одной выбранной истории этот выход расходуется только один раз. Поэтому наблюдение неподтверждённого перевода не равнозначно окончательному решению о том, какая версия будет включена.
В учебном журнале это можно показать двумя кандидатами PAYMENT_A и PAYMENT_B, которые ссылаются на FUNDING_TXID:0. Пока мы рассматриваем разные варианты истории, сохраняем оба наблюдения. В выбранной цепочке применяем только допустимое расходование. Не складываем выходы двух конфликтующих кандидатов так, будто они созданы независимыми входами.
Такая модель объясняет и восстановление после реорганизации. Если блок с расходованием удаляется из выбранной цепочки, состояние возвращается к предыдущему набору. Но это ещё не гарантирует, что исходный выход снова свободен: новая ветвь может содержать другое расходование. Правильный пересчёт опирается на фактически выбранные блоки, а не на простое переключение одного флага.
Что хранит сайт
Для библиотеки полезны два слоя. Первый — журнал наблюдаемых выходов с суммой, назначением и сетевым состоянием. Второй — решение по счёту, рассчитанное по версии политики принятия. Кошелёк помогает получить первый слой, а цена и выдача материалов относятся ко второму.
Не нужно копировать весь глобальный UTXO-набор в базу сайта. Достаточно хранить собственные назначения и необходимые наблюдения, сохраняя возможность свериться с узлом. При этом поле «баланс кошелька» не заменяет журнал счетов. Общий баланс меняется и при возврате, и при объединении выходов, и при отправке собственной сдачи.
Историческое расходование не отменяет поступление
Предположим, библиотека получила выход 30 000, затем использовала его для согласованного возврата. Он перестал быть UTXO, но факт первоначальной оплаты курса остаётся в истории. Если сайт ищет оплаченные заказы только по текущим неизрасходованным выходам, после возврата или обычного перемещения выручки заказ ошибочно исчезнет. Для учёта нужен журнал получения и последующих действий. Текущий набор отвечает на вопрос о возможном расходовании, а историческая запись — о прошедшем событии.
Модель выходов описана в документации Bitcoin. После урока вы можете вручную применить транзакцию к небольшому набору UTXO: убрать израсходованные объекты и добавить созданные. Следующая страница уточнит расчёт сдачи и покажет, какие ошибки возникают при выборе комиссии.