スマートコントラクトの緊急停止の作り方|全部止めない理由・鍵の分け方・再開手順
約14分で読めます
約14分
目次(タップで折りたたみ)
ある貸付サービスで、価格の異常が見つかりました。運用チームは、用意していた緊急停止のボタンを押します。コントラクトの機能は、すべて止まりました。
すると、利用者から問い合わせが相次ぎます。「返済したいのに、返済もできない」。借りた状態を立て直したい利用者が、何もできなくなっていたのです。
緊急停止のボタンで全部を止めると、かえって損をすることがあります。そこで最初に、2種類の操作を分けます。
- 続けると、損失が広がる操作
- 止めている間も、資産を安全な側へ動かすために残す操作
次に、止める権限・再開する権限・中身を変える権限を、別々の鍵に持たせます。1つの鍵に集めると、その鍵が乗っ取られたとき、止めた直後に再開や変更までされてしまうからです。
最後に、再開は確かめられる条件を満たしたときだけ、少しずつ進めます。一気に戻すと、誤検知だったのか、直っていないまま再発したのかを見分けられないからです。
以下では、EVM(Ethereum Virtual Machine)系のスマートコントラクトについて、この3つを順に決めていきます。何をチェーン上で強制し、何をチェーンの外での判断や手順に残すかも、あわせて具体的にします。
この記事で使う言葉
- 不変条件:どんなときも成り立っていなければならない関係(例:残高の合計が供給量と合う)
- 清算:担保が足りなくなった借入を、担保を売って強制的に片付けること
- マルチシグ:複数人の署名がそろわないと実行できない仕組み
- タイムロック:決めた待ち時間が過ぎるまで、変更を実行できない仕組み
- EOA(外部所有アカウント):1つの秘密鍵で動かす、ふつうのアカウント
何を止めるかは、「止めないと損が広がる操作」から選ぶ
1つの停止フラグで全機能を止める作りは、単純です。ただ、止めることで利用者の損失が増える場合があります。
- 貸付:新しい借入は止める。でも返済まで止めると、利用者は借りた状態を立て直せない。
- ブリッジ:新しい入金を止めても、確かめ済みのメッセージの確定や、資産の回収まで一律に止めるべきとは限らない。
ethereum.orgのEmergency stopsも、2種類の関数を分けています。停止中に拒否する関数(例:deposit)と、停止中だけ使える緊急出庫のような関数(例:emergencyWithdraw)です。
先に関数の一覧を作るのではありません。「続けると損失が増える操作」と「止めている間も資産を安全な側へ動かす操作」を見分けるところから始めます。
ただし、返済や償還も、障害の原因によっては安全とは限りません。出金や償還なら、取り付け騒ぎや会計の食い違いを招くこともあります。障害の原因と不変条件を確かめて、続けてよいかを決めます。操作ごとの既定の動きは、たとえば次のようになります。
| 操作 | 異常時の既定動作 | 主な判断根拠 |
|---|---|---|
| 新規の預入・発行(deposit / mint) | 停止候補 | 新たな資産・債務を危険な状態へ入れない |
| 借入・レバレッジ(borrow) | 停止候補 | 債務とプロトコル損失の拡大を抑える |
| 出金・償還(withdraw / redeem) | 条件付き継続 | 利用者の退避経路。ただし取り付け騒ぎ(bank run)や会計の不整合も評価 |
| 返済・取消(repay / cancel) | 原則継続 | リスクを減らす操作を残す |
| 清算・決済(liquidation / settlement) | 方式別 | 価格異常時は誤清算、停止時は不良債権増加の双方がある |
| 管理設定の変更・アップグレード | 別経路 | 緊急用の鍵による恒久変更を防ぐ |
止めている間に確かめる不変条件は、後半の「実装する人向けの詳細」にまとめています。
実例もあります。Compound IIIの公式Cometの実装は、供給、送金、出金、清算の取り込み、担保の買い取り(supply、transfer、withdraw、absorb、buy)を、別々のフラグで止められます(CometWithExtendedAssetList.sol)。そのまま使うひな形ではありません。操作ごとの影響の範囲を、コードで表せることを示す例です。
価格オラクルの異常に限った、操作別の止め方はオラクル障害のフォールバックとサーキットブレーカーで扱っています。
止めるのはすぐに、戻すのは少しずつ
異常を見つけた後、一度に元の運転へ戻したとします。そこで再び異常が出ても、最初の検知が誤りだったのか、直っていないまま再発したのかを見分けられません。
そこで状態を、少なくとも3つ用意します。通常運転(ACTIVE)、停止(PAUSED)、限定再開(RECOVERY)です。
- 通常運転 → 停止:閾値を超えた、または障害が確かめられたとき
- 停止 → 限定再開:原因が分かり、修正かパラメータが確かめられたとき
- 限定再開 → 通常運転:少額の上限、監視期間、不変条件をすべて満たしたとき
- 限定再開 → 停止:再発した、または不変条件が破れたとき
限定再開では、利用者、資産、金額、時間あたりの件数を絞ります。先に返済・取消・少額の出庫を戻し、新しい受け入れやリスクの高い操作は後にします。
状態を変えるたびに、チェーン上に記録(イベント)を出します。理由を示す障害管理番号や、公開文書のハッシュを一緒に残せば、誰がいつ何を変えたかを追いやすくなります。ただし、機密にすべき弱点の詳細や個人情報は、チェーン上に置きません。
止める鍵・戻す鍵・変える鍵・資金を動かす鍵を分ける
OpenZeppelin Contracts 5.xのPausableは、一時停止用の部品です。ただし、誰が止めたり戻したりできるかは、使う側が決めなければなりません(Pausable API)。部品を組み込んだだけでは、権限の設計も再開の手順もできあがりません。
役割ごとに、できることとできないことを分けます。
| 役割 | 許可する操作 | 許可しない操作 |
|---|---|---|
| 監視・自動化ボット | 証跡収集、提案、通知 | 単独での状態変更 |
| 停止担当(Pause guardian) | 定義済みの機能を安全側へ停止 | 解除、アップグレード、送金、ロール追加 |
| 復旧委員会 | 段階解除、上限変更 | 任意の実装への差し替え |
| ガバナンス・タイムロック | アップグレード、ロール・待機時間の変更 | 検知直後の単独即時実行 |
| 運用担当 | 運用手順書(runbook)の実行、連絡、オフチェーンでの停止 | 署名権限外のオンチェーン変更 |
待ち時間も役割で違います。ボット(証跡収集・提案・通知)は即時、停止担当は即時か短時間で動きます。復旧委員会は複数の承認と冷却期間を経て、ガバナンスはふだんの変更と同じ待ち時間を経て動きます。役割ごとの統制は、後半の表にまとめました。
Aaveは、Protocol Emergency GuardianがAaveの各市場でEMERGENCY_ADMINロールを持つと説明しています(Aave Community)。大事なのは名前ではありません。緊急時に動く人の権限を、安全な側の操作だけに絞ることです。停止、凍結(freeze)、悪意ある提案の取り消しなどです。そして、ふだんの管理の変更とは分けておきます。
停止の権限を1つのEOAに持たせると、速く動けます。その代わり、鍵が漏れたり担当者が不在だったりすると、そこが唯一の弱点(単一障害点)になります。
マルチシグにする場合は、運用の要件に次のことまで含めます。
- 署名者と、何人の署名で動くか(閾値)
- 地域・端末の分散、代わりの署名者、鍵の定期的な交換
- 署名画面に出ない呼び出しの中身(calldata)を、別の手段で読み解くこと
署名者全員が同じチャット、同じ端末管理、同じクラウドに頼っていれば、形の上では複数の承認でも、障害の影響は分かれません。マルチシグとタイムロックの権限の組み方そのものはマルチシグ・タイムロックによる本番権限管理で扱っています。
チェーン上で強制することと、人が判断することを分ける
チェーン上に置くのは、実行時に機械的に確かめられる条件です。誰がどの関数を呼べるか、どの状態で拒否するか、上限、待ち時間、期限、状態の変化の記録などです。
一方、次のことはチェーンの外で扱います。異常かどうかの調査、法的・事業的な停止の判断、利用者への連絡、弱点の詳細、復旧試験の記録です。承認した結果だけを、署名かハッシュでチェーン上の操作に結びつけます。両者の対応は、後半の表にまとめています。
リリース記録の作り方と、承認した変更と実行結果の照合はスマートコントラクトのリリース管理で詳しく扱っています。
自動で止める作りにも注意が要ります。オラクルの値を1つ読んで、その場で自動停止すれば、すばやく反応できます。ただ、誤った価格やオラクルの障害が、そのまま停止の引き金になります。
自動化するなら、次のものを組み合わせます。
- 複数の観測、時間の幅、最大の変化率
- 発動した後に、同じ処理を繰り返さない仕組み
ボットが再開まで自動で行わない構成が基本です。自動停止がサービス妨害に悪用された場合に、損失がどこまで出るかも見積もります。
再開してよいかを、確かめられる条件で決める
「原因を直した」「問題なさそう」は、チェーン上では確かめられません。担当者によって判断も揺れます。そこで再開の条件を、変更内容、状態の照合、権限の照合、試験の結果、再開時の上限、監視期間に分けます。
- どこまで被害が出たかを一覧にする:対象のチェーン、コントラクト、資産、ブロックの範囲、失敗した操作、確定していないトランザクションを書き出します。
- 原因になった入口をふさぐ:弱点のある関数、オラクル、ブリッジ、管理用の鍵、フロントエンドなど、止めた理由に合った修正か隔離を確かめます。
- 残高や債務が合っているかを確かめる:供給量、残高の合計、担保、債務、準備資産、nonce、処理済みのメッセージを、別のデータ源と突き合わせます。
- 権限が承認どおりかを確かめる:停止担当、復旧、管理者、アップグレードの権限、Proxyの実装コントラクト、タイムロックの待ち時間が、承認済みのリリース記録と一致するかを見ます。
- 本番に近い状態で通して試す:メインネットのフォーク環境などで、攻撃・誤検知・修正・停止中の操作・段階的な再開・再停止を通します。
- 小さく再開する:少額、許可リスト、リスクの低い操作から始めます。時間の区切りごとに不変条件を満たしたときだけ、範囲を広げます。
再開に要る署名の数を停止と同じにすると、乗っ取られた停止用の鍵だけで、止めた直後に再開できてしまいます。だから同じにはしません。逆に、再開を全員一致だけにすると、署名者が欠けたときに永久に止まりかねません。
ふだんから、署名者の欠け、チェーンの混雑、RPCの停止、ハードウェアウォレットの故障まで演習します。代わりの手段が、何でもできる裏口になっていないかも確かめます。
緊急停止を付けるべきか、付けないべきか
緊急停止は、未知の弱点が見つかったときに対応の時間を稼ぐ手段です。弱点そのものを防ぐものではありません。そして、新しいリスクも持ち込みます。
- 権限の乗っ取りによるサービス妨害、検閲
- 誤った停止、永久停止
- 止めている間の価格変動、清算の遅れ、ほかのプロトコルとの食い違い
止められるという事実は、利用者と連携先に開示します。何を止められるか、誰が管理するか、最長でどれだけ止まるか、どう再開するか、変更の履歴も含めます。
停止機能を付けない判断もありえます。たとえば次のような場合です。
- 資産を持たない単純なコントラクト
- 止めても安全な状態に移れない、完全に自律したプロトコル
- 停止権限の管理者を長く維持できない仕組み
その場合も、代わりの手を用意します。変更できないコード、移行先、フロントエンドの停止、利用者への警告、外部との連携の切断です。
逆に、「念のためオーナーが全部止められる」ようにしておく作りは、何を止め、誰が再開の責任を持つかを決めないまま残します。だから、それは付ける理由になりません。
導入前のチェック
- 操作ごとに、損失が広がる速さと、止めることの副作用を比べた
- 返済・取消・安全な退避など、止めている間も残す操作を決めた
- 停止担当に、再開、アップグレード、資金移動の権限を与えていない
- 状態の変化、記録、期限、上限をチェーン上で強制する
- 再開の条件を、状態の照合、試験、複数の承認、小さな再開に分けた
- ボット、オラクル、マルチシグ、RPC、署名者の欠けを含む演習をした
- 権限の一覧と停止の履歴を見張り、利用者に開示できる
監視・オンコール・証拠保全まで含む全体の設計はスマートコントラクト本番運用、ふだんの変更と緊急時の手段の分け方はスマートコントラクトのアップグレード設計で扱っています。停止権限の分け方と再開の手順は、この記事がいちばん詳しく扱っています。
実装する人向けの詳細
ここからは、コントラクトと運用の仕組みを実際に組む開発者向けの補足です。
止めている間に確かめる不変条件
- 新規の預入・発行:受け入れた額が増えていない
- 借入・レバレッジ:新しい債務が作られていない
- 出金・償還:担保・準備資産・上限を破らない
- 返済・取消:債務か、確定していない注文だけが減る
- 清算・決済:参照価格の鮮度と債務の総額
- 管理設定の変更・アップグレード:ロール、実装コントラクト、待機時間が変わっていない
役割ごとの待機時間と統制
| 役割 | 待機時間 | 主な統制 |
|---|---|---|
| 監視・自動化ボット | 即時 | 複数のデータ源、実行回数の上限、人による確認 |
| 停止担当(Pause guardian) | 即時または短時間 | マルチシグ、対象の限定、使用期限 |
| 復旧委員会 | 複数承認+冷却期間 | 根拠資料、試験結果、監視期間 |
| ガバナンス・タイムロック | 通常変更の待機時間 | 提案の公開、取消、実行後の照合 |
| 運用担当 | 手順による | 職務分離、操作ログ、演習 |
Pausableの部品と、呼べる人の決め方
PausableはwhenNotPausedとwhenPausedの修飾子を提供します。一方、内部関数の_pauseと_unpauseを誰が呼べるかは、使う側で定義します。
チェーン上とチェーンの外の対応
| オンチェーンで強制 | オフチェーンで判断・保持 | 接続点 |
|---|---|---|
| ロール、関数セレクタ、状態、上限、待機時間 | 警報の信頼度、事業への影響、障害の深刻度 | 承認済みの停止トランザクション |
| 停止・解除のイベント、実行者、ブロック時刻 | 根拠ログ、通信記録、利用者への通知 | 障害管理番号・公開資料のハッシュ |
| 解除可能時刻、閾値による承認、再開時の上限 | フォーク環境での試験、監査、照合、責任者の承認 | リリース記録(release manifest)のハッシュ |
チェーン上で強制する条件には、nonceも含めます。
コードの概念例
止める役と、限定再開に入れる役を、別の鍵(ロール)にする例です。
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant RECOVERY_ROLE = keccak256("RECOVERY_ROLE");
function pauseDeposits() external onlyRole(PAUSER_ROLE) {
depositsPaused = true;
emit FunctionPauseChanged("DEPOSIT", true, msg.sender);
}
function enterRecovery(uint256 dailyLimit)
external
onlyRole(RECOVERY_ROLE)
{
require(depositsPaused, "not paused");
require(dailyLimit <= approvedRecoveryCap, "cap exceeded");
recoveryLimit = dailyLimit;
recoveryMode = true;
}
上は概念の例で、監査済みのコードではありません。実装では、外からの入口だけを守っても足りません。次の道から、止めた機能に届かないかを確かめます。
- 内部関数、まとめて実行する入口(batch、multicall)
- delegatecall、別のProxy、ブリッジからのコールバック
- 署名付きメタトランザクション(第三者が代わりに送る署名済みの操作)
OpenZeppelinのAccess control guideが示すように、ロールごとに対象の関数を割り当てます。強い既定の管理者ロールの管理にも、遅延と二段階の移管を使います。
保護の漏れと、再開の抜け道を試す
| 試験 | 注入する失敗 | 合格条件 |
|---|---|---|
| 権限 | ロールなし、単独署名、期限切れ、別チェーン、署名者の交代中 | 不正な操作を拒否し、正規の経路は利用できる |
| 範囲 | 各フラグの全組合せ、まとめて実行する経路、コールバック、別の入口 | 停止対象だけが全経路で拒否される |
| 安全側の操作 | 停止中の返済、取消、限定出庫 | 新たなリスクを増やさず完了する |
| 解除 | 未修復、未照合、冷却期間前、上限超過 | ACTIVEへ遷移できない |
| 再停止 | RECOVERY中の異常再発 | 即時にPAUSEDへ戻り、二重処理しない |
| 可用性 | RPC、署名者、ボット、監視基盤の停止 | 定めた目標復旧時間(RTO)内に代替経路で停止できる |
XTELAができること
私たちは、損失経路の洗い出しから、機能別の停止フラグ、権限表、状態遷移、監視の条件、フォーク環境での試験、運用手順書までを設計し、PoCと本番実装に落とし込みます。停止担当・復旧・アップグレードの鍵を分けた構成を、貴社の署名体制に合わせて組み立て、停止から段階解除までを演習で確かめます。法的な判断が絡む停止義務や資産の凍結は、弁護士と連携して進めます。技術と運用の境界を具体化したい場合はお問い合わせフォームからご相談ください。
主要参考資料
- ethereum.org: Smart contract security — Emergency stops(2026年9月24日確認)
- OpenZeppelin Contracts 5.x: Pausable API(2026年9月24日確認)
- OpenZeppelin Contracts 5.x: Access Control(2026年8月13日確認)
- Compound III: CometWithExtendedAssetList.sol(2026年9月24日確認)
- Aave Community — Guardian roles(2026年9月24日確認)
資料の確認日と注意
主な仕様・公式実装は、2026年9月24日に確認し直した内容です。停止義務や資産凍結など法的な判断が関わる場合は、社内の法務担当や弁護士へ確認してください。