# [ACP181] ICM検証を安定化するP-Chainエポック化の仕組みを理解しよう！

> ACP181は、P-Chain（プライマリチェーン）におけるエポック（一定期間の単位）化のための標準スキームを定義する提案です。

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

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

今回は、AvalancheのP-Chainに「**エポック**」という時間区切りを導入し、一定期間ごとにP-Chainの状態（Block Height）を固定することで、バリデータ情報の取得を効率化し、ICMメッセージ検証やネットワークの安定性を高める提案している**ACP181**についてまとめていきます！

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

https://github.com/avalanche-foundation/ACPs/blob/main/ACPs/181-p-chain-epoched-views/README.md

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

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

## 概要

**ACP181**は、P-Chain（プライマリチェーン）におけるエポック（一定期間の単位）化のための標準スキームを定義する提案です。
**ACP181**を実装する仮想マシン（VM）は、次のブロックを生成する前にどのP-Chain Block Height（ブロックの高さ）を使用するかを事前に知ることができます。

つまり、VMが次のブロックを生成するときに「**どのP-Chain状態を参照するのか**」を明確にする仕組みです。
これにより、現在はブロック実行時に行わなければならないバリデータセット（検証者リスト）の取得処理を事前に最適化できるようになります。

この標準は、P-Chain自体のVM（仮想マシン）にエポック機能を直接追加するものではありません。
代わりに、**ProposerVM**（ブロック提案機能をもつVM）など、P-Chainの状態をVMへ反映するレイヤーで**ACP181**を実装できるようにします。

## 動機

P-Chainは、L1チェーン（レイヤー1）のバリデータ（ネットワークの検証者）を管理しています。
この中には、プライマリネットワーク（Avalancheのメインネット）のバリデータも含まれます。

バリデータの追加・削除・重み（ステーク量など）の変更は、P-Chainトランザクションとして記録され、P-Chainブロックに含まれることで反映されます。
したがって、L1やサブネットのバリデータセットを説明するとき、それは「**特定のP-Chainブロック高における有効なバリデータ（重み、BLS鍵、ノードID）**」を表しています。

ここで問題になるのは、アプリケーションによって「**どの時点のバリデータ情報が必要か**」が異なる点です。

* **オンデマンドで過去の状態を確認したい場合**
    → 任意のP-Chainブロック高でのバリデータセットを取得する必要があります。
* **最新の状態を常に把握したい場合**
    → ほぼ毎ブロックごとにバリデータ情報を取得する必要があります。

しかし、この頻繁な取得は効率が悪く、特に過去にさかのぼってデータをたどる場合には計算コストが大きくなります。

そこで、この提案では「**一定期間（エポック）の間、P-ChainのBlock Heightを固定する**」という仕組みを導入します。
これにより、VMやサブネットはその期間中に安定したP-Chainの状態を参照できるようになります。

このエポック化により、以下のような最適化が可能になります。

- **事前取得の効率化（プリフェッチ）**
    エポックごとに一度だけバリデータセットを取得すればよくなり、毎ブロックの取得が不要になります。
- **履歴参照の効率化**
    エポック単位で過去のバリデータ状態をたどれるようになり、ブロックごとの解析よりも高速に処理できます。

このように、P-Chainのエポック化は、バリデータ情報を扱うVMやネットワーク層のパフォーマンスと一貫性を大きく改善する仕組みとして位置付けられています。

## 仕様

:::message
#### 課題

AvalancheのP-Chainでは、L1チェーンやサブネットのバリデータセット（検証者の一覧）を参照する必要があります。
しかし現状では、各VM（仮想マシン）がブロックを実行するたびに、どのP-Chain Block Heightを参照するかをその場で遡って探しに行く必要があります。
この方法には以下の問題があります。

* **計算コストが高い**
    探索する範囲が不定のため、最悪のケースを想定して多めのガス（手数料）を消費する必要があります。
* **ICM（チェーン間メッセージ）の検証が不安定**
    Relayer（オフチェーンでメッセージを中継するノード）は、送信時と検証時でP-Chainの状態が変わると検証に失敗します。
* **履歴参照が非効率**
    過去のバリデータ状態を確認する時も、ブロックごとにP-Chainをたどる必要があり処理が重くなります。

このように、P-Chain状態の参照タイミングがブロックごとに異なることが、パフォーマンスと安定性の両面で課題となっていました。

#### 解決の仕組み

**ACP181**は、この問題を「**エポック（一定期間の単位）**」という仕組みでP-ChainのBlock Heightを固定することで解決しようとしています。

##### エポックによるP-Chain状態の固定

