From 9113580063a57b87835b73775ad78014f6e297ea Mon Sep 17 00:00:00 2001 From: Cyrill Leutwiler Date: Tue, 17 Dec 2024 17:04:19 +0100 Subject: [PATCH] the gas limit API Signed-off-by: Cyrill Leutwiler --- .../create_transient_storage_and_call.rs | 2 +- .../revive/fixtures/contracts/gas_limit.rs | 34 +++++++++++++++++++ .../frame/revive/src/benchmarking/mod.rs | 11 ++++++ substrate/frame/revive/src/limits.rs | 2 +- substrate/frame/revive/src/tests.rs | 26 ++++++++++++-- substrate/frame/revive/src/wasm/runtime.rs | 11 ++++++ substrate/frame/revive/src/weights.rs | 15 ++++++++ substrate/frame/revive/uapi/src/host.rs | 3 ++ .../frame/revive/uapi/src/host/riscv64.rs | 5 +++ 9 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 substrate/frame/revive/fixtures/contracts/gas_limit.rs diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs index d2efb26e5ceb..bce504eb9d5a 100644 --- a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -22,7 +22,7 @@ use common::input; use uapi::{HostFn, HostFnImpl as api, StorageFlags}; -static BUFFER: [u8; 512] = [0u8; 512]; +static BUFFER: [u8; 384] = [0u8; 384]; #[no_mangle] #[polkavm_derive::polkavm_export] diff --git a/substrate/frame/revive/fixtures/contracts/gas_limit.rs b/substrate/frame/revive/fixtures/contracts/gas_limit.rs new file mode 100644 index 000000000000..9ce82227b64d --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/gas_limit.rs @@ -0,0 +1,34 @@ +// This file is part of Substrate. + +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Returns the block ref_time limit back to the caller. + +#![no_std] +#![no_main] + +extern crate common; +use uapi::{HostFn, HostFnImpl as api, ReturnFlags}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + api::return_value(ReturnFlags::empty(), &api::gas_limit().to_le_bytes()); +} diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index 1fb4d7ab58a4..9e5c43e25a78 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -786,6 +786,17 @@ mod benchmarks { assert_eq!(U256::from_little_endian(&memory[..]), U256::from(128)); } + #[benchmark(pov_mode = Measured)] + fn seal_gas_limit() { + build_runtime!(runtime, memory: []); + let result; + #[block] + { + result = runtime.bench_gas_limit(&mut memory); + } + assert_eq!(result.unwrap(), T::BlockWeights::get().max_block.ref_time()); + } + #[benchmark(pov_mode = Measured)] fn seal_block_number() { build_runtime!(runtime, memory: [[0u8;32], ]); diff --git a/substrate/frame/revive/src/limits.rs b/substrate/frame/revive/src/limits.rs index 2e112baae301..df0b97198f9b 100644 --- a/substrate/frame/revive/src/limits.rs +++ b/substrate/frame/revive/src/limits.rs @@ -47,7 +47,7 @@ pub const NUM_EVENT_TOPICS: u32 = 4; pub const DELEGATE_DEPENDENCIES: u32 = 32; /// Maximum size of events (including topics) and storage values. -pub const PAYLOAD_BYTES: u32 = 512; +pub const PAYLOAD_BYTES: u32 = 384; /// The maximum size of the transient storage in bytes. /// diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 27ec7948e8fd..4a613fc0ab53 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -374,7 +374,7 @@ impl RegisteredChainExtension for TempStorageExtension { parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max( - Weight::from_parts(2u64 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), + Weight::from_parts(2 * WEIGHT_REF_TIME_PER_SECOND, u64::MAX), ); pub static ExistentialDeposit: u64 = 1; } @@ -382,6 +382,7 @@ parameter_types! { #[derive_impl(frame_system::config_preludes::TestDefaultConfig)] impl frame_system::Config for Test { type Block = Block; + type BlockWeights = BlockWeights; type AccountId = AccountId32; type Lookup = IdentityLookup; type AccountData = pallet_balances::AccountData; @@ -3483,7 +3484,7 @@ fn deposit_limit_in_nested_calls() { // Require more than the sender's balance. // Limit the sub call to little balance so it should fail in there let ret = builder::bare_call(addr_caller) - .data((512u32, &addr_callee, U256::from(1u64)).encode()) + .data((384u32, &addr_callee, U256::from(1u64)).encode()) .build_and_unwrap_result(); assert_return_code!(ret, RuntimeReturnCode::OutOfResources); @@ -4818,6 +4819,27 @@ fn skip_transfer_works() { }); } +#[test] +fn gas_limit_api_works() { + let (code, _) = compile_module("gas_limit").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create fixture: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + // Call the contract: It echoes back the value returned by the gas limit API. + let received = builder::bare_call(addr).build_and_unwrap_result(); + assert_eq!(received.flags, ReturnFlags::empty()); + assert_eq!( + u64::from_le_bytes(received.data[..].try_into().unwrap()), + ::BlockWeights::get().max_block.ref_time() + ); + }); +} + #[test] fn unknown_syscall_rejected() { let (code, _) = compile_module("unknown_syscall").unwrap(); diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 648a1621c198..d4a2afd72480 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -305,6 +305,8 @@ pub enum RuntimeCosts { BlockHash, /// Weight of calling `seal_now`. Now, + /// Weight of calling `seal_gas_limit`. + GasLimit, /// Weight of calling `seal_weight_to_fee`. WeightToFee, /// Weight of calling `seal_terminate`, passing the number of locked dependencies. @@ -452,6 +454,7 @@ impl Token for RuntimeCosts { BlockNumber => T::WeightInfo::seal_block_number(), BlockHash => T::WeightInfo::seal_block_hash(), Now => T::WeightInfo::seal_now(), + GasLimit => T::WeightInfo::seal_gas_limit(), WeightToFee => T::WeightInfo::seal_weight_to_fee(), Terminate(locked_dependencies) => T::WeightInfo::seal_terminate(locked_dependencies), DepositEvent { num_topic, len } => T::WeightInfo::seal_deposit_event(num_topic, len), @@ -1508,6 +1511,14 @@ pub mod env { )?) } + /// Returns the block ref_time limit. + /// See [`pallet_revive_uapi::HostFn::gas_limit`]. + #[stable] + fn gas_limit(&mut self, memory: &mut M) -> Result { + self.charge_gas(RuntimeCosts::GasLimit)?; + Ok(::BlockWeights::get().max_block.ref_time()) + } + /// Stores the value transferred along with this call/instantiate into the supplied buffer. /// See [`pallet_revive_uapi::HostFn::value_transferred`]. #[stable] diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index e8fec31b19e5..0b276943c8ea 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -84,6 +84,7 @@ pub trait WeightInfo { fn seal_block_number() -> Weight; fn seal_block_hash() -> Weight; fn seal_now() -> Weight; + fn seal_gas_limit() -> Weight; fn seal_weight_to_fee() -> Weight; fn seal_call_data_load() -> Weight; fn seal_input(n: u32, ) -> Weight; @@ -518,6 +519,13 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 244_000 picoseconds. Weight::from_parts(298_000, 0) } + fn seal_gas_limit() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 244_000 picoseconds. + Weight::from_parts(298_000, 0) + } fn seal_weight_to_fee() -> Weight { // Proof Size summary in bytes: // Measured: `0` @@ -1367,6 +1375,13 @@ impl WeightInfo for () { // Minimum execution time: 244_000 picoseconds. Weight::from_parts(298_000, 0) } + fn seal_gas_limit() -> Weight { + // Proof Size summary in bytes: + // Measured: `0` + // Estimated: `0` + // Minimum execution time: 244_000 picoseconds. + Weight::from_parts(298_000, 0) + } fn seal_weight_to_fee() -> Weight { // Proof Size summary in bytes: // Measured: `0` diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index aa3203697898..7cb51cfad70d 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -325,6 +325,9 @@ pub trait HostFn: private::Sealed { /// - `output`: A reference to the output data buffer to write the timestamp. fn now(output: &mut [u8; 32]); + /// Returns the block ref_time limit. + fn gas_limit() -> u64; + /// Cease contract execution and save a data buffer as a result of the execution. /// /// This function never returns as it stops execution of the caller. diff --git a/substrate/frame/revive/uapi/src/host/riscv64.rs b/substrate/frame/revive/uapi/src/host/riscv64.rs index d5a695262a24..10ff74e326c8 100644 --- a/substrate/frame/revive/uapi/src/host/riscv64.rs +++ b/substrate/frame/revive/uapi/src/host/riscv64.rs @@ -83,6 +83,7 @@ mod sys { pub fn chain_id(out_ptr: *mut u8); pub fn value_transferred(out_ptr: *mut u8); pub fn now(out_ptr: *mut u8); + pub fn gas_limit() -> u64; pub fn minimum_balance(out_ptr: *mut u8); pub fn deposit_event( topics_ptr: *const [u8; 32], @@ -393,6 +394,10 @@ impl HostFn for HostFnImpl { unsafe { sys::call_data_load(out_ptr.as_mut_ptr(), offset) }; } + fn gas_limit() -> u64 { + unsafe { sys::gas_limit() } + } + fn return_value(flags: ReturnFlags, return_value: &[u8]) -> ! { unsafe { sys::seal_return(flags.bits(), return_value.as_ptr(), return_value.len() as u32) } panic!("seal_return does not return");