# [ERC8179] Blobを複数プロトコルで分けて使うBlob Space Segmentsの仕組みを理解しよう！

> ERC8179は、EIP4844のBlobを「FEの連続範囲」として区切り、その区間をオンチェーンイベントで宣言するためのインターフェースです。Blobは4096個のFEで構成され、1つのFEは32バイトです。

- URL: https://cardene.dev/articles/0f439e5747638415df85
- 公開日: 2026-06-11
- 更新日: 2026-06-11
- トピック: Blockchain
- タグ: blockchain, ethereum, smartcontract, erc

## はじめに

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

https://amzn.asia/d/gxvJ0Pw

以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください！

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://twitter.com/cardene777

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

今回は「**EIP4844のBlobをFE単位のセグメントに分けて、複数プロトコルが同じBlob空間を共有できるようにするERC8179**」についてまとめていきます。

**ERC8179**は、Blobのどの範囲を誰がどの用途で使ったかを、`BlobSegmentDeclared` イベントとして宣言するための提案です。
コア仕様はストレージへ書き込まず、イベントログだけを正本にするため、Blob共有の境界を安いガスで公開できます。

:::message
以下にまとめられているものを解説しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-8179

## 概要

**ERC8179**は、EIP4844のBlobを「**FEの連続範囲**」として区切り、その区間をオンチェーンイベントで宣言するためのインターフェースです。
Blobは4096個のFEで構成され、1つのFEは32バイトです。
ただし、EIP4844の標準的なエンコードでは高位1バイトを0にして低位31バイトにデータを入れるため、1Blobで使えるデータ量はおよそ126,976バイトです。

**EIP4844**は、ロールアップなどが大量データを安く投稿するために、通常のCalldataとは別のBlobデータ領域を導入した提案です。
BlobそのものはEVMから直接読めませんが、トランザクションに含まれるBlobの `versionedHash` は `BLOBHASH` オペコードで取得できます。

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

[\[EIP4844\] Shard Blob Transactionsの仕組みについて理解しよう！](/articles/4966cf7f6691d7220367)

ERC8179の中心は、`declareBlobSegment` を呼び出して、Blob内の `[startFE, endFE)` という半開区間を宣言することです。
半開区間では `startFE` を含み、`endFE` を含みません。
例えば `[0, 1500)` と `[1500, 3000)` は、境界のFEを重複させずに並べられます。

Blobを複数プロトコルで分ける全体像は以下です。

![ERC8179の全体像](/images/qiita-5b34bdb7-12ee-46ae-9f60-c95c1addd308.webp)

1つのBlobを、L2バッチ、ソーシャルメッセージ、アプリ別データのような複数用途へ分けられます。
各参加者は同じ `BlobSegmentDeclared` イベントフォーマットで、自分が使うFE範囲と `contentTag` を公開します。

## 動機

EIP4844のBlobは128KiBの固定サイズですが、すべてのロールアップが毎回Blobを埋め切るわけではありません。
提案で参照されている調査では、26個のロールアップを6か月分析し、大きいロールアップはBlob利用率が高く、小さいロールアップは5%未満に留まる傾向が示されています。
また、Blob共有によって80%から99%のデータ可用性コスト削減を目指せるとされています。

Blobの未使用FEは、データ可用性としては支払済みの空間です。
小さいプロトコルが空いているFEを使えるなら、既存のBlob送信者は費用の一部を回収でき、別のプロトコルは自分だけでBlobを出すより低コストでデータを投稿できます。
ただし、どのFE範囲を誰が使ったかを共通フォーマットで示す方法がなければ、インデクサー、検証コントラクト、分析ツールがプロトコルごとの独自実装に分かれます。

ERC8179は、このうち「**区間の宣言**」だけを標準化します。
Blobの組み立て方、参加者間の料金精算、セグメントの交渉方法は対象外です。
対象を狭くすることで、既存のBlob送信処理に追加のコントラクト呼び出しを足すだけで採用できます。

## 仕様

