diff --git a/eth/backend.go b/eth/backend.go index 64e196562a4a..70e31a372e14 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -248,7 +248,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) { return nil, err } - eth.miner = miner.New(eth, &config.Miner, eth.engine) + eth.miner = miner.New(eth, config.Miner, eth.engine) eth.miner.SetExtra(makeExtraData(config.Miner.ExtraData)) eth.APIBackend = &EthAPIBackend{stack.Config().ExtRPCEnabled(), stack.Config().AllowUnprotectedTxs, eth, nil} @@ -413,7 +413,7 @@ func (s *Ethereum) SetEtherbase(etherbase common.Address) { s.miner.SetEtherbase(etherbase) } -func (s *Ethereum) IsMining() bool { return s.miner.Mining() } +func (s *Ethereum) IsMining() bool { return true } func (s *Ethereum) Miner() *miner.Miner { return s.miner } func (s *Ethereum) AccountManager() *accounts.Manager { return s.accountManager } @@ -480,7 +480,6 @@ func (s *Ethereum) Stop() error { s.bloomIndexer.Close() close(s.closeBloomHandler) s.txPool.Close() - s.miner.Close() s.blockchain.Stop() s.engine.Close() diff --git a/miner/miner.go b/miner/miner.go index 2d031a85953f..5ef0796980d0 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -21,7 +21,6 @@ import ( "fmt" "math/big" "sync" - "sync/atomic" "time" "github.com/ethereum/go-ethereum/common" @@ -48,8 +47,7 @@ type Config struct { ExtraData hexutil.Bytes `toml:",omitempty"` // Block extra data set by the miner GasCeil uint64 // Target gas ceiling for mined blocks. GasPrice *big.Int // Minimum gas price for mining a transaction - - Recommit time.Duration // The time interval for miner to re-create mining work. + Recommit time.Duration // The time interval for miner to re-create mining work. } // DefaultConfig contains default settings for miner. @@ -64,90 +62,75 @@ var DefaultConfig = Config{ Recommit: 2 * time.Second, } -// Update the pending block at most every second. -const pendingTimeout = 1 * time.Second - // Miner is the main object which takes care of submitting new work to consensus engine // and gathering the sealing result. type Miner struct { - confMu sync.RWMutex // The lock used to protect the config - config *Config - chainConfig *params.ChainConfig - engine consensus.Engine - txpool *txpool.TxPool - chain *core.BlockChain - - pendingMu sync.Mutex // The lock used to protect the pending cache - pendingCache *newPayloadResult - cacheTime time.Time - - // Feeds + confMu sync.RWMutex // The lock used to protect the config + config *Config + chainConfig *params.ChainConfig + engine consensus.Engine + txpool *txpool.TxPool + chain *core.BlockChain + pending *pending pendingLogsFeed event.Feed - - running atomic.Bool } -// New creates a new miner. -func New(eth Backend, config *Config, engine consensus.Engine) *Miner { - if config == nil { - config = &DefaultConfig - } - worker := &Miner{ - config: config, +// New creates a new miner with provided config. +func New(eth Backend, config Config, engine consensus.Engine) *Miner { + return &Miner{ + config: &config, chainConfig: eth.BlockChain().Config(), engine: engine, txpool: eth.TxPool(), chain: eth.BlockChain(), + pending: &pending{}, } - worker.running.Store(true) - return worker } -func (miner *Miner) Close() { - miner.running.Store(false) +// Pending returns the currently pending block and associated state. The returned +// values can be nil in case the pending block is not initialized +func (miner *Miner) Pending() (*types.Block, *state.StateDB) { + pending := miner.getPending() + if pending == nil { + return nil, nil + } + return pending.block, pending.stateDB.Copy() } -func (miner *Miner) Mining() bool { - return miner.running.Load() +// PendingBlockAndReceipts returns the currently pending block and corresponding receipts. +// The returned values can be nil in case the pending block is not initialized. +func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { + pending := miner.getPending() + if pending == nil { + return nil, nil + } + return pending.block, pending.receipts } -// setExtra sets the content used to initialize the block extra field. +// SetExtra sets the content used to initialize the block extra field. func (miner *Miner) SetExtra(extra []byte) error { if uint64(len(extra)) > params.MaximumExtraDataSize { return fmt.Errorf("extra exceeds max length. %d > %v", len(extra), params.MaximumExtraDataSize) } miner.confMu.Lock() - defer miner.confMu.Unlock() miner.config.ExtraData = extra + miner.confMu.Unlock() return nil } -// Pending returns the currently pending block and associated state. The returned -// values can be nil in case the pending block is not initialized -func (miner *Miner) Pending() (*types.Block, *state.StateDB) { - block := miner.pending() - return block.block, block.stateDB -} - -// PendingBlockAndReceipts returns the currently pending block and corresponding receipts. -// The returned values can be nil in case the pending block is not initialized. -func (miner *Miner) PendingBlockAndReceipts() (*types.Block, types.Receipts) { - block := miner.pending() - return block.block, block.receipts -} - +// SetEtherbase sets the address of fee recipient. func (miner *Miner) SetEtherbase(addr common.Address) { miner.confMu.Lock() - defer miner.confMu.Unlock() miner.config.Etherbase = addr + miner.confMu.Unlock() } // SetGasCeil sets the gaslimit to strive for when mining blocks post 1559. // For pre-1559 blocks, it sets the ceiling. func (miner *Miner) SetGasCeil(ceil uint64) { miner.confMu.Lock() - defer miner.confMu.Unlock() miner.config.GasCeil = ceil + miner.confMu.Unlock() } // SubscribePendingLogs starts delivering logs from pending transactions @@ -161,35 +144,30 @@ func (miner *Miner) BuildPayload(args *BuildPayloadArgs) (*Payload, error) { return miner.buildPayload(args) } -// pending returns the pending state and corresponding block. The returned -// values can be nil in case the pending block is not initialized. -func (miner *Miner) pending() *newPayloadResult { - // Read config +// getPending retrieves the pending block based on the current head block. +// The result might be nil if pending generation is failed. +func (miner *Miner) getPending() *newPayloadResult { miner.confMu.RLock() coinbase := miner.config.Etherbase miner.confMu.RUnlock() - // Lock pending block - miner.pendingMu.Lock() - defer miner.pendingMu.Unlock() - if time.Since(miner.cacheTime) < pendingTimeout && coinbase == miner.pendingCache.block.Coinbase() { - return miner.pendingCache + + header := miner.chain.CurrentHeader() + if cached := miner.pending.resolve(header, coinbase); cached != nil { + return cached } - pending := miner.generateWork(&generateParams{ + ret := miner.generateWork(&generateParams{ timestamp: uint64(time.Now().Unix()), forceTime: true, - parentHash: miner.chain.CurrentBlock().Hash(), + parentHash: header.Hash(), coinbase: coinbase, random: common.Hash{}, withdrawals: nil, beaconRoot: nil, noTxs: false, }) - if pending.err != nil { - // force subsequent calls to recreate pending block - miner.cacheTime = time.Time{} - return &newPayloadResult{} + if ret.err != nil { + return nil } - miner.pendingCache = pending - miner.cacheTime = time.Now() - return pending + miner.pending.update(header, coinbase, ret) + return ret } diff --git a/miner/miner_test.go b/miner/miner_test.go index 3b1f2b702dd8..21ae76583f26 100644 --- a/miner/miner_test.go +++ b/miner/miner_test.go @@ -168,8 +168,8 @@ func createMiner(t *testing.T) *Miner { pool := legacypool.New(testTxPoolConfig, blockchain) txpool, _ := txpool.New(new(big.Int).SetUint64(testTxPoolConfig.PriceLimit), blockchain, []txpool.SubPool{pool}) - backend := NewMockBackend(bc, txpool) // Create Miner - miner := New(backend, &config, engine) + backend := NewMockBackend(bc, txpool) + miner := New(backend, config, engine) return miner } diff --git a/miner/payload_building.go b/miner/payload_building.go index d108f2f5935e..841e71cd0d0c 100644 --- a/miner/payload_building.go +++ b/miner/payload_building.go @@ -45,7 +45,6 @@ type BuildPayloadArgs struct { // Id computes an 8-byte identifier by hashing the components of the payload arguments. func (args *BuildPayloadArgs) Id() engine.PayloadID { - // Hash hasher := sha256.New() hasher.Write(args.Parent[:]) binary.Write(hasher, binary.BigEndian, args.Timestamp) @@ -175,7 +174,7 @@ func (payload *Payload) ResolveFull() *engine.ExecutionPayloadEnvelope { } // buildPayload builds the payload according to the provided parameters. -func (w *Miner) buildPayload(args *BuildPayloadArgs) (*Payload, error) { +func (miner *Miner) buildPayload(args *BuildPayloadArgs) (*Payload, error) { // Build the initial version with no transaction included. It should be fast // enough to run. The empty payload can at least make sure there is something // to deliver for not missing slot. @@ -189,7 +188,7 @@ func (w *Miner) buildPayload(args *BuildPayloadArgs) (*Payload, error) { beaconRoot: args.BeaconRoot, noTxs: true, } - empty := w.generateWork(emptyParams) + empty := miner.generateWork(emptyParams) if empty.err != nil { return nil, empty.err } @@ -225,11 +224,11 @@ func (w *Miner) buildPayload(args *BuildPayloadArgs) (*Payload, error) { select { case <-timer.C: start := time.Now() - r := w.generateWork(fullParams) + r := miner.generateWork(fullParams) if r.err == nil { payload.update(r, time.Since(start)) } - timer.Reset(w.config.Recommit) + timer.Reset(miner.config.Recommit) case <-payload.stop: log.Info("Stopping work on payload", "id", payload.id, "reason", "delivery") return diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go index ce50a890ca38..5b767190f6b7 100644 --- a/miner/payload_building_test.go +++ b/miner/payload_building_test.go @@ -57,7 +57,7 @@ var ( pendingTxs []*types.Transaction newTxs []*types.Transaction - testConfig = &Config{ + testConfig = Config{ Recommit: time.Second, GasCeil: params.GenesisGasLimit, } diff --git a/miner/pending.go b/miner/pending.go new file mode 100644 index 000000000000..f695c8ead292 --- /dev/null +++ b/miner/pending.go @@ -0,0 +1,70 @@ +// Copyright 2024 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +package miner + +import ( + "sync" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" +) + +// pendingTTL indicates the period of time a generated pending block should +// exist to serve RPC requests before being discarded if the parent block +// has not changed yet. The value is chosen to align with the recommit interval. +const pendingTTL = 2 * time.Second + +// pending wraps a pending block with additional metadata. +type pending struct { + created time.Time + parent *types.Header + coinbase common.Address + result *newPayloadResult + lock sync.Mutex +} + +// resolve retrieves the cached pending result if it's available. Nothing will be +// returned if the parent/coinbase is not matched or the result is already too old. +// +// Note, don't modify the returned payload result. +func (p *pending) resolve(parent *types.Header, coinbase common.Address) *newPayloadResult { + p.lock.Lock() + defer p.lock.Unlock() + + if p.result == nil || p.parent == nil { + return nil + } + if parent.Hash() != p.parent.Hash() || p.coinbase != coinbase { + return nil + } + if time.Since(p.created) > pendingTTL { + return nil + } + return p.result +} + +// update refreshes the cached pending block with newly created one. +func (p *pending) update(parent *types.Header, coinbase common.Address, result *newPayloadResult) { + p.lock.Lock() + defer p.lock.Unlock() + + p.parent = parent + p.coinbase = coinbase + p.result = result + p.created = time.Now() +} diff --git a/miner/stress/main.go b/miner/stress/main.go new file mode 100644 index 000000000000..3b41e7b33a55 --- /dev/null +++ b/miner/stress/main.go @@ -0,0 +1,224 @@ +// Copyright 2018 The go-ethereum Authors +// This file is part of the go-ethereum library. +// +// The go-ethereum library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The go-ethereum library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the go-ethereum library. If not, see . + +// This file contains a miner stress test based on the Clique consensus engine. +package main + +import ( + "bytes" + "crypto/ecdsa" + "math/big" + "math/rand" + "os" + "os/signal" + "time" + + "github.com/ethereum/go-ethereum/accounts/keystore" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/fdlimit" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/txpool/legacypool" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/eth" + "github.com/ethereum/go-ethereum/eth/catalyst" + "github.com/ethereum/go-ethereum/eth/downloader" + "github.com/ethereum/go-ethereum/eth/ethconfig" + "github.com/ethereum/go-ethereum/log" + "github.com/ethereum/go-ethereum/miner" + "github.com/ethereum/go-ethereum/node" + "github.com/ethereum/go-ethereum/p2p" + "github.com/ethereum/go-ethereum/p2p/enode" + "github.com/ethereum/go-ethereum/params" +) + +func main() { + log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelInfo, true))) + fdlimit.Raise(2048) + + // Generate a batch of accounts to seal and fund with + faucets := make([]*ecdsa.PrivateKey, 128) + for i := 0; i < len(faucets); i++ { + faucets[i], _ = crypto.GenerateKey() + } + sealers := make([]*ecdsa.PrivateKey, 1) + for i := 0; i < len(sealers); i++ { + sealers[i], _ = crypto.GenerateKey() + } + // Create a Clique network based off of the Sepolia config + genesis := makeGenesis(faucets, sealers) + + // Handle interrupts. + interruptCh := make(chan os.Signal, 5) + signal.Notify(interruptCh, os.Interrupt) + + var ( + stacks []*node.Node + nodes []*eth.Ethereum + enodes []*enode.Node + beacons []*catalyst.SimulatedBeacon + ) + for _, sealer := range sealers { + // Start the node and wait until it's up + stack, ethBackend, err := makeSealer(genesis) + if err != nil { + panic(err) + } + defer stack.Close() + + for stack.Server().NodeInfo().Ports.Listener == 0 { + time.Sleep(250 * time.Millisecond) + } + // Connect the node to all the previous ones + for _, n := range enodes { + stack.Server().AddPeer(n) + } + // Start tracking the node and its enode + stacks = append(stacks, stack) + nodes = append(nodes, ethBackend) + enodes = append(enodes, stack.Server().Self()) + + // Inject the signer key and start sealing with it + ks := keystore.NewKeyStore(stack.KeyStoreDir(), keystore.LightScryptN, keystore.LightScryptP) + signer, err := ks.ImportECDSA(sealer, "") + if err != nil { + panic(err) + } + if err := ks.Unlock(signer, ""); err != nil { + panic(err) + } + stack.AccountManager().AddBackend(ks) + + beacon, err := catalyst.NewSimulatedBeacon(3, ethBackend) + if err != nil { + panic(err) + } + beacon.Start() + + beacons = append(beacons, beacon) + } + + // Iterate over all the nodes and start signing on them + time.Sleep(3 * time.Second) + + // Start injecting transactions from the faucet like crazy + nonces := make([]uint64, len(faucets)) + for { + // Stop when interrupted. + select { + case <-interruptCh: + for _, node := range stacks { + node.Close() + } + return + default: + } + + // Pick a random signer node + index := rand.Intn(len(faucets)) + backend := nodes[index%len(nodes)] + + // Create a self transaction and inject into the pool + tx, err := types.SignTx(types.NewTransaction(nonces[index], crypto.PubkeyToAddress(faucets[index].PublicKey), new(big.Int), 21000, big.NewInt(100000000000), nil), types.HomesteadSigner{}, faucets[index]) + if err != nil { + panic(err) + } + errs := backend.TxPool().Add([]*types.Transaction{tx}, true, false) + for _, err := range errs { + if err != nil { + panic(err) + } + } + nonces[index]++ + + // Wait if we're too saturated + if pend, _ := backend.TxPool().Stats(); pend > 2048 { + time.Sleep(100 * time.Millisecond) + } + } +} + +// makeGenesis creates a custom Clique genesis block based on some pre-defined +// signer and faucet accounts. +func makeGenesis(faucets []*ecdsa.PrivateKey, sealers []*ecdsa.PrivateKey) *core.Genesis { + // Create a Clique network based off of the Sepolia config + genesis := core.DeveloperGenesisBlock(25000000, nil) + genesis.Alloc = core.GenesisAlloc{} + for _, faucet := range faucets { + genesis.Alloc[crypto.PubkeyToAddress(faucet.PublicKey)] = core.GenesisAccount{ + Balance: new(big.Int).Exp(big.NewInt(2), big.NewInt(128), nil), + } + } + // Sort the signers and embed into the extra-data section + signers := make([]common.Address, len(sealers)) + for i, sealer := range sealers { + signers[i] = crypto.PubkeyToAddress(sealer.PublicKey) + } + for i := 0; i < len(signers); i++ { + for j := i + 1; j < len(signers); j++ { + if bytes.Compare(signers[i][:], signers[j][:]) > 0 { + signers[i], signers[j] = signers[j], signers[i] + } + } + } + genesis.ExtraData = make([]byte, 32+len(signers)*common.AddressLength+65) + for i, signer := range signers { + copy(genesis.ExtraData[32+i*common.AddressLength:], signer[:]) + } + // Return the genesis block for initialization + return genesis +} + +func makeSealer(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) { + // Define the basic configurations for the Ethereum node + datadir, _ := os.MkdirTemp("", "") + + config := &node.Config{ + Name: "geth", + Version: params.Version, + DataDir: datadir, + P2P: p2p.Config{ + ListenAddr: "0.0.0.0:0", + NoDiscovery: true, + MaxPeers: 25, + }, + } + // Start the node and configure a full Ethereum node on it + stack, err := node.New(config) + if err != nil { + return nil, nil, err + } + // Create and register the backend + ethBackend, err := eth.New(stack, ðconfig.Config{ + Genesis: genesis, + NetworkId: genesis.Config.ChainID.Uint64(), + SyncMode: downloader.FullSync, + DatabaseCache: 256, + DatabaseHandles: 256, + TxPool: legacypool.DefaultConfig, + GPO: ethconfig.Defaults.GPO, + Miner: miner.Config{ + GasCeil: genesis.GasLimit * 11 / 10, + GasPrice: big.NewInt(1), + Recommit: time.Second, + }, + }) + if err != nil { + return nil, nil, err + } + err = stack.Start() + return stack, ethBackend, err +} diff --git a/miner/worker.go b/miner/worker.go index 61660cc634bd..651110136e7e 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -54,6 +54,7 @@ type environment struct { txs []*types.Transaction receipts []*types.Receipt sidecars []*types.BlobTxSidecar + logs [][]*types.Log blobs int } @@ -72,6 +73,7 @@ type newPayloadResult struct { sidecars []*types.BlobTxSidecar // collected blobs of blob transactions stateDB *state.StateDB // StateDB after executing the transactions receipts []*types.Receipt // Receipts collected during construction + logs [][]*types.Log // EVM logs collected during execution } // generateParams wraps various of settings for generating sealing task. @@ -81,7 +83,7 @@ type generateParams struct { parentHash common.Hash // Parent block hash, empty means the latest chain head coinbase common.Address // The fee recipient address for including transaction random common.Hash // The randomness generated by beacon chain, empty before the merge - withdrawals types.Withdrawals // List of withdrawals to include in block. + withdrawals types.Withdrawals // List of withdrawals to include in block (shanghai field) beaconRoot *common.Hash // The beacon root (cancun field). noTxs bool // Flag whether an empty block without any transaction is expected } @@ -92,7 +94,6 @@ func (miner *Miner) generateWork(params *generateParams) *newPayloadResult { if err != nil { return &newPayloadResult{err: err} } - if !params.noTxs { interrupt := new(atomic.Int32) timer := time.AfterFunc(miner.config.Recommit, func() { @@ -115,6 +116,7 @@ func (miner *Miner) generateWork(params *generateParams) *newPayloadResult { sidecars: work.sidecars, stateDB: work.state, receipts: work.receipts, + logs: work.logs, } } @@ -209,33 +211,33 @@ func (miner *Miner) makeEnv(parent *types.Header, header *types.Header, coinbase if err != nil { return nil, err } - // Note the passed coinbase may be different with header.Coinbase. - env := &environment{ + return &environment{ signer: types.MakeSigner(miner.chainConfig, header.Number, header.Time), state: state, coinbase: coinbase, header: header, - } - // Keep track of transactions which return errors so they can be removed - env.tcount = 0 - return env, nil + }, nil } -func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { +func (miner *Miner) commitTransaction(env *environment, tx *types.Transaction) error { if tx.Type() == types.BlobTxType { return miner.commitBlobTransaction(env, tx) } receipt, err := miner.applyTransaction(env, tx) if err != nil { - return nil, err + return err } env.txs = append(env.txs, tx) env.receipts = append(env.receipts, receipt) - return receipt.Logs, nil + if len(receipt.Logs) > 0 { + env.logs = append(env.logs, receipt.Logs) + } + env.tcount++ + return nil } -func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transaction) ([]*types.Log, error) { +func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transaction) error { sc := tx.BlobTxSidecar() if sc == nil { panic("blob transaction without blobs in miner") @@ -245,18 +247,22 @@ func (miner *Miner) commitBlobTransaction(env *environment, tx *types.Transactio // and not during execution. This means core.ApplyTransaction will not return an error if the // tx has too many blobs. So we have to explicitly check it here. if (env.blobs+len(sc.Blobs))*params.BlobTxBlobGasPerBlob > params.MaxBlobGasPerBlock { - return nil, errors.New("max data blobs reached") + return errors.New("max data blobs reached") } receipt, err := miner.applyTransaction(env, tx) if err != nil { - return nil, err + return err } env.txs = append(env.txs, tx.WithoutBlobTxSidecar()) env.receipts = append(env.receipts, receipt) + if len(receipt.Logs) > 0 { + env.logs = append(env.logs, receipt.Logs) + } env.sidecars = append(env.sidecars, sc) env.blobs += len(sc.Blobs) *env.header.BlobGasUsed += receipt.BlobGasUsed - return receipt.Logs, nil + env.tcount++ + return nil } // applyTransaction runs the transaction. If execution fails, state and gas pool are reverted. @@ -278,8 +284,6 @@ func (miner *Miner) commitTransactions(env *environment, txs *transactionsByPric if env.gasPool == nil { env.gasPool = new(core.GasPool).AddGas(gasLimit) } - var coalescedLogs []*types.Log - for { // Check interruption signal and abort building if it's fired. if interrupt != nil { @@ -329,7 +333,7 @@ func (miner *Miner) commitTransactions(env *environment, txs *transactionsByPric // Start executing the transaction env.state.SetTxContext(tx.Hash(), env.tcount) - logs, err := miner.commitTransaction(env, tx) + err := miner.commitTransaction(env, tx) switch { case errors.Is(err, core.ErrNonceTooLow): // New head notification data race between the transaction pool and miner, shift @@ -338,8 +342,6 @@ func (miner *Miner) commitTransactions(env *environment, txs *transactionsByPric 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() default: @@ -349,21 +351,6 @@ func (miner *Miner) commitTransactions(env *environment, txs *transactionsByPric txs.Pop() } } - if !miner.Mining() && 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 - } - miner.pendingLogsFeed.Send(cpy) - } return nil } @@ -381,7 +368,6 @@ func (miner *Miner) fillTransactions(interrupt *atomic.Int32, env *environment) localTxs[account] = txs } } - // Fill the block with all available pending transactions. if len(localTxs) > 0 { txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)