From 4b3a72c82248061cc1a876bb83de5570d72f03e8 Mon Sep 17 00:00:00 2001 From: tgmichel Date: Tue, 13 Dec 2022 14:26:39 +0100 Subject: [PATCH] Add `gas_limit_multiplier_support` runtime api (#935) * Add `gas_limit_multiplier_support` runtime api * fmt * clippy * Ts test default gas limit * prettier --- client/rpc/src/eth/execute.rs | 18 +++++++--- primitives/rpc/src/lib.rs | 3 ++ template/runtime/src/lib.rs | 2 ++ ts-tests/contracts/ForceGasLimit.sol | 12 +++++++ ts-tests/tests/test-execute.ts | 49 ++++++++++++++++++++++++++-- 5 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 ts-tests/contracts/ForceGasLimit.sol diff --git a/client/rpc/src/eth/execute.rs b/client/rpc/src/eth/execute.rs index 202c4189cb..d35b647a6a 100644 --- a/client/rpc/src/eth/execute.rs +++ b/client/rpc/src/eth/execute.rs @@ -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(); @@ -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) => { @@ -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(); diff --git a/primitives/rpc/src/lib.rs b/primitives/rpc/src/lib.rs index 31f1d89913..e297d75ca3 100644 --- a/primitives/rpc/src/lib.rs +++ b/primitives/rpc/src/lib.rs @@ -164,6 +164,9 @@ sp_api::decl_runtime_apis! { ) -> Vec; /// Return the elasticity multiplier. fn elasticity() -> Option; + /// Used to determine if gas limit multiplier for non-transactional calls (eth_call/estimateGas) + /// is supported. + fn gas_limit_multiplier_support(); } #[api_version(2)] diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 71b03f911c..ab87f08e99 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -744,6 +744,8 @@ impl_runtime_apis! { fn elasticity() -> Option { Some(BaseFee::elasticity()) } + + fn gas_limit_multiplier_support() {} } impl fp_rpc::ConvertTransactionRuntimeApi for Runtime { diff --git a/ts-tests/contracts/ForceGasLimit.sol b/ts-tests/contracts/ForceGasLimit.sol new file mode 100644 index 0000000000..97eb4f9579 --- /dev/null +++ b/ts-tests/contracts/ForceGasLimit.sol @@ -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; + } +} \ No newline at end of file diff --git a/ts-tests/tests/test-execute.ts b/ts-tests/tests/test-execute.ts index 44a961f323..7c3aedbd65 100644 --- a/ts-tests/tests/test-execute.ts +++ b/ts-tests/tests/test-execute.ts @@ -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", [ @@ -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, + }); + }); });