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

Bitcoin-платежи: обработка реорганизации блокчейна

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

Продолжим счёт курса 17 на 30 000 сатоши. Используем условные обозначения блоков A и B, не настоящие хеши. Узел, ветки и транзакции сейчас не запускаются; схема объясняет ожидаемое поведение будущего наблюдателя.

Почему высоты недостаточно

Допустим, платёж вошёл в блок A102, а вершина A103 дала два подтверждения. После реорганизации основная ветка содержит B102 и B103. Высоты остались теми же, но блоки другие. Если хранить только число 102, приложение ошибочно продолжит считать прежнее включение действительным.

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

             A102(payment) → A103
A100 → A101
             B102          → B103 → B104

В этой условной картине A101 является общим предком. Старые сведения после него больше не описывают выбранную ветку. При этом прежние события журнала не становятся «не существовавшими»: сервис действительно видел их и мог принять на их основании решение.

Находим границу пересчёта

Наблюдатель сравнивает сохранённый checkpoint с текущей цепочкой источника. Если хеш контрольного блока больше не является частью основной ветки, ищет общий предок либо использует документированный механизм истории источника. В listsinceblock описано поведение при контрольном блоке вне основной цепочки и возврат удалённых сведений.

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

Также учитываем ограничения pruned-узла. Источник может не сохранить все данные, необходимые для такого восстановления. В этом случае наблюдатель переходит к явному расширенному восстановлению или ручному разбору, а не объявляет отсутствие исторического ответа доказательством отсутствия платежа.

Обновляем проекцию обратимо

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

Сумма самого outpoint при этом не меняется. Обновляется состояние его допустимости. Журнал добавляет переход с причиной chain_reorganization, сохраняя прежний блок и новую контрольную точку. Нельзя выполнить общее удаление всех платежей «после высоты 101», потеряв их историю и назначения.

detect divergence
→ suspend new decisions for affected snapshot
→ update old-branch output projections
→ apply replacement-branch observations
→ save checkpoint with projection changes
→ recompute affected invoice decisions

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

Что происходит с квалификацией платежа

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

Историческая запись payment_qualified сохраняется. Она объясняет, почему система ранее запланировала выдачу доступа. Если удалить её, оператор потеряет причинную связь, а служба доставки не поймёт, почему возникла задача на выдачу материала.

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

Нельзя отменить уже прочитанный файл

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

В лаборатории при изменении квалификации после выдачи создаём payment_review_after_grant. Он связывает платёж, выдачу и новую цепочку. Автоматический запрет будущего доступа или сохранение права выбираются отдельной политикой; мы не скрываем этот выбор внутри SQL-отката наблюдений.

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

Меняем условие: платёж вернулся в новой ветке

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

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

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

Повторное обнаружение той же ветки

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

Разбор результата

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

В следующем уроке свяжем квалификацию с выдачей доступа через transactional outbox. Оглавление курса показывает дальнейший маршрут восстановления.

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