GasFree4you
Инженерный разбор·8 минут чтения

Ошибка OUT OF ENERGY в TRON: постмортем нашего сбоя

31 час сбоя, менее 5% клиентов: почему после делегирования не хватало Energy для перевода в TRON и как мы нашли причину в java-tron.

затронутых клиентов
<5%
длительность сбоя
31 час
блокчейн
TRON

Что произошло

Мы делегировали достаточно энергии для перевода, но часть клиентов получала FAILED — OUT OF ENERGY. Сбой длился 31 час и затронул менее 5% клиентов. Это постмортем нашего сервиса GasFree4you: что изменилось в управлении арендой, как мы исследовали ошибку и почему вернули автоматический отзыв после перевода.

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

Как работала аренда Energy

Мы делегируем Energy на один час. Раньше сервис сам отзывал её сразу после перевода; если энергия не была использована, отзыв происходил через час.

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

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

Энергия была. Куда она исчезла?

Первые жалобы выглядели противоречиво: объёма делегирования хватало, а расход смарт-контракта перевода не изменился. Мы собрали анализатор, который восстанавливал количество доступной энергии по каждому адресу во времени.

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

Порядок событий, который привёл к ошибке

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

  1. 01 · Делегирован старый заказ A
  2. 02 · Перевод выполнен; клиент не запросил отзыв A
  3. 03 · Делегирован новый заказ B на тот же адрес
  4. 04 · Через час после A срабатывает его автоматический отзыв
  5. 05 · Доступная Energy падает перед новым переводом

Что делает отзыв в java-tron

Мы предположили баг блокчейна и пошли читать Java-код TRON. В UnDelegateResourceActuator.java нашли пропорциональный расчёт transferUsage. Код подтвердил механику, которую наша логика заказов не учитывала.

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

В знаменателе getAllFrozenBalanceForEnergy() учитывается весь стейк, обеспечивающий Energy адреса: собственный и полученный через делегирования, включая других владельцев. Формула не хранит соответствие «этот расход — от старого заказа, а новый заказ ещё чистый».

Java · UnDelegateResourceActuator

transferUsage = (long) (receiverCapsule.getEnergyUsage()
    * ((double) (unDelegateBalance) / receiverCapsule.getAllFrozenBalanceForEnergy()));
transferUsage = min(unDelegateMaxUsage, transferUsage);

Исходный код TRON: java-tron · UnDelegateResourceActuator.java (ENERGY), java-tron · AccountCapsule.getAllFrozenBalanceForEnergy().

Пример: новый заказ есть, доступной Energy меньше

Рассмотрим два равных делегирования по 100 условных единиц Energy. Старый заказ уже израсходовал 100, новый добавил ещё 100. До отзыва общий лимит равен 200, учтённый расход — 100, доступно 100.

Отзыв половины обеспечивающего стейка убирает 100 из лимита, но только 50 из расхода: 100 × 1/2. После отзыва лимит равен 100, расход — 50, доступно всего 50. Если следующему вызову нужно 80, этих ресурсов уже недостаточно.

Доступная Energy: до и после отзыва
До отзыва A
100
После отзыва A
50

Для следующего вызова нужно 80 условных единиц

Как мы устранили сбой

Мы вернули самостоятельный отзыв энергии сервисом сразу после перевода. Больше не ждём, пока клиент запросит отзыв или истечёт час аренды. Для неиспользованной энергии сохраняется часовой срок.

Так мы убрали зависимость завершения использованной аренды от действий клиента. Главный вывод для нашей интеграции: жизненный цикл заказа должен учитывать состояние адреса и порядок ончейн-операций.

Что проверить при FAILED — OUT OF ENERGY

Если перевод USDT TRC-20 или другой вызов контракта завершился OUT OF ENERGY, одного факта покупки энергии недостаточно. Важно состояние ресурсов при исполнении транзакции.

  1. Найдите TxID и результат исполнения в TRONSCAN. Зафиксируйте адрес отправителя и порядок блоков.
  2. Сопоставьте делегирования и отзывы на адресе, особенно старые аренды, истекающие перед переводом. Текущий баланс ресурсов не заменяет историю.
  3. Проверьте доступную Energy, оценку вызова, fee_limit и возможность покрытия недостающих ресурсов TRX. Одинаковое сообщение об ошибке не доказывает одинаковую причину.
  4. Передайте поддержке TxID, адрес и время заказа. Приватный ключ и seed-фраза для расследования не нужны.

Исходный код TRON: TRON · FeeLimit and Energy cost, TRON · Transaction receipt.

Что этот случай говорит об интеграциях с TRON

Этот сбой стал проверкой нашей модели работы с ресурсами TRON. Отдельный заказ в базе сервиса не создаёт отдельный изолированный запас Energy в блокчейне.

Наша экспертиза платежей и переводов складывается из таких расследований: от жалобы клиента и восстановления событий до чтения исходного кода и изменения поведения сервиса. В блоге продолжим разбирать TRON на конкретных технических примерах.

Вопросы об ошибке OUT OF ENERGY

Почему энергии не хватило после новой аренды?

В нашем случае после нового делегирования срабатывал отзыв старого. Он уменьшал общие доступные ресурсы адреса перед следующим переводом.

Это был сбой всей сети TRON?

Нет: описан сбой нашего сервиса, затронувший менее 5% клиентов. Разбор исходного кода объяснил поведение протокола; он не доказывает баг или остановку сети.

Что изменилось после расследования?

Мы снова сами отзываем энергию после перевода, не дожидаясь запроса клиента или окончания часа аренды.

← Все материалы