以太坊源码解析(1)— miner挖矿模块

以太坊版本:release/1.4

在以太坊1.4版本及之前以太坊共识算法还是使用的PoW工作量证明,因为本团队开发使用的还是1.4版本,本篇文章是对1.4版本的挖矿源代码进行分析讲解,欲看更高版本的请自行绕过或等待博主更新,另外关于PoW共识可以阅读下篇文章:

点击打开链接 https://mp.csdn.net/postedit/80535270

挖矿的生命周期

挖矿模块包括四个源码文件:miner.go(模块核心入口文件)、worker.go(模块核心执行文件)、agent.go(基于CPU挖矿代理实现)、remote_agent.go(远程挖矿代理实现);所谓代理可以理解为真正的挖矿工人,而worker可以理解为是包工头,后两者分别是对代理的两种实现,在这里主要使用的和要讲解的是两者中的前者;

读完整个挖矿模块的源码后,我按照自己的理解把以太坊挖矿大致分成了六个过程即生命周期:创建(New)、注册代理(register)、启动(Start)、发布任务(push)、共识挖矿(mine)、停止(stop),流程图如下:

以太坊源码解析(1)— miner挖矿模块_第1张图片

创建(New):

该阶段初始化miner对象和worker对象。worker对象是挖矿模块的核心对象,包含链的基本配置信息、代理对象的切片、挖矿结果、pow、链对象、账户和交易队列等信息。

注册代理(register):

程序状态触发启动时会向worker对象中注册代理对象,可以理解为向包工头分配工人。如果包工头手下没有工人可能工作就没法进行,所以程序成功执行的条件要保证worker对象至少包含一个agent对象。

启动(Start):

在代理注册之后miner对象会调用worker对象的start方法,实际上是遍历启动worker中的所有agent对象。agent对象的start方法逻辑上是启动一个for循环监听work挖矿任务,如果有挖矿任务就进行挖矿,如果有退出类型消息就退出循环体

发布任务(push):

在启动之后worker对象向其中每一个agent对象的work管道中推送work挖矿任务,agent监听到管道中的任务后调用执行该任务挖矿

共识挖矿(mine):

agent对象挖矿的过程其实就是调用PoW模块获取符合规则的随机数的过程,如果agent对象中的quitCurrentOp消息管道不为空,那么获取的随机数为0即该work任务挖矿失败,如果quitCurrentOp消息管道为空,那么会一直尝试寻找符合规则的随机数直到找到为止

停止(stop):

触发停止动作后会关闭worker对象中的所有agent并移除,同时关闭相关的事件,挖矿进程结束

核心源码分析如下(参考注释部分):

miner.go :

/**
*启动挖矿进程
*/
func (self *Miner) Start(coinbase common.Address, threads int) {
    atomic.StoreInt32(&self.shouldStart, 1)
    self.threads = threads
    self.worker.coinbase = coinbase
    self.coinbase = coinbase
 
  
    if atomic.LoadInt32(&self.canStart) == 0 {
        glog.V(logger.Info).Infoln("Can not start mining operation due to network sync (starts when finished)")
        return
    }
 
  
    atomic.StoreInt32(&self.mining, 1)
 
  
    //注册代理对象
    self.worker.register(NewCpuAgent(0, self.pow))
    glog.V(logger.Info).Infof("Starting Accounting operation as a Bookkeeper\n")    
    //启动代理对象
 
  
    self.worker.start()
    //发布work任务,将worker对象中的当前的挖矿任务发布到管道队列中  
 
  
    self.worker.commitNewWork()
    //发送post请求
    self.mux.Post(core.StartBookingEvent{Address: &coinbase})
}


worker.go :

