# [ACP194] トランザクション順序と処理を分けるSAEの仕組みを理解しよう！

> StreamingAsynchronousExecution（SAE）は、consensus（トランザクションの順序決定）とexecution（決定順に従ってstateを更新）の結びつきをqueueで分離します。

- URL: https://cardene.dev/articles/a5f0eaf1703a393a92f0
- 公開日: 2025-09-24
- 更新日: 2025-09-24
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、ブロックの順序決定（consensus）と実行（execution）を切り離して並行させ、混雑時でも安全に処理を進めながら手数料と待ち時間を安定させる仕組みを提案している**ACP194**についてまとめていきます！

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

https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/194-streaming-asynchronous-execution/README.md

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

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

:::message
**ACP194**は、ブロックの順序確定（consensus）とトランザクション実行（execution）を切り離して並行処理する仕組みを導入する提案です。
これにより、これまで相互に待ち合っていた処理が効率化され、1秒あたりに処理できるガス量が増え、レイテンシのばらつきも小さくなります。

具体的には、ブロックはまずconsensusで「**順序だけ**」が確定し、queueに追加されます。
その後、executorが非同期でトランザクションを実行し、後続のブロックが結果を取り込むことで正式に確定（settlement）します。
さらに、最悪ケースでのガスコスト検証や新しい課金ルールを導入することで、DoS攻撃を防ぎつつ安全性を維持します。

この仕組みによって、スループット向上・一時停止イベントへの耐性・将来の新機能（暗号化mempoolやVRF利用など）への布石が整えられます。
:::

StreamingAsynchronousExecution（SAE）は、consensus（トランザクションの順序決定）とexecution（決定順に従ってstateを更新）の結びつきを**queue**で分離します。
consensusはqueue上で順序だけを確定し、並行して動くexecutionstreamがそのqueueを順に実行して遅延したstateRoot（stateを要約するハッシュ）を生成します。
このstateRootは後続のconsensusラウンドで記録されます。
queueにenqueue（追加）する時のvalidationは軽量ですが、queueに採用されたトランザクションは、確定した順序に基づいて必ず実行されて後続ラウンドで記録されるstateRootに反映されますという保証を前提に設計されています。

```mermaid
flowchart LR
  IN[Transactions] --> C[Consensus（順序のみ確定）]
  C --> Q[Queue（ordered）]
  Q --> E[Execution Stream（順次実行）]
  E --> SR[Delayed stateRoot]
  SR --> L[Later Consensus（記録）]
```

## 動機

### 性能向上

#### consensusとexecutionを並行化してコンテキストスイッチを削減

SAEはconsensusとexecutionを別ストリームで同時に動かします。
これにより、ノードは両処理の切り替えや相互待機が減ります。
結果として、VMが命令を実行している時間（VMtime）が実時間（walltime）に近づきます。
そのため、VM-secondあたりのgas設定を上げなくても、1秒（wall-second）あたりに実際に処理できるgas量が増え、スループットが向上します。

#### execution-onlyクライアントでreceipts/state計算を加速

consensusで順序が確定したqueueは、合意機能を持たない軽量の**execution-onlyクライアント*でそのまま実行できます。
このクライアントは、実行のたびに**stateRoot**の計算や**Merkle tree**の更新を行うことを前提にしません。
つまり、重い更新処理を後回しにできるため、トランザクションレシートの表示や中間stateの計算を素早く進められます。
ユーザーが画面で見る実行結果は高速になりますが、後続ラウンドで記録されるstateRootに基づくネットワークの結果と内容は一致します。

#### stop-the-worldイベントの平準化

データベースのcompactionのように、一時的にノードの処理が止まるイベントが発生することがあります。
SAEではconsensusとexecutionが独立して動いているため、executionが止まってもconsensusはトランザクションの順序決定を続けられます。

executionが処理を再開すると、停止中にqueueへ追加されていたトランザクションを順番に実行していきます。
この仕組みにより、一度の停止による影響をまとめて1ブロックに押し付けるのではなく、複数のブロックに分散して処理できるようになります。
その結果、処理の遅れが一時的に大きく跳ね上がることを避け、ユーザーが感じる応答時間のばらつきも小さく抑えられます。

#### eager acceptによる短時間のスループット上乗せ

