はじめに
ビットコインから続くブロックチェーンの10余年の歩みは「コード=資産」という新しい常識を生みました。しかし一度デプロイしたスマートコントラクトは簡単に修正できず、1行のバグが数億円規模の損失になる事例が後を絶ちません。LogRocket の「Complete guide to blockchain testing」は、ブロックチェーン特有のテスト課題と解決策を体系的に整理しています 。本記事ではその内容を深掘りし、ブロックチェーンテストの全体像と実践方法を解説します。
ブロックチェーンテストの全体設計
主要テストレイヤー
レイヤー | 目的 | 代表ツール |
---|---|---|
単体テスト | 関数単位のロジック検証 | Hardhat/Chai, Foundry/Forge, Brownie/PyTest |
統合テスト | 複数コントラクト間の相互作用 | Hardhat Network fork, Deployment scripts |
ノード/ネットワークテスト | コンセンサス、フォークハンドリング | Ganache, Anvil, Geth dev net |
セキュリティテスト | 再入可能性・整数演算・権限漏れ | Slither, Mythril, Echidna, Manticore |
性能テスト | TPS、Gas効率、スループット | Hardhat‑gas‑reporter, Hyperledger Caliper |
テストフェーズ
- テスト計画:脅威モデル作成、成功基準(KPI)を定義
- テストデザイン:シナリオ、エッジケース、ファズ入力を設計
- 実装:テストコード+モック+スタブを作成
- 実行:ローカル→テストネット→シミュレーション環境で段階的実行
- レポート:カバレッジ、Gas使用量、失敗ケースを可視化
単体テストを極める
Hardhat × Chai 基本例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
contract Counter {
uint256 public n;
function inc() external { n += 1; }
}
// test/Counter.ts
import { expect } from "chai";
import { ethers } from "hardhat";
describe("Counter", () => {
it("増分ロジック", async () => {
const Counter = await ethers.getContractFactory("Counter");
const c = await Counter.deploy();
await c.inc();
expect(await c.n()).to.equal(1);
});
});
npx hardhat test
で高速実行evm_snapshot/evm_revert
で状態ロールバックしテスト独立性を確保
Foundry で Fuzz & Invariant
import "forge-std/Test.sol";
contract CounterTest is Test {
Counter c;
function setUp() public { c = new Counter(); }
function testFuzz(uint256 x) public {
vm.assume(x < 1000);
uint256 before = c.n();
c.inc();
assertEq(c.n(), before + 1);
}
}
forge test --fuzz-runs 500
で500ケース自動生成。異常系を自動発見しやすい。
統合テストとフォークシミュレーション
Mainnet Fork
module.exports = {
networks: {
fork: {
url: "https://eth-mainnet.g.alchemy.com/v2/<API_KEY>",
forking: { blockNumber: 19000000 }
}
}
};
- 実際のUniswap V3コントラクトと自作戦略コントラクトをローカル統合
- 価格オラクルの再入攻撃など実戦さながらの検証が可能
Mock & Stub
- Chainlink AggregatorV3Interface をダミー実装し価格を固定
- ERC20 Permit をスタブして署名検証をスキップしガス測定に集中
セキュリティテストツールの活用
ツール | テスト種別 | 強み |
---|---|---|
Slither | 静的解析 | 20+ルール、CI統合容易 |
Mythril | バイトコードSWC検査 | SMTベースで深いパス探索 |
Echidna | Fuzz & Invariant | Solidityでプロパティ定義、見落とし低減 |
Manticore | 代数的実行 | 低レイヤー応用、複雑パス発見 |
例:Slither CI
run-slither:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Slither
uses: crytic/slither-action@v0.2.0
with:
truffle-version: 5.9.0
PRごとにセキュリティ静的解析を自動実行し、リスクを早期検知。
性能テストとガス最適化
Hardhat‑gas‑reporter
npm i --save-dev hardhat-gas-reporter
hardhat.config.js
require("hardhat-gas-reporter");
module.exports = {
gasReporter: { currency: "USD", coinmarketcap: "<KEY>" }
};
- 単体テストと同時にGasコスト一覧をMarkdown出力
- コスト重い関数を把握→Unchecked Math・Constant Caching で最適化
Hyperledger Caliper でTPS測定
- ノード数やBlockGasLimitを変化させ、Peak TPSとレイテンシを計測
- 結果をPrometheus + Grafanaで可視化し、Bottleneck分析
CI/CD パイプラインに組み込む
- Lint:Solhint / Prettier でスタイル統一
- Unit Test:Hardhat or Foundry with Coverage≥90%
- Security Scan:Slither & Mythril
- Fuzz:Echidna 1k runs (nightly)
- Gas Report:PRコメントに自動投稿
- Testnet Deploy:成功時のみSepoliaへ自動デプロイ
- Etherscan Verify:APIで自動検証し透明性を担保
現実のトラブル事例と教訓
- DAO Hack:再帰呼び出しを想定した単体テスト不備
- bZx Protocol:価格フィードモックが単一Oracleで多重価格を見落とし
- Sushi MISO:ステート変数初期化未テスト→一括NFT購入バグ
教訓:テストは「想定どおり動くか」より「想定外で止まるか」を検証することが重要。
まとめ
ブロックチェーン・スマートコントラクトのテストは「単体→統合→セキュリティ→性能」という多層防御が欠かせません。HardhatやFoundryでの高速テスト、Slither/Echidnaでの自動脆弱性検査、Caliperでの性能測定を組み合わせることで、品質と開発速度を両立できます。LogRocketが指摘するように、テスト漏れは開発者の信頼を一瞬で失います 。本記事で紹介したツールと手法をCI/CDに組み込み、デプロイ前に壊れないことを証明する文化をチーム全体で育てましょう。