// Agent can register themself with the worker
//声明了代理的接口,agent.go是对Agent的CPU挖矿类型的声明
type Agent interface {
    Work() chan<- *Work //获取work管道只写无缓冲队列
    SetReturnCh(chan<- *Result) //挖矿结果管道队列   类型:只写无缓冲队列
    Stop() //停止挖矿
    Start() //启动挖矿
    GetHashRate() int64 //获取算力
}
 
  
type uint64RingBuffer struct {
    ints []uint64 //array of all integers in buffer
    next int      //where is the next insertion? assert 0 <= next < len(ints)
}
 
  
// environment is the workers current environment and holds
// all of the current state information
type Work struct {
    config             *core.ChainConfig //链的基本配置
    state              *state.StateDB // apply state changes here 挖矿进程状态
    ancestors          *set.Set       // ancestor set (used for checking uncle parent validity)
    family             *set.Set       // family set (used for checking uncle invalidity)
    uncles             *set.Set       // uncle set
    remove             *set.Set       // tx which will be removed
    tcount             int            // tx count in cycle
    ignoredTransactors *set.Set
    lowGasTransactors  *set.Set
    ownedAccounts      *set.Set
    lowGasTxs          types.Transactions
    localMinedBlocks   *uint64RingBuffer // the most recent block numbers that were mined locally 
 
  
    Block *types.Block // the new block
 
  
    header   *types.Header //区块头
    txs      []*types.Transaction //交易信息
    receipts []*types.Receipt
 
  
    createdAt time.Time //创建时间
}
/**
*区块的挖矿结果
*/
type Result struct {
    Work  *Work
    Block *types.Block
}
 
  
// worker is the main object which takes care of applying messages to the new state
type worker struct {
    config *core.ChainConfig //链的基本配置
    mu sync.Mutex
    // update loop
    mux    *event.TypeMux
    events event.Subscription
    wg     sync.WaitGroup
    agents map[Agent]struct{} //代理对象切片
    recv   chan *Result //挖矿结果
    pow    pow.PoW //共识
    eth     core.Backend
    chain   *core.BlockChain
    proc    core.Validator
    chainDb ethdb.Database //区块链数据库
    coinbase common.Address //账户
    gasPrice *big.Int
    extra    []byte
    currentMu sync.Mutex
    current   *Work
    uncleMu        sync.Mutex
    possibleUncles map[common.Hash]*types.Block
    txQueueMu sync.Mutex
    txQueue   map[common.Hash]*types.Transaction //交易队列
    // atomic status counters
    mining int32 
    atWork int32
    fullValidation bool
}
func newWorker(config *core.ChainConfig, coinbase common.Address, eth core.Backend) *worker {
    worker := &worker{
        config:         config,
        eth:            eth,
        mux:            eth.EventMux(),
        chainDb:        eth.ChainDb(),
        recv:           make(chan *Result, resultQueueSize),
        gasPrice:       new(big.Int),
        chain:          eth.BlockChain(),
        proc:           eth.BlockChain().Validator(),
        possibleUncles: make(map[common.Hash]*types.Block),
        coinbase:       coinbase,
        txQueue:        make(map[common.Hash]*types.Transaction),
        agents:         make(map[Agent]struct{}),
        fullValidation: false,
    }
    worker.events = worker.mux.Subscribe(core.ChainHeadEvent{}, core.ChainSideEvent{}, core.TxPreEvent{})
    //启动状态事件监听
    go worker.update()
 
  
    go worker.wait()
    //验证区块信息发布区块挖矿任务
    worker.commitNewWork()
 
  
    return worker
}
 
  
 
  
 
  
//启动所有代理对象,监听work任务
func (self *worker) start() {
    self.mu.Lock()
    defer self.mu.Unlock()
 
  
    atomic.StoreInt32(&self.mining, 1)
 
  
    // spin up agents
    for agent := range self.agents {
        agent.Start()
    }
}
 
  
//停止所有代理挖矿
func (self *worker) stop() {
    self.wg.Wait()
 
  
    self.mu.Lock()
    defer self.mu.Unlock()
    if atomic.LoadInt32(&self.mining) == 1 {
        // Stop all agents.
        for agent := range self.agents {
            agent.Stop()
            // Remove CPU agents.
            if _, ok := agent.(*CpuAgent); ok {
                delete(self.agents, agent)
            }
        }
    }
 
  
    atomic.StoreInt32(&self.mining, 0)
    atomic.StoreInt32(&self.atWork, 0)
}
 
  
//注册代理
func (self *worker) register(agent Agent) {
    self.mu.Lock()
    defer self.mu.Unlock()
    self.agents[agent] = struct{}{}
    agent.SetReturnCh(self.recv)
}
 
  
func (self *worker) unregister(agent Agent) {
    self.mu.Lock()
    defer self.mu.Unlock()
    delete(self.agents, agent)
    agent.Stop()
}
 
  
func (self *worker) update() {
    for event := range self.events.Chan() {
        // A real event arrived, process interesting content
        switch ev := event.Data.(type) {
        case core.ChainHeadEvent:
            self.commitNewWork()
        case core.ChainSideEvent:
            self.uncleMu.Lock()
            self.possibleUncles[ev.Block.Hash()] = ev.Block
            self.uncleMu.Unlock()
        case core.TxPreEvent:
            // Apply transaction to the pending state if we're not mining
            if atomic.LoadInt32(&self.mining) == 0 {
                self.currentMu.Lock()
                self.current.commitTransactions(self.mux, types.Transactions{ev.Tx}, self.gasPrice, self.chain)
                self.currentMu.Unlock()
            }
        }
    }
}
 
  
f

