Skip to content

Commit

Permalink
Add gas_limit_multiplier_support runtime api (polkadot-evm#935)
Browse files Browse the repository at this point in the history
* Add `gas_limit_multiplier_support` runtime api

* fmt

* clippy

* Ts test default gas limit

* prettier
  • Loading branch information
tgmichel authored Dec 13, 2022
1 parent e8c1c9f commit 07c3a8e
Show file tree
Hide file tree
Showing 5 changed files with 78 additions and 6 deletions.
18 changes: 14 additions & 4 deletions client/rpc/src/eth/execute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,12 @@ where
}
amount
}
None => max_gas_limit,
// If gas limit is not specified in the request we either use the multiplier if supported
// or fallback to the block gas limit.
None => match api.gas_limit_multiplier_support(&id) {
Ok(_) => max_gas_limit,
_ => block_gas_limit,
},
};

let data = data.map(|d| d.0).unwrap_or_default();
Expand Down Expand Up @@ -367,6 +372,8 @@ 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 All @@ -378,11 +385,14 @@ where
}
amount
}
None => max_gas_limit,
// If gas limit is not specified in the request we either use the multiplier if supported
// or fallback to the block gas limit.
None => match api.gas_limit_multiplier_support(&BlockId::Hash(best_hash)) {
Ok(_) => max_gas_limit,
_ => block_gas_limit,
},
};

let api = client.runtime_api();

// Recap the highest gas allowance with account's balance.
if let Some(from) = request.from {
let gas_price = gas_price.unwrap_or_default();
Expand Down
3 changes: 3 additions & 0 deletions primitives/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,9 @@ sp_api::decl_runtime_apis! {
) -> Vec<ethereum::TransactionV2>;
/// Return the elasticity multiplier.
fn elasticity() -> Option<Permill>;
/// Used to determine if gas limit multiplier for non-transactional calls (eth_call/estimateGas)
/// is supported.
fn gas_limit_multiplier_support();
}

#[api_version(2)]
Expand Down
2 changes: 2 additions & 0 deletions template/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,8 @@ impl_runtime_apis! {
fn elasticity() -> Option<Permill> {
Some(BaseFee::elasticity())
}

fn gas_limit_multiplier_support() {}
}

impl fp_rpc::ConvertTransactionRuntimeApi<Block> for Runtime {
Expand Down
12 changes: 12 additions & 0 deletions ts-tests/contracts/ForceGasLimit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract ForceGasLimit {
uint public number;
function force_gas(uint require_gas) public returns (uint) {
require(gasleft() > require_gas, "not enough gas");
number++;
return number;
}
}
49 changes: 47 additions & 2 deletions ts-tests/tests/test-execute.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { assert, expect } from "chai";
import { step } from "mocha-steps";
import { BLOCK_GAS_LIMIT, GENESIS_ACCOUNT } from "./config";
import { BLOCK_GAS_LIMIT, GENESIS_ACCOUNT, GENESIS_ACCOUNT_PRIVATE_KEY } from "./config";

import { describeWithFrontier, customRequest } from "./util";
import { describeWithFrontier, customRequest, createAndFinalizeBlock } from "./util";
import { AbiItem } from "web3-utils";

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

// EXTRINSIC_GAS_LIMIT = [BLOCK_GAS_LIMIT - BLOCK_GAS_LIMIT * (NORMAL_DISPATCH_RATIO - AVERAGE_ON_INITIALIZE_RATIO) - EXTRINSIC_BASE_Weight] / WEIGHT_PER_GAS = (1_000_000_000_000 * 2 * (0.75-0.1) - 125_000_000) / 20000
const EXTRINSIC_GAS_LIMIT = 64995685;
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 (RPC execution)", (context) => {
step("should call with gas limit under block gas limit", async function () {
const result = await customRequest(context.web3, "eth_call", [
Expand Down Expand Up @@ -88,4 +94,43 @@ describeWithFrontier("Frontier RPC (RPC execution)", (context) => {
"provided gas limit is too high (can be up to 10x the block gas limit)"
);
});

step("should use the gas limit multiplier fallback", async function () {
const contract = new context.web3.eth.Contract(FORCE_GAS_CONTRACT_ABI);

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

await customRequest(context.web3, "eth_sendRawTransaction", [tx.rawTransaction]);
await createAndFinalizeBlock(context.web3);

const block = await context.web3.eth.getBlock("latest");

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

// When not specifying gas we expect the gas limit to default to a 10x block gas limit
// non-transactional call. The contract's method used requires close to block gas limit * 10.
const result = await customRequest(context.web3, "eth_call", [
{
to: contractAddress,
// require something close to the block gas limit * 10
data: contract.methods.force_gas(block.gasLimit * 10 - 500_000).encodeABI(),
},
]);

expect(result).to.include({
jsonrpc: "2.0",
result: "0x0000000000000000000000000000000000000000000000000000000000000001",
id: 1,
});
});
});

0 comments on commit 07c3a8e

Please sign in to comment.