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

     1  // Copyright 2014 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 implements Ethereum block creation and mining.
    18  package miner
    19  
    20  import (
    21  	"fmt"
    22  	"sync"
    23  	"sync/atomic"
    24  
    25  	"time"
    26  
    27  	"github.com/SmartMeshFoundation/Spectrum/accounts"
    28  	"github.com/SmartMeshFoundation/Spectrum/common"
    29  	"github.com/SmartMeshFoundation/Spectrum/consensus"
    30  	"github.com/SmartMeshFoundation/Spectrum/consensus/tribe"
    31  	"github.com/SmartMeshFoundation/Spectrum/core"
    32  	"github.com/SmartMeshFoundation/Spectrum/core/state"
    33  	"github.com/SmartMeshFoundation/Spectrum/core/types"
    34  	"github.com/SmartMeshFoundation/Spectrum/eth/downloader"
    35  	"github.com/SmartMeshFoundation/Spectrum/ethdb"
    36  	"github.com/SmartMeshFoundation/Spectrum/event"
    37  	"github.com/SmartMeshFoundation/Spectrum/log"
    38  	"github.com/SmartMeshFoundation/Spectrum/params"
    39  )
    40  
    41  // Backend wraps all methods required for mining.
    42  type Backend interface {
    43  	AccountManager() *accounts.Manager
    44  	BlockChain() *core.BlockChain
    45  	TxPool() *core.TxPool
    46  	ChainDb() ethdb.Database
    47  }
    48  
    49  // Miner creates blocks and searches for proof-of-work values.
    50  type Miner struct {
    51  	stop *sync.Map
    52  	mux  *event.TypeMux
    53  
    54  	worker *worker
    55  
    56  	coinbase common.Address
    57  	mining   int32
    58  	eth      Backend
    59  	engine   consensus.Engine
    60  
    61  	canStart    int32 // can start indicates whether we can start the mining operation
    62  	shouldStart int32 // should start indicates whether we should start after sync
    63  }
    64  
    65  func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner {
    66  	miner := &Miner{
    67  		eth:      eth,
    68  		mux:      mux,
    69  		engine:   engine,
    70  		worker:   newWorker(config, engine, common.Address{}, eth, mux),
    71  		canStart: 1,
    72  		stop:     new(sync.Map),
    73  	}
    74  	miner.Register(NewCpuAgent(eth.BlockChain(), engine))
    75  	go miner.update()
    76  	if tribe, ok := miner.engine.(*tribe.Tribe); ok {
    77  		close(params.TribeReadyForAcceptTxs)
    78  		go func() {
    79  			log.Info("miner wait miner address")
    80  			rtn := make(chan common.Address)
    81  			tribe.Status.GetMinerAddressByChan(rtn)
    82  			tma := <-rtn
    83  			log.Info("miner get miner address")
    84  			go miner.Start(tma)
    85  			log.Info("👷 Tribe and miner is started .")
    86  		}()
    87  	}
    88  	return miner
    89  }
    90  
    91  // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
    92  // It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and
    93  // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
    94  // and halt your mining operation for as long as the DOS continues.
    95  func (self *Miner) update() {
    96  	events := self.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
    97  out:
    98  	for ev := range events.Chan() {
    99  		switch ev.Data.(type) {
   100  		case downloader.StartEvent:
   101  			atomic.StoreInt32(&self.canStart, 0)
   102  			if self.Mining() {
   103  				self.Stop()
   104  				atomic.StoreInt32(&self.shouldStart, 1)
   105  				log.Info("Mining aborted due to sync")
   106  			}
   107  		case downloader.DoneEvent, downloader.FailedEvent:
   108  			shouldStart := atomic.LoadInt32(&self.shouldStart) == 1
   109  
   110  			atomic.StoreInt32(&self.canStart, 1)
   111  			atomic.StoreInt32(&self.shouldStart, 0)
   112  			if shouldStart {
   113  				log.Info("miner start after sync complete")
   114  				go self.Start(self.coinbase)
   115  			}
   116  			// unsubscribe. we're only interested in this event once
   117  			events.Unsubscribe()
   118  			// stop immediately and ignore all further pending events
   119  			break out
   120  		}
   121  	}
   122  }
   123  
   124  // liangc : The caller will prevents multiple execution.
   125  // async run
   126  var xx int32 = 0
   127  
   128  func (self *Miner) Start(coinbase common.Address) {
   129  	stop := make(chan struct{})
   130  	self.stop.Store(stop, struct{}{})
   131  	atomic.AddInt32(&xx, 1)
   132  	atomic.StoreInt32(&self.shouldStart, 1)
   133  
   134  	self.worker.setEtherbase(coinbase)
   135  	self.coinbase = coinbase
   136  	if atomic.LoadInt32(&self.canStart) == 0 {
   137  		log.Info("Network syncing, will start miner afterwards")
   138  		return
   139  	}
   140  	atomic.StoreInt32(&self.mining, 1)
   141  	log.Info("Starting mining operation")
   142  
   143  	if tribe, ok := self.engine.(*tribe.Tribe); ok && self.eth.BlockChain().CurrentBlock().NumberU64() > 3 {
   144  		i := 0
   145  		for {
   146  			log.Debug("<<MinerStart>> loop_start", "i", i, "num", self.eth.BlockChain().CurrentBlock().Number())
   147  			m := tribe.Status.GetMinerAddress()
   148  			s, err := self.worker.chain.State()
   149  			if err != nil {
   150  				log.Error("miner start fail", err)
   151  			}
   152  			cn := self.eth.BlockChain().CurrentBlock().Number()
   153  			// SIP100 skip this verfiy
   154  			if params.IsSIP100Block(cn) {
   155  				break
   156  			}
   157  			if params.IsReadyMeshbox(cn) {
   158  				if params.MeshboxExistAddress(m) {
   159  					break
   160  				}
   161  			}
   162  			if s.GetBalance(m).Cmp(params.ChiefBaseBalance) >= 0 {
   163  				break
   164  			}
   165  			if atomic.LoadInt32(&self.mining) == 0 {
   166  				return
   167  			}
   168  			select {
   169  			case <-stop:
   170  				return
   171  			default:
   172  				<-time.After(time.Second * 7)
   173  			}
   174  			i++
   175  		}
   176  	}
   177  	// may be pending at 'tribe.WaitingNomination' in 'worker.start' so change to async
   178  	go func() {
   179  		s := make(chan int)
   180  		self.worker.start(s)
   181  		select {
   182  		case <-stop:
   183  			return
   184  		case <-s:
   185  			self.worker.commitNewWork()
   186  		}
   187  	}()
   188  
   189  }
   190  
   191  func (self *Miner) dostop() {
   192  	self.stop.Range(func(k, v interface{}) bool {
   193  		defer func() { recover() }()
   194  		defer close(k.(chan struct{}))
   195  		self.stop.Delete(k)
   196  		return true
   197  	})
   198  }
   199  
   200  func (self *Miner) Stop() {
   201  	self.dostop()
   202  	self.worker.stop()
   203  	atomic.StoreInt32(&self.mining, 0)
   204  	atomic.StoreInt32(&self.shouldStart, 0)
   205  }
   206  
   207  func (self *Miner) Register(agent Agent) {
   208  	if self.Mining() {
   209  		agent.Start()
   210  	}
   211  	self.worker.register(agent)
   212  }
   213  
   214  func (self *Miner) Unregister(agent Agent) {
   215  	self.worker.unregister(agent)
   216  }
   217  
   218  func (self *Miner) Mining() bool {
   219  	return atomic.LoadInt32(&self.mining) > 0
   220  }
   221  
   222  func (self *Miner) HashRate() (tot int64) {
   223  	if pow, ok := self.engine.(consensus.PoW); ok {
   224  		tot += int64(pow.Hashrate())
   225  	}
   226  	// do we care this might race? is it worth we're rewriting some
   227  	// aspects of the worker/locking up agents so we can get an accurate
   228  	// hashrate?
   229  	for agent := range self.worker.agents {
   230  		if _, ok := agent.(*CpuAgent); !ok {
   231  			tot += agent.GetHashRate()
   232  		}
   233  	}
   234  	return
   235  }
   236  
   237  func (self *Miner) SetExtra(extra []byte) error {
   238  	if uint64(len(extra)) > params.MaximumExtraDataSize {
   239  		return fmt.Errorf("Extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize)
   240  	}
   241  	self.worker.setExtra(extra)
   242  	return nil
   243  }
   244  
   245  // Pending returns the currently pending block and associated state.
   246  func (self *Miner) Pending() (*types.Block, *state.StateDB) {
   247  	return self.worker.pending()
   248  }
   249  
   250  // PendingBlock returns the currently pending block.
   251  //
   252  // Note, to access both the pending block and the pending state
   253  // simultaneously, please use Pending(), as the pending state can
   254  // change between multiple method calls
   255  func (self *Miner) PendingBlock() *types.Block {
   256  	return self.worker.pendingBlock()
   257  }
   258  
   259  func (self *Miner) SetEtherbase(addr common.Address) {
   260  	self.coinbase = addr
   261  	self.worker.setEtherbase(addr)
   262  }