consensusはトランザクションの順序だけを先に確定し、executionは確定順どおりに処理します。
これにより、consensusはトランザクションを継続的にqueueへ取り込みやすくなり、取り込み段階の渋滞が起きにくくなります。
順序はconsensusで固定され、executionはdeterministicに同じ結果を生成するため、セキュリティモデルや整合性は変わりません。
処理の結果はstateRoo*としてまとめられ、後続のconsensusラウンドで記録されます。

#### 非データ依存transactionの第三者会計を実行前に可能に

同一batch内の他のtransactionに影響されにくい非データ依存のtransaction（例：EOA→EOAの単純なvalue transfer）は、実行前でも結果を見通しやすい性質があります。
カストディ事業者は、queueから自社EOA宛のtransactionを識別し、ユーザー残高の表示をその時点で加算して更新できます。
続いてexecutionが確定順どおりに処理され、**stateRoot**が記録されることで、画面に反映した残高とネットワークの記録が同じ内容になります。

### 将来の機能

#### 実行中にVRFを使う

SAEでは、まずconsensusでトランザクションの順序だけを確定し、そのあとにexecutionが処理します。
この流れにすると、executionの最中にVRF（VerifiableRandomFunction：検証可能な乱数）を安全に参照できます。
抽選や割当のように「**あとから誰でも検証できる乱数**」を使う処理を、実行フェーズの中で正しく扱えるようになります。

#### 暗号化mempoolでfront-runningを抑える

mempool（未実行トランザクションの待機領域）の内容を暗号化しても、SAEなら順序は先にconsensusで固定し、中身の解読と実行はあとにできます。
これにより、順序が決まる前に内容を読んで並べ替えるfront-running（先回り注文）を起こしにくくなります。
なお、ここでは仕組みの前提を説明しており、暗号化mempool自体は別提案の対象です。

### ユーザーストーリー

#### 高度なDeFiトレーダー

最適化された**execution-onlyクライアント**（合意機能を持たない実行専用クライアント）をローカルで動かし、consensusで順序が決まったqueueを素早く実行します。
その結果、ネットワークより早い段階で同じ順序に基づく実行結果を把握でき、高頻度取引（HFT）の判断やリスク管理を迅速に行えます。
最終的な記録は、後続ラウンドで**state root**としてネットワークに反映されます。

#### カストディ事業者

queueを監視し、自社のEOA宛てのトランザクションだけを抽出します。
これに基づき、ユーザーの残高表示を即時に更新できます（自社の基準に沿った表示運用）。
その後、executionが処理を進め、後続のconsensusで**state root**が記録されることで、ネットワークの記録と画面表示の内容が**一致**します。

```mermaid
sequenceDiagram
  participant User as ユーザー
  participant Cons as コンセンサス（順序決定）
  participant Q as キュー（順序済み）
  participant Exec as 実行（順序どおり処理）
  participant Chain as 記録（state root）

  User->>Cons: トランザクション送信
  Cons->>Q: 順序を確定してキューへ
  Exec->>Q: キュー先頭から実行
  Exec-->>Chain: 遅延state rootを提出
  Chain-->>Cons: 後続ラウンドで記録
```

上記の流れにより、「**順序はすぐ決まる／実行とstate rootの記録は少し遅れて反映される**」が明確に分かれます。
これが、VRFの実行時利用や暗号化mempoolの導入と相性が良い土台になります。

## 説明

通常の同期実行（synchronous execution）では、ブロックは提案（proposed）→検証→受理（accepted）の順に進みます。
有効性の判定は「**先にそのブロック内のtransactionを実行（executed）して結果を得てから、consensusが受理する**」方式です。
受理の時点で、そのブロックの実行結果が台帳に取り込まれるため、transactionはその場で確定（settled）します。

```mermaid
flowchart LR
    I[Proposed] --> E[Executed] --> A[Accepted/Settled]
```

SAEでは考え方が異なります。
ブロックは、「**将来の実行時点でも手数料を支払えること**」が満たされれば有効とみなされ、consensusにより先に受理されます。
受理されたブロックは実行キュー（FIFO）に入れられ、非同期で実行されます。
のちに、ある後続ブロックがその実行結果を参照し、対象ブロック内のすべてのtransactionを確定（settled）します。

```mermaid
flowchart LR
    I[Proposed] --> A[Accepted]
    A -->|variable delay| E[Executed]
    E -->|τ seconds| S[Settled]
    A -. guarantees .-> S
```

