QRコードをスキャンしてダウンロードしてください。
BTC $66,689.70 -1.20%
ETH $1,956.55 -1.31%
BNB $604.51 -1.87%
XRP $1.42 -4.56%
SOL $81.67 -4.53%
TRX $0.2795 -0.47%
DOGE $0.0974 -3.83%
ADA $0.2735 -4.22%
BCH $550.00 -1.59%
LINK $8.64 -2.97%
HYPE $28.98 -1.81%
AAVE $122.61 -3.42%
SUI $0.9138 -6.63%
XLM $0.1605 -4.62%
ZEC $260.31 -8.86%
BTC $66,689.70 -1.20%
ETH $1,956.55 -1.31%
BNB $604.51 -1.87%
XRP $1.42 -4.56%
SOL $81.67 -4.53%
TRX $0.2795 -0.47%
DOGE $0.0974 -3.83%
ADA $0.2735 -4.22%
BCH $550.00 -1.59%
LINK $8.64 -2.97%
HYPE $28.98 -1.81%
AAVE $122.61 -3.42%
SUI $0.9138 -6.63%
XLM $0.1605 -4.62%
ZEC $260.31 -8.86%

Geth ソースコードシリーズ:取引の設計と実装

Summary: この記事はGethソースコードシリーズの第4篇であり、私たちはイーサリアムの取引について深く掘り下げ、取引メカニズムの設計や取引のライフサイクルを探ります。最後に、実行層とコンセンサス層の相互プロセスを含む取引の実行フローについて詳しく説明します。
LXDAO
2025-11-08 10:08:33
コレクション
この記事はGethソースコードシリーズの第4篇であり、私たちはイーサリアムの取引について深く掘り下げ、取引メカニズムの設計や取引のライフサイクルを探ります。最後に、実行層とコンセンサス層の相互プロセスを含む取引の実行フローについて詳しく説明します。

1. 取引の概要

イーサリアムの実行層は、取引駆動型の状態機械と見なすことができ、取引は状態を変更する唯一の方法です。取引は EOA によってのみ開始され、取引にはプライベートキーの署名が付加されます。取引が実行された後、イーサリアムネットワークの状態が更新されます。イーサリアムネットワークで最も単純な取引は ETH の送金であり、あるアカウントから別のアカウントに送金されます。

ここで説明しておく必要があるのは、EOA が EIP-7702 のアップグレードによって契約の能力をサポートできるようになるにつれて、今後は契約アカウントと EOA の概念が徐々に曖昧になることですが、現在のバージョンでは、プライベートキーによって制御される EOA のみが取引を開始できると考えられています。

イーサリアムは異なるタイプの取引をサポートしており、イーサリアムのメインネットが最初に立ち上がったときは、1種類の取引のみをサポートしていましたが、その後、イーサリアムの継続的なアップグレードの過程で、さまざまなタイプの取引が順次サポートされるようになりました。現在主流の取引は、動的料金 EIP-1559 をサポートする取引であり、大多数のユーザーが提出するのはこのタイプの取引です。EIP-4844 では、Layer2 または他のオフチェーン拡張ソリューションに対してより安価なデータストレージを提供できるようにすることが導入され、最新の Pectra アップグレードでは、EIP-7702 によって EOA を契約に拡張できる取引形式が導入されました。

イーサリアムの発展に伴い、今後は他の取引タイプもサポートされる可能性がありますが、取引全体の処理フローの変化はあまり大きくなく、すべての取引は取引の提出 → 取引の検証 → 取引プールへの入場 → 取引の伝播 → ブロックへのパッケージ化というフローを経る必要があります。

2. 取引構造の進化

イーサリアムのメインネットが立ち上がって以来、イーサリアムの取引構造は4回の大きな変化を経ており、それぞれが安全性と拡張性の基礎を築き、今後は低コストで取引タイプを増やすことができるようになります。

クロスチェーンリプレイ攻撃の防止

最初の取引構造は以下のように示され、RLP で取引データが RLP 構造にエンコードされた後に伝播および処理されます:

RLP([nonce, gasPrice, gasLimit, to, value, data, v, r, s])