エポックとは、連続するブロックのまとまりで、以下の3つの値を共有します。

| 項目                   | 内容                     |
| -------------------- | ---------------------- |
| Epoch Number         | エポックを識別する番号            |
| Epoch P-Chain Height | そのエポックで固定されるP-ChainのBlock Height |
| Epoch Start Time     | エポックの開始時刻              |

各エポックは一定時間（D）経過後に「**封鎖（Sealing）**」され、次のブロックが新しいエポックの開始となります。
これにより、一定期間中は同じP-Chain状態を参照し続けることができるようになります。

##### ICM検証とリレーラーの安定化

エポック化によって、ICM検証時に参照するP-ChainのBlock Heightがエポック単位で固定されます。
その結果、以下のような改善が得られます。

* **ガスコストの削減**
    P-Chainの探索をブロックごとに行う必要がなくなり、バリデータセットをエポック開始時にまとめて取得できます。
* **リレーラーの信頼性向上**
    エポック中は検証に使うP-Chainの状態が変わらないため、Relayerはどのバリデータセットを使えばよいかを事前に把握できます。

##### 履歴参照の効率化

過去の状態を調べる時も、ブロックごとではなく**エポック単位**でたどればよくなります。
これにより、過去のバリデータ情報を効率的に取得でき、履歴処理が軽量化されます。

#### まとめ

ACP181は、P-Chainの状態を時間的に安定させる「**エポック**」構造を導入することで、以下のような課題をまとめて解決します。

| 課題                | 解決方法                               |
| ----------------- | ---------------------------------- |
| バリデータセットの取得コストが高い | エポックごとにBlock Heightを固定し、事前取得を可能にする |
| リレーラーが検証失敗しやすい | 検証に使うP-Chainの状態をエポック期間中固定する  |
| 履歴参照が非効率 | エポック単位で過去をたどることで高速化        |

**ACP181**は、P-ChainとVM間の同期を「**エポック化**」で整理し、安定性・効率性・予測可能性を高める仕組みです。
:::

### 前提条件

**ACP181**では、あるブロック $b_m$ が以下の2つの情報を持つと仮定しています。

| 項目    | 意味                                             |
| ----- | ---------------------------------------------- |
| $t_m$ | ブロック $b_m$ のタイムスタンプ（生成時刻）　|
| $p_m$ | ブロック $b_m$ のP-Chain Block Height（P-Chain上でのBlock Height） |

この前提に基づいて、エポック（一定期間内で区切られたブロックのまとまり）を定義します。

### エポックの定義

エポックとは、連続する複数のブロックが共有する3つの値によって定義される期間です。

| 項目                   | 内容                            |
| -------------------- | ----------------------------- |
| Epoch Number         | エポックを識別するための番号                |
| Epoch P-Chain Height | エポックに対応するP-ChainのBlock Height |
| Epoch Start Time     | エポックの開始時刻                     |

エポック番号 $N$ のエポックを $E_N$ と表します。
このとき、$E_N$ の開始時刻を $T_{start}^N$、P-Chain Height を $P_N$ とします。

ここで、ブロック $b_a$ が**ACP181**を有効化するブロックであるとします。
最初のエポック $E_0$ は以下のように定義されます。

* $T_{start}^0 = t_{a-1}$
    → 有効化ブロックの直前のブロックのタイムスタンプが、最初のエポックの開始時刻になります。
* $P_0 = p_{a-1}$
    → 同様に、直前のブロックのP-Chain Heightが最初のエポックのP-Chain Heightになります。

つまり、ACPが有効になる直前のブロックの情報をもとに、最初のエポックが始まります。

### エポックの封鎖

エポック $E_N$ は、以下の条件を満たす最初のブロックによって「**封鎖（sealed）**」されます。

* ブロックのタイムスタンプが $T_{start}^N + D$ 以上であること
  （$D$ はこの仕様を有効化するネットワークアップグレードで定義された定数）

この条件を満たす最初のブロックを **$B_{S_N}$（封鎖ブロック）** と呼びます。

ここで重要なのは、封鎖ブロック自体も、その封鎖されるエポックの一部であるという点です。
この定義により、どのエポックも必ず1つ以上のブロックを含むことが保証されます。

### エポックの進行

現在のエポック $E_N$ から次のエポック $E_{N+1}$ へ進むのは、封鎖ブロック $B_{S_N}$ の次のブロックが生成されたときです。
次のエポックの最初のブロックは、以下のような値を持ちます。

| 項目                | 内容                               |
| ----------------- | -------------------------------- |
| $P_{N+1}$         | 封鎖ブロック $B_{S_N}$ のP-Chain Height |
| $T_{start}^{N+1}$ | 封鎖ブロック $B_{S_N}$ のタイムスタンプ        |
| Epoch Number      | $N+1$（前のエポック番号に1を加えた値）           |

