
Оценка безопасности протокола Garden: Trail of Bits
Технические
TL;DR, Trail of Bits провёл аудит контрактов GardenStaker, HTLC и FEEAccount протокола Garden в рамках двухнедельного проекта. Выявлено девять находок с уровнями серьёзности High, Medium, Low и Informational; проблем уровня Critical нет. Шесть устранены непосредственно в коде; три закрыты за счёт операционных изменений на уровне протокола.
Garden привлёк Trail of Bits для проведения оценки безопасности ончейн-контрактов протокола, реализующих кросс-чейн свопы BTC через HTLCs, распределение комиссий филлерам, а также механику стейкинга и делегирования, на которой строится участие солверов. Проверку выполнили Richie Humphrey и Vara Prasad Bandaru под руководством инженерного директора Josselin Feist в объёме двух инженеро-недель. Затем была проведена проверка исправлений, а итоговый отчёт был передан в апреле 2024 года.
Trail of Bits отметил, что сам код высокого качества и в его архитектуре явно уделено внимание безопасности. Главной сложностью проверки было не понимание отдельных контрактов, а понимание того, как они взаимодействуют с системой в целом, — в первую очередь логики координации между ордербуком, подписанием комиссий и выбором филлера, которая определяет, как ончейн-контракты используются на практике.
Область аудита
В область аудита вошли три ончейн-контракта:
- GardenStaker: управляет регистрацией филлеров, стейкингом делегаторов, весом голоса и жизненным циклом стейка
- GardenHTLC: реализует механизм HTLC для инициации, погашения и возврата свопа
- FEEAccount: обслуживает платёжные каналы для комиссий между fee manager, филлерами и делегаторами (ранее назывался GardenFEEAccount)
Trail of Bits использовал Slither для статического анализа и Echidna для фаззинга инвариантов с сохранением состояния в GardenStaker. Проверялись два свойства, и оба подтвердились: баланс токенов SEED на контракте staker равен чистой сумме всех стейков, и все филлеры владеют ролью филлера исключительно.
Находки
Trail of Bits выявил девять находок. Распределение по уровню серьёзности: три High, три Medium, две Low, одна Informational. Проблем уровня серьёзности Critical выявлено не было.
TOB-CATALOG-1; Ликвидность филлеров уязвима к DoS-атаке | High
Поскольку за возврат ордеров нет штрафа, атакующий может инициировать своп-ордера без намерения их завершать, блокируя ликвидность филлеров на всё время таймлока HTLC, а затем вызывая refund после истечения срока. При повторении в масштабе это исчерпало бы доступную ёмкость филлеров и фактически вывело бы систему свопов из строя.
Сложность эксплуатации оценена как High, поскольку атакующий должен заблокировать собственные средства на срок, вдвое превышающий длительность таймлока филлера, что делает атаку капиталоёмкой. Trail of Bits предложил несколько направлений устранения: требовать от инициатора депозит, возвращаемый при успешном погашении, взимать комиссию за инициацию или требовать от инициаторов стейк в SEED.
TOB-CATALOG-2; Любой может вызвать самоуничтожение шаблона FeeAccount | High
Конструктор GardenFEEAccountFactory разворачивает контракт-шаблон GardenFEEAccount (теперь называется FEEAccount), не инициализируя его и не отключая его initializers. Поскольку FEEAccount.close и .settle оба вызывают closeChannel, который запускает selfdestruct, атакующий может инициализировать шаблон своими адресами и вызвать на нём close, уничтожив шаблон. Так как клоны обращаются к шаблону через delegate-call, все последующие вызовы к активным комиссионным каналам превратились бы в no-operation, а внесённые токены были бы безвозвратно потеряны.
TOB-CATALOG-3; Комиссии можно вывести до инициации ордера | Medium
Fee manager заранее подписывает сообщения о выплате для делегаторов ещё до того, как своп-ордер инициирован ончейн, используя secretHash, предоставленный создателем. Злонамеренный делегатор, имеющий доступ к такому заранее подписанному сообщению и знающий секрет, может получить комиссии, хотя соответствующий своп так и не был инициирован. Это проблема порядка операций в потоке протокола: уязвимость заключается в том, как система выстраивает последовательность операций подписания относительно ончейн-инициации.
TOB-CATALOG-4; Создатель может получить комиссии, не погасив ордер | Medium
HTLCs для выплаты комиссий и HTLCs свопа используют одинаковую длительность таймлока (48 часов) и один и тот же секрет. Так как секрет известен только создателю, он может дождаться истечения HTLC свопа, вызвать refund, чтобы вернуть свои заблокированные токены, а затем воспользоваться секретом и получить комиссии делегаторов за ордер, который он так и не завершил. Garden выявил эту проблему самостоятельно в ходе проверки.
TOB-CATALOG-5; Делегаторы могут продлить стейк с более высоким множителем голоса | Low
Функция extend в DelegateManager корректно повышает множитель голоса делегатора при продлении на более длительный срок, но не понижает его при продлении на более короткий срок. Делегатор, изначально застейкавший на четыре года, мог бы ближе к концу периода блокировки продлить стейк на шесть месяцев и бессрочно сохранить четырёхлетний множитель.
Garden подтвердил, что это сделано намеренно: протокол спроектирован так, чтобы вознаграждать стейкеров с длительным сроком, и сохранение множителя за уже завершённые периоды блокировки соответствует этой цели. Garden добавил документацию, поясняющую предполагаемое поведение.
TOB-CATALOG-6; Непоследовательное использование термина "expiry" | Informational
Термин expiry в GardenStaker означал абсолютный номер блока, а в GardenHTLC — относительную длительность (число блоков). Комментарии NatSpec и README усугубляли путаницу, повсеместно в кодовой базе неверно описывая expiry как номер блока. Эта семантическая несогласованность создаёт риск для сопровождаемости: разработчик, рассуждающий об expiry применительно к обоим контрактам, мог бы внести некорректное поведение.
TOB-CATALOG-7; Некорректная обработка delegateStakeIDs отключает критически важную функциональность | High
Когда филлер снимается с регистрации и вызывает refund, структура Filler удаляется из маппинга fillers оператором delete в Solidity. Однако поле delegateStakeIDs — это EnumerableSet из OpenZeppelin, который внутри использует и массив, и маппинг. Оператор delete в Solidity обнуляет длину массива, но не очищает индексные записи маппинга. Когда последующие вызовы пытаются удалить элементы из набора, они читают ненулевой valueIndex из устаревшего маппинга, но встречают массив нулевой длины, что вызывает panic revert.
Практическое следствие: любой делегатор, чей филлер снялся с регистрации и вызвал refund, навсегда теряет возможность вызывать changeVote или refund для собственного стейка. Даже если филлер зарегистрируется снова, существующие привязки стейков не восстанавливаются — стейки оказываются заблокированы навсегда. Эта проблема была выявлена с помощью Slither.
TOB-CATALOG-8; Пользователь не может продлить стейк на максимальный срок | Low
Функция renew вычисляет новый expiry как block.number + newLockBlocks. Когда пользователь передаёт максимальное значение uint256, чтобы обозначить бессрочный стейкинг, это сложение переполняется и приводит к откату транзакции. Пользователь, у которого стейк истёк, не может продлить его на максимальный срок.
TOB-CATALOG-9; Делегаторы теряют вознаграждения, когда одни HTLCs истекают раньше других в канале | Medium
Функция claim в GardenFEEAccount перебирает массив HTLCs и молча исключает те, у которых истёк срок, не откатывая транзакцию и не отслеживая недостачу. В платёжном канале с несколькими активными HTLCs, если один из них истечёт до того, как делегатор заберёт выплату, из-за задержек с завершением свопа связанные с ним вознаграждения теряются безвозвратно. Сложность эксплуатации оценена как High, поскольку для срабатывания условия требуется определённая последовательность событий во времени в нескольких свопах.
Действия
Шесть из девяти находок устранены в коде. Три закрыты за счёт операционных изменений на уровне протокола.
Устранено в коде:
- TOB-CATALOG-2 исправлена добавлением функции initialize в реализацию GardenFEEAccount, которая вызывает _disableInitializers и не допускает какой-либо будущей инициализации контракта-шаблона.
- TOB-CATALOG-5 признана намеренным проектным решением и закрыта добавлением документации, поясняющей предполагаемое поведение при сохранении множителя.
- TOB-CATALOG-6 устранена переименованием переменной; теперь expiry во всех случаях означает абсолютный номер блока, а timelock — относительную длительность в обоих контрактах. (по состоянию на 23 апреля 2026 года срок действия ордера определяется через timelock, а не expiry)
- TOB-CATALOG-7 устранена изменением логики снятия филлера с регистрации: теперь сбрасываются отдельные поля данных, а не удаляется вся структура, благодаря чему delegateStakeIDs остаётся нетронутым. Это позволяет существующим делегаторам по-прежнему вызывать changeVote и refund и гарантирует, что повторная регистрация автоматически восстанавливает привязки их стейков.
- TOB-CATALOG-8 устранена добавлением проверки условия максимальной длительности в функцию renew: при выборе максимального периода блокировки stake.expiry напрямую устанавливается в type(uint).max.
- TOB-CATALOG-9 устранена обновлением функции claim, которая теперь хранит в маппингах как заявленные, так и отправленные секреты, что позволяет частичным выплатам проходить без потери вознаграждений по другим HTLCs в том же канале.
TOB-CATALOG-1, TOB-CATALOG-3 и TOB-CATALOG-4 были закрыты за счёт операционных изменений на уровне протокола. Для TOB-CATALOG-1 Garden ввёл лимит в 1.5 BTC/WBTC на адрес за 24 часа на период беты, а на публичном запуске запланирована невозвратная комиссия платформы (сейчас действует). Для TOB-CATALOG-3 Garden изменил порядок в потоке подписания комиссий: филлеры передают свою подпись в FeeHub только после того, как пользователь инициирует операцию в исходной сети. Для TOB-CATALOG-4 Garden ужесточил временные ограничения между инициацией свопа и подписанием комиссионного HTLC. Trail of Bits подтвердил, что эти меры по устранению описаны и выглядят правдоподобно.
Итоги
Девять находок: три High, три Medium, две Low и одна Informational. Проблем уровня Critical нет. Все проблемы устранены.
После этой оценки Garden завершил оценку безопасности приложения на Move с Zellic (июнь 2025 года) и провёл конкурсный bug bounty на Code4rena с призовым фондом $37,500 за 813 заявок, направленный на архитектуру HTLC в EVM, Solana, Sui и Starknet.
Last updated