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 }