0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[EIP7949] genesis.jsonを標準化してEthereumネットワーク起動の互換性を高める仕組みを理解しよう!

0
Posted at

はじめに

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

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

今回は「EIP7949」についてまとめていきます。

Ethereumネットワークを起動する時に必要なgenesis.jsonファイルの構造を標準化するための提案です。
これまで事実上の標準(Gethの実装)はありましたが、公式な仕様書がなかったことで、クライアント間の非互換性や混乱が生じていました。

genesis.jsonとは

Ethereumネットワークの起動ファイル

Ethereumノードを立ち上げる時、そのネットワークがどんな設定から始まるかを定義するファイルがgenesis.jsonです。
メインネットを動かすノードでは既定の設定が使われますが、テストネットやプライベートネットを自分で立ち上げる時には、このファイルを用意して渡す必要があります。

genesis.jsonには以下のような情報が含まれています。

  • どのチェーンIDで動作するか(メインネット・テストネット・プライベートネットの区別)
  • 各ハードフォークをどのブロック高またはタイムスタンプで有効化するか
  • ネットワーク開始時点でどのアドレスにETHやコードを事前配置するか
  • ブロックのガスリミットや難易度などの初期パラメータ

現状の課題

このファイルはEthereumネットワークを動かすうえで必須ですが、これまで公式な仕様書が存在しませんでした。
事実上の標準として機能していたのはGeth(Go-Ethereum)の実装で、他のクライアント(Besu、Nethermindなど)もこれに倣ってきました。

しかし「Gethの実装を見て推測する」状態では、以下のような問題が起きます。

  • クライアントによってフィールドの解釈が微妙に異なる
  • テストネットを複数クライアントで動かす際に設定ミスが発生する
  • 新しいフォークやパラメータを追加するEIPが、基礎の仕様を参照できない

EIP7949はこの「根本となる設定仕様が未定義」という問題を解決するために提案されました。

EIP7949の概要

提案の目的

EIP7949の目標は、genesis.jsonのCanonical(正規)な構造をJSON Schemaとして定義することです。

この提案自体はInformational(情報提供型)のEIPです。新しい機能を追加したり、既存の動作を変えたりするものではなく、すでにGethで動いている実装をドキュメント化・標準化することが主眼です。

具体的には以下を達成しようとしています。

  • 各フィールドの型・意味・形式を明確に定義する
  • JSON Schemaでバリデーション可能にする
  • 後続のEIP(ジェネシス設定を変更するもの)が参照できる基盤を作る

関連するEIPとの関係

この提案が「基礎仕様」として位置づけられているのには理由があります。
近年、ジェネシス設定を変更するEIPがいくつか提案されています。

以下の図はEIP7949と関連するEIPの関係を示しています。

EIP7949関連EIP関係図

EIP7840EIP7892EIP7910はいずれもgenesis.jsonの構造を変更・拡張する提案です。GethのデファクトスタンダードをベースにしたEIP7949が定義されることで、これらが共通の仕様を参照できるようになります。

これらはすべて「ジェネシス設定の何かを変える」提案ですが、その変更対象であるgenesis.jsonの構造が未定義のままでは、変更内容を正確に記述できません。
EIP7949はそうした提案が参照できる土台を作ることで、Ethereumの仕様整備を進める役割を担っています。

仕様

トップレベルフィールド

genesis.jsonはJSONオブジェクトで、以下のトップレベルフィールドを持ちます。

フィールド 説明
config Object チェーン設定オブジェクト 後述
alloc Object 事前配置するアドレスと残高・コードのマップ 後述
nonce String ブロックnonce(16進数文字列) 0x0
timestamp String UNIXタイムスタンプ(16進数文字列) 0x6720f180
extraData String 任意の追加データ(16進数文字列) 0x00
gasLimit String ブロックのガスリミット(16進数文字列) 0x1c9c380
difficulty String ブロック難易度(16進数文字列) 0x1
mixhash String Mixハッシュ(16進数文字列) 0x000...000
coinbase String Coinbaseアドレス(16進数文字列) 0x000...000

数値を16進数文字列で表現している点が特徴的です。これはEthereumのRLP(Recursive Length Prefix)エンコードと整合するためで、0xプレフィックスで始まる形式が標準です。

以下の図はgenesis.jsonのフィールド構造全体を示しています。

genesis.json構造図

genesis.jsonはトップレベルにconfigallocという2つの主要オブジェクトを持ち、その下にそれぞれのフィールドが展開されます。オレンジ色のフィールドはハードフォークと連動して変化する動的な設定です。

configオブジェクト

configフィールドはハードフォークの有効化タイミングとチェーン固有の設定を保持します。

