github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/miner/agent.go (about)

     1  // Copyright 2015 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The Spectrum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package miner
    18  
    19  import (
    20  	"sync"
    21  
    22  	"sync/atomic"
    23  
    24  	"fmt"
    25  
    26  	"github.com/SmartMeshFoundation/Spectrum/consensus"
    27  	"github.com/SmartMeshFoundation/Spectrum/log"
    28  )
    29  
    30  type CpuAgent struct {
    31  	mu sync.Mutex
    32  
    33  	workCh        chan *Work
    34  	stop          chan struct{}
    35  	quitCurrentOp chan struct{}
    36  	returnCh      chan<- *Result
    37  
    38  	chain  consensus.ChainReader
    39  	engine consensus.Engine
    40  
    41  	isMining int32 // isMining indicates whether the agent is currently mining
    42  }
    43  
    44  func NewCpuAgent(chain consensus.ChainReader, engine consensus.Engine) *CpuAgent {
    45  	miner := &CpuAgent{
    46  		chain:  chain,
    47  		engine: engine,
    48  		stop:   make(chan struct{}, 1),
    49  		workCh: make(chan *Work, 1),
    50  	}
    51  	return miner
    52  }
    53  
    54  func (self *CpuAgent) Work() chan<- *Work            { return self.workCh }
    55  func (self *CpuAgent) SetReturnCh(ch chan<- *Result) { self.returnCh = ch }
    56  
    57  func (self *CpuAgent) Stop() {
    58  	log.Info("call cpu agent stop")
    59  	if !atomic.CompareAndSwapInt32(&self.isMining, 1, 0) {
    60  		return // agent already stopped
    61  	}
    62  	//log.Info("call cpu agent stop2")
    63  	self.stop <- struct{}{}
    64  done:
    65  	// Empty work channel
    66  	for {
    67  		select {
    68  		case <-self.workCh:
    69  		default:
    70  			break done
    71  		}
    72  	}
    73  }
    74  
    75  func (self *CpuAgent) Start() {
    76  	if !atomic.CompareAndSwapInt32(&self.isMining, 0, 1) {
    77  		return // agent already started
    78  	}
    79  	go self.update()
    80  }
    81  
    82  func (self *CpuAgent) update() {
    83  out:
    84  	for {
    85  		select {
    86  		case work := <-self.workCh:
    87  			self.mu.Lock()
    88  			if self.quitCurrentOp != nil {
    89  				close(self.quitCurrentOp)
    90  			}
    91  			self.quitCurrentOp = make(chan struct{})
    92  			go self.mine(work, self.quitCurrentOp)
    93  			self.mu.Unlock()
    94  		case <-self.stop:
    95  			self.mu.Lock()
    96  			if self.quitCurrentOp != nil {
    97  				close(self.quitCurrentOp)
    98  				self.quitCurrentOp = nil
    99  			}
   100  			self.mu.Unlock()
   101  			break out
   102  		}
   103  	}
   104  }
   105  
   106  func (self *CpuAgent) mine(work *Work, stop <-chan struct{}) {
   107  	result, err := self.engine.Seal(self.chain, work.Block, stop)
   108  	if result != nil {
   109  		ch := self.chain.CurrentHeader()
   110  		if result.Number().Cmp(ch.Number) > 0 {
   111  			log.Warn(fmt.Sprintf("😄 [%d] == done ==> num=%d, diff=%d, miner=%s", ch.Number, result.Number(), result.Header().Difficulty, result.Header().Coinbase.Hex()))
   112  			self.returnCh <- &Result{work, result}
   113  		} else {
   114  			log.Warn(fmt.Sprintf("👿 [%d] == discard ==> num=%d, diff=%d, miner=%s", ch.Number, result.Number(), result.Header().Difficulty, result.Header().Coinbase.Hex()))
   115  			self.returnCh <- nil
   116  		}
   117  	} else {
   118  		if err != nil {
   119  			log.Debug("❌ Block sealing failed", "err", err)
   120  		}
   121  		self.returnCh <- nil
   122  	}
   123  }
   124  
   125  func (self *CpuAgent) GetHashRate() int64 {
   126  	if pow, ok := self.engine.(consensus.PoW); ok {
   127  		return int64(pow.Hashrate())
   128  	}
   129  	return 0
   130  }