最も単純な ETH の送金は固定で 21000 ガスを消費します(イエローペーパー付録 G の Gtransaction)。ERC-20 の送金の消費量はコントラクトの実装とスロットのコールド/ホット状態に依存し、通常は数万ガスという桁に収まります。これは桁の見積もりであり、実測値ではありません。これらの数値はプロトコルが定めるもので、変わるのはハードフォークのときだけです。一方、同じ 1 件のトランザクションで実際に何 ETH を支払うかは、数時間のうちに 10 倍以上変わりえます。前者は計量単位、後者は価格です。この二つの概念を分けておいて初めて、手数料の議論、スケーリングの語り、実行中止の挙動がはっきり語れます。
Gas の仕組みは同時に三つの役割を担います。計算と状態操作を統一された単位に換算すること、価格市場によって限られたブロック空間を配分すること、そして資源が尽きたときに確定したロールバックの境界を与えることです。以下ではこの三本の線に沿って展開します。固有コストと動的コストがそれぞれ何を含むか、コールド/ホットアクセスがどう価格設定されるか、トランザクションが無効、REVERT、out-of-gas の三種類の中止でそれぞれ何を返すか、EIP-1559 以降で base fee、priority fee、実行層の gas の境界がどこにあるか、そして Gas が DoS を価格付け可能にする一方で、価格設定を永遠に正しくはできないのはなぜかです。
Gas は計算とストレージの計量単位であり、コインの価格とは別物
Gas は抽象的な計量単位で、命令を 1 つ実行し、状態を 1 回読み書きし、メモリを 1 語拡張するのに消費する抽象的な資源量に対応します。その値はプロトコルの定数で与えられます。たとえば加算は 3 ガス(Gverylow)、ベルリン・アップグレード(ブロック 12,244,000、2021 年 4 月 15 日)以降の SLOAD のホットアクセスは 100 ガス、SSTORE で 0 から非ゼロへ書く場合は 20000 ガスです。これらの定数はハードフォークを通じてのみ変化し、市場の需給とは関係ありません。
Gas の価格は wei 建てで、よく使われる単位は gwei で、1 gwei は 10⁹ wei です。1 件のトランザクションの手数料は gas_used に effective_gas_price を掛けたもので、前者は実行過程が決め、後者は手数料市場とトランザクション署名の入札が共同で決めます。この掛け算の関係が、以降のすべての議論の土台です。
この二層を分けておくと、いくつか直接の利点があります。「ガスを下げる」を議論するときは、まず目標が gas_used を下げることなのか gas price を下げることなのかを確認します。前者は操作を減らすか、ストレージ配置を最適化するか、EVM の価格規則を変更する必要があり、後者はその時点の需要と手数料市場の形に依存します。EIP-1559 は後者をより予測可能にしましたが、どの命令の価格も変えていません。「手数料は誰に帰属するか」を議論するときは、base fee の部分はプロトコルによって焼却され、受け取り手が存在せず、priority fee だけがバリデータのアカウントに入ります。「トランザクションの上限」を議論するときは、gas_limit は容量の上限であって費用の上限ではなく、費用の上限は max_fee_per_gas で、両者は別のフィールドです。
固有コスト:実行が始まる前にすでに差し引かれる部分
各トランザクションはまず固有コスト(intrinsic gas)を支払い、これは EVM が最初の命令を実行する前に差し引かれます。イエローペーパーの式 (64) はこれを g₀ と表記します。21000 の固定の底値(Gtransaction)に、データフィールドのゼロバイトごとに 4 ガス、非ゼロバイトごとに 16 ガス(非ゼロバイトの価格は EIP-2028 によってイスタンブール・アップグレード、ブロック 9,069,000、2019 年 12 月 8 日に 68 から 16 へ下がりました)を加え、コントラクト作成のトランザクションならさらに 32000(Gtxcreate)を加えます。EIP-2930(ベルリン・アップグレード、ブロック 12,244,000、2021 年 4 月 15 日)以降は、access list の各アドレスに 2400 ガス、各ストレージキーに 1900 ガスです。EIP-3860(上海アップグレード、ブロック 17,034,870、2023 年 4 月 12 日)以降は、コントラクト作成の initcode に 32 バイト語ごとにさらに 2 ガスを取ります。
21000 の意味は「送金の手数料」と誤読されがちです。正確には、「1 件のトランザクションが存在する」ことの底値であり、送金額とは関係ありません。最も単純な ETH の送金は calldata が空で、データ部分が追加コストを生まないため、ちょうど 21000 になります。
EIP-2929(ベルリン・アップグレード、ブロック 12,244,000、2021 年 4 月 15 日)は、トランザクション開始時点のホット集合も定めています。送信者のアドレス、受信者のアドレス(作成トランザクションなら作成されるアドレス)、そしてすべてのプリコンパイル・コントラクトのアドレスはすでに accessed_addresses に入っており、それらにはコールドアクセスの費用がかかりません。これが、普通のアドレスへ送金するとき、受信者のアカウントアクセスに追加料金がかからない理由です。
固有コストは実行前に差し引かれるため、三つの挙動上の境界があります。gas_limit が固有コストを下回るトランザクションはそのまま無効で、ブロックに含まれず、ガスも一切差し引かれません。実行の途中で out-of-gas によって中止した場合、固有コストは返ってきません。access list の 2400 と 1900(ベルリン以降の価格)は前払いです。これからアクセスするアドレスとスロットを一通り走査し、事前にホットにしておくものですが、実際の実行でアクセスしなかった場合、この分は返金されません。
動的コスト:メモリ拡張とコールド/ホットアクセスがどう積み上がるか
実行段階のコストは、静的部分と動的部分の和です。静的部分はオペコードの固定価格で、イエローペーパー付録 G の料金表ではいくつかの区分で表されます。Gverylow 3 ガス(ADD、SUB、MLOAD、MSTORE、PUSH 系など)、Glow 5、Gmid 8、Ghigh 10、Gbase 2、Gjumpdest 1 です。
動的部分はオペランドの規模と状態アクセスのコールド/ホットに依存します。メモリ拡張は C_mem(a) = 3a + ⌊a² / 512⌋ で価格設定され(イエローペーパー式 (328)、Gmemory = 3)、a は語数で、実際の差し引きは拡張の前後の差です。この項は 704 バイト(22 語)以内ではほぼ線形で、それを越えると二次項が効き始めます。コピー系の操作は基礎価格に加えて語数ごとに 3 ガス/語(Gcopy)を取ります。KECCAK256 は 30 ガス(Gkeccak256)に 6 ガス/語(Gkeccak256word)を加え、LOG は 375 ガス(Glog)に topic ごとに 375 ガス(Glogtopic)、さらに 8 ガス/バイト(Glogdata)を加え、EXP は 10 ガス(Gexp)に 50 ガス/指数バイト(Gexpbyte)を加えます。
コールド/ホットアクセスは EIP-2929(ベルリン・アップグレード)が定義します。ある (アドレス, スロット) の組への初回アクセスはコールドアクセスで、SLOAD は 2100 ガスを取られます。同一トランザクション内で再度アクセスするとホットアクセスで、100 ガスです。アカウント系のアクセスのコールド価格は 2600 ガスで、CALL、CALLCODE、DELEGATECALL、STATICCALL、BALANCE と EXTCODESIZE、EXTCODECOPY、EXTCODEHASH を対象に含みます。この集合はトランザクション・スコープで、スコープがロールバックすると集合も一緒にロールバックします。
| 操作(ベルリン・アップグレード以降の口径) | コールドアクセス | ホットアクセス |
|---|---|---|
| SLOAD | 2100 | 100 |
| CALL 系、BALANCE、EXT* | 2600 | 100 |
| SSTORE の追加コールドアクセス費 | 2100 | 加算なし |
コールド/ホットの仕組みの直接の由来は、2 回の DoS 攻撃が残した教訓です。EIP-150(Tangerine Whistle、ブロック 2,463,000、2016 年 10 月 18 日)は、2016 年 9 月と 10 月の攻撃を受けて、すでに SLOAD を 50 ガスから 200 へ、CALL を 40 から 700 へ、SELFDESTRUCT を 0 から 5000 へ引き上げていました。EIP-2929 の動機は 2019 年の論文 arXiv:1909.07220 のある測定を引用しています。著者らのハードウェアでイーサリアムの履歴を再生したとき、同じ一群の悪意あるトランザクションは 20 から 80 秒かかったのに対し、普通のトランザクションは数ミリ秒しかかからず、状態読み取り系のオペコードの価格が依然として低すぎることを示していました。これは 1 回限りの測定で、2019 年のハードウェアと負荷を代表するものにすぎず、継続的に追跡する口径として使うことはできません。
実行中止の三つの結末:無効トランザクション、REVERT、out-of-gas
トランザクションは三つの異なる段階で終わりうるため、ロールバックの範囲と費用の扱いがそれぞれ異なります。
第一は無効トランザクションで、nonce の不一致、署名エラー、残高が gas の前払いを賄えない、gas_limit が固有コストを下回る、といった場合が含まれます。この種のトランザクションはそもそもブロックに入らず、gas も消費しません。これはチェーン外での拒否であり、実行中止とは別物です。
第二は REVERT で、revert 文、require の失敗、インライン・アセンブリの revert によって引き起こされます。現在のフレームの状態変更はすべてロールバックされ、残りの gas は 1 つ上の層へ返され、すでに消費した部分はそのまま支払われます。返金カウンタもそのフレームに入る前の状態へロールバックします。REVERT は「理由のある失敗」で、エラーデータを返せるため、呼び出し側に失敗の原因を区別させたい場面に適しています。
第三は異常中止(exceptional halt)で、out-of-gas、不正なオペコード、スタックオーバーフロー、静的呼び出しでの状態書き込み、initcode の上限超過などが含まれます。状態は同様にロールバックされますが、現在のフレームに残った gas はすべて破棄されます。異常が最上位のフレームで起きた場合、トランザクションの gas_used は gas_limit に等しく、費用は満額で差し引かれ、返ってくる部分はありません。これが「OOG の後は gas がすべて消費される」の正確な意味です。現在のフレームの残り gas がゼロになるということで、チェーン全体の残高とは関係ありません。
// REVERT:状態をロールバック、残りの gas は返却、消費済みはそのまま支払い
function guarded(uint256 x) external pure returns (uint256) {
require(x != 0, "zero");
return 1e18 / x;
}
// 異常中止:無限ループで out-of-gas を発生させ、現在のフレームの残り gas をすべて破棄
function oog() external pure {
while (true) {}
}
子呼び出しの失敗が親フレームに波及するかどうかは、EIP-150(Tangerine Whistle)が導入した「1/64 を保持する」規則が決めます。子フレームを呼び出すとき、親フレームは自分の残り gas の最大 63/64(つまり gas - gas // 64)しか転送できず、少なくとも 1/64 を保持します。CREATE と CREATE2 も同様に 63/64 しか渡しません。したがって子フレームの OOG が親フレームの OOG を連鎖させることはなく、親フレームは保持した gas で失敗を捕まえて実行を続けられます。この規則は 2016 年にそれまでの呼び出し深さの制限を置き換え、「深さ爆弾」を構造的な攻撃の一種から純粋な gas の問題へ格下げしました。
// 子呼び出しへは gasleft() の最大 63/64 を転送し、親フレームは 1/64 を保持する
(bool ok, ) = target.call{gas: gasleft()}("");
value を伴う CALL にはさらに二つの追加項があります(イエローペーパー付録 G)。非ゼロの送金には Gcallvalue 9000 ガスを、対象のアカウントが存在しない場合はさらに Gnewaccount 25000 ガスを取ります。呼び出される側は 2300 ガスの呼び出し手当(Gcallstipend)を受け取り、最も単純な受け取りロジックに使えます。EIP-2200(イスタンブール・アップグレード、ブロック 9,069,000、2019 年 12 月 8 日)は、gasleft が 2300 以下だと SSTORE がそのまま OOG で失敗すると定めています。これはまさに、この手当の経路が状態書き込みに使われるのを防ぐためです。
返金(refund)はトランザクションの実行が終わった後に精算され、実行中は見えません。EIP-3529(ロンドン・アップグレード、ブロック 12,965,000、2021 年 8 月 5 日)は、非ゼロをゼロへ書き換えたときの返金を 15000 から 4800 へ下げ、SELFDESTRUCT の返金を廃止し、1 トランザクションの返金総額の上限を gas_used // 5 に抑えました。返金は実行中には使えないため、コントラクトはトランザクションの途中で必要な gas を立て替えるためにそれに頼ることはできません。
EIP-1559:base fee、tip と実行層の境界
EIP-1559(ロンドン・アップグレード、ブロック 12,965,000、2021 年 8 月 5 日)はトランザクション手数料を二段に分けました。base fee はプロトコルが親ブロックの gas_used と目標値(gas_limit の半分)に応じて計算し、1 ブロックあたり最大 12.5% 上下に調整され、焼却されます。priority fee(tip)はユーザーがバリデータに支払う追加の単価で、より早くブロックに入れてもらうために使います。トランザクション署名では max_fee_per_gas と max_priority_fee_per_gas の二つの上限を与え、実際に有効な単価は次のとおりです。priority fee は max_priority_fee_per_gas と (max_fee_per_gas - base fee) の小さいほうを取り、effective_gas_price は priority fee に base fee を加えたものです。使われなかった gas はこの単価で返されます。
実行層の境界もはっきりさせておきます。GASPRICE オペコードは EIP-1559 以降、effective_gas_price、つまり送信者が実際に支払う単価を返します。バリデータが実際に受け取る部分は、実行環境から直接読み取ることはできません。これは、GASPRICE に頼って「マイナーの収入」を判断する古いロジックがもはや成立しないことも意味します。
二段の費用は異なる問いに答えます。base fee はブロックに入るための最低のしきい値を決め、tip は同一ブロック内の優先度を決めます。ブロック空間の総量は変わらず、持続的な使用量が目標値を超えると、base fee がブロックごとに上がって需要を押し出します。これは待ち行列の仕組みであり、生産能力の拡張とは関係ありません。EIP-1559 を gas_used を下げる方策と見なすのはよくある誤読で、それが変えるのは価格の形成方法だけです。
混同しやすい境界がもう一層あります。blob の費用は実行層の gas には属しません。EIP-4844(カンクン・アップグレード、ブロック 19,426,587、2024 年 3 月 13 日)は blob データに独立した基礎費と計量単位(blob gas)を導入し、EIP-7516 は blob base fee を返すオペコードを追加しました。ロールアップのデータ公開コストが議論するのは blob 市場であり、コントラクト実行が消費する gas とは別の会計です。
DoS の「価格付け可能」は価格設定が正しいことを意味しない
Gas の核心的な安全属性は、資源の占有をコストに換算することです。あるブロックをスパム・トランザクションで埋め尽くすには、攻撃者はそのブロックの全 gas に対応する費用を支払わなければなりません。これが「価格付け可能」の意味です。無料のネットワークと比べると、攻撃の限界費用はゼロから、占有に比例する値へ変わります。
しかしこれは、価格設定が実際のコストと一致しているときにだけ有効です。2016 年 9 月と 10 月の DoS 攻撃が利用したのは、まさに過小評価されていた状態読み取り系と呼び出し系のオペコードで、Tangerine Whistle ハードフォークを直接引き起こしました。その後、EIP-1884(イスタンブール・アップグレード、ブロック 9,069,000、2019 年 12 月 8 日)は SLOAD を 200 から 800 へ、BALANCE と EXTCODEHASH を 400 から 700 へ引き上げ、EIP-2929 はさらに状態アクセスをコールドとホットの二段に分けてコールドアクセスの価格を大幅に上げました。理由はやはり、同じパターンで 1 ブロックの処理時間を数十秒まで引き延ばせるからです。EIP-3860 が扱うのは initcode のジャンプ先解析で、それまでこの作業量はまったく計量されていませんでした。価格規則と実装コストの間には構造的な遅れがあります。クライアントのデータベース配置と実行エンジンは最適化され続けますが、gas の定数は次のハードフォークまで再評価されません。
返金の仕組みは逆向きの例証を与えます。本来はコントラクトが使わなくなった状態を掃除することを促すものでしたが、実際には GasToken のような、状態スロットを電池のように扱い、低料金のときに gas を溜め、高料金のときに放出する手法を生み出し、状態の膨張とブロック gas 使用量の余分な分散をもたらしました。EIP-3529 が返金を削減し上限を設けたのは、まさにこの経路を閉じるためです。
仕組みが機能しなくなる条件は、いくつか個別に指摘すべき点があります。価格設定はガバナンスの産物で、定数は通常、各実装の最悪ケースに合わせて選ばれます(EIP-3860 は、その 2 ガス/語が異なる実装の最悪ケースの基準から来ていると明記しています)。一方でクライアントごとに同じ一群の操作にかかる時間は異なるため、同じ gas 価格に対応する実際の資源コストは一致しません。これは定性的な判断です。EIP-2929 が値上げのために示した定量的な根拠は、2019 年にあるテスト用ハードウェアで行った 1 回限りの測定で、継続的な口径としては使えません。順序づけ市場の価値は tip だけが担うものではなく、MEV オークションとクライアントの戦略がともにブロックに入る順序へ影響し、tip が高いことは次のブロックに入りやすくなるインセンティブの一つにすぎません。ブロックの gas 上限そのものがスループットと検証コストのトレードオフで、上限を引き上げればフルノードのハードウェア要件も同時に引き上がります。
この節の落としどころは、これが定期的な調整を必要とする仕組みだという点にあります。「gas の価格設定は信頼できる」も「gas の価格設定は信頼できない」も、どちらも強すぎる結論です。EIP-150、EIP-1884、EIP-2929、EIP-3860 はいずれも調整の動作です。次の調整が何によって引き起こされるかについては、結論ではなく推測しか出せません。4 回の調整の直接の誘因は、いずれも公に観測された資源増幅攻撃でした。これに基づき本稿は、次回もおそらく同種の出来事だろうと推測します。この推測には公開された根拠がなく、状態の成長やブロック容量といった別の考慮から前倒しで再評価される可能性も排除しません。
境界と不確実なところ
引用した口径は、イエローペーパー付録 G の料金表と式 (64)、式 (328)(ダウンロード版のフッターには Shanghai 版と記載されています)、および EIP-150、EIP-1884、EIP-2028、EIP-2200、EIP-1559、EIP-2929、EIP-2930、EIP-3529、EIP-3860 の仕様文書です。具体的な数値はフォークによって変わり、本文で示したフォークとブロック高は次のとおりです。イスタンブール(2019 年 12 月 8 日、ブロック 9,069,000)、ベルリン(2021 年 4 月 15 日、ブロック 12,244,000)、ロンドン(2021 年 8 月 5 日、ブロック 12,965,000)、上海(2023 年 4 月 12 日、ブロック 17,034,870)、カンクン(2024 年 3 月 13 日、ブロック 19,426,587)。EVM 互換チェーンで gas を議論する前に、そのチェーンがどのフォークを有効にしているかを確認する必要があり、価格設定の定数はイーサリアム・メインネットと異なる場合があります。Tangerine Whistle のブロック 2,463,000 と 2016 年 10 月 18 日も ethereum.org のアップグレード履歴から来ています。
「価格設定の遅れ」の深刻さについては、傍証しか出せず、定量的な結論は出せません。公開資料には「プロトコルの gas 定数と実際の実行所要時間の比」を継続的に追跡した時系列が存在しないため、遅れが拡大しているのか収束しているのかを判断できません。この点は不確実な項目として残すべきで、それを使って「悪化している」とか「すでに収束した」といった結論を逆算すべきではありません。
また、手数料市場の挙動は MEV とクライアントの戦略に影響され、本稿は順序づけ市場の仕組みの詳細には立ち入っておらず、blob の費用市場と実行層の gas を同じ枠組みで比較してもいません。両者は EIP-4844 以降、分かれた価格体系です。これらの数値を引用してコストモデルを作る際は、先に口径とバージョンを確認する必要があります。
出典
- EIP-1559、Fee market change for ETH 1.0 chain:https://eips.ethereum.org/EIPS/eip-1559
- EIP-2929、Gas cost increases for state access opcodes:https://eips.ethereum.org/EIPS/eip-2929
- EIP-3529、Reduction in refunds:https://eips.ethereum.org/EIPS/eip-3529
- EIP-150、Gas cost changes for IO-heavy operations:https://eips.ethereum.org/EIPS/eip-150
- EIP-1884、Repricing for trie-size-dependent opcodes:https://eips.ethereum.org/EIPS/eip-1884
- EIP-2028、Transaction data gas cost reduction:https://eips.ethereum.org/EIPS/eip-2028
- EIP-2200、Structured Definitions for Net Gas Metering:https://eips.ethereum.org/EIPS/eip-2200
- EIP-2930、Optional access lists:https://eips.ethereum.org/EIPS/eip-2930
- EIP-3860、Limit and meter initcode:https://eips.ethereum.org/EIPS/eip-3860
- EIP-4844、Shard Blob Transactions:https://eips.ethereum.org/EIPS/eip-4844
- EIP-7516、BLOBBASEFEE opcode:https://eips.ethereum.org/EIPS/eip-7516
- Ethereum Yellow Paper、付録 G の料金表、式 (64) 固有コスト、式 (328) メモリ価格関数:https://ethereum.github.io/yellowpaper/paper.pdf
- ethereum.org 開発者ドキュメント:Gas と手数料:https://ethereum.org/en/developers/docs/gas/
- ethereum.org オペコード・リファレンス:https://ethereum.org/en/developers/docs/evm/opcodes/
- ethereum.org ネットワーク・アップグレード履歴(各フォークのブロック高と日付):https://ethereum.org/en/history/
- arXiv:1909.07220、EIP-2929 が引用した状態アクセス所要時間の測定:https://arxiv.org/abs/1909.07220
関連記事
本稿は EVM 基礎シリーズに属し、前置は 0.5「三種類のストレージを混同しない:Memory、Storage、Transient Storage」です。