github.com/sixexorg/magnetic-ring@v0.0.0-20191119090307-31705a21e419/miner/worker.go (about)

     1  package miner
     2  
     3  import (
     4  	"sync"
     5  	"sync/atomic"
     6  	"time"
     7  
     8  	"fmt"
     9  
    10  	"github.com/sixexorg/magnetic-ring/common"
    11  	"github.com/sixexorg/magnetic-ring/config"
    12  	"github.com/sixexorg/magnetic-ring/consense/poa"
    13  	"github.com/sixexorg/magnetic-ring/core/orgchain/types"
    14  	"github.com/sixexorg/magnetic-ring/log"
    15  	"github.com/sixexorg/magnetic-ring/store/orgchain/storages"
    16  	"github.com/sixexorg/magnetic-ring/store/storelaw"
    17  	common2 "github.com/sixexorg/magnetic-ring/txpool/orgchain"
    18  )
    19  
    20  const (
    21  	resultQueueSize  = 10
    22  	miningLogAtDepth = 5
    23  
    24  	// txChanSize is the size of channel listening to TxPreEvent.
    25  	// The number is referenced from the size of tx pool.
    26  	txChanSize = 4096
    27  	// chainHeadChanSize is the size of channel listening to ChainHeadEvent.
    28  	chainHeadChanSize = 10
    29  	// chainSideChanSize is the size of channel listening to ChainSideEvent.
    30  	chainSideChanSize = 10
    31  )
    32  
    33  // Agent can register themself with the worker
    34  type Agent interface {
    35  	Work() chan<- *Work
    36  	SetReturnCh(chan<- *Result)
    37  	Stop()
    38  	Start()
    39  	//GetHashRate() int64
    40  }
    41  
    42  // Work is the workers current environment and holds
    43  // all of the current state information
    44  type Work struct {
    45  	config    *config.CliqueConfig
    46  	orgname   common.Hash
    47  	tcount    int          // tx count in cycle
    48  	Block     *types.Block // the new block
    49  	header    *types.Header
    50  	txs       []*types.Transaction
    51  	receipts  []*types.Receipt
    52  	storedata *storelaw.OrgBlockInfo
    53  
    54  	createdAt time.Time
    55  }
    56  
    57  type Result struct {
    58  	Work  *Work
    59  	Block *types.Block
    60  }
    61  
    62  type worker struct {
    63  	config    *config.CliqueConfig
    64  	agent     Agent
    65  	recv      chan *Result
    66  	coinbase  common.Address
    67  	extra     []byte
    68  	ledger    *storages.LedgerStoreImp
    69  	currentMu sync.Mutex
    70  	current   *Work
    71  	poaIns    *poa.Clique
    72  	txpool    *common2.SubPool
    73  	mining    int32
    74  	atWork    int32
    75  	orgname   common.Address
    76  
    77  	mu sync.Mutex
    78  	wg sync.WaitGroup
    79  }
    80  
    81  func newWorker(config *config.CliqueConfig, coinbase common.Address, ledger *storages.LedgerStoreImp, orgname common.Address, poains *poa.Clique, txpool *common2.SubPool) *worker {
    82  	worker := &worker{
    83  		config:   config,
    84  		recv:     make(chan *Result, resultQueueSize),
    85  		ledger:   ledger,
    86  		coinbase: coinbase,
    87  		orgname:  orgname,
    88  		poaIns:   poains,
    89  		txpool:   txpool,
    90  	}
    91  
    92  	//go worker.update()
    93  	go worker.wait()
    94  	//worker.commitNewWork()
    95  
    96  	return worker
    97  }
    98  
    99  func (self *worker) setEtherbase(addr common.Address) {
   100  	self.mu.Lock()
   101  	defer self.mu.Unlock()
   102  	self.coinbase = addr
   103  }
   104  
   105  func (self *worker) setExtra(extra []byte) {
   106  	self.mu.Lock()
   107  	defer self.mu.Unlock()
   108  	self.extra = extra
   109  }
   110  
   111  func (self *worker) start() {
   112  	self.mu.Lock()
   113  	defer self.mu.Unlock()
   114  
   115  	atomic.StoreInt32(&self.mining, 1)
   116  
   117  	self.agent.Start()
   118  }
   119  
   120  func (self *worker) stop() {
   121  	self.wg.Wait()
   122  
   123  	self.mu.Lock()
   124  	defer self.mu.Unlock()
   125  	if atomic.LoadInt32(&self.mining) == 1 {
   126  		self.agent.Stop()
   127  	}
   128  	atomic.StoreInt32(&self.mining, 0)
   129  	atomic.StoreInt32(&self.atWork, 0)
   130  }
   131  
   132  func (self *worker) register(agent Agent) {
   133  	self.mu.Lock()
   134  	defer self.mu.Unlock()
   135  	self.agent = agent
   136  	agent.SetReturnCh(self.recv)
   137  }
   138  
   139  func (self *worker) unregister() {
   140  	self.mu.Lock()
   141  	defer self.mu.Unlock()
   142  	self.agent.Stop()
   143  }
   144  
   145  func (self *worker) wait() {
   146  	for {
   147  		for result := range self.recv {
   148  			atomic.AddInt32(&self.atWork, -1)
   149  			if result == nil {
   150  				time.Sleep(time.Second * 5)
   151  				self.commitNewWork()
   152  				continue
   153  			}
   154  			fmt.Println("~~~~~~~~~~øøøøøøøøøsave block", time.Unix(0, int64(result.Work.storedata.Block.Header.Timestamp)).Format("15:04:05.000"),
   155  				result.Work.storedata.Block.Header.Height, result.Work.storedata.Block.Header.Coinbase.ToString())
   156  			result.Work.storedata.Block = result.Block
   157  			err := self.ledger.SaveAll(result.Work.storedata)
   158  			fmt.Println("øøøøøøøøøsave block", result.Work.storedata.Block.Header.Height, result.Work.storedata.Block.Header.Coinbase.ToString(), err)
   159  			self.txpool.RefreshValidator(self.ledger, self.orgname)
   160  
   161  			if result.Work.storedata.Block.Header.Height == self.ledger.GetCurrentBlockHeight() {
   162  				time.Sleep(time.Millisecond * 50)
   163  			}
   164  			self.commitNewWork()
   165  		}
   166  	}
   167  }
   168  
   169  // push sends a new work task to currently live miner agents.
   170  func (self *worker) push(work *Work) {
   171  	if atomic.LoadInt32(&self.mining) != 1 {
   172  		return
   173  	}
   174  	atomic.AddInt32(&self.atWork, 1)
   175  	if ch := self.agent.Work(); ch != nil {
   176  		ch <- work
   177  	} else {
   178  		log.Info("worker push no work", "work", work)
   179  	}
   180  }
   181  
   182  // makeCurrent creates a new environment for the current cycle.
   183  func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error {
   184  	work := &Work{
   185  		config:    self.config,
   186  		header:    header,
   187  		createdAt: time.Now(),
   188  	}
   189  
   190  	work.tcount = 0
   191  	self.current = work
   192  	return nil
   193  }
   194  
   195  func (self *worker) commitNewWork() {
   196  	self.mu.Lock()
   197  	defer self.mu.Unlock()
   198  	self.currentMu.Lock()
   199  	defer self.currentMu.Unlock()
   200  
   201  	tstart := time.Now()
   202  	h, _ := self.ledger.GetCurrentBlock()
   203  	parent, _ := self.ledger.GetBlockByHeight(h)
   204  	//tstamp := uint64(tstart.Unix())
   205  	//if parent.Header.Timestamp >= tstamp {
   206  	//	tstamp = parent.Header.Timestamp + 1
   207  	//}
   208  	// this will ensure we're not going off too far in the future
   209  	//if now := uint64(time.Now().Unix()); tstamp > now+1 {
   210  	//	wait := time.Duration(tstamp-now) * 100 * time.Millisecond//wait := time.Duration(tstamp-now) * time.Second
   211  	//	log.Info("Mining too far in the future", "wait", wait)
   212  	//	time.Sleep(wait)
   213  	//}
   214  	//self.txpool.RefreshValidator(self.ledger, self.orgname)
   215  	blkInfo := self.txpool.Execute()
   216  	blkInfo.Block.Header.Coinbase = self.coinbase
   217  	blkInfo.Block.Header.Timestamp = parent.Header.Timestamp
   218  	blkInfo.Block.Header.Timestamp = uint64(time.Unix(0, int64(blkInfo.Block.Header.Timestamp)).Add(time.Millisecond * 500).UnixNano())
   219  	if time.Now().After(time.Unix(0, int64(blkInfo.Block.Header.Timestamp))) {
   220  		blkInfo.Block.Header.Timestamp = uint64(time.Now().UnixNano())
   221  	}
   222  	if err := self.poaIns.Prepare(blkInfo.Block.Header); err != nil {
   223  		log.Error("Failed to prepare header for mining", "err", err)
   224  		return
   225  	}
   226  
   227  	err := self.makeCurrent(parent, blkInfo.Block.Header)
   228  	if err != nil {
   229  		log.Error("Failed to create mining context", "err", err)
   230  		return
   231  	}
   232  
   233  	work := self.current
   234  	work.storedata = blkInfo
   235  	if work.Block, err = self.poaIns.Finalize(blkInfo); err != nil {
   236  		log.Error("Failed to finalize block for sealing", "err", err)
   237  		return
   238  	}
   239  	//self.txpool.RefreshValidator(self.ledger, self.orgname)
   240  	// We only care about logging if we're actually mining.
   241  	if atomic.LoadInt32(&self.mining) == 1 {
   242  		log.Info("Commit new mining work", "number", work.Block.Header.Height, "txs", work.tcount, "uncles", 0, "elapsed", time.Since(tstart))
   243  	}
   244  	self.push(work)
   245  }