github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/miner/worker.go (about) 1 package miner 2 3 import ( 4 "fmt" 5 "math/big" 6 "sync" 7 "sync/atomic" 8 "time" 9 10 "github.com/neatlab/neatio/chain/consensus" 11 ntcTypes "github.com/neatlab/neatio/chain/consensus/neatcon/types" 12 "github.com/neatlab/neatio/chain/core" 13 "github.com/neatlab/neatio/chain/core/state" 14 "github.com/neatlab/neatio/chain/core/types" 15 "github.com/neatlab/neatio/chain/core/vm" 16 "github.com/neatlab/neatio/chain/log" 17 "github.com/neatlab/neatio/params" 18 "github.com/neatlab/neatio/utilities/common" 19 "github.com/neatlab/neatio/utilities/event" 20 "github.com/neatlib/set-go" 21 ) 22 23 const ( 24 resultQueueSize = 10 25 miningLogAtDepth = 5 26 27 txChanSize = 4096 28 29 chainHeadChanSize = 10 30 31 chainSideChanSize = 10 32 ) 33 34 type Agent interface { 35 Work() chan<- *Work 36 SetReturnCh(chan<- *Result) 37 Stop() 38 Start() 39 } 40 41 type Work struct { 42 signer types.Signer 43 44 state *state.StateDB 45 ancestors *set.Set 46 family *set.Set 47 uncles *set.Set 48 tcount int 49 50 Block *types.Block 51 52 header *types.Header 53 txs []*types.Transaction 54 receipts []*types.Receipt 55 56 ops *types.PendingOps 57 58 createdAt time.Time 59 logger log.Logger 60 } 61 62 type Result struct { 63 Work *Work 64 Block *types.Block 65 Intermediate *ntcTypes.IntermediateBlockResult 66 } 67 68 type worker struct { 69 config *params.ChainConfig 70 engine consensus.Engine 71 eth Backend 72 chain *core.BlockChain 73 74 gasFloor uint64 75 gasCeil uint64 76 77 mu sync.Mutex 78 79 mux *event.TypeMux 80 txCh chan core.TxPreEvent 81 txSub event.Subscription 82 chainHeadCh chan core.ChainHeadEvent 83 chainHeadSub event.Subscription 84 chainSideCh chan core.ChainSideEvent 85 chainSideSub event.Subscription 86 wg sync.WaitGroup 87 88 agents map[Agent]struct{} 89 resultCh chan *Result 90 exitCh chan struct{} 91 92 proc core.Validator 93 94 coinbase common.Address 95 extra []byte 96 97 currentMu sync.Mutex 98 current *Work 99 100 uncleMu sync.Mutex 101 possibleUncles map[common.Hash]*types.Block 102 103 unconfirmed *unconfirmedBlocks 104 105 mining int32 106 atWork int32 107 108 logger log.Logger 109 cch core.CrossChainHelper 110 } 111 112 func newWorker(config *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, gasFloor, gasCeil uint64, cch core.CrossChainHelper) *worker { 113 worker := &worker{ 114 config: config, 115 engine: engine, 116 eth: eth, 117 mux: mux, 118 gasFloor: gasFloor, 119 gasCeil: gasCeil, 120 txCh: make(chan core.TxPreEvent, txChanSize), 121 chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), 122 chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), 123 resultCh: make(chan *Result, resultQueueSize), 124 exitCh: make(chan struct{}), 125 chain: eth.BlockChain(), 126 proc: eth.BlockChain().Validator(), 127 possibleUncles: make(map[common.Hash]*types.Block), 128 agents: make(map[Agent]struct{}), 129 unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth, config.ChainLogger), 130 logger: config.ChainLogger, 131 cch: cch, 132 } 133 134 worker.txSub = eth.TxPool().SubscribeTxPreEvent(worker.txCh) 135 136 worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) 137 worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) 138 139 go worker.mainLoop() 140 141 go worker.resultLoop() 142 143 return worker 144 } 145 146 func (self *worker) setCoinbase(addr common.Address) { 147 self.mu.Lock() 148 defer self.mu.Unlock() 149 self.coinbase = addr 150 } 151 152 func (self *worker) setExtra(extra []byte) { 153 self.mu.Lock() 154 defer self.mu.Unlock() 155 self.extra = extra 156 } 157 158 func (self *worker) pending() (*types.Block, *state.StateDB) { 159 self.currentMu.Lock() 160 defer self.currentMu.Unlock() 161 162 if atomic.LoadInt32(&self.mining) == 0 { 163 return types.NewBlock( 164 self.current.header, 165 self.current.txs, 166 nil, 167 self.current.receipts, 168 ), self.current.state.Copy() 169 } 170 return self.current.Block, self.current.state.Copy() 171 } 172 173 func (self *worker) pendingBlock() *types.Block { 174 self.currentMu.Lock() 175 defer self.currentMu.Unlock() 176 177 if atomic.LoadInt32(&self.mining) == 0 { 178 return types.NewBlock( 179 self.current.header, 180 self.current.txs, 181 nil, 182 self.current.receipts, 183 ) 184 } 185 return self.current.Block 186 } 187 188 func (self *worker) start() { 189 self.mu.Lock() 190 defer self.mu.Unlock() 191 192 atomic.StoreInt32(&self.mining, 1) 193 194 if neatcon, ok := self.engine.(consensus.NeatCon); ok { 195 err := neatcon.Start(self.chain, self.chain.CurrentBlock, self.chain.HasBadBlock) 196 if err != nil { 197 self.logger.Error("Starting NeatCon failed", "err", err) 198 } 199 } 200 201 for agent := range self.agents { 202 agent.Start() 203 } 204 } 205 206 func (self *worker) stop() { 207 self.wg.Wait() 208 209 self.mu.Lock() 210 defer self.mu.Unlock() 211 if atomic.LoadInt32(&self.mining) == 1 { 212 for agent := range self.agents { 213 agent.Stop() 214 } 215 } 216 217 if stoppableEngine, ok := self.engine.(consensus.EngineStartStop); ok { 218 engineStopErr := stoppableEngine.Stop() 219 if engineStopErr != nil { 220 self.logger.Error("Stop Engine failed.", "err", engineStopErr) 221 } else { 222 self.logger.Info("Stop Engine Success.") 223 } 224 } 225 226 atomic.StoreInt32(&self.mining, 0) 227 atomic.StoreInt32(&self.atWork, 0) 228 } 229 230 func (self *worker) isRunning() bool { 231 return atomic.LoadInt32(&self.mining) == 1 232 } 233 234 func (self *worker) close() { 235 close(self.exitCh) 236 } 237 238 func (self *worker) register(agent Agent) { 239 self.mu.Lock() 240 defer self.mu.Unlock() 241 self.agents[agent] = struct{}{} 242 agent.SetReturnCh(self.resultCh) 243 } 244 245 func (self *worker) unregister(agent Agent) { 246 self.mu.Lock() 247 defer self.mu.Unlock() 248 delete(self.agents, agent) 249 agent.Stop() 250 } 251 252 func (self *worker) mainLoop() { 253 defer self.txSub.Unsubscribe() 254 defer self.chainHeadSub.Unsubscribe() 255 defer self.chainSideSub.Unsubscribe() 256 257 for { 258 259 select { 260 261 case ev := <-self.chainHeadCh: 262 if h, ok := self.engine.(consensus.Handler); ok { 263 h.NewChainHead(ev.Block) 264 } 265 self.commitNewWork() 266 267 case ev := <-self.chainSideCh: 268 self.uncleMu.Lock() 269 self.possibleUncles[ev.Block.Hash()] = ev.Block 270 self.uncleMu.Unlock() 271 272 case ev := <-self.txCh: 273 274 if !self.isRunning() && self.current != nil { 275 self.currentMu.Lock() 276 acc, _ := types.Sender(self.current.signer, ev.Tx) 277 txs := map[common.Address]types.Transactions{acc: {ev.Tx}} 278 txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs) 279 280 self.commitTransactionsEx(txset, self.coinbase, big.NewInt(0), self.cch) 281 self.currentMu.Unlock() 282 } 283 284 case <-self.exitCh: 285 return 286 case <-self.txSub.Err(): 287 return 288 case <-self.chainHeadSub.Err(): 289 return 290 case <-self.chainSideSub.Err(): 291 return 292 } 293 } 294 } 295 296 func (self *worker) resultLoop() { 297 for { 298 mustCommitNewWork := true 299 select { 300 case result := <-self.resultCh: 301 atomic.AddInt32(&self.atWork, -1) 302 303 if result == nil { 304 continue 305 } 306 307 var block *types.Block 308 var receipts types.Receipts 309 var state *state.StateDB 310 var ops *types.PendingOps 311 312 if result.Work != nil { 313 block = result.Block 314 hash := block.Hash() 315 work := result.Work 316 317 for i, receipt := range work.receipts { 318 319 receipt.BlockHash = hash 320 receipt.BlockNumber = block.Number() 321 receipt.TransactionIndex = uint(i) 322 323 for _, l := range receipt.Logs { 324 l.BlockHash = hash 325 } 326 } 327 for _, log := range work.state.Logs() { 328 log.BlockHash = hash 329 } 330 receipts = work.receipts 331 state = work.state 332 ops = work.ops 333 } else if result.Intermediate != nil { 334 block = result.Intermediate.Block 335 336 for i, receipt := range result.Intermediate.Receipts { 337 338 receipt.BlockHash = block.Hash() 339 receipt.BlockNumber = block.Number() 340 receipt.TransactionIndex = uint(i) 341 } 342 343 receipts = result.Intermediate.Receipts 344 state = result.Intermediate.State 345 ops = result.Intermediate.Ops 346 } else { 347 continue 348 } 349 350 self.chain.MuLock() 351 352 stat, err := self.chain.WriteBlockWithState(block, receipts, state) 353 if err != nil { 354 self.logger.Error("Failed writing block to chain", "err", err) 355 self.chain.MuUnLock() 356 continue 357 } 358 359 for _, op := range ops.Ops() { 360 if err := core.ApplyOp(op, self.chain, self.cch); err != nil { 361 log.Error("Failed executing op", op, "err", err) 362 } 363 } 364 365 if stat == core.CanonStatTy { 366 367 mustCommitNewWork = false 368 } 369 370 self.mux.Post(core.NewMinedBlockEvent{Block: block}) 371 var ( 372 events []interface{} 373 logs = state.Logs() 374 ) 375 events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) 376 if stat == core.CanonStatTy { 377 events = append(events, core.ChainHeadEvent{Block: block}) 378 } 379 380 self.chain.MuUnLock() 381 382 self.chain.PostChainEvents(events, logs) 383 384 self.unconfirmed.Insert(block.NumberU64(), block.Hash()) 385 386 if mustCommitNewWork { 387 self.commitNewWork() 388 } 389 case <-self.exitCh: 390 return 391 } 392 } 393 } 394 395 func (self *worker) push(work *Work) { 396 if atomic.LoadInt32(&self.mining) != 1 { 397 return 398 } 399 for agent := range self.agents { 400 atomic.AddInt32(&self.atWork, 1) 401 if ch := agent.Work(); ch != nil { 402 ch <- work 403 } 404 } 405 } 406 407 func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { 408 state, err := self.chain.StateAt(parent.Root()) 409 if err != nil { 410 return err 411 } 412 work := &Work{ 413 signer: types.NewEIP155Signer(self.config.ChainId), 414 state: state, 415 ancestors: set.New(), 416 family: set.New(), 417 uncles: set.New(), 418 header: header, 419 ops: new(types.PendingOps), 420 createdAt: time.Now(), 421 logger: self.logger, 422 } 423 424 for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { 425 for _, uncle := range ancestor.Uncles() { 426 work.family.Add(uncle.Hash()) 427 } 428 work.family.Add(ancestor.Hash()) 429 work.ancestors.Add(ancestor.Hash()) 430 } 431 432 work.tcount = 0 433 self.current = work 434 return nil 435 } 436 437 func (self *worker) commitNewWork() { 438 self.mu.Lock() 439 defer self.mu.Unlock() 440 self.uncleMu.Lock() 441 defer self.uncleMu.Unlock() 442 self.currentMu.Lock() 443 defer self.currentMu.Unlock() 444 445 tstart := time.Now() 446 parent := self.chain.CurrentBlock() 447 448 tstamp := tstart.Unix() 449 if parent.Time() >= uint64(tstamp) { 450 tstamp = int64(parent.Time() + 1) 451 } 452 453 if now := time.Now().Unix(); tstamp > now+1 { 454 wait := time.Duration(tstamp-now) * time.Second 455 self.logger.Info("Mining too far in the future", "suppose but not wait", common.PrettyDuration(wait)) 456 457 } 458 459 num := parent.Number() 460 header := &types.Header{ 461 ParentHash: parent.Hash(), 462 Number: num.Add(num, common.Big1), 463 GasLimit: core.CalcGasLimit(parent, self.gasFloor, self.gasCeil), 464 Extra: self.extra, 465 Time: big.NewInt(tstamp), 466 } 467 468 if self.isRunning() { 469 if self.coinbase == (common.Address{}) { 470 log.Error("Refusing to mine without coinbase") 471 return 472 } 473 header.Coinbase = self.coinbase 474 } 475 if err := self.engine.Prepare(self.chain, header); err != nil { 476 self.logger.Error("Failed to prepare header for mining", "err", err) 477 return 478 } 479 480 err := self.makeCurrent(parent, header) 481 if err != nil { 482 self.logger.Error("Failed to create mining context", "err", err) 483 return 484 } 485 486 work := self.current 487 488 pending, err := self.eth.TxPool().Pending() 489 if err != nil { 490 self.logger.Error("Failed to fetch pending transactions", "err", err) 491 return 492 } 493 494 totalUsedMoney := big.NewInt(0) 495 txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending) 496 497 rmTxs := self.commitTransactionsEx(txs, self.coinbase, totalUsedMoney, self.cch) 498 499 if len(rmTxs) > 0 { 500 self.eth.TxPool().RemoveTxs(rmTxs) 501 } 502 503 var ( 504 uncles []*types.Header 505 badUncles []common.Hash 506 ) 507 for hash, uncle := range self.possibleUncles { 508 if len(uncles) == 2 { 509 break 510 } 511 if err := self.commitUncle(work, uncle.Header()); err != nil { 512 self.logger.Trace("Bad uncle found and will be removed", "hash", hash) 513 self.logger.Trace(fmt.Sprint(uncle)) 514 515 badUncles = append(badUncles, hash) 516 } else { 517 self.logger.Debug("Committing new uncle to block", "hash", hash) 518 uncles = append(uncles, uncle.Header()) 519 } 520 } 521 for _, hash := range badUncles { 522 delete(self.possibleUncles, hash) 523 } 524 525 if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, totalUsedMoney, uncles, work.receipts, work.ops); err != nil { 526 self.logger.Error("Failed to finalize block for sealing", "err", err) 527 return 528 } 529 530 if self.isRunning() { 531 532 self.unconfirmed.Shift(work.Block.NumberU64() - 1) 533 } 534 self.push(work) 535 } 536 537 func (self *worker) commitUncle(work *Work, uncle *types.Header) error { 538 hash := uncle.Hash() 539 if work.uncles.Has(hash) { 540 return fmt.Errorf("uncle not unique") 541 } 542 if !work.ancestors.Has(uncle.ParentHash) { 543 return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4]) 544 } 545 if work.family.Has(hash) { 546 return fmt.Errorf("uncle already in family (%x)", hash) 547 } 548 work.uncles.Add(uncle.Hash()) 549 return nil 550 } 551 552 func (self *worker) commitTransactionsEx(txs *types.TransactionsByPriceAndNonce, coinbase common.Address, totalUsedMoney *big.Int, cch core.CrossChainHelper) (rmTxs types.Transactions) { 553 554 gp := new(core.GasPool).AddGas(self.current.header.GasLimit) 555 556 var coalescedLogs []*types.Log 557 558 for { 559 560 if gp.Gas() < params.TxGas { 561 self.logger.Trace("Not enough gas for further transactions", "have", gp, "want", params.TxGas) 562 break 563 } 564 565 tx := txs.Peek() 566 if tx == nil { 567 break 568 } 569 570 from, _ := types.Sender(self.current.signer, tx) 571 572 if tx.Protected() && !self.config.IsEIP155(self.current.header.Number) { 573 self.logger.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", self.config.EIP155Block) 574 575 txs.Pop() 576 continue 577 } 578 579 self.current.state.Prepare(tx.Hash(), common.Hash{}, self.current.tcount) 580 581 logs, err := self.commitTransactionEx(tx, coinbase, gp, totalUsedMoney, cch) 582 switch err { 583 case core.ErrGasLimitReached: 584 585 self.logger.Trace("Gas limit exceeded for current block", "sender", from) 586 txs.Pop() 587 588 case core.ErrNonceTooLow: 589 590 self.logger.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 591 txs.Shift() 592 593 case core.ErrNonceTooHigh: 594 595 self.logger.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) 596 txs.Pop() 597 598 case core.ErrInvalidTx4: 599 600 rmTxs = append(rmTxs, tx) 601 self.logger.Trace("Invalid Tx4, this tx will be removed", "hash", tx.Hash()) 602 txs.Shift() 603 604 case nil: 605 606 coalescedLogs = append(coalescedLogs, logs...) 607 self.current.tcount++ 608 txs.Shift() 609 610 default: 611 612 self.logger.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 613 txs.Shift() 614 } 615 } 616 617 if len(coalescedLogs) > 0 || self.current.tcount > 0 { 618 619 cpy := make([]*types.Log, len(coalescedLogs)) 620 for i, l := range coalescedLogs { 621 cpy[i] = new(types.Log) 622 *cpy[i] = *l 623 } 624 go func(logs []*types.Log, tcount int) { 625 if len(logs) > 0 { 626 self.mux.Post(core.PendingLogsEvent{Logs: logs}) 627 } 628 if tcount > 0 { 629 self.mux.Post(core.PendingStateEvent{}) 630 } 631 }(cpy, self.current.tcount) 632 } 633 return 634 } 635 636 func (self *worker) commitTransactionEx(tx *types.Transaction, coinbase common.Address, gp *core.GasPool, totalUsedMoney *big.Int, cch core.CrossChainHelper) ([]*types.Log, error) { 637 snap := self.current.state.Snapshot() 638 639 receipt, err := core.ApplyTransactionEx(self.config, self.chain, nil, gp, self.current.state, self.current.ops, self.current.header, tx, &self.current.header.GasUsed, totalUsedMoney, vm.Config{}, cch, true) 640 if err != nil { 641 self.current.state.RevertToSnapshot(snap) 642 return nil, err 643 } 644 645 self.current.txs = append(self.current.txs, tx) 646 self.current.receipts = append(self.current.receipts, receipt) 647 648 return receipt.Logs, nil 649 }