上図の「A →（保証）→ S」は、受理（acceptance）の時点で「**実行可能性（手数料を払える）を満たしている＝いずれsettleできる**」ことを示します。

### ブロックのライフサイクル

#### ブロックの提案

ブロック提案者（block builder）の選ばれ方自体は従来と変わりません。
ただし、これまでのように「**ブロックを作るときにその場でtransactionを実行して結果を確認する**」ことは求められなくなります。

builderは、最後にsettleが完了したstateを基点にして新しいtransactionを集めます。
そして、その基点から過去にさかのぼる未settleのブロックについて、最悪ケースを想定した条件（worst-case bounds）をあらかじめ適用します。

worst-case boundsとは、例えば「**送信者の残高が最低でも手数料を払える額以上であること**」や「**必要となる最大のbase feeを支払えること**」といった条件です。
これを満たすようにしておけば、実行をまだしていない状態でも「**後で実行しても手数料の支払いができる**」ことが保証されるため、ブロックは安全にconsensusへ提出できます。

最悪ケース境界は、少なくとも以下を含みます。

* 送信者アカウントの**最低残高**（手数料を支払えること）
* 要求され得る**最大base fee**

consensusに提案を載せる前に、すべてのvalidatorは「**builderが最悪ケース境界を正しく適用していたか**」を検証する必要があります。
これにより、そのブロックが受理後に問題なく実行できる（=手数料面で成立する）ことが担保されます。

:::message
この仕組みで確認できるのは、「**transactionが手数料を支払える状態にあるか**」という点だけです。
一方で、そのtransactionがrevertしないことやgasの消費が上限に達しないことまでは確認できません。
つまり、worst-case boundsによる検証は、あくまで手数料の支払い可能性を保証するものに限られます。
:::

#### ブロックの受理

consensusがブロックを受理（accepted）すると、そのブロックはFIFOの実行キューに入ります。

#### ブロックの実行

各クライアントは並列に**block executor**を動かし、FIFOキューからブロックを順に実行し続けます。
executorは、各ブロック実行の開始時刻と終了時刻を決定的（deterministic）に記録します。

executorが用いる「**時間（timestamp）**」は次の2系統です。

1. ブロックヘッダーに含まれるtimestamp
2. ガス課金量（実行で消費・課金されたgasの合計）

:::message
blockexecutorが記録する実行timestamp（時刻）は、ブロックヘッダーのtimestampより短い時間単位で記録します。
例えば、ヘッダーが「**秒**」単位なら、executorは「**ミリ秒**」などで記録します。
これにより、1秒未満の実行も正確に扱えます。
:::

#### 実行の流れ

1. **実行開始**
    ブロックが実行キューに入ると、executorはすぐにそのブロックの処理を始めます。
2. **タイムスタンプの調整**
    executorの現在のtimestampが、対象ブロックのtimestampよりも古い場合、executorは自分のtimestampをブロックのtimestampに合わせて進めます。
    このとき、実際には処理をしていない時間を進めるため、未使用のgas容量が発生し、その分だけ**gas excess**（価格計算に使う余剰指標）が減少します。
3. **ブロックの実行**
    executorは、直前に実行した（ただしsettleはまだしていない）stateを基盤に、対象のブロックを実行します。
4. **ガス使用量に基づくtimestampの更新**
    実行が終わると、そのブロックで実際に使ったgas量に応じてexecutorのtimestampを進めます。
5. **gas excessの調整**
    timestampを進めると**gas excess**が増加し、これが料金アルゴリズムに反映されます。
6. **settle可能状態になる**
    こうしてブロックには実行時刻が記録され、**settle（確定処理）**に移れる状態になります。

#### ブロックの確定

すでに実行が完了したブロックは、後続のブロックがその実行結果を取り込み、consensusで受理された時点で確定（settled）となります。
つまり、単に実行しただけではまだ最終確定ではなく、その結果が次のブロックに組み込まれた瞬間に正式な記録としてチェーン上に定着します。
#### ブロックの確定（書き直し・番号付き）

1. **確定のタイミング**
    実行が終わったブロックは、後続のブロックがその実行結果を取り込み、consensusで受理された時点で確定（settled）とみなします。
