スマートコントラクトの緊急停止の作り方|全部止めない理由・鍵の分け方・再開手順

コラム

/約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つ読んで、その場で自動停止すれば、すばやく反応できます。ただ、誤った価格やオラクルの障害が、そのまま停止の引き金になります。

    自動化するなら、次のものを組み合わせます。

    • 複数の観測、時間の幅、最大の変化率
    • 発動した後に、同じ処理を繰り返さない仕組み

    ボットが再開まで自動で行わない構成が基本です。自動停止がサービス妨害に悪用された場合に、損失がどこまで出るかも見積もります。

    再開してよいかを、確かめられる条件で決める

    「原因を直した」「問題なさそう」は、チェーン上では確かめられません。担当者によって判断も揺れます。そこで再開の条件を、変更内容、状態の照合、権限の照合、試験の結果、再開時の上限、監視期間に分けます。

    1. どこまで被害が出たかを一覧にする:対象のチェーン、コントラクト、資産、ブロックの範囲、失敗した操作、確定していないトランザクションを書き出します。
    2. 原因になった入口をふさぐ:弱点のある関数、オラクル、ブリッジ、管理用の鍵、フロントエンドなど、止めた理由に合った修正か隔離を確かめます。
    3. 残高や債務が合っているかを確かめる:供給量、残高の合計、担保、債務、準備資産、nonce、処理済みのメッセージを、別のデータ源と突き合わせます。
    4. 権限が承認どおりかを確かめる:停止担当、復旧、管理者、アップグレードの権限、Proxyの実装コントラクト、タイムロックの待ち時間が、承認済みのリリース記録と一致するかを見ます。
    5. 本番に近い状態で通して試す:メインネットのフォーク環境などで、攻撃・誤検知・修正・停止中の操作・段階的な再開・再停止を通します。
    6. 小さく再開する:少額、許可リスト、リスクの低い操作から始めます。時間の区切りごとに不変条件を満たしたときだけ、範囲を広げます。

    再開に要る署名の数を停止と同じにすると、乗っ取られた停止用の鍵だけで、止めた直後に再開できてしまいます。だから同じにはしません。逆に、再開を全員一致だけにすると、署名者が欠けたときに永久に止まりかねません。

    ふだんから、署名者の欠け、チェーンの混雑、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と本番実装に落とし込みます。停止担当・復旧・アップグレードの鍵を分けた構成を、貴社の署名体制に合わせて組み立て、停止から段階解除までを演習で確かめます。法的な判断が絡む停止義務や資産の凍結は、弁護士と連携して進めます。技術と運用の境界を具体化したい場合はお問い合わせフォームからご相談ください。

    主要参考資料

    資料の確認日と注意

    主な仕様・公式実装は、2026年9月24日に確認し直した内容です。停止義務や資産凍結など法的な判断が関わる場合は、社内の法務担当や弁護士へ確認してください。

    お問い合わせ

    どんなフェーズからでも、お持ちのアイデアや企画をもとにご提案可能です。
    まずはお気軽にご相談下さい!