以太坊源碼分析:交易緩衝池txpool

區塊鏈就是何交易打交道,我們今天就介紹下,交易處理過程中的一個重要組成部分:txpool。這篇文章主要從功能角度介紹,通過這篇文章會了解:

  1. txpool的在交易中的位置和作用。
  2. txpool的功能,核心組成部分queued和pending。
  3. txpool如何實現它的功能。
  4. txpool源碼的重要關注點。

以太坊內部有個重要的內部功能是txpool,從字面意思就能看出來,交易池就是存放交易的池子。它在以太坊中的位置如下圖,只要有新交易,無論是本節點創建的,還是其他peer節點廣播來的,都會先加入到交易池裏,在打包區塊的時候,就從這個池子裏提取,區塊產生之後,共識區塊,交易上鍊。

txpool主圖

txpool有4個功能:

  1. 作爲存放交易的緩衝區,大量交易到來時,先存起來
  2. 爲打包區塊服務,合適交易會被打包到區塊
  3. 清理交易
  4. 當交易的數量多於緩衝區大小時,過濾/懲罰發送大量交易的賬戶(攻擊者)

我們來一張稍微詳細點的模塊交互圖,看txpool怎麼實現上面4個功能的。

txpool模塊交互

緩存功能的設計

txpool中的交易分爲queued和pending 2種,其中queued存放未來的、當前無法執行的交易。以太坊使用nonce值決定某個賬戶的交易順序,多條交易值nonce值必須連續,如果和過去的交易不連續,則無法執行,我們不妨使用nonce值,標記交易的號碼,nonce爲10的交易,稱爲第10號交易。舉個例子,當前賬戶的nonce是10,txpool中有該賬戶的第100號交易,但txpool中沒有第11~99號交易,這些交易的缺失,造成第100號交易無法執行,所以第100號交易就是未來的交易、不可執行的交易,存放在queue中。

pending存放可執行的交易。比如我們把上面的11~99號交易補全了,那麼11~100號交易都可以進入到pending,因爲這些交易都是連續的,都可以打包進區塊。

當節點收到交易(本地節點發起的或peer廣播來的)時,會先存放到queued,txpool在某些情況下,把queued中可執行的交易,轉移到pending中。

爲區塊打包服務

這是txpool最核心的功能,worker在打包區塊的時候,會獲取所有的pending交易,但這些交易還存在txpool中,worker只是讀取出來,至於txpool何時刪除交易,稍後從txpool清理交易的角度單獨在看。

清理交易

txpool清理交易有以下幾種條件,符合任意以下1條的,都是無效交易,會被從pending或者queued中移除

  1. 交易的nonce值已經低於賬戶在當前高度上的nonce值,代表交易已過期,交易已經上鍊就屬於這種情況
  2. 交易的GasLimit大於區塊的GasLimit,區塊容不下交易
  3. 賬戶的餘額已不足以支持該交易要消耗的費用
  4. 交易的數量超過了queued和pending的緩衝區大小,需要進行清理

交易清理主要有3個場景

  1. txpool訂閱了ChainHeadEvent事件,該事件代表主鏈上有新區塊產生,txpool會根據最新的區塊,檢查每個賬號的交易,有些無效的會被刪除,有些由於區塊回滾會從pending移動到queued,然後把queued中可執行的交易移動到pending,爲下一輪區塊打包組號準備。
  2. queued交易移動到pending被稱爲“提升”(promote),這個過程中,同樣會檢查交易,當交易不符合以上條件時,就會被直接從queued中刪除。
  3. 刪除停留在queued中超過3小時的交易,3小時這個超時時間是可以通過geth的啓動參數調整的。txpool記錄了某個賬戶交易進入pending的時間,如果這個時間超過了3小時,代表該賬號的交易遲遲不能被主鏈打包,既然無法被主鏈接受,就刪除掉在queued中本來就無法執行的交易。

懲罰惡意賬號

這也是txpool很重要的一個屬性,可以防止惡意賬戶以發起大量垃圾交易。防止惡意用戶造成:

  1. 佔用txpool空間
  2. 浪費節點大量內存和CPU
  3. 降低打包性能

只有當交易的總數量超過緩衝區大小時,txpool纔會認爲有惡意賬戶發起大量交易。pending和queued緩衝區大小不同,但處理策略類似:

  1. pending的緩衝區容量是4096,當pending的交易數量多於此時,就會運行檢查,每個賬號的交易數量是否多於16,把這些賬號蒐集出來,進行循環依次清理,什麼意思呢?就是每輪只刪除(移動到queued)這些賬號的每個賬號1條交易,然後看數量是否降下來了,不滿足再進行下一輪,直到滿足。
  2. queued的緩衝區容量是1024,超過之後清理策略和pending差不多,但這裏可是真刪除了。

該部分功能未抽象成單獨的函數,而是在promoteExecutables()中,就是在每次把queued交易轉移到pending後執行的。

本地交易的特權,txpool雖然對交易有諸多限制,但如果交易是本節點的賬號發起的,以上數量限制等都對他無效。所以,如果你用本節點賬號不停的發送交易,並不會被認爲是攻擊者,你用txpool.status命令,可以查看到交易的數量,肯定可以大於4096,我曾達到過60000+。

重點關注的源碼

txpool的主要設計上面就講完了,如果你想把txpool的代碼閱讀一番,我建議你重點關注一下這些函數和變量,按圖索驥能就完全掌握txpool的實現。

  • TxPoolConfig:txpool的配置參數
  • chainHeadCh:txpool訂閱了新區塊事件
  • pending:pending的交易,每個賬號都有一個交易列表
  • queue:queued的交易,每個賬號都有一個交易列表
  • loop:txpool的事件處理函數
  • addTx:添加1條交易的源頭,你能找到類似的函數
  • promoteExecutables:queued交易移動到pending
  • reset:根據當前區塊的最新高度,重置txpool中的交易

仔細閱讀一遍,你會發現txpool會涉及多個鎖(TxPool.mu, TxPool.all, TxPool.priced.all),所以當txpool中的交易很多時,它的性能是很低的,這也會影響到區塊的打包。

  1. 如果這篇文章對你有幫助,請點個贊/喜歡,鼓勵我持續分享,感謝。
  2. 我的文章列表,點此可查看
  3. 如果喜歡本文,隨意轉載,但請保留此原文鏈接

一起學Golang-分享有料的Go語言技術

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章