【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

猜你喜欢

转载自blog.csdn.net/dwjpeng2/article/details/81713521