Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eth, miner: fix enforcing the minimum miner tip #28933

Merged
merged 3 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion accounts/abi/bind/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func TestWaitDeployed(t *testing.T) {

// Create the transaction
head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))

tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey)
Expand Down
1 change: 1 addition & 0 deletions eth/api_miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func (api *MinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
api.e.lock.Unlock()

api.e.txPool.SetGasTip((*big.Int)(&gasPrice))
api.e.Miner().SetGasTip((*big.Int)(&gasPrice))
return true
}

Expand Down
4 changes: 2 additions & 2 deletions ethclient/simulated/backend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) {

// create a signed transaction to send
head, _ := client.HeaderByNumber(context.Background(), nil) // Should be child's, good enough
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
addr := crypto.PubkeyToAddress(key.PublicKey)
chainid, _ := client.ChainID(context.Background())
nonce, err := client.PendingNonceAt(context.Background(), addr)
Expand All @@ -62,7 +62,7 @@ func newTx(sim *Backend, key *ecdsa.PrivateKey) (*types.Transaction, error) {
tx := types.NewTx(&types.DynamicFeeTx{
ChainID: chainid,
Nonce: nonce,
GasTipCap: big.NewInt(1),
GasTipCap: big.NewInt(params.GWei),
GasFeeCap: gasPrice,
Gas: 21000,
To: &addr,
Expand Down
16 changes: 16 additions & 0 deletions ethclient/simulated/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package simulated

import (
"math/big"

"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/node"
)
Expand All @@ -37,3 +39,17 @@ func WithCallGasLimit(gaslimit uint64) func(nodeConf *node.Config, ethConf *ethc
ethConf.RPCGasCap = gaslimit
}
}

// WithMinerMinTip configures the simulated backend to require a specific minimum
// gas tip for a transaction to be included.
//
// 0 is not possible as a live Geth node would reject that due to DoS protection,
// so the simulated backend will replicate that behavior for consisntency.
func WithMinerMinTip(tip *big.Int) func(nodeConf *node.Config, ethConf *ethconfig.Config) {
if tip == nil || tip.Cmp(new(big.Int)) <= 0 {
panic("invalid miner minimum tip")
}
return func(nodeConf *node.Config, ethConf *ethconfig.Config) {
ethConf.Miner.GasPrice = tip
}
}
5 changes: 5 additions & 0 deletions miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,11 @@ func (miner *Miner) SetExtra(extra []byte) error {
return nil
}

func (miner *Miner) SetGasTip(tip *big.Int) error {
miner.worker.setGasTip(tip)
return nil
}

// SetRecommitInterval sets the interval for sealing work resubmitting.
func (miner *Miner) SetRecommitInterval(interval time.Duration) {
miner.worker.setRecommitInterval(interval)
Expand Down
6 changes: 3 additions & 3 deletions miner/ordering.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,11 +119,11 @@ func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address]
}

// Peek returns the next transaction by price.
func (t *transactionsByPriceAndNonce) Peek() *txpool.LazyTransaction {
func (t *transactionsByPriceAndNonce) Peek() (*txpool.LazyTransaction, *big.Int) {
if len(t.heads) == 0 {
return nil
return nil, nil
}
return t.heads[0].tx
return t.heads[0].tx, t.heads[0].fees
}

// Shift replaces the current best head with the next one from the same account.
Expand Down
4 changes: 2 additions & 2 deletions miner/ordering_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
txset := newTransactionsByPriceAndNonce(signer, groups, baseFee)

txs := types.Transactions{}
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
for tx, _ := txset.Peek(); tx != nil; tx, _ = txset.Peek() {
txs = append(txs, tx.Tx)
txset.Shift()
}
Expand Down Expand Up @@ -170,7 +170,7 @@ func TestTransactionTimeSort(t *testing.T) {
txset := newTransactionsByPriceAndNonce(signer, groups, nil)

txs := types.Transactions{}
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
for tx, _ := txset.Peek(); tx != nil; tx, _ = txset.Peek() {
txs = append(txs, tx.Tx)
txset.Shift()
}
Expand Down
28 changes: 23 additions & 5 deletions miner/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ type worker struct {
mu sync.RWMutex // The lock used to protect the coinbase and extra fields
coinbase common.Address
extra []byte
tip *big.Int // Minimum tip needed for non-local transaction to include them

pendingMu sync.RWMutex
pendingTasks map[common.Hash]*task
Expand Down Expand Up @@ -251,6 +252,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
isLocalBlock: isLocalBlock,
coinbase: config.Etherbase,
extra: config.ExtraData,
tip: config.GasPrice,
pendingTasks: make(map[common.Hash]*task),
txsCh: make(chan core.NewTxsEvent, txChanSize),
chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize),
Expand Down Expand Up @@ -327,6 +329,13 @@ func (w *worker) setExtra(extra []byte) {
w.extra = extra
}

// setGasTip sets the minimum miner tip needed to include a non-local transaction.
func (w *worker) setGasTip(tip *big.Int) {
w.mu.Lock()
defer w.mu.Unlock()
w.tip = tip
}

// setRecommitInterval updates the interval for miner sealing work recommitting.
func (w *worker) setRecommitInterval(interval time.Duration) {
select {
Expand Down Expand Up @@ -554,7 +563,7 @@ func (w *worker) mainLoop() {
}
txset := newTransactionsByPriceAndNonce(w.current.signer, txs, w.current.header.BaseFee)
tcount := w.current.tcount
w.commitTransactions(w.current, txset, nil)
w.commitTransactions(w.current, txset, nil, new(big.Int))

// Only update the snapshot if any new transactions were added
// to the pending block
Expand Down Expand Up @@ -792,7 +801,7 @@ func (w *worker) applyTransaction(env *environment, tx *types.Transaction) (*typ
return receipt, err
}

func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32) error {
func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAndNonce, interrupt *atomic.Int32, minTip *big.Int) error {
gasLimit := env.header.GasLimit
if env.gasPool == nil {
env.gasPool = new(core.GasPool).AddGas(gasLimit)
Expand All @@ -812,7 +821,7 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
break
}
// Retrieve the next transaction and abort if all done.
ltx := txs.Peek()
ltx, tip := txs.Peek()
if ltx == nil {
break
}
Expand All @@ -827,6 +836,11 @@ func (w *worker) commitTransactions(env *environment, txs *transactionsByPriceAn
txs.Pop()
continue
}
// If we don't receive enough tip for the next transaction, skip the account
if tip.Cmp(minTip) < 0 {
log.Trace("Not enough tip for transaction", "hash", ltx.Hash, "tip", tip, "needed", minTip)
break // If the next-best is too low, surely no better will be available
}
// Transaction seems to fit, pull it up from the pool
tx := ltx.Resolve()
if tx == nil {
Expand Down Expand Up @@ -997,15 +1011,19 @@ func (w *worker) fillTransactions(interrupt *atomic.Int32, env *environment) err
}

// Fill the block with all available pending transactions.
w.mu.RLock()
tip := w.tip
w.mu.RUnlock()

if len(localTxs) > 0 {
txs := newTransactionsByPriceAndNonce(env.signer, localTxs, env.header.BaseFee)
if err := w.commitTransactions(env, txs, interrupt); err != nil {
if err := w.commitTransactions(env, txs, interrupt, new(big.Int)); err != nil {
return err
}
}
if len(remoteTxs) > 0 {
txs := newTransactionsByPriceAndNonce(env.signer, remoteTxs, env.header.BaseFee)
if err := w.commitTransactions(env, txs, interrupt); err != nil {
if err := w.commitTransactions(env, txs, interrupt, tip); err != nil {
return err
}
}
Expand Down
Loading