Skip to content

Commit ac1bbac

Browse files
authored
Merge pull request #876 from onflow/mpeter/estimate-gas-with-block-overrides
Apply block overrides in `eth_estimateGas` JSON-RPC endpoint
2 parents cdaa9b8 + 3ef41ff commit ac1bbac

7 files changed

Lines changed: 172 additions & 2 deletions

File tree

api/api.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,7 @@ func (b *BlockChainAPI) EstimateGas(
693693
args ethTypes.TransactionArgs,
694694
blockNumberOrHash *rpc.BlockNumberOrHash,
695695
stateOverrides *ethTypes.StateOverride,
696+
blockOverrides *ethTypes.BlockOverrides,
696697
) (hexutil.Uint64, error) {
697698
l := b.logger.With().
698699
Str("endpoint", EthEstimateGas).
@@ -723,7 +724,13 @@ func (b *BlockChainAPI) EstimateGas(
723724
return handleError[hexutil.Uint64](err, l, b.collector)
724725
}
725726

726-
estimatedGas, err := b.evm.EstimateGas(args, from, height, stateOverrides)
727+
estimatedGas, err := b.evm.EstimateGas(
728+
args,
729+
from,
730+
height,
731+
stateOverrides,
732+
blockOverrides,
733+
)
727734
if err != nil {
728735
return handleError[hexutil.Uint64](err, l, b.collector)
729736
}

services/requester/requester.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ type Requester interface {
7878
from common.Address,
7979
height uint64,
8080
stateOverrides *ethTypes.StateOverride,
81+
blockOverrides *ethTypes.BlockOverrides,
8182
) (uint64, error)
8283

8384
// GetNonce gets nonce from the network at the given EVM block height.
@@ -320,14 +321,15 @@ func (e *EVM) EstimateGas(
320321
from common.Address,
321322
height uint64,
322323
stateOverrides *ethTypes.StateOverride,
324+
blockOverrides *ethTypes.BlockOverrides,
323325
) (uint64, error) {
324326
iterations := 0
325327

326328
dryRun := func(gasLimit uint64) (*evmTypes.Result, error) {
327329
gas := hexutil.Uint64(gasLimit)
328330
txArgs.Gas = &gas
329331
tx := txArgs.ToTransaction(types.LegacyTxType, blockGasLimit)
330-
result, err := e.dryRunTx(tx, from, height, stateOverrides, nil)
332+
result, err := e.dryRunTx(tx, from, height, stateOverrides, blockOverrides)
331333
iterations += 1
332334
return result, err
333335
}

tests/e2e_web3js_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ func TestWeb3_E2E(t *testing.T) {
4646
runWeb3Test(t, "contract_call_overrides_test")
4747
})
4848

49+
t.Run("test gas estimation block overrides", func(t *testing.T) {
50+
runWeb3Test(t, "estimate_gas_overrides_test")
51+
})
52+
4953
t.Run("test setup sanity check", func(t *testing.T) {
5054
runWeb3Test(t, "setup_test")
5155
})

tests/fixtures/blockOverrides.byte

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
6080604052348015600e575f5ffd5b506101c58061001c5f395ff3fe608060405234801561000f575f5ffd5b5060043610610034575f3560e01c80637b16441c14610038578063f8a8fd6d14610042575b5f5ffd5b61004061004c565b005b61004a61007e565b005b5f5f90505f5f90505b6103e881101561007a57808261006b919061015c565b91508080600101915050610055565b5050565b61238243036100905761008f61004c565b5b63674db1e142036100a4576100a361004c565b5b7f7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc044036100d4576100d361004c565b5b73658bdf435d810c91414ec09147daa6db6240637973ffffffffffffffffffffffffffffffffffffffff164173ffffffffffffffffffffffffffffffffffffffff16036101245761012361004c565b5b565b5f819050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f61016682610126565b915061017183610126565b92508282019050808211156101895761018861012f565b5b9291505056fea26469706673582212206f7542706dc282fc8d2da0c0cc27398671a845e2c6ae871e23591b016bf3969464736f6c634300081e0033

tests/fixtures/blockOverrides.sol

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// SPDX-License-Identifier: GPL-3.0
2+
3+
pragma solidity >=0.8.2 <0.9.0;
4+
5+
contract BlockOverrides {
6+
7+
function test() public view {
8+
// Add some hard-coded if conditions, to simulate overriding
9+
// certain block header fields. The condition body is a simple
10+
// way to check that the block header fields were indeed
11+
// overrided, resulting in higher gas usage.
12+
if (block.number == 9090) {
13+
sumValues();
14+
}
15+
16+
if (block.timestamp == 1733145057) {
17+
sumValues();
18+
}
19+
20+
if (block.prevrandao == 0x7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc0) {
21+
sumValues();
22+
}
23+
24+
if (block.coinbase == 0x658Bdf435d810C91414eC09147DAA6DB62406379) {
25+
sumValues();
26+
}
27+
}
28+
29+
function sumValues() public pure {
30+
uint sum = 0;
31+
for (uint i = 0; i < 1000; i++) {
32+
sum += i;
33+
}
34+
}
35+
36+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[
2+
{
3+
"inputs": [],
4+
"name": "sumValues",
5+
"outputs": [],
6+
"stateMutability": "pure",
7+
"type": "function"
8+
},
9+
{
10+
"inputs": [],
11+
"name": "test",
12+
"outputs": [],
13+
"stateMutability": "view",
14+
"type": "function"
15+
}
16+
]
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
const { assert } = require('chai')
2+
const conf = require('./config')
3+
const helpers = require('./helpers')
4+
const web3 = conf.web3
5+
6+
let deployed = null
7+
let contractAddress = null
8+
9+
before(async () => {
10+
deployed = await helpers.deployContract('blockOverrides')
11+
contractAddress = deployed.receipt.contractAddress
12+
13+
assert.equal(deployed.receipt.status, conf.successStatus)
14+
})
15+
16+
it('should apply block overrides on eth_estimateGas', async () => {
17+
assert.equal(deployed.receipt.status, conf.successStatus)
18+
19+
let receipt = await web3.eth.getTransactionReceipt(deployed.receipt.transactionHash)
20+
assert.equal(receipt.contractAddress, contractAddress)
21+
22+
// Check the `block.number` value, without overrides
23+
let testFuncSelector = deployed.contract.methods.test().encodeABI()
24+
let txArgs = {
25+
from: conf.eoa.address,
26+
to: contractAddress,
27+
gas: '0x493E0',
28+
gasPrice: web3.utils.toHex(conf.minGasPrice),
29+
value: '0x0',
30+
data: testFuncSelector,
31+
}
32+
33+
let response = await helpers.callRPCMethod(
34+
'eth_estimateGas',
35+
[txArgs, 'latest', null, null]
36+
)
37+
assert.equal(response.status, 200)
38+
assert.isDefined(response.body)
39+
assert.equal(web3.utils.hexToNumber(response.body.result), 21473n)
40+
41+
// Override the `block.number` value to `9090`.
42+
response = await helpers.callRPCMethod(
43+
'eth_estimateGas',
44+
[txArgs, 'latest', null, { number: '0x2382' }]
45+
)
46+
assert.equal(response.status, 200)
47+
assert.isDefined(response.body)
48+
assert.equal(web3.utils.hexToNumber(response.body.result), 273693n)
49+
50+
// Check the `block.timestamp` value, without overrides
51+
response = await helpers.callRPCMethod(
52+
'eth_estimateGas',
53+
[txArgs, 'latest', null, null]
54+
)
55+
assert.equal(response.status, 200)
56+
assert.isDefined(response.body)
57+
assert.equal(web3.utils.hexToNumber(response.body.result), 21473n)
58+
59+
// Override the `block.timestamp` value to `0x674DB1E1`.
60+
response = await helpers.callRPCMethod(
61+
'eth_estimateGas',
62+
[txArgs, 'latest', null, { time: '0x674DB1E1' }]
63+
)
64+
assert.equal(response.status, 200)
65+
assert.isDefined(response.body)
66+
assert.equal(web3.utils.hexToNumber(response.body.result), 273693n)
67+
68+
// Check the `block.prevrandao` value, without overrides
69+
response = await helpers.callRPCMethod(
70+
'eth_estimateGas',
71+
[txArgs, 'latest', null, null]
72+
)
73+
assert.equal(response.status, 200)
74+
assert.isDefined(response.body)
75+
assert.equal(web3.utils.hexToNumber(response.body.result), 21473n)
76+
77+
// Override the `block.prevrandao` value to `0x7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc0`.
78+
let random = '0x7914bb5b13bac6f621bc37bbf6e406fbf4472aaaaf17ec2f309a92aca4e27fc0'
79+
response = await helpers.callRPCMethod(
80+
'eth_estimateGas',
81+
[txArgs, 'latest', null, { random: random }]
82+
)
83+
assert.equal(response.status, 200)
84+
assert.isDefined(response.body)
85+
assert.equal(web3.utils.hexToNumber(response.body.result), 273693n)
86+
87+
// Check the `block.coinbase` value, without overrides
88+
response = await helpers.callRPCMethod(
89+
'eth_estimateGas',
90+
[txArgs, 'latest', null, null]
91+
)
92+
assert.equal(response.status, 200)
93+
assert.isDefined(response.body)
94+
assert.equal(web3.utils.hexToNumber(response.body.result), 21473n)
95+
96+
// Override the `block.coinbase` value to `0x658Bdf435d810C91414eC09147DAA6DB62406379`.
97+
response = await helpers.callRPCMethod(
98+
'eth_estimateGas',
99+
[txArgs, 'latest', null, { coinbase: '0x658Bdf435d810C91414eC09147DAA6DB62406379' }]
100+
)
101+
assert.equal(response.status, 200)
102+
assert.isDefined(response.body)
103+
assert.equal(web3.utils.hexToNumber(response.body.result), 273693n)
104+
})

0 commit comments

Comments
 (0)