はじめに

『DApps開発入門』という本や色々記事を書いているかるでねです。

今回は、Ethereum上で処理負荷の高い一部の命令(オペコード)のガス価格を見直し、コントラクト自身のETH残高を取得できる軽量な命令(SELFBALANCE)を新たに追加してネットワーク全体の処理効率とガス使用の公平性を改善する仕組みを提案しているEIP1884についてまとめていきます!

以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。

他にも様々なEIPについてまとめています。

概要

EIP1884は、Ethereum上で動作するいくつかのオペコード(EVMが実行する命令)のガス価格を見直すことを提案しています。 ガスとは、Ethereumネットワーク上でスマートコントラクトやトランザクションを実行する際に必要となる「処理コスト」の単位です。 オペコードとは、スマートコントラクト内部で使われる命令のことを指し、それぞれの命令には一定のガスコストが割り当てられています。

Ethereumのステートが年々大きくなる中で、一部のオペコードが当初想定されていたよりもはるかに多くのリソース(CPU時間、メモリなど)を消費するようになっています。 これに対応するため、EIP1884では対象となるオペコードのガスコストを引き上げ、ネットワーク全体の負荷と処理効率のバランスを取り戻すことを目的としています。

動機

Ethereumでは、全てのトランザクションがガスという仕組みによって処理コストを計算されます。 理想的には、オペコードのガス価格は、それを実行する際に実際に必要とされるリソース量(CPUの処理時間やメモリ消費など)に見合ったものであるべきです。

しかし、Ethereumのステートサイズが増大するにつれて、かつては軽量だったオペコードの一部が、現在では非常に重い処理を伴うようになりました。 それにも関わらず、これらのオペコードのガス価格が適切に引き上げられていないことで、いくつかの問題が発生しています。

1つ目は、攻撃の温床になり得ることです。 処理コストに対してガスが安すぎる命令を大量に使ったトランザクションを詰め込んだブロックが作成されると、ノードのブロック処理時間が過度に長引いてしまいます。 これによりネットワーク全体の同期が遅れ、セキュリティリスクが高まります。

2つ目は、ブロックガスリミットの歪みです。 ガスリミットとは、1つのブロックで許容される最大ガス量のことで、Ethereumのスループットを管理する重要なパラメータです。 ガス価格が適切でないと、「ガスリミットいっぱいまで使われたブロック」であっても、処理が速い場合と遅い場合の差が大きくなってしまいます。これにより、ブロック生成の一貫性が失われ、ネットワーク効率が低下します。

3つ目は、ガスリミットの最適化が難しくなるという点です。 ガス価格が実際のリソース消費と合っていれば、ブロックに詰められるトランザクションを最適化しやすくなり、ブロックごとの処理時間を安定させることができます。 これはEthereumのパフォーマンス全体にとって重要です。

このような背景から、EIP1884では特定のオペコードのガス価格を現実のリソース消費に合わせて引き上げることにより、Ethereumネットワークの健全性と効率性を保つことを目指しています。

仕様

背景と目的

EIP1884では、EVMにおけるいくつかの重要なオペコードのガス価格を調整し、同時に新しいオペコード SELFBALANCE を追加することが提案されています。 これらの変更は、実際のリソース使用量とガスコストの整合性を高め、より公平かつ安全なガス料金体系を構築することを目的としています。

オペコードのガス価格の変更

Ethereumでは各オペコードに対して固定のガス価格が設定されていますが、ステートの増加やノードのリソース負荷に応じて、その価格が実態と乖離することがあります。 EIP1884では、以下の3つのオペコードについてガス価格を引き上げることで、リソース消費とのバランスを取り戻そうとしています。

SLOAD (0x54) のガス価格変更

SLOAD は、コントラクトのストレージスロットから値を読み取る命令です。 従来は 200 ガスで実行できましたが、EIP1884ではこれを 800 ガスに引き上げるとされています。 ストレージの読み取りは、現在のEthereumでは特に重たい処理となっており、ガス価格を上げることで実態に合わせた調整が行われます。

BALANCE (0x31) のガス価格変更