ERC8179は、EIP4844上のBlobを前提にし、`BLOBHASH` オペコードで得られる `versionedHash` とFE範囲をイベントへ結びつけます。
コアインターフェースはストレージを使わず、宣言の記録はイベントログだけです。

### 用語

仕様で使う主な用語は以下です。

| 用語 | 内容 |
|---|---|
| FE | EIP4844のBlobを構成するフィールド要素。1Blobあたり4096個あり、1個は32バイトです。 |
| セグメント | Blob内の連続したFE範囲です。`[startFE, endFE)` の半開区間で表します。 |
| `versionedHash` | EIP4844のBlobコミットメントに基づくハッシュです。`BLOBHASH` オペコードで取得します。 |
| `contentTag` | セグメントを使うプロトコルやデータ種類を表す `bytes32` の識別子です。 |
| 宣言者 | `declareBlobSegment` を呼び出す `msg.sender` です。 |

`contentTag` は所有権ではなく、データ種類を識別するラベルです。
そのため、特定のプロトコル名を名乗る宣言が本物かどうかは、宣言者アドレスやプロトコル側の公開ポリシーと合わせて判断します。

### IERC_BSS

ERC8179に準拠するコントラクトは、`IERC_BSS` インターフェースを実装します。

```js
interface IERC_BSS {
    event BlobSegmentDeclared(
        bytes32 indexed versionedHash,
        address indexed declarer,
        uint16 startFE,
        uint16 endFE,
        bytes32 indexed contentTag
    );

    error InvalidSegment(uint16 startFE, uint16 endFE);

    error NoBlobAtIndex(uint256 blobIndex);

    function declareBlobSegment(
        uint256 blobIndex,
        uint16 startFE,
        uint16 endFE,
        bytes32 contentTag
    ) external returns (bytes32 versionedHash);
}
```

このインターフェースは、1つの関数、1つのイベント、2つのエラーで構成されます。
`versionedHash`、`declarer`、`contentTag` はイベントのインデックス付き引数で、ログ検索の主な軸になります。
一方、`startFE` と `endFE` は範囲を示す値であり、イベントデータとして記録されます。

### declareBlobSegment

```js
function declareBlobSegment(
    uint256 blobIndex,
    uint16 startFE,
    uint16 endFE,
    bytes32 contentTag
) external returns (bytes32 versionedHash);
```

Blob内のFE範囲を宣言し、対象Blobの `versionedHash` を返します。

- **引数**

| 引数 | 内容 |
|---|---|
| `blobIndex` | 現在のトランザクションに含まれるBlobの0始まりインデックス |
| `startFE` | セグメントの開始FE。範囲に含まれます |
| `endFE` | セグメントの終了FE。範囲に含まれません |
| `contentTag` | プロトコルやデータ種類を表す `bytes32` 識別子 |

- **戻り値**

| 戻り値 | 内容 |
|---|---|
| `versionedHash` | `BLOBHASH(blobIndex)` で取得したBlobの `versionedHash` |

処理順は以下です。

![declareBlobSegmentの処理フロー](/images/qiita-27ead362-cb23-4a28-a037-283da2714410.webp)

まず `startFE < endFE` と `endFE <= 4096` を満たすか確認します。
条件を満たさない時は `InvalidSegment(startFE, endFE)` でrevertします。
次に、`BLOBHASH(blobIndex)` でBlobの `versionedHash` を取得します。
取得結果が `bytes32(0)` の時は、そのインデックスにBlobが存在しないため `NoBlobAtIndex(blobIndex)` でrevertします。

検証に通ると、`BlobSegmentDeclared` イベントを発行し、`versionedHash` を返します。
コア実装はストレージへ書き込みません。
同じ引数で2回呼び出されても重複排除せず、2つのイベントを発行します。

#### BlobSegmentDeclaredイベント

```js
event BlobSegmentDeclared(
    bytes32 indexed versionedHash,
    address indexed declarer,
    uint16 startFE,
    uint16 endFE,
    bytes32 indexed contentTag
);
```

