gitlab.com/aquachain/aquachain@v1.17.16-rc3.0.20221018032414-e3ddf1e1c055/opt/miner/worker.go (about) 1 // Copyright 2018 The aquachain Authors 2 // This file is part of the aquachain library. 3 // 4 // The aquachain 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 aquachain 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 aquachain 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 set "github.com/deckarep/golang-set" 27 "gitlab.com/aquachain/aquachain/aqua/event" 28 "gitlab.com/aquachain/aquachain/aquadb" 29 "gitlab.com/aquachain/aquachain/common" 30 "gitlab.com/aquachain/aquachain/common/log" 31 "gitlab.com/aquachain/aquachain/consensus" 32 "gitlab.com/aquachain/aquachain/consensus/misc" 33 "gitlab.com/aquachain/aquachain/core" 34 "gitlab.com/aquachain/aquachain/core/state" 35 "gitlab.com/aquachain/aquachain/core/types" 36 "gitlab.com/aquachain/aquachain/core/vm" 37 "gitlab.com/aquachain/aquachain/params" 38 ) 39 40 const ( 41 resultQueueSize = 10 42 miningLogAtDepth = 5 43 44 // txChanSize is the size of channel listening to TxPreEvent. 45 // The number is referenced from the size of tx pool. 46 txChanSize = 4096 47 // chainHeadChanSize is the size of channel listening to ChainHeadEvent. 48 chainHeadChanSize = 10 49 // chainSideChanSize is the size of channel listening to ChainSideEvent. 50 chainSideChanSize = 10 51 ) 52 53 // Agent can register themself with the worker 54 type Agent interface { 55 Work() chan<- *Work 56 SetReturnCh(chan<- *Result) 57 Stop() 58 Start() 59 GetHashRate() int64 60 } 61 62 // Work is the workers current environment and holds 63 // all of the current state information 64 type Work struct { 65 config *params.ChainConfig 66 signer types.Signer 67 68 state *state.StateDB // apply state changes here 69 ancestors set.Set // ancestor set (used for checking uncle parent validity) 70 family set.Set // family set (used for checking uncle invalidity) 71 uncles set.Set // uncle set 72 tcount int // tx count in cycle 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 txCh chan core.TxPreEvent 98 txSub 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 aqua Backend 109 chain *core.BlockChain 110 proc core.Validator 111 chainDb aquadb.Database 112 113 coinbase common.Address 114 extra []byte 115 116 currentMu sync.Mutex 117 current *Work 118 119 uncleMu sync.Mutex 120 possibleUncles map[common.Hash]*types.Block 121 122 unconfirmed *unconfirmedBlocks // set of locally mined blocks pending canonicalness confirmations 123 124 // atomic status counters 125 mining int32 126 atWork int32 127 } 128 129 func newWorker(config *params.ChainConfig, engine consensus.Engine, coinbase common.Address, aqua Backend, mux *event.TypeMux) *worker { 130 worker := &worker{ 131 config: config, 132 engine: engine, 133 aqua: aqua, 134 mux: mux, 135 txCh: make(chan core.TxPreEvent, txChanSize), 136 chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), 137 chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), 138 chainDb: aqua.ChainDb(), 139 recv: make(chan *Result, resultQueueSize), 140 chain: aqua.BlockChain(), 141 proc: aqua.BlockChain().Validator(), 142 possibleUncles: make(map[common.Hash]*types.Block), 143 coinbase: coinbase, 144 agents: make(map[Agent]struct{}), 145 unconfirmed: newUnconfirmedBlocks(aqua.BlockChain(), miningLogAtDepth), 146 } 147 // Subscribe TxPreEvent for tx pool 148 worker.txSub = aqua.TxPool().SubscribeTxPreEvent(worker.txCh) 149 // Subscribe events for blockchain 150 worker.chainHeadSub = aqua.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh) 151 worker.chainSideSub = aqua.BlockChain().SubscribeChainSideEvent(worker.chainSideCh) 152 go worker.update() 153 154 go worker.wait() 155 worker.commitNewWork() 156 157 return worker 158 } 159 160 func (w *worker) setAquabase(addr common.Address) { 161 w.mu.Lock() 162 defer w.mu.Unlock() 163 w.coinbase = addr 164 } 165 166 func (w *worker) setExtra(extra []byte) { 167 w.mu.Lock() 168 defer w.mu.Unlock() 169 w.extra = extra 170 } 171 172 func (w *worker) pending() (*types.Block, *state.StateDB) { 173 w.currentMu.Lock() 174 defer w.currentMu.Unlock() 175 176 if atomic.LoadInt32(&w.mining) == 0 { 177 return types.NewBlock( 178 w.current.header, 179 w.current.txs, 180 nil, 181 w.current.receipts, 182 ), w.current.state.Copy() 183 } 184 return w.current.Block, w.current.state.Copy() 185 } 186 187 func (w *worker) pendingBlock() *types.Block { 188 w.currentMu.Lock() 189 defer w.currentMu.Unlock() 190 191 if atomic.LoadInt32(&w.mining) == 0 { 192 return types.NewBlock( 193 w.current.header, 194 w.current.txs, 195 nil, 196 w.current.receipts, 197 ) 198 } 199 return w.current.Block 200 } 201 202 func (w *worker) start() { 203 w.mu.Lock() 204 defer w.mu.Unlock() 205 206 atomic.StoreInt32(&w.mining, 1) 207 208 // spin up agents 209 for agent := range w.agents { 210 agent.Start() 211 } 212 } 213 214 func (w *worker) stop() { 215 w.wg.Wait() 216 217 w.mu.Lock() 218 defer w.mu.Unlock() 219 if atomic.LoadInt32(&w.mining) == 1 { 220 for agent := range w.agents { 221 agent.Stop() 222 } 223 } 224 atomic.StoreInt32(&w.mining, 0) 225 atomic.StoreInt32(&w.atWork, 0) 226 } 227 228 func (w *worker) register(agent Agent) { 229 w.mu.Lock() 230 defer w.mu.Unlock() 231 w.agents[agent] = struct{}{} 232 agent.SetReturnCh(w.recv) 233 } 234 235 func (w *worker) unregister(agent Agent) { 236 w.mu.Lock() 237 defer w.mu.Unlock() 238 delete(w.agents, agent) 239 agent.Stop() 240 } 241 242 func (w *worker) update() { 243 defer w.txSub.Unsubscribe() 244 defer w.chainHeadSub.Unsubscribe() 245 defer w.chainSideSub.Unsubscribe() 246 247 for { 248 // A real event arrived, process interesting content 249 select { 250 // Handle ChainHeadEvent 251 case <-w.chainHeadCh: 252 w.commitNewWork() 253 254 // Handle ChainSideEvent 255 case ev := <-w.chainSideCh: 256 w.uncleMu.Lock() 257 w.possibleUncles[ev.Block.Hash()] = ev.Block 258 w.uncleMu.Unlock() 259 260 // Handle TxPreEvent 261 case ev := <-w.txCh: 262 // Apply transaction to the pending state if we're not mining 263 if atomic.LoadInt32(&w.mining) == 0 { 264 w.currentMu.Lock() 265 acc, _ := types.Sender(w.current.signer, ev.Tx) 266 txs := map[common.Address]types.Transactions{acc: {ev.Tx}} 267 txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs) 268 269 w.current.commitTransactions(w.mux, txset, w.chain, w.coinbase) 270 w.currentMu.Unlock() 271 } 272 273 // System stopped 274 case <-w.txSub.Err(): 275 return 276 case <-w.chainHeadSub.Err(): 277 return 278 case <-w.chainSideSub.Err(): 279 return 280 } 281 } 282 } 283 284 func (w *worker) wait() { 285 for { 286 mustCommitNewWork := true 287 for result := range w.recv { 288 atomic.AddInt32(&w.atWork, -1) 289 290 if result == nil { 291 continue 292 } 293 block := result.Block 294 if block == nil { 295 log.Error("submitblock: Skipping nil block") 296 continue 297 } 298 work := result.Work 299 hash := block.Hash() 300 // work is nil when using submitblock rpc 301 if work == nil { 302 log.Trace("inserting block via submitblock RPC", block.Number()) 303 _, err := w.chain.InsertChain(types.Blocks{block}) 304 if err != nil { 305 log.Error("Failed writing block to chain", "err", err) 306 } 307 continue 308 } 309 310 // Update the block hash in all logs since it is now available and not when the 311 // receipt/log of individual transactions were created. 312 for _, r := range work.receipts { 313 for _, l := range r.Logs { 314 l.BlockHash = hash 315 } 316 } 317 for _, log := range work.state.Logs() { 318 log.BlockHash = hash 319 } 320 stat, err := w.chain.WriteBlockWithState(block, work.receipts, work.state) 321 if err != nil { 322 log.Error("Failed writing block to chain", "err", err) 323 continue 324 } 325 326 // check if canon block and write transactions 327 if stat == core.CanonStatTy { 328 // implicit by posting ChainHeadEvent 329 mustCommitNewWork = false 330 } 331 // Broadcast the block and announce chain insertion event 332 w.mux.Post(core.NewMinedBlockEvent{Block: block}) 333 var ( 334 events []interface{} 335 logs = work.state.Logs() 336 ) 337 events = append(events, core.ChainEvent{Block: block, Hash: hash, Logs: logs}) 338 if stat == core.CanonStatTy { 339 events = append(events, core.ChainHeadEvent{Block: block}) 340 } 341 w.chain.PostChainEvents(events, logs) 342 343 // Insert the block into the set of pending ones to wait for confirmations 344 w.unconfirmed.Insert(block.NumberU64(), hash) 345 346 if mustCommitNewWork { 347 w.commitNewWork() 348 } 349 } 350 } 351 } 352 353 // push sends a new work task to currently live miner agents. 354 func (w *worker) push(work *Work) { 355 if atomic.LoadInt32(&w.mining) != 1 { 356 return 357 } 358 for agent := range w.agents { 359 atomic.AddInt32(&w.atWork, 1) 360 if ch := agent.Work(); ch != nil { 361 ch <- work 362 } 363 } 364 } 365 366 // makeCurrent creates a new environment for the current cycle. 367 func (w *worker) makeCurrent(parent *types.Block, header *types.Header) error { 368 state, err := w.chain.StateAt(parent.Root()) 369 if err != nil { 370 return err 371 } 372 373 work := &Work{ 374 config: w.config, 375 signer: types.NewEIP155Signer(w.config.ChainId), 376 state: state, 377 ancestors: set.NewSet(), 378 family: set.NewSet(), 379 uncles: set.NewSet(), 380 header: header, 381 createdAt: time.Now(), 382 } 383 384 // when 08 is processed ancestors contain 07 (quick block) 385 for _, ancestor := range w.chain.GetBlocksFromHash(parent.Hash(), 7) { 386 for _, uncle := range ancestor.Uncles() { 387 work.family.Add(uncle.SetVersion(byte(w.config.GetBlockVersion(uncle.Number)))) 388 } 389 work.family.Add(ancestor.Hash()) 390 work.ancestors.Add(ancestor.Hash()) 391 } 392 393 // Keep track of transactions which return errors so they can be removed 394 work.tcount = 0 395 w.current = work 396 return nil 397 } 398 399 func (w *worker) commitNewWork() { 400 w.mu.Lock() 401 defer w.mu.Unlock() 402 w.uncleMu.Lock() 403 defer w.uncleMu.Unlock() 404 w.currentMu.Lock() 405 defer w.currentMu.Unlock() 406 407 tstart := time.Now() 408 parent := w.chain.CurrentBlock() 409 410 tstamp := tstart.Unix() 411 if parent.Time().Cmp(new(big.Int).SetInt64(tstamp)) >= 0 { 412 tstamp = parent.Time().Int64() + 1 413 } 414 // this will ensure we're not going off too far in the future 415 if now := time.Now().Unix(); tstamp > now+1 { 416 wait := time.Duration(tstamp-now) * time.Second 417 log.Info("Mining too far in the future", "wait", common.PrettyDuration(wait)) 418 time.Sleep(wait) 419 } 420 421 num := parent.Number() 422 numnew := num.Add(num, common.Big1) 423 header := &types.Header{ 424 ParentHash: parent.Hash(), 425 Number: numnew, 426 GasLimit: core.CalcGasLimit(parent), 427 Extra: w.extra, 428 Time: big.NewInt(tstamp), 429 Version: w.chain.Config().GetBlockVersion(numnew), 430 } 431 // Only set the coinbase if we are mining (avoid spurious block rewards) 432 if atomic.LoadInt32(&w.mining) == 1 { 433 header.Coinbase = w.coinbase 434 } 435 if err := w.engine.Prepare(w.chain, header); err != nil { 436 log.Error("Failed to prepare header for mining", "err", err) 437 return 438 } 439 // // If we are care about TheDAO hard-fork check whether to override the extra-data or not 440 // if daoBlock := w.config.DAOForkBlock; daoBlock != nil { 441 // // Check whether the block is among the fork extra-override range 442 // limit := new(big.Int).Add(daoBlock, params.DAOForkExtraRange) 443 // if header.Number.Cmp(daoBlock) >= 0 && header.Number.Cmp(limit) < 0 { 444 // // Depending whether we support or oppose the fork, override differently 445 // if w.config.DAOForkSupport { 446 // header.Extra = common.CopyBytes(params.DAOForkBlockExtra) 447 // } else if bytes.Equal(header.Extra, params.DAOForkBlockExtra) { 448 // header.Extra = []byte{} // If miner opposes, don't let it use the reserved extra-data 449 // } 450 // } 451 // } 452 // Could potentially happen if starting to mine in an odd state. 453 err := w.makeCurrent(parent, header) 454 if err != nil { 455 log.Error("Failed to create mining context", "err", err) 456 return 457 } 458 // Create the current work task and check any fork transitions needed 459 work := w.current 460 461 // Mutate the work state according to any hard-fork specs 462 if hf4 := work.config.GetHF(4); hf4 != nil && hf4.Cmp(work.header.Number) == 0 { 463 misc.ApplyHardFork4(work.state) 464 } 465 if hf5 := work.config.GetHF(5); hf5 != nil && hf5.Cmp(work.header.Number) == 0 { 466 misc.ApplyHardFork5(work.state) 467 } 468 469 // compute uncles for the new block. 470 var ( 471 uncles []*types.Header 472 badUncles []common.Hash 473 ) 474 475 for hash, uncle := range w.possibleUncles { 476 if len(uncles) == 1 { 477 break 478 } 479 unclehead := uncle.Header() 480 unclehead.Version = w.chain.Config().GetBlockVersion(unclehead.Number) 481 if err := w.commitUncle(work, unclehead); err != nil { 482 log.Trace("Bad uncle found and will be removed", "hash", hash, "reason", err) 483 log.Trace(uncle.String()) 484 485 badUncles = append(badUncles, hash) 486 continue 487 } 488 log.Debug("Committing new uncle to block", "hash", hash) 489 uncles = append(uncles, uncle.Header()) 490 491 } 492 for _, hash := range badUncles { 493 delete(w.possibleUncles, hash) 494 } 495 496 // compute tx for new block 497 pending, err := w.aqua.TxPool().Pending() 498 if err != nil { 499 log.Error("Failed to fetch pending transactions", "err", err) 500 return 501 } 502 txs := types.NewTransactionsByPriceAndNonce(w.current.signer, pending) 503 work.commitTransactions(w.mux, txs, w.chain, w.coinbase) 504 505 // Create the new block to seal with the consensus engine 506 if work.Block, err = w.engine.Finalize(w.chain, header, work.state, work.txs, uncles, work.receipts); err != nil { 507 log.Error("Failed to finalize block for sealing", "err", err) 508 return 509 } 510 // We only care about logging if we're actually mining. 511 if atomic.LoadInt32(&w.mining) == 1 { 512 log.Info("Commit new mining work", "number", work.Block.Number(), 513 "txs", work.tcount, "uncles", len(uncles), "fees", header.GasUsed, 514 "bench", common.PrettyDuration(time.Since(tstart)), 515 "algo", header.Version, "diff", header.Difficulty) 516 w.unconfirmed.Shift(work.Block.NumberU64() - 1) 517 } 518 w.push(work) 519 } 520 521 func (w *worker) commitUncle(work *Work, uncle *types.Header) error { 522 hash := uncle.Hash() 523 if work.uncles.Cardinality() > 0 { 524 return fmt.Errorf("too many uncles") 525 } 526 if work.uncles.Contains(hash) { 527 return fmt.Errorf("uncle not unique") 528 } 529 if !work.ancestors.Contains(uncle.ParentHash) { 530 return fmt.Errorf("uncle's parent unknown (%x)", uncle.ParentHash[0:4]) 531 } 532 if work.family.Contains(hash) { 533 return fmt.Errorf("uncle already in family (%x)", hash) 534 } 535 work.uncles.Add(uncle.Hash()) 536 return nil 537 } 538 539 func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, bc *core.BlockChain, coinbase common.Address) { 540 gp := new(core.GasPool).AddGas(env.header.GasLimit) 541 542 var coalescedLogs []*types.Log 543 544 for { 545 // If we don't have enough gas for any further transactions then we're done 546 if gp.Gas() < params.TxGas { 547 log.Trace("Not enough gas for further transactions", "gp", gp) 548 break 549 } 550 // Retrieve the next transaction and abort if all done 551 tx := txs.Peek() 552 if tx == nil { 553 break 554 } 555 // Error may be ignored here. The error has already been checked 556 // during transaction acceptance is the transaction pool. 557 // 558 // We use the eip155 signer regardless of the current hf. 559 from, _ := types.Sender(env.signer, tx) 560 // Check whether the tx is replay protected. If we're not in the EIP155 hf 561 // phase, start ignoring the sender until we do. 562 if tx.Protected() && !env.config.IsEIP155(env.header.Number) { 563 log.Trace("Ignoring replay protected transaction", "hash", tx.Hash(), "eip155", env.config.EIP155Block) 564 txs.Pop() 565 continue 566 } 567 // Start executing the transaction 568 env.state.Prepare(tx.Hash(), common.Hash{}, env.tcount) 569 570 err, logs := env.commitTransaction(tx, bc, coinbase, gp) 571 switch err { 572 case core.ErrGasLimitReached: 573 // Pop the current out-of-gas transaction without shifting in the next from the account 574 log.Trace("Fuel limit exceeded for current block", "sender", from) 575 txs.Pop() 576 577 case core.ErrNonceTooLow: 578 // New head notification data race between the transaction pool and miner, shift 579 log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce()) 580 txs.Shift() 581 582 case core.ErrNonceTooHigh: 583 // Reorg notification data race between the transaction pool and miner, skip account = 584 log.Trace("Skipping account, nonce too high", "sender", from, "nonce", tx.Nonce()) 585 txs.Pop() 586 587 case nil: 588 // Everything ok, collect the logs and shift in the next transaction from the same account 589 coalescedLogs = append(coalescedLogs, logs...) 590 env.tcount++ 591 txs.Shift() 592 593 default: 594 // Strange error, discard the transaction and get the next in line (note, the 595 // nonce-too-high clause will prevent us from executing in vain). 596 log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err) 597 txs.Shift() 598 } 599 } 600 601 if len(coalescedLogs) > 0 || env.tcount > 0 { 602 // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined 603 // logs by filling in the block hash when the block was mined by the local miner. This can 604 // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed. 605 cpy := make([]*types.Log, len(coalescedLogs)) 606 for i, l := range coalescedLogs { 607 cpy[i] = new(types.Log) 608 *cpy[i] = *l 609 } 610 go func(logs []*types.Log, tcount int) { 611 if len(logs) > 0 { 612 mux.Post(core.PendingLogsEvent{Logs: logs}) 613 } 614 if tcount > 0 { 615 mux.Post(core.PendingStateEvent{}) 616 } 617 }(cpy, env.tcount) 618 } 619 } 620 621 func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, coinbase common.Address, gp *core.GasPool) (error, []*types.Log) { 622 snap := env.state.Snapshot() 623 624 receipt, _, err := core.ApplyTransaction(env.config, bc, &coinbase, gp, env.state, env.header, tx, &env.header.GasUsed, vm.Config{}) 625 if err != nil { 626 env.state.RevertToSnapshot(snap) 627 return err, nil 628 } 629 env.txs = append(env.txs, tx) 630 env.receipts = append(env.receipts, receipt) 631 632 return nil, receipt.Logs 633 }