Deep EVM #20: CI/CD для смарт-контрактов — тесты, газ-регрессия и безопасность
Engineering Team
Почему CI/CD критичен для смарт-контрактов
Смарт-контракты неизменяемы после деплоя. Ошибка в продакшене — это не «откатим и пофиксим», а потенциальная потеря миллионов долларов. CI/CD-пайплайн для блокчейн-проектов должен быть строже, чем для обычного софта, потому что цена ошибки несоизмеримо выше.
В этой статье мы построим полноценный пайплайн, который включает компиляцию, юнит-тесты, фазз-тесты, газ-регрессию, статический анализ безопасности и безопасный деплой.
Архитектура пайплайна
Оптимальный CI/CD-пайплайн для смарт-контрактов состоит из пяти этапов:
- Lint & Format — проверка стиля кода
- Compile — компиляция контрактов
- Test — юнит-тесты, фаззинг, инварианты
- Security — статический анализ, проверка зависимостей
- Deploy — контролируемый деплой с верификацией
Этап 1: Lint и форматирование
lint:
stage: lint
script:
- forge fmt --check
- solhint 'src/**/*.sol'
- forge build --sizes # Проверка размера контрактов
rules:
- if: $CI_MERGE_REQUEST_ID
Форматирование с forge fmt должно быть единообразным. Solhint проверяет паттерны безопасности: правильное использование external vs public, наличие NatSpec-документации, запрет tx.origin.
Этап 2: Компиляция и размер контрактов
EVM ограничивает размер контракта 24 576 байтами (EIP-170). Проверяйте это в CI:
forge build --sizes 2>&1 | while IFS= read -r line; do
size=$(echo "$line" | grep -oP '\d+\.\d+')
if [ "$(echo "$size > 24" | bc)" -eq 1 ]; then
echo "ERROR: Contract exceeds size limit: $line"
exit 1
fi
done
Этап 3: Многоуровневое тестирование
Юнит-тесты с покрытием
forge test --match-test "test_" -vvv
forge coverage --report lcov
Фаззинг
forge test --match-test "testFuzz_" --fuzz-runs 50000
Инвариантное тестирование
forge test --match-test "invariant_" --fuzz-runs 1000
Этап 4: Газ-регрессия
Газ-снепшоты позволяют отслеживать потребление газа между коммитами:
forge snapshot --check .gas-snapshot --tolerance 5
Толеранс 5% допускает незначительные колебания из-за оптимизаций компилятора, но поймает значительные регрессии. Если порог превышен — MR блокируется.
Для автоматического обновления базового уровня на main:
update-gas-snapshot:
stage: post-merge
script:
- forge snapshot
- git add .gas-snapshot
- git commit -m "chore: Update gas snapshot"
- git push
rules:
- if: $CI_COMMIT_BRANCH == "main"
Этап 5: Статический анализ безопасности
Slither
slither . --filter-paths "test/|script/" \
--exclude-dependencies \
--sarif results.sarif
Slither обнаруживает reentrancy, неконтролируемые делегат-коллы, некорректное использование tx.origin и десятки других паттернов уязвимостей.
Mythril
myth analyze src/Contract.sol --solc-json mythril.config.json
Mythril использует символическое выполнение для обнаружения уязвимостей, которые пропускает статический анализ.
Aderyn
aderyn . --src src/
Aderyn — новый инструмент от Cyfrin, специализирующийся на Foundry-проектах.
Безопасный деплой
Деплой смарт-контрактов должен быть контролируемым:
# Деплой с верификацией на Etherscan
forge script script/Deploy.s.sol:DeployScript \
--rpc-url $RPC_URL \
--broadcast \
--verify \
--etherscan-api-key $ETHERSCAN_KEY \
--slow # Ожидание подтверждения каждой транзакции
Важные принципы:
- Dry run перед деплоем:
--broadcastбез--verifyсначала - Мультисиг для критичных операций: используйте Safe (бывший Gnosis Safe)
- Timelock: задержка между предложением и исполнением
- Мониторинг после деплоя: подписка на события через Tenderly
Пример полного пайплайна GitLab CI
stages:
- lint
- build
- test
- security
- deploy
variables:
FOUNDRY_PROFILE: ci
lint:
stage: lint
script:
- forge fmt --check
- solhint 'src/**/*.sol'
build:
stage: build
script:
- forge build --sizes
artifacts:
paths: [out/]
test-unit:
stage: test
script:
- forge test --match-test "test_" -vvv
- forge coverage --report summary
test-fuzz:
stage: test
script:
- forge test --match-test "testFuzz_|invariant_" --fuzz-runs 50000
timeout: 30m
gas-check:
stage: test
script:
- forge snapshot --check .gas-snapshot --tolerance 5
allow_failure: false
security:
stage: security
script:
- slither . --filter-paths "test/|script/"
- aderyn . --src src/
deploy-testnet:
stage: deploy
script:
- forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC --broadcast --verify
when: manual
environment: staging
Заключение
CI/CD для смарт-контрактов — это не роскошь, а необходимость. Автоматизированный пайплайн с многоуровневым тестированием, газ-регрессией и статическим анализом безопасности — единственный надёжный способ обеспечить качество неизменяемого кода. Инвестируйте в инфраструктуру CI/CD сейчас — это окупится при первом же предотвращённом инциденте.