feat(eth,miner): wire miner gas tip updates from RPC #28933 (#2136)

Synchronize miner gas tip updates across subsystems when RPC updates gas price.

- update eth miner API to apply gas tip changes to both txpool and miner
- add miner/worker SetGasTip path and initialize worker tip from config
- adjust bind util test to use params.GWei over base fee

Ref: #28933
This commit is contained in:
Daniel Liu 2026-03-11 09:56:47 +08:00 committed by GitHub
parent de142957b3
commit 338d1b4632
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 83 additions and 8 deletions

View file

@ -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)

View file

@ -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 {
api.e.lock.Lock()
api.e.gasPrice = (*big.Int)(&gasPrice)
api.e.lock.Unlock()
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
}
err := api.e.txPool.SetGasTip((*big.Int)(&gasPrice))
return err == nil
api.e.lock.Lock()
api.e.gasPrice = new(big.Int).Set(tip)
api.e.lock.Unlock()
return true
}
// SetEtherbase sets the etherbase of the miner

View file

@ -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()

View file

@ -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) {

View file

@ -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)
}
}