セグメント宣言をイベントログへ記録します。

| フィールド | 内容 |
|---|---|
| `versionedHash` | 宣言対象Blobの `versionedHash` |
| `declarer` | 宣言者アドレス |
| `startFE` | セグメントの開始FE |
| `endFE` | セグメントの終了FE |
| `contentTag` | プロトコルやデータ種類を表す識別子 |

イベントログは、インデクサーや検証コントラクトの起点になります。
ただし、イベントだけではBlob内のデータ内容までは保証しません。
実際のデータを使う側は、Blobデータ、KZG証明、プロトコル固有のエンコード規則を使って確認します。

#### InvalidSegmentエラー

```js
error InvalidSegment(uint16 startFE, uint16 endFE);
```

セグメント範囲が無効な時にrevertします。
`startFE >= endFE` または `endFE > 4096` の時に発生します。

#### NoBlobAtIndexエラー

```js
error NoBlobAtIndex(uint256 blobIndex);
```

`BLOBHASH(blobIndex)` が `bytes32(0)` を返した時にrevertします。
指定された `blobIndex` にBlobが存在しないことを示します。

`blobIndex` には上限を固定しません。
Blob数の上限は今後のEIPで増える余地があり、存在しないインデックスは `BLOBHASH` のゼロ値で判定できます。

### contentTag

`contentTag` は、`keccak256("protocol.version")` で生成することが推奨されています。
例えば、以下のような値です。

| 用途 | `contentTag` |
|---|---|
| ソーシャルメッセージ v4 | `keccak256("social-blobs.v4")` |
| Optimismバッチ | `keccak256("optimism.bedrock")` |
| Celestia名前空間 | `keccak256("celestia.namespace")` |
| 汎用ロールアップデータ | `keccak256("rollup.generic")` |

`bytes32(0)` の `contentTag` は許可されていますが、推奨されません。
ゼロ値は `keccak256` の出力として現実的に作れず、インデクサーが未設定値の目印として扱う設計と衝突します。

### Blob全体の宣言

Blob全体を使うプロトコルは、`startFE = 0`、`endFE = 4096` で宣言します。

```js
bss.declareBlobSegment(0, 0, 4096, keccak256("myprotocol.v1"));
```

この宣言は、Blobを共有しない既存の使い方と意味が一致します。
Blob全体を使うプロトコルでも、同じイベントフォーマットを出せるため、インデクサーや分析ツールは部分利用と全体利用を同じフォーマットで扱えます。

### Queryable拡張

オンチェーンで宣言済みセグメントを問い合わせたい用途向けに、任意拡張として `IERC_BSS_Queryable` が定義されています。

```js
interface IERC_BSS_Queryable is IERC_BSS {
    struct BlobSegment {
        address declarer;
        uint16 startFE;
        uint16 endFE;
        bytes32 contentTag;
    }

    function getSegments(bytes32 versionedHash, uint256 offset, uint256 limit)
        external
        view
        returns (BlobSegment[] memory segments, uint256 nextOffset);

    function segmentCount(bytes32 versionedHash) external view returns (uint256);
}
```

この拡張は、`versionedHash` ごとのセグメント一覧をストレージへ保存します。
そのため、コアインターフェースより大きいガスコストを受け入れる必要があります。
読み出し時に無制限配列を返すとガスやメモリの問題が出るため、`offset` と `limit` によるページングが前提です。

### Batch拡張

複数のセグメントを1回の呼び出しで宣言する用途向けに、任意拡張として `IERC_BSS_Batch` が定義されています。

```js
interface IERC_BSS_Batch is IERC_BSS {
    struct BlobSegmentParams {
        uint256 blobIndex;
        uint16 startFE;
        uint16 endFE;
        bytes32 contentTag;
    }

    function declareBlobSegments(BlobSegmentParams[] calldata segments)
        external
        returns (bytes32[] memory versionedHashes);
}
```

