github.com/nnlgsakib/mind-dpos@v0.0.0-20230606105614-f3c8ca06f808/miner/worker.go (about) 1 // Copyright 2015 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum 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 go-ethereum 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package miner 18 19 import ( 20 "fmt" 21 "math/big" 22 "sync" 23 "sync/atomic" 24 "time" 25 26 "github.com/TTCECO/gttc/common" 27 "github.com/TTCECO/gttc/consensus" 28 "github.com/TTCECO/gttc/core" 29 "github.com/TTCECO/gttc/core/state" 30 "github.com/TTCECO/gttc/core/types" 31 "github.com/TTCECO/gttc/core/vm" 32 "github.com/TTCECO/gttc/ethdb" 33 "github.com/TTCECO/gttc/event" 34 "github.com/TTCECO/gttc/log" 35 "github.com/TTCECO/gttc/params" 36 "gopkg.in/fatih/set.v0" 37 ) 38 39 const ( 40 resultQueueSize = 10 41 miningLogAtDepth = 5 42 43 // txChanSize is the size of channel listening to NewTxsEvent. 44 // The number is referenced from the size of tx pool. 45 txChanSize = 4096 46 // chainHeadChanSize is the size of channel listening to ChainHeadEvent. 47 chainHeadChanSize = 10 48 // chainSideChanSize is the size of channel listening to ChainSideEvent. 49 chainSideChanSize = 10 50 ) 51 52 // Agent can register themself with the worker 53 type Agent interface { 54 Work() chan<- *Work 55 SetReturnCh(chan<- *Result) 56 Stop() 57 Start() 58 GetHashRate() int64 59 } 60 61 // Work is the workers current environment and holds 62 // all of the current state information 63 type Work struct { 64 config *params.ChainConfig 65 signer types.Signer 66 67 state *state.StateDB // apply state changes here 68 ancestors *set.Set // ancestor set (used for checking uncle parent validity) 69 family *set.Set // family set (used for checking uncle invalidity) 70 uncles *set.Set // uncle set 71 tcount int // tx count in cycle 72 gasPool *core.GasPool // available gas used to pack transactions 73 74 Block *types.Block // the new block 75 76 header *types.Header 77 txs []*types.Transaction 78 receipts []*types.Receipt 79 80 createdAt time.Time 81 } 82 83 type Result struct { 84 Work *Work 85 Block *types.Block 86 } 87 88 // worker is the main object which takes care of applying messages to the new state 89 type worker struct { 90 config *params.ChainConfig 91 engine consensus.Engine 92 93 mu sync.Mutex 94 95 // update loop 96 mux *event.TypeMux 97 txsCh chan core.NewTxsEvent 98 txsSub event.Subscription 99 chainHeadCh chan core.ChainHeadEvent 100 chainHeadSub event.Subscription 101 chainSideCh chan core.ChainSideEvent 102 chainSideSub event.Subscription 103 wg sync.WaitGroup 104 105 agents map[Agent]struct{} 106 recv chan *Result 107 108 eth Backend 109 chain *core.BlockChain 110 proc core.Validator 111 chainDb ethdb.Database 112 113 coinbase common.Address 114 extra []byte 115 116 currentMu sync.Mutex 117 current *Work 118 119 snapshotMu sync.RWMutex 120 snapshotBlock *types.Block 121 snapshotState *state.StateDB 122 123 receiveBlockMu sync.Mutex 124 125 uncleMu sync.Mutex 126 possibleUncles map[common.Hash]*types.Block 127 128 unconfirmed *unconfirmedBlocks // set of locally mined blocks pending canonicalness confirmations 129 130 // atomic status counters 131 mining int32 132 atWork int32 133 } 134 135 func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, eth Backend, mux *event.TypeMux) *worker { 136 worker := &worker{ 137 config: config, 138 engine: engine, 139 eth: eth, 140 mux: mux, 141 txsCh: make(chan core.NewTxsEvent, txChanSize), 142 chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), 143 chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), 144 chainDb: eth.ChainDb(), 145 recv: make(chan *Result, resultQueueSize), 146 chain: eth.BlockChain(), 147 proc: eth.BlockChain().Validator(), 148 possibleUncles: make(map[common.Hash]*types.Block), 149 coinbase: coinbase, 150 agents: make(map[Agent]struct{}), 151 unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), miningLogAtDepth), 152 } 153 // Subscribe NewTxsEvent for tx pool 154 worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh) 155 // Subscribe events for blockchain 156 worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) 157 worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) 158 go worker.update() 159 160 go worker.wait() 161 worker.commitNewWork() 162 163 return worker 164 } 165 166 func (self *worker) setEtherbase(addr common.Address) { 167 self.mu.Lock() 168 defer self.mu.Unlock() 169 self.coinbase = addr 170 } 171 172 func (self *worker) setExtra(extra []byte) { 173 self.mu.Lock() 174 defer self.mu.Unlock() 175 self.extra = extra 176 } 177 178 func (self *worker) pending() (*types.Block, *state.StateDB) { 179 if atomic.LoadInt32(&self.mining) == 0 { 180 // return a snapshot to avoid contention on currentMu mutex 181 self.snapshotMu.RLock() 182 defer self.snapshotMu.RUnlock() 183 return self.snapshotBlock, self.snapshotState.Copy() 184 } 185 186 self.currentMu.Lock() 187 defer self.currentMu.Unlock() 188 return self.current.Block, self.current.state.Copy() 189 } 190 191 func (self *worker) pendingBlock() *types.Block { 192 if atomic.LoadInt32(&self.mining) == 0 { 193 // return a snapshot to avoid contention on currentMu mutex 194 self.snapshotMu.RLock() 195 defer self.snapshotMu.RUnlock() 196 return self.snapshotBlock 197 } 198 199 self.currentMu.Lock() 200 defer self.currentMu.Unlock() 201 return self.current.Block 202 } 203 204 func (self *worker) start() { 205 self.mu.Lock() 206 defer self.mu.Unlock() 207 208 atomic.StoreInt32(&self.mining, 1) 209 210 // spin up agents 211 for agent := range self.agents { 212 agent.Start() 213 } 214 } 215 216 func (self *worker) stop() { 217 self.wg.Wait() 218 219 self.mu.Lock() 220 defer self.mu.Unlock() 221 if atomic.LoadInt32(&self.mining) == 1 { 222 for agent := range self.agents { 223 agent.Stop() 224 } 225 } 226 atomic.StoreInt32(&self.mining, 0) 227 atomic.StoreInt32(&self.atWork, 0) 228 } 229 230 func (self *worker) register(agent Agent) { 231 self.mu.Lock() 232 defer self.mu.Unlock() 233 self.agents[agent] = struct{}{} 234 agent.SetReturnCh(self.recv) 235 } 236 237 func (self *worker) unregister(agent Agent) { 238 self.mu.Lock() 239 defer self.mu.Unlock() 240 delete(self.agents, agent) 241 agent.Stop() 242 } 243 244 func (self *worker) update() { 245 defer self.txsSub.Unsubscribe() 246 defer self.chainHeadSub.Unsubscribe() 247 defer self.chainSideSub.Unsubscribe() 248 249 // set the delay equal to period if use alien consensus 250 alienDelay := time.Duration(300) * time.Second 251 if self.config.Alien != nil && self.config.Alien.Period > 0 { 252 alienDelay = time.Duration(self.config.Alien.Period) * time.Second 253 } 254 255 for { 256 // A real event arrived, process interesting content 257 select { 258 // Handle ChainHeadEvent 259 case <-self.chainHeadCh: 260 self.commitNewWork() 261 262 // Handle ChainSideEvent 263 case ev := <-self.chainSideCh: 264 if self.config.Alien != nil { 265 // uncle block useless in Alien consensus 266 } else if self.config.Clique != nil { 267 // uncle block useless in Clique consensus 268 } else { 269 self.uncleMu.Lock() 270 self.possibleUncles[ev.Block.Hash()] = ev.Block 271 self.uncleMu.Unlock() 272 } 273 // Handle NewTxsEvent 274 case ev := <-self.txsCh: 275 // Apply transactions to the pending state if we're not mining. 276 // 277 // Note all transactions received may not be continuous with transactions 278 // already included in the current mining block. These transactions will 279 // be automatically eliminated. 280 if atomic.LoadInt32(&self.mining) == 0 { 281 self.currentMu.Lock() 282 txs := make(map[common.Address]types.Transactions) 283 for _, tx := range ev.Txs { 284 acc, _ := types.Sender(self.current.signer, tx) 285 txs[acc] = append(txs[acc], tx) 286 } 287 txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs) 288 self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase) 289 self.updateSnapshot() 290 self.currentMu.Unlock() 291 } else { 292 // If we're mining, but nothing is being processed, wake on new transactions 293 if self.config.Clique != nil && self.config.Clique.Period == 0 { 294 self.commitNewWork() 295 } 296 } 297 case <-time.After(alienDelay): 298 // try to seal block in each period, even no new block received in dpos 299 if self.config.Alien != nil && self.config.Alien.Period > 0 { 300 self.commitNewWork() 301 } 302 303 // System stopped 304 case <-self.txsSub.Err(): 305 return 306 case <-self.chainHeadSub.Err(): 307 return 308 case <-self.chainSideSub.Err(): 309 return 310 } 311 } 312 } 313 314 func (self *worker) receiveBlock(mustCommitNewWork bool, result *Result) bool { 315 self.receiveBlockMu.Lock() 316 defer self.receiveBlockMu.Unlock() 317 318 atomic.AddInt32(&self.atWork, -1) 319 320 if result == nil { 321 return mustCommitNewWork 322 } 323 block := result.Block 324 work := result.Work 325 326 // Update the block hash in all logs since it is now available and not when the 327 // receipt/log of individual transactions were created. 328 for _, r := range work.receipts { 329 for _, l := range r.Logs { 330 l.BlockHash = block.Hash() 331 } 332 } 333 for _, log := range work.state.Logs() { 334 log.BlockHash = block.Hash() 335 } 336 stat, err := self.chain.WriteBlockWithState(block, work.receipts, work.state) 337 if err != nil { 338 log.Error("Failed writing block to chain", "err", err) 339 return mustCommitNewWork 340 } 341 // check if canon block and write transactions 342 if stat == core.CanonStatTy { 343 // implicit by posting ChainHeadEvent 344 mustCommitNewWork = false 345 } 346 // Broadcast the block and announce chain insertion event 347 self.mux.Post(core.NewMinedBlockEvent{Block: block}) 348 var ( 349 events []interface{} 350 logs = work.state.Logs() 351 ) 352 events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs}) 353 if stat == core.CanonStatTy { 354 events = append(events, core.ChainHeadEvent{Block: block}) 355 } 356 self.chain.PostChainEvents(events, logs) 357 358 // Insert the block into the set of pending ones to wait for confirmations 359 self.unconfirmed.Insert(block.NumberU64(), block.Hash()) 360 361 if mustCommitNewWork { 362 self.commitNewWork() 363 } 364 365 return mustCommitNewWork 366 } 367 368 func (self *worker) wait() { 369 for { 370 mustCommitNewWork := true 371 for result := range self.recv { 372 mustCommitNewWork = self.receiveBlock(mustCommitNewWork, result) 373 } 374 } 375 } 376 377 // push sends a new work task to currently live miner agents. 378 func (self *worker) push(work *Work) { 379 if atomic.LoadInt32(&self.mining) != 1 { 380 return 381 } 382 for agent := range self.agents { 383 atomic.AddInt32(&self.atWork, 1) 384 if ch := agent.Work(); ch != nil { 385 ch <- work 386 } 387 } 388 } 389 390 // makeCurrent creates a new environment for the current cycle. 391 func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error { 392 state, err := self.chain.StateAt(parent.Root()) 393 if err != nil { 394 return err 395 } 396 work := &Work{ 397 config: self.config, 398 signer: types.NewEIP155Signer(self.config.ChainId), 399 state: state, 400 ancestors: set.New(), 401 family: set.New(), 402 uncles: set.New(), 403 header: header, 404 createdAt: time.Now(), 405 } 406 407 // when 08 is processed ancestors contain 07 (quick block) 408 for _, ancestor := range self.chain.GetBlocksFromHash(parent.Hash(), 7) { 409 for _, uncle := range ancestor.Uncles() { 410 work.family.Add(uncle.Hash()) 411 } 412 work.family.Add(ancestor.Hash()) 413 work.ancestors.Add(ancestor.Hash()) 414 } 415 416 // Keep track of transactions which return errors so they can be removed 417 work.tcount = 0 418 self.current = work 419 return nil 420 } 421 422 func (self *worker) commitNewWork() { 423 self.receiveBlockMu.Lock() 424 defer self.receiveBlockMu.Unlock() 425 self.mu.Lock() 426 defer self.mu.Unlock() 427 self.uncleMu.Lock() 428 defer self.uncleMu.Unlock() 429 self.currentMu.Lock() 430 defer self.currentMu.Unlock() 431 432 tstart := time.Now() 433 parent := self.chain.CurrentBlock() 434 435 tstamp := tstart.Unix() 436 if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { 437 tstamp = parent.Time().Int64() + 1 438 } 439 // this will ensure we're not going off too far in the future 440 if now := time.Now().Unix(); tstamp > now+1 { 441 wait := time.Duration(tstamp-now) * time.Second 442 log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) 443 time.Sleep(wait) 444 } 445 446 num := parent.Number() 447 header := &types.Header{ 448 ParentHash: parent.Hash(), 449 Number: num.Add(num, common.Big1), 450 GasLimit: core.CalcGasLimit(parent), 451 Extra: self.extra, 452 Time: big.NewInt(tstamp), 453 } 454 // Only set the coinbase if we are mining (avoid spurious block rewards) 455 if atomic.LoadInt32(&self.mining) == 1 { 456 header.Coinbase = self.coinbase 457 } 458 if err := self.engine.Prepare(self.chain, header); err != nil { 459 log.Error("Failed to prepare header for mining", "err", err) 460 return 461 } 462 463 // Could potentially happen if starting to mine in an odd state. 464 err := self.makeCurrent(parent, header) 465 if err != nil { 466 log.Error("Failed to create mining context", "err", err) 467 return 468 } 469 // Create the current work task and check any fork transitions needed 470 work := self.current 471 472 pending, err := self.eth.TxPool().Pending() 473 if err != nil { 474 log.Error("Failed to fetch pending transactions", "err", err) 475 return 476 } 477 txs := types.NewTransactionsByPriceAndNonce(self.current.signer, pending) 478 work.commitTransactions(self.mux, txs, self.chain, self.coinbase) 479 480 // compute uncles for the new block. 481 var ( 482 uncles []*types.Header 483 badUncles []common.Hash 484 ) 485 for hash, uncle := range self.possibleUncles { 486 if len(uncles) == 2 { 487 break 488 } 489 if err := self.commitUncle(work, uncle.Header()); err != nil { 490 log.Trace("Bad uncle found and will be removed", "hash", hash) 491 log.Trace(fmt.Sprint(uncle)) 492 493 badUncles = append(badUncles, hash) 494 } else { 495 log.Debug("Committing new uncle to block", "hash", hash) 496 uncles = append(uncles, uncle.Header()) 497 } 498 } 499 for _, hash := range badUncles { 500 delete(self.possibleUncles, hash) 501 } 502 // Create the new block to seal with the consensus engine 503 if work.Block, err = self.engine.Finalize(self.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { 504 log.Error("Failed to finalize block for sealing", "err", err) 505 return 506 } 507 // We only care about logging if we're actually mining. 508 if atomic.LoadInt32(&self.mining) == 1 { 509 log.Info("Commit new mining work", "number", work.Block.Number(), "txs", work.tcount, "uncles", len(uncles), "elapsed", common.PrettyDuration(time.Since(tstart))) 510 self.unconfirmed.Shift(work.Block.NumberU64() - 1) 511 } 512 self.push(work) 513 self.updateSnapshot() 514 // todo: add params into gttc, to decide if or not send this tx 515 if self.config.Alien != nil && self.config.Alien.PBFTEnable { 516 err = self.sendConfirmTx(parent.Number()) 517 if err != nil { 518 log.Info("Fail to Sign the transaction by coinbase", "err", err) 519 } 520 } 521 522 } 523 524 // For PBFT of alien consensus, the follow code may move later 525 // After confirm a new block, the miner send a custom transaction to self, which value is 0 526 // and data like "ufo:1:event:confirm:123" (ufo is prefix, 1 is version, 123 is block number 527 // to let the next signer now this signer already confirm this block 528 func (self *worker) sendConfirmTx(blockNumber *big.Int) error { 529 wallets := self.eth.AccountManager().Wallets() 530 // wallets check 531 if len(wallets) == 0 { 532 return nil 533 } 534 for _, wallet := range wallets { 535 if len(wallet.Accounts()) == 0 { 536 continue 537 } else { 538 for _, account := range wallet.Accounts() { 539 if account.Address == self.coinbase { 540 // coinbase account found 541 // send custom tx 542 nonce := self.snapshotState.GetNonce(account.Address) 543 tmpTx := types.NewTransaction(nonce, account.Address, big.NewInt(0), uint64(100000), big.NewInt(10000), []byte(fmt.Sprintf("ufo:1:event:confirm:%d", blockNumber))) 544 signedTx, err := wallet.SignTx(account, tmpTx, self.eth.BlockChain().Config().ChainId) 545 if err != nil { 546 return err 547 } else { 548 err = self.eth.TxPool().AddLocal(signedTx) 549 if err != nil { 550 return err 551 } 552 } 553 return nil 554 } 555 } 556 } 557 558 } 559 return nil 560 } 561 562 func (self *worker) commitUncle(work *Work, uncle *types.Header) error { 563 hash := uncle.Hash() 564 if work.uncles.Has(hash) { 565 return fmt.Errorf("uncle not unique") 566 } 567 if !work.ancestors.Has(uncle.ParentHash) { 568 return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4]) 569 } 570 if work.family.Has(hash) { 571 return fmt.Errorf("uncle already in family (%x)", hash) 572 } 573 work.uncles.Add(uncle.Hash()) 574 return nil 575 } 576 577 func (self *worker) updateSnapshot() { 578 self.snapshotMu.Lock() 579 defer self.snapshotMu.Unlock() 580 581 self.snapshotBlock = types.NewBlock( 582 self.current.header, 583 self.current.txs, 584 nil, 585 self.current.receipts, 586 ) 587 self.snapshotState = self.current.state.Copy() 588 } 589 590 func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) { 591 if env.gasPool == nil { 592 env.gasPool = new(core.GasPool).AddGas(env.header.GasLimit) 593 } 594 595 var coalescedLogs []*types.Log 596 597 for { 598 // If we don't have enough gas for any further transactions then we're done 599 if env.gasPool.Gas() < params.TxGas { 600 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) 601 break 602 } 603 // Retrieve the next transaction and abort if all done 604 tx := txs.Peek() 605 if tx == nil { 606 break 607 } 608 // Error may be ignored here. The error has already been checked 609 // during transaction acceptance is the transaction pool. 610 // 611 // We use the eip155 signer regardless of the current hf. 612 from, _ := types.Sender(env.signer, tx) 613 // Check whether the tx is replay protected. If we're not in the EIP155 hf 614 // phase, start ignoring the sender until we do. 615 if tx.Protected() && !env.config.IsEIP155(env.header.Number) { 616 log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block) 617 618 txs.Pop() 619 continue 620 } 621 // Start executing the transaction 622 env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) 623 624 err, logs := env.commitTransaction(tx, bc, coinbase, env.gasPool) 625 switch err { 626 case core.ErrGasLimitReached: 627 // Pop the current out-of-gas transaction without shifting in the next from the account 628 log.Trace("Gas limit exceeded for current block", "sender", from) 629 txs.Pop() 630 631 case core.ErrNonceTooLow: 632 // New head notification data race between the transaction pool and miner, shift 633 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 634 txs.Shift() 635 636 case core.ErrNonceTooHigh: 637 // Reorg notification data race between the transaction pool and miner, skip account = 638 log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce()) 639 txs.Pop() 640 641 case nil: 642 // Everything ok, collect the logs and shift in the next transaction from the same account 643 coalescedLogs = append(coalescedLogs, logs...) 644 env.tcount++ 645 txs.Shift() 646 647 default: 648 // Strange error, discard the transaction and get the next in line (note, the 649 // nonce-too-high clause will prevent us from executing in vain). 650 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 651 txs.Shift() 652 } 653 } 654 655 if len(coalescedLogs) > 0 || env.tcount > 0 { 656 // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined 657 // logs by filling in the block hash when the block was mined by the local miner. This can 658 // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. 659 cpy := make([]*types.Log, len(coalescedLogs)) 660 for i, l := range coalescedLogs { 661 cpy[i] = new(types.Log) 662 *cpy[i] = *l 663 } 664 go func(logs []*types.Log, tcount int) { 665 if len(logs) > 0 { 666 mux.Post(core.PendingLogsEvent{Logs: logs}) 667 } 668 if tcount > 0 { 669 mux.Post(core.PendingStateEvent{}) 670 } 671 }(cpy, env.tcount) 672 } 673 } 674 675 func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) { 676 snap := env.state.Snapshot() 677 678 receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) 679 if err != nil { 680 env.state.RevertToSnapshot(snap) 681 return err, nil 682 } 683 env.txs = append(env.txs, tx) 684 env.receipts = append(env.receipts, receipt) 685 686 return nil, receipt.Logs 687 }