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)