つまり、各エポックは明確な境界を持ち、封鎖ブロックの情報を基準として次のエポックへ移行します。

### 特性

#### エポックの期間

エポックの開始時刻は、前のエポックを封鎖したブロックのタイムスタンプに設定されます。
そのため、どのエポックも少なくとも期間 $D$ を持つことが保証されます。

ただし、封鎖ブロックは「**自分が封鎖するエポックの一部**」として定義されているため、エポックの上限時間（最大の長さ）は存在しません。

つまり、封鎖ブロックがいつ生成されるかによって、エポックの実際の長さは可変となります。
理論上、封鎖ブロックが非常に遅く生成されれば、そのエポックは非常に長く続くことになります。

#### P-Chain Heightの固定

Avalancheブロックチェーンでは、ブロックを生成する時に、そのブロック内に埋め込まれたP-Chain Heightを使ってバリデータセット（検証者リスト）を決定します。

もし、個々のブロックのP-Chain Heightではなく、エポック単位のP-Chain Heightを使用するようにすれば、
次のブロック生成時に利用するバリデータセットを事前に確定できるようになります。

* ブロック $b_m$ がエポック $E_N$ を封鎖した場合
    次のブロック $b_{m+1}$ は新しいエポック $E_{N+1}$ に属し、そのP-Chain Height $P_{N+1}$ は $b_m$ のP-Chain Height（$p_m$）になります。

* 一方で、$b_m$ が $E_N$ を封鎖しない場合
    $b_{m+1}$ は引き続き $E_N$ に属し、$P_N$ を使用します。

どちらの場合でも、次のブロック $b_{m+1}$ が使う可能性のあるP-Chain Height（$p_m$ または $P_N$）は、ブロック $b_m$ の生成時点で確定しています。

この設計により、ブロック生成時にバリデータセットを即座に参照できるため、
バリデータセット取得処理の効率化と確定性の向上が実現されます。

## ユースケース

### ICM検証の最適化

ICM（Interchain Messaging、チェーン間メッセージング）の検証では、メッセージを署名したL1またはサブネットのバリデータセットを取得する必要があります。
現行の仕組みでは、バリデータセットの取得はブロック実行中に行われ、以下のような問題があります。

ブロックの検証時、現在のP-Chain Height（P-Chain上でのブロックの高さ）から、ProposerVMが提供するP-Chain Heightまで逆方向にたどってバリデータセットを探しに行きます。
この「**逆方向の探索**」にどれだけ時間がかかるかは状況によって大きく変わるため、最悪のケースを想定してVM実装では固定的に多めのガス（手数料）を消費するようになっています。

#### エポック導入による改善

エポックを導入すると、バリデータセットの取得が一定のP-Chain Height間隔で行われるようになります。
これは、各エポックごとにP-Chain Heightが固定されるためです。

このようにエポックが明確に区切られることで、バリデータ情報の取得に以下のような最適化が可能になります。

| 改善点 | 内容    |
| ----- | ------ |
| 一定間隔での取得 | P-Chain Heightが一定期間ごとに固定されるため、取得ポイントが予測可能になります。              |
| 非同期取得の実現 | エポック開始から一定時間（$D$）が経過した時点で、ブロック実行とは別にバリデータセットを事前取得できるようになります。 |
| ガスコストの削減 | 取得処理の予測が可能になることで、検証処理にかかるガスコストを安全に大幅削減できます。                  |

これにより、ICMメッセージの検証がより効率的かつコスト面でも最適化されます。

### リレーラー信頼性の向上

現行のICM VM実装では、ICMメッセージの検証に使用するP-Chainの状態はProposerVMが設定したP-Chain Heightに基づいて行われます。
この仕組みのもとで、オフチェーンのリレーラー（ICMメッセージを他のチェーンに中継する役割のノード）は、以下の手順でメッセージを送信します。

| ステップ | 処理内容   |
| ---- | -------- |
| 1    | 検証側チェーンの現在の提案P-Chain Heightに基づいて、送信元チェーンのバリデータセットを取得する。 |
| 2 | そのバリデータセットからBLS署名を収集し、署名済みのICMメッセージを構築する。  |
| 3  | 署名済みメッセージをトランザクションとして検証側チェーンに送信する。  |

この流れの中で、ステップ1から3の間にバリデータセットが変化すると、メッセージの検証が失敗します。
つまり、送信と検証の間にP-Chainの状態が変わることが信頼性の低下につながっています。

