はじめに

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

今回は、AvalancheのEVMチェーン(C-ChainおよびSubnet-EVM)の手数料計算を動的化し、バリデータが需要に応じて目標ガス消費量を調整できるようにすることで、手数料の急騰を防ぎつつ処理能力を柔軟に拡張する仕組みを提案しているACP176についてまとめていきます!

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

他にも様々なEIP・BIP・SLIP・CAIP・ENSIP・RFC・ACPについてまとめています。

概要

ACP176では、AvalancheのC-ChainおよびSubnet-EVMチェーンにおいて、P-Chainのアップグレード提案であるACP103で導入された「動的手数料(dynamic fee)メカニズム」と類似した仕組みを採用することが提案されています。 ただし、そのままではなくブロック提案者(=バリデータ)自身がガス消費量の目標値を時間単位で動的に調整できるようにする改良を加える点が特徴です。

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

つまり、EVMチェーンが現在固定的に設定している「ガス消費目標」を、ネットワークの状態や需要に応じてバリデータが柔軟に変更できる仕組みを取り入れることで、より効率的でスケーラブルなガス手数料調整が可能になるという提案です。

この提案により、以下の改善が期待できます。

  • 大規模ブロックの影響を抑える 個別の大きなブロックによってガス価格が不自然に跳ね上がる現象を軽減できます。
  • 柔軟なキャパシティ調整 バリデータが動的にガス目標を設定できるため、ネットワークの実力に応じて処理能力を拡大でき、将来的なトランザクション負荷の増加にも適応可能です。

結果として、AvalancheのC-ChainおよびSubnet-EVMは、よりスムーズかつ持続的に成長し続けるための基盤を整えることができます。

動機

現在のC-Chainには「静的(固定的)なガス目標」と「修正されたEIP1559ベースの手数料調整メカニズム」が導入されています。

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

具体的には以下のようになっています。

  • ガス目標は1,500万gasに設定されています。
  • これは10秒間のローリングウィンドウ(rolling window)ごとに適用され、過去10秒間のガス消費量に基づいて次のブロックのベース手数料が調整されます。

この仕組みには以下の2つの問題点があります。

大きなブロックによる手数料スパイク

ウィンドウ方式により、もしあるブロックがガス上限いっぱいまで使われると、その後に続くブロックが小さいものであっても、同じウィンドウ内に含まれるためガス価格が上がり続けてしまうという問題があります。 その結果、ガス価格が一時的に過剰に上昇し、利用者にとって予期せぬ高コストが発生してしまいます。

静的なガス目標の硬直性

現在のガス目標(例: 1,500万gas)はネットワークアップグレードによってしか変更できません。 つまり、ノードのハードウェア性能向上やソフトウェアの最適化が進んでも、それに合わせて柔軟にガス目標を調整することができず、ネットワーク全体のキャパシティ調整が遅れるという欠点があります。

仕様

C-Chainにおける動的手数料メカニズムの仕組み

AvalancheのC-Chainでは、これまで固定されたガス目標(毎秒1,500,000gas)と、EIP1559を改良した手数料調整方式が使われてきました。 しかし、この方式は大きなブロックによって手数料が急上昇する問題や、ガス目標の変更にネットワークアップグレードが必要になる問題がありました。 そこで導入が提案されているのが、P-ChainのACP103を基盤とした動的手数料メカニズムです。

ACP176では、1秒あたりの目標ガス消費量(TT)を基準にガス価格が決まります。 ガスの利用量が目標を超えれば価格は上昇し、下回れば価格は下がるというシンプルな構造です。 ただし、C-Chain版ではTTをブロックごとにバリデータが調整可能とした点が大きな特徴です。

qqとTTの関係

目標ガス消費量TTは、以下の式で定義されます。

T=P⋅eqDT = P \cdot e^{\frac{q}{D}}

ここで、PPは最低限の目標値、qqはブロックごとに増減できる整数、DDは変化を緩やかにするための定数です。 バリデータは自分の望むTTを設定し、それに対応するqqを計算します。 各ブロックでは、現在のqqを少しずつ望む方向に近づけることができます。 これにより、バリデータ全体の多数決のような力学で、ネットワークの実効的なTTが決まっていく仕組みになっています。

他のパラメータの連動

TTが決まると、以下のパラメータも連動して調整されます。

  • R=2⋅TR = 2 \cdot T(1秒あたりの回復量)
  • C=10⋅TC = 10 \cdot T(最大蓄積容量)
  • K=87⋅TK = 87 \cdot T(価格変化の感度係数)

