T21 · オラクル
チェーンは、外部の価格をどう知るか?
- 練習する能力
- BuilderProtocol Literacy
- 実践
- 価格フィードを接続し、更新頻度と乖離のしきい値を記録して、データが古くなった場合のコントラクトの動きを模擬する。
- AI Lab
- AI に現物プール価格をオラクルに使うリスクを分析させ、操作に必要な資金を自分で計算する。
現実の問題
トークン X を担保にステーブルコインを貸すコントラクトを作ります。
担保価値を、借入額の一定倍以上に保つのが中心です。
uint256 collateralValue = amount * getPrice(token) / 1e18;問題は getPrice です。チェーンのコントラクトは HTTP を送れず、取引所価格も読めず、チェーンにあるデータしか読めません。
AMM プールなら、準備金比が無料で即時の価格になり、第三者も不要です。
こう書きました。
function getPrice(address token) public view returns (uint256) {
(uint256 r0, uint256 r1) = pair.getReserves();
return r1 * 1e18 / r0; // この一行の価値はいくらか?
}T19 では、定積価格を二倍へ動かすには、片側準備金の約 41% が必要でした。
片側 100 万なら 41 万で、安くは見えません。
しかし攻撃者は、41 万を所有する必要がありません。一取引で借り、価格を動かし、過剰借入し、価格を戻して返済できます。同じブロックで、元本はほぼ不要、費用は手数料だけです。
その一行は、コントラクトの全資金に値します。
思考実験
窓がなく、扉に細い隙間だけある部屋へ閉じ込められた人を考えます。
外の金価格に応じて訪問者へ支払いますが、自分では見えません。
隙間から数値の紙が入ります。
従う前に、四つの問いが必要です。
第一は、誰が書いたかです。 誰でも入れられるなら最後の人が価格を決めます。特定の筆跡だけを認めます。
第二は、いつ書いたかです。 時刻なしでは、5 秒前と 3 日前を区別できず、変動時に危険です。
第三は、前回との差です。 100 から 10000 なら、相場でなく悪ふざけか、情報源の故障かもしれません。
第四は、長く紙が来なければ何をするかです。 最後を使うか、止めるか、保守的に動くか?
普遍的な正解はなくても、全問にコード上の明確な答えが必要です。 省けば最も危険な既定値を選びます。
AMM は、誰でもお金で書き換えられる紙で、時刻すらありません。
あなたが決める
借貸用の価格源を選びます。
結果を観察する
比較します。
| 情報源 | 操作費用 | 遅延 | 依存先 | 失敗の形 |
|---|---|---|---|---|
| 瞬間価格 | 非常に低い、借りた資金で可能 | なし | なし | 一取引で書き換え |
| 同プールの平均 | 中、維持が必要 | 窓の長さ | なし | 急変に遅れる |
| 外部更新 | 高い、複数提供者の侵害 | 秒〜分 | 外部コントラクトと運営 | 停止、遅延、境界固定 |
| 複数源の中央値 | 最大 | 遅い源次第 | 複数外部 | 一源失敗の処理 |
費用を一般化します。
安全な情報源という絶対値はなく、操作費用が抽出価値より高い情報源があります。
比率が役立ちます。
安全余裕 = 価格操作費用 / 攻撃者が取り出せる最大額
1 未満 → 時間の問題で攻撃される
1 付近 → 相場や Gas の変化で逆転する
1 より大 → 暫定的に安全だが、両側が時間で変わる分母は総 TVL でなく、誤価格で引き出せる最大額です。 総量の 1% の資産でも、全ステーブルプールを借りられるなら分母は全体です。
分子も分母も定数ではありません。
流動性移動で操作費用が減り、担保率増で抽出額が増えます。コードを変えなくても、半年後に安全でなくなり得ます。
モデルを作る
連鎖を見ます。
- 実際の市場価格
- 情報提供者
- オフチェーン集約
- チェーンの価格コントラクト
- 自コントラクトの判断
| 環 | 失敗 | 最後でできること |
|---|---|---|
| 市場 | 流動性枯渇、公正価格なし | 関連操作の停止 |
| 提供者 | 停止、誤値 | 集約の中央値に依存 |
| 外の集約 | 全体停止、遅延 | 時刻確認 |
| 価格コントラクト | 更新、停止、境界値 | 範囲と合理性の確認 |
| 自コントラクト | 未確認で使用 | 本章全体 |
四パラメータは設定可能です。源、資産、チェーンで異なるので、例の桁だけを示し、実数は Lab で調べます。
| パラメータ | 意味 | 典型の桁 | 必要な理由 |
|---|---|---|---|
| Heartbeat | 最大更新間隔 | 分〜時間 | 期限設定の基準 |
| Deviation | 早期更新の価格差 | 0 数%〜数% | 静穏時の古さ |
| Decimals | 値の桁数 | 通常 8 か 18 | 誤ると桁違い |
| 値の境界 | 最大、最小 | 資産別 | 極端相場で偽価格を返す |
境界保護では、実価格が範囲外へ出ても、正常な形式で境界値を返します。時刻だけ確認すると、誤価格で動き続けます。
四確認をコードにします。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPriceSource {
// 価格と生成時刻
function latest() external view returns (int256 price, uint256 updatedAt);
}
contract PriceGuard {
IPriceSource public source;
uint256 public maxStaleness; // 最大の古さ。通常 heartbeat の数倍
uint256 public minPrice; // 合理的な下限
uint256 public maxPrice; // 合理的な上限
uint256 public maxJumpPercent; // 前回信頼価格からの最大変動率
uint256 private lastGoodPrice;
uint256 private lastGoodAt;
error StalePrice(uint256 age);
error PriceOutOfRange(uint256 price);
error PriceJump(uint256 from, uint256 to);
function readPrice() public view returns (uint256) {
(int256 raw, uint256 updatedAt) = source.latest();
// 1:ゼロと負値を拒否
if (raw <= 0) revert PriceOutOfRange(0);
uint256 price = uint256(raw);
// 2:期限切れを拒否
uint256 age = block.timestamp - updatedAt;
if (age > maxStaleness) revert StalePrice(age);
// 3:集約境界へ接した値を拒否
if (price <= minPrice || price >= maxPrice) revert PriceOutOfRange(price);
// 4:前回から閾値を超える変動を拒否
if (lastGoodPrice > 0) {
uint256 hi = price > lastGoodPrice ? price : lastGoodPrice;
uint256 lo = price > lastGoodPrice ? lastGoodPrice : price;
if ((hi - lo) * 100 > lo * maxJumpPercent) revert PriceJump(lastGoodPrice, price);
}
return price;
}
}第三が漏れやすく、第四には議論があります。
実際にも数十% の急変が起きます。 厳しすぎると最も必要な時に拒否し、清算を止め、不良債務を増やします。操作防止と停止防止の取引で、普遍的な値はありません。
さらに重要なのは、データが使えない時の動作です。三方式から明示的に選びます。
| 方針 | 動作 | 適する操作 | 代償 |
|---|---|---|---|
| 全巻き戻し | revert | 借入、担保引き出し | 清算も止まる |
| 縮退 | リスク減少のみ許可 | 返済、担保追加 | 全入口の分類が必要 |
| 代替価格 | 予備源か最後の信頼値 | 清算 | 代替も操作され得る |
全面 revert は誤りやすい方法です。 安全そうでも、急変で最も清算が必要な時に止めます。
通常は、リスク増加は新価格を要し、減少は価格なしでも許します。
何と呼ぶか
外部情報をチェーンへ持ち込む機構です。
技術より信頼の問題です。同履歴は検証できても、外の金価格は自分で検証できず、外部に信頼を置きます。
継続的に価格を書き込むコントラクトと運営です。
価格と生成時刻を読む必要があります。価格だけでは安全に使えません。
価格が変わらなくても更新する最大間隔です。
期限が短すぎれば誤検出、長すぎれば古値を許します。通常数倍にし、資産別に設定します。
前回からの変動で、予定前の更新を起こす幅です。
平穏時は周期、変動時は乖離で更新します。静穏時の古さは通常問題なく、変動時に追随するかが重要です。
一定窓の累積から求める平均です。
高めるのは精度より操作費用です。誤値を維持させる代わりに遅れ、窓は安全性と応答性の取引です。
取引で情報源を変え、依存コントラクトから利益を得ます。
多くはバグでなく、合法な取引の組み合わせです。動かし、対象を呼び、戻し、正しいコードの想定を回避します。
異常時に一部機能を止めます。
鍵は何を止めるかです。全部なら清算も止まり、増加だけなら自力回復を続けられます。
実践
コードの前に調査します。 主流と小規模の二資産で埋めます。
| 主流資産 | 小資産 | |
|---|---|---|
| 最大更新間隔 | ||
| 乖離閾値 | ||
| 桁数 | ||
| 24 時間の実更新数 | ||
| 最長更新間隔 | ||
| 上下界の有無と値 |
最後の二行は実データから統計し、文書を写しません。約束と実際が違うこともあります。
PriceGuard の四確認を実装します。非正値、期限、範囲、急変に、別のエラーを設けます。
フォークで読みます。 エクスプローラーと一致し、特に精度を確認します。8 を 18 と扱うと十桁違っても、エラーが出ません。
制御可能なスタブで四故障を試します。
- 一時間前の時刻 → 期限切れ
- 0 → 非正値
- 上界 → 範囲外
- 前回の 10 倍 → 急変
区別できて第一層が完成します。
期限切れの動作を決めます。
借入、担保引き出し、返済、清算の各入口を分類し、テストします。
| 入口 | 期限切れ時 | 理由 |
|---|---|---|
| 借入 | ||
| 担保引き出し | ||
| 返済 | ||
| 清算 |
全部を巻き戻すなら、第三、第四を再考します。 返済と清算を拒否すると、不良債務を増やします。
操作費用を計算します。中心の演習です。
瞬間価格の版で計算します。
片側深度 D、貸せるステーブル額 L、担保率 c(例えば 0.75)
1. X を m 倍に動かす資金は約 D * (sqrt(m) - 1)
2. 実価値 V の X から借りられるのは min(V * m * c, L)
3. 元本は同取引で借り返せてほぼ不要。
実費 = 二交換手数料 + Gas + フラッシュローン費用
≈ D * (sqrt(m) - 1) * 0.6% + その他
4. 純利益 = 2 - 3 - 担保の実価値
5. 安全余裕 = 3 / 2D = 100 万、1000 万、5000 万、m = 2、L = 500 万で試します。
第一の安全余裕は 1 よりはるかに小さくなります。 記録し、新源導入時には比率を先に求め、許容する倍数未満なら公開しません。
L = 500 万なら、攻撃が割に合わない最低深度はいくらですか? 安全に担保へ入れる最低流動性です。
テストネットとローカルフォークだけで、実資金は使いません。 卒業課題でも第五段階の安全性確認後です。
AI Lab
三段階で問いかけます。
第 1 段階:
定積の瞬間準備金比を担保価格へ使い、深度 D、借出可能 L とします。
全攻撃工程と、代入可能な費用式を示してください。
第 2 段階:
同プールの N ブロック TWAP へ変え、費用を再導出してください。
倍率と、その前提も示してください。
第 3 段階:
残る五つのリスクを、確率と損失の積で並べ、検出信号を示してください。第一の概要は答えられても、費用で間違えます。41 万元本を費用とし、借り返せることを漏らせば、百倍程度の差が安全判断を変えます。
第二は「より安全」だけでなく倍率が必要です。各ブロックの費用と、裁定者の逆取引を追問します。
第三が成果です。第五工程の表と照合し、未考慮の入口を探します。
具体値はすべてチェーンで確認します。 幻覚が起きやすく、もっともらしいので気付きにくいものです。
AI の回答後、自分で必ず検証すること
- 同取引で借り返せることを費用計算へ含め、必要元本だけを費用としない
- 平方根を含む式が、手計算と一致する
- 抽出価値を総 TVL でなく、誤価格で実際に借りられる額にする
- TWAP の費用増加倍率を自分で導ける
- 生成時間が窓内の操作費用を変えることを扱う
- 更新間隔、閾値、担保率の具体値を、記憶でなくチェーンで照合した
実例
大口を一取引で借り、価格を上げ、小担保で過剰なステーブルを借り、戻して返済する同じ形です。
コントラクトにバグはありません。 呼び出しも送金も確認も正常で、回避したのは価格が信頼できるという想定です。
大きな損失を生む経済攻撃でも、情報源アドレスの変更で防げることがあります。
情報源は正常でも、市場が小さく、どこでも操作が安価でした。
実市場で買い上げ、主流資産を借り、担保を放棄すると、換金不能の資産と不良債務が残ります。
正しい価格だけでなく、その背後の深度も安全性です。 担保適格性は市場深度と上限に依存します。
混雑、提供者故障、廃止で停止しました。
時刻未確認では、急落時も清算すべきポジションが健康に見えます。再開時に一斉に債務超過となります。
時刻確認と、桁ほど違い得る更新間隔を資産別に設定する理由です。
実価格が下界を下回っても、新時刻、正値、正形式で下界を返しました。
担保を過大評価して清算せず、調整時には既に債務超過でした。
新鮮さだけでなく合理性も確認します。 独立源との乖離で停止し、複数源の価値を異常検出に使います。
変数を一つ変える
応答が六倍速く、清算が適時になり、T22 の不良債務が減ります。
操作費用は窓にほぼ比例して減り、逆裁定を受ける時間も短くなるため、実際にはさらに大きく減り得ます。
守る額から窓を逆算します。 許容安全余裕を先に決めます。
30 ブロックが 6 分から 12 秒になり、ブロック数の窓なら、保護が静かに三十分の一になります。
時刻なら長さは同じでも、維持するブロックが増え、費用が増える可能性があります。
時間関連の設定は換算し直し、コピーしません。 跨チェーンの危険な想定です。
単一故障を減らし、能動的な異常信号を得ます。
一方の揺れでも止まり、可用性が思った以上に落ちます。
停止中も返済と担保追加を許し、最も緊張した時に自力救済を拒否しません。
費用の分子が下がり、抽出額は同じで、比率が 1 未満になります。
資産自体を動かしやすいため、完璧な情報源でも、実市場の買い上げを防げません。
抽出上限を操作費用未満へ制限します。データ源でなくパラメータの問題です。
持ち帰る問い
なぜ Blockchain が必要ですか?同履歴を保証しても、外部事実を自分では検証できません。オラクルが境界のインターフェースで、最も分散的でない部分です。
誰が流動性を提供しますか?深度は操作費用を決めます。市場深度は、価格源の安全予算です。 移動すればコードなしでも危険になります。
誰がリスクを負いますか?情報提供者でなく、準備金と LP です。価格を出す人は、誤価格の結果をほぼ負いません。
動機は何ですか?罰没可能な担保、評判、契約のどれで誠実にするか確認します。信頼の深さを決めます。
章末チェック
一取引で書き換えられ、資金も同取引で借り返せて、実費は手数料だからです。
不正確さより、攻撃者が入力を選べることが問題です。
ごく小さい影響額で、費用が全抽出価値を超える場合だけ例外です。借貸、永続、清算ではほぼありません。
開始点です。四つ必要です。
- 時刻確認と資産別の期限。
- 範囲確認で境界固定を防ぐ。
- 各入口のデータ不可時の動作を決め、一律 revert しない。
- 実更新の継続監視で、約束と実態を比較。
制御外の更新、停止、設定、廃止も監視します。
維持が必要で、裁定者の逆取引が各ブロックで資金を消費します。一回のずれの費用から、窓内の裁定回数を掛けたものへ増えます。
遅延は急落時の清算を遅らせ、不良債務になります。
両側に損失のある取引です。
最も必要な急変時に、清算と返済を凍結します。
悪いポジションを清算できず、救済の返済もできず、再開時に損失が増えます。
増加は新価格を必要とし、減少は価格なしでも許し、清算は明確な代替方針を持ちます。
唯一の正解はありません。次を確認します。
- 各価格利用の出所、更新周期、精度、期限を説明できるか?
- 各資産の安全比率を計算し、分母を総 TVL でなく最大抽出額にしたか?
- 停止、境界値、一時停止で各入口が何をし、テストがあるか?
- 深度半減で最初に失敗する設定と、監視通知は何か?
- 異なる速度のチェーンで再計算する値は何か?
第四が最も少なく、質を分けます。一度正しく設定して終わりでなく、時間で変わる安全状態として扱います。
要点
オラクルはプロトコルで最も一般的な単一障害点であり、攻撃の入口でもある。