フィールド 説明
chainId Integer チェーンを識別する一意のID(10進整数) 1(メインネット)
<hardfork名>Block / <hardfork名>Time Integer ハードフォークを有効化するブロック高またはタイムスタンプ(10進整数) shanghaiTime: 1681338455
terminalTotalDifficulty String PoWからPoSへ切り替わる総難易度(16進数文字列) 0xc70d815d562d3cfa955
depositContractAddress String デポジットコントラクトのアドレス 0x00000000219ab540356cBB839Cbe05303d7705Fa
blobSchedule Object ハードフォークごとのBlob設定 後述

ハードフォークの有効化タイミングには2種類の指定方法があります。
Merge以前のフォーク(Homestead、Berlinなど)はブロック高(xxxBlock)で、Merge以降のフォーク(Shanghai、Cancunなど)はUnixタイムスタンプ(xxxTime)で指定します。
これはEthereumがPoW(ブロック高が増加)からPoS(時刻ベースのスロット)に移行したことを反映しています。

blobScheduleオブジェクト

EIP4844で導入されたBlobトランザクション(大容量データを安価に送受信する仕組み)のパラメータを、ハードフォークごとに設定するオブジェクトです。

EIP4844(Proto-Danksharding)は、Blob(Binary Large Object)と呼ばれる大きなデータ領域を持つ特殊なトランザクション形式を導入したアップグレードです。
L2(レイヤー2チェーン)がロールアップのデータをEthereumに書き込む時のコストを大幅に削減しました。Cancunアップグレード(2024年3月)で本番導入されています。

フィールド 説明
target Integer 1ブロックに含める目標Blob数(10進整数) 3
max Integer 1ブロックに含める最大Blob数(10進整数) 6
baseFeeUpdateFraction Integer Blob基本手数料の調整に使う係数(10進整数) 3338477

これらの値はハードフォーク名をキーとするオブジェクトで指定します。たとえば以下のような形式になります。

"blobSchedule": {
  "cancun": {
    "target": 3,
    "max": 6,
    "baseFeeUpdateFraction": 3338477
  },
  "prague": {
    "target": 6,
    "max": 9,
    "baseFeeUpdateFraction": 5007716
  }
}

ハードフォークごとに値を変えられるため、Blobの許容量を段階的に引き上げるロードマップ(Danksharding)に対応できる設計になっています。

allocオブジェクト

allocフィールドはネットワーク起動時点での初期状態を定義します。
アドレス(16進数文字列)をキーとして、そのアドレスに事前配置する内容をマッピングします。

フィールド 説明
balance String 残高(weiの16進数文字列) 0xde0b6b3a7640000(1 ETH)
code String EVMバイトコード(16進数文字列) 0x6060604052...
nonce String アカウントnonce(16進数文字列) 0x0
storage Object ストレージスロットのキー・バリューマップ 32バイト16進数文字列のペア

allocが特に重要になるのはプリコンパイル済みコントラクト(ecrecoverなどのネイティブ実装)やシステムコントラクト(デポジットコントラクトなど)の事前配置です。
Ethereumメインネットやテストネットでは、これらが起動時点から存在している必要があり、allocでコードや残高を設定します。

JSON Schema

この提案の中核となるのがJSON Schemaです。スキーマを使うことで、genesis.jsonの構造をプログラムで自動検証できるようになります。

以下がEIP7949で定義されたスキーマです。

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "$defs": {
    "hexUint": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]+$"
    },
    "address": {
      "type": "string",
      "pattern": "^0x[0-9a-fA-F]{40}$"
    },
    "hash": {
      "type": "string",
      "pattern": "^0x[0-9a-f]{64}$"
    }
  },
  "title": "Ethereum Genesis File",
  "type": "object",
  "required": ["alloc", "gasLimit", "difficulty"],
  "properties": {
    "config": {
      "type": "object",
      "properties": {
        "chainId": { "type": "integer" },
        "homesteadBlock": { "type": "integer" },
        "daoForkBlock": { "type": "integer" },
        "eip150Block": { "type": "integer" },
        "tangerineWhistleBlock": { "type": "integer" },
        "eip155Block": { "type": "integer" },
        "spuriousDragonBlock": { "type": "integer" },
        "byzantiumBlock": { "type": "integer" },
        "constantinopleBlock": { "type": "integer" },
        "petersburgBlock": { "type": "integer" },
        "istanbulBlock": { "type": "integer" },
        "muirGlacierBlock": { "type": "integer" },
        "berlinBlock": { "type": "integer" },
        "londonBlock": { "type": "integer" },
        "arrowGlacierBlock": { "type": "integer" },
        "grayGlacierBlock": { "type": "integer" },
        "terminalTotalDifficulty": { "$ref": "#/$defs/hexUint" },
        "mergeNetsplitBlock": { "type": "integer" },
        "shanghaiTime": { "type": "integer" },
        "cancunTime": { "type": "integer" },
        "pragueTime": { "type": "integer" },
        "osakaTime": { "type": "integer" },
        "depositContractAddress": { "$ref": "#/$defs/address" },
        "blobSchedule": {
          "type": "object",
          "additionalProperties": {
            "type": "object",
            "properties": {
              "target": { "type": "integer" },
              "max": { "type": "integer" },
              "baseFeeUpdateFraction": { "type": "integer" }
            }
          }
        }
      },
      "additionalProperties": true
    },
    "nonce": { "$ref": "#/$defs/hexUint" },
    "timestamp": { "$ref": "#/$defs/hexUint" },
    "extraData": {
      "anyOf": [
        { "type": "string", "const": "" },
        { "type": "string", "pattern": "^0x([0-9a-fA-F]{2})*$" }
      ]
    },
    "gasLimit": { "$ref": "#/$defs/hexUint" },
    "difficulty": { "$ref": "#/$defs/hexUint" },
    "mixhash": { "$ref": "#/$defs/hash" },
    "coinbase": { "$ref": "#/$defs/address" },
    "alloc": {
      "type": "object",
      "patternProperties": {
        "^0x[0-9a-fA-F]{40}$": {
          "type": "object",
          "properties": {
            "balance": { "$ref": "#/$defs/hexUint" },
            "nonce": { "$ref": "#/$defs/hexUint" },
            "code": { "type": "string", "pattern": "^0x([0-9a-f])*$" },
            "storage": {
              "type": "object",
              "patternProperties": {
                "^0x[0-9a-f]{64}$": {
                  "$ref": "#/$defs/hash"
                }
              }
            }
          }
        },
        "additionalProperties": false
      },
      "additionalProperties": false
    }
  },
  "additionalProperties": true
}

