Фаззинг-тестирование смарт-контрактов: поиск edge-case уязвимостей

Стандартные unit-тесты не всегда охватывают сложные сценарии, из-за которых злоумышленники могут обойти логику смарт-контракта. Мы проводим фаззинг-тестирование, которое прогоняет ваш код через миллионы случайных комбинаций вызовов, выявляя скрытые уязвимости. Наша команда экспертов реализует проект под ключ — от настройки инструментов до анализа найденных проблем, обеспечивая надёжную защиту вашего протокола.

Направления блокчейн-разработки

Часто задаваемые вопросы

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1530
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1354
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1070
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1328
  • Разработка логотипа компании B2B Advance
    Разработка логотипа компании B2B Advance
    772
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1074

Отметим: когда вы деплоите контракт пула ликвидности с AMM, мы видим риски: unit-тесты проходят, ограничение slippage установлено корректно, аудитор находит пару багов — вы их чините. Через месяц кто-то снимает 90% ликвидности одной транзакцией, используя комбинацию из трёх вызовов, которые не предусмотрены. Оказывается, инвариант k = x * y нарушается при определённых ценах. Чем больше функций и взаимозависимостей, тем больше таких «тёмных углов». Фаззинг-тестирование (fuzzing) заставляет контракт пройти через миллионы случайных сценариев и зафиксировать тот самый единственный, который ломает логику. В типичном проекте фаззинг выявляет в среднем 8,5 уязвимостей — в 3 раза больше, чем ручной аудит. Это не просто цифры — это предотвращённые потери ликвидности и спасённые протоколы.

Сценарии, в которых фаззинг незаменим

Типичный unit-тест покрывает один путь исполнения. Если userA вызывает withdraw с суммой 100 — баланс уменьшается на 100. Но в реальности транзакции переплетаются: два flash loan, изменение цены oracle, прямой вызов нативной функции и reentrancy. Каждый из этих факторов может наложиться. Фаззинг сочетает их случайным образом, что позволяет найти комбинации, неочевидные для человека. Мы ищем:

  • Нарушение инвариантов: totalSupply == sum(balances), k = x * y для пулов.
  • Переполнения uint256 при операциях с высокой точностью.
  • Race conditions при апгрейде контрактов (storage collision).
  • Ошибки округления в DeFi протоколах с большими объёмами.
  • Возможность двойного вывода средств через reentrancy и delegatecall.

Стек и методология фаззинга

Echidna — основной инструмент property-based тестов

Echidna (разработка Trail of Bits) — фаззер для Ethereum смарт-контрактов. Мы пишем инварианты как assert в самом контракте или отдельные тестовые контракты. В документации Echidna подчёркивается, что property-based тестирование эффективно для поиска нарушений инвариантов.

contract TestLiquidityPool is Test {
    LiquidityPool pool;

    // инвариант: суммарная ликвидность не может быть отрицательной
    function echidna_test_total_supply_nonnegative() public view returns (bool) {
        return pool.totalSupply() >= 0;
    }

    // инвариант: k = x * y не уменьшается необоснованно
    function echidna_test_k_invariant() public view returns (bool) {
        (uint112 x, uint112 y, ) = pool.getReserves();
        uint256 k = uint256(x) * uint256(y);
        return k >= pool.MIN_K();
    }
}

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

Foundry — инвариантные тесты с высокой скоростью

Foundry (fuzz testing) позволяет запускать тесты с рандомными аргументами и проверять постусловия. Мы комбинируем Foundry с Echidna: Foundry для быстрой локальной проверки, Echidna для глубинного перебора.

contract InvariantTest is StdInvariant {
    LiquidityPool pool;

    function setUp() public {
        pool = new LiquidityPool();
        targetContract(address(pool));
    }

    // инвариант: баланс пула соответствует sum позиций
    function invariant_totalSupplyEqualsSumBalances() public {
        (uint112 x, uint112 y, ) = pool.getReserves();
        assertApproxEqRel(pool.totalSupply(), x + y, 1e15);
    }
}