同じBlobに対する複数区間だけでなく、異なる `blobIndex` の区間も1回で宣言できます。
ただし、どれか1つのセグメントが無効なら、呼び出し全体がrevertします。
これにより、L2バッチとソーシャルメッセージのような複数宣言を同じトランザクション内でまとめて成立させられます。

### 利用例

2つのプロトコルが1つのBlobを共有する例では、ロールアップバッチが前半を使い、ソーシャルメッセージが後半を使います。

```js
optimismBatcher.submitBatch(...);
bss.declareBlobSegment(0, 0, 2000, keccak256("optimism.bedrock"));
bss.declareBlobSegment(0, 2000, 4096, keccak256("social-blobs.v4"));
```

この時、BlobのFE範囲は以下のように分かれます。

| FE範囲 | 内容 | 使用可能データ量の目安 |
|---|---|---|
| `[0, 2000)` | Optimismロールアップバッチ | 62,000バイト |
| `[2000, 4096)` | ソーシャルメッセージ | 64,976バイト |

3つのプロトコルが同じBlobを埋める例もあります。

```js
baseBatcher.submitBatch(...);
bss.declareBlobSegment(0, 0, 1500, keccak256("base.bedrock"));
bss.declareBlobSegment(0, 1500, 3000, keccak256("social-blobs.v4"));
bss.declareBlobSegment(0, 3000, 4096, keccak256("celestia.namespace"));
```

この時、インデクサーは `contentTag` で必要なセグメントだけを抽出できます。
Blob全体を1つのプロトコルが使う設計と違い、複数プロトコルが同じBlob空間を0から4096まで分割して使えます。

## 補足

ERC8179の設計では、オンチェーンで何を保証し、何をオフチェーンに任せるかがはっきり分かれています。
コアは宣言イベントに絞り、ストレージ、重複排除、重複範囲の禁止、料金精算を持ちません。

#### イベントのみの設計

コアインターフェースは `SSTORE` を使いません。
提案では、`declareBlobSegment` の追加コストはおよそ3,500ガスで、状態を持つ実装のおよそ25,600ガスより大幅に安いと説明されています。

| 要素 | ガスの目安 |
|---|---:|
| `BLOBHASH` オペコード | 3 |
| 範囲検証 | 約6 |
| `LOG4` | 約1,875 |
| イベントデータ | 約512 |
| Calldata | 約1,100 |
| 合計 | 約3,500 |

状態を持つ設計では、EIP2929以降の冷たい `SSTORE` が22,100ガスかかります。
ERC8179は宣言をイベントに閉じることで、Blobトランザクションに追加しやすいコストに抑えています。

#### declareという名前

関数名は `registerBlobSegment` ではなく `declareBlobSegment` です。
`register` はストレージに登録して後から問い合わせられる印象を与えます。
一方、コア仕様は永続状態を作らず、イベントで「この範囲を使う」と宣言するだけです。

#### versionedHashを返す理由

`declareBlobSegment` は `versionedHash` を返します。
呼び出し元が後続処理で同じBlobの `versionedHash` を使う時、もう一度 `BLOBHASH` を呼ぶ必要がありません。
ルーティングコントラクトやマルチコールの中で、宣言結果をそのまま後段へ渡せます。

#### uint16を使う理由

Blob内のFE数は4096個で、12ビットあれば表現できます。
Solidityの標準整数型では `uint16` が十分に小さく、4096を表現できます。
ABIエンコード上は `uint16` も `uint32` も32バイトへパディングされますが、`uint16` は値の範囲が小さいことを型から伝えられます。
また、Queryable拡張の `BlobSegment` 構造体では、ストレージ上の詰め込みにも効きます。

#### 半開区間

セグメントは `[startFE, endFE)` の半開区間です。
この方式では、`[0, 2000)` と `[2000, 4096)` をそのまま並べるだけで、重複も隙間もないBlob全体になります。
閉区間の `[startFE, endFE]` では境界で `+1` の計算が必要になり、境界値のミスが入りやすくなります。

#### Blob内ヘッダーではなくイベント

セグメント情報をBlobの先頭にヘッダーとして入れる設計も候補になります。
ERC8179はそれを採用せず、オンチェーンイベントを使います。

理由は主に4つです。

- EVMはBlobの内容を直接読めません。
- Blob内ヘッダーはペイロードに使えるFEを消費します。
- 既存のBlob投稿プロトコルがBlobエンコードを変える必要があります。
- イベントログは `eth_getLogs`、インデクサー、サブグラフで扱いやすい既存プリミティブです。

Blob内ヘッダーは、密に連携したBlob構築をするプロトコルには合います。
一方、汎用標準としては、既存Blobに追加コントラクト呼び出しを足すだけで済むイベント方式の方が採用しやすい設計です。

#### KZG検証の起点

`BlobSegmentDeclared` イベントは、`versionedHash` とFE範囲を記録します。
この値は、EIP4844のポイント評価プリコンパイルでBlob内の値を検証する時の起点になります。

イベントから検証へつなぐ流れは以下です。

![イベントを起点にしたBlobセグメント検証](/images/qiita-49a424f9-0652-4df5-87e8-876c0e613eaa.webp)

検証者はイベントログから `versionedHash`、`startFE`、`endFE`、`contentTag` を取得します。
その後、対象BlobデータとKZG証明を取得し、FEごとにポイント評価プリコンパイルへ `versionedHash`、評価点 `z`、値 `y`、コミットメント、証明を渡します。
プリコンパイルは、指定したBlobコミットメントの評価点に値 `y` が含まれることだけを確認します。
そのバイト列がプロトコルとして正しいかどうかは、各アプリケーションが別途検証します。

オンチェーンKZG検証はFE数に比例して重くなります。
提案では1FEあたり約50,000ガス、10FEで約500,000ガス、100FEで約5,000,000ガス、Blob全体では約200,000,000ガスとされています。
そのため、オンチェーン検証は小さいセグメントに向いています。

#### 重複範囲の扱い

ERC8179は、重複するセグメント宣言をオンチェーンで禁止しません。
重複を禁止するには、既存範囲をストレージに保存し、呼び出しごとに比較する必要があります。
これはコア仕様の低コスト設計と衝突します。

重複範囲があれば、重なったFEでは2つのプロトコルのデータが衝突します。
この問題はインデクサーが検出し、消費側のプロトコルが受け入れる宣言者や範囲を判断します。

#### ERC165を必須にしない理由

ERC8179は `ERC165` を必須にしません。
1関数のインターフェースでは、イベントシグネチャや関数シグネチャで十分に識別できます。
また、`supportsInterface` はデプロイ時や問い合わせ時のコストを増やします。
実装がERC165に対応することはできますが、準拠条件には含まれません。

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

[\[ERC165\] コントラクトが特定のインターフェースをサポートしているか確認する仕組みを理解しよう！](/articles/541184977a5cba51b759)

インターフェースIDを使う実装では、以下の式で求められます。

```js
bytes4(keccak256("declareBlobSegment(uint256,uint16,uint16,bytes32)"))
```

## 互換性

ERC8179は新しいインターフェースを追加するだけで、既存のERCやEVM仕様を変更しません。
EIP4844の `BLOBHASH` オペコードを前提にするため、EIP4844に対応していないチェーンでは使えません。

既にBlob全体を使っているプロトコルは、`[0, 4096)` を宣言するだけで採用できます。
独自のBlob登録イベントやレジストリを使っているプロトコルは、`IERC_BSS` を直接実装するか、独立した `BlobSpaceSegments` コントラクトを同じトランザクションで呼び出す方法を取れます。

## テストケース

提案で示されている成功ケースは以下です。

| `blobIndex` | `startFE` | `endFE` | `contentTag` | 期待結果 |
|---:|---:|---:|---|---|
| 0 | 0 | 4096 | `keccak256("test.v1")` | Blob全体の宣言イベントを発行し、`versionedHash` を返す |
| 0 | 2000 | 4096 | `keccak256("test.v1")` | 部分セグメントの宣言イベントを発行する |
| 0 | 4095 | 4096 | `keccak256("test.v1")` | 1FEだけのセグメント宣言に成功する |
| 0 | 0 | 2000 | `keccak256("test.a")` | 同じBlobに対する1つ目のセグメント宣言に成功する |
| 0 | 2000 | 4096 | `keccak256("test.b")` | 同じBlobに対する2つ目のセグメント宣言に成功する |

revertケースは以下です。

| `blobIndex` | `startFE` | `endFE` | 期待エラー |
|---:|---:|---:|---|
| 0 | 4096 | 0 | `InvalidSegment(4096, 0)` |
| 0 | 100 | 100 | `InvalidSegment(100, 100)` |
| 0 | 0 | 5000 | `InvalidSegment(0, 5000)` |
| 0 | 5000 | 6000 | `InvalidSegment(5000, 6000)` |
| 99 | 0 | 4096 | `NoBlobAtIndex(99)` |

境界として重要なのは、`startFE` と `endFE` が同じ値のセグメントを許さない点です。
また、`endFE = 4096` は有効ですが、4096を超える値は無効です。

## 参考実装

参考実装は、`BLOBHASH` オペコードで `versionedHash` を取得し、イベントを発行するだけの小さいコントラクトです。

```js
// SPDX-License-Identifier: CC0-1.0
pragma solidity ^0.8.24;

import {IERC_BSS} from "./IERC_BSS.sol";

contract BlobSpaceSegments is IERC_BSS {
    uint16 internal constant MAX_FIELD_ELEMENTS = 4096;

    function declareBlobSegment(
        uint256 blobIndex,
        uint16 startFE,
        uint16 endFE,
        bytes32 contentTag
    ) external returns (bytes32 versionedHash) {
        if (startFE >= endFE || endFE > MAX_FIELD_ELEMENTS) {
            revert InvalidSegment(startFE, endFE);
        }

        assembly {
            versionedHash := blobhash(blobIndex)
        }

        if (versionedHash == bytes32(0)) revert NoBlobAtIndex(blobIndex);

        emit BlobSegmentDeclared(versionedHash, msg.sender, startFE, endFE, contentTag);
    }
}
```

実装上の読みどころは、ストレージ変数を持たない点です。
`MAX_FIELD_ELEMENTS` で範囲上限を固定し、`blobhash(blobIndex)` の戻り値がゼロなら対象Blobなしとしてrevertします。
それ以外の情報は、`BlobSegmentDeclared` イベントとしてログに残します。

## セキュリティ

ERC8179は、イベントで区間を宣言する軽量な標準です。
そのため、宣言の正しさ、データ内容、宣言者の正当性をすべてオンチェーンで保証する設計ではありません。

#### 虚偽の宣言

`BLOBHASH` は現在のトランザクションに含まれるBlobに対してだけ非ゼロ値を返します。
そのため、別トランザクションのBlobを勝手に参照する宣言はできません。
一方、同じトランザクション内のBlobは呼び出しチェーン上のコントラクトから参照できます。
制約は呼び出し元単位ではなく、トランザクション単位です。

#### 重複するセグメント

同じFE範囲に複数の宣言が出ることは許可されています。
オンチェーン上で排他制御をしないため、どの宣言を採用するかはインデクサーや消費側プロトコルが決めます。
重複したFEではデータが衝突するため、インデクサーは重複範囲を検出して警告できます。

#### スパム宣言

セグメント宣言には、Blobトランザクションの基本ガス、`declareBlobSegment` の実行ガス、Blobガスが必要です。
実用的なデータを持たない宣言は、宣言者自身のコストになります。
他の利用者のBlobを参照してスパム宣言することは、`BLOBHASH` の性質上できません。

#### フロントランニング

他者のBlobに対するフロントランニングは成立しにくい設計です。
攻撃者が同じ `contentTag` や範囲を先に宣言したいなら、自分のトランザクションに同じBlobデータを含めてBlob料金を支払う必要があります。
他者のトランザクションに含まれるBlobの `versionedHash` を、自分のトランザクションから `BLOBHASH` で取得することはできません。

