TGStat
TGStat
Введите текст для поиска
Расширенный поиск каналов
  • flag Russian
    Язык сайта
    flag Russian flag English flag Uzbek
  • Вход на сайт
  • Каталог
    Каталог каналов и чатов Региональные подборки Тематические подборки Платные каналы Поиск каналов
    Добавить канал/чат
  • Рейтинги
    Рейтинг каналов Рейтинг чатов Рейтинг публикаций
    Рейтинги брендов и персон
  • Аналитика
  • Поиск по публикациям
  • Мониторинг Telegram
  • Продвижение
    Реклама через Яндекс Бизнес Реклама в каналах через TGStat Agency Реклама на сайте TGStat.ru
Из Solidity в AI и дальше

21 Nov 2023, 12:22

Открыть в Telegram Поделиться Пожаловаться

Foundry с 0. Часть 27

Следующие пару дней мы посвятим теме invariant тестов. Сейчас о них много, кто говорит, но крайне мало команд используют в своей работе. А все из-за того, что этот паттерн, возможно, для некоторых разработчиков несколько сложен для понимания и написания.

Для начала поговорим о том, что вообще такое инварианты.

Я сам, давно, потратил некоторое время, пытаясь усвоить это определение. И то, что практически в каждом описании, которое я встречал, было "ивариант - это некоторое условие, которое всегда должно оставаться неизменным..." не давало мне полного представления.

Ну вот есть у нас переменные, есть константы, есть immutables, а как установить инвариант? Как его прописать в коде?

Забавно было понять для себя, что инварианты появляются сами собой при написании проекта. Ну, в том смысле, что для каждого протокола они могут быть и одинаковыми и разными. И заранее определить количество инвариантов в своем будущем проекте крайне сложно. К слову сказать, их может и не быть вовсе!

Попробую объяснить, что такое инвариант на простом популярном примере.

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

Это такое условие в нашем коде, которое всегда должно оставаться одинаковым. Вне зависимости от действий пользователя: перевода, сжигания и т.д.

Другим инвариантом, например, может быть условие, что каждый ваш конкретный пользователь токена не может пересылать больше, чем у него есть на балансе.

Или, в случае nft, что, например, у пользователя может быть только один токен на счету и не больше.

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

Теперь давайте поговорим об инвариант тестах в Foundry на простом примере. У нас есть небольшой контракт:

contract InvariantIntro {
bool public flag;

function func_1() external {}
function func_2() external {}
function func_3() external {}
function func_4() external {}

function func_5() external {
flag = true;
}
}

Представим, что переменная flag всегда должна оставаться false не смотря ни на что.

И небольшой тест к нему:

contract IntroInvariantTest is Test {
InvariantIntro private target;

function setUp() public {
target = new InvariantIntro();
}

function invariant_flag_is_always_false() public {
assertEq(target.flag(), false);
}
}

Мы как обычно создаем переменную объекта нашего контракта и в setUp() подключаем его. А вот дальше...

Помните перед каждым тестом нам требовалось прописывать название, начиная с test..., например testIncrement(), testBlaBlaBla() и т.д.?

С инвариант тестами теперь нужно писать ключевое слово invariant. Это покажет Foundry, что мы переходим на другой вид тестов.

Как вообще работают инвариант тесты?

Если в обычных тестах (unit test) мы хотели сделать проверку конкретной части кода, например, require, то перед каждым конкретным тестом выполнялась функция setUp() и разворачивался новый контракт и его состояние памяти обнулялось.

Каждый тест мы проводили "с чистого листа".

Также и в случае с фаззингом. Каждый отдельный тест - это обнуленное состояние контракта. Поэтому, в случае unit и fuzz тестов, можно встретить определение - stateless tests.

Инвариант тесты - это уже stateful tests, когда состояние контракта не обнуляется, а наоборот, запоминается для каждого последующего вызова функции.

Смотрите, что получается:

function invariant_flag_is_always_false() public {
assertEq(target.flag(), false);
}

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

389 0 2 2
Каталог
Каталог каналов и чатов Подборки каналов Поиск каналов Добавить канал/чат
Рейтинги
Рейтинг каналов Telegram Рейтинг чатов Telegram Рейтинг публикаций Рейтинги брендов и персон
API
API статистики API поиска публикаций API Callback
Наши каналы
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Почитать
Академия TGStat Исследование Telegram 2019 Исследование Telegram 2021 Исследование Telegram 2023
Контакты
Справочный центр Поддержка Почта Вакансии
Всякая всячина
Пользовательское соглашение Политика конфиденциальности Публичная оферта
Наши боты
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot