diff --git a/turbo/jsonrpc/eth_api.go b/turbo/jsonrpc/eth_api.go index 7c71f661b66..a3f98427bd5 100644 --- a/turbo/jsonrpc/eth_api.go +++ b/turbo/jsonrpc/eth_api.go @@ -91,7 +91,7 @@ type EthAPI interface { // Sending related (see ./eth_call.go) Call(ctx context.Context, args ethapi2.CallArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *ethapi2.StateOverrides) (hexutility.Bytes, error) - EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs) (hexutil.Uint64, error) + EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs, blockNrOrHash *rpc.BlockNumberOrHash) (hexutil.Uint64, error) SendRawTransaction(ctx context.Context, encodedTx hexutility.Bytes) (common.Hash, error) SendTransaction(_ context.Context, txObject interface{}) (common.Hash, error) Sign(ctx context.Context, _ common.Address, _ hexutility.Bytes) (hexutility.Bytes, error) diff --git a/turbo/jsonrpc/eth_call.go b/turbo/jsonrpc/eth_call.go index d88be770a0b..de0566ed6c1 100644 --- a/turbo/jsonrpc/eth_call.go +++ b/turbo/jsonrpc/eth_call.go @@ -114,7 +114,7 @@ func headerByNumberOrHash(ctx context.Context, tx kv.Tx, blockNrOrHash rpc.Block } // EstimateGas implements eth_estimateGas. Returns an estimate of how much gas is necessary to allow the transaction to complete. The transaction will not be added to the blockchain. -func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs) (hexutil.Uint64, error) { +func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs, blockNrOrHash *rpc.BlockNumberOrHash) (hexutil.Uint64, error) { var args ethapi2.CallArgs // if we actually get CallArgs here, we use them if argsOrNil != nil { @@ -138,39 +138,36 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs args.From = new(libcommon.Address) } - chainConfig, err := api.chainConfig(ctx, dbtx) - if err != nil { - return 0, err - } - - latestCanBlockNumber, latestCanHash, isLatest, err := rpchelper.GetCanonicalBlockNumber(latestNumOrHash, dbtx, api.filters) // DoCall cannot be executed on non-canonical blocks - if err != nil { - return 0, err - } - - // try and get the block from the lru cache first then try DB before failing - block := api.tryBlockFromLru(latestCanHash) - if block == nil { - block, err = api.blockWithSenders(ctx, dbtx, latestCanHash, latestCanBlockNumber) - if err != nil { - return 0, err - } - } - if block == nil { - return 0, fmt.Errorf("could not find latest block in cache or db") - } - - stateReader, err := rpchelper.CreateStateReaderFromBlockNumber(ctx, dbtx, latestCanBlockNumber, isLatest, 0, api.stateCache, api.historyV3(dbtx), chainConfig.ChainName) - if err != nil { - return 0, err + bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber) + if blockNrOrHash != nil { + bNrOrHash = *blockNrOrHash } - header := block.HeaderNoCopy() // Determine the highest gas limit can be used during the estimation. if args.Gas != nil && uint64(*args.Gas) >= params.TxGas { hi = uint64(*args.Gas) } else { - hi = header.GasLimit + // Retrieve the block to act as the gas ceiling + h, err := headerByNumberOrHash(ctx, dbtx, bNrOrHash, api) + if err != nil { + return 0, err + } + if h == nil { + // if a block number was supplied and there is no header return 0 + if blockNrOrHash != nil { + return 0, nil + } + + // block number not supplied, so we haven't found a pending block, read the latest block instead + h, err = headerByNumberOrHash(ctx, dbtx, latestNumOrHash, api) + if err != nil { + return 0, err + } + if h == nil { + return 0, nil + } + } + hi = h.GasLimit } var feeCap *big.Int @@ -224,8 +221,35 @@ func (api *APIImpl) EstimateGas(ctx context.Context, argsOrNil *ethapi2.CallArgs } gasCap = hi + chainConfig, err := api.chainConfig(ctx, dbtx) + if err != nil { + return 0, err + } engine := api.engine() + latestCanBlockNumber, latestCanHash, isLatest, err := rpchelper.GetCanonicalBlockNumber(latestNumOrHash, dbtx, api.filters) // DoCall cannot be executed on non-canonical blocks + if err != nil { + return 0, err + } + + // try and get the block from the lru cache first then try DB before failing + block := api.tryBlockFromLru(latestCanHash) + if block == nil { + block, err = api.blockWithSenders(ctx, dbtx, latestCanHash, latestCanBlockNumber) + if err != nil { + return 0, err + } + } + if block == nil { + return 0, fmt.Errorf("could not find latest block in cache or db") + } + + stateReader, err := rpchelper.CreateStateReaderFromBlockNumber(ctx, dbtx, latestCanBlockNumber, isLatest, 0, api.stateCache, api.historyV3(dbtx), chainConfig.ChainName) + if err != nil { + return 0, err + } + header := block.HeaderNoCopy() + caller, err := transactions.NewReusableCaller(engine, stateReader, nil, header, args, api.GasCap, latestNumOrHash, dbtx, api._blockReader, chainConfig, api.evmCallTimeout) if err != nil { return 0, err diff --git a/turbo/jsonrpc/eth_call_test.go b/turbo/jsonrpc/eth_call_test.go index d3c6ee4d39d..80a9e665269 100644 --- a/turbo/jsonrpc/eth_call_test.go +++ b/turbo/jsonrpc/eth_call_test.go @@ -50,7 +50,7 @@ func TestEstimateGas(t *testing.T) { if _, err := api.EstimateGas(context.Background(), ðapi.CallArgs{ From: &from, To: &to, - }); err != nil { + }, nil); err != nil { t.Errorf("calling EstimateGas: %v", err) } }