#### contentTagのなりすまし

誰でも任意の `contentTag` を使ってイベントを発行できます。
`contentTag` は所有権を表す仕組みではありません。
そのため、プロトコルやインデクサーは、`chainId`、`contentTag`、宣言者アドレスの組み合わせで正規の宣言者を管理する必要があります。

#### リオーグ

セグメント宣言は、Blobトランザクションを含むブロックのファイナリティに従います。
リオーグでブロックが取り消された場合、そのブロック内の宣言イベントも取り消されます。
インデクサーは、取り消されたブロックのイベントをロールバックする必要があります。

#### 重複イベント

同じ引数で `declareBlobSegment` を複数回呼ぶと、同じ内容のイベントが複数発行されます。
実装は重複排除しません。
インデクサーは、`versionedHash`、`declarer`、`startFE`、`endFE`、`contentTag` に加えて、ログインデックスも含めて個別イベントとして扱います。

#### インデクサーの信頼モデル

重複範囲の検出、宣言の追跡、重複イベントの扱いはオフチェーンインデクサーが担います。
ただし、インデクサーを信頼する必要はありません。
誰でもイベントログを読み直し、自分のルールで同じ宣言集合を再構築できます。

#### KZG検証コスト

ポイント評価プリコンパイルはFEごとにコストがかかります。
小さいメッセージならオンチェーン検証できますが、Blob全体をFEごとに検証するとブロックガス制限を超えます。
検証コントラクトは、宣言された `[startFE, endFE)` のうち必要なFEだけを対象にする設計が必要です。

プリコンパイルが保証するのは、指定された評価点に値 `y` が含まれることです。
値 `y` の中身がJSON、バイナリメッセージ、証明データとして正しいかどうかは、プロトコル固有のパーサーと検証ロジックで確認します。

#### 過大宣言

プロトコルは、実際に書いた量より大きい範囲を宣言できます。
例えば、1000FEだけを書いたのに `[0, 4096)` を宣言できます。
EVMはBlob内容を直接読めないため、コア仕様だけでは過大宣言を防げません。

消費側は、宣言を保証ではなく主張として扱います。
正確な帰属が必要な時は、既知のエンコード規則、KZG証明、正規宣言者の一覧を組み合わせて判断します。

#### Blobデータの削除

EIP4844のBlobデータは、約18日後にコンセンサスレイヤーノードから削除されます。
一方、`BlobSegmentDeclared` イベントは実行レイヤーのログとして残り続けます。
削除後も「この範囲を宣言した」という事実は追えますが、参照先のBlobデータは通常ノードから取得できません。
長期検証が必要なプロトコルは、Blobデータや証明を別途保存する設計が必要です。

#### Queryable拡張のストレージ増加

Queryable拡張は、`versionedHash` ごとにセグメント配列を保存します。
悪意ある利用者が同じBlobへ多数のセグメントを宣言すると、読み出しコストが膨らみます。
この拡張を使う実装では、ページサイズに上限を設け、実装ごとの制限を明記する必要があります。

## 最後に

今回は「**EIP4844のBlobをFE単位のセグメントに分けて、複数プロトコルが同じBlob空間を共有できるようにするERC8179**」についてまとめてきました。

ERC8179は、Blob共有の全体設計ではなく、どのFE範囲を誰がどの用途で使うかを宣言する最小インターフェースです。
コア仕様をイベントのみに絞ることで、安いガスで導入しやすくし、重複検出や料金精算はオフチェーンやプロトコル固有の設計へ分けています。
小さいセグメントをKZG証明で検証する用途では、`BlobSegmentDeclared` が `versionedHash` とFE範囲をつなぐ起点になります。

他でも色々記事を書いているのでぜひよろしければ読んでいってください！

https://amzn.asia/d/gxvJ0Pw

https://cardene.notion.site/EIP-2a03fa3ea33d43baa9ed82288f98d4a9

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://twitter.com/cardene777

https://cardene.substack.com/