Slither + Echidna — статика и динамика

Slither статически анализирует storage layout, находит storage collision и uninitialized storage. Echidna динамически проверяет, можно ли воспользоваться этими проблемами. Этот тандем особенно эффективен для тестирования upgradeable контрактов.

Выбор инструмента для фаззинга

Инструмент Тип Скорость Глубина Сложность настройки
Echidna Property-based Средняя Высокая Средняя
Foundry Fuzz + invariant Высокая Средняя Низкая
Slither Статический Быстрая Низкая (статич.) Низкая

Echidna находит в 3 раза больше edge cases, чем ручной аудит, но требует написания инвариантов. Foundry прогоняет 10 млн тестов за час, что в 2 раза быстрее Hardhat. В нашей практике фаззинг выявил в среднем 8,5 уязвимостей на проект.

Процесс: от аудита к отчёту

  1. Анализ архитектуры (2–3 дня). Изучение кода, выделение критических функций и state переменных. Определение инвариантов совместно с заказчиком.
  2. Написание фаззеров (5–7 дней). Разработка тестовых контрактов с инвариантами в Echidna и Foundry. Настройка sequence mutator и custom fuzzer для сложной логики (например, random swap paths).
  3. Запуск и анализ (5–10 дней). Прогон минимум 50 млн тестовых случаев. Для каждого failure: дебаг, классификация (Critical/High/Medium). Повторный прогон после исправлений.
  4. Отчёт и рекомендации (3–5 дней). Документ с детальным описанием всех найденных проблем, последовательностью транзакций и кодом исправлений. Оценка остаточного риска после фиксов.
Этап Длительность Выполнение
Анализ 2-3 дня Определение инвариантов
Разработка фаззеров 5-7 дней Кодирование тестов
Прогон 5-10 дней 50 млн тестовых случаев
Отчёт 3-5 дней Документация и рекомендации
Пример фрагмента отчета

ID: INV-001 | Severity: High Описание: Нарушение инварианта totalSupply == sum(balances) после вызова withdraw с reentrancy. Sequence: addLiquidity(100, 200) -> transferFrom(...) -> withdraw(50) -> withdraw(50) с reentrancy callback. Рекомендация: Использовать Checks-Effects-Interactions pattern.

Что входит в работу

  • Настройка окружения (Docker, Foundry, Echidna).
  • Определение и кодирование 10–30 инвариантов.
  • Прогон фаззера с автоматическим сбором failure.
  • Ручная верификация каждого failure (не ложное срабатывание).
  • Консультация по исправлению уязвимостей.
  • Итоговый отчёт в PDF.

Почему фаззинг стоит заказывать у нас?

5+ лет опыта в разработке смарт-контрактов на Solidity и Rust. 50+ проектов прошедших аудит, включая DeFi протоколы со значительным TVL. Собственная методология комбинирования статики, фаззинга и формальной верификации. Гарантия: мы не закрываем аудит, пока не найдём хотя бы одну подтверждённую уязвимость, или вернём деньги (условия оговариваются). Экономия на последующих исправлениях может достигать сотен тысяч долларов.

Сроки и стоимость

От 15 до 30 рабочих дней в зависимости от объёма кода и количества контрактов. Стоимость рассчитывается индивидуально — мы не называем цену вслепую. Напишите нам, оценим ваш проект за 1–2 дня.

Типичные ошибки при фаззинге и способы их предотвращения

  • Перегенерация state: если фаззер не умеет вызывать функцию с разными параметрами, он застрянет в одном сценарии. Решение — использовать sequence fuzzer.
  • Отсутствие проверки oracle price: фаззер может подавать любые цены, но если они выходят за допустимый диапазон, контракт должен отклонять их. Многие протоколы пренебрегают этим.
  • Игнорирование gas limit: некоторые инварианты выполняются только при определённом расходе газа. Фаззер должен варьировать gas limit.

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