ethapi: add block override to estimateGas

This commit is contained in:
Antony Denyer 2024-10-17 12:57:39 +01:00
parent f3b4bbbaf3
commit abab3441b6
No known key found for this signature in database
GPG key ID: 579B30F3FDE8F90A
7 changed files with 131 additions and 25 deletions

1
.gitignore vendored
View file

@ -37,6 +37,7 @@ profile.cov
# IdeaIDE # IdeaIDE
.idea .idea
*.iml
# VS Code # VS Code
.vscode .vscode

View file

@ -309,3 +309,13 @@ func UnpackRevert(data []byte) (string, error) {
return "", errors.New("invalid data for unpacking") return "", errors.New("invalid data for unpacking")
} }
} }
func PackRevert(revertMessage string) []byte {
stringType, _ := NewType("string", "", nil)
args := Arguments{
{Type: stringType},
}
encodedMessage, _ := args.Pack(revertMessage)
return append(revertSelector, encodedMessage...)
}

View file

@ -1208,7 +1208,7 @@ func (b *Block) Call(ctx context.Context, args struct {
func (b *Block) EstimateGas(ctx context.Context, args struct { func (b *Block) EstimateGas(ctx context.Context, args struct {
Data ethapi.TransactionArgs Data ethapi.TransactionArgs
}) (hexutil.Uint64, error) { }) (hexutil.Uint64, error) {
return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, b.r.backend.RPCGasCap()) return ethapi.DoEstimateGas(ctx, b.r.backend, args.Data, *b.numberOrHash, nil, nil, b.r.backend.RPCGasCap())
} }
type Pending struct { type Pending struct {
@ -1272,7 +1272,7 @@ func (p *Pending) EstimateGas(ctx context.Context, args struct {
Data ethapi.TransactionArgs Data ethapi.TransactionArgs
}) (hexutil.Uint64, error) { }) (hexutil.Uint64, error) {
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, latestBlockNr, nil, p.r.backend.RPCGasCap()) return ethapi.DoEstimateGas(ctx, p.r.backend, args.Data, latestBlockNr, nil, nil, p.r.backend.RPCGasCap())
} }
// Resolver is the top-level object in the GraphQL hierarchy. // Resolver is the top-level object in the GraphQL hierarchy.

View file

@ -973,20 +973,28 @@ func (api *BlockChainAPI) SimulateV1(ctx context.Context, opts simOpts, blockNrO
// successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if // successfully at block `blockNrOrHash`. It returns error if the transaction would revert, or if
// there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil & // there are unexpected failures. The gas limit is capped by both `args.Gas` (if non-nil &
// non-zero) and `gasCap` (if non-zero). // non-zero) and `gasCap` (if non-zero).
func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, gasCap uint64) (hexutil.Uint64, error) { func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides, gasCap uint64) (hexutil.Uint64, error) {
// Retrieve the base state and mutate it with any overrides // Retrieve the base state and mutate it with any overrides
state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash) state, header, err := b.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
if state == nil || err != nil { if state == nil || err != nil {
return 0, err return 0, err
} }
if err := overrides.Apply(state, nil); err != nil { chainContext := NewChainContext(ctx, b)
blockCtx := core.NewEVMBlockContext(header, chainContext, nil)
if blockOverrides != nil {
blockOverrides.Apply(&blockCtx)
}
rules := b.ChainConfig().Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time)
precompiles := maps.Clone(vm.ActivePrecompiledContracts(rules))
if err := overrides.Apply(state, precompiles); err != nil {
return 0, err return 0, err
} }
// Construct the gas estimator option from the user input // Construct the gas estimator option from the user input
opts := &gasestimator.Options{ opts := &gasestimator.Options{
Config: b.ChainConfig(), Config: b.ChainConfig(),
Chain: NewChainContext(ctx, b), Chain: chainContext,
Header: header, Header: blockOverrides.MakeHeader(header),
State: state, State: state,
ErrorRatio: estimateGasErrorRatio, ErrorRatio: estimateGasErrorRatio,
} }
@ -1017,12 +1025,12 @@ func DoEstimateGas(ctx context.Context, b Backend, args TransactionArgs, blockNr
// value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap // value is capped by both `args.Gas` (if non-nil & non-zero) and the backend's RPCGasCap
// configuration (if non-zero). // configuration (if non-zero).
// Note: Required blob gas is not computed in this method. // Note: Required blob gas is not computed in this method.
func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Uint64, error) { func (api *BlockChainAPI) EstimateGas(ctx context.Context, args TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash, overrides *StateOverride, blockOverrides *BlockOverrides) (hexutil.Uint64, error) {
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
if blockNrOrHash != nil { if blockNrOrHash != nil {
bNrOrHash = *blockNrOrHash bNrOrHash = *blockNrOrHash
} }
return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, api.b.RPCGasCap()) return DoEstimateGas(ctx, api.b, args, bNrOrHash, overrides, blockOverrides, api.b.RPCGasCap())
} }
// RPCMarshalHeader converts the given header to the RPC output . // RPCMarshalHeader converts the given header to the RPC output .

