BTCC / BTCC Square / Btcbaike /
Geth 源碼系列:交易設計及實現

Geth 源碼系列:交易設計及實現

Author:
Btcbaike
Published:
2025-11-08 10:40:39
13
3

1. 交易簡介

以太坊執行層可以看作是一個交易驅動的狀態機,交易是唯一修改狀態的方式。 交易只能由 EOA 發起,並且會在交易中附加私鑰的簽名,交易執行之後就會更新以太坊網絡的狀態。 以太坊網絡中最簡單的交易就是 ETH 的轉賬,從一個帳戶轉帳到另一個帳戶。

這裡需要說明一下,隨著 EOA 通過 EIP-7702 的升級可以支持合約的能力,以後合約賬戶和 EOA 的概念會逐漸模糊,但在當前的版本中,還是認為只有私鑰控制的 EOA 才能發起交易。

以太坊支持不同類型的交易,在以太坊主網剛上線時,只支持一種交易,後續在以太坊不斷升級的過程中,才陸續支持了不同類型的交易。 當前主流的交易是可以支持動態費率 EIP-1559 的交易,大多數用戶提交的都是這類交易。 EIP-4844 中則引入了可以為 LAYER2 或者其他鏈下擴容方案提供更加便宜的數據存儲,而在最新的 Pectra 升級中,則通過 EIP-7702 引入了可以將 EOA 擴展為合約的交易形式。

隨著以太坊的發展,後續可能還會支持其他的交易類型,但是交易總體的處理流程變化不會太大,都需要經過交易提交 → 交易校驗 → 進入交易池 → 交易傳播 → 打包進入區塊的流程。

2. 交易結構的演進

從以太坊主網上線開始,以太坊交易結構可以總結為四次大的變化,分別從安全性和擴展性打好了基礎,後續以太坊可以用低成本的方式增加交易類型。

最開始的交易結構如下所示,RLP 標識交易數據會被編碼成 RLP 結構後再傳播和處理:

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

這個結構最大的問題在於沒有和鏈關聯,在主網生成的交易可以被隨意放到其他鏈上去執行,所以中通過在簽名v值中嵌入chainId(如主網ID=1),隔離不同鏈的交易,從而保證每條鏈的交易都無法在其他鏈重放。

