From 0737df6c7d069c12c53190911d1cfa064c28e1b3 Mon Sep 17 00:00:00 2001 From: wit Date: Thu, 18 Dec 2025 15:26:00 +0800 Subject: [PATCH] core/txpool: add validation for func SetGasPrice --- core/txpool/txpool.go | 11 +++ core/txpool/txpool_setgastip_test.go | 112 +++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 core/txpool/txpool_setgastip_test.go diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index a314a83f1b..b373828b49 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -257,6 +257,17 @@ func (p *TxPool) loop(head *types.Header) { // SetGasTip updates the minimum gas tip required by the transaction pool for a // new transaction, and drops all transactions below this threshold. func (p *TxPool) SetGasTip(tip *big.Int) { + if tip == nil { + log.Warn("SetGasTip called with nil tip") + return + } + if tip.Sign() <= 0 { + log.Warn("SetGasTip called with non-positive tip", "tip", tip) + return + } + if tip.Cmp(big.NewInt(1_000_000_000_000_000)) > 0 { + log.Warn("SetGasTip called with very high tip", "tip", tip) + } for _, subpool := range p.subpools { subpool.SetGasTip(tip) } diff --git a/core/txpool/txpool_setgastip_test.go b/core/txpool/txpool_setgastip_test.go new file mode 100644 index 0000000000..f97d622871 --- /dev/null +++ b/core/txpool/txpool_setgastip_test.go @@ -0,0 +1,112 @@ +package txpool + +import ( + "math/big" + "sync" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// mockSubPool is a lightweight test double implementing the SubPool interface +// used to observe SetGasTip invocations. +type mockSubPool struct { + mu sync.Mutex + callCount int + lastTip *big.Int +} + +func (m *mockSubPool) Filter(tx *types.Transaction) bool { return false } +func (m *mockSubPool) FilterType(kind byte) bool { return false } +func (m *mockSubPool) Init(gasTip uint64, head *types.Header, reserver Reserver) error { + return nil +} +func (m *mockSubPool) Close() error { return nil } +func (m *mockSubPool) Reset(oldHead, newHead *types.Header) {} +func (m *mockSubPool) SetGasTip(tip *big.Int) { + m.mu.Lock() + defer m.mu.Unlock() + m.callCount++ + if tip == nil { + m.lastTip = nil + return + } + // copy value + m.lastTip = new(big.Int).Set(tip) +} +func (m *mockSubPool) Has(hash common.Hash) bool { return false } +func (m *mockSubPool) Get(hash common.Hash) *types.Transaction { return nil } +func (m *mockSubPool) GetRLP(hash common.Hash) []byte { return nil } +func (m *mockSubPool) GetMetadata(hash common.Hash) *TxMetadata { return nil } +func (m *mockSubPool) ValidateTxBasics(tx *types.Transaction) error { return nil } +func (m *mockSubPool) Add(txs []*types.Transaction, sync bool) []error { return nil } +func (m *mockSubPool) Pending(filter PendingFilter) map[common.Address][]*LazyTransaction { + return nil +} +func (m *mockSubPool) SubscribeTransactions(ch chan<- core.NewTxsEvent, reorgs bool) event.Subscription { + return nil +} +func (m *mockSubPool) Nonce(addr common.Address) uint64 { return 0 } +func (m *mockSubPool) Stats() (int, int) { return 0, 0 } +func (m *mockSubPool) Content() (map[common.Address][]*types.Transaction, map[common.Address][]*types.Transaction) { + return nil, nil +} +func (m *mockSubPool) ContentFrom(addr common.Address) ([]*types.Transaction, []*types.Transaction) { + return nil, nil +} +func (m *mockSubPool) Status(hash common.Hash) TxStatus { return TxStatusUnknown } +func (m *mockSubPool) Clear() {} + +func TestSetGasTip(t *testing.T) { + tests := []struct { + name string + tip *big.Int + expectInvokes int + }{ + {"nil tip", nil, 0}, + {"zero tip", big.NewInt(0), 0}, + {"negative tip", big.NewInt(-1), 0}, + {"very high tip", big.NewInt(1_000_000_000_000_001), 2}, + {"valid tip", big.NewInt(5), 2}, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + m1 := &mockSubPool{} + m2 := &mockSubPool{} + p := &TxPool{ + subpools: []SubPool{m1, m2}, + } + // call SetGasTip under test + p.SetGasTip(tc.tip) + + // verify invocation counts + m1.mu.Lock() + got1 := m1.callCount + m1.mu.Unlock() + m2.mu.Lock() + got2 := m2.callCount + m2.mu.Unlock() + + if got1+got2 != tc.expectInvokes { + t.Fatalf("unexpected total SetGasTip invocations: got %d want %d", got1+got2, tc.expectInvokes) + } + + // when invoked, ensure the lastTip equals the passed tip (for non-nil) + if tc.tip != nil && tc.expectInvokes > 0 { + // pick one mock to assert + m1.mu.Lock() + if m1.lastTip == nil { + t.Fatalf("expected lastTip set on subpool but was nil") + } + if m1.lastTip.Cmp(tc.tip) != 0 { + t.Fatalf("lastTip mismatch: got %v want %v", m1.lastTip, tc.tip) + } + m1.mu.Unlock() + } + }) + } +}