diff --git a/accounts/abi/bind/v2/util_test.go b/accounts/abi/bind/v2/util_test.go index 3318e846ba71..07d246c806da 100644 --- a/accounts/abi/bind/v2/util_test.go +++ b/accounts/abi/bind/v2/util_test.go @@ -69,7 +69,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.HomesteadSigner{}, testKey) diff --git a/eth/api_miner.go b/eth/api_miner.go index 2213058a9033..d7c77d9589a0 100644 --- a/eth/api_miner.go +++ b/eth/api_miner.go @@ -94,12 +94,18 @@ func (api *MinerAPI) SetExtra(extra string) (bool, error) { // SetGasPrice sets the minimum accepted gas price for the miner. func (api *MinerAPI) SetGasPrice(gasPrice hexutil.Big) bool { + tip := (*big.Int)(&gasPrice) + if err := api.e.txPool.SetGasTip(tip); err != nil { + return false + } + if err := api.e.Miner().SetGasTip(tip); err != nil { + return false + } + api.e.lock.Lock() - api.e.gasPrice = (*big.Int)(&gasPrice) + api.e.gasPrice = new(big.Int).Set(tip) api.e.lock.Unlock() - - err := api.e.txPool.SetGasTip((*big.Int)(&gasPrice)) - return err == nil + return true } // SetEtherbase sets the etherbase of the miner diff --git a/miner/miner.go b/miner/miner.go index d8a18d937a00..b6fcbe8f42d3 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -173,6 +173,10 @@ func (m *Miner) SetExtra(extra []byte) error { return nil } +func (miner *Miner) SetGasTip(tip *big.Int) error { + return miner.worker.setGasTip(tip) +} + // Pending returns the currently pending block and associated state. func (m *Miner) Pending() (*types.Block, *state.StateDB) { return m.worker.pending() diff --git a/miner/worker.go b/miner/worker.go index 1153f3e1792a..cce840f7a351 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -70,6 +70,7 @@ var ( blockCommitTimer = metrics.NewRegisteredTimer("miner/time/commit", nil) blockFinalizeTimer = metrics.NewRegisteredTimer("miner/time/finalize", nil) blockTotalTimer = metrics.NewRegisteredTimer("miner/time/total", nil) + maxGasTip = big.NewInt(1000 * params.GWei) ) // Agent can register themself with the worker @@ -117,8 +118,6 @@ type worker struct { chainConfig *params.ChainConfig engine consensus.Engine - mu sync.Mutex - // Feeds pendingLogsFeed event.Feed @@ -142,8 +141,10 @@ type worker struct { proc core.Validator chainDb ethdb.Database + mu sync.Mutex coinbase common.Address extra []byte + tip *big.Int // Minimum tip needed for non-local transaction to include them snapshotMu sync.RWMutex // The lock used to protect the block snapshot and state snapshot snapshotBlock *types.Block @@ -174,6 +175,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus mux: mux, coinbase: config.Etherbase, extra: config.ExtraData, + tip: config.GasPrice, txsCh: make(chan core.NewTxsEvent, txChanSize), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), @@ -214,6 +216,27 @@ 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) error { + w.mu.Lock() + defer w.mu.Unlock() + + if tip == nil { + return errors.New("reject nil gas tip") + } + if tip.Sign() < 0 { + return fmt.Errorf("reject negative gas tip: %v", tip) + } + if tip.Cmp(maxGasTip) > 0 { + return fmt.Errorf("reject too high gas tip: %v, maximum: %v", tip, maxGasTip) + } + + // Copy the value to avoid external mutation through shared pointers. + w.tip = new(big.Int).Set(tip) + log.Info("Worker tip updated", "tip", w.tip) + return nil +} + // pending returns the pending state and corresponding block. The returned // values can be nil in case the pending block is not initialized. func (w *worker) pending() (*types.Block, *state.StateDB) { diff --git a/miner/worker_test.go b/miner/worker_test.go index e272c532b4fd..7df6920d30a3 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -130,3 +130,45 @@ func TestWorkerCheckPreCommitXDPoSMismatch(t *testing.T) { t.Fatalf("expected genesis parent, got number %v", parent.Number()) } } + +func TestWorkerSetGasTipValidation(t *testing.T) { + w := &worker{tip: big.NewInt(1)} + old := new(big.Int).Set(w.tip) + + tests := []struct { + name string + tip *big.Int + }{ + {name: "nil", tip: nil}, + {name: "negative", tip: big.NewInt(-1)}, + {name: "too high", tip: new(big.Int).Add(maxGasTip, big.NewInt(1))}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if err := w.setGasTip(tc.tip); err == nil { + t.Fatalf("expected error for %s tip", tc.name) + } + if w.tip.Cmp(old) != 0 { + t.Fatalf("tip changed on invalid input: have %v want %v", w.tip, old) + } + }) + } +} + +func TestWorkerSetGasTipCopiesValue(t *testing.T) { + w := &worker{} + input := big.NewInt(2 * params.GWei) + + if err := w.setGasTip(input); err != nil { + t.Fatalf("setGasTip failed: %v", err) + } + if w.tip == input { + t.Fatal("worker tip shares pointer with input") + } + + input.Add(input, big.NewInt(1)) + if w.tip.Cmp(big.NewInt(2*params.GWei)) != 0 { + t.Fatalf("worker tip mutated via input pointer: have %v", w.tip) + } +}