@@ -21,6 +21,7 @@ import (
2121 "encoding/json"
2222 "errors"
2323 "fmt"
24+ "math"
2425 "math/big"
2526 "time"
2627
@@ -168,14 +169,47 @@ func (m *simChainHeadReader) GetVerifiedBlockByHash(hash common.Hash) *types.Hea
168169 return m .Backend .Chain ().GetVerifiedBlockByHash (hash )
169170}
170171
172+ // gasBudget tracks the remaining gas allowed across all simulated blocks.
173+ // It enforces the RPC-level gas cap to prevent DoS.
174+ type gasBudget struct {
175+ remaining uint64
176+ }
177+
178+ // newGasBudget creates a gas budget with the given cap.
179+ // A cap of 0 is treated as unlimited.
180+ func newGasBudget (cap uint64 ) * gasBudget {
181+ if cap == 0 {
182+ cap = math .MaxUint64
183+ }
184+ return & gasBudget {remaining : cap }
185+ }
186+
187+ // cap returns the given gas value clamped to the remaining budget.
188+ func (b * gasBudget ) cap (gas uint64 ) uint64 {
189+ if gas > b .remaining {
190+ return b .remaining
191+ }
192+ return gas
193+ }
194+
195+ // consume deducts the given amount from the budget.
196+ // Returns an error if the amount exceeds the remaining budget.
197+ func (b * gasBudget ) consume (amount uint64 ) error {
198+ if amount > b .remaining {
199+ return fmt .Errorf ("RPC gas cap exhausted: need %d, remaining %d" , amount , b .remaining )
200+ }
201+ b .remaining -= amount
202+ return nil
203+ }
204+
171205// simulator is a stateful object that simulates a series of blocks.
172206// it is not safe for concurrent use.
173207type simulator struct {
174208 b Backend
175209 state * state.StateDB
176210 base * types.Header
177211 chainConfig * params.ChainConfig
178- gp * core. GasPool
212+ budget * gasBudget
179213 traceTransfers bool
180214 validate bool
181215 fullTx bool
@@ -261,6 +295,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
261295 }
262296 var (
263297 gasUsed , blobGasUsed uint64
298+ gp = new (core.GasPool ).AddGas (blockContext .GasLimit )
264299 txes = make ([]* types.Transaction , len (block .Calls ))
265300 callResults = make ([]simCallResult , len (block .Calls ))
266301 receipts = make ([]* types.Receipt , len (block .Calls ))
@@ -309,7 +344,7 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
309344 sim .state .SetTxContext (txHash , i )
310345 // EoA check is always skipped, even in validation mode.
311346 msg := call .ToMessage (header .BaseFee , ! sim .validate )
312- result , err := applyMessageWithEVM (ctx , evm , msg , timeout , sim . gp )
347+ result , err := applyMessageWithEVM (ctx , evm , msg , timeout , gp )
313348 if err != nil {
314349 txErr := txValidationError (err )
315350 return nil , nil , nil , txErr
@@ -324,6 +359,13 @@ func (sim *simulator) processBlock(ctx context.Context, block *simBlock, header,
324359 gasUsed += result .UsedGas
325360 receipts [i ] = core .MakeReceipt (evm , result , sim .state , blockContext .BlockNumber , common.Hash {}, blockContext .Time , tx , gasUsed , root )
326361 blobGasUsed += receipts [i ].BlobGasUsed
362+
363+ // Make sure the gas cap is still enforced. It's only for
364+ // internally protection.
365+ if err := sim .budget .consume (result .UsedGas ); err != nil {
366+ return nil , nil , nil , err
367+ }
368+
327369 logs := tracer .Logs ()
328370 callRes := simCallResult {ReturnValue : result .Return (), Logs : logs , GasUsed : hexutil .Uint64 (result .UsedGas )}
329371 if result .Failed () {
@@ -400,10 +442,11 @@ func (sim *simulator) sanitizeCall(call *TransactionArgs, state vm.StateDB, head
400442 if * gasUsed + uint64 (* call .Gas ) > blockContext .GasLimit {
401443 return & blockGasLimitReachedError {fmt .Sprintf ("block gas limit reached: %d >= %d" , gasUsed , blockContext .GasLimit )}
402444 }
403- if err := call .CallDefaults (sim .gp .Gas (), header .BaseFee , sim .chainConfig .ChainID ); err != nil {
404- return err
405- }
406- return nil
445+ // Clamp to the cross-block gas budget.
446+ gas := sim .budget .cap (uint64 (* call .Gas ))
447+ call .Gas = (* hexutil .Uint64 )(& gas )
448+
449+ return call .CallDefaults (0 , header .BaseFee , sim .chainConfig .ChainID )
407450}
408451
409452func (sim * simulator ) activePrecompiles (base * types.Header ) vm.PrecompiledContracts {
0 commit comments