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

Транзакция Bitcoin: поля и ссылки на выходы outpoint

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

Продолжаем пример 80 000 → 30 000 + 49 300 + 700. Идентификаторы в листингах обозначены словами, чтобы их нельзя было принять за реальные значения сети. Транзакции и декодирование в текущей работе не выполняются.

Вход ссылается назад

Обычный вход содержит ссылку на предыдущий выход: txid предыдущей транзакции и его индекс vout. Сумма расходуемого объекта не появляется из заявления отправителя. Она определяется данным предыдущего выхода, с которым узел сверяет ссылку. Данные для удовлетворения условия траты находятся в соответствующих полях, зависящих от вида расходования.

В объясняющем представлении:

{
  "vin": [{
    "txid": "FUNDING_TXID",
    "vout": 0,
    "sequence": 4294967295
  }],
  "vout": [
    {"n": 0, "valueSats": "30000", "destination": "library"},
    {"n": 1, "valueSats": "49300", "destination": "reader-change"}
  ]
}

Это модель статьи, а не буквальный ответ RPC: в реальном подробном ответе Core суммы и скрипты имеют собственные поля. Мы используем valueSats и понятные назначения, чтобы отделить смысл структуры от формата API. В будущем адаптер явно преобразует реальные поля в такую внутреннюю модель.

Первый новый выход определяется PAYMENT_TXID:0, второй — PAYMENT_TXID:1. Ссылка входа при этом указывает на FUNDING_TXID:0. Совпадение числового индекса не объединяет объекты: индекс существует внутри конкретной транзакции.

Выход определяет будущее

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

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

Изменим пример: библиотеке предназначены 10 000 в выходе 0 и 20 000 в выходе 2; между ними находится сдача. Для одного счёта могут быть полезны оба поступления. Если журнал хранит только txid, он не сможет выразить два самостоятельных объекта и правильно обработать последующее расходование каждого из них.

Версия, sequence и locktime

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

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

locktime задаёт условие абсолютной финальности по высоте либо времени при предусмотренных правилах. Оно не означает «отправить перевод ровно в этот час» и не заставляет майнера включить транзакцию после достижения условия. Подробный разбор времени появится в отдельном уроке; сейчас отмечаем поле как часть проверяемой структуры.

Txid и двоичное представление

Транзакция передаётся в бинарном формате. Подробный JSON удобен человеку и приложению, но не заменяет сериализацию. Идентификатор вычисляется по протокольному правилу, а не по хешу JSON-объекта с красивыми отступами. Для SegWit позднее различим идентификатор без свидетеля и идентификатор со свидетелем.

При обмене шестнадцатеричными сырыми данными полезно хранить явный тип поля. Строка, содержащая байты транзакции, может быть значительно длиннее 64 символов идентификатора. Обе строки выглядят как hex, но выполняют разные роли. Передать сырую транзакцию в место, где ожидается txid, — ошибка интерфейса данных.

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

Содержание и принадлежность блока

Транзакционные поля описывают сам объект. Поля вроде blockhash и количества подтверждений в RPC-ответе описывают его текущее положение относительно наблюдаемой цепочки. Они могут измениться без изменения содержания транзакции. Поэтому в журнале библиотеки разделяем неизменные данные выхода и изменяемое сетевое наблюдение.

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

Для будущего адаптера зафиксируем минимальную внутреннюю структуру: сеть, txid, vout, сумма в сатоши, условие назначения и наблюдаемый блок. Время ответа и источник добавляются отдельно. Такое представление достаточно для связи со счётом и устойчивого повторного чтения.

Преобразование ответа адаптером

Адаптер библиотеки не обязан передавать браузеру все поля подробного RPC. Он извлекает нужные выходы, переводит суммы по точному правилу и сохраняет происхождение данных. Если в ответе отсутствует обязательный индекс или условие назначения, это ошибка полноты наблюдения, а не пустая оплата. Для каждой внутренней записи полезна версия преобразователя. Тогда изменение формата API можно разобрать отдельно от изменения сетевого состояния. Сырые ответы, если они архивируются, остаются внутренним источником и очищаются от лишних чувствительных данных. Пользовательская карточка получает предметное состояние счёта, а не непрозрачную структуру кошелька.

Подробный формат исторической сериализации описан в справочнике транзакций; расширение свидетеля — в BIP 141. Исторические замечания старого справочника о версиях не следует принимать за описание текущих возможностей Core. Следующий урок покажет, почему даже правильно сформированный объект может находиться только в локальной очереди узла.

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