#### エポック導入による信頼性の向上

エポックの導入により、一定期間（エポック期間）中は検証に使用するP-Chain Heightが固定されます。
これにより、オフチェーンのリレーラーは次のような改善を得られます。

| 改善点 | 内容  |
| ----- | ------ |
| 状態の安定化    | エポック期間中はP-Chain Heightが変わらないため、バリデータセットが安定します。                              |
| 予測可能な動作   | リレーラーはエポックの境界を仕様から計算したり、ノードAPI経由で取得できるため、どのP-Chain Heightを参照すべきかを正確に判断できます。 |
| 検証ルールの柔軟化 | VMが前エポックのバリデータセットでの検証を許可するようにすれば、エポック境界での誤差やタイミングのズレによる失敗を回避できます。 |

このように、エポックによってリレーラーは「**いつ・どのP-Chain Heightで検証が行われるか**」を予測できるようになり、チェーン間メッセージ伝達の信頼性と安定性が大幅に向上します。

## 互換性

この変更はネットワークアップグレードを前提としており、既存ネットワークとの後方互換性はありません。
アップグレード適用後は、VMが参照するP-Chainの見え方がエポック単位になります。

そのため、VMのP-Chain状態に依存している周辺コンポーネントや外部システムも、エポック化されたP-Chainのビューに対応する必要があります。
具体例として、ICMメッセージは「**特定のP-Chain Block Height**」におけるL1のバリデータセットで署名されます。
これまでは、実運用上はP-Chainの先頭（チップ）近辺の状態を使っても、各Avalanche VMがP-Chainに対して一定の最大遅延（固定ブロック数以内）しか持たないため実用上問題が起きにくい状況でした。

しかしエポック化後は、検証側チェーンのエポックP-Chain Block Heightを考慮する必要があります。
このBlock Heightは、P-Chainのチップから任意の距離だけ遅れている可能性があります。
したがって、ICMメッセージの作成時には、検証される側のチェーンが属するエポックのP-Chain Block Heightを前提にバリデータセットを選ぶ必要があります。

## 参考実装

以下の擬似コードは、あるブロックに対して所属エポックをどのように決定するかを示します。
基本的な考え方は、親ブロックのタイムスタンプが、親エポックの**開始時刻 + D（エポック期間）**を過ぎているかどうかで判定することです。

```go
// Epoch Duration
const D time.Duration

type Epoch struct {
    PChainHeight uint64
    Number uint64
    StartTime time.Time
}

type Block interface {
    Timestamp() time.Time
    PChainHeight() uint64
    Epoch() Epoch
}

func GetPChainEpoch(parent Block) Epoch {
	parentTimestamp := parent.Timestamp()
	parentEpoch := parent.Epoch()
	epochEndTime := parentEpoch.StartTime.Add(D)
	if parentTimestamp.Before(epochEndTime) {
		// If the parent was issued before the end of its epoch, then it did not
		// seal the epoch.
		return parentEpoch
	}

	// The parent sealed the epoch, so the child is the first block of the new
	// epoch.
	return Epoch{
		PChainHeight: parent.PChainHeight(),
		Number:       parentEpoch.Number + 1,
		StartTime:    parentTimestamp,
	}
}
```

処理の要点は以下です。

* 親ブロックのタイムスタンプが、親エポックの開始時刻 + D より前であれば、親はエポックを封鎖していないため、同じエポックを返します。
* 親ブロックのタイムスタンプが、開始時刻 + D に到達または超過していれば、親はエポックを封鎖したことになります。子ブロックは新しいエポックの先頭となり、以下で新しいエポックを開始します。

  * `PChainHeight` は親ブロックのP-Chain Block Height、
  * `Number` は親エポック番号 + 1、
  * `StartTime` は親ブロックのタイムスタンプ

### エポック期間の設定

エポック期間 $D$ はネットワーク全体で共通の値として設定します。
**ACP181**の有効化時点では、**Fuji（Network ID 5）**と**Mainnet（Network ID 1）**において、$D = 5$分に設定されます。
将来 $D$ を変更する場合は、新たなネットワークアップグレードが必要です。

#### エポック期間の変更

将来のネットワークアップグレードでエポック期間を $D$ から $D'$ に変更する場合、$D'$は現在進行中のエポックが終了するまで適用しません。
すなわち、アップグレードの有効化時刻ではなく、現行エポックの終了時点から $D'$ を有効にします。

この取り扱いにより、アップグレードが適用された瞬間に進行中のエポックの実現期間が、$D$ と $D'$ の両方より短くなってしまうことを避けられます。
結果として、エポックの最小持続時間の保証が維持され、VMや周辺コンポーネントが依存する時間的前提が崩れません。

