Мониторинг транзакций: подтверждения, реорганизации, повторы
«Транзакция отправлена» и «транзакция прошла» — это два разных момента, и между ними может пройти заметное время. Если сервис принимает решения по деньгам — зачисляет баланс, отгружает товар, закрывает заявку, — важно понимать, что происходит с переводом между этими двумя точками.
Жизненный цикл транзакции
Сначала транзакция попадает в мемпул — пул неподтверждённых транзакций, которые видела нода, но которые ещё не вошли ни в один блок. На этом этапе транзакцию теоретически можно вытеснить конкурирующей — с тем же номером nonce у отправителя, но с большей комиссией. Затем транзакция попадает в блок — считается включённой, но пока с одним подтверждением. Дальше на цепочку добавляются новые блоки, и с каждым из них счётчик подтверждений растёт.
Что такое подтверждение
Подтверждение — это ещё один блок, добавленный поверх блока с вашей транзакцией. Чем их больше, тем менее вероятно, что цепочка в этом месте изменится и транзакция «отвалится». Сколько подтверждений считать достаточным — вопрос без единого верного ответа: это зависит от сети, от скорости её финализации и от того, какую сумму и риск сервис готов принять. Один и тот же сервис может ждать разное число подтверждений для маленького и крупного перевода.
Реорганизации: когда блок отменяется
Иногда два майнера или валидатора почти одновременно предлагают блок — и на какое-то время в сети существуют две конкурирующие версии цепочки. Побеждает та, что в итоге набирает больше подтверждений; блоки проигравшей ветки отбрасываются, а транзакции, которые были только в них, возвращаются в мемпул, будто их и не подтверждали. Это называется реорганизацией. Именно от неё защищает ожидание нескольких подтверждений: чем глубже транзакция закопана под другими блоками, тем менее вероятна реорганизация вокруг неё. У разных сетей — от Bitcoin до XRP Ledger — разная скорость появления блоков и разная механика финализации, поэтому и глубина реорганизаций отличается.
Практический вывод из этого — хранить не только высоту блока, в котором нашлась транзакция, но и хеш этого блока. При каждой следующей проверке можно сравнить сохранённый хеш с тем, что нода отдаёт на этой высоте сейчас. Если хеш совпал — блок остался тем же, реорганизации не было. Если изменился — значит, ветка переписалась, и транзакцию нужно снова считать неподтверждённой, пока она не найдётся в новой версии цепочки.
Повторы: как не засчитать перевод дважды
Если сервис узнаёт о переводах через периодический опрос ноды, легко случайно увидеть одну и ту же транзакцию два раза — например, если опрос идёт по недавним блокам с перекрытием диапазонов. Разумная защита — использовать хеш транзакции как идемпотентный ключ: прежде чем зачислить перевод, проверить, не обработан ли уже такой хеш. Это же правило спасает и в обратной ситуации — когда на сервис случайно пришёл повторный вебхук или ответ ноды продублировался при повторной попытке запроса после таймаута.
Есть и обратный случай: отправитель может заменить свою же неподтверждённую транзакцию на новую — с тем же назначением, но другой комиссией и, соответственно, другим хешем. Если сервис уже показал клиенту старый хеш как «отправлено», стоит понимать, что итоговая транзакция в блоке может прийти под другим идентификатором. Ориентироваться в таких случаях лучше на пару адрес-сумма или на явное подтверждение от отправителя, а не только на изначальный хеш.
Как это выглядит на практике через прокси
Прокси на CryptoSafe Nodes не присылает уведомления сам — он отвечает только на запрос, который отправили вы. Поэтому мониторинг строится на регулярном опросе: сервис периодически запрашивает статус нужной транзакции или последние блоки и сравнивает результат с тем, что видел раньше.
# ethereum — имя ноды для примера, точное название — в личном кабинете
curl -X POST https://nodes-api.cryptosafe.pro/proxy/ethereum \
-H "Authorization: <ваш-ключ>" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getTransactionReceipt","params":["<хеш-транзакции>"],"id":1}'
# поле blockNumber в ответе появляется только после включения в блок
Для TRON похожий статус возвращает wallet/gettransactioninfobyid
на странице TRON API, а для XRP Ledger — метод
tx или account_tx, описанный на странице
XRP API. Логика одна и та же для любой сети:
опрашивать нужный метод, следить за глубиной подтверждений и не считать
перевод окончательным раньше времени.
Что важно не забыть
Три практических вывода: ждите подтверждений соразмерно риску, а не фиксированное число «на все случаи»; отслеживайте не только высоту блока, но и то, что блок с транзакцией не исчез при реорганизации; используйте хеш транзакции как ключ идемпотентности, чтобы повторный опрос не привёл к двойному зачислению. Если задача — именно приём платежей в USDT, дополнительные детали разобраны в статье «Как принимать USDT», а если нужно поднять историю за прошлые периоды — в статье об архивных нодах.