// 发布work任务
func (self *worker) push(work *Work) {
    if atomic.LoadInt32(&self.mining) != 1 {
        return
    }
    for agent := range self.agents {
        atomic.AddInt32(&self.atWork, 1)
        if ch := agent.Work(); ch != nil {
            ch <- work
        }
    }
}



agent.go :

 
  
 
  
//CPU挖矿
 
  
type CpuAgent struct {
    mu sync.Mutex
 
  
    workCh        chan *Work //任务无缓冲队列
    //???channel
    quit          chan struct{} //退出消息队列
    quitCurrentOp chan struct{} //停止当前挖矿动作消息队列
    //??channel
    returnCh      chan<- *Result //挖矿结果消息队列
 
  
    index int
    pow   pow.PoW //工作量证明共识模块
 
  
    isMining int32 // 是否在挖矿状态
}
 
  
func NewCpuAgent(index int, pow pow.PoW) *CpuAgent {
    miner := &CpuAgent{
        pow:   pow,
        index: index,
    }
 
  
    return miner
}
 
  
/**
*以下方法是对Agent接口的声明
*/
func (self *CpuAgent) Work() chan<- *Work            { return self.workCh }
func (self *CpuAgent) Pow() pow.PoW                  { return self.pow }
func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch }
 
  
func (self *CpuAgent) Stop() {
    self.mu.Lock()
    defer self.mu.Unlock()
 
  
    close(self.quit)
}
func (self *CpuAgent) Start() {
    self.mu.Lock()
    defer self.mu.Unlock()
 
  
    if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) {
        return // agent already started
    }
 
  
    self.quit = make(chan struct{})
    // creating current op ch makes sure we're not closing a nil ch
    // later on
    self.workCh = make(chan *Work, 1)
 
  
    go self.update()
}
 
  
/**
*状态更新,for循环随机从两个管道中获取消息,如果有work消息,调用mine方法挖矿,如果有退出消息跳出for循环
*/
func (self *CpuAgent) update() {
out:
    for {
        select {
        case work := <-self.workCh:
            self.mu.Lock()
            if self.quitCurrentOp != nil {
                close(self.quitCurrentOp)
            }
            self.quitCurrentOp = make(chan struct{})
            go self.mine(work, self.quitCurrentOp)
            self.mu.Unlock()
        case <-self.quit:
            self.mu.Lock()
            if self.quitCurrentOp != nil {
                close(self.quitCurrentOp)
                self.quitCurrentOp = nil
            }
            self.mu.Unlock()
            break out
        }
    }
 
  
done:
    // Empty work channel
    //确保work管道为空
    for {
        select {
        case <-self.workCh:
        default:
            close(self.workCh)
            break done
        }
    }
 
  
    atomic.StoreInt32(&self.isMining, 0)
}
/**
*挖矿
*/
func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
    glog.V(logger.Debug).Infof("(re)started agent[%d]. mining...\n", self.index)
 
  
    // 获取随机数,如果stop中含有退出当前操作的消息,结束当前挖矿,挖矿失败
    nonce, mixDigest := self.pow.Search(work.Block, stop, self.index)
    if nonce != 0 {
        //如果随机数!=0即挖矿成功修改区块头的随机数和摘要信息
        block := work.Block.WithMiningResult(nonce, common.BytesToHash(mixDigest))
        //记录挖矿成功结果
        self.returnCh <- &Result{work, block}
    } else {
        self.returnCh <- nil
    }
}
 
  
func (self *CpuAgent) GetHashRate() int64 {
    return self.pow.GetHashrate()
}
 
  

你可能感兴趣的:(blockchain)