この構造の最大の問題は、チェーンと関連付けられていないことです。メインネットで生成された取引は、他のチェーンで自由に実行される可能性があるため、EIP-155 では署名の v 値に chainId(例えばメインネットID=1)を埋め込むことで、異なるチェーンの取引を隔離し、各チェーンの取引が他のチェーンでリプレイされることを保証します。

関連する EIP:

  • EIP-155

取引拡張の標準化

イーサリアムの発展に伴い、最初の取引フォーマットは一部のシナリオのニーズを満たすことができなくなったため、新しい取引タイプを追加する必要がありましたが、取引のタイプを無作為に追加すると、後に管理が複雑になり、標準化できない問題に直面する可能性があります。EIP-2718 では、今後の取引のフォーマットが定義され、主に TransactionType || TransactionPayload 構造が定義されました。その中で:

  • TransactionType は取引タイプを定義し、最大で128種類の取引タイプに拡張可能で、新しい取引タイプの使用に十分です。

  • TransactionPayload は取引のデータフォーマットを定義し、現在は RLP を使用してデータをエンコードしていますが、将来的には SSZ や他のエンコーディングにアップグレードされる可能性もあります。

このアップグレードはベルリンアップグレードで完了し、EIP-2718 の他に、EIP-2930 では Access List 取引タイプが導入され、この取引タイプではユーザーが取引内でアクセスする必要のある契約とストレージを事前に宣言でき、取引実行プロセス中のガス消費を削減できます。

関連する EIP:

  • EIP-2718

  • EIP-2930

イーサリアム経済モデルの変革

ロンドンアップグレードでは、EIP-1559 が Base Fee メカニズムを導入し、ETH の発行速度を減速させ、さらにはデフレを引き起こす可能性があります。ステーキングに参加するノードにとっては、チップ(maxPriorityFeePerGas)を通じて追加の収入を得る可能性もあります。EIP-1559 取引は Access List メカニズムを継承しており、これは現在最も主要な取引です。また、パリの The Merge アップグレード後、イーサリアムは PoW から PoS に移行し、以前のマイニング経済モデルはもはや適用されず、イーサリアムはステーキング時代に突入しました。

さらに、EIP-1559 ではターゲットメカニズムを導入することで、Base Fee を動的に調整できるようになり、イーサリアムに負荷分散の能力をもたらします。ターゲット値はブロックのガスリミットの半分であり、これを超えると Base Fee は継続的に上昇します。このため、多くの取引は混雑時間を避けることができ、チェーン全体の混雑状況を緩和し、ユーザー体験が向上します。

関連する EIP:

  • EIP-1559

さまざまな拡張取引の追加

EIP-2718 と EIP-1559 がそれぞれ拡張取引の標準と経済モデルを定義した後、新しい取引タイプが次々と追加されました。最近の2回のアップグレードでは、EIP-4844 と EIP-7702 がそれぞれ追加され、前者は Blob 取引タイプを追加し、オフチェーン拡張ソリューションに理想的なストレージソリューションを提供し、スペースが大きく、価格も低く、EIP-1559 の経済モデルと負荷メカニズムに類似しています。EIP-7702 は EOA をプライベートキーを持つスマートコントラクトアカウントに変換できるようにし、今後のアカウント抽象化の大規模な採用に備えています。

関連する EIP:

  • EIP-4844

  • EIP-7702

3. 取引モジュールアーキテクチャ

取引はイーサリアムの状態機械の入力として、ほぼすべての主なプロセスが取引を中心に展開されます。取引が取引プールに入る前に、取引のフォーマットや署名などの情報を検証する必要があります。取引プールに入った後は、異なるノード間で伝播し、出塊ノードによって取引プールから選択され、その後取引は EVM 上で実行され、状態データベースが変更され、最終的にブロックにパッケージ化され、実行層と合意層の間で伝達されます。

出塊ノードと非出塊ノードでは、取引処理のフローにいくつかの違いがあります。出塊ノードは取引プールから取引を選択し、ブロックにパッケージ化し、ローカルの状態データベースを更新する責任があります。非出塊ノードは、同期された最新のブロック内の取引を再実行し、ローカルの状態を最新に更新するだけです。