2. **取り込み内容（state root の設定）**
    後続ブロックは、自身の**state root**を「**最後に実行されたブロック**」のstate rootに設定します。
    これにより、直前までの実行結果がチェーン上に反映されます。
3. **取り込み内容（receipt root の設定）**
    後続ブロックは、自身の**receipt root**を「**直近の確定以降に発生したすべてのreceipt**」をまとめた**MPT（Merkle Patricia Trie）**のrootに設定します。
    ここに含めるreceiptは、複数ブロック分でも問題ないです。
4. **どの実行済みブロックを確定させるかの判定**
    判定には後続ブロックのtimestampを使います。
    後続ブロックのtimestampが、対象ブロックの**実行時刻＋定数遅延（constant delay）**以上であれば、その対象ブロックを確定します。
5. **定数遅延（constant delay）の意味**
    この固定の待ち時間は、block executorでたまに起きる処理の遅れをならすためのバッファです。
    これにより、個々の遅延が確定タイミングに与える影響を平準化できます。

## 仕様

#### 背景

ガス価格を決定する仕組みは、過去の提案（**ACP103**、**ACP176**）を土台としています。

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

[\[ACP103\] Avalanche P-Chainの動的手数料の仕組みを理解しよう！](/articles/347024f238de4641e7b3)

:::message
**ACP176**については以下の記事を参考にしてください。
:::

[\[ACP176\] AvalancheEVMの動的ガス調整の仕組みを理解しよう！](/articles/8e9ccb3b0316ca1ad7c8)

**ACP103**では以下の変数が導入されました。

| 変数    | 説明               |
| ----- | ---------------- |
| $T$ | 1秒あたりに消費するガスの目標値 |
| $M$ | 最小ガス価格           |
| $K$ | ガス価格更新のための定数     |
| $R$ | 1秒あたりに追加されるガス容量  |

さらに**ACP176**では、$T$を動的にする仕組みが加えられて以下のように定義されました。

$$
\begin{align}
R &= 2 \cdot T \\
K &= 87 \cdot T
\end{align}
$$

実際の消費が目標値を超えた分を「**excess（超過量）$x \ge 0$**」として追跡し、これを用いてガス価格を以下の式で計算します。

$$
M \cdot \exp\left(\frac{x}{K}\right)
$$

#### ガスの課金方法

トランザクションごとに、以下の3つの指標を導入します。

* $g_L$
    gas limit（トランザクションが宣言するガス上限）
* $g_U$
    gas used（実際に使ったガス量）
* $g_C$
    gas charged（課金対象となるガス量）

定義は以下です。

$$
g_C := \max\left(g_U, \frac{g_L}{\lambda}\right)
$$

ここで $\lambda$ は、gas limitに基づいて最低限課金されるガス量を決めるための定数です。
$\dfrac{g_L}{\lambda}$ は切り上げで計算されます。
式としては以下のように表されます。

$$
\frac{g_L + \lambda - 1}{\lambda}
$$

今後、ガス使用量に基づいて計算していた箇所は、すべて $g_U$ ではなく $g_C$ を使います。
例えば、excess $x$ を更新するときには $g_C$ を用います。

#### ブロックサイズ

ブロックの実行からsettleまでの遅延を表す定数を $\tau$（秒）とします。

ブロックの最大サイズ $\omega_B$ は以下の式で表されます。

$$
\omega_B := R \cdot \tau \cdot \lambda
$$

もしトランザクションのgas limitの合計が $\omega_B$ を超える場合、そのブロックは無効とされます。

#### キューサイズ

ブロックがキューに追加される前に、現在のキューサイズが制限を超えていないかを確認します。

実行キューの最大サイズは次の式で表されます。

$$
\omega_Q := 2 \cdot \omega_B
$$

もし新しいブロックを追加する時点でキューサイズが $\omega_Q$ を超えていれば、そのブロックは無効となります。
これにより、ブロックサイズの制限は常に $\omega_B$ だけに依存することが保証されます。

#### ブロックエグゼキュータ

SAEが有効化されると、block executorのtimestamp $t_e$ は最後にacceptedされたブロックのtimestampに初期化されます。

ブロック（timestamp $t_b$ を持つ）を実行する前に、executorは次のようにtimestampとexcessを更新します。

$$
\begin{align}
\Delta{t} &:= \max\left(0, t_b - t_e\right) \\
t_e &:= t_e + \Delta{t} \\
x &:= \max\left(x - T \cdot \Delta{t}, 0\right)
\end{align}
$$

