package miner // TODO - Fix this - Arpit // 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/core/txpool/legacypool" // "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 = legacypool.DefaultConfig // testTxPoolConfig.Journal = "" // ethashChainConfig = new(params.ChainConfig) // *ethashChainConfig = *params.TestChainConfig // cliqueChainConfig = new(params.ChainConfig) // *cliqueChainConfig = *params.TestChainConfig // cliqueChainConfig.Clique = ¶ms.CliqueConfig{ // Period: 10, // Epoch: 30000, // } // TestBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) // 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 // } // 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, // 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 // }