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

Bitcoin-платежи: сверка журнала и кошелька

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

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

Выбираем одинаковую область сравнения

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

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

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

Создаём категории расхождений

Есть несколько принципиально разных случаев. Выход известен кошельку, но отсутствует в журнале. Выход существует в журнале, но источник теперь сообщает другое состояние. Адрес поступления принадлежит кошельку, но не назначен счёту. Задача доступа сохранена, а её результат не подтверждён.

source_only_output
journal_state_mismatch
unassigned_receipt
grant_delivery_unknown
refund_result_unknown
history_unavailable

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

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

Сопоставляем выходы по ключу

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

def output_key(item):
    return (item.network, item.txid, item.vout)

def compare_outputs(source_outputs, journal_outputs):
    source = {output_key(item): item for item in source_outputs}
    journal = {output_key(item): item for item in journal_outputs}
    report = []
    for key in source.keys() | journal.keys():
        if key not in journal:
            report.append((key, "source_only_output"))
        elif key not in source:
            report.append((key, "requires_history_review"))
        elif source[key].amount_sats != journal[key].amount_sats:
            report.append((key, "immutable_amount_mismatch"))
        elif source[key].state != journal[key].state:
            report.append((key, "journal_state_mismatch"))
    return report

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

Отсутствие ключа в ограниченном снимке источника ещё не доказывает отсутствие исторического выхода. Именно поэтому категория называется requires_history_review. Сначала выясняем полноту диапазона и режим хранения узла. Простой список текущих UTXO непригоден как полная история поступлений.

Разбираем неназначенный перевод

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

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

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

Сверяем выдачу доступа

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

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

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

Меняем условие: проекция повреждена

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

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

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

Сохраняем область отчёта

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

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

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

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

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

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