その後、この時点での $x$ をもとにガス価格を計算してブロックを実行します。

ブロックの実行が終わり、合計で $g_C$ ガスが課金された場合、executorは以下のように更新します。

$$
\begin{align}
\Delta{t} &:= \frac{g_C}{R} \\
t_e &:= t_e + \Delta{t} \\
x &:= x + \Delta{t} \cdot (R - T)
\end{align}
$$

:::message
$t_e$ はgas消費と実時間の両方で進むタイムスタンプです。
$\dfrac{g_C}{R}$ の計算結果は、整数に切り捨てたり切り上げたりしてはいけません。
ガスの累積は小数を含む値のまま保持して正確に加算していく必要があります。
:::

この更新後の $t_e$ が、そのブロックの実行timestampになります。

#### ガスターゲット変更時の扱い

もしブロックの中で $T$ が変更される場合、consensusスレッドとexecutionスレッドの両方が、ブロックを処理したあとに新しい $T$ を採用します。

* キューサイズの制限は親ブロックの $T$ に基づいて計算します。
* ブロック実行にかかる時間も、親ブロックの $T$ を使います。

#### ブロックの確定（settlement）

あるブロックが timestamp $t_b$ を持って提案されたとき、そのブロックより前に実行されたブロックのうち、実行が終わった時刻 $t_e$ が $t_b - \tau$ 以前のものは、すでに確定済み（settled）とみなされます。

つまり、一定の遅延時間 $\tau$ を過ぎてから初めて、そのブロックの実行結果がチェーン上で正式に確定するという仕組みです。

$$
t_e \leq t_b - \tau
$$

ここで $t_e$ はfractional seconds（小数を含む実行時刻）であり、ブロックヘッダーの $t_b$ とは異なり整数ではありません。

提案されたブロックには、直近でsettledされたブロックのstateRootを必ず含める必要があります。

提案中のブロックで、新たに確定させるブロックがある場合は、以下の実行結果データを必ず含めてください。

* `receiptsRoot`
* `logsBloom`
* `gasUsed`

