github.com/amazechain/amc@v0.1.3/internal/miner/worker.go (about) 1 // Copyright 2022 The AmazeChain Authors 2 // This file is part of the AmazeChain library. 3 // 4 // The AmazeChain 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 AmazeChain 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 AmazeChain library. If not, see <http://www.gnu.org/licenses/>. 16 17 package miner 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "github.com/amazechain/amc/core" 24 "github.com/amazechain/amc/internal/api" 25 "github.com/amazechain/amc/internal/consensus/misc" 26 "github.com/amazechain/amc/internal/metrics/prometheus" 27 "github.com/holiman/uint256" 28 "sort" 29 "sync" 30 31 "github.com/amazechain/amc/modules/rawdb" 32 33 "github.com/amazechain/amc/internal" 34 vm2 "github.com/amazechain/amc/internal/vm" 35 "github.com/amazechain/amc/modules/state" 36 37 "sync/atomic" 38 "time" 39 40 "github.com/amazechain/amc/common" 41 "github.com/amazechain/amc/common/block" 42 "github.com/amazechain/amc/common/transaction" 43 "github.com/amazechain/amc/common/types" 44 "github.com/amazechain/amc/conf" 45 46 "github.com/amazechain/amc/internal/consensus" 47 "github.com/amazechain/amc/log" 48 event "github.com/amazechain/amc/modules/event/v2" 49 "github.com/amazechain/amc/params" 50 51 mapset "github.com/deckarep/golang-set" 52 "golang.org/x/sync/errgroup" 53 ) 54 55 var ( 56 blockSignGauge = prometheus.GetOrCreateCounter("block_sign_counter", true) 57 ) 58 59 type task struct { 60 receipts []*block.Receipt 61 state *state.IntraBlockState 62 block block.IBlock 63 createdAt time.Time 64 nopay map[types.Address]*uint256.Int 65 } 66 67 type newWorkReq struct { 68 interrupt *atomic.Int32 69 noempty bool 70 timestamp int64 71 } 72 73 type generateParams struct { 74 timestamp uint64 // The timstamp for sealing task 75 parentHash types.Hash // Parent block hash, empty means the latest chain head 76 coinbase types.Address // The fee recipient address for including transaction 77 random types.Hash // The randomness generated by beacon chain, empty before the merge 78 noTxs bool // Flag whether an empty block without any transaction is expected 79 } 80 81 type environment struct { 82 //signer types.Signer 83 84 //state *state.IntraBlockState 85 ancestors mapset.Set // ancestor set (used for checking uncle parent validity) 86 family mapset.Set // family set (used for checking uncle invalidity) 87 tcount int // tx count in cycle 88 gasPool *common.GasPool // available gas used to pack transactions 89 coinbase types.Address 90 91 header *block.Header 92 txs []*transaction.Transaction 93 receipts []*block.Receipt 94 } 95 96 func (env *environment) copy() *environment { 97 cpy := &environment{ 98 ancestors: env.ancestors.Clone(), 99 family: env.family.Clone(), 100 tcount: env.tcount, 101 coinbase: env.coinbase, 102 header: block.CopyHeader(env.header), 103 receipts: env.receipts, 104 } 105 if env.gasPool != nil { 106 gasPool := *env.gasPool 107 cpy.gasPool = &gasPool 108 } 109 110 cpy.txs = make([]*transaction.Transaction, len(env.txs)) 111 copy(cpy.txs, env.txs) 112 return cpy 113 } 114 115 const ( 116 commitInterruptNone int32 = iota 117 commitInterruptNewHead 118 commitInterruptResubmit 119 commitInterruptTimeout 120 ) 121 122 const ( 123 minPeriodInterval = 1 * time.Second // 1s 124 staleThreshold = 7 125 resubmitAdjustChanSize = 10 126 127 // maxRecommitInterval is the maximum time interval to recreate the sealing block with 128 // any newly arrived transactions. 129 maxRecommitInterval = 12 * time.Second 130 131 intervalAdjustRatio = 0.1 132 133 intervalAdjustBias = 200 * 1000.0 * 1000.0 134 ) 135 136 var ( 137 errBlockInterruptedByNewHead = errors.New("new head arrived while building block") 138 errBlockInterruptedByRecommit = errors.New("recommit interrupt while building block") 139 errBlockInterruptedByTimeout = errors.New("timeout while building block") 140 ) 141 142 // intervalAdjust represents a resubmitting interval adjustment. 143 type intervalAdjust struct { 144 ratio float64 145 inc bool 146 } 147 148 type worker struct { 149 minerConf conf.MinerConfig 150 engine consensus.Engine 151 chain common.IBlockChain 152 txsPool common.ITxsPool 153 154 coinbase types.Address 155 chainConfig *params.ChainConfig 156 157 isLocalBlock func(header *block.Header) bool 158 pendingTasks map[types.Hash]*task 159 160 wg sync.WaitGroup 161 mu sync.RWMutex 162 163 startCh chan struct{} 164 newWorkCh chan *newWorkReq 165 resultCh chan block.IBlock 166 taskCh chan *task 167 168 resubmitAdjustCh chan *intervalAdjust 169 170 running int32 171 newTxs int32 172 173 group *errgroup.Group 174 ctx context.Context 175 cancel context.CancelFunc 176 //current *environment 177 newTaskHook func(*task) 178 179 snapshotMu sync.RWMutex // The lock used to protect the snapshots below 180 snapshotBlock block.IBlock 181 snapshotReceipts block.Receipts 182 } 183 184 func newWorker(ctx context.Context, group *errgroup.Group, chainConfig *params.ChainConfig, engine consensus.Engine, bc common.IBlockChain, txsPool common.ITxsPool, isLocalBlock func(header *block.Header) bool, init bool, minerConf conf.MinerConfig) *worker { 185 c, cancel := context.WithCancel(ctx) 186 worker := &worker{ 187 engine: engine, 188 chain: bc, 189 txsPool: txsPool, 190 chainConfig: chainConfig, 191 mu: sync.RWMutex{}, 192 startCh: make(chan struct{}, 1), 193 group: group, 194 isLocalBlock: isLocalBlock, 195 ctx: c, 196 cancel: cancel, 197 taskCh: make(chan *task), 198 newWorkCh: make(chan *newWorkReq), 199 resultCh: make(chan block.IBlock), 200 pendingTasks: make(map[types.Hash]*task), 201 minerConf: minerConf, 202 resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize), 203 } 204 recommit := worker.minerConf.Recommit 205 if recommit < minPeriodInterval { 206 recommit = minPeriodInterval 207 } 208 209 // machine verify 210 group.Go(func() error { 211 return api.MachineVerify(ctx) 212 }) 213 214 group.Go(func() error { 215 return worker.workLoop(recommit) 216 }) 217 218 group.Go(func() error { 219 return worker.runLoop() 220 }) 221 222 group.Go(func() error { 223 return worker.taskLoop() 224 }) 225 226 group.Go(func() error { 227 return worker.resultLoop() 228 }) 229 230 if init { 231 worker.startCh <- struct{}{} 232 } 233 234 return worker 235 } 236 237 func (w *worker) start() { 238 atomic.StoreInt32(&w.running, 1) 239 w.startCh <- struct{}{} 240 } 241 242 func (w *worker) stop() { 243 atomic.StoreInt32(&w.running, 0) 244 } 245 246 func (w *worker) close() { 247 248 } 249 250 func (w *worker) isRunning() bool { 251 return atomic.LoadInt32(&w.running) == 1 252 } 253 func (w *worker) setCoinbase(addr types.Address) { 254 w.mu.Lock() 255 defer w.mu.Unlock() 256 w.coinbase = addr 257 } 258 259 func (w *worker) runLoop() error { 260 defer w.cancel() 261 defer w.stop() 262 for { 263 select { 264 case <-w.ctx.Done(): 265 return w.ctx.Err() 266 case req := <-w.newWorkCh: 267 err := w.commitWork(req.interrupt, req.noempty, req.timestamp) 268 if err != nil { 269 log.Error("runLoop err:", err.Error()) 270 //w.startCh <- struct{}{} 271 } 272 } 273 } 274 } 275 276 func (w *worker) resultLoop() error { 277 defer w.cancel() 278 defer w.stop() 279 280 for { 281 select { 282 case <-w.ctx.Done(): 283 return w.ctx.Err() 284 case blk := <-w.resultCh: 285 if blk == nil { 286 continue 287 } 288 289 // Short circuit when receiving duplicate result caused by resubmitting. 290 if w.chain.HasBlock(blk.Hash(), blk.Number64().Uint64()) { 291 continue 292 } 293 294 var ( 295 sealhash = w.engine.SealHash(blk.Header()) 296 hash = blk.Hash() 297 ) 298 w.mu.RLock() 299 task, exist := w.pendingTasks[sealhash] 300 w.mu.RUnlock() 301 if !exist { 302 log.Error("Block found but no relative pending task", "number", blk.Number64().Uint64(), "sealhash", sealhash, "hash", hash) 303 //w.startCh <- struct{}{} 304 continue 305 } 306 307 // Different block could share same sealhash, deep copy here to prevent write-write conflict. 308 var ( 309 receipts = make([]*block.Receipt, len(task.receipts)) 310 logs []*block.Log 311 ) 312 for i, taskReceipt := range task.receipts { 313 receipt := new(block.Receipt) 314 receipts[i] = receipt 315 *receipt = *taskReceipt 316 317 // add block location fields 318 receipt.BlockHash = hash 319 receipt.BlockNumber = blk.Number64() 320 receipt.TransactionIndex = uint(i) 321 322 // Update the block hash in all logs since it is now available and not when the 323 // receipt/log of individual transactions were created. 324 receipt.Logs = make([]*block.Log, len(taskReceipt.Logs)) 325 for i, taskLog := range taskReceipt.Logs { 326 log := new(block.Log) 327 receipt.Logs[i] = log 328 *log = *taskLog 329 log.BlockHash = hash 330 } 331 logs = append(logs, receipt.Logs...) 332 } 333 334 // Commit block and state to database. 335 err := w.chain.WriteBlockWithState(blk, receipts, task.state, task.nopay) 336 if err != nil { 337 log.Error("Failed writing block to chain", "err", err) 338 continue 339 } 340 blockSignGauge.Set(uint64(len(blk.Body().Verifier()))) 341 342 if len(logs) > 0 { 343 event.GlobalEvent.Send(common.NewLogsEvent{Logs: logs}) 344 } 345 346 log.Info("🔨 Successfully sealed new block", 347 "sealhash", sealhash, 348 "hash", hash, 349 "number", blk.Number64().Uint64(), 350 "used gas", blk.GasUsed(), 351 "diff", blk.Difficulty().Uint64(), 352 "headerTime", time.Unix(int64(blk.Time()), 0).Format(time.RFC3339), 353 "verifierCount", len(blk.Body().Verifier()), 354 "rewardCount", len(blk.Body().Reward()), 355 "elapsed", common.PrettyDuration(time.Since(task.createdAt)), 356 "txs", len(blk.Transactions())) 357 358 if err = w.chain.SealedBlock(blk); err != nil { 359 log.Error("Failed Broadcast block to p2p network", "err", err) 360 continue 361 } 362 event.GlobalEvent.Send(common.ChainHighestBlock{Block: *blk.(*block.Block), Inserted: true}) 363 } 364 } 365 } 366 367 func (w *worker) taskLoop() error { 368 defer w.cancel() 369 defer w.stop() 370 371 var ( 372 stopCh chan struct{} 373 prev types.Hash 374 ) 375 376 interrupt := func() { 377 if stopCh != nil { 378 close(stopCh) 379 stopCh = nil 380 } 381 } 382 383 for { 384 select { 385 case <-w.ctx.Done(): 386 return w.ctx.Err() 387 case task := <-w.taskCh: 388 389 if w.newTaskHook != nil { 390 w.newTaskHook(task) 391 } 392 393 sealHash := w.engine.SealHash(task.block.Header()) 394 hash := task.block.Hash() 395 stateRoot := task.block.StateRoot() 396 if sealHash == prev { 397 continue 398 } 399 interrupt() 400 stopCh, prev = make(chan struct{}), sealHash 401 w.mu.Lock() 402 w.pendingTasks[sealHash] = task 403 w.mu.Unlock() 404 405 if err := w.engine.Seal(w.chain, task.block, w.resultCh, stopCh); err != nil { 406 w.mu.Lock() 407 delete(w.pendingTasks, sealHash) 408 w.mu.Unlock() 409 log.Warn("delete task", "sealHash", sealHash, "hash", hash, "stateRoot", stateRoot, "err", err) 410 if errors.Is(err, consensus.ErrNotEnoughSign) { 411 time.Sleep(1 * time.Second) 412 w.startCh <- struct{}{} 413 } 414 } else { 415 log.Debug("send task", "sealHash", sealHash, "hash", hash, "stateRoot", stateRoot) 416 } 417 } 418 } 419 } 420 421 func (w *worker) commitWork(interrupt *atomic.Int32, noempty bool, timestamp int64) error { 422 start := time.Now() 423 if w.isRunning() { 424 if w.coinbase == (types.Address{}) { 425 return fmt.Errorf("coinbase is empty") 426 } 427 } 428 429 current, err := w.prepareWork(&generateParams{timestamp: uint64(timestamp), coinbase: w.coinbase}) 430 if err != nil { 431 log.Error("cannot prepare work", "err", err) 432 return err 433 } 434 435 tx, err := w.chain.DB().BeginRo(w.ctx) 436 if nil != err { 437 log.Error("work.commitWork failed", err) 438 return err 439 } 440 defer tx.Rollback() 441 442 stateReader := state.NewPlainStateReader(tx) 443 stateWriter := state.NewNoopWriter() 444 ibs := state.New(stateReader) 445 // generate state for mobile verify 446 ibs.BeginWriteSnapshot() 447 ibs.BeginWriteCodes() 448 headers := make([]*block.Header, 0) 449 getHeader := func(hash types.Hash, number uint64) *block.Header { 450 h := rawdb.ReadHeader(tx, hash, number) 451 if nil != h { 452 headers = append(headers, h) 453 } 454 return h 455 } 456 457 err = w.fillTransactions(interrupt, current, ibs, getHeader) 458 switch { 459 case err == nil: 460 w.resubmitAdjustCh <- &intervalAdjust{inc: false} 461 case errors.Is(err, errBlockInterruptedByRecommit): 462 gaslimit := current.header.GasLimit 463 ratio := float64(gaslimit-current.gasPool.Gas()) / float64(gaslimit) 464 if ratio < 0.1 { 465 ratio = 0.1 466 } 467 w.resubmitAdjustCh <- &intervalAdjust{ 468 ratio: ratio, 469 inc: true, 470 } 471 } 472 473 //var rewards []*block.Reward 474 //if w.chainConfig.IsBeijing(current.header.Number.Uint64()) { 475 // rewards, err = w.engine.Rewards(tx, block.CopyHeader(current.header), ibs, false) 476 // if err != nil { 477 // return err 478 // } 479 //} 480 481 if err = w.commit(current, stateWriter, ibs, start, headers); nil != err { 482 log.Errorf("w.commit failed, error %v\n", err) 483 return err 484 } 485 486 return nil 487 } 488 489 // recalcRecommit recalculates the resubmitting interval upon feedback. 490 func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) time.Duration { 491 var ( 492 prevF = float64(prev.Nanoseconds()) 493 next float64 494 ) 495 if inc { 496 next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target+intervalAdjustBias) 497 max := float64(maxRecommitInterval.Nanoseconds()) 498 if next > max { 499 next = max 500 } 501 } else { 502 next = prevF*(1-intervalAdjustRatio) + intervalAdjustRatio*(target-intervalAdjustBias) 503 min := float64(minRecommit.Nanoseconds()) 504 if next < min { 505 next = min 506 } 507 } 508 return time.Duration(int64(next)) 509 } 510 511 func (w *worker) workLoop(recommit time.Duration) error { 512 defer w.cancel() 513 defer w.stop() 514 var ( 515 interrupt *atomic.Int32 516 minRecommit = recommit // minimal resubmit interval specified by user. 517 timestamp int64 // timestamp for each round of sealing. 518 ) 519 520 newBlockCh := make(chan common.ChainHighestBlock) 521 defer close(newBlockCh) 522 523 newBlockSub := event.GlobalEvent.Subscribe(newBlockCh) 524 defer newBlockSub.Unsubscribe() 525 526 timer := time.NewTimer(0) 527 defer timer.Stop() 528 <-timer.C // discard the initial tick 529 530 commit := func(noempty bool, s int32) { 531 if interrupt != nil { 532 interrupt.Store(s) 533 } 534 interrupt = new(atomic.Int32) 535 select { 536 case w.newWorkCh <- &newWorkReq{interrupt: interrupt, noempty: noempty, timestamp: timestamp}: 537 case <-w.ctx.Done(): 538 return 539 } 540 timer.Reset(recommit) 541 //atomic.StoreInt32(&w.newTxs, 0) 542 } 543 544 clearPending := func(number *uint256.Int) { 545 w.mu.Lock() 546 for h, t := range w.pendingTasks { 547 if number.Cmp(uint256.NewInt(0).Add(t.block.Number64(), uint256.NewInt(staleThreshold))) < 1 { 548 delete(w.pendingTasks, h) 549 } 550 } 551 w.mu.Unlock() 552 } 553 554 for { 555 select { 556 case <-w.ctx.Done(): 557 return w.ctx.Err() 558 case <-w.startCh: 559 clearPending(w.chain.CurrentBlock().Number64()) 560 timestamp = time.Now().Unix() 561 commit(false, commitInterruptNewHead) 562 563 case blockEvent := <-newBlockCh: 564 clearPending(blockEvent.Block.Number64()) 565 timestamp = time.Now().Unix() 566 commit(false, commitInterruptNewHead) 567 case err := <-newBlockSub.Err(): 568 return err 569 570 case <-timer.C: 571 // If sealing is running resubmit a new work cycle periodically to pull in 572 // higher priced transactions. Disable this overhead for pending blocks. 573 //if w.isRunning() && (w.chainConfig.Apos == nil && w.chainConfig.Clique == nil) { 574 // continue 575 // commit(false, commitInterruptResubmit) 576 //} 577 case adjust := <-w.resubmitAdjustCh: 578 // Adjust resubmit interval by feedback. 579 if adjust.inc { 580 before := recommit 581 target := float64(recommit.Nanoseconds()) / adjust.ratio 582 recommit = recalcRecommit(minRecommit, recommit, target, true) 583 log.Trace("Increase miner recommit interval", "from", before, "to", recommit) 584 } else { 585 before := recommit 586 recommit = recalcRecommit(minRecommit, recommit, float64(minRecommit.Nanoseconds()), false) 587 log.Trace("Decrease miner recommit interval", "from", before, "to", recommit) 588 } 589 } 590 } 591 } 592 593 func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment, ibs *state.IntraBlockState, getHeader func(hash types.Hash, number uint64) *block.Header) error { 594 // todo fillTx 595 env.txs = []*transaction.Transaction{} 596 txs, err := w.txsPool.GetTransaction() 597 if err != nil { 598 log.Warn("get transaction error", "err", err) 599 return err 600 } 601 602 header := env.header 603 noop := state.NewNoopWriter() 604 var miningCommitTx = func(txn *transaction.Transaction, coinbase types.Address, vmConfig *vm2.Config, chainConfig *params.ChainConfig, ibs *state.IntraBlockState, current *environment) ([]*block.Log, error) { 605 ibs.Prepare(txn.Hash(), types.Hash{}, env.tcount) 606 gasSnap := current.gasPool.Gas() 607 snap := ibs.Snapshot() 608 log.Debug("addTransactionsToMiningBlock", "txn hash", txn.Hash()) 609 receipt, _, err := internal.ApplyTransaction(chainConfig, internal.GetHashFn(header, getHeader), w.engine, &coinbase, env.gasPool, ibs, noop, current.header, txn, &header.GasUsed, *vmConfig) 610 if err != nil { 611 ibs.RevertToSnapshot(snap) 612 env.gasPool = new(common.GasPool).AddGas(gasSnap) // restore gasPool as well as ibs 613 return nil, err 614 } 615 616 current.txs = append(current.txs, txn) 617 current.receipts = append(current.receipts, receipt) 618 return receipt.Logs, nil 619 } 620 621 log.Tracef("fillTransactions txs len:%d", len(txs)) 622 for _, tx := range txs { 623 // Check interruption signal and abort building if it's fired. 624 if interrupt != nil { 625 if signal := interrupt.Load(); signal != commitInterruptNone { 626 return signalToErr(signal) 627 } 628 } 629 if env.gasPool.Gas() < params.TxGas { 630 log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas) 631 break 632 } 633 // Start executing the transaction 634 _, err := miningCommitTx(tx, env.coinbase, &vm2.Config{}, w.chainConfig, ibs, env) 635 636 switch { 637 case errors.Is(err, core.ErrGasLimitReached): 638 continue 639 case errors.Is(err, core.ErrNonceTooHigh): 640 continue 641 case errors.Is(err, core.ErrNonceTooLow): 642 continue 643 case errors.Is(err, nil): 644 env.tcount++ 645 continue 646 default: 647 log.Error("miningCommitTx failed ", "error", err) 648 } 649 } 650 651 return nil 652 } 653 654 func (w *worker) prepareWork(param *generateParams) (*environment, error) { 655 w.mu.RLock() 656 defer w.mu.RUnlock() 657 658 timestamp := param.timestamp 659 660 parent := w.chain.CurrentBlock().Header().(*block.Header) 661 if param.parentHash != (types.Hash{}) { 662 b, _ := w.chain.GetBlockByHash(param.parentHash) 663 if b == nil { 664 return nil, fmt.Errorf("missing parent") 665 } 666 parent = b.Header().(*block.Header) 667 } 668 669 if parent.Time >= param.timestamp { 670 timestamp = parent.Time + 1 671 } 672 673 header := &block.Header{ 674 //Root: parent.StateRoot(), 675 ParentHash: parent.Hash(), 676 Coinbase: param.coinbase, 677 Number: uint256.NewInt(0).Add(parent.Number64(), uint256.NewInt(1)), 678 GasLimit: CalcGasLimit(parent.GasLimit, w.minerConf.GasCeil), 679 Time: uint64(timestamp), 680 Difficulty: uint256.NewInt(0), 681 // just for now 682 BaseFee: uint256.NewInt(0), 683 } 684 685 // Set baseFee and GasLimit if we are on an EIP-1559 chain 686 if w.chainConfig.IsLondon(header.Number.Uint64()) { 687 header.BaseFee, _ = uint256.FromBig(misc.CalcBaseFee(w.chainConfig, parent)) 688 if !w.chainConfig.IsLondon(parent.Number64().Uint64()) { 689 parentGasLimit := parent.GasLimit * params.ElasticityMultiplier 690 header.GasLimit = CalcGasLimit(parentGasLimit, w.minerConf.GasCeil) 691 } 692 } 693 694 if err := w.engine.Prepare(w.chain, header); err != nil { 695 return nil, err 696 } 697 698 return w.makeEnv(parent, header, param.coinbase), nil 699 } 700 701 func (w *worker) makeEnv(parent *block.Header, header *block.Header, coinbase types.Address) *environment { 702 //rtx, err := w.chain.DB().BeginRo(context.Background()) 703 //if nil != err { 704 // return nil 705 //} 706 //defer rtx.Rollback() 707 env := &environment{ 708 ancestors: mapset.NewSet(), 709 family: mapset.NewSet(), 710 coinbase: coinbase, 711 header: header, 712 //state: ibs, 713 gasPool: new(common.GasPool), 714 tcount: 0, 715 } 716 717 env.gasPool = new(common.GasPool).AddGas(header.GasLimit) 718 //} 719 720 for _, ancestor := range w.chain.GetBlocksFromHash(parent.ParentHash, 3) { 721 env.family.Add(ancestor.(*block.Block).Hash()) 722 env.ancestors.Add(ancestor.Hash()) 723 } 724 725 return env 726 } 727 728 func (w *worker) commit(env *environment, writer state.WriterWithChangeSets, ibs *state.IntraBlockState, start time.Time, needHeaders []*block.Header) error { 729 if w.isRunning() { 730 env := env.copy() 731 iblock, rewards, unpay, err := w.engine.FinalizeAndAssemble(w.chain, env.header, ibs, env.txs, nil, env.receipts) 732 if nil != err { 733 return err 734 } 735 736 if w.chainConfig.IsBeijing(env.header.Number.Uint64()) { 737 txs := make([][]byte, len(env.txs)) 738 for i, tx := range env.txs { 739 var err error 740 741 txs[i], err = tx.Marshal() 742 if nil != err { 743 panic(err) 744 } 745 } 746 747 entri := state.Entire{Header: iblock.Header().(*block.Header), Uncles: nil, Transactions: txs, Senders: nil, Snap: ibs.Snap(), Proof: types.Hash{}} 748 cs := ibs.CodeHashes() 749 hs := make(state.HashCodes, 0, len(cs)) 750 for k, v := range cs { 751 hs = append(hs, &state.HashCode{Hash: k, Code: v}) 752 } 753 sort.Sort(hs) 754 755 event.GlobalEvent.Send(common.MinedEntireEvent{Entire: state.EntireCode{Codes: hs, Headers: needHeaders, Entire: entri, Rewards: rewards, CoinBase: env.coinbase}}) 756 } 757 758 // 759 w.updateSnapshot(env, rewards) 760 761 select { 762 case w.taskCh <- &task{receipts: env.receipts, block: iblock, createdAt: time.Now(), state: ibs, nopay: unpay}: 763 log.Debug("Commit new sealing work", 764 "number", iblock.Header().Number64().Uint64(), 765 "sealhash", w.engine.SealHash(iblock.Header()), 766 "txs", env.tcount, 767 "gas", iblock.GasUsed(), 768 "elapsed", common.PrettyDuration(time.Since(start)), 769 "headerTime", time.Unix(int64(iblock.Time()), 0).Format(time.RFC3339), 770 "rewardCount", len(iblock.Body().Reward()), 771 ) 772 case <-w.ctx.Done(): 773 return w.ctx.Err() 774 } 775 } 776 return nil 777 } 778 779 // copyReceipts makes a deep copy of the given receipts. 780 func copyReceipts(receipts []*block.Receipt) []*block.Receipt { 781 result := make([]*block.Receipt, len(receipts)) 782 for i, l := range receipts { 783 cpy := *l 784 result[i] = &cpy 785 } 786 return result 787 } 788 789 // pendingBlockAndReceipts returns pending block and corresponding receipts. 790 func (w *worker) pendingBlockAndReceipts() (block.IBlock, block.Receipts) { 791 // return a snapshot to avoid contention on currentMu mutex 792 w.snapshotMu.RLock() 793 defer w.snapshotMu.RUnlock() 794 return w.snapshotBlock, w.snapshotReceipts 795 } 796 797 // updateSnapshot updates pending snapshot block, receipts and state. 798 func (w *worker) updateSnapshot(env *environment, rewards []*block.Reward) { 799 w.snapshotMu.Lock() 800 defer w.snapshotMu.Unlock() 801 802 w.snapshotBlock = block.NewBlockFromReceipt( 803 env.header, 804 env.txs, 805 nil, 806 env.receipts, 807 rewards, 808 ) 809 w.snapshotReceipts = copyReceipts(env.receipts) 810 } 811 812 func signalToErr(signal int32) error { 813 switch signal { 814 case commitInterruptNewHead: 815 return errBlockInterruptedByNewHead 816 case commitInterruptResubmit: 817 return errBlockInterruptedByRecommit 818 case commitInterruptTimeout: 819 return errBlockInterruptedByTimeout 820 default: 821 panic(fmt.Errorf("undefined signal %d", signal)) 822 } 823 }