取引の種類

現在、イーサリアムは合計で5種類の取引をサポートしています。これらの取引の主要な構造は似ており、異なる取引は取引タイプフィールドによって区別され、異なるタイプの取引の中では特定の目的を実現するためにいくつかの拡張フィールドが使用されます。

  • LegacyTxType:創世ブロックから現在まで使用されている基本フォーマットで、第一価格オークションモデル(ユーザーが手動で gasPrice を設定)を採用しています。EIP-155 アップグレード後はデフォルトで chainId が埋め込まれ、クロスチェーンリプレイ攻撃を防止します。現在、イーサリアムのメインネットでの使用量はかなり少なく、イーサリアムは現在この取引タイプを互換性を持たせていますが、今後は徐々に廃止される予定です。

  • AccessListTxType:ストレージアクセスを事前に宣言することでガスコストを大幅に削減します。この機能は後続の取引タイプに引き継がれ、直接この取引タイプを使用する取引は少なくなっています。

  • DynamicFeeTxType:イーサリアムの経済モデルを更新する取引タイプで、Base Fee とターゲットメカニズムを導入し、AccessList 特性を継承しています。これも現在最も主流の取引タイプです。

  • BlobTxType:オフチェーン拡張に関連する取引タイプで、取引が blob 構造を通じて低コストで大量のデータを持ち運ぶことを許可し、オフチェーン拡張ソリューションのコストを削減します。AccessList と DynamicFee 特性を継承し、取引内の blob には EIP-1559 に類似した独自の課金メカニズムがあります。

  • SetCodeTxType:EOA を 契約アカウントに変換することを許可し(取引を通じて契約機能を取り消すことも可能)、EOA 内の対応する契約コードを実行でき、AccessList と DynamicFee 特性を継承します。

取引ライフサイクル

取引がブロックにパッケージ化されると、状態データの変更が完了し、取引のライフサイクルが終了したと理解できます。このプロセスでは、取引は4つのサイクルを経ます:

  • 取引検証:EOA によって提出された取引は、一連の基本的な検証を経てから取引プールに追加されます。

  • 取引ブロードキャスト:取引プールに新しく提出された取引は、他のノードの取引プールにブロードキャストされます。

  • 取引実行:出塊ノードは取引プールから取引を選択して実行します。

  • 取引パッケージ化:取引は一定の順序(まずローカル取引かどうかを区別し、その後ガス料金の大きさに従って)でブロックにパッケージ化され、検証に失敗する取引は無視されます。

取引プール

取引プールは取引を一時的に保存する場所であり、取引がパッケージ化される前にすべて取引プールに保存されます。取引プール内の取引は他のノードに同期され、同時に他のノードの取引プールから取引を同期します。ユーザーが提出した取引は最初に取引プールに入った後、合意層を経て合意プロセスをトリガーし、取引の実行とブロックへのパッケージ化を促進します。

現在の取引プールの実装には2つのタイプがあります:

  • Blob 取引プール(Blob TxPool)

  • その他の取引の取引プール(Legacy TxPool)

Blob 取引はデータの処理フローが他の取引とは異なるため、別の取引プールを使用して処理されます。他のタイプの取引はタイプが異なりますが、異なるノード間での同期やパッケージ化のプロセスは基本的に一致するため、同じ取引プールで処理されます。取引プール内の取引はすべて外部 EOA の所有者によって提出され、取引プールに取引を提出する方法は2つあります:

  • SendTransaction

  • SendRawTransaction

SendTransaction はクライアントが未署名の取引オブジェクトを送信し、ノードは取引内の発信アドレス from に対応するプライベートキーを使用して取引に署名します。一方、SendRawTransaction は事前に取引に署名し、署名された取引をノードに提出する必要があります。この方法は一般的に使用され、Metamask、Rabby などのウォレットで使用されています。

ここでは SendRawTransaction を例にとります。ノードが起動すると、ノードは外部のさまざまな API リクエストを処理するための API モジュールを起動します。SendRawTransaction はその中の1つの API であり、ソースコードは internal/ethapi/api.go にあります:

func (api *TransactionAPI) SendRawTransaction(ctx context.Context, input hexutil.Bytes) (common.Hash, error) {
tx := new(types.Transaction)
if err := tx.UnmarshalBinary(input); err != nil {
return common.Hash{}, err
}
return SubmitTransaction(ctx, api.b, tx)
}

4. コアデータ構造

取引モジュールにとって、コアデータ構造は2つの部分から成り立っています。一つは取引自体を表すデータ構造で、もう一つは取引を一時的に保存する取引プール構造です。取引は取引プール内で異なるノード間で伝播する必要があるため、取引プールの実装は基盤となる p2p プロトコルに依存します。

取引構造

core/types/transaction.go の Transaction を使用して、すべての取引タイプを統一的に表現しています:

type Transaction struct {
inner TxData // 取引の実際のデータはここに保存されます
time time.Time
//….
}

TxData はインターフェース型で、すべての取引タイプが実装する必要のあるプロパティ取得メソッドを定義していますが、LegacyTxType のような取引では、多くのフィールドが存在しないため、以前から存在するフィールドを代わりに使用するか、空を返します:

type TxData interface {
txType() byte // 取引タイプ
copy() TxData // 取引データの深いコピーを作成
chainID() *big.Int // チェーンID、異なるイーサリアムネットワークを区別するために使用
accessList() AccessList // ガス消費を最適化するための事前コンパイルされたアクセスリスト(EIP-2930 に導入)
data() []byte // 取引の入力データ、契約呼び出しまたは作成に使用
gas() uint64 // ガス制限、取引が最大で消費できるガスの量を示す
gasPrice() *big.Int // 1単位のガスの価格(Legacy 取引用)
gasTipCap() *big.Int // チップ上限(EIP-1559 取引用)
gasFeeCap() *big.Int // 総費用上限(EIP-1559 取引用)
value() *big.Int // 取引で送信される ETH の量
nonce() uint64 // 取引のシーケンス番号、リプレイ攻撃を防ぐために使用
to() *common.Address // 受取人アドレス、契約作成の場合は nil
rawSignatureValues() (v, r, s *big.Int) // 原始署名値(v, r, s)
setSignatureValues(chainID, v, r, s *big.Int) // 署名値を設定
effectiveGasPrice(dst *big.Int, baseFee *big.Int) *big.Int // 実際のガス価格を計算(baseFee を考慮)
encode(*bytes.Buffer) error // 取引をバイトストリームにエンコード
decode([]byte) error // バイトストリームから取引をデコード
sigHash(*big.Int) common.Hash // 署名が必要な取引ハッシュ
}

上記の各取引が必要とする詳細に加えて、各新しく追加された取引には独自の拡張部分があります。

Blob 取引では:

  • BlobFeeCap:各 blob データの最大費用上限で、maxFeePerGas に類似していますが、blob データの費用を計算するために特別に使用されます。

  • BlobHashes:すべての blob データのハッシュ値の配列を保存し、これらのデータは実行層に保存され、Blob データの完全性と真実性を証明するために使用されます。

  • Sidecar:実際の blob データとその証明を含み、これらのデータは実行層には保存されず、合意層に一時的に保存され、取引にはエンコードされません。

SetCode 取引では:

  • AuthList:契約コードの多重承認メカニズムを実現するための承認リストで、EOA がスマートコントラクトの機能を取得するのを助けます。

すべての取引タイプは TxData を実装する必要があり、各取引の差別化処理は取引タイプの内部で実装されます。このようにインターフェース指向の設計により、新しい取引タイプを簡単に追加でき、現在の取引フローを変更する必要がありません。

取引プール構造

取引構造と同様に、取引プールも同じ設計パターンを採用し、core/txpool/txpool.go の TxPool を使用して取引プールを統一的に管理しています。その中で SubPool はインターフェースであり、各取引プールの具体的な実装はこのインターフェースを実装する必要があります:

type TxPool struct {
subpools []SubPool // 取引プールの具体的な実装
chain BlockChain
// …
}
type LegacyPool struct {
config Config // 取引プールのパラメータ設定
chainconfig *params.ChainConfig // ブロックチェーンのパラメータ設定
chain BlockChain // ブロックチェーンインターフェース
gasTip atomic.Pointer[uint256.Int] // 現在受け入れられている最低ガスチップ
txFeed event.Feed // 取引イベントの発行購読システム
signer types.Signer // 取引署名検証器
pending map[common.Address]*list // 現在処理可能な取引
queue map[common.Address]*list // 一時的に処理できない取引
//…
}
type BlobPool struct {
config Config // 取引プールのパラメータ設定
reserve txpool.AddressReserver //
store billy.Database // 永続データストレージ、取引メタデータと blob データを保存するために使用
stored uint64 //
limbo *limbo //
signer types.Signer //
chain BlockChain //
index map[common.Address][]*blobTxMeta //
spent map[common.Address]*uint256.Int //
//…
}

現在実装されている SubPool の2つの取引プールは:

  • BlobTxPool:Blob 取引を管理するために使用されます。

  • LegacyTxPool:Blob 取引以外の他の取引を管理するために使用されます。

Blob 取引が他の取引と分けて管理される理由は、Blob 取引が大量の blob データを持ち運ぶ可能性があるためです。他の取引はメモリ内で直接管理および同期できますが、Blob 取引内の blob データは永続的に保存する必要があるため、他の取引と同じ管理方法を使用できません。

5. 費用メカニズム

イーサリアム自体は停止問題を処理できないため、ガスメカニズムを使用して悪意のある攻撃を防ぎます。さらに、ガス自体はユーザーの手数料としても使用され、これがガスの最初の2つの用途です。

これらの数年間の発展を経て、ガスは上記の2つの用途に加えて、イーサリアム経済モデルの重要な構成要素となり、ETH の発行量を制御し、イーサリアムのデフレを助けることができます。さらには、イーサリアムネットワークのトラフィックを動的に調整し、ユーザーの使用体験を向上させることも可能です。

イーサリアムの費用メカニズムはガスを使用して実現されており、ネットワークの安全性を維持し、経済モデルのバランスを実現するなどの多くの役割を果たしています。

ガス

イーサリアムが取引を処理する際、EVM 上で実行される各操作はガスを消費する必要があります。たとえば、メモリの使用、データの読み取り、データの書き込みなどが含まれます。一部の操作は多くのガスを消費し、他の操作は少なくなります。たとえば、ETH の送金操作には 21,000 ガスが必要です。各取引では、その取引が最大で消費するガスの量を設定する必要があります。ガスが消費されると、取引は終了し、このプロセスで消費されたガスは返金されません。このメカニズムは、イーサリアムの停止問題を処理するために使用されます。

イーサリアムのブロックサイズも具体的なサイズ単位ではなく、ガスを使用して制限されます。ブロック内のすべての取引が実際に消費するガスは、ブロック自体のガス制限を超えてはなりません。ガスは EVM 実行プロセス中の計量単位であり、各取引に消費されるガスの支払いに ETH を使用します。ガスの価格は通常 Gwei で表され、1 Ether = 10\^9 Gwei です。

現在のイーサリアムネットワークでは、現在のブロックのサイズ制限は 36M ガスですが、現在のコミュニティではブロックのガス制限を引き上げる声が非常に大きく、ブロックのガス制限を 60M に引き上げることが比較的合理的な選択であると考えられています。この数字はネットワークの容量を向上させ、同時にネットワークの安全性を脅かすことはありません。現在、テストネットでテスト中です。また、コミュニティ内では、単にガス制限を使用してブロックのサイズを制御することは不合理であり、バイトサイズの制限を導入する必要があると考えられています。現在、これらはコミュニティで議論されています。

EIP-1559

EIP-1559 メカニズムを導入した後、以前の GasPrice は Base Fee と Priority Fee(maxPriorityFeePerGas) に直接分割されます。Base Fee はすべて破棄され、イーサリアム内の ETH の増加速度を制御します。Priority Fee は出塊ノードに対応する検証者に与えられます。ユーザーは取引内で maxFeePerGas を設定して、最終的に支払う費用が制限されるようにします。