`receiptsRoot`の計算は [**EIP2718**](https://eips.ethereum.org/EIPS/eip-2718) に従います。
ただし、今回まとめて確定させる複数ブロック分のreceiptを連結してから木構造を作る点が通常と異なります。

:::message
**EIP2718**については以下の記事を参考にしてください。
:::

[\[EIP2718\] Ethereumのトランザクションを拡張するための仕組みを理解しよう！](/articles/1d273e5564158eebe463)

:::message
blockexecutorの処理が遅れていると、「**提案中のブロックより前に実行が終わっているどのブロックを、今確定扱いにできるか**」をノードが即時に判定できない場合があります。
このときは、validatorはexecutorが追いつくまで、そのブロックの有効性判断を保留してください。
:::

#### ブロックの有効性と構築

まず「**今回の提案ブロックで確定（settlement）させる範囲**」を決めたあとで、それより前に並んでいる未確定の先行ブロックについて、次の2点の**worst-casebound（最悪ケースの上限見積もり）**を計算します。

* **excess $x$ のworst-case**
    blockexecutorの更新式をそのまま使いますが、ガス量には $g_C$ ではなく **$g_L$** を代入してシミュレーションします。
    これにより、「**価格計算に使う $x$ が最悪どこまで大きくなり得るか**」を保守的に見積もれます。

* **アカウント残高のworst-case**
    各senderについて、上で求めた**worst-caseの $x$** から得られる**gasprice**を用い、そのトランザクションに対して**最大のガスコストを負担する**と仮定して残高から差し引きます。
    あわせて、そのトランザクションの **value**（送金額）も差し引きます。
    これで「**最悪条件でも残高不足にならないか**」を検証します。

最後に、ブロックヘッダーの `baseFeePerGas` は、ブロック実行開始時点のworst-case $x$ に基づいて計算した**gasprice**で埋めます。
この手順により、builderは実行を伴わずとも、手数料の支払い要件を満たすことが検証済みのブロックだけを安全に提案できます。

#### 設定パラメータ

SAEでは、$\tau$ と $\lambda$ をパラメータとして設定します。
$\omega_B$ と $\omega_Q$ は $T$ から自動的に算出されます。

C-Chainでの推奨設定値は以下のとおりです。

| パラメータ       | 説明                      | C-Chainでの値 |
| ----------- | ----------------------- | ---------- |
| $\tau$    | 実行からsettlementまでの遅延時間   | 5秒         |
| $\lambda$ | gas limitに対する課金量の最低換算比率 | 2          |

この仕様によって、ガス料金計算、ブロックサイズやキューサイズの制御、executorの動作、settlementのルールが一貫して定義され、非同期実行（SAE）を安全かつ効率的に進められるようになります。

## 互換性

**ACP194**では、ブロック内部の複数のフィールドの意味が変更されます。
具体的にどのように変わるかの詳細な一覧は、参考実装が完成した時点でまとめられる予定です。

現時点で変更が予想されている主なフィールドは以下のとおりです。

* `stateRoot`
* `receiptsRoot`
* `logsBloom`
* `gasUsed`
* `extraData`

これらのフィールドは、ブロックの確定タイミングや実行結果の扱い方が変わることによって、これまでと解釈が異なるようになります。
そのため、既存のクライアントやツールは、この変更に対応するための調整が必要になる可能性があります。

## 参考実装

**ACP194**の参考実装は、現在まだ開発途中の段階です。
完成次第、このACPの内容は更新され、実装例が含まれる予定です。

## セキュリティ

#### 最悪ケースに基づくトランザクションの有効性

SAEでは、実行時におけるDoS攻撃（実行を妨害する攻撃）を防ぐため、通常のトランザクション有効性チェック（nonceや署名の確認など）に加えて、トランザクションの手数料支払い能力に関する上限チェックが必要になります。
これを「**worst-case cost validity（最悪ケースコスト有効性）**」と呼びます。

これは「**各トランザクションがガスlimitいっぱいまで使った場合でも支払いが可能であるか**」を確認する仕組みです。
この条件を課すことで、以下の2点が最悪ケースとして成立します。

1. 各トランザクションが最大限にガスを消費する（ガスlimitによる定義）。
2. 実行時点での**gas excess**（目標を超えて消費した分、$x$）が最大化し、ガス価格が最も高くなる。

queue内のブロック $Q={i}_{i \ge 0}$ において、ブロック $j$ を実行する直前の excess $x_j$ は、先行ブロックのガス使用量に依存する単調非減少関数です。
つまり、あるブロックのガス消費を減らせば、その後の excess は必ず予測よりも小さくなり、結果的に後続ブロックのガス価格も下がります。

よって、すべてのトランザクションが最大ガスを消費すると仮定したときが「**最悪のexcess**」となり、このときのガス価格を基に各トランザクションの**worst-case gas cost**を計算できます。
送信者がこの最悪コストを支払えるのであれば、実際の実行コストも必ず支払可能です。

したがって、worst-case cost validityに基づいてトランザクションがacceptされることは、そのトランザクションが最終的に**settleされる**ことの保証につながります。

#### Queueに対するDoS保護

worst-case cost validityは実行段階のDoSを防ぎますが、キューに対しては別の攻撃の可能性が残ります。

例えば、攻撃者が単純なtransfer（通常は約21k gas使用）に対して、ブロック全体のgas limit分を上限に設定したトランザクションを大量に送ることが考えられます。
この場合、実際には21k gasしか使わなくても、キュー上ではworst-caseで莫大なコストが予約されたことになります。
これが繰り返されれば、他のユーザーのトランザクションのworst-caseコスト見積もりを不自然に押し上げることが可能です。

これを防ぐため、ガスの課金方法を修正しました。従来は「**実際に使ったガス量 = gas used**」に基づいていましたが、SAEでは次のように定義します。

$$
g_C := \max\left(g_U, \frac{g_L}{\lambda}\right)
$$

ここで $\lambda$ は調整用のパラメータです。

* $\lambda = 1$ だと、攻撃者が高いgas limitを設定しても影響を防げません。
* $\lambda = \infty$ だと、逆に小さな消費量でも大きな攻撃が成立してしまいます。

そこで $\lambda = 2$ と設定します。
これは「**予測より2倍のガス消費までは許容する**」という余裕を持たせつつ、根拠のない高いgas limitを付けるインセンティブをなくすことを狙っています。

#### Queue DoSの上限値

DoS攻撃の効果を定量化するために、以下の指標を使います。

* 実際のexcessを $x_A$ とし、それに基づくガス価格を $f_A$ とする。
* worst-caseのexcessを $x_W$ とし、それに基づくガス価格を $f_W$ とする。

このとき、攻撃者「Mallory」のDoS効果は以下の式で表せます。

$$
D := \frac{f_W}{f_A}
$$

つまり、worst-caseの見積もりと実際の価格の比率です。
計算を進めると、攻撃によって生じる最悪の差は以下のように表せます。

$$
D \le \exp \left( \frac{2 \cdot T \cdot \tau \cdot (\lambda-1)}{K} \right)
$$

ここで $K = 87 \cdot T$ を代入すると、

$$
D \le \exp \left( \frac{2 \cdot \tau \cdot (\lambda-1)}{87} \right)
$$

となります。

提案されているパラメータ $\tau = 5, \lambda = 2$ を適用すると、

$$
\begin{align}
D &\le \exp \left( \frac{2 \cdot 5 \cdot (2-1)}{87} \right) \\
   &= \exp \left( \frac{10}{87} \right) \\
   &\approx 1.12
\end{align}
$$

つまり、攻撃者が行える影響は、ユーザーに最大で約12%程度のガス価格上昇を強いる程度です。

実際のネットワークでは、ガス価格は日常的に12%以上変動するため、この影響は大きな問題にはならないと考えられます。
ただし、入札メカニズムの健全性を損なう可能性がある点は引き続き注意が必要です。

## 付録

#### JSON RPCメソッド

非同期実行（SAE）では、トランザクションとそのreceipt（実行結果の記録）が、必ずしも同じブロックに結びついて記録されるわけではありません。
しかし、APIの動作はこの変更に合わせて変えてはいけません。

特に `eth_getBlockReceipts` メソッドは、ブロックに含まれるトランザクションに対応するreceiptを返す必要があります。
「**そのブロックでsettleされたreceipt**」を返してはいけません。
これにより、既存のアプリケーションがAPIの変更に影響を受けないことが保証されます。

### 名前付きブロックの扱い

Ethereum MainnetのAPIでは、特定の名前を使ってブロックを取得できます。
例えば、 `_latest_` や `_pending_` といった指定です。
これはconsensusの仕組みに基づいてAPIサーバが解釈するもので、SAEでは以下のように解釈が対応付けられます。

* ***earliest***
    ジェネシスブロック（最初のブロック）。これは従来と変わりません。
* ***pending***
    もっとも最近 **accepted** されたブロック。
* ***latest***
    もっとも最近 **executed** されたブロック。
* ***safe* / *finalized***
    もっとも最近 **settled** されたブロック。

:::message
Snowman consensusでは**finality（最終性）**の保証が強固であるため、`safe` と `finalized` の間に意味の違いはありません。
また `latest` ブロックはre-org（チェーンの巻き戻し）のリスクがなく、APIノードのローカルなデータ破損によるごくわずかなリスクだけが残ります。
:::

#### トランザクションの優先順位に関する観察

SAEにおいて、EOA（Externally Owned Account）同士の単純なvalue transferは、ブロックがconsensusでacceptされた時点で「**必ず実行されることが確定する**」という扱いになります。
つまり、この種類のトランザクションは、後続のexecutionやsettlementを待たずに「**実行されることが合意済み**」になるため、ブロックビルダーは場合によってはこれらをすぐに処理せず、他の複雑なトランザクションを優先させることも可能です。

こうした単純なtransferは、gas limitが21,000に設定されていることで識別できます。
これは、送信者が「**コントラクト呼び出しを伴わない純粋な送金だけを意図している**」ことを示しています。

ただし、このtransferを遅らせると、それに依存している後続トランザクション（例えば送金先のアカウント残高を利用する取引）の実行が遅れる可能性があります。
そのため、ブロックビルダーはどのトランザクションをどの順序で扱うかを、ネットワークの状況や最適化方針に応じて判断する必要があります。

## 最後に

今回は「ブロックの順序決定（consensus）と実行（execution）を切り離して並行させ、混雑時でも安全に処理を進めながら手数料と待ち時間を安定させる仕組みを提案している**ACP194**」についてまとめてきました！
いかがだったでしょうか？

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

[Twitter @cardene777](https://twitter.com/cardene777)

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

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