涉及 EIP:

  • EIP-155

  • 隨著以太坊的發展,最開始的交易格式已經無法滿足一些場景的需求,所以需要增加新的交易類型,但是如果隨意增加交易的類型,後續可能會面臨管理複雜,無法標準化的問題。 在中,定義了後續交易的格式,主要定義了 TransactionTyPE || TransactionPayload 結構,其中:

  • TransactionType 定義類交易類型,至多可以擴展到 128 種交易類型,足夠新增交易類型使用

  • TransactionPayload 定義了交易的數據格式,當前使用 RLP 來編碼數據,後續也有可能會升級到 SSZ 或者其他編碼

  • 這次升級是在 Berlin 升級中完成,除了 EIP-2718 之外,這次升級中還通過引入了交易類型,這個交易類型允許用戶在交易中預先聲明需要訪問的合約和存儲,可以降低交易執行過程中的 gas 消耗。

    涉及 EIP:

  • EIP-2718

  • EIP-2930

  • 在 London 升級中,EIP-1559 引入了機制,讓 ETH 發行的速度減緩甚至通縮,對於參與質押的節點來說,還有可能通過小費(maxPriorityFeePerGas)獲得額外的收入。 EIP-1559 交易繼承了 Access List 機制,這已經是目前最主要的交易。 並且在 Paris 的 The Merge 升級之後,以太坊從 PoW 轉向 PoS,原先的挖礦經濟模型已經不再適用,以太坊進入 Staking 時代。

    另外在 EIP-1559 中,還通過引入 tARget 機制,可以動態調整 Base Fee,相當於為以太坊引入了負載均衡的能力,target 值是區塊 Gas Limit 的一半,如果超出這個值,Base Fee 就會持續上漲,這樣很多交易就會避開擁堵時間,這樣可以讓鏈的整體擁堵情況能減緩,用戶體驗更好。

    涉及 EIP:

  • EIP-1559

  • 在 EIP-2718 和 EIP-1559 分別定義好擴展交易的標準和經濟模型之後,就陸續有新的交易類型增加。 在最近兩次的升級中,分別增加了 EIP-4844 和 EIP-7702,前者增加了 Blob 交易類型,為鏈下擴容方案提供了理想化的存儲方案,空間大,價格還低,而且也有著類似 EIP-1559 的經濟模型和負載機制,EIP-7702 則可以將 EOA 改造成掌握私鑰的智能合約賬戶,為後續帳號抽象的大規模採用做好準備。

    涉及 EIP:

  • EIP-4844

  • EIP-7702

  • 3. 交易模塊架構

    交易作為以太坊這個狀態機的輸入,幾乎所有的主流程都圍繞交易來進行,交易進入交易池之前需要校驗交易的格式和簽名等信息,進入交易池之後,需要在不同的節點之間傳播,再被出塊節點從交易池中選擇,然後交易會在 evm 上執行,並修改狀態數據庫,最後被打包進區塊在執行層和共識層之間傳遞。

    對於出塊節點和非出塊節點,處理交易的流程會有一些差別。 對於出塊節點,會負責從交易池中挑選交易,然後打包進區塊,並更新本地的狀態數據庫,對於非出塊節點,只需要將同步到的最新區塊中的交易重新執行一遍,讓本地的狀態更新到最新。

    目前以太坊總共支持五類交易。 這些交易的主要結構都類似,不同交易會通過交易類型字段來區分,不同類型的交易中通過一些擴展字段來實現特定的用途。

  • LegacyTxType:創世區塊沿用至今的基礎格式,採用第一價格拍賣模型(用戶手動設置gasPrice),EIP-155 升級後默認嵌入 chainId 防跨鏈重放攻擊,當前在以太坊主網上的使用量已經比較少,以太坊當前兼容這個交易類型,後續會逐步淘汰

  • AccessListTxType:預熱存儲訪問大幅降低 Gas成本,這個特性已經被後續的交易類型繼承,直接使用這個交易類型的交易也較少

  • DynamicFeeTxType:更新了以太坊的經濟模型的交易類型,引入 Base Fee 和 target 機制,繼承了 AccessList 特性,這也是當前最主流的交易類型

  • BlobTxType:專為鏈下擴容涉及的交易類型,允許交易通過 blob 結構攜帶低成本的大量數據,降低鏈下擴容方案的成本,繼承了 AccessList 和 DynamicFee 特性,並且交易中的 blob 有與 EIP-1559 類似的單獨計費機制

  • SetCodeTxType:允許 EOA 轉換為合約賬戶(也可以通過交易撤銷合約能力),並可以執行 EOA 中對應的合約代碼,繼承了 AccessList 和 DynamicFee 特性

  • 當交易被打包進行區塊之後,已經完成了對狀態數據的修改,可以理解為交易的生命週期已經結束,在這個過程中,交易會經歷四個週期:

  • 交易驗證:EOA 提交的交易會經過一系列基礎驗證之後才會被加入到交易池中

  • 交易廣播:新提交到交易池的交易會被廣播到其他的節點的交易池中

  • 交易執行:出塊節點會從交易池中挑選交易進行執行

  • 交易打包:交易會按照一定的順序(先區分是否是本地交易,然後再按照 gas fee 大小)打包進區塊,會忽略那些會驗證不通過的交易

  • 交易池是臨時存放交易的地方,交易在被打包之前,都會被存儲在交易池中,交易池中的交易會被同步到其他節點,同時也會從其他節點的交易池中同步交易。 用戶提交的交易會首先進入到交易池中,然後經過共識層觸發共識流程,驅動交易執行以及被打包進區塊。

    當前交易池的實現有兩種類型:

  • Blob 交易池(Blob TxPool)

  • 其他交易的交易池(Legacy TxPool)

  • 由於 Blob 交易攜帶的數據與其他交易攜帶的數據處理流程不一樣,所以會使用單獨的交易池來處理,是其他類型的交易雖然類型不一致,但在不同節點之間同步以及被打包的流程基本一致,所以會在同一個交易池中被處理,交易池中交易都由外部 EOA 的所有者提交,向交易池中提交交易有兩種方式:

  • SendTransaction

  • SendRawTransaction

  • SendTransaction 是客戶端發送的是一個未簽名的交易對象,節點會用交易中發起地址 from 對應的私鑰來對交易進行簽名。 而 SendRawTransaction 則需要提前對交易簽名,然後將簽名完成的交易提交到節點。 這種方式更常用,使用 Metamask、RABby 等錢包使用的就是這種方式。

    在這里以 SendRawTransaction 為例,在節點啟動之後,節點會啟動以一個 API 模塊,用來處理外部的各類 API 請求,SendRawTransaction 就是其中的一個 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. 核心數據結構

    對於交易模塊來說,核心的數據結構就兩部分,一部分是用來表示交易本身的數據結構,另一部分就是表示臨時存儲交易的交易池結構。 由於交易在交易池中需要在不同的節點之間傳播,所以在交易池中的實現中會依賴底層的 p2p 協議。

    使用了 CORE/types/transaction.go 的 Transaction 來統一表示了所有的交易類型:

    type Transaction struct { inner TxData    // 交易實際的數據會存儲在這裡 time  time.Time  //.... }

    TxData 是一個 interface 類型,定義了所有交易類型都需要實現的屬性獲取方法,但是對於 LegacyTxType 這類交易,其中很多字段都沒有,那就會使用之前已經存在的字段替代或者直接返回空:

    type TxData interface { txType() byte // 交易類型 copy() TxData // 創建交易數據的深度拷貝   chainID() *big.Int // 鏈ID,用於區分不同的以太坊網絡 accessList() AccessList // 預編譯的訪問列表,用於優化gas消耗(EIP-2930引入) data() []byte   // 交易的輸入數據,用於合約調用或創建 gas() uint64    // Gas 限制,表示交易最多可以消耗的 gas 數量 gasPrice() *big.Int  // 每單位 Gas 的價格(用於 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 // 計算實際的gaS價格(考慮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 是一個 interface,每個交易池的具體實現都需要實現這個 interface:

    type TxPool struct { subpools []SubPool // 交易池的具體實現 chain    BlockChain // ... }   type LegacyPool struct { config      Config     // 交易池參數配置 chainconfig *params.ChainConfig // 區塊鏈參數配置 chain       BlockChain // 區塊鏈接口 gasTip      ATOMic.Pointer[uint256.Int] // 當前接受的最低 gas 小費 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 的兩個交易池為:

  • BlobTxPool:用於管理 Blob 交易

  • LegacyTxPool:用於管理 Blob 交易之外的其他交易

  • 之所以 Blob 交易需要和其他交易分開管理,是因為 Blob 交易中有可能會攜帶大量的 blob 數據,其他的交易都可以直接在內存中管理和同步,而 Blob 交易中的 blob 數據則需要持久化存儲,所以不能使用和其他交易一樣的管理方式。

    5. 費用機制

    由於以太坊本身無法處理,所以使用 Gas 機制來防止一些惡意的攻擊,另外 Gas 本身也作為用戶的手續費在使用,這是 Gas 最初的兩個用途。

    經過了這些年的發展之後,Gas 除了上面的兩個用途,還是以太坊經濟模型的重要組成部分,可以控制 eth 的發行數量,還可以幫助以太坊完成通縮。 甚至還可以動態調節以太坊網絡的流量,提升用戶使用體驗。

    以太坊的費用機制使用 Gas 來實現,有維護網絡安全、實現經濟模型平衡等多種作用。

    以太坊在處理交易時,在 EVM 上執行的每個操作都需要消耗 Gas,比如使用內存、讀取數據、寫入數據等等,有的操作消耗 Gas 多,有的消耗少,比如 ETH 的轉賬操作需要消耗 21,000 Gas。 在每一筆交易中,都需要設定該交易最高需要消耗多少 Gas,如果 Gas 消耗完,那麼交易就執行結束,而且這個過程中消耗的 Gas 不會退還,也正是這個機制可以用來處理以太坊的停機問題。

    以太坊中區塊的大小也是使用 Gas 來限制,而不是使用具體的大小單位,。 Gas 只是 EVM 執行過程中的計量單位,用於需要為每筆交易消耗的 Gas 支付 ETH,Gas 的價格通常使用 GWei 來表示, 1 Ether = 10^9 Gwei。

    在當前的以太坊網絡中,當前的一個區塊的大小限制是 36M Gas,但是在當前的社區中對於提升區塊的 Gas 限制的聲量很大,認為提升區塊 Gas 限製到 60M 是比較合理的選擇,這個數字會提升網絡的容量,而且同時不會威脅到網絡的安全,目前已經在測試網測試中。 同時社區中也有人認為單純使用 Gas 限制來控制的區塊的大小不合理,需要引入字節大小的限制,目前這些正在社區討論中。

    在引入 EIP-1559 的機制之後,將之前的 GasPrice 直接拆分成了 Base Fee 和,其中 Base Fee 會全部被銷毀,以控制以太坊中 ETH 的增長速度,則會給出出塊節點對應的驗證者。 用戶可以在交易中設置 maxFeePerGas 來保證最終支付的費用是受限的。

    如果要保證交易成功,那麼就需要保證 maxFeePerGas ≥ Base Fee Priority Fee,否則交易會執行失敗,並且費用也不會退換。 用戶需要實際支出的費用為 (Base Fee Priority Fee)×Gas Used,多餘的費用會退還給交易發起的地址。

    Base Fee 處在動態變化中,以區塊中 Gas 的實際用量為基準,區塊最大 Gas 限制的一半稱之為 target,如果上一個區塊的實際用量超過了 target,那麼當前的區塊的 Base Fee 就會增加,如果前一個區塊的 Gas 用量低於 target,那麼 Base Fee 就會減少,否則就不變。

    Blob 交易的費用結算分為兩部分,一部分是使用 EIP-1559 來和其他交易共同調整 Base Fee,另一部分是 Blob 交易中的 Blob 數據有一個獨立的 Blob Fee 機制,其中 target 值是最大 Blob 數量的一半,也是根據 Blob 數據塊的使用量來調整 Blob Fee,但是不單獨設置 Priority Fee,因為 Blob 交易也可以直接設置交易中的 Priority Fee 來促使 Blob 交易被更快打包。

    6. 交易處理流程源碼分析

    在上面詳細介紹了以太坊中交易機制的設計及實現,接下來,我們將通過分析代碼,詳細介紹交易在 Geth 中具體實現,包括交易在整個生命週期中的處理流程。

    無論是通過 SendTransaction 還是 SendURawTransaction 的方式提交交易,都會調用 internal/ethaPi/api.go 中的 SubmitTransaction 函數向交易池提交交易。

    在 這個函數中,會對交易做兩個基本的檢查,一個是檢查 gas fee 是否合理,另一個是檢查是交易是否滿足 EIP-155 的規範,EIP-155 通過在交易簽名中引入 chainID 參數,解決了跨鏈交易重放問題。 該檢查確保當節點配置開啟 EIP155Required 時,所有向交易池提交的交易都必須符合這個標準。

    在完成檢查之後,就會把交易提交到交易池,由 eth/api_backend.go 中的 SendTx 來實現添加邏輯:

    在交易池中,會通過 Filter 方法來為交易匹配對應的交易池,當前有兩個交易池實現,如果是 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)   // Before replacing the old transaction, ensure the _new_ transaction fee is reasonable. 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 } // Iterate the pending list for replacement 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 { // Match. Re-sign and send the transaction. 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 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 區分了兩種事件源:

  • discoverFeed :僅包含新發現的交易

  • insertFeed :包含所有交易,包括因重組而重新進入池的交易

  •  

    func (p *BlobPool) SubscribeTransactions(ch chan if reorgs { return p.insertFeed.Subscribe(ch) } else { return p.discoverFeed.Subscribe(ch) } }

    LagacyPool 不區分新交易和重組交易,它使用單一的 txFeed 來發送所有交易事件。

    func (pool *LegacyPool) SubscribeTransactions(ch chan 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.BroadcastTransactions(event.Txs) case return } } }

    在廣播交易時,需要對交易進行分類,如果是 blob 交易或者是超過了一定大小的交易,無法直接傳播,對於普通的交易,則標記為可以直接傳播。然後從當前節點的對等節點中去找那些沒有這筆交易的節點。如果節點可以直接廣播,則標記為 true,這個過程也是在 BroadcastTransactions 方法中實現的:

    依照上面的原則對交易分類完成,可以直接傳播的交易就直接發送,blob 交易或者大交易則只廣播 hash,等到需要用到這筆交易的時候再來獲取。

    廣播中只發送 hash 的交易會被放到 peer 節點的這個字段中:

    新交易會通過 p2p 模塊廣播出去,同時也會從 p2p 網絡中接收新的交易。在 eth/backend.go 中初始化 Ethereum 實例時,會初始化 p2p 模塊,添加交易池的接口。p2p 模塊運行起來之後,會從 p2p 消息中解析出交易請求添加到交易池中。

    具體來說,在實例化 handler 的時候,會指定好從其他節點獲取交易的方式,會通過 eth/fetcher 中的 TxFetcher 去獲取遠端的交易,TxFetcher 會通過這裡的 fetchTx 方法去獲取遠端的交易,實際是調用了 eth/protocols/eth 協議中實現的 RequestTxs 方法去獲取交易:

      // eth/backend.go New 函數 if eth.handler, err = newHandler(&handlerConfig{ NodeID:         eth.p2pServer.Self().ID(), Database:       chainDb, Chain:          eth.blockchain, TxPool:         eth.txPool, Network:        networkID, Sync:           config.SyncMode, BloomCache:     uint64(cacheLimit), EventMux:       eth.eventMux, RequiredBlocks: config.RequiredBlocks, }); err != nil { return nil, err } // eth/handler.go newHandler 函數,註冊獲取新交易的過程 fetchTx := func(peer string, hashes []common.Hash) error { p := h.peers.peer(peer) if p == nil { return errors.New("unknown peer") } return p.RequestTxs(hashes) // 去其他節點請求交易 } addTxs := func(txs []*types.Transaction) []error { return h.txpool.Add(txs, false) // 將交易加入交易池 } h.txFetcher = fetcher.NewTxFetcher(h.txpool.Has, addTxs, fetchTx, h.reMOVEPeer) // eth/handler_eth.go Handle 方法,在接收到新的交易之後,會添加到交易池中 for _, tx := range *packet { if tx.Type() == types.BlobTxType { return errors.New("disallowed broadcast blob transaction") } } return h.txFetcher.Enqueue(peer.ID(), *packet, false) // eth/fetcher/tx_fetcher.go 的 Handle 方法會調用上面註冊的 addTxs 來將講義添加到交易池 for j, err := range f.addTxs(batch) { //.... }

    RequestTxs 方法通過發送 GetPooledTransactionsMsg 消息,然後收到其他節點發送的 PooledTransactionsMsg 的響應,由 backend 中的 Handle 方法來處理,在這個方法中調用了 txFETcher 的  Enqueue 方法,最終 Enqueue 方法調用了 adTxs 方法把從其他節點獲取的交易添加到交易池:

    在交易池中還有一個延遲加載的設計,通過core/txpool/subpool.go中的 LazyTransaction 來實現,通過延遲加載機制減少內存使用並提高交易處理效率。它存儲交易的關鍵元數據,只在真正需要時才加載完整交易數據,在以太坊處理大量交易時發揮著重要作用。這種設計特別適合交易池和區塊打包這樣的場景,其中大多數交易可能最終不會被包含在區塊中,因此不需要完整加載所有交易數據。

    type LazyTransaction struct { Pool LazyReSOLver       // Transaction resolver to pull the real transaction up Hash common.Hash        // Transaction hash to pull up if needed Tx   *types.Transaction // Transaction if already resolved   Time      time.Time    // Time when the transaction was first seen GasFeeCap *uint256.Int // Maximum fee per gas the transaction may consume GasTipCap *uint256.Int // Maximum miner tip per gas the transaction can pay   Gas     uint64 // Amount of gas required by the transaction BlobGas uint64 // Amount of blob gas required by the transaction }   func (ltx *LazyTransaction) Resolve() *types.Transaction { if ltx.Tx != nil { return ltx.Tx } return ltx.Pool.Get(ltx.Hash) }

    另外由於以太坊是一個 perminssionless 的網絡,節點有可能會從網絡中接收到一些惡意的請求,極端情況下,節點可能會面臨 DDos 攻擊,所以節點會使用一系列的方式來防止來自網絡的惡意攻擊:

  • 交易基礎驗證

  • 節點資源限制

  • 交易驅逐機制

  • p2p 網絡層防護

  • 這里以 Legacypool 為例(Blobpool 也有類似的機制),在交易被添加進交易池之前,首先會經過一個基礎的驗證,在 core/txpool/validation.go 中的 ValidateTransaction 方法中,會對交易做一個基礎的驗證,包括交易類型、交易大小、Gas 等是否符合要求,如果不符合,就會拒絕接收交易。

    這裡的交易大小使用 Slot 來規定,在 core/txpool/legacypool/legacypool.go 中定義了 Slot:

    const ( txSlotSize = 32 * 1024 txMAXSize = 4 * txSlotSize // 128KB )

    每個交易不能超過 4 個 Slot,而且對於每個賬戶、整個節點都有最大 Slot 的限制,對於賬戶,達到限制之後就不能提交新的交易。對於節點,達到限制之後就需要剔除舊的交易,在core/txpool/legacypool/legacypool.go中的 truncatePending 方法中會公平驅逐交易,防止單個賬戶佔用過多交易池資源:

    type Config struct { AccountSlots uint64 GlobalSlots  uint64  }

    在網絡層上, 對於 blob 交易或者是超過了一定大小的交易,不會直接在網絡上傳播交易內容,只會傳播交易 Hash,從而避免網絡上傳播的數據量過大,造成 DDos 攻擊。

    在交易提交到交易池之後,會在以太坊網絡中的節點之間傳播,在某個節點的驗證者被選中為出塊節點之後,驗證者就會委託共識層和執行層構造區塊。

    驗證者會首先從共識層觸發區塊構造流程,共識層在接收到區塊構造請求之後,就會調用執行層的 engineAPI 來構造區塊,engineAPI 的實現在 eth/catalyst/api.go 。共識層會先調用 ForkchoiceUpdated API 來發送構造區塊的請求,ForkchoiceUpdated 有多個版本,具體調用哪個版本依據當前網絡版本來決定,調用完成之後會返回 PayloadID,然後根據這個參數調用 GetPayload 對應版本 API 來獲取區塊的構造結果。

    無論調用的是 ForkchoiceUpdated 的哪個版本,最終都是調用 forkchoiceUpdated 方法來構造區塊:

    在 ForkchoiceUpdated 方法中會對執行層當前的狀態做校驗,如果當前執行層正在同步區塊、或者獲得最終性的區塊不符合預期,那麼該方法都會向共識層直接返回錯誤信息,構造區塊失敗:

    在對執行層的信息校驗完成之後,就會調用 miner/miner.go 中的 BuildPayload 方法來構造區塊。構造區塊的具體操作都在 miner/payload_building.go 中的 generateWork 方法中完成,但這裡需要注意,在調用這個方法之後,就會先產生了一個空的 payload,並把這個 payloadID 返回給共識層。同時會啟動一個 goroutine 真正去完成區塊的打包流程,這個 goroutine 會持續去交易池中找價值更高的交易,每次重新打包交易之後,都會更新 payload。

    打包交易的是通過 miner/worker.go 中的 fillTransactions 方法來完成,實際上就是調用 txpool 的 Pending 方法來獲取待打包的交易:

    共識層在 slot 結束之前會調用 getPayload API 來獲取最終打包好的區塊。如果提交的交易被打包在這個區塊當中,那麼交易就會被 EVM 執行,並改變狀態數據庫。如果這次沒有被打包,那麼就會等待下一次被打包。

    在打包交易的過程中,同時也會將交易在 EVM 中執行,得到區塊交易完成之後狀態的變化,同樣還是在 generateWork 函數中,準備好當前區塊執行的環境變量,主要是獲取最新區塊和最新狀態數據庫:

    這裡的 state 就是代表狀態數據庫:

    在這裡形成了一個 StateDB → stateObjects→ stateAcount 的結構,分別代表完整的狀態數據庫、賬號對象集合以及單個賬戶對象。其中 StateObject 中結構中,dirtyStorage 表示當前交易執行後已改變的狀態,pendingstorage 表示當前區塊執行之後已改變的狀態,originStorage 表示原始的狀態,所以這三個狀態從新到舊是 dirtyStorage → pendingStorage → originStorage,這里關於存儲的詳細解析可以查看之前關於存儲的詳細解析:

    在eth/backend.go的 New 方法中啟動時會加載交易池的配置,其中有一個 Locals 的配置,這個配置中的地址會被視為本地地址,這些本地地址提交的交易會被優先處理。

    在獲取到當前的環境變量之後,就可以執行交易了,首先會獲取全部待打包的交易,並把其中的本地交易挑選出來,區分成本地交易和正常交易,然後會對本地交易和正常交易分別按照手續費從高到低打包交易。交易具體的執行都在miner/worker.go中 cOMmitTransactions 方法中進行:

    最終都是調用 ApplyTransaction 函數,在這個函數中,會調用 EVM 執行交易,並修改狀態數據庫:

    func ApplyTransaction(evm *vm.EVM, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *uint64) (*types.Receipt, error) { msg, err := TransactionToMessage(tx, types.MakeSigner(evm.ChainConfig(), header.Number, header.Time), header.BaseFee) if err != nil { return nil, err } // Create a new context to be used in the EVM enviRONment return ApplyTransactionWithEVM(msg, gp, statedb, header.Number, header.Hash(), tx, usedGas, evm) }

    上面討論的情況都是交易被打包進區塊的流程,大多數情況下,節點只會驗證已經被打包好的區塊,而不是自己打包區塊。

    共識層在同步到區塊之後,使用 engine API 將同步到的最新區塊傳輸到執行層。使用的是 engine_NewPayload 系列方法。這系列的方法最後都會調用 newPayload 方法,在這個方法中將共識層的 payload 組裝成一個 block:

    然後檢查這個區塊是否已經存在了,如果存在了,那麼就直接返回取消有效的狀態:

    如果當前執行層還在同步狀態,那麼暫時就無法接收新的區塊:

    如果上面的條件都滿足,那麼就開始將區塊插入到區塊鏈中,這裡需要注意,在插入區塊的時候不會直接指定鏈頭,因為鏈頭的決策會涉及到鏈分叉的選擇,這個需要依靠共識層來決定:

    共識層會調用 forkChoiceupdated API 來調用core/blockchain.go中的SetCanonical方法來決定區塊頭:

    還有一種情況會觸發區塊頭的設置,那就時區塊發生重組,區塊重組會執行 core/blockchain.go 的 reorg 方法,在這個方法中同樣會設置當前最新確定的區塊頭。

    回到區塊的執行過程,在 core/blockchain.go 中的 InsertBlockWithoutSetHead 方法會調用 insertChain 方法,在這個方法中,會做一系列條件的檢查,檢查完成之後,就會開始處理區塊:

    在具體的 Process 裡面,處理邏輯就很清晰了,和之前打包交易的流程類似,不斷在 evm 中執行交易,然後修改狀態數據庫,與打包不同的地方在於,這裡只是將新區塊中的交易重放一遍,而不需要去交易池中獲取。

    7. 總結

    交易是驅動以太坊狀態變化的唯一方式,交易在以太坊中的處理需要經過多個階段。 交易需要先經過驗證,再被提交到交易池,在不同的節點之間通過 p2p 網絡傳播,然後被出塊節點打包進區塊,最後其他節點同步區塊,在本地執行區塊中的交易,並同步狀態變更。

    隨著以太坊協議的不斷發展,從最開始只能支持一種交易,到目前可以支持 5 種交易。 這些不同類型的交易可以讓以太坊適應不同的角色,及可以作為 DApp 的運行平台,也可以作為 Layer2 或者其他鏈下擴容的結算層。 最近新增加的 EIP-7702 為以太坊被大規模採用做好了技術上的準備。

    [1]https://ethereum.org/zh/developers/docs/transactions/

    [2]https://hackmd.io/@danielrachi/engine_api

    [3]https://github.com/ethereum/go-ethereum/commit/c8a9a9c0917dd57d077a79044e65dbbdd421458b

    [4]https://pumpthegas.org/

    [5]https://github.com/ethereum/EIPs/pull/9698

    免責聲明:
    1.資訊內容不構成投資建議,投資者應獨立決策並自行承擔風險
    2.本文版權歸屬原作所有,僅代表作者本人觀點,不代表本站的觀點或立場上一篇:庸俗的創業項目 下一篇:返回欄目

    您可能感興趣

    • Geth 源码系列:交易设计及实现Geth 源碼系列:交易設計及實現

      1. 交易簡介 以太坊執行層可以看作是一個交易驅動的狀態機,交易是唯一修改狀態的方式。 交易只能由 EOA 發起,並且會在交易中附加私鑰的簽名,交易執行之後就會更新以太坊網絡的狀態。 以太坊網絡中最簡單的

      每日資訊2025-11-08 10:400
    • 庸俗的创业项目庸俗的創業項目

      週末的時候,一位很久不太聯繫的朋友給我發來一條微信,問這段時間以來加密生態有沒有什麼好玩新鮮的項目。 我很遺憾地回复他,這個週期以來在加密生態中除了極少數賽道以外,我都沒有發現什麼新鮮的項目。 於是朋

      每日資訊2025-11-08 10:3010
    • WLFI 与 Solana 合作,美元市值突破 30 亿美元,内部人士担忧情绪蔓延WLFI 與 Solana 合作,美元市值突破 30 億美元,內部人士擔憂情緒蔓延

      11月5日,World Liberty Financial(WLFI)投下了一枚重磅炸彈,在加密貨幣社區引起了軒然大波。 這家由特朗普支持的去中心化金融(DeFi)平台宣布與Solana的兩大巨頭Bon

      每日資訊2025-11-08 10:2012
    • RWA 2025 报告:资产代币化与未来展望RWA 2025 報告:資產代幣化與未來展望

      華爾街已然鏈上化,就在你沉睡時悄然變革。 瑞銀、阿波羅等金融巨頭已將數百億資產代幣化,這些資產如今24/7全天候流轉,不再局限於朝九晚五。 本報告由DigiFT與Tiger Research聯合撰寫,全面

      每日資訊2025-11-07 22:1052
    • 揭秘 KOL 轮:被流量裹挟的一场财富实验揭秘 KOL 輪:被流量裹挾的一場財富實驗

      作者:Viee,Biteye 核心貢獻者 編輯:Denise,Biteye 核心貢獻者曾幾何時,一級市場的玩法相對清晰:VC 出錢、KOL 發聲、散戶提供流動性。 但今天,這套玩法似乎正在被打破。 V

      每日資訊2025-11-07 22:0039
    • 再谈 Virutal 和 X402再談 Virutal 和 X402

      - 再談Virutal和X402 儘管在前面的文章中我經常談Virtual,也聊過一些X402的話題,不過還是有不少讀者會問相關的問題。 關於Virtual,我記得幾個月前當Genesis Lauch

      每日資訊2025-11-07 21:5040
    • 活动很简单,折扣不简单——火币HTX三大借贷福利同步上线,助力低成本精准捕捉市场机遇活動很簡單,折扣不簡單——火幣HTX三大借貸福利同步上線,助力低成本精準捕捉市場機遇

      未來,火幣HTX將繼續迭代創新,推出更靈活、更低成本的資金管理解決方案,賦能用戶在任何行情下都能從容佈局、穩健前行。 11月7日,火幣HTX宣布將重磅推出為期7天的“波動掘金周”活動。 自11月8日00:

      每日資訊2025-11-07 20:5044
    • 头部矿池及算力生态加入 Psy Protocol 测试网,共筑新一代 PoW 智能合约平台頭部礦池及算力生態加入 Psy Protocol 測試網,共築新一代 PoW 智能合約平台

      Psy Protocol 今日宣布,四家行業頭部礦池及算力生態將加入其測試網,這對於一個全新的 PoW 智能合約平台而言尤為罕見。 全球最大的 Dogecoin 與 Litecoin 礦池 F2Pool

      每日資訊2025-11-07 20:4051
    • 成交量排行
    • 幣種熱搜榜
    名稱最新價24H額($)

    泰達幣$0.9994511.1億

    比特幣$100068.45233.8億

    以太坊$3235.69167.3億

    USD Coin$1.000472.2億

    Solana$152.5851.1億

    大零幣$628.9541.8億

    瑞波幣$2.173238.5億

    First Digital USD$0.997130.9億

    OFFICIAL TRUMP$7.423827.2億

    幣安幣$936.7625.1億

    Filecoin$2.102619.6億

    狗狗幣$0.162517.3億

    Internet Computer(Dfinity)$7.823314.2億

    Pepe$5.61E-613.5億

    達世幣$106.1310.2億名稱最新價24h漲幅

    大零币

    ZEC$638.48+21.56%

    Dfinity

    ICP$8.0889+26.27%

    FIL6

    FIL$2.0893+54.24%

    Horizen

    ZEN$19.423-3.99%

    Arweave

    AR$5.5503+28.65%

    OK币

    OKB$117.62-1.86%

    比特币SV

    BSV$23.2399+5.89%

    比特币

    btc$100209.36-2.91%

    以太经典

    ETC$15.9931+11.89%

    柚子

    EOS$0.2701+6.76%

    以太坊

    ETH$3239.66-4.75%

    狗狗币

    DOGE$0.163+0.06%

    币安币

    BNB$943.16-1.3%

    Mina Protocol

    MINA$0.1714-2.89%

    Conflux Token

    CFX$0.090247+1.16%

    一周熱點

    • Geth 源码系列:交易设计及实现Geth 源碼系列:交易設計及實現
    • 庸俗的创业项目庸俗的創業項目
    • WLFI 与 Solana 合作,美元市值突破 30 亿美元,内部人士担忧情绪蔓延WLFI 與 Solana 合作,美元市值突破 30 億美元,內部人士擔憂情緒蔓延
    • RWA 2025 报告:资产代币化与未来展望RWA 2025 報告:資產代幣化與未來展望
    • 揭秘 KOL 轮:被流量裹挟的一场财富实验揭秘 KOL 輪:被流量裹挾的一場財富實驗
    • 再谈 Virutal 和 X402再談 Virutal 和 X402
    • 活动很简单,折扣不简单——火币HTX三大借贷福利同步上线,助力低成本精准捕捉市场机遇活動很簡單,折扣不簡單——火幣HTX三大借貸福利同步上線,助力低成本精準捕捉市場機遇
    • 头部矿池及算力生态加入 Psy Protocol 测试网,共筑新一代 PoW 智能合约平台頭部礦池及算力生態加入 Psy Protocol 測試網,共築新一代 PoW 智能合約平台
    • Stable 的筹款乱象:我为什么没搭上 FOMO 列车Stable 的籌款亂象:我為什麼沒搭上 FOMO 列車

    熱門專題

    • GFT槓桿使用全攻略:這5大要點你不能錯過!
    • 驚爆! 門羅幣飆破關鍵節點,隱私加密貨幣新時代降臨?
    • THL平台大揭秘! 你不知道的5大真相
    • 驚! 幣安幣單日暴跌4.2%背後:威廉指標超賣警報與626美元關口爭奪戰
    • WIF平台大揭秘! 99%的人都不知道的那些事兒
    • 回顧萊特幣價格走勢:曾短期暴漲後暴跌,也曾受疫情影響下跌
    • FUSE交易平台深度剖析:你不可不知的5大關鍵點
    • 2025年虛擬貨幣排行榜大揭秘! 這些幣種值得關注
    • FLTd合約深度解析:你所不知道的10大關鍵要點
    • OKX交易全知道! 10個你不可不知的交易秘籍
    • 2025比特幣有望迎來牛市,機構預測價格將大幅飆升!
    • PKOIN交易大揭秘:你不可不知的5大關鍵要點
    查看更多關於我們|聯繫我們|免責聲明|網站地圖|訪問手機版友情鏈接:加密貨幣新聞|幣界網|Copyright © 2017-2021[志如坤文化] 上海志如坤文化傳播有限公司•滬ICP備2021030065號-10tongji();layuitab();

    |Square

    下載BTCC APP,您的加密之旅從這啟程

    立即行動 掃描 加入我們的 100M+ 用戶行列

    本站轉載文章均源自公開網絡平台,僅為傳遞行業信息之目的,不代表BTCC任何官方立場。原創權益均歸屬原作者所有。如發現內容存在版權爭議或侵權嫌疑,請透過[email protected]與我們聯絡,我們將依法及時處理。BTCC不對轉載信息的準確性、時效性或完整性提供任何明示或暗示的保證,亦不承擔因依賴這些信息所產生的任何直接或間接責任。所有內容僅供行業研究參考,不構成任何投資、法律或商業決策建議,BTCC不對任何基於本文內容採取的行為承擔法律責任。