### 実装時の観点

* **境界条件の一貫性**
    封鎖判定は「**`StartTime + D` に到達した最初のブロックが封鎖する**」ことに注意します。
    封鎖ブロックは封鎖対象エポックの一員です。

* **P-Chain Block Heightの確定性**
    新エポックの `PChainHeight` は封鎖ブロックのP-Chain Block Heightに等しく、次のブロック生成時点で次エポックの検証者集合が確定します。

* **ネットワーク設定の配布**
    $D$ の値はコンセンサスに影響するため、ネットワークアップグレードで一斉に合意させる必要があります。
    Fuji／Mainnetともに同一の初期値（5分）で開始することで、ツールや運用の共通化がしやすくなります。

## セキュリティ

### エポックP-Chain Heightのずれ

エポックは理論上無制限の期間（unbounded duration）を持つ可能性があります。
そのため、ブロックが持つ `PChainEpochHeight`（エポックで固定されたP-Chainの高さ）が、実際のP-Chainの先頭（tip）から大きく遅れる場合があります。

この遅れは、P-Chainエポックビューを実装しているVM内でのICM（Interchain Messaging）検証の有効性には影響しません。
なぜなら、`PChainEpochHeight`におけるバリデータセットは常に確定しているためです。
ただし、以下のような懸念が発生します。

| 想定される問題  | 説明 |
| --------- | ------------------- |
| ノードの応答不能  | バリデータがセットから退出すると、物理的なノードが署名要求（BLS署名リクエスト）に応答できなくなる場合があります。その結果、ICMメッセージを有効な形で構築するのが難しくなります。       |
| 古いバリデータセットによる署名 | 有効なICMメッセージであっても、古いバリデータセットによる署名である可能性があります。`PChainEpochHeight`と最新のP-Chain tipの間でセットから離脱したバリデータの署名は、すでにアクティブなステークを反映しません。 |

これらのリスクを軽減するための実践的な対策としては、以下の2点が挙げられます。

* **エポック期間を短く設定すること**
    P-Chainの更新とL1でのICM検証との間の遅延を小さくできます。
* **安定したブロック生成を維持すること**
    $D$（エポック期間）経過後、速やかに新しいブロックが生成されるようにすることで、エポックが不必要に長引かないようにします。

これにより、P-Chainの進行と検証プロセスの同期が保たれ、古い情報に依存するリスクを抑えられます。

### 過度なバリデータ変動

P-Chainの**エポック化されたビュー**をコンセンサスエンジンが使用する場合、エポック期間中のバリデータセットの変更はエポック境界の1つのブロックに集中します。

このとき、バリデータの入れ替えやステークの増減が短時間に集中しすぎると、以下のような危険が生じます。

* コンセンサスの不一致（フォークや失敗）
* 不安定なバリデータ構成によるネットワーク障害

このような事態を防ぐため、エポック境界でのバリデータ重み変動量を制限することが重要です。
対策としては以下のような方法が考えられます。

| 戦略  | 説明            |
| ------------------- | -------------------------------- |
| バリデータ変更をキュー化     | 変更要求を蓄積し、複数のエポックに分散して適用します。      |
| 同一バリデータへの変更をバッチ化 | 1つのバリデータへの増減を相殺して、実際の変化量を最小化します。 |

**ACP181**は主にICM検証の最適化を目的としており、これらのコンセンサス層の安全策はVMレベルの改善範囲外として設計から除外されています。

## 未解決の課題

提案段階で残されている検討課題は以下の2点です。

| 質問  | 説明     |
| ------------- | ---------------- |
| エポック期間 $D$ の最適値はどれくらいか？   | $D$ が短すぎるとブロック生成や検証コストが増加しますが、長すぎると古いバリデータセットのリスクが高まります。ネットワークの安定性と効率のバランスを考慮した値を決定する必要があります。 |
| `PChainEpochHeight` と `PChainHeight` の差が大きくても安全か？ | エポックが長引いた場合、ブロック内でこの2つの高さに大きな差が生じます。この差がコンセンサスやICM検証に与える影響については、さらなる検証が必要です。 |

これらの課題は、ネットワーク運用上のパラメータチューニングやVM設計の安全性評価に関わるものであり、今後の実装・運用段階での実証と議論が求められています。

## 最後に

今回は「AvalancheのP-Chainに「**エポック**」という時間区切りを導入し、一定期間ごとにP-Chainの状態（Block Height）を固定することで、バリデータ情報の取得を効率化し、ICMメッセージ検証やネットワークの安定性を高める提案している**ACP181**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下の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