この設計により、ガス価格の上昇と下降のスピードが釣り合い、また最大容量や価格の変化速度もTTに応じて自動的にスケーリングされます。

ガス価格の安定性

ガス価格は内部変数xx(過剰ガス消費量)を使って計算されます。 KKが変わっても価格が急に跳ねないように、xxも比例して補正されます。 さらに、qqやそれに付随する各パラメータの更新はブロック実行後に反映されるため、そのブロックに含まれるトランザクションの価格計算には影響せず次のブロックから有効になります。

全体の効果

この仕組みによって、C-Chainは以下のような特徴を得ます。

  • 大きなブロックの影響を緩和し、手数料が急激に跳ね上がらない
  • ネットワークの処理能力に応じて柔軟にスケーリングできる
  • バリデータの多数意見で最適な水準に収束する

EthereumのEIP4844に導入されたblobガス市場や、P-Chainでの実運用を通じて、この方式が需要に応じて価格を安定的に調整し、DoS攻撃を防ぐ方向に働くことは既に検証されています。

ガス価格の決定

ACP176で用いるブロックのベース手数料(base fee)の決め方は、P-ChainでACP103が採用したガス価格決定メカニズムと同じ考え方です。 ブロックbbのガス価格は、以下のパラメータに基づいて計算します。

TT 1秒あたりの目標ガス消費量(target)
MM 最小ガス価格(下限)
KK ガス価格の更新係数(感度を決める定数)
CC 最大ガス容量(ためておける上限)
RR 1秒あたりに追加されるガス容量(回復速度)

ここでのポイントは、ネットワークに「容量」、「回復」、「上限」をもたせ、その超過度合いに応じてガス価格を上下させるということです。 TTは「どのくらいのペースでガスを使うのが理想か」を表す目標、RRは1秒あたりの容量の回復量、CCは貯めておける容量の上限、KKはガス価格がどれくらいの速さで反応するかの係数、MMは下限です。 また、過剰ガス消費量を表す内部状態xx(excess)が使われ、ブロックで消費したガスGGや各定数に応じて更新されます(xxは後述の式で調整します)。

TTを動的にする

上記のガス価格決定は、1秒あたりの目標ガス消費量 TT を前提にしています。 ここでは、TTをブロックごとに動的に調整できるようにします。

まず、非負整数qqを用意し、この仕組みの有効化時にq=0q=0で初期化します。 目標ガス消費量は以下の式で表します。

T=P⋅eqDT = P \cdot e^{\frac{q}{D}}

ここでPPはネットワーク全体で許される最小の目標ガス消費率、DDは変化速度をなだらかにする定数です(eeは自然対数の底、e≈2.71828e\approx2.71828)。

ブロックbbのトランザクション実行が終わった後、qqは1ブロックあたり最大でQQだけ増減できます。 必ず∣Δq∣≤Q\lvert \Delta q \rvert \le Qを満たさなければならず、これに違反するブロックは無効です。 どれだけqqを変えるかは、ブロックビルダー(=バリデータ)が指定します。

各ノード(バリデータ)は、自分の望むTT を設定ファイルに指定できます。 そのときの望ましいqqは次式で求めます。

qdesired=D⋅ln⁡ ⁣(TdesiredP)q_{\text{desired}} = D \cdot \ln\!\left(\frac{T_{\text{desired}}}{P}\right)

ln⁡\lnは自然対数です。 qdesiredq_{\text{desired}}はローカルでのみ使う値で、ノードごとに異なっても安全です。 実装ではln⁡!(TdesiredP)\ln!\left(\frac{T_{\text{desired}}}{P}\right)を近似して四捨五入(整数化)して問題ないです。

ブロックを組み立てる時、現在のネットワークのqq(q_current)から、次に採用したいqqを以下の関数で計算できます(1ブロックで変えられる幅はmax_change=Qまで)。

python
# 指定ブロックにおける次のqを計算
def calc_next_q(q_current: int, q_desired: int, max_change: int) -> int:
    if q_desired > q_current:
        return q_current + min(q_desired - q_current, max_change)
    else:
        return q_current - min(q_current - q_desired, max_change)

qqはブロック内のトランザクション実行が終わった後に更新されます。 そのためTTも常に以下の式で更新されます。

T=P⋅eqDT = P \cdot e^{\frac{q}{D}}

TTが変わると、1秒あたりの容量追加量RRも以下のように更新します。

