Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add gas_limit_multiplier_support runtime api #935

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
});
});
});