1. 程式人生 > >【6】以太坊Miner模組原始碼分析

【6】以太坊Miner模組原始碼分析

Miner模組

類結構如下:

主要結構體

Miner、Worker、Work、CpuAgent、Result、unconfiredBlocks

Miner為挖礦的核心物件(礦工),核心成員包括註冊處理事件(mux)、工人(worker)、coinbase(預設賬戶地址hash)、後臺處理物件(eth)、共識處理引擎(engine)、判定能否挖礦的物件(canstart)、判定是否同步挖礦(shouldstart)

方法:

方法名

引數

主要描述

update

防止DOS攻擊

Start

coinbase

關閉同步,啟動挖礦

Stop

停止挖礦

Register

註冊代理

Unregister

解除代理

Mining

是否挖礦

HashRate

返回Pow當前挖礦的雜湊率

SetExtra

設定worker的extra值(byte陣列)

Pending

掛起返回當前掛起的塊和關聯的狀態。

PendingBlock

掛起返回當前掛起的塊

SetEtherbase

設定coinbase

 

Worker為挖礦管理的核心物件,主要的屬性包括區塊配置項(BlockConfig)、共識引擎(consensus.Engine)、交易事件處理通道或生產訂閱處理(txsch、chainHead、chainSide Ch or Sub)、代理物件集、

屬性

屬性名

型別

主要描述

config

*params.ChainConfig

區塊鏈config配置

engine

consensus.Engine

共識引擎

mu

sync.Mutex

互斥鎖

mux

   *event.TypeMux

Type MeXUX將事件傳送到已註冊的接收方。接收機可以註冊處理某些型別的事件。任何操作MUX停止後呼叫將返回ErrMxxOutlook零值已經準備好使用了。棄用:使用飼料

txsCh

 

chan core.NewTxsEvent

 

txsSub

event.Subscription

 

chainHeadCh

chan core.ChainHeadEvent

 

chainHeadSub

event.Subscription

 

……

……

……

agents

agents map[Agent]struct{}

挖礦代理通道

recv

chan *Result

結果通道

eth

Backend

後臺處理物件(賬號管理、鏈、交易池以及鏈儲存管理)

chain

*core.BlockChain

鏈物件

proc

core.Validator

區塊核查物件,核查區塊兒內容

chainDb

ethdb.Database

資料庫封裝所有資料庫操作。所有的方法都是安全的併發使用。

coinbase

common.Address

Coinbase

extra

[]byte

區塊的extraData

currentMu

sync.Mutex

當前互斥鎖

current

*Work

當前執行的工作work

snapshotMu

sync.RWMutex

讀寫鎖快照

snapshotBlock

*types.Block

區塊快照

snapshotState

*state.StateDB

狀態快照

uncleMu

sync.Mutex

數塊互斥鎖

possibleUncles

map[common.Hash]*types.Block

數塊雜湊對映

unconfirmed

*unconfirmedBlocks

未確認區塊

atWork

int32

執行中的引擎數量

running

int32

狀態判定欄位

 

方法:

方法名

引數

描述

newWorker

Config,engine,eth,mux返回*worker

初始化Worker函式

setEtherbase

addr (common.Address)

設定coinbase

setExtra

extra ([]byte)

設定extra

pending

返回(*types.Block, *state.StateDB)

返回快照防止房錢互斥鎖徵用

pendingBlock

返回 *types.Block

返回區塊快照

start

啟動所有agent挖礦工作

stop

停止所有agent挖礦工作

isRunning

返回是否執行bool值

register

agent

註冊新代理,若worker執行則啟動代理挖礦

unregister

Agent

解除已存在的agent,並停止挖礦

update

處理交易、區塊頭等事件,更新區塊、叔塊和交易資訊

wait

更新所有日誌中的塊雜湊,從它現在可用時,而不是在建立單個事務的收據/日誌時,廣播pending的區塊等待確認

push

Work

建立一個新的work 任務給當前活動的礦工代理

makeCurrent

parent *types.Block, header *types.Header

為當前迴圈,建立一個新的環境

commitNewWork

提交新的work

commitUncle

Work,uncle  

在新的work中新增叔塊hash

updateSnapshot

更新快照

commitTransactions

Mux、 txs、bc、coinbase

提交交易

commitTransaction

Tx,bc、coinbase、gp

 

 

Work是礦工的執行環境,掌握所有當前狀態資訊

屬性

屬性名

型別

主要描述

config

*params.ChainConfig

區塊鏈config配置

signer

types.Signer

簽名器封裝事務簽名處理

state

*state.StateDB

網路狀態儲存

ancestors

mapset.Set

祖先 用於檢查叔塊父塊是否合法

family

mapset.Set

用於檢查叔塊合法

uncles

mapset.Set

叔塊集合

gasPool

*core.GasPool

Gas池 用於打包交易的可用gas值

Block

*types.Block

新塊兒

header

*types.Header

……

txs

[]*types.Transaction

……

receipts

[]*types.Receipt

……

createdAt

time.Time

……

 

Agent:挖礦代理

屬性

屬性名

型別

主要描述

mu

sync.Mutex

互斥鎖

workCh

chan *Work

Work 通道

quitCurrentOp

chan struct{}

取消當前操作

returnCh

chan<- *Result