R=2⋅TR = 2 \cdot T

これにより、ガス価格が上がる速さと下がる速さが同程度になるように保ちます。

さらに、最大容量CCもTTに比例させて以下のようにします。

C=10⋅TC = 10 \cdot T

これは、ブロックがしばらく受け入れられない状態が5秒続くと、ストックできる容量が上限に達する(R=2TR=2TなのでC/R=10T/2T=5C/R=10T/2T=5秒)という意味です。

また、価格の反応係数KKもTTに比例させて以下のようにします。

K=87⋅TK = 87 \cdot T

これにより、最大利用(=持続的に2T2Tガス/秒)のときに、約60.3秒で手数料が2倍になる性質を保ちます(比較として、EIP1559は約70秒、現行C-Chainはブロック間隔に依存しつつ約50秒程度で倍化)。

KKを変えても、その瞬間にガス価格自体が不連続に跳ねないよう、内部ステートである過剰ガス消費xxも比例調整します。 ACP103ではxxをx+Gx+Gと更新しますが、本メカニズムでは次ブロックn+1n+1のKKに合わせて以下の式で更新します。

xn+1=(x+G)⋅Kn+1Knx_{n+1} = (x + G) \cdot \frac{K_{n+1}}{K_{n}}

重要な注意点として、qq(それに伴ってT,R,C,K,xT, R, C, K, x)の更新はブロックbbの実行後に行われ、影響は次のブロックb+1b+1の価格決定から反映されます。 したがって、ブロックbbに入っているトランザクションの価格計算には影響しません。

さらに、この「ブロックビルダーがTTを調整できる」設計は、多数決で釣り合う点に収束しやすい性質があります。 バリデータはステーク重み(持分)に比例してブロックを多く提案するため、「上げたい」重みと「下げたい」重みが50%ずつになる水準に、実効的なTTが時間とともに近づくことが期待されます。

最後に、ACP103の性質として、ある時間幅Δt\Delta tで消費できる最大ガスはr+R⋅Δtr + R \cdot \Delta t(rrは直前ブロック終了時点の残容量)で、全Δt\Delta tにわたる上限はC+R⋅ΔtC + R \cdot \Delta tです。 これを1ブロックbbに着目して表すと、次のブロックガス上限になります。

gasLimitb=min⁡(r+R⋅Δt,  C)gasLimit_{b} = \min(r + R \cdot \Delta t,\; C)

設定パラメータ

本メカニズムは、T,M,K,C,RT,M,K,C,Rに依存します。 TTは初期値からDDとPPに基づいて動的に調整され、RRとCCはTTから導出されます。C-Chainの有効化時のパラメータは次のとおりです。

パラメータ 説明 C-Chainでの設定値
PP 1秒あたりの最小目標ガス消費量 1,000,0001{,}000{,}000
DD 目標ガス消費率の更新を制御する定数 2252^{25}
QQ 目標ガス消費率の更新幅を制限する上限値 2152^{15}
MM 最小ガス価格 1×10−181 \times 10^{-18} AVAX
KK 初期ガス価格更新係数 87,000,00087{,}000{,}000

PPはC-Chainで安全に最低限といえる目標値として選定されています(現在のC-Chainの目標は毎秒1,500,000gas)。 ネットワークの過半のステーク重みがPPを望む場合に限って、実効的な目標はPPにとどまります。

DDとQQは、1ブロックでTTを現在値の約1/10241/1024刻みで調整できるように設計されています。 これはEthereumが実行レイヤのガスリミット変更幅に用いている境界(bound divisor)に合わせたもので、さらに2152^{15}倍のスケールをかけてより細かい粒度で調整できるようにしています。

MMはネイティブ資産の最小単位にそろえてあり、常に価格発見が起きやすいようにしています。 この価格発見の仕組みは、すでにP-Chainで実運用されています。 また、EthereumのEIP4844(“blob”という大容量データに専用の手数料市場を導入した改定)でも同種の手法が用いられています。 これらの実運用の結果から、需要が高まった際に手数料が適切に上昇し、過負荷(DoS)を抑制する方向に働くことが確認されています。

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

KKは、最大利用(2T2Tgas/秒が持続)の下で、約60.3秒で手数料が2倍になるように選んでいます(参考: EIP1559は約70秒、現行C-Chainは条件により約50秒)。

なお、瞬間的な価格倍率の上限は以下の式で表せます。