View file

@ -34,6 +34,8 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
@ -641,6 +643,7 @@ func TestEstimateGas(t *testing.T) {
signer = types.HomesteadSigner{} signer = types.HomesteadSigner{}
randomAccounts = newAccounts(2) randomAccounts = newAccounts(2)
) )
api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) { api := NewBlockChainAPI(newTestBackend(t, genBlocks, genesis, beacon.New(ethash.NewFaker()), func(i int, b *core.BlockGen) {
// Transfer from account[0] to account[1] // Transfer from account[0] to account[1]
// value: 1000 wei // value: 1000 wei
@ -649,14 +652,16 @@ func TestEstimateGas(t *testing.T) {
b.AddTx(tx) b.AddTx(tx)
b.SetPoS() b.SetPoS()
})) }))
var testSuite = []struct { var testSuite = []struct {
blockNumber rpc.BlockNumber blockNumber rpc.BlockNumber
call TransactionArgs call TransactionArgs
overrides StateOverride overrides StateOverride
expectErr error blockOverrides BlockOverrides
want uint64 expectErr error
want uint64
}{ }{
// simple transfer on latest block //simple transfer on latest block
{ {
blockNumber: rpc.LatestBlockNumber, blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{ call: TransactionArgs{
@ -759,16 +764,65 @@ func TestEstimateGas(t *testing.T) {
}, },
want: 21000, want: 21000,
}, },
// // SPDX-License-Identifier: GPL-3.0
//pragma solidity >=0.8.2 <0.9.0;
//
//contract BlockOverridesTest {
// function call() public view returns (uint256) {
// return block.number;
// }
//
// function estimate() public view {
// revert(string.concat("block ", uint2str(block.number)));
// }
//
// function uint2str(uint256 _i) internal pure returns (string memory str) {
// if (_i == 0) {
// return "0";
// }
// uint256 j = _i;
// uint256 length;
// while (j != 0) {
// length++;
// j /= 10;
// }
// bytes memory bstr = new bytes(length);
// uint256 k = length;
// j = _i;
// while (j != 0) {
// bstr[--k] = bytes1(uint8(48 + (j % 10)));
// j /= 10;
// }
// str = string(bstr);
// }
//}
{
blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{
From: &accounts[0].addr,
To: &accounts[1].addr,
Data: hex2Bytes("0x3592d016"), //estimate
},
overrides: StateOverride{
accounts[1].addr: OverrideAccount{
Code: hex2Bytes("608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806328b5e32b146100385780633592d0161461004b575b5f5ffd5b4360405190815260200160405180910390f35b610053610055565b005b61005e4361009d565b60405160200161006e91906101a5565b60408051601f198184030181529082905262461bcd60e51b8252610094916004016101cd565b60405180910390fd5b6060815f036100c35750506040805180820190915260018152600360fc1b602082015290565b815f5b81156100ec57806100d681610216565b91506100e59050600a83610242565b91506100c6565b5f8167ffffffffffffffff81111561010657610106610255565b6040519080825280601f01601f191660200182016040528015610130576020820181803683370190505b508593509050815b831561019c57610149600a85610269565b61015490603061027c565b60f81b8261016183610295565b92508281518110610174576101746102aa565b60200101906001600160f81b03191690815f1a905350610195600a85610242565b9350610138565b50949350505050565b650313637b1b5960d51b81525f82518060208501600685015e5f920160060191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161022757610227610202565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826102505761025061022e565b500490565b634e487b7160e01b5f52604160045260245ffd5b5f826102775761027761022e565b500690565b8082018082111561028f5761028f610202565b92915050565b5f816102a3576102a3610202565b505f190190565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220a253cad1e2e3523b8c053c1d0cd1e39d7f3bafcedd73440a244872701f05dab264736f6c634300081c0033"),
},
},
blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},
expectErr: newRevertError(abi.PackRevert("block 11")),
},
} }
for i, tc := range testSuite { for i, tc := range testSuite {
result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides) result, err := api.EstimateGas(context.Background(), tc.call, &rpc.BlockNumberOrHash{BlockNumber: &tc.blockNumber}, &tc.overrides, &tc.blockOverrides)
if tc.expectErr != nil { if tc.expectErr != nil {
if err == nil { if err == nil {
t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr) t.Errorf("test %d: want error %v, have nothing", i, tc.expectErr)
continue continue
} }
if !errors.Is(err, tc.expectErr) { if !errors.Is(err, tc.expectErr) {
t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err) if !reflect.DeepEqual(err, tc.expectErr) {
t.Errorf("test %d: error mismatch, want %v, have %v", i, tc.expectErr, err)
}
} }
continue continue
} }
@ -929,16 +983,49 @@ func TestCall(t *testing.T) {
}, },
want: "0x000000000000000000000000000000000000000000000000000000000000007b", want: "0x000000000000000000000000000000000000000000000000000000000000007b",
}, },
// Block overrides should work // // SPDX-License-Identifier: GPL-3.0
//pragma solidity >=0.8.2 <0.9.0;
//
//contract BlockOverridesTest {
// function call() public view returns (uint256) {
// return block.number;
// }
//
// function estimate() public view {
// revert(string.concat("block ", uint2str(block.number)));
// }
//
// function uint2str(uint256 _i) internal pure returns (string memory str) {
// if (_i == 0) {
// return "0";
// }
// uint256 j = _i;
// uint256 length;
// while (j != 0) {
// length++;
// j /= 10;
// }
// bytes memory bstr = new bytes(length);
// uint256 k = length;
// j = _i;
// while (j != 0) {
// bstr[--k] = bytes1(uint8(48 + (j % 10)));
// j /= 10;
// }
// str = string(bstr);
// }
//}
{ {
name: "block-override", name: "block-override-with-state-override",
blockNumber: rpc.LatestBlockNumber, blockNumber: rpc.LatestBlockNumber,
call: TransactionArgs{ call: TransactionArgs{
From: &accounts[1].addr, From: &accounts[1].addr,
Input: &hexutil.Bytes{ To: &accounts[2].addr,
0x43, // NUMBER Data: hex2Bytes("0x28b5e32b"), //call
0x60, 0x00, 0x52, // MSTORE offset 0 },
0x60, 0x20, 0x60, 0x00, 0xf3, overrides: StateOverride{
accounts[2].addr: OverrideAccount{
Code: hex2Bytes("608060405234801561000f575f5ffd5b5060043610610034575f3560e01c806328b5e32b146100385780633592d0161461004b575b5f5ffd5b4360405190815260200160405180910390f35b610053610055565b005b61005e4361009d565b60405160200161006e91906101a5565b60408051601f198184030181529082905262461bcd60e51b8252610094916004016101cd565b60405180910390fd5b6060815f036100c35750506040805180820190915260018152600360fc1b602082015290565b815f5b81156100ec57806100d681610216565b91506100e59050600a83610242565b91506100c6565b5f8167ffffffffffffffff81111561010657610106610255565b6040519080825280601f01601f191660200182016040528015610130576020820181803683370190505b508593509050815b831561019c57610149600a85610269565b61015490603061027c565b60f81b8261016183610295565b92508281518110610174576101746102aa565b60200101906001600160f81b03191690815f1a905350610195600a85610242565b9350610138565b50949350505050565b650313637b1b5960d51b81525f82518060208501600685015e5f920160060191825250919050565b602081525f82518060208401528060208501604085015e5f604082850101526040601f19601f83011684010191505092915050565b634e487b7160e01b5f52601160045260245ffd5b5f6001820161022757610227610202565b5060010190565b634e487b7160e01b5f52601260045260245ffd5b5f826102505761025061022e565b500490565b634e487b7160e01b5f52604160045260245ffd5b5f826102775761027761022e565b500690565b8082018082111561028f5761028f610202565b92915050565b5f816102a3576102a3610202565b505f190190565b634e487b7160e01b5f52603260045260245ffdfea2646970667358221220a253cad1e2e3523b8c053c1d0cd1e39d7f3bafcedd73440a244872701f05dab264736f6c634300081c0033"),
}, },
}, },
blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))}, blockOverrides: BlockOverrides{Number: (*hexutil.Big)(big.NewInt(11))},

View file

@ -160,7 +160,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend, skipGas
BlobHashes: args.BlobHashes, BlobHashes: args.BlobHashes,
} }
latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber) latestBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.LatestBlockNumber)
estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, b.RPCGasCap()) estimated, err := DoEstimateGas(ctx, b, callArgs, latestBlockNr, nil, nil, b.RPCGasCap())
if err != nil { if err != nil {
return err return err
} }

View file

@ -503,8 +503,8 @@ web3._extend({
new web3._extend.Method({ new web3._extend.Method({
name: 'estimateGas', name: 'estimateGas',
call: 'eth_estimateGas', call: 'eth_estimateGas',
params: 3, params: 4,
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null], inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter, null, null],
outputFormatter: web3._extend.utils.toDecimal outputFormatter: web3._extend.utils.toDecimal
}), }),
new web3._extend.Method({ new web3._extend.Method({