go-ethereum/miner/test_backend.go
2023-09-04 14:14:19 +05:30

898 lines
32 KiB
Go

package miner
// import (
// "context"
// "crypto/rand"
// "errors"
// "fmt"
// "math/big"
// "os"
// "sync/atomic"
// "time"
// "github.com/ethereum/go-ethereum/accounts" // nolint:typecheck
// "github.com/ethereum/go-ethereum/consensus/bor"
// "github.com/ethereum/go-ethereum/consensus/clique"
// "github.com/ethereum/go-ethereum/consensus/ethash"
// "github.com/ethereum/go-ethereum/core/state"
// "github.com/ethereum/go-ethereum/core/txpool"
// "github.com/ethereum/go-ethereum/crypto"
// "github.com/ethereum/go-ethereum/ethdb"
// "github.com/ethereum/go-ethereum/common"
// cmath "github.com/ethereum/go-ethereum/common/math"
// "github.com/ethereum/go-ethereum/common/tracing"
// "github.com/ethereum/go-ethereum/consensus"
// "github.com/ethereum/go-ethereum/core"
// "github.com/ethereum/go-ethereum/core/types"
// "github.com/ethereum/go-ethereum/core/vm"
// "github.com/ethereum/go-ethereum/event"
// "github.com/ethereum/go-ethereum/log"
// "github.com/ethereum/go-ethereum/params"
// "go.opentelemetry.io/otel"
// "go.opentelemetry.io/otel/attribute"
// "go.opentelemetry.io/otel/trace"
// lru "github.com/hashicorp/golang-lru"
// )
// const (
// // testCode is the testing contract binary code which will initialises some
// // variables in constructor
// testCode = "0x60806040527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060005534801561003457600080fd5b5060fc806100436000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80630c4dae8814603757806398a213cf146053575b600080fd5b603d607e565b6040518082815260200191505060405180910390f35b607c60048036036020811015606757600080fd5b81019080803590602001909291905050506084565b005b60005481565b806000819055507fe9e44f9f7da8c559de847a3232b57364adc0354f15a2cd8dc636d54396f9587a6000546040518082815260200191505060405180910390a15056fea265627a7a723058208ae31d9424f2d0bc2a3da1a5dd659db2d71ec322a17db8f87e19e209e3a1ff4a64736f6c634300050a0032"
// // testGas is the gas required for contract deployment.
// testGas = 144109
// storageContractByteCode = "608060405234801561001057600080fd5b50610150806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80632e64cec11461003b5780636057361d14610059575b600080fd5b610043610075565b60405161005091906100a1565b60405180910390f35b610073600480360381019061006e91906100ed565b61007e565b005b60008054905090565b8060008190555050565b6000819050919050565b61009b81610088565b82525050565b60006020820190506100b66000830184610092565b92915050565b600080fd5b6100ca81610088565b81146100d557600080fd5b50565b6000813590506100e7816100c1565b92915050565b600060208284031215610103576101026100bc565b5b6000610111848285016100d8565b9150509291505056fea2646970667358221220322c78243e61b783558509c9cc22cb8493dde6925aa5e89a08cdf6e22f279ef164736f6c63430008120033"
// storageContractTxCallData = "0x6057361d0000000000000000000000000000000000000000000000000000000000000001"
// storageCallTxGas = 100000
// )
// func init() {
// testTxPoolConfig = txpool.DefaultConfig
// testTxPoolConfig.Journal = ""
// ethashChainConfig = new(params.ChainConfig)
// *ethashChainConfig = *params.TestChainConfig
// cliqueChainConfig = new(params.ChainConfig)
// *cliqueChainConfig = *params.TestChainConfig
// cliqueChainConfig.Clique = &params.CliqueConfig{
// Period: 10,
// Epoch: 30000,
// }
// signer := types.LatestSigner(params.TestChainConfig)
// tx1 := types.MustSignNewTx(testBankKey, signer, &types.AccessListTx{
// ChainID: params.TestChainConfig.ChainID,
// Nonce: 0,
// To: &testUserAddress,
// Value: big.NewInt(1000),
// Gas: params.TxGas,
// GasPrice: big.NewInt(params.InitialBaseFee),
// })
// pendingTxs = append(pendingTxs, tx1)
// tx2 := types.MustSignNewTx(testBankKey, signer, &types.LegacyTx{
// Nonce: 1,
// To: &testUserAddress,
// Value: big.NewInt(1000),
// Gas: params.TxGas,
// GasPrice: big.NewInt(params.InitialBaseFee),
// })
// newTxs = append(newTxs, tx2)
// }
// // testWorkerBackend implements worker.Backend interfaces and wraps all information needed during the testing.
// type testWorkerBackend struct {
// DB ethdb.Database
// txPool *txpool.TxPool
// chain *core.BlockChain
// Genesis *core.Genesis
// uncleBlock *types.Block
// }
// // PeerCount implements Backend.
// func (*testWorkerBackend) PeerCount() int {
// panic("unimplemented")
// }
// func newTestWorkerBackend(t TensingObject, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, n int) *testWorkerBackend {
// var gspec = core.Genesis{
// Config: chainConfig,
// Alloc: core.GenesisAlloc{TestBankAddress: {Balance: testBankFunds}},
// GasLimit: 30_000_000,
// }
// switch e := engine.(type) {
// case *bor.Bor:
// gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
// copy(gspec.ExtraData[32:32+common.AddressLength], TestBankAddress.Bytes())
// e.Authorize(TestBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
// return crypto.Sign(crypto.Keccak256(data), testBankKey)
// })
// case *clique.Clique:
// gspec.ExtraData = make([]byte, 32+common.AddressLength+crypto.SignatureLength)
// copy(gspec.ExtraData[32:32+common.AddressLength], TestBankAddress.Bytes())
// e.Authorize(TestBankAddress, func(account accounts.Account, s string, data []byte) ([]byte, error) {
// return crypto.Sign(crypto.Keccak256(data), testBankKey)
// })
// case *ethash.Ethash:
// default:
// t.Fatalf("unexpected consensus engine type: %T", engine)
// }
// genesis := gspec.MustCommit(db)
// chain, _ := core.NewBlockChain(db, &core.CacheConfig{TrieDirtyDisabled: true}, &gspec, nil, engine, vm.Config{}, nil, nil, nil)
// txpool := txpool.NewTxPool(testTxPoolConfig, chainConfig, chain)
// // Generate a small n-block chain and an uncle block for it
// if n > 0 {
// blocks, _ := core.GenerateChain(chainConfig, genesis, engine, db, n, func(i int, gen *core.BlockGen) {
// gen.SetCoinbase(TestBankAddress)
// })
// if _, err := chain.InsertChain(blocks); err != nil {
// t.Fatalf("failed to insert origin chain: %v", err)
// }
// }
// parent := genesis
// if n > 0 {
// parent = chain.GetBlockByHash(chain.CurrentBlock().ParentHash)
// }
// blocks, _ := core.GenerateChain(chainConfig, parent, engine, db, 1, func(i int, gen *core.BlockGen) {
// gen.SetCoinbase(testUserAddress)
// })
// return &testWorkerBackend{
// DB: db,
// chain: chain,
// txPool: txpool,
// Genesis: &gspec,
// uncleBlock: blocks[0],
// }
// }
// func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
// func (b *testWorkerBackend) TxPool() *txpool.TxPool { return b.txPool }
// func (b *testWorkerBackend) StateAtBlock(block *types.Block, reexec uint64, base *state.StateDB, checkLive bool, preferDisk bool) (statedb *state.StateDB, err error) {
// return nil, errors.New("not supported")
// }
// func (b *testWorkerBackend) newRandomUncle() (*types.Block, error) {
// var parent *types.Block
// cur := b.chain.CurrentBlock()
// if cur.Number.Uint64() == 0 {
// parent = b.chain.Genesis()
// } else {
// parent = b.chain.GetBlockByHash(b.chain.CurrentBlock().ParentHash)
// }
// var err error
// blocks, _ := core.GenerateChain(b.chain.Config(), parent, b.chain.Engine(), b.DB, 1, func(i int, gen *core.BlockGen) {
// var addr = make([]byte, common.AddressLength)
// _, err = rand.Read(addr)
// if err != nil {
// return
// }
// gen.SetCoinbase(common.BytesToAddress(addr))
// })
// return blocks[0], err
// }
// // newRandomTx creates a new transaction.
// func (b *testWorkerBackend) newRandomTx(creation bool) *types.Transaction {
// var tx *types.Transaction
// gasPrice := big.NewInt(10 * params.InitialBaseFee)
// if creation {
// tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey)
// } else {
// tx, _ = types.SignTx(types.NewTransaction(b.txPool.Nonce(TestBankAddress), testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey)
// }
// return tx
// }
// // newRandomTxWithNonce creates a new transaction with the given nonce.
// func (b *testWorkerBackend) newRandomTxWithNonce(creation bool, nonce uint64) *types.Transaction {
// var tx *types.Transaction
// gasPrice := big.NewInt(100 * params.InitialBaseFee)
// if creation {
// tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(testCode)), types.HomesteadSigner{}, testBankKey)
// } else {
// tx, _ = types.SignTx(types.NewTransaction(nonce, testUserAddress, big.NewInt(1000), params.TxGas, gasPrice, nil), types.HomesteadSigner{}, testBankKey)
// }
// return tx
// }
// // newStorageCreateContractTx creates a new transaction to deploy a storage smart contract.
// func (b *testWorkerBackend) newStorageCreateContractTx() (*types.Transaction, common.Address) {
// var tx *types.Transaction
// gasPrice := big.NewInt(10 * params.InitialBaseFee)
// tx, _ = types.SignTx(types.NewContractCreation(b.txPool.Nonce(TestBankAddress), big.NewInt(0), testGas, gasPrice, common.FromHex(storageContractByteCode)), types.HomesteadSigner{}, testBankKey)
// contractAddr := crypto.CreateAddress(TestBankAddress, b.txPool.Nonce(TestBankAddress))
// return tx, contractAddr
// }
// // newStorageContractCallTx creates a new transaction to call a storage smart contract.
// func (b *testWorkerBackend) newStorageContractCallTx(to common.Address, nonce uint64) *types.Transaction {
// var tx *types.Transaction
// gasPrice := big.NewInt(10 * params.InitialBaseFee)
// tx, _ = types.SignTx(types.NewTransaction(nonce, to, nil, storageCallTxGas, gasPrice, common.FromHex(storageContractTxCallData)), types.HomesteadSigner{}, testBankKey)
// return tx
// }
// // NewTestWorker creates a new test worker with the given parameters.
// func NewTestWorker(t TensingObject, chainConfig *params.ChainConfig, engine consensus.Engine, db ethdb.Database, blocks int, noempty bool, delay uint, opcodeDelay uint) (*worker, *testWorkerBackend, func()) {
// backend := newTestWorkerBackend(t, chainConfig, engine, db, blocks)
// backend.txPool.AddLocals(pendingTxs)
// var w *worker
// if delay != 0 || opcodeDelay != 0 {
// //nolint:staticcheck
// w = newWorkerWithDelay(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false, delay, opcodeDelay)
// } else {
// //nolint:staticcheck
// w = newWorker(testConfig, chainConfig, engine, backend, new(event.TypeMux), nil, false)
// }
// w.setEtherbase(TestBankAddress)
// // enable empty blocks
// w.noempty.Store(noempty)
// return w, backend, w.close
// }
// // newWorkerWithDelay is newWorker() with extra params to induce artficial delays for tests such as commit-interrupt.
// // nolint:staticcheck
// func newWorkerWithDelay(config *Config, chainConfig *params.ChainConfig, engine consensus.Engine, eth Backend, mux *event.TypeMux, isLocalBlock func(header *types.Header) bool, init bool, delay uint, opcodeDelay uint) *worker {
// worker := &worker{
// config: config,
// chainConfig: chainConfig,
// engine: engine,
// eth: eth,
// mux: mux,
// chain: eth.BlockChain(),
// isLocalBlock: isLocalBlock,
// localUncles: make(map[common.Hash]*types.Block),
// remoteUncles: make(map[common.Hash]*types.Block),
// unconfirmed: newUnconfirmedBlocks(eth.BlockChain(), sealingLogAtDepth),
// pendingTasks: make(map[common.Hash]*task),
// txsCh: make(chan core.NewTxsEvent, txChanSize),
// chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
// chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize),
// newWorkCh: make(chan *newWorkReq),
// getWorkCh: make(chan *getWorkReq),
// taskCh: make(chan *task),
// resultCh: make(chan *types.Block, resultQueueSize),
// exitCh: make(chan struct{}),
// startCh: make(chan struct{}, 1),
// resubmitIntervalCh: make(chan time.Duration),
// resubmitAdjustCh: make(chan *intervalAdjust, resubmitAdjustChanSize),
// interruptCommitFlag: config.CommitInterruptFlag,
// }
// worker.noempty.Store(true)
// worker.profileCount = new(int32)
// // Subscribe NewTxsEvent for tx pool
// worker.txsSub = eth.TxPool().SubscribeNewTxsEvent(worker.txsCh)
// // Subscribe events for blockchain
// worker.chainHeadSub = eth.BlockChain().SubscribeChainHeadEvent(worker.chainHeadCh)
// worker.chainSideSub = eth.BlockChain().SubscribeChainSideEvent(worker.chainSideCh)
// interruptedTxCache, err := lru.New(vm.InterruptedTxCacheSize)
// if err != nil {
// log.Warn("Failed to create interrupted tx cache", "err", err)
// }
// worker.interruptedTxCache = &vm.TxCache{
// Cache: interruptedTxCache,
// }
// if !worker.interruptCommitFlag {
// worker.noempty.Store(false)
// }
// // Sanitize recommit interval if the user-specified one is too short.
// recommit := worker.config.Recommit
// if recommit < minRecommitInterval {
// log.Warn("Sanitizing miner recommit interval", "provided", recommit, "updated", minRecommitInterval)
// recommit = minRecommitInterval
// }
// ctx := tracing.WithTracer(context.Background(), otel.GetTracerProvider().Tracer("MinerWorker"))
// worker.wg.Add(4)
// go worker.mainLoopWithDelay(ctx, delay, opcodeDelay)
// go worker.newWorkLoop(ctx, recommit)
// go worker.resultLoop()
// go worker.taskLoop()
// // Submit first work to initialize pending state.
// if init {
// worker.startCh <- struct{}{}
// }
// return worker
// }
// // mainLoopWithDelay is mainLoop() with extra params to induce artficial delays for tests such as commit-interrupt.
// // nolint:gocognit
// func (w *worker) mainLoopWithDelay(ctx context.Context, delay uint, opcodeDelay uint) {
// defer w.wg.Done()
// defer w.txsSub.Unsubscribe()
// defer w.chainHeadSub.Unsubscribe()
// defer w.chainSideSub.Unsubscribe()
// defer func() {
// if w.current != nil {
// w.current.discard()
// }
// }()
// cleanTicker := time.NewTicker(time.Second * 10)
// defer cleanTicker.Stop()
// for {
// select {
// case req := <-w.newWorkCh:
// i := req.interrupt.Load()
// //nolint:contextcheck
// w.commitWorkWithDelay(req.ctx, &i, req.noempty, req.timestamp, delay, opcodeDelay)
// case req := <-w.getWorkCh:
// //nolint:contextcheck
// block, _, err := w.generateWork(req.ctx, req.params)
// if err != nil {
// req.result <- nil
// } else {
// payload := newPayloadResult{
// err: nil,
// block: block,
// fees: block.BaseFee(),
// }
// req.result <- &payload
// }
// case ev := <-w.chainSideCh:
// // Short circuit for duplicate side blocks
// if _, exist := w.localUncles[ev.Block.Hash()]; exist {
// continue
// }
// if _, exist := w.remoteUncles[ev.Block.Hash()]; exist {
// continue
// }
// // Add side block to possible uncle block set depending on the author.
// if w.isLocalBlock != nil && w.isLocalBlock(ev.Block.Header()) {
// w.localUncles[ev.Block.Hash()] = ev.Block
// } else {
// w.remoteUncles[ev.Block.Hash()] = ev.Block
// }
// // If our sealing block contains less than 2 uncle blocks,
// // add the new uncle block if valid and regenerate a new
// // sealing block for higher profit.
// if w.isRunning() && w.current != nil && len(w.current.uncles) < 2 {
// start := time.Now()
// if err := w.commitUncle(w.current, ev.Block.Header()); err == nil {
// commitErr := w.commit(ctx, w.current.copy(), nil, true, start)
// if commitErr != nil {
// log.Error("error while committing work for mining", "err", commitErr)
// }
// }
// }
// case <-cleanTicker.C:
// chainHead := w.chain.CurrentBlock()
// for hash, uncle := range w.localUncles {
// if uncle.NumberU64()+staleThreshold <= chainHead.Number.Uint64() {
// delete(w.localUncles, hash)
// }
// }
// for hash, uncle := range w.remoteUncles {
// if uncle.NumberU64()+staleThreshold <= chainHead.Number.Uint64() {
// delete(w.remoteUncles, hash)
// }
// }
// case ev := <-w.txsCh:
// // Apply transactions to the pending state if we're not sealing
// //
// // Note all transactions received may not be continuous with transactions
// // already included in the current sealing block. These transactions will
// // be automatically eliminated.
// // nolint : nestif
// if !w.isRunning() && w.current != nil {
// // If block is already full, abort
// if gp := w.current.gasPool; gp != nil && gp.Gas() < params.TxGas {
// continue
// }
// txs := make(map[common.Address]types.Transactions)
// for _, tx := range ev.Txs {
// acc, _ := types.Sender(w.current.signer, tx)
// txs[acc] = append(txs[acc], tx)
// }
// txset := types.NewTransactionsByPriceAndNonce(w.current.signer, txs, cmath.FromBig(w.current.header.BaseFee))
// tcount := w.current.tcount
// w.commitTransactions(w.current, txset, nil, context.Background())
// // Only update the snapshot if any new transactions were added
// // to the pending block
// if tcount != w.current.tcount {
// w.updateSnapshot(w.current)
// }
// } else {
// // Special case, if the consensus engine is 0 period clique(dev mode),
// // submit sealing work here since all empty submission will be rejected
// // by clique. Of course the advance sealing(empty submission) is disabled.
// if w.chainConfig.Clique != nil && w.chainConfig.Clique.Period == 0 {
// w.commitWork(ctx, nil, true, time.Now().Unix())
// }
// }
// w.newTxs.Add(int32(len(ev.Txs)))
// // System stopped
// case <-w.exitCh:
// return
// case <-w.txsSub.Err():
// return
// case <-w.chainHeadSub.Err():
// return
// case <-w.chainSideSub.Err():
// return
// }
// }
// }
// // commitWorkWithDelay is commitWork() with extra params to induce artficial delays for tests such as commit-interrupt.
// func (w *worker) commitWorkWithDelay(ctx context.Context, interrupt *int32, noempty bool, timestamp int64, delay uint, opcodeDelay uint) {
// start := time.Now()
// var (
// work *environment
// err error
// )
// tracing.Exec(ctx, "", "worker.prepareWork", func(ctx context.Context, span trace.Span) {
// // Set the coinbase if the worker is running or it's required
// var coinbase common.Address
// if w.isRunning() {
// if w.coinbase == (common.Address{}) {
// log.Error("Refusing to mine without etherbase")
// return
// }
// coinbase = w.coinbase // Use the preset address as the fee recipient
// }
// work, err = w.prepareWork(&generateParams{
// timestamp: uint64(timestamp),
// coinbase: coinbase,
// })
// })
// if err != nil {
// return
// }
// //nolint:contextcheck
// var interruptCtx = context.Background()
// stopFn := func() {}
// defer func() {
// stopFn()
// }()
// if !noempty && w.interruptCommitFlag {
// block := w.chain.GetBlockByHash(w.chain.CurrentBlock().Hash())
// interruptCtx, stopFn = getInterruptTimer(ctx, work, block)
// // nolint : staticcheck
// interruptCtx = vm.PutCache(interruptCtx, w.interruptedTxCache)
// // nolint : staticcheck
// interruptCtx = context.WithValue(interruptCtx, vm.InterruptCtxDelayKey, delay)
// // nolint : staticcheck
// interruptCtx = context.WithValue(interruptCtx, vm.InterruptCtxOpcodeDelayKey, opcodeDelay)
// }
// ctx, span := tracing.StartSpan(ctx, "commitWork")
// defer tracing.EndSpan(span)
// tracing.SetAttributes(
// span,
// attribute.Int("number", int(work.header.Number.Uint64())),
// )
// // Create an empty block based on temporary copied state for
// // sealing in advance without waiting block execution finished.
// if !noempty && !w.noempty.Load() {
// err = w.commit(ctx, work.copy(), nil, false, start)
// if err != nil {
// return
// }
// }
// // Fill pending transactions from the txpool
// w.fillTransactionsWithDelay(ctx, interrupt, work, interruptCtx)
// err = w.commit(ctx, work.copy(), w.fullTaskHook, true, start)
// if err != nil {
// return
// }
// // Swap out the old work with the new one, terminating any leftover
// // prefetcher processes in the mean time and starting a new one.
// if w.current != nil {
// w.current.discard()
// }
// w.current = work
// }
// // fillTransactionsWithDelay is fillTransactions() with extra params to induce artficial delays for tests such as commit-interrupt.
// // nolint:gocognit
// func (w *worker) fillTransactionsWithDelay(ctx context.Context, interrupt *int32, env *environment, interruptCtx context.Context) {
// ctx, span := tracing.StartSpan(ctx, "fillTransactions")
// defer tracing.EndSpan(span)
// // Split the pending transactions into locals and remotes
// // Fill the block with all available pending transactions.
// var (
// localTxsCount int
// remoteTxsCount int
// localTxs = make(map[common.Address]types.Transactions)
// remoteTxs map[common.Address]types.Transactions
// )
// // TODO: move to config or RPC
// const profiling = false
// if profiling {
// doneCh := make(chan struct{})
// defer func() {
// close(doneCh)
// }()
// go func(number uint64) {
// closeFn := func() error {
// return nil
// }
// for {
// select {
// case <-time.After(150 * time.Millisecond):
// // Check if we've not crossed limit
// if attempt := atomic.AddInt32(w.profileCount, 1); attempt >= 10 {
// log.Info("Completed profiling", "attempt", attempt)
// return
// }
// log.Info("Starting profiling in fill transactions", "number", number)
// dir, err := os.MkdirTemp("", fmt.Sprintf("bor-traces-%s-", time.Now().UTC().Format("2006-01-02-150405Z")))
// if err != nil {
// log.Error("Error in profiling", "path", dir, "number", number, "err", err)
// return
// }
// // grab the cpu profile
// closeFnInternal, err := startProfiler("cpu", dir, number)
// if err != nil {
// log.Error("Error in profiling", "path", dir, "number", number, "err", err)
// return
// }
// closeFn = func() error {
// err := closeFnInternal()
// log.Info("Completed profiling", "path", dir, "number", number, "error", err)
// return nil
// }
// case <-doneCh:
// err := closeFn()
// if err != nil {
// log.Info("closing fillTransactions", "number", number, "error", err)
// }
// return
// }
// }
// }(env.header.Number.Uint64())
// }
// tracing.Exec(ctx, "", "worker.SplittingTransactions", func(ctx context.Context, span trace.Span) {
// prePendingTime := time.Now()
// pending := w.eth.TxPool().Pending(ctx, true)
// remoteTxs = pending
// postPendingTime := time.Now()
// for _, account := range w.eth.TxPool().Locals() {
// if txs := remoteTxs[account]; len(txs) > 0 {
// delete(remoteTxs, account)
// localTxs[account] = txs
// }
// }
// postLocalsTime := time.Now()
// localTxsCount = len(localTxs)
// remoteTxsCount = len(remoteTxs)
// tracing.SetAttributes(
// span,
// attribute.Int("len of local txs", localTxsCount),
// attribute.Int("len of remote txs", remoteTxsCount),
// attribute.String("time taken by Pending()", fmt.Sprintf("%v", postPendingTime.Sub(prePendingTime))),
// attribute.String("time taken by Locals()", fmt.Sprintf("%v", postLocalsTime.Sub(postPendingTime))),
// )
// })
// var (
// localEnvTCount int
// remoteEnvTCount int
// committed bool
// )
// if localTxsCount > 0 {
// var txs *types.TransactionsByPriceAndNonce
// tracing.Exec(ctx, "", "worker.LocalTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) {
// txs = types.NewTransactionsByPriceAndNonce(env.signer, localTxs, cmath.FromBig(env.header.BaseFee))
// tracing.SetAttributes(
// span,
// attribute.Int("len of tx local Heads", txs.GetTxs()),
// )
// })
// tracing.Exec(ctx, "", "worker.LocalCommitTransactions", func(ctx context.Context, span trace.Span) {
// committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCtx)
// })
// if committed {
// return
// }
// localEnvTCount = env.tcount
// }
// if remoteTxsCount > 0 {
// var txs *types.TransactionsByPriceAndNonce
// tracing.Exec(ctx, "", "worker.RemoteTransactionsByPriceAndNonce", func(ctx context.Context, span trace.Span) {
// txs = types.NewTransactionsByPriceAndNonce(env.signer, remoteTxs, cmath.FromBig(env.header.BaseFee))
// tracing.SetAttributes(
// span,
// attribute.Int("len of tx remote Heads", txs.GetTxs()),
// )
// })
// tracing.Exec(ctx, "", "worker.RemoteCommitTransactions", func(ctx context.Context, span trace.Span) {
// committed = w.commitTransactionsWithDelay(env, txs, interrupt, interruptCtx)
// })
// if committed {
// return
// }
// remoteEnvTCount = env.tcount
// }
// tracing.SetAttributes(
// span,
// attribute.Int("len of final local txs ", localEnvTCount),
// attribute.Int("len of final remote txs", remoteEnvTCount),
// )
// }
// // commitTransactionsWithDelay is commitTransactions() with extra params to induce artficial delays for tests such as commit-interrupt.
// // nolint:gocognit, unparam
// func (w *worker) commitTransactionsWithDelay(env *environment, txs *types.TransactionsByPriceAndNonce, interrupt *int32, interruptCtx context.Context) bool {
// gasLimit := env.header.GasLimit
// if env.gasPool == nil {
// env.gasPool = new(core.GasPool).AddGas(gasLimit)
// }
// var coalescedLogs []*types.Log
// initialGasLimit := env.gasPool.Gas()
// initialTxs := txs.GetTxs()
// var breakCause string
// defer func() {
// log.OnDebug(func(lg log.Logging) {
// lg("commitTransactions-stats",
// "initialTxsCount", initialTxs,
// "initialGasLimit", initialGasLimit,
// "resultTxsCount", txs.GetTxs(),
// "resultGapPool", env.gasPool.Gas(),
// "exitCause", breakCause)
// })
// }()
// mainloop:
// for {
// if interruptCtx != nil {
// // case of interrupting by timeout
// select {
// case <-interruptCtx.Done():
// log.Warn("Interrupt")
// break mainloop
// default:
// }
// }
// // In the following three cases, we will interrupt the execution of the transaction.
// // (1) new head block event arrival, the interrupt signal is 1
// // (2) worker start or restart, the interrupt signal is 1
// // (3) worker recreate the sealing block with any newly arrived transactions, the interrupt signal is 2.
// // For the first two cases, the semi-finished work will be discarded.
// // For the third case, the semi-finished work will be submitted to the consensus engine.
// if interrupt != nil && atomic.LoadInt32(interrupt) != commitInterruptNone {
// // Notify resubmit loop to increase resubmitting interval due to too frequent commits.
// if atomic.LoadInt32(interrupt) == commitInterruptResubmit {
// ratio := float64(gasLimit-env.gasPool.Gas()) / float64(gasLimit)
// if ratio < 0.1 {
// // nolint:goconst
// ratio = 0.1
// }
// w.resubmitAdjustCh <- &intervalAdjust{
// ratio: ratio,
// inc: true,
// }
// }
// // nolint:goconst
// breakCause = "interrupt"
// return atomic.LoadInt32(interrupt) == commitInterruptNewHead
// }
// // If we don't have enough gas for any further transactions then we're done
// if env.gasPool.Gas() < params.TxGas {
// log.Trace("Not enough gas for further transactions", "have", env.gasPool, "want", params.TxGas)
// // nolint:goconst
// breakCause = "Not enough gas for further transactions"
// break
// }
// // Retrieve the next transaction and abort if all done
// tx := txs.Peek()
// if tx == nil {
// // nolint:goconst
// breakCause = "all transactions has been included"
// break
// }
// // Error may be ignored here. The error has already been checked
// // during transaction acceptance is the transaction pool.
// //
// // We use the eip155 signer regardless of the current hf.
// from, _ := types.Sender(env.signer, tx)
// // Check whether the tx is replay protected. If we're not in the EIP155 hf
// // phase, start ignoring the sender until we do.
// if tx.Protected() && !w.chainConfig.IsEIP155(env.header.Number) {
// log.Trace("Ignoring reply protected transaction", "hash", tx.Hash(), "eip155", w.chainConfig.EIP155Block)
// txs.Pop()
// continue
// }
// // Start executing the transaction
// env.state.SetTxContext(tx.Hash(), env.tcount)
// var start time.Time
// log.OnDebug(func(log.Logging) {
// start = time.Now()
// })
// logs, err := w.commitTransaction(env, tx, interruptCtx)
// if interruptCtx != nil {
// if delay := interruptCtx.Value(vm.InterruptCtxDelayKey); delay != nil {
// // nolint : durationcheck
// time.Sleep(time.Duration(delay.(uint)) * time.Millisecond)
// }
// }
// switch {
// case errors.Is(err, core.ErrGasLimitReached):
// // Pop the current out-of-gas transaction without shifting in the next from the account
// log.Trace("Gas limit exceeded for current block", "sender", from)
// txs.Pop()
// case errors.Is(err, core.ErrNonceTooLow):
// // New head notification data race between the transaction pool and miner, shift
// log.Trace("Skipping transaction with low nonce", "sender", from, "nonce", tx.Nonce())
// txs.Shift()
// case errors.Is(err, core.ErrNonceTooHigh):
// // Reorg notification data race between the transaction pool and miner, skip account =
// log.Trace("Skipping account with hight nonce", "sender", from, "nonce", tx.Nonce())
// txs.Pop()
// case errors.Is(err, nil):
// // Everything ok, collect the logs and shift in the next transaction from the same account
// coalescedLogs = append(coalescedLogs, logs...)
// env.tcount++
// txs.Shift()
// log.OnDebug(func(lg log.Logging) {
// lg("Committed new tx", "tx hash", tx.Hash(), "from", from, "to", tx.To(), "nonce", tx.Nonce(), "gas", tx.Gas(), "gasPrice", tx.GasPrice(), "value", tx.Value(), "time spent", time.Since(start))
// })
// case errors.Is(err, core.ErrTxTypeNotSupported):
// // Pop the unsupported transaction without shifting in the next from the account
// log.Trace("Skipping unsupported transaction type", "sender", from, "type", tx.Type())
// txs.Pop()
// default:
// // Strange error, discard the transaction and get the next in line (note, the
// // nonce-too-high clause will prevent us from executing in vain).
// log.Debug("Transaction failed, account skipped", "hash", tx.Hash(), "err", err)
// txs.Shift()
// }
// }
// if !w.isRunning() && len(coalescedLogs) > 0 {
// // We don't push the pendingLogsEvent while we are sealing. The reason is that
// // when we are sealing, the worker will regenerate a sealing block every 3 seconds.
// // In order to avoid pushing the repeated pendingLog, we disable the pending log pushing.
// // make a copy, the state caches the logs and these logs get "upgraded" from pending to mined
// // logs by filling in the block hash when the block was mined by the local miner. This can
// // cause a race condition if a log was "upgraded" before the PendingLogsEvent is processed.
// cpy := make([]*types.Log, len(coalescedLogs))
// for i, l := range coalescedLogs {
// cpy[i] = new(types.Log)
// *cpy[i] = *l
// }
// w.pendingLogsFeed.Send(cpy)
// }
// // Notify resubmit loop to decrease resubmitting interval if current interval is larger
// // than the user-specified one.
// if interrupt != nil {
// w.resubmitAdjustCh <- &intervalAdjust{inc: false}
// }
// return false
// }