BALANCE は、指定されたアドレスのETH残高を取得する命令です。 以前は 400 ガスで実行されていましたが、これが 700 ガスに引き上げられます。 アカウントのバランスを取得する処理もノードにとってそれなりに重い処理であるため、これに見合った価格設定に変更されます。

EXTCODEHASH (0x3F) のガス価格変更

EXTCODEHASH は、指定アドレスにデプロイされているコードのハッシュ値を取得する命令です。これも同様に 400 ガスから 700 ガス への引き上げが行われます。コード領域へのアクセスは、ディスクI/Oなどを伴うため、ガス価格の見直し対象となります。

新オペコード SELFBALANCE の導入

今回の提案では、新たなオペコード SELFBALANCE(オペコード番号 0x47)も導入されます。 この命令は、コントラクト自身(すなわち address(this))のETH残高を取得し、スタックの上にその値を積む処理を行います。

SELFBALANCE の特徴は、引数を必要とせずスタックから何も取り出さずに、1つの値(コントラクト自身の残高)を積む点です。 また、このオペコードのガスコストは非常に軽量な 5 ガスに設定されており、既存の BALANCE 命令と比べて効率的に自己残高を取得できます。

これは特に、送金処理などにおいて自身の残高確認を行うユースケースにおいて、無駄な計算コストを省くことができるという利点があります。

補足

なぜガス価格の見直しが必要か

run3.total-bars-5.png

Ethereumノード(ここではGethを使用)によるフル同期時のデータを分析すると、特定のオペコードがブロック処理時間に大きく影響していることが明らかになっています。

geth_processing.png

ブロック単位で集計されたオペコードの実行時間を可視化したグラフでは、SLOAD や BALANCE、EXTCODEHASH が上位にランクインしており、これらが重い処理としてネットワーク全体に負荷をかけていることが確認されています。

SLOAD の処理負荷と過去の経緯

SLOAD-run3.png

SLOAD はコントラクトのストレージからデータを読み取る命令であり、ストレージアクセスはディスクI/Oを伴うため高コストです。 この命令は以前、EIP150で50ガスから200ガスに引き上げられた経緯があります。

EIP150については以下の記事を参考にしてください。

Gethの同期ログから、10,000ブロックごとの SLOAD の実行時間を追ったグラフでは、EIP150実施直後に処理時間が大きく減少したことが示されています。 しかしブロックが進むにつれて再び処理時間は増加し、ブロック700万時点ではEIP150以前の水準を上回る状態となっています。

このEIPでは、SLOAD の価格を200から800へと4倍に引き上げることで、処理時間を再び40程度まで抑えることを目指しています。今後さらに状態(ステート)サイズが膨張すれば、再度の再価格化が必要になる可能性があるため、本提案は恒久的な解決というよりは、現状に対する中間的な対応と位置付けられています。

BALANCE の課題と新オペコード導入の背景

BALANCE-run3.png

BALANCE は指定されたアドレスの残高を取得する命令で、これもステートツリーからの情報取得を伴うためコストが高くなりがちです。 EIP150で20ガスから400ガスへと価格が見直されましたが、現時点でもなおコストとリソース使用量の不均衡が見られています。

特に問題なのは、BALANCE が「自分自身の残高確認」という使い方もできる一方で、「任意アカウントの残高確認」というトライ(trie)アクセスを伴う重い処理もできてしまうことです。 このように、同じオペコードが複数の用途にも使われることで価格設定のバランスが難しくなっています。

このような背景から、本提案では自分自身の残高取得専用の軽量オペコード SELFBALANCE を新設することが提案されています。BALANCE は今後700ガスに引き上げられますが、SELFBALANCE は定額5ガスで動作するよう設計されています。

SELFBALANCEの技術的な位置づけ

新しく追加される SELFBALANCE(0x47)は、トランザクション送信中のコントラクト自身の残高を取得する命令です。 これはガスコストを抑えた自己参照向けのオペコードであり、スタックに1つの値(残高)を積むだけのシンプルな構造をしています。 従来の BALANCE では常にアドレス引数を取りましたが、SELFBALANCE は引数なしで実行可能です。

命令セットの割り当てとしては、BALANCE 系の命令が集まる 0x3X 範囲がすでに埋まっているため、新しい命令である SELFBALANCE は 0x4X 範囲(0x47)に追加される形となります。

