Skip to content

Commit

Permalink
Add support for estimate gas historically (#1031)
Browse files Browse the repository at this point in the history
* Add support for estimate gas historically

* prettier
  • Loading branch information
tgmichel authored May 8, 2023
1 parent de9368d commit 690eba4
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 8 deletions.
32 changes: 25 additions & 7 deletions client/rpc/src/eth/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,15 +351,36 @@ where
}
}

pub async fn estimate_gas(&self, request: CallRequest, _: Option<BlockNumber>) -> Result<U256> {
pub async fn estimate_gas(
&self,
request: CallRequest,
number: Option<BlockNumber>,
) -> Result<U256> {
let client = Arc::clone(&self.client);
let block_data_cache = Arc::clone(&self.block_data_cache);

// Define the lower bound of estimate
const MIN_GAS_PER_TX: U256 = U256([21_000, 0, 0, 0]);

// Get best hash (TODO missing support for estimating gas historically)
let substrate_hash = client.info().best_hash;
// Get substrate hash and runtime api
let (substrate_hash, api) = match frontier_backend_client::native_block_id::<B, C>(
self.client.as_ref(),
self.backend.as_ref(),
number,
)? {
Some(id) => {
let hash = client
.expect_block_hash_from_id(&id)
.map_err(|_| crate::err(JSON_RPC_ERROR_DEFAULT, "header not found", None))?;
(hash, client.runtime_api())
}
None => {
// Not mapped in the db, assume pending.
let hash = client.info().best_hash;
let api = pending_runtime_api(client.as_ref(), self.graph.as_ref())?;
(hash, api)
}
};

// Adapt request for gas estimation.
let request = EC::EstimateGasAdapter::adapt_request(request);
Expand All @@ -371,8 +392,7 @@ where
};
if is_simple_transfer {
if let Some(to) = request.to {
let to_code = client
.runtime_api()
let to_code = api
.account_code_at(substrate_hash, to)
.map_err(|err| internal_err(format!("runtime error: {:?}", err)))?;
if to_code.is_empty() {
Expand Down Expand Up @@ -405,8 +425,6 @@ where

let max_gas_limit = block_gas_limit * self.execute_gas_limit_multiplier;

let api = client.runtime_api();

// Determine the highest possible gas limits
let mut highest = match request.gas {
Some(amount) => {
Expand Down
97 changes: 96 additions & 1 deletion ts-tests/tests/test-execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,109 @@ import { describeWithFrontier, customRequest, createAndFinalizeBlock } from "./u
import { AbiItem } from "web3-utils";

import Test from "../build/contracts/Test.json";
import Storage from "../build/contracts/Storage.json";
import ForceGasLimit from "../build/contracts/ForceGasLimit.json";

const TEST_CONTRACT_BYTECODE = Test.bytecode;
const TEST_CONTRACT_DEPLOYED_BYTECODE = Test.deployedBytecode;

const FORCE_GAS_CONTRACT_BYTECODE = ForceGasLimit.bytecode;
const FORCE_GAS_CONTRACT_ABI = ForceGasLimit.abi as AbiItem[];
const FORCE_GAS_CONTRACT_DEPLOYED_BYTECODE = ForceGasLimit.deployedBytecode;

describeWithFrontier("Frontier RPC (estimate gas historically)", (context) => {
const TEST_CONTRACT_BYTECODE = Storage.bytecode;
const TEST_CONTRACT_ABI = Storage.abi as AbiItem[];

it("estimate gas historically should work", async function () {
const contract = new context.web3.eth.Contract(TEST_CONTRACT_ABI);

this.timeout(15000);
const tx = await context.web3.eth.accounts.signTransaction(
{
from: GENESIS_ACCOUNT,
data: TEST_CONTRACT_BYTECODE,
value: "0x00",
gasPrice: "0x3B9ACA00",
gas: "0x100000",
},
GENESIS_ACCOUNT_PRIVATE_KEY
);

expect(await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction])).to.include({
id: 1,
jsonrpc: "2.0",
});

await createAndFinalizeBlock(context.web3);
let receipt0 = await context.web3.eth.getTransactionReceipt(tx.transactionHash);
let contractAddress = receipt0.contractAddress;

// Estimate what a sstore set costs at block number 1
const SSTORE_SET_DATA = contract.methods
.setStorage(
"0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc",
"0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
)
.encodeABI();

const ESTIMATE_AT_1 = context.web3.utils.hexToNumber(
(
await customRequest(context.web3, "eth_estimateGas", [
{
to: contractAddress,
data: SSTORE_SET_DATA,
},
])
).result
);

// Set the storage and create a block
const tx1 = await context.web3.eth.accounts.signTransaction(
{
from: GENESIS_ACCOUNT,
to: contractAddress,
data: SSTORE_SET_DATA,
value: "0x00",
gasPrice: "0x3B9ACA00",
gas: "0x500000",
},
GENESIS_ACCOUNT_PRIVATE_KEY
);
await customRequest(context.web3, "eth_sendRawTransaction", [tx1.rawTransaction]);
await createAndFinalizeBlock(context.web3);

// Estimate what a sstore reset costs at block number 2
const ESTIMATE_AT_2 = context.web3.utils.hexToNumber(
(
await customRequest(context.web3, "eth_estimateGas", [
{
to: contractAddress,
data: SSTORE_SET_DATA,
},
])
).result
);

// SSTORE over an existing storage is cheaper
expect(ESTIMATE_AT_2).to.be.lt(ESTIMATE_AT_1);

// Estimate what a sstore reset costed at block number 1, queried historically
const ESTIMATE_AT_1_QUERY = context.web3.utils.hexToNumber(
(
await customRequest(context.web3, "eth_estimateGas", [
{
to: contractAddress,
data: SSTORE_SET_DATA,
},
1,
])
).result
);

// Expect to get the original estimated gas at block 1
expect(ESTIMATE_AT_1_QUERY).to.be.eq(ESTIMATE_AT_1);
});
});

describeWithFrontier("Frontier RPC (RPC execution)", (context) => {
step("should call with gas limit under block gas limit", async function () {
Expand Down

0 comments on commit 690eba4

Please sign in to comment.