取引を成功させるためには、maxFeePerGas ≥ Base Fee + Priority Fee を保証する必要があります。そうでない場合、取引は失敗し、費用も返金されません。ユーザーが実際に支出する費用は (Base Fee + Priority Fee) × ガス使用量であり、余分な費用は取引を発起したアドレスに返金されます。

Base Fee は動的に変化し、ブロック内のガスの実際の使用量を基準にします。ブロックの最大ガス制限の半分をターゲットと呼び、前のブロックの実際の使用量がターゲットを超えた場合、現在のブロックの Base Fee は増加します。前のブロックのガス使用量がターゲットを下回った場合、Base Fee は減少し、そうでなければ変わりません。

Blob 取引の費用メカニズム

Blob 取引の費用決済は2つの部分に分かれています。一つは EIP-1559 を使用して他の取引と共に Base Fee を調整する部分で、もう一つは Blob 取引内の Blob データに独自の Blob Fee メカニズムがあります。ターゲット値は最大 Blob 数量の半分であり、Blob データブロックの使用量に基づいて Blob Fee を調整しますが、Priority Fee は単独で設定されません。Blob 取引は取引内の Priority Fee を直接設定して、Blob 取引がより早くパッケージ化されるように促すことができます。

6. 取引処理フローのソースコード分析

上記ではイーサリアムにおける取引メカニズムの設計と実装について詳しく説明しました。次に、コードを分析することで、Geth における取引の具体的な実装、取引のライフサイクル全体における処理フローを詳しく説明します。

取引の提出

SendTransaction または SendRawTransaction のいずれかの方法で取引を提出する場合、internal/ethapi/api.goSubmitTransaction 関数を呼び出して取引プールに取引を提出します。

この関数では、取引に対して2つの基本的なチェックが行われます。一つはガス料金が合理的かどうかのチェックで、もう一つは取引が EIP-155 の規範を満たしているかのチェックです。EIP-155 は取引署名に chainID パラメータを導入することで、クロスチェーン取引のリプレイ問題を解決します。このチェックにより、ノードの設定が EIP155Required に設定されている場合、取引プールに提出されるすべての取引がこの基準を満たす必要があります。

チェックが完了すると、取引は取引プールに提出され、eth/api_backend.go の SendTx によって追加ロジックが実装されます:

取引プール内では、Filter メソッドを使用して取引に対応する取引プールをマッチングします。現在、2つの取引プールの実装があり、Blob 取引の場合は BlobPool に入れられ、それ以外の場合は LegacyPool に入れられます:

ここまでで、EOA によって提出された取引は取引プールに追加され、この取引は取引プール内で伝播を開始し、後続の取引のパッケージ化と実行プロセスに入ります。

取引のパッケージ化前に新しい取引が再送信された場合、新しい取引は新しい gasPrice と gasLimit を設定し、元の取引プール内の取引が削除され、新しい gasPrice と gasLimit に置き換えられた後、再度取引プールに戻ります。この方法は、実行したくない取引をキャンセルするためにも使用できます。

func (api *TransactionAPI) Resend(ctx context.Context, sendArgs TransactionArgs, gasPrice *hexutil.Big, gasLimit *hexutil.Uint64) (common.Hash, error) {
if sendArgs.Nonce == nil {
return common.Hash{}, errors.New("missing transaction nonce in transaction spec")
}
if err := sendArgs.setDefaults(ctx, api.b, false); err != nil {
return common.Hash{}, err
}
matchTx := sendArgs.ToTransaction(types.LegacyTxType)
// 古い取引を置き換える前に、新しい取引手数料が合理的であることを確認します。
price := matchTx.GasPrice()
if gasPrice != nil {
price = gasPrice.ToInt()
}
gas := matchTx.Gas()
if gasLimit != nil {
gas = uint64(*gasLimit)
}
if err := checkTxFee(price, gas, api.b.RPCTxFeeCap()); err != nil {
return common.Hash{}, err
}
// 置き換えのために保留リストを反復処理します
pending, err := api.b.GetPoolTransactions()
if err != nil {
return common.Hash{}, err
}
for _, p := range pending {
wantSigHash := api.signer.Hash(matchTx)
pFrom, err := types.Sender(api.signer, p)
if err == nil \&\& pFrom == sendArgs.from() \&\& api.signer.Hash(p) == wantSigHash {
// マッチ。再署名して取引を送信します。
if gasPrice != nil \&\& (*big.Int)(gasPrice).Sign() != 0 {
sendArgs.GasPrice = gasPrice
}
if gasLimit != nil \&\& *gasLimit != 0 {
sendArgs.Gas = gasLimit
}
signedTx, err := api.sign(sendArgs.from(), sendArgs.ToTransaction(types.LegacyTxType))
if err != nil {
return common.Hash{}, err
}
if err = api.b.SendTx(ctx, signedTx); err != nil {
return common.Hash{}, err
}
return signedTx.Hash(), nil
}
}
return common.Hash{}, fmt.Errorf("transaction %#x not found", matchTx.Hash())
}