返回執行結果(work 和區塊)

chain

consensus.ChainReader

Uncle核查時提供查詢區塊鏈的物件

Engine

consensus.Engine

共識處理引擎

Started

Int32

指定agent當前是否啟動

方法:

方法名

引數

描述

Work

無 返回chan<-*Work

返回工作通道

SetReturnCh

ch

設定Work 通道

Start

呼叫update,啟動挖礦

Stop

清空work

Update

consensus.ChainReader

Uncle核查時提供查詢區塊鏈的物件

Mine

work. stop

呼叫engine.Seal封裝交易,打包新塊兒

 

 

Miner 中Gas及gasPrice相關重點分析:( blockheader gaslimit來源commitNewWork)

1)從miner、worker、work、agent的相關介面可知並無直接修改或者接受gas、gasprice、gaslimit的直接設定和修改,miner對gas、gasPrice不具有大的自主性修改許可權和接收許可權

2)挖礦打包的過程由eth.backend.go實現,其中物件Ethereum處理交易池、區塊鏈儲存、共識引擎、礦工、RPC服務等關鍵操作,值得注意的是ethereum物件包含gasprice物件,但該值初始化配置源於config的預設設定為big.NewInt(18 * params.Shannon),eth.config.go檔案中的DefaultConfig;

3)從commitTransactions函式分析,若新的work 可用gas值gasPool為nil預設將work正在處理的區塊的區塊頭中的Gaslimit作為該work的gaspool,作為當前工作的可用gas。當前work.header.Gaslimit來源於work. commitNewWork()core.CalcGasLimit(parent),計算機制如下:

// contrib = (parentGasUsed * 3 / 2) / 1024,當父區塊Gas使用量>2/3的時候,會較上次加大gaslimit,反之減小

計算新區塊gaslimit的貢獻度值contrib := (parent.GasUsed() + parent.GasUsed()/2) / params.GasLimitBoundDivisor(1024)

計算衰減量:decay = parentGasLimit / 1024 -1

  1. 最後計算得到limit := parent.GasLimit() - decay + contrib
  2. gaslimit  不可小於5000 不大於創世塊兒的gaslimit限制4712388

 

此處work打包時要進行gas判斷:env.gasPool.Gas() < params.TxGas(21000),每個work的gas值(也就是說交易傳送者提供的每筆交易gas)不能低於21000;

commitTransaction呼叫了core.ApplyTransaction函式,該函式的具體實現Gas的消耗和獎勵機制,過程如下圖:

  • ApplyTransaction() 首先根據輸入引數分別封裝出一個Message物件和一個EVM物件;
  • 然後加上一個傳入的GasPool型別變數,由TransitionDb()函式完成tx的執行,待TransitionDb()返回之後,建立一個收據Receipt物件,最後返回該Recetip物件,以及整個tx執行過程所消耗Gas數量。
  • GasPool物件是在一個Block執行開始時建立,並在該Block內所有tx的執行過程中共享,對於一個tx的執行可視為“全域性”儲存物件; Message由此次待執行的tx物件轉化而來,並攜帶了解析出的tx的(轉帳)轉出方地址,屬於待處理的資料物件;
  • EVM 作為Ethereum世界裡的虛擬機器(Virtual Machine),作為此次tx的實際執行者,完成轉帳和合約(Contract)的相關操作。

 

TransitioinDb()的執行過程(/core/state_transition.go)。假設有StateTransition物件st,  其成員變數initialGas表示初始可用Gas數量,gas表示即時可用Gas數量,初始值均為0,於是st.TransitionDb() 可由以下步驟展開:

  1. 購買Gas。首先從交易的(轉帳)轉出方賬戶扣除一筆Ether用等於tx.data.GasLimit * tx.data.Price;同 st.initialGas = st.gas = tx.data.GasLimit;然後(GasPool) gp -= st.gas
  2. 計算tx的固有Gas消耗 - intrinsicGas。它分兩個部分,每一個tx預設的消耗量,這個消耗量tx是否含有(轉帳)轉入方地址而略有不同;以及針對tx.data.PayloadGas消耗,Payload型別是[]byte,關於它的固有消耗依[]byte中非00度。最st.gas -= intrinsicGas
  3. EVM執行。如果交易的(轉帳)轉入方地址(tx.data.Recipient)為空,呼叫EVMCreate()函式;否調Call()函式。無哪個函式返回後,更新st.gas
  4. 計算本次執行交易的實際Gas消耗: requiredGas = st.initialGas - st.gas
  5. 償退Gas。它包括兩個部分:首先將剩餘st.gas 折算成Ether歸還給交易的(轉帳)轉出方賬戶;然後,基於實際消耗量requiredGas,系提供一定的補償,數量refundGasrefundGas 所折算的Ether會被立即加在(轉帳)轉出方賬戶上,同時st.gas += refundGasgp += st.gas,即剩餘的Gas加上系統補償Gas,被一起GasPool,供之後的交易行使用。
  6. 獎勵所屬區塊的挖掘者:系統給所屬區的作者,亦即挖掘者賬戶,增加一筆金,數等於 st.data,Price * (st.initialGas - st.gas)。注意,裡的st.gas在步5中被加上了refundGas, 所以勵金所對應Gas,其數量小於交易實際消耗量requiredGas