eCK=e10⋅T87⋅T=e1087≈1.12e^{\frac{C}{K}} = e^{\frac{10 \cdot T}{87 \cdot T}} = e^{\frac{10}{87}} \approx 1.12

C/KC/Kが一定(TTに比例)になる設計なので、TTを変えても瞬間的に跳ねる倍率は一定に保たれます。

TdesiredT_{desired}の選び方

本メカニズムでは、各バリデータが設定でTdesiredT_{desired}(望む目標ガス消費率)を指定できます。 この指定は、時間とともにネットワーク全体の実効的なTTに影響します。TTが 高いほど、ネットワークが利用できるリソース(ストレージ/計算など)が増え、スループットも上がる一方で、その水準を安定運用できるノード資源が必要になります。

したがって、各バリデータは、①そのTTを支えるために必要な自ノードの資源要件、②TPS向上によるネットワークの便益、③その水準に達した場合のネットワーク安定性を総合的に考慮してTdesiredT_{desired}を選ぶべきです。 クライアント実装はデフォルトを用意できますが、最終的な値は各バリデータが独自判断で設定できます。

互換性

ACP176の変更は必須のNetworkUpgrade(全ネットワーク一斉の有効化)を経て初めて効力を持ちます。 有効化までの間は、C-Chainにおける現行のガスリミットと価格発見メカニズムがそのまま使われます。 有効化後の開発者向けツールへの影響は小さく、特にトランザクション形式は変わらないため、ウォレットにも影響は及びません。

一方で、有効化後はCC(最大ガス容量)が動的に調整されるため、単一ブロックで消費可能な最大ガス量、つまり単一トランザクションが消費可能な最大ガス量も動的に変化します。 結果として、ある時点では無効と判定されたトランザクションが別の時点では有効になり得る(逆に有効だったものが無効になる場合もある)という挙動が生じます。 これは直感に反するかもしれませんが、最小ガス消費率(ネットワークが最低限確保する処理ペース)が十分高く設定されていれば、実務上の影響は大きくなりにくいと考えられます。 この挙動は**Ethereum Mainnetでも現在確認されているものです。

有効化後、手数料が上昇局面にあるときの大型トランザクションの取り込み遅延について懸念が挙がりました。 これへの対策として、ブロックプロデューサは十分な容量があるときにのみブロックを生成すべきとされます。 有効化前の単一トランザクション最大サイズは1500万gasでした。 そこでの推奨ヒューリスティックは、容量がmin⁡(8⋅T,;1500万)\min(8\cdot T,;1500\text{万})以上になるまでブロック生成を控えることです。 記載時点では、これにより最大1280万gasのトランザクションがブロックスペースに入札できる状態を確保できます。

参考実装

本ACPはCorethに実装され、Fortunaアップグレードフラグの背後にマージ済みです。完全な実装は次のリリースにまとめられています。

  • coreth@v0.14.1-acp-176.1(GitHubReleases参照)

この形態は、コードとしては取り込まれているが、プロトコルの有効化フラグで制御するという意味であり、ネットワークの有効化時期に合わせて挙動が切り替わる運用を想定しています。

セキュリティ上の考慮

ACP176は、AvalancheEVMチェーンにおけるガス価格決定メカニズムを変更します。 ガス価格は需要変動に応じて動的に適応し、最終的に価格発見(需要と供給の釣り合う水準に調整される仕組み)を行います。 もし期待どおりに反応しなければ、価格が低すぎてDoS攻撃に晒される、あるいは低負荷時に利用者へ過大な料金を課すといったリスクが生じ得ます。 このメカニズムはP-Chainで既に実戦投入されていますが、C-ChainでのMainnet有効化に先立ち、改めて十分なテストを行うべきです。

さらにACP176は、バリデータがC-Chainのガスリミットを変更できる仕組みも導入します。 もしガスリミットが過度に高く設定されると、バリデータノードがブロック処理に追随できない可能性があります。 これへの緩和策として、最大ガスリミットの上限を導入する選択肢はありますが、その場合はその上限を超えてスケールするには再度NetworkUpgradeが必要になります。 設計・運用では、過負荷での処理追随性と将来の拡張余地のバランスを十分に検討する必要があります。

最後に

今回は「AvalancheのEVMチェーン(C-ChainおよびSubnet-EVM)の手数料計算を動的化し、バリデータが需要に応じて目標ガス消費量を調整できるようにすることで、手数料の急騰を防ぎつつ処理能力を柔軟に拡張する仕組みを提案しているACP176」についてまとめてきました! いかがだったでしょうか?

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

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