スキーマの設計で注目すべき点がいくつかあります。

まず$defsで再利用可能な型を3つ定義しています。hexUint0xから始まる16進数文字列、addressは40文字の16進数アドレス、hashは64文字の16進数ハッシュです。これらをスキーマ各所で$ref参照することで、同じパターンを何度も書かずに済む構造になっています。

次にrequiredフィールドはallocgasLimitdifficultyの3つだけです。他のフィールドはオプションで、additionalProperties: trueを指定しています。これは将来のハードフォークで新しいフィールドが追加されても、スキーマを更新しなくてもバリデーションが通るようにするための設計判断です。

allocのパターン定義も興味深い構造です。キーはアドレス形式の正規表現でマッチングし、値にはbalance・code・nonce・storageを持てるようにしています。alloc直下にadditionalProperties: falseが指定されており、アドレス形式でないキーを持つことを禁止しています。

設計方針

なぜGethの実装を基準にしたか

このスキーマはGethが長年使ってきたフォーマットを定式化したものです。
新しい形式を設計して既存の実装を置き換えるのではなく、すでに動いている実装を標準として文書化するアプローチが取られています。

これには実用上の理由があります。Gethはシェアが高く、テストネットツール(Kurtosis、Assertoor等)もGeth互換のgenesis.jsonを前提に作られています。
新形式を定義してもエコシステム全体の移行コストが大きいため、既存フォーマットをそのまま仕様として採用するのが現実的な判断です。

また、この提案はあくまで「今ある実装の文書化」であって、すべてのクライアントが完全に準拠することを強制するものではありません。
additionalProperties: trueでクライアント固有の拡張を許容しているのも、このスタンスを反映しています。

後続EIPの基盤として

もう一つの重要な設計意図は「参照できる仕様書を作ること」です。

EIP7840(BlobスケジュールをEL設定ファイルに追加)やEIP7892(Blobパラメータのみを変更するフォーク)など、ジェネシス設定を変更する提案が増えています。
これらのEIPはそれまで「変更対象の構造が未定義のまま変更を記述する」という状態でした。

EIP7949が正式に採用されれば、後続のEIPは「EIP7949のスキーマを拡張する」「EIP7949blobScheduleに新しいフィールドを追加する」という形で正確な変更内容を記述できるようになります。

セキュリティの考慮事項

EIP7949はInformational(情報提供型)の提案であり、新しい機能を追加するものではありません。
また、genesis.jsonを操作できるのはノードへの管理者アクセスを持つオペレータに限られます。

そのため、この提案自体が新たなセキュリティリスクを生み出すことはなく、セキュリティ上の考慮事項は特にないとされています。

実務的な観点では、genesis.jsonallocフィールドは初期配置するコードやETHを定義するため、プライベートネットやテストネットの設定ミスには注意が必要です。
ただしこれはJSON Schemaのバリデーションがあることで、型エラーや形式ミスを事前に検出できるようになり、むしろ安全性が向上する方向です。

最後に

今回は「EIP7949」についてまとめてきました。

genesis.jsonの標準化は地味に見えますが、テストネットやL2チェーンを複数クライアントで安定して動かすうえで重要な基盤整備です。
特に、EIP7840EIP7892など今後のジェネシス関連EIPが「参照できる仕様書がある」状態になることで、仕様の記述精度が高まり、クライアント間の互換性維持がしやすくなります。

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?