なお、ガス価格が2ガスではなく5ガスに設定された理由としては、EVM内部ではキャッシュを活用していても状態トライ(state trie)への参照が発生するため、完全にゼロコストとはならないことが挙げられています。 加えて、残高は gasPrice や timestamp のような定数とは異なり、トランザクション実行中に変動し得るため、ある程度の実行コストを要します。

EXTCODEHASHとBALANCEの価格整合性

EXTCODEHASH は、指定アドレスにデプロイされているコードのハッシュ値を取得する命令であり、EIP1052によりConstantinopleハードフォークで導入されました。 このオペコードの価格は、当初から BALANCE と同じ400ガスに設定されていました。 理由は、どちらの命令もアカウント情報の取得に同様のリソースを必要とするためです。

したがって、BALANCE を700ガスに引き上げる場合、リソース消費の観点から見て EXTCODEHASH も同様に700ガスへと引き上げるのが妥当とされています。

EIP1052については以下の記事を参考にしてください。

テストケース

EIP1884における機能の正当性を確認するためには、いくつかの重要なテストを実施する必要があります。 まず、SELFBALANCE 命令が正しく自分自身のETH残高をスタックに積むことを確認する必要があります。 この値は balance(address(this)) と同一であることが期待されるため、それらが一致するかを比較するテストが必要です。

また、既存の BALANCE(this) 命令が従来通りのガスコストで動作すること、SELFBALANCE がスタックから値を取り出さないことなども確認されなければなりません。 これらのテストは、命令の動作がEVMの仕様に正確に従っていることを保証するために不可欠です。

さらに、SLOAD、EXTCODEHASH、 SELFBALANCE の各命令が新しいガス価格で正しく実行されるかを検証する必要があります。

最後に、SELFBALANCE はIstanbulハードフォーク以前のブロックでは使用できない(=無効)という制約があるため、その挙動もブロック番号をまたいだ形で確認しなければなりません。 これらのテストの一部は、すでに以下のリポジトリでステートベースのテストとして提供されています。

実装例

Geth(go-ethereum)における SELFBALANCE の実装は以下のようになっています。

go
func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
    stack.push(interpreter.intPool.get().Set(interpreter.evm.StateDB.GetBalance(contract.Address())))
    return nil, nil
}

この実装では、contract.Address() に対応する残高を StateDB から取得し、その値をスタックに積んでいます。 引数をスタックから取り出す処理が存在しない点が、従来の BALANCE 命令との違いです。 このように、オペコードの動作をシンプルに保ちながらも、残高情報を効率よく取得できるよう設計されています。

セキュリティ

この提案による新たなセキュリティリスクは基本的に存在しません。SELFBALANCE は BALANCE の簡略版であり、スタック引数を取らずに自分自身のアドレスの残高を取得するだけなので、仕様的には BALANCE(address(this)) と同義です。 BALANCE の挙動が既に仕様として定義されているため、それを踏襲する形となります。

ただし、Solidityコンパイラがこれらの命令に対してガスコストをハードコードしている場合には注意が必要です。 例えば、ガス消費を見積もって require 条件に組み込んでいるようなケースでは、ガス価格変更によって意図しない動作が発生する可能性があります。

また、実務的な影響として、受信側のコントラクトが CALL でETHを受け取った際に LOG を発行しようとした場合などに注意が必要です。 LOG は 375 ガスに加えてトピックごとに追加で 375 ガスを消費しますが、これに SLOAD 処理が追加されると、従来ギリギリ実行できていた処理がガス不足で失敗する可能性があります。

引用

Martin Holst Swende (@holiman), "EIP-1884: Repricing for trie-size-dependent opcodes," Ethereum Improvement Proposals, no. 1884, March 2019. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-1884.

最後に

今回は「Ethereum上で処理負荷の高い一部の命令(オペコード)のガス価格を見直し、コントラクト自身のETH残高を取得できる軽量な命令(SELFBALANCE)を新たに追加してネットワーク全体の処理効率とガス使用の公平性を改善する仕組みを提案しているEIP1884」についてまとめてきました! いかがだったでしょうか?

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!

他の媒体でも情報発信しているのでぜひ他も見ていってください!