取引のブロードキャスト

ノードが EOA によって提出された取引を受信すると、ネットワーク内で伝播する必要があります。txpool(core/txpool/txpool.go)は、取引プール内の新しいイベントを購読するための SubscribeTransactions メソッドを提供します。Blob 取引プールと Legacy 取引プールは、購読の実装方法が異なります:

func (p *TxPool) SubscribeTransactions(ch chan\<- core.NewTxsEvent, reorgs bool) event.Subscription {
subs := make([]event.Subscription, len(p.subpools))
for i, subpool := range p.subpools {
subs[i] = subpool.SubscribeTransactions(ch, reorgs)
}
return p.subs.Track(event.JoinSubscriptions(subs…))
}

BlobPool は2つのイベントソースを区別します:

  • discoverFeed :新たに発見された取引のみを含む

  • insertFeed :再構成により再度プールに入る取引を含む

func (p *BlobPool) SubscribeTransactions(ch chan\<- core.NewTxsEvent, reorgs bool) event.Subscription {
if reorgs {
return p.insertFeed.Subscribe(ch)
} else {
return p.discoverFeed.Subscribe(ch)
}
}

LagacyPool は新しい取引と再構成取引を区別せず、単一の txFeed を使用してすべての取引イベントを送信します。

func (pool *LegacyPool) SubscribeTransactions(ch chan\<- core.NewTxsEvent, reorgs bool) event.Subscription {
return pool.txFeed.Subscribe(ch)
}

全体として、 SubscribeTransactions はイベントメカニズムを通じて取引プールと他のコンポーネントをデカップリングし、この購読は複数のモジュールで使用できます。たとえば、取引のブロードキャスト、取引のパッケージ化、対外 RPC はすべてこのプロセスをリスンし、対応する処理を行う必要があります。

同時に、p2p モジュール(eth/handler.go)は新しい取引イベントを継続的にリスンし、新しい取引を受信すると、ブロードキャストを送信し、取引を広めます:

// eth/handler.go 新しい取引が発生した後、p2p ネットワークを通じてブロードキャストされます
func (h *handler) txBroadcastLoop() {
defer h.wg.Done()
for {
select {
case event := \<-h.txsCh: // ここで新しい取引情報をリスン
h.BroadcastTransactions(event.Txs)
case \<-h.txsSub.Err():
return
}
}
}

取引をブロードキャストする際には、取引を分類する必要があります。Blob 取引または一定のサイズを超える取引は直接伝播できず、通常の取引は直接伝播可能としてマークされます。その後、現在のノードのピアノードからこの取引を持っていないノードを探します。ノードが直接ブロードキャストできる場合は、true とマークされます。このプロセスも BroadcastTransactions メソッド内で実装されています:

上記の原則に従って取引の分類が完了すると、直接伝播可能な取引は直接送信され、Blob 取引または大きな取引はハッシュのみをブロードキャストし、必要なときにこの取引を取得します。

ハッシュのみを送信する取引は、ピアノードのこのフィールドに

関連タグ
warnning リスク警告
app_icon
ChainCatcher Building the Web3 world with innovations.