diff --git a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs index 671c250ce2c9..db8198db4df6 100644 --- a/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs +++ b/cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs @@ -1078,7 +1078,7 @@ impl pallet_revive::Config for Runtime { type DepositPerByte = DepositPerByte; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; - type ChainExtension = (); + type Precompiles = (); type AddressMapper = pallet_revive::AccountId32Mapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; diff --git a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs index 50ee9cee032d..eb21aafd02f9 100644 --- a/cumulus/parachains/runtimes/testing/penpal/src/lib.rs +++ b/cumulus/parachains/runtimes/testing/penpal/src/lib.rs @@ -842,7 +842,7 @@ impl pallet_revive::Config for Runtime { type DepositPerByte = DepositPerByte; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; - type ChainExtension = (); + type Precompiles = (); type AddressMapper = pallet_revive::AccountId32Mapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; diff --git a/prdoc/pr_8262.prdoc b/prdoc/pr_8262.prdoc new file mode 100644 index 000000000000..3b821dac325f --- /dev/null +++ b/prdoc/pr_8262.prdoc @@ -0,0 +1,33 @@ +title: 'pallet_revive: Replace adhoc pre-compiles with pre-compile framework' +doc: +- audience: Runtime Dev + description: |- + This PRs adds the ability to define custom pre-compiles from outside the pallet. Before, all pre-compiles were hard coded as part of the pallet. + + ## Design + 1. Adding a pre-compile is as easy as implementing the new `Precompile` trait on any type. It can be added to the pallet by passing it into `Config::Precompiles`. This config know excepts a tuple of multiple types that each implement `Precompile`. + 2. Each pre-compile has to implement Solidity ABI. Meaning its inputs and outputs are encoded according to Eth ABI. This makes writing a pre-compile much simpler since it doesn't have to implement its own decoding logic. More importantly: It makes it trivial to consume the API from a Solidity contract. + 3. We constrain the address space of pre-compiles to a safe range so that they cannot accidentally match a wide range creating a collision with real contracts. + 4. We check that pre-compile address ranges do not overlap at compile time. + 5. Pre-compiles behave exactly as a normal contract. They exist as frames on the call stack and the environment they observe is their own (not the one of the calling contract). They can also be delegate called which changes the semantics in the same way as for normal contracts: They observe the environment of the calling contract. + 6. They can also be called by the origin without any other contract in-between. + + Check the rustdocs of the `precompile` module on how to write a pre-compile. + + ## Changes + 1. A new module `precompiles` is added that contains the framework to write pre-compiles. It also contains the sub module `builtin` that contains hard coded pre-compiles which exist Ethereum. + 2. The `pure_precompiles` module was deleted since all its pre-compiles were ported over to the new framework and are now housed in `builtin`. + 4. The `CallSetup` type is moved outside of the `benchmarking` module because it is also needed for testing code now. It is intended to be used for implementors outside of the crate to test the pre-compiles (in addition to benchmarking them). + + ## Follow Ups + - Enrich the `CallSetup` API with more functions in order to allow testing more complex scenarios. Should probably be done in tandem with writing the ERC20 pre-compile. + - The collision checks for pre-compile addresses are done at compile time. They need some `try_build` tests to make sure it works as intended. +crates: +- name: asset-hub-westend-runtime + bump: major +- name: pallet-revive + bump: major +- name: penpal-runtime + bump: major +- name: pallet-revive-fixtures + bump: major diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index fd5676ed5e0d..76ee3d161511 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -1475,7 +1475,7 @@ impl pallet_revive::Config for Runtime { type DepositPerByte = DepositPerByte; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_revive::weights::SubstrateWeight; - type ChainExtension = (); + type Precompiles = (); type AddressMapper = pallet_revive::AccountId32Mapper; type RuntimeMemory = ConstU32<{ 128 * 1024 * 1024 }>; type PVFMemory = ConstU32<{ 512 * 1024 * 1024 }>; diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 1ced936dae25..8eafd86e143b 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -62,6 +62,7 @@ xcm-builder = { workspace = true } [dev-dependencies] array-bytes = { workspace = true, default-features = true } assert_matches = { workspace = true } +polkavm-common = { version = "0.21.0" } pretty_assertions = { workspace = true } secp256k1 = { workspace = true, features = ["recovery"] } serde_json = { workspace = true } diff --git a/substrate/frame/revive/fixtures/contracts/call_and_returncode.rs b/substrate/frame/revive/fixtures/contracts/call_and_returncode.rs new file mode 100644 index 000000000000..82d7dbdabf15 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_and_returncode.rs @@ -0,0 +1,61 @@ +// 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. + +//! This calls another contract and returns the returncode and output. + +#![no_std] +#![no_main] +include!("../panic_handler.rs"); + +use uapi::{input, u256_bytes, HostFn, HostFnImpl as api}; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() {} + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + input!( + 512, + callee_addr: &[u8; 20], + value: u64, + callee_input: [u8], + ); + + // the first 4 bytes are reserved for the return code + let mut output = [0u8; 512]; + let output_ptr = &mut &mut output[4..]; + + let code = match api::call( + uapi::CallFlags::empty(), + callee_addr, + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &[u8::MAX; 32], // No deposit limit. + &u256_bytes(value), // Value transferred to the contract. + callee_input, + Some(output_ptr), + ) { + Ok(_) => 0, + Err(code) => code as u32, + }; + + let len = 4 + output_ptr.len(); + output[0..4].copy_from_slice(&code.to_le_bytes()); + api::return_value(uapi::ReturnFlags::empty(), &output[..len]); +} diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs index c90b42ca1bde..b42ed0e2c2a9 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs @@ -30,6 +30,9 @@ pub extern "C" fn deploy() {} pub extern "C" fn call() { input!(address: &[u8; 20],); + let mut output = [0; 512]; + let ptr = &mut &mut output[..]; + // Delegate call into passed address. let input = [0u8; 0]; api::delegate_call( @@ -39,7 +42,9 @@ pub extern "C" fn call() { u64::MAX, &[u8::MAX; 32], &input, - None, + Some(ptr), ) .unwrap(); + + assert_eq!(ptr.len(), 0); } diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking.rs similarity index 90% rename from substrate/frame/revive/src/benchmarking/mod.rs rename to substrate/frame/revive/src/benchmarking.rs index 5fcd7d42fa58..6d2618e03ade 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking.rs @@ -15,18 +15,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Benchmarks for the revive pallet +//! Benchmarks for the revive pallet. #![cfg(feature = "runtime-benchmarks")] -mod call_builder; -mod code; -use self::{call_builder::CallSetup, code::WasmModule}; use crate::{ + call_builder::{caller_funding, default_deposit_limit, CallSetup, Contract, WasmModule}, evm::runtime::GAS_PRICE, - exec::{Ext, Key, MomentOf}, + exec::{Key, MomentOf, PrecompileExt}, limits, - pure_precompiles::Precompile, + precompiles::{self, run::builtin as run_builtin_precompile}, storage::WriteOutcome, ConversionPrecision, Pallet as Contracts, *, }; @@ -47,10 +45,7 @@ use sp_consensus_babe::{ BABE_ENGINE_ID, }; use sp_consensus_slots::Slot; -use sp_runtime::{ - generic::{Digest, DigestItem}, - traits::{Bounded, Hash}, -}; +use sp_runtime::generic::{Digest, DigestItem}; /// How many runs we do per API benchmark. /// @@ -59,165 +54,28 @@ use sp_runtime::{ /// but might make the results less precise. const API_BENCHMARK_RUNS: u32 = 1600; -/// Number of layers in a Radix16 unbalanced trie. -const UNBALANCED_TRIE_LAYERS: u32 = 20; - -/// An instantiated and deployed contract. -#[derive(Clone)] -struct Contract { - caller: T::AccountId, - account_id: T::AccountId, - address: H160, -} - -impl Contract -where - T: Config, - BalanceOf: Into + TryFrom, - MomentOf: Into, - T::Hash: frame_support::traits::IsType, -{ - /// Create new contract and use a default account id as instantiator. - fn new(module: WasmModule, data: Vec) -> Result, &'static str> { - Self::with_index(0, module, data) - } - - /// Create new contract and use an account id derived from the supplied index as instantiator. - fn with_index( - index: u32, - module: WasmModule, - data: Vec, - ) -> Result, &'static str> { - Self::with_caller(account("instantiator", index, 0), module, data) - } - - /// Create new contract and use the supplied `caller` as instantiator. - fn with_caller( - caller: T::AccountId, - module: WasmModule, - data: Vec, - ) -> Result, &'static str> { - T::Currency::set_balance(&caller, caller_funding::()); - let salt = Some([0xffu8; 32]); - let origin: T::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); - - Contracts::::map_account(origin.clone()).unwrap(); - - let outcome = Contracts::::bare_instantiate( - origin, - 0u32.into(), - Weight::MAX, - DepositLimit::Balance(default_deposit_limit::()), - Code::Upload(module.code), - data, - salt, - ); - - let address = outcome.result?.addr; - let account_id = T::AddressMapper::to_fallback_account_id(&address); - let result = Contract { caller, address, account_id }; - - ContractInfoOf::::insert(&address, result.info()?); - - Ok(result) - } - - /// Create a new contract with the supplied storage item count and size each. - fn with_storage(code: WasmModule, stor_num: u32, stor_size: u32) -> Result { - let contract = Contract::::new(code, vec![])?; - let storage_items = (0..stor_num) - .map(|i| { - let hash = T::Hashing::hash_of(&i) - .as_ref() - .try_into() - .map_err(|_| "Hash too big for storage key")?; - Ok((hash, vec![42u8; stor_size as usize])) - }) - .collect::, &'static str>>()?; - contract.store(&storage_items)?; - Ok(contract) - } - - /// Store the supplied storage items into this contracts storage. - fn store(&self, items: &Vec<([u8; 32], Vec)>) -> Result<(), &'static str> { - let info = self.info()?; - for item in items { - info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) - .map_err(|_| "Failed to write storage to restoration dest")?; - } - >::insert(&self.address, info); - Ok(()) - } - - /// Create a new contract with the specified unbalanced storage trie. - fn with_unbalanced_storage_trie(code: WasmModule, key: &[u8]) -> Result { - if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 { - return Err("Key size too small to create the specified trie"); - } - - let value = vec![16u8; limits::PAYLOAD_BYTES as usize]; - let contract = Contract::::new(code, vec![])?; - let info = contract.info()?; - let child_trie_info = info.child_trie_info(); - child::put_raw(&child_trie_info, &key, &value); - for l in 0..UNBALANCED_TRIE_LAYERS { - let pos = l as usize / 2; - let mut key_new = key.to_vec(); - for i in 0u8..16 { - key_new[pos] = if l % 2 == 0 { - (key_new[pos] & 0xF0) | i - } else { - (key_new[pos] & 0x0F) | (i << 4) - }; - - if key == &key_new { - continue; - } - child::put_raw(&child_trie_info, &key_new, &value); - } - } - Ok(contract) - } - - /// Get the `ContractInfo` of the `addr` or an error if it no longer exists. - fn address_info(addr: &T::AccountId) -> Result, &'static str> { - ContractInfoOf::::get(T::AddressMapper::to_address(addr)) - .ok_or("Expected contract to exist at this point.") - } - - /// Get the `ContractInfo` of this contract or an error if it no longer exists. - fn info(&self) -> Result, &'static str> { - Self::address_info(&self.account_id) - } - - /// Set the balance of the contract to the supplied amount. - fn set_balance(&self, balance: BalanceOf) { - T::Currency::set_balance(&self.account_id, balance); - } - - /// Returns `true` iff all storage entries related to code storage exist. - fn code_exists(hash: &sp_core::H256) -> bool { - >::contains_key(hash) && >::contains_key(&hash) - } - - /// Returns `true` iff no storage entry related to code storage exist. - fn code_removed(hash: &sp_core::H256) -> bool { - !>::contains_key(hash) && !>::contains_key(&hash) - } -} - -/// The funding that each account that either calls or instantiates contracts is funded with. -fn caller_funding() -> BalanceOf { - // Minting can overflow, so we can't abuse of the funding. This value happens to be big enough, - // but not too big to make the total supply overflow. - BalanceOf::::max_value() / 10_000u32.into() -} - -/// The deposit limit we use for benchmarks. -fn default_deposit_limit() -> BalanceOf { - (T::DepositPerByte::get() * 1024u32.into() * 1024u32.into()) + - T::DepositPerItem::get() * 1024u32.into() -} +macro_rules! memory( + ($($bytes:expr,)*) => {{ + vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::>() + }}; +); + +macro_rules! build_runtime( + ($runtime:ident, $memory:ident: [$($segment:expr,)*]) => { + build_runtime!($runtime, _contract, $memory: [$($segment,)*]); + }; + ($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => { + build_runtime!($runtime, $contract); + let mut $memory = memory!($($bytes,)*); + }; + ($runtime:ident, $contract:ident) => { + let mut setup = CallSetup::::default(); + let $contract = setup.contract(); + let input = setup.data(); + let (mut ext, _) = setup.ext(); + let mut $runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input); + }; +); #[benchmarks( where @@ -1724,6 +1582,8 @@ mod benchmarks { let mut setup = CallSetup::::default(); setup.set_storage_deposit_limit(deposit); + // We benchmark the overhead of cloning the input. Not passing it to the contract. + // This is why we set the input here instead of passig it as pointer to the `bench_call`. setup.set_data(vec![42; i as usize]); setup.set_origin(Origin::from_account_id(setup.contract().account_id.clone())); @@ -1748,6 +1608,68 @@ mod benchmarks { assert_ok!(result); } + // d: 1 if the associated pre-compile has a contract info that needs to be loaded + // i: size of the input data + #[benchmark(pov_mode = Measured)] + fn seal_call_precompile(d: Linear<0, 1>, i: Linear<0, { limits::code::BLOB_BYTES }>) { + use alloy_core::sol_types::SolInterface; + use precompiles::{BenchmarkNoInfo, BenchmarkWithInfo, BuiltinPrecompile, IBenchmarking}; + + let callee_bytes = if d == 1 { + BenchmarkWithInfo::::MATCHER.base_address().to_vec() + } else { + BenchmarkNoInfo::::MATCHER.base_address().to_vec() + }; + let callee_len = callee_bytes.len() as u32; + + let deposit: BalanceOf = (u32::MAX - 100).into(); + let deposit_bytes = Into::::into(deposit).encode(); + let deposit_len = deposit_bytes.len() as u32; + + let value: BalanceOf = Zero::zero(); + let value_bytes = Into::::into(value).encode(); + let value_len = value_bytes.len() as u32; + + let input_bytes = IBenchmarking::IBenchmarkingCalls::bench(IBenchmarking::benchCall { + input: vec![42; i as usize].into(), + }) + .abi_encode(); + let input_len = input_bytes.len() as u32; + + let mut setup = CallSetup::::default(); + setup.set_storage_deposit_limit(deposit); + + let (mut ext, _) = setup.ext(); + let mut runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, vec![]); + let mut memory = memory!(callee_bytes, deposit_bytes, value_bytes, input_bytes,); + + let mut do_benchmark = || { + runtime.bench_call( + memory.as_mut_slice(), + pack_hi_lo(0, 0), // flags + callee + u64::MAX, // ref_time_limit + u64::MAX, // proof_size_limit + pack_hi_lo(callee_len, callee_len + deposit_len), /* deposit_ptr + + * value_pr */ + pack_hi_lo(input_len, callee_len + deposit_len + value_len), /* input len + + * input ptr */ + pack_hi_lo(0, SENTINEL), // output len + output ptr + ) + }; + + // first call of the pre-compile will create its contract info and account + // so we make sure to create it + assert_eq!(do_benchmark().unwrap(), ReturnErrorCode::Success); + + let result; + #[block] + { + result = do_benchmark(); + } + + assert_eq!(result.unwrap(), ReturnErrorCode::Success); + } + #[benchmark(pov_mode = Measured)] fn seal_delegate_call() -> Result<(), BenchmarkError> { let Contract { account_id: address, .. } = @@ -1862,7 +1784,11 @@ mod benchmarks { let result; #[block] { - result = pure_precompiles::Sha256::execute(ext.gas_meter_mut(), &input); + result = run_builtin_precompile( + &mut ext, + H160::from_low_u64_be(2).as_fixed_bytes(), + input.clone(), + ); } assert_eq!(sp_io::hashing::sha2_256(&input).to_vec(), result.unwrap().data); } @@ -1876,7 +1802,11 @@ mod benchmarks { let result; #[block] { - result = pure_precompiles::Identity::execute(ext.gas_meter_mut(), &input); + result = run_builtin_precompile( + &mut ext, + H160::from_low_u64_be(4).as_fixed_bytes(), + input.clone(), + ); } assert_eq!(input, result.unwrap().data); } @@ -1892,7 +1822,11 @@ mod benchmarks { let result; #[block] { - result = pure_precompiles::Ripemd160::execute(ext.gas_meter_mut(), &input); + result = run_builtin_precompile( + &mut ext, + H160::from_low_u64_be(3).as_fixed_bytes(), + input.clone(), + ); } let mut expected = [0u8; 32]; expected[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input)); @@ -1976,7 +1910,7 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn ecdsa_recover() { use hex_literal::hex; - let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549"); + let input = hex!("18c547e4f7b0f325ad1e56f57e26c745b09a3e503d86e00e5255ff7f715d3d1c000000000000000000000000000000000000000000000000000000000000001c73b1693892219d736caba55bdb67216e485557ea6b6af75f37096c9aa6a5a75feeb940b1d03b21e36b0e47e79769f095fe2ab855bd91e3a38756b7d75a9c4549").to_vec(); let expected = hex!("000000000000000000000000a94f5374fce5edbc8e2a8697c15331677e6ebf0b"); let mut call_setup = CallSetup::::default(); let (mut ext, _) = call_setup.ext(); @@ -1985,7 +1919,8 @@ mod benchmarks { #[block] { - result = pure_precompiles::ECRecover::execute(ext.gas_meter_mut(), &input); + result = + run_builtin_precompile(&mut ext, H160::from_low_u64_be(1).as_fixed_bytes(), input); } assert_eq!(result.unwrap().data, expected); @@ -1994,16 +1929,16 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn bn128_add() { use hex_literal::hex; - let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf"); + let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b3625f8c89ea3437f44f8fc8b6bfbb6312074dc6f983809a5e809ff4e1d076dd5850b38c7ced6e4daef9c4347f370d6d8b58f4b1d8dc61a3c59d651a0644a2a27cf").to_vec(); let expected = hex!("0a6678fd675aa4d8f0d03a1feb921a27f38ebdcb860cc083653519655acd6d79172fd5b3b2bfdd44e43bcec3eace9347608f9f0a16f1e184cb3f52e6f259cbeb"); let mut call_setup = CallSetup::::default(); let (mut ext, _) = call_setup.ext(); let result; - #[block] { - result = pure_precompiles::Bn128Add::execute(ext.gas_meter_mut(), &input); + result = + run_builtin_precompile(&mut ext, H160::from_low_u64_be(6).as_fixed_bytes(), input); } assert_eq!(result.unwrap().data, expected); @@ -2012,34 +1947,66 @@ mod benchmarks { #[benchmark(pov_mode = Measured)] fn bn128_mul() { use hex_literal::hex; - let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + let input = hex!("089142debb13c461f61523586a60732d8b69c5b38a3380a74da7b2961d867dbf2d5fc7bbc013c16d7945f190b232eacc25da675c0eb093fe6b9f1b4b4e107b36ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").to_vec(); let expected = hex!("0bf982b98a2757878c051bfe7eee228b12bc69274b918f08d9fcb21e9184ddc10b17c77cbf3c19d5d27e18cbd4a8c336afb488d0e92c18d56e64dd4ea5c437e6"); let mut call_setup = CallSetup::::default(); let (mut ext, _) = call_setup.ext(); let result; - #[block] { - result = pure_precompiles::Bn128Mul::execute(ext.gas_meter_mut(), &input); + result = + run_builtin_precompile(&mut ext, H160::from_low_u64_be(7).as_fixed_bytes(), input); } assert_eq!(result.unwrap().data, expected); } // `n`: pairings to perform - // This is a slow call: We reduce the number of runs to 20 to avoid the benchmark taking too - // long. #[benchmark(pov_mode = Measured)] - fn bn128_pairing(n: Linear<0, 20>) { - let input = pure_precompiles::generate_random_ecpairs(n as usize); + fn bn128_pairing(n: Linear<0, { 20 }>) { + fn generate_random_ecpairs(n: usize) -> Vec { + use bn::{AffineG1, AffineG2, Fr, Group, G1, G2}; + use rand::SeedableRng; + use rand_pcg::Pcg64; + let mut rng = Pcg64::seed_from_u64(1); + + let mut buffer = vec![0u8; n * 192]; + + let mut write = |element: &bn::Fq, offset: &mut usize| { + element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap(); + *offset += 32 + }; + + for i in 0..n { + let mut offset = i * 192; + let scalar = Fr::random(&mut rng); + + let g1 = G1::one() * scalar; + let g2 = G2::one() * scalar; + let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve"); + let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve"); + + write(&a.x(), &mut offset); + write(&a.y(), &mut offset); + write(&b.x().imaginary(), &mut offset); + write(&b.x().real(), &mut offset); + write(&b.y().imaginary(), &mut offset); + write(&b.y().real(), &mut offset); + } + + buffer + } + + let input = generate_random_ecpairs(n as usize); let mut call_setup = CallSetup::::default(); let (mut ext, _) = call_setup.ext(); let result; #[block] { - result = pure_precompiles::Bn128Pairing::execute(ext.gas_meter_mut(), &input); + result = + run_builtin_precompile(&mut ext, H160::from_low_u64_be(8).as_fixed_bytes(), input); } assert_ok!(result); } @@ -2056,7 +2023,8 @@ mod benchmarks { let result; #[block] { - result = pure_precompiles::Blake2F::execute(ext.gas_meter_mut(), &input); + result = + run_builtin_precompile(&mut ext, H160::from_low_u64_be(9).as_fixed_bytes(), input); } assert_ok!(result); } diff --git a/substrate/frame/revive/src/benchmarking/call_builder.rs b/substrate/frame/revive/src/benchmarking/call_builder.rs deleted file mode 100644 index 9cc1cedd6fc8..000000000000 --- a/substrate/frame/revive/src/benchmarking/call_builder.rs +++ /dev/null @@ -1,208 +0,0 @@ -// 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. - -use crate::{ - address::AddressMapper, - benchmarking::{default_deposit_limit, Contract, WasmModule}, - exec::{ExportedFunction, Ext, Key, Stack}, - storage::meter::Meter, - transient_storage::MeterEntry, - wasm::{PreparedCall, Runtime}, - BalanceOf, Config, Error, GasMeter, MomentOf, Origin, WasmBlob, Weight, -}; -use alloc::{vec, vec::Vec}; -use frame_benchmarking::benchmarking; -use sp_core::{H256, U256}; - -type StackExt<'a, T> = Stack<'a, T, WasmBlob>; - -/// A builder used to prepare a contract call. -pub struct CallSetup { - contract: Contract, - dest: T::AccountId, - origin: Origin, - gas_meter: GasMeter, - storage_meter: Meter, - value: BalanceOf, - data: Vec, - transient_storage_size: u32, -} - -impl Default for CallSetup -where - T: Config, - BalanceOf: Into + TryFrom, - MomentOf: Into, - T::Hash: frame_support::traits::IsType, -{ - fn default() -> Self { - Self::new(WasmModule::dummy()) - } -} - -impl CallSetup -where - T: Config, - BalanceOf: Into + TryFrom, - MomentOf: Into, - T::Hash: frame_support::traits::IsType, -{ - /// Setup a new call for the given module. - pub fn new(module: WasmModule) -> Self { - let contract = Contract::::new(module.clone(), vec![]).unwrap(); - let dest = contract.account_id.clone(); - let origin = Origin::from_account_id(contract.caller.clone()); - - let storage_meter = Meter::new(&origin, default_deposit_limit::(), 0u32.into()).unwrap(); - - // Whitelist contract account, as it is already accounted for in the call benchmark - benchmarking::add_to_whitelist( - frame_system::Account::::hashed_key_for(&contract.account_id).into(), - ); - - // Whitelist the contract's contractInfo as it is already accounted for in the call - // benchmark - benchmarking::add_to_whitelist( - crate::ContractInfoOf::::hashed_key_for(&T::AddressMapper::to_address( - &contract.account_id, - )) - .into(), - ); - - Self { - contract, - dest, - origin, - gas_meter: GasMeter::new(Weight::MAX), - storage_meter, - value: 0u32.into(), - data: vec![], - transient_storage_size: 0, - } - } - - /// Set the meter's storage deposit limit. - pub fn set_storage_deposit_limit(&mut self, balance: BalanceOf) { - self.storage_meter = Meter::new(&self.origin, balance, 0u32.into()).unwrap(); - } - - /// Set the call's origin. - pub fn set_origin(&mut self, origin: Origin) { - self.origin = origin; - } - - /// Set the contract's balance. - pub fn set_balance(&mut self, value: BalanceOf) { - self.contract.set_balance(value); - } - - /// Set the call's input data. - pub fn set_data(&mut self, value: Vec) { - self.data = value; - } - - /// Set the transient storage size. - pub fn set_transient_storage_size(&mut self, size: u32) { - self.transient_storage_size = size; - } - - /// Get the call's input data. - pub fn data(&self) -> Vec { - self.data.clone() - } - - /// Get the call's contract. - pub fn contract(&self) -> Contract { - self.contract.clone() - } - - /// Build the call stack. - pub fn ext(&mut self) -> (StackExt<'_, T>, WasmBlob) { - let mut ext = StackExt::bench_new_call( - T::AddressMapper::to_address(&self.dest), - self.origin.clone(), - &mut self.gas_meter, - &mut self.storage_meter, - self.value, - ); - if self.transient_storage_size > 0 { - Self::with_transient_storage(&mut ext.0, self.transient_storage_size).unwrap(); - } - ext - } - - /// Prepare a call to the module. - pub fn prepare_call<'a>( - ext: &'a mut StackExt<'a, T>, - module: WasmBlob, - input: Vec, - aux_data_size: u32, - ) -> PreparedCall<'a, StackExt<'a, T>> { - module - .prepare_call(Runtime::new(ext, input), ExportedFunction::Call, aux_data_size) - .unwrap() - } - - /// Add transient_storage - fn with_transient_storage(ext: &mut StackExt, size: u32) -> Result<(), &'static str> { - let &MeterEntry { amount, limit } = ext.transient_storage().meter().current(); - ext.transient_storage().meter().current_mut().limit = size; - for i in 1u32.. { - let mut key_data = i.to_le_bytes().to_vec(); - while key_data.last() == Some(&0) { - key_data.pop(); - } - let key = Key::try_from_var(key_data).unwrap(); - if let Err(e) = ext.set_transient_storage(&key, Some(Vec::new()), false) { - // Restore previous settings. - ext.transient_storage().meter().current_mut().limit = limit; - ext.transient_storage().meter().current_mut().amount = amount; - if e == Error::::OutOfTransientStorage.into() { - break; - } else { - return Err("Initialization of the transient storage failed"); - } - } - } - Ok(()) - } -} - -#[macro_export] -macro_rules! memory( - ($($bytes:expr,)*) => {{ - vec![].iter()$(.chain($bytes.iter()))*.cloned().collect::>() - }}; -); - -#[macro_export] -macro_rules! build_runtime( - ($runtime:ident, $memory:ident: [$($segment:expr,)*]) => { - $crate::build_runtime!($runtime, _contract, $memory: [$($segment,)*]); - }; - ($runtime:ident, $contract:ident, $memory:ident: [$($bytes:expr,)*]) => { - $crate::build_runtime!($runtime, $contract); - let mut $memory = $crate::memory!($($bytes,)*); - }; - ($runtime:ident, $contract:ident) => { - let mut setup = CallSetup::::default(); - let $contract = setup.contract(); - let input = setup.data(); - let (mut ext, _) = setup.ext(); - let mut $runtime = crate::wasm::Runtime::<_, [u8]>::new(&mut ext, input); - }; -); diff --git a/substrate/frame/revive/src/benchmarking/code.rs b/substrate/frame/revive/src/benchmarking/code.rs deleted file mode 100644 index 4512d6da2e90..000000000000 --- a/substrate/frame/revive/src/benchmarking/code.rs +++ /dev/null @@ -1,126 +0,0 @@ -// 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. - -//! Functions to procedurally construct contract code used for benchmarking. -//! -//! In order to be able to benchmark events that are triggered by contract execution -//! (API calls into seal, individual instructions), we need to generate contracts that -//! perform those events. Because those contracts can get very big we cannot simply define -//! them as text (.wat) as this will be too slow and consume too much memory. Therefore -//! we define this simple definition of a contract that can be passed to `create_code` that -//! compiles it down into a `WasmModule` that can be used as a contract's code. - -use crate::limits; -use alloc::{fmt::Write, string::ToString, vec::Vec}; -use pallet_revive_fixtures::bench as bench_fixtures; -use sp_core::H256; -use sp_io::hashing::keccak_256; - -/// A wasm module ready to be put on chain. -#[derive(Clone)] -pub struct WasmModule { - pub code: Vec, - pub hash: H256, -} - -impl WasmModule { - /// Return a contract code that does nothing. - pub fn dummy() -> Self { - Self::new(bench_fixtures::DUMMY.to_vec()) - } - - /// Same as [`Self::dummy`] but uses `replace_with` to make the code unique. - pub fn dummy_unique(replace_with: u32) -> Self { - Self::new(bench_fixtures::dummy_unique(replace_with)) - } - - /// Same as as `with_num_instructions` but based on the blob size. - /// - /// This is needed when we weigh a blob without knowing how much instructions it - /// contains. - pub fn sized(size: u32) -> Self { - // Due to variable length encoding of instructions this is not precise. But we only - // need rough numbers for our benchmarks. - Self::with_num_instructions(size / 3) - } - - /// A contract code of specified number of instructions that uses all its bytes for instructions - /// but will return immediately. - /// - /// All the basic blocks are maximum sized (only the first is important though). This is to - /// account for the fact that the interpreter will compile one basic block at a time even - /// when no code is executed. Hence this contract will trigger the compilation of a maximum - /// sized basic block and then return with its first instruction. - /// - /// All the code will be put into the "call" export. Hence this code can be safely used for the - /// `instantiate_with_code` benchmark where no compilation of any block should be measured. - pub fn with_num_instructions(num_instructions: u32) -> Self { - let mut text = " - pub @deploy: - ret - pub @call: - " - .to_string(); - for i in 0..num_instructions { - match i { - // return execution right away without breaking up basic block - // SENTINEL is a hard coded syscall that terminates execution - 0 => writeln!(text, "ecalli {}", crate::SENTINEL).unwrap(), - i if i % (limits::code::BASIC_BLOCK_SIZE - 1) == 0 => - text.push_str("fallthrough\n"), - _ => text.push_str("a0 = a1 + a2\n"), - } - } - text.push_str("ret\n"); - let code = polkavm_common::assembler::assemble(&text).unwrap(); - Self::new(code) - } - - /// A contract code that calls the "noop" host function in a loop depending in the input. - pub fn noop() -> Self { - Self::new(bench_fixtures::NOOP.to_vec()) - } - - /// A contract code that does unaligned memory accessed in a loop. - pub fn instr(do_load: bool) -> Self { - let load = match do_load { - false => "", - true => "a0 = u64 [a0]", - }; - let text = alloc::format!( - " - pub @deploy: - ret - pub @call: - @loop: - jump @done if t0 == a1 - {load} - t0 = t0 + 1 - jump @loop - @done: - ret - " - ); - let code = polkavm_common::assembler::assemble(&text).unwrap(); - Self::new(code) - } - - fn new(code: Vec) -> Self { - let hash = keccak_256(&code); - Self { code, hash: H256(hash) } - } -} diff --git a/substrate/frame/revive/src/call_builder.rs b/substrate/frame/revive/src/call_builder.rs new file mode 100644 index 000000000000..eb882378e3d2 --- /dev/null +++ b/substrate/frame/revive/src/call_builder.rs @@ -0,0 +1,475 @@ +// 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. + +//! Types to build an environment that can be used to test and benchmark host function / +//! pre-compiles. + +#![cfg(any(feature = "runtime-benchmarks", test))] +// A lot of this code is only used in benchmarking but we also want to use the code for testing +// pre-compiles eventually. For that we will probably export some of those types from the crate. +// Until then we simply ignore the warnings that arise when compiling tests without runtime +// benchmarks. +#![cfg_attr(test, allow(dead_code))] + +use crate::{ + address::AddressMapper, + exec::{ExportedFunction, Key, PrecompileExt, Stack}, + limits, + storage::meter::Meter, + transient_storage::MeterEntry, + wasm::{PreparedCall, Runtime}, + BalanceOf, Code, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DepositLimit, Error, + GasMeter, MomentOf, Origin, Pallet as Contracts, PristineCode, WasmBlob, Weight, +}; +use alloc::{vec, vec::Vec}; +use frame_support::{storage::child, traits::fungible::Mutate}; +use frame_system::RawOrigin; +use pallet_revive_fixtures::bench as bench_fixtures; +use sp_core::{Get, H160, H256, U256}; +use sp_io::hashing::keccak_256; +use sp_runtime::traits::{Bounded, Hash}; + +type StackExt<'a, T> = Stack<'a, T, WasmBlob>; + +/// A builder used to prepare a contract call. +pub struct CallSetup { + contract: Contract, + dest: T::AccountId, + origin: Origin, + gas_meter: GasMeter, + storage_meter: Meter, + value: BalanceOf, + data: Vec, + transient_storage_size: u32, +} + +impl Default for CallSetup +where + T: Config, + BalanceOf: Into + TryFrom, + MomentOf: Into, + T::Hash: frame_support::traits::IsType, +{ + fn default() -> Self { + Self::new(WasmModule::dummy()) + } +} + +impl CallSetup +where + T: Config, + BalanceOf: Into + TryFrom, + MomentOf: Into, + T::Hash: frame_support::traits::IsType, +{ + /// Setup a new call for the given module. + pub fn new(module: WasmModule) -> Self { + let contract = Contract::::new(module, vec![]).unwrap(); + let dest = contract.account_id.clone(); + let origin = Origin::from_account_id(contract.caller.clone()); + + let storage_meter = Meter::new(&origin, default_deposit_limit::(), 0u32.into()).unwrap(); + + #[cfg(feature = "runtime-benchmarks")] + { + // Whitelist contract account, as it is already accounted for in the call benchmark + frame_benchmarking::benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&contract.account_id).into(), + ); + + // Whitelist the contract's contractInfo as it is already accounted for in the call + // benchmark + frame_benchmarking::benchmarking::add_to_whitelist( + crate::ContractInfoOf::::hashed_key_for(&T::AddressMapper::to_address( + &contract.account_id, + )) + .into(), + ); + } + + Self { + contract, + dest, + origin, + gas_meter: GasMeter::new(Weight::MAX), + storage_meter, + value: 0u32.into(), + data: vec![], + transient_storage_size: 0, + } + } + + /// Set the meter's storage deposit limit. + pub fn set_storage_deposit_limit(&mut self, balance: BalanceOf) { + self.storage_meter = Meter::new(&self.origin, balance, 0u32.into()).unwrap(); + } + + /// Set the call's origin. + pub fn set_origin(&mut self, origin: Origin) { + self.origin = origin; + } + + /// Set the contract's balance. + pub fn set_balance(&mut self, value: BalanceOf) { + self.contract.set_balance(value); + } + + /// Set the call's input data. + pub fn set_data(&mut self, value: Vec) { + self.data = value; + } + + /// Set the transient storage size. + pub fn set_transient_storage_size(&mut self, size: u32) { + self.transient_storage_size = size; + } + + /// Get the call's input data. + pub fn data(&self) -> Vec { + self.data.clone() + } + + /// Get the call's contract. + pub fn contract(&self) -> Contract { + self.contract.clone() + } + + /// Build the call stack. + pub fn ext(&mut self) -> (StackExt<'_, T>, WasmBlob) { + let mut ext = StackExt::bench_new_call( + T::AddressMapper::to_address(&self.dest), + self.origin.clone(), + &mut self.gas_meter, + &mut self.storage_meter, + self.value, + ); + if self.transient_storage_size > 0 { + Self::with_transient_storage(&mut ext.0, self.transient_storage_size).unwrap(); + } + ext + } + + /// Prepare a call to the module. + pub fn prepare_call<'a>( + ext: &'a mut StackExt<'a, T>, + module: WasmBlob, + input: Vec, + aux_data_size: u32, + ) -> PreparedCall<'a, StackExt<'a, T>> { + module + .prepare_call(Runtime::new(ext, input), ExportedFunction::Call, aux_data_size) + .unwrap() + } + + /// Add transient_storage + fn with_transient_storage(ext: &mut StackExt, size: u32) -> Result<(), &'static str> { + let &MeterEntry { amount, limit } = ext.transient_storage().meter().current(); + ext.transient_storage().meter().current_mut().limit = size; + for i in 1u32.. { + let mut key_data = i.to_le_bytes().to_vec(); + while key_data.last() == Some(&0) { + key_data.pop(); + } + let key = Key::try_from_var(key_data).unwrap(); + if let Err(e) = ext.set_transient_storage(&key, Some(Vec::new()), false) { + // Restore previous settings. + ext.transient_storage().meter().current_mut().limit = limit; + ext.transient_storage().meter().current_mut().amount = amount; + if e == Error::::OutOfTransientStorage.into() { + break; + } else { + return Err("Initialization of the transient storage failed"); + } + } + } + Ok(()) + } +} + +/// The deposit limit we use for benchmarks. +pub fn default_deposit_limit() -> BalanceOf { + (T::DepositPerByte::get() * 1024u32.into() * 1024u32.into()) + + T::DepositPerItem::get() * 1024u32.into() +} + +/// The funding that each account that either calls or instantiates contracts is funded with. +pub fn caller_funding() -> BalanceOf { + // Minting can overflow, so we can't abuse of the funding. This value happens to be big enough, + // but not too big to make the total supply overflow. + BalanceOf::::max_value() / 10_000u32.into() +} + +/// An instantiated and deployed contract. +#[derive(Clone)] +pub struct Contract { + pub caller: T::AccountId, + pub account_id: T::AccountId, + pub address: H160, +} + +impl Contract +where + T: Config, + BalanceOf: Into + TryFrom, + MomentOf: Into, + T::Hash: frame_support::traits::IsType, +{ + /// Create new contract and use a default account id as instantiator. + pub fn new(module: WasmModule, data: Vec) -> Result, &'static str> { + let caller = T::AddressMapper::to_fallback_account_id(&crate::test_utils::ALICE_ADDR); + Self::with_caller(caller, module, data) + } + + /// Create new contract and use an account id derived from the supplied index as instantiator. + #[cfg(feature = "runtime-benchmarks")] + pub fn with_index( + index: u32, + module: WasmModule, + data: Vec, + ) -> Result, &'static str> { + Self::with_caller(frame_benchmarking::account("instantiator", index, 0), module, data) + } + + /// Create new contract and use the supplied `caller` as instantiator. + pub fn with_caller( + caller: T::AccountId, + module: WasmModule, + data: Vec, + ) -> Result, &'static str> { + T::Currency::set_balance(&caller, caller_funding::()); + let salt = Some([0xffu8; 32]); + let origin: T::RuntimeOrigin = RawOrigin::Signed(caller.clone()).into(); + + // We ignore the error since we might also pass an already mapped account here. + Contracts::::map_account(origin.clone()).ok(); + + #[cfg(feature = "runtime-benchmarks")] + frame_benchmarking::benchmarking::add_to_whitelist( + frame_system::Account::::hashed_key_for(&caller).into(), + ); + + let outcome = Contracts::::bare_instantiate( + origin, + 0u32.into(), + Weight::MAX, + DepositLimit::Balance(default_deposit_limit::()), + Code::Upload(module.code), + data, + salt, + ); + + let address = outcome.result?.addr; + let account_id = T::AddressMapper::to_fallback_account_id(&address); + let result = Contract { caller, address, account_id }; + + ContractInfoOf::::insert(&address, result.info()?); + + Ok(result) + } + + /// Create a new contract with the supplied storage item count and size each. + pub fn with_storage( + code: WasmModule, + stor_num: u32, + stor_size: u32, + ) -> Result { + let contract = Contract::::new(code, vec![])?; + let storage_items = (0..stor_num) + .map(|i| { + let hash = T::Hashing::hash_of(&i) + .as_ref() + .try_into() + .map_err(|_| "Hash too big for storage key")?; + Ok((hash, vec![42u8; stor_size as usize])) + }) + .collect::, &'static str>>()?; + contract.store(&storage_items)?; + Ok(contract) + } + + /// Store the supplied storage items into this contracts storage. + pub fn store(&self, items: &Vec<([u8; 32], Vec)>) -> Result<(), &'static str> { + let info = self.info()?; + for item in items { + info.write(&Key::Fix(item.0), Some(item.1.clone()), None, false) + .map_err(|_| "Failed to write storage to restoration dest")?; + } + >::insert(&self.address, info); + Ok(()) + } + + /// Create a new contract with the specified unbalanced storage trie. + pub fn with_unbalanced_storage_trie( + code: WasmModule, + key: &[u8], + ) -> Result { + /// Number of layers in a Radix16 unbalanced trie. + const UNBALANCED_TRIE_LAYERS: u32 = 20; + + if (key.len() as u32) < (UNBALANCED_TRIE_LAYERS + 1) / 2 { + return Err("Key size too small to create the specified trie"); + } + + let value = vec![16u8; limits::PAYLOAD_BYTES as usize]; + let contract = Contract::::new(code, vec![])?; + let info = contract.info()?; + let child_trie_info = info.child_trie_info(); + child::put_raw(&child_trie_info, &key, &value); + for l in 0..UNBALANCED_TRIE_LAYERS { + let pos = l as usize / 2; + let mut key_new = key.to_vec(); + for i in 0u8..16 { + key_new[pos] = if l % 2 == 0 { + (key_new[pos] & 0xF0) | i + } else { + (key_new[pos] & 0x0F) | (i << 4) + }; + + if key == &key_new { + continue; + } + child::put_raw(&child_trie_info, &key_new, &value); + } + } + Ok(contract) + } + + /// Get the `ContractInfo` of the `addr` or an error if it no longer exists. + pub fn address_info(addr: &T::AccountId) -> Result, &'static str> { + ContractInfoOf::::get(T::AddressMapper::to_address(addr)) + .ok_or("Expected contract to exist at this point.") + } + + /// Get the `ContractInfo` of this contract or an error if it no longer exists. + pub fn info(&self) -> Result, &'static str> { + Self::address_info(&self.account_id) + } + + /// Set the balance of the contract to the supplied amount. + pub fn set_balance(&self, balance: BalanceOf) { + T::Currency::set_balance(&self.account_id, balance); + } + + /// Returns `true` iff all storage entries related to code storage exist. + pub fn code_exists(hash: &sp_core::H256) -> bool { + >::contains_key(hash) && >::contains_key(&hash) + } + + /// Returns `true` iff no storage entry related to code storage exist. + pub fn code_removed(hash: &sp_core::H256) -> bool { + !>::contains_key(hash) && !>::contains_key(&hash) + } +} + +/// A wasm module ready to be put on chain. +#[derive(Clone)] +pub struct WasmModule { + pub code: Vec, + pub hash: H256, +} + +impl WasmModule { + /// Return a contract code that does nothing. + pub fn dummy() -> Self { + Self::new(bench_fixtures::DUMMY.to_vec()) + } + + fn new(code: Vec) -> Self { + let hash = keccak_256(&code); + Self { code, hash: H256(hash) } + } +} + +#[cfg(feature = "runtime-benchmarks")] +impl WasmModule { + /// Same as [`Self::dummy`] but uses `replace_with` to make the code unique. + pub fn dummy_unique(replace_with: u32) -> Self { + Self::new(bench_fixtures::dummy_unique(replace_with)) + } + + /// Same as as `with_num_instructions` but based on the blob size. + /// + /// This is needed when we weigh a blob without knowing how much instructions it + /// contains. + pub fn sized(size: u32) -> Self { + // Due to variable length encoding of instructions this is not precise. But we only + // need rough numbers for our benchmarks. + Self::with_num_instructions(size / 3) + } + + /// A contract code of specified number of instructions that uses all its bytes for instructions + /// but will return immediately. + /// + /// All the basic blocks are maximum sized (only the first is important though). This is to + /// account for the fact that the interpreter will compile one basic block at a time even + /// when no code is executed. Hence this contract will trigger the compilation of a maximum + /// sized basic block and then return with its first instruction. + /// + /// All the code will be put into the "call" export. Hence this code can be safely used for the + /// `instantiate_with_code` benchmark where no compilation of any block should be measured. + pub fn with_num_instructions(num_instructions: u32) -> Self { + use alloc::{fmt::Write, string::ToString}; + let mut text = " + pub @deploy: + ret + pub @call: + " + .to_string(); + for i in 0..num_instructions { + match i { + // return execution right away without breaking up basic block + // SENTINEL is a hard coded syscall that terminates execution + 0 => writeln!(text, "ecalli {}", crate::SENTINEL).unwrap(), + i if i % (limits::code::BASIC_BLOCK_SIZE - 1) == 0 => + text.push_str("fallthrough\n"), + _ => text.push_str("a0 = a1 + a2\n"), + } + } + text.push_str("ret\n"); + let code = polkavm_common::assembler::assemble(&text).unwrap(); + Self::new(code) + } + + /// A contract code that calls the "noop" host function in a loop depending in the input. + pub fn noop() -> Self { + Self::new(bench_fixtures::NOOP.to_vec()) + } + + /// A contract code that does unaligned memory accessed in a loop. + pub fn instr(do_load: bool) -> Self { + let load = match do_load { + false => "", + true => "a0 = u64 [a0]", + }; + let text = alloc::format!( + " + pub @deploy: + ret + pub @call: + @loop: + jump @done if t0 == a1 + {load} + t0 = t0 + 1 + jump @loop + @done: + ret + " + ); + let code = polkavm_common::assembler::assemble(&text).unwrap(); + Self::new(code) + } +} diff --git a/substrate/frame/revive/src/chain_extension.rs b/substrate/frame/revive/src/chain_extension.rs deleted file mode 100644 index 5b3e886a5628..000000000000 --- a/substrate/frame/revive/src/chain_extension.rs +++ /dev/null @@ -1,348 +0,0 @@ -// 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. - -//! A mechanism for runtime authors to augment the functionality of contracts. -//! -//! The runtime is able to call into any contract and retrieve the result using -//! [`bare_call`](crate::Pallet::bare_call). This already allows customization of runtime -//! behaviour by user generated code (contracts). However, often it is more straightforward -//! to allow the reverse behaviour: The contract calls into the runtime. We call the latter -//! one a "chain extension" because it allows the chain to extend the set of functions that are -//! callable by a contract. -//! -//! In order to create a chain extension the runtime author implements the [`ChainExtension`] -//! trait and declares it in this pallet's [configuration Trait](Config). All types -//! required for this endeavour are defined or re-exported in this module. There is an -//! implementation on `()` which can be used to signal that no chain extension is available. -//! -//! # Using multiple chain extensions -//! -//! Often there is a need for having multiple chain extensions. This is often the case when -//! some generally useful off-the-shelf extensions should be included. To have multiple chain -//! extensions they can be put into a tuple which is then passed to [`Config::ChainExtension`] like -//! this `type Extensions = (ExtensionA, ExtensionB)`. -//! -//! However, only extensions implementing [`RegisteredChainExtension`] can be put into a tuple. -//! This is because the [`RegisteredChainExtension::ID`] is used to decide which of those extensions -//! should be used when the contract calls a chain extensions. Extensions which are generally -//! useful should claim their `ID` with [the registry](https://github.com/paritytech/chainextension-registry) -//! so that no collisions with other vendors will occur. -//! -//! **Chain specific extensions must use the reserved `ID = 0` so that they can't be registered with -//! the registry.** -//! -//! # Security -//! -//! The chain author alone is responsible for the security of the chain extension. -//! This includes avoiding the exposure of exploitable functions and charging the -//! appropriate amount of weight. In order to do so benchmarks must be written and the -//! [`charge_weight`](Environment::charge_weight) function must be called **before** -//! carrying out any action that causes the consumption of the chargeable weight. -//! It cannot be overstated how delicate of a process the creation of a chain extension -//! is. Check whether using [`bare_call`](crate::Pallet::bare_call) suffices for the -//! use case at hand. -//! -//! # Benchmarking -//! -//! The builtin contract callable functions that pallet-revive provides all have -//! benchmarks that determine the correct weight that an invocation of these functions -//! induces. In order to be able to charge the correct weight for the functions defined -//! by a chain extension benchmarks must be written, too. In the near future this crate -//! will provide the means for easier creation of those specialized benchmarks. -//! -//! # Example -//! -//! The ink-examples repository maintains an -//! [end-to-end example](https://github.com/paritytech/ink-examples/tree/main/rand-extension) -//! on how to use a chain extension in order to provide new features to ink! contracts. - -use crate::{ - wasm::{Memory, Runtime, RuntimeCosts}, - Error, -}; -use alloc::vec::Vec; -use codec::Decode; -use frame_support::weights::Weight; -use sp_runtime::DispatchError; - -pub use crate::{exec::Ext, gas::ChargedAmount, storage::meter::Diff, Config}; -pub use frame_system::Config as SysConfig; -pub use pallet_revive_uapi::ReturnFlags; - -/// Result that returns a [`DispatchError`] on error. -pub type Result = core::result::Result; - -/// A trait used to extend the set of contract callable functions. -/// -/// In order to create a custom chain extension this trait must be implemented and supplied -/// to the pallet contracts configuration trait as the associated type of the same name. -/// Consult the [module documentation](self) for a general explanation of chain extensions. -/// -/// # Lifetime -/// -/// The extension will be [`Default`] initialized at the beginning of each call -/// (**not** per call stack) and dropped afterwards. Hence any value held inside the extension -/// can be used as a per-call scratch buffer. -pub trait ChainExtension { - /// Call the chain extension logic. - /// - /// This is the only function that needs to be implemented in order to write a - /// chain extensions. It is called whenever a contract calls the `seal_call_chain_extension` - /// imported wasm function. - /// - /// # Parameters - /// - `env`: Access to the remaining arguments and the execution environment. - /// - /// # Return - /// - /// In case of `Err` the contract execution is immediately suspended and the passed error - /// is returned to the caller. Otherwise the value of [`RetVal`] determines the exit - /// behaviour. - /// - /// # Note - /// - /// The [`Self::call`] can be invoked within a read-only context, where any state-changing calls - /// are disallowed. This information can be obtained using `env.ext().is_read_only()`. It is - /// crucial for the implementer to handle this scenario appropriately. - fn call, M: ?Sized + Memory>( - &mut self, - env: Environment, - ) -> Result; - - /// Determines whether chain extensions are enabled for this chain. - /// - /// The default implementation returns `true`. Therefore it is not necessary to overwrite - /// this function when implementing a chain extension. In case of `false` the deployment of - /// a contract that references `seal_call_chain_extension` will be denied and calling this - /// function will return [`NoChainExtension`](Error::NoChainExtension) without first calling - /// into [`call`](Self::call). - fn enabled() -> bool { - true - } -} - -/// A [`ChainExtension`] that can be composed with other extensions using a tuple. -/// -/// An extension that implements this trait can be put in a tuple in order to have multiple -/// extensions available. The tuple implementation routes requests based on the first two -/// most significant bytes of the `id` passed to `call`. -/// -/// If this extensions is to be used by multiple runtimes consider -/// [registering it](https://github.com/paritytech/chainextension-registry) to ensure that there -/// are no collisions with other vendors. -/// -/// # Note -/// -/// Currently, we support tuples of up to ten registered chain extensions. If more chain extensions -/// are needed consider opening an issue. -pub trait RegisteredChainExtension: ChainExtension { - /// The extensions globally unique identifier. - const ID: u16; -} - -#[impl_trait_for_tuples::impl_for_tuples(10)] -#[tuple_types_custom_trait_bound(RegisteredChainExtension)] -impl ChainExtension for Tuple { - fn call, M: ?Sized + Memory>( - &mut self, - mut env: Environment, - ) -> Result { - for_tuples!( - #( - if (Tuple::ID == env.ext_id()) && Tuple::enabled() { - return Tuple.call(env); - } - )* - ); - Err(Error::::NoChainExtension.into()) - } - - fn enabled() -> bool { - for_tuples!( - #( - if Tuple::enabled() { - return true; - } - )* - ); - false - } -} - -/// Determines the exit behaviour and return value of a chain extension. -pub enum RetVal { - /// The chain extensions returns the supplied value to its calling contract. - Converging(u32), - /// The control does **not** return to the calling contract. - /// - /// Use this to stop the execution of the contract when the chain extension returns. - /// The semantic is the same as for calling `seal_return`: The control returns to - /// the caller of the currently executing contract yielding the supplied buffer and - /// flags. - Diverging { flags: ReturnFlags, data: Vec }, -} - -/// Grants the chain extension access to its parameters and execution environment. -pub struct Environment<'a, 'b, E: Ext, M: ?Sized> { - /// The runtime contains all necessary functions to interact with the running contract. - runtime: &'a mut Runtime<'b, E, M>, - /// Reference to the contract's memory. - memory: &'a mut M, - /// Verbatim argument passed to `seal_call_chain_extension`. - id: u32, - /// Verbatim argument passed to `seal_call_chain_extension`. - input_ptr: u32, - /// Verbatim argument passed to `seal_call_chain_extension`. - input_len: u32, - /// Verbatim argument passed to `seal_call_chain_extension`. - output_ptr: u32, - /// Verbatim argument passed to `seal_call_chain_extension`. - output_len_ptr: u32, -} - -/// Functions that are available in every state of this type. -impl<'a, 'b, E: Ext, M: ?Sized + Memory> Environment<'a, 'b, E, M> { - /// Creates a new environment for consumption by a chain extension. - pub fn new( - runtime: &'a mut Runtime<'b, E, M>, - memory: &'a mut M, - id: u32, - input_ptr: u32, - input_len: u32, - output_ptr: u32, - output_len_ptr: u32, - ) -> Self { - Self { runtime, memory, id, input_ptr, input_len, output_ptr, output_len_ptr } - } - - /// The function id within the `id` passed by a contract. - /// - /// It returns the two least significant bytes of the `id` passed by a contract as the other - /// two bytes represent the chain extension itself (the code which is calling this function). - pub fn func_id(&self) -> u16 { - (self.id & 0x0000FFFF) as u16 - } - - /// The chain extension id within the `id` passed by a contract. - /// - /// It returns the two most significant bytes of the `id` passed by a contract which represent - /// the chain extension itself (the code which is calling this function). - pub fn ext_id(&self) -> u16 { - (self.id >> 16) as u16 - } - - /// Charge the passed `amount` of weight from the overall limit. - /// - /// It returns `Ok` when there the remaining weight budget is larger than the passed - /// `weight`. It returns `Err` otherwise. In this case the chain extension should - /// abort the execution and pass through the error. - /// - /// The returned value can be used to with [`Self::adjust_weight`]. Other than that - /// it has no purpose. - /// - /// # Note - /// - /// Weight is synonymous with gas in substrate. - pub fn charge_weight(&mut self, amount: Weight) -> Result { - self.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)) - } - - /// Adjust a previously charged amount down to its actual amount. - /// - /// This is when a maximum a priori amount was charged and then should be partially - /// refunded to match the actual amount. - pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) { - self.runtime.adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight)) - } - - /// Grants access to the execution environment of the current contract call. - /// - /// Consult the functions on the returned type before re-implementing those functions. - pub fn ext(&mut self) -> &mut E { - self.runtime.ext() - } - - /// Reads `min(max_len, in_len)` from contract memory. - /// - /// This does **not** charge any weight. The caller must make sure that the an - /// appropriate amount of weight is charged **before** reading from contract memory. - /// The reason for that is that usually the costs for reading data and processing - /// said data cannot be separated in a benchmark. Therefore a chain extension would - /// charge the overall costs either using `max_len` (worst case approximation) or using - /// [`in_len()`](Self::in_len). - pub fn read(&self, max_len: u32) -> Result> { - self.memory.read(self.input_ptr, self.input_len.min(max_len)) - } - - /// Reads `min(buffer.len(), in_len) from contract memory. - /// - /// This takes a mutable pointer to a buffer fills it with data and shrinks it to - /// the size of the actual data. Apart from supporting pre-allocated buffers it is - /// equivalent to to [`read()`](Self::read). - pub fn read_into(&self, buffer: &mut &mut [u8]) -> Result<()> { - let len = buffer.len(); - let sliced = { - let buffer = core::mem::take(buffer); - &mut buffer[..len.min(self.input_len as usize)] - }; - self.memory.read_into_buf(self.input_ptr, sliced)?; - *buffer = sliced; - Ok(()) - } - - /// Reads and decodes a type with a dynamic size from contract memory. - /// - /// Make sure to include `len` in your weight calculations. - pub fn read_as_unbounded(&mut self, len: u32) -> Result { - self.memory.read_as_unbounded(self.input_ptr, len) - } - - /// The length of the input as passed in as `input_len`. - /// - /// A chain extension would use this value to calculate the dynamic part of its - /// weight. For example a chain extension that calculates the hash of some passed in - /// bytes would use `in_len` to charge the costs of hashing that amount of bytes. - /// This also subsumes the act of copying those bytes as a benchmarks measures both. - pub fn in_len(&self) -> u32 { - self.input_len - } - - /// Write the supplied buffer to contract memory. - /// - /// If the contract supplied buffer is smaller than the passed `buffer` an `Err` is returned. - /// If `allow_skip` is set to true the contract is allowed to skip the copying of the buffer - /// by supplying the guard value of `pallet-revive::SENTINEL` as `out_ptr`. The - /// `weight_per_byte` is only charged when the write actually happens and is not skipped or - /// failed due to a too small output buffer. - pub fn write( - &mut self, - buffer: &[u8], - allow_skip: bool, - weight_per_byte: Option, - ) -> Result<()> { - self.runtime.write_sandbox_output( - self.memory, - self.output_ptr, - self.output_len_ptr, - buffer, - allow_skip, - |len| { - weight_per_byte.map(|w| RuntimeCosts::ChainExtension(w.saturating_mul(len.into()))) - }, - ) - } -} diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index d88d4f206fa4..25a1d1ebc214 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -19,8 +19,8 @@ use crate::{ address::{self, AddressMapper}, gas::GasMeter, limits, + precompiles::{All as AllPrecompiles, Instance as PrecompileInstance, Precompiles}, primitives::{ExecReturnValue, StorageDeposit}, - pure_precompiles::{self, is_precompile}, runtime_decl_for_revive_api::{Decode, Encode, RuntimeDebugNoBound, TypeInfo}, storage::{self, meter::Diff, WriteOutcome}, tracing::if_tracing, @@ -184,32 +184,8 @@ impl Origin { } } -/// An interface that provides access to the external environment in which the -/// smart-contract is executed. -/// -/// This interface is specialized to an account of the executing code, so all -/// operations are implicitly performed on that account. -/// -/// # Note -/// -/// This trait is sealed and cannot be implemented by downstream crates. -pub trait Ext: sealing::Sealed { - type T: Config; - - /// Call (possibly transferring some amount of funds) into the specified account. - /// - /// Returns the code size of the called contract. - fn call( - &mut self, - gas_limit: Weight, - deposit_limit: U256, - to: &H160, - value: U256, - input_data: Vec, - allows_reentry: bool, - read_only: bool, - ) -> Result<(), ExecError>; - +/// Environment functions only available to host functions. +pub trait Ext: PrecompileWithInfoExt { /// Execute code in the current frame. /// /// Returns the code size of the called contract. @@ -221,21 +197,6 @@ pub trait Ext: sealing::Sealed { input_data: Vec, ) -> Result<(), ExecError>; - /// Instantiate a contract from the given code. - /// - /// Returns the original code size of the called contract. - /// The newly created account will be associated with `code`. `value` specifies the amount of - /// value transferred from the caller to the newly created account. - fn instantiate( - &mut self, - gas_limit: Weight, - deposit_limit: U256, - code: H256, - value: U256, - input_data: Vec, - salt: Option<&[u8; 32]>, - ) -> Result; - /// Transfer all funds to `beneficiary` and delete the contract. /// /// Since this function removes the self contract eagerly, if succeeded, no further actions @@ -245,6 +206,36 @@ pub trait Ext: sealing::Sealed { /// call stack. fn terminate(&mut self, beneficiary: &H160) -> DispatchResult; + /// Returns the code hash of the contract being executed. + fn own_code_hash(&mut self) -> &H256; + + /// Sets new code hash and immutable data for an existing contract. + fn set_code_hash(&mut self, hash: H256) -> DispatchResult; + + /// Get the length of the immutable data. + /// + /// This query is free as it does not need to load the immutable data from storage. + /// Useful when we need a constant time lookup of the length. + fn immutable_data_len(&mut self) -> u32; + + /// Returns the immutable data of the current contract. + /// + /// Returns `Err(InvalidImmutableAccess)` if called from a constructor. + fn get_immutable_data(&mut self) -> Result; + + /// Set the immutable data of the current contract. + /// + /// Returns `Err(InvalidImmutableAccess)` if not called from a constructor. + /// + /// Note: Requires &mut self to access the contract info. + fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError>; + + /// Call some dispatchable and return the result. + fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo; +} + +/// Environment functions which are available to pre-compiles with `HAS_CONTRACT_INFO = true`. +pub trait PrecompileWithInfoExt: PrecompileExt { /// Returns the storage entry of the executing account by the given `key`. /// /// Returns `None` if the `key` wasn't previously set by `set_storage` or @@ -266,6 +257,43 @@ pub trait Ext: sealing::Sealed { take_old: bool, ) -> Result; + /// Charges `diff` from the meter. + fn charge_storage(&mut self, diff: &Diff); + + /// Instantiate a contract from the given code. + /// + /// Returns the original code size of the called contract. + /// The newly created account will be associated with `code`. `value` specifies the amount of + /// value transferred from the caller to the newly created account. + fn instantiate( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + code: H256, + value: U256, + input_data: Vec, + salt: Option<&[u8; 32]>, + ) -> Result; +} + +/// Environment functions which are available to all pre-compiles. +pub trait PrecompileExt: sealing::Sealed { + type T: Config; + + /// Call (possibly transferring some amount of funds) into the specified account. + /// + /// Returns the code size of the called contract. + fn call( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + to: &H160, + value: U256, + input_data: Vec, + allows_reentry: bool, + read_only: bool, + ) -> Result<(), ExecError>; + /// Returns the transient storage entry of the executing account for the given `key`. /// /// Returns `None` if the `key` wasn't previously set by `set_transient_storage` or @@ -306,9 +334,6 @@ pub trait Ext: sealing::Sealed { /// Returns the code size of the contract at the given `address` or zero. fn code_size(&self, address: &H160) -> u64; - /// Returns the code hash of the contract being executed. - fn own_code_hash(&mut self) -> &H256; - /// Check if the caller of the current contract is the origin of the whole call stack. /// /// This can be checked with `is_contract(self.caller())` as well. @@ -326,24 +351,6 @@ pub trait Ext: sealing::Sealed { ::AddressMapper::to_address(self.account_id()) } - /// Get the length of the immutable data. - /// - /// This query is free as it does not need to load the immutable data from storage. - /// Useful when we need a constant time lookup of the length. - fn immutable_data_len(&mut self) -> u32; - - /// Returns the immutable data of the current contract. - /// - /// Returns `Err(InvalidImmutableAccess)` if called from a constructor. - fn get_immutable_data(&mut self) -> Result; - - /// Set the immutable data of the current contract. - /// - /// Returns `Err(InvalidImmutableAccess)` if not called from a constructor. - /// - /// Note: Requires &mut self to access the contract info. - fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError>; - /// Returns the balance of the current contract. /// /// The `value_transferred` is already added. @@ -390,12 +397,6 @@ pub trait Ext: sealing::Sealed { /// Get a mutable reference to the nested gas meter. fn gas_meter_mut(&mut self) -> &mut GasMeter; - /// Charges `diff` from the meter. - fn charge_storage(&mut self, diff: &Diff); - - /// Call some dispatchable and return the result. - fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo; - /// Recovers ECDSA compressed public key based on signature and message hash. fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()>; @@ -412,12 +413,9 @@ pub trait Ext: sealing::Sealed { /// Get a mutable reference to the transient storage. /// Useful in benchmarks when it is sometimes necessary to modify and inspect the transient /// storage directly. - #[cfg(feature = "runtime-benchmarks")] + #[cfg(any(feature = "runtime-benchmarks", test))] fn transient_storage(&mut self) -> &mut TransientStorage; - /// Sets new code hash and immutable data for an existing contract. - fn set_code_hash(&mut self, hash: H256) -> DispatchResult; - /// Check if running in read-only context. fn is_read_only(&self) -> bool; @@ -558,14 +556,39 @@ struct DelegateInfo { pub callee: H160, } -/// Used in a delegate call frame arguments in order to override the executable and caller. -struct DelegatedCall { - /// The executable which is run instead of the contracts own `executable`. - executable: E, - /// The caller of the contract. - caller: Origin, - /// The address of the contract the call was delegated to. - callee: H160, +/// When calling an address it can either lead to execution of contract code or a pre-compile. +enum ExecutableOrPrecompile, Env> { + /// Contract code. + Executable(E), + /// Code inside the runtime (so called pre-compile). + Precompile { instance: PrecompileInstance, _phantom: PhantomData }, +} + +impl, Env> ExecutableOrPrecompile { + fn as_executable(&self) -> Option<&E> { + if let Self::Executable(executable) = self { + Some(executable) + } else { + None + } + } + + fn as_precompile(&self) -> Option<&PrecompileInstance> { + if let Self::Precompile { instance, .. } = self { + Some(instance) + } else { + None + } + } + + #[cfg(any(feature = "runtime-benchmarks", test))] + fn into_executable(self) -> Option { + if let Self::Executable(executable) = self { + Some(executable) + } else { + None + } + } } /// Parameter passed in when creating a new `Frame`. @@ -580,7 +603,7 @@ enum FrameArgs<'a, T: Config, E> { /// This frame was created by `seal_delegate_call` and hence uses different code than /// what is stored at [`Self::Call::dest`]. Its caller ([`DelegatedCall::caller`]) is the /// account which called the caller contract - delegated_call: Option>, + delegated_call: Option>, }, Instantiate { /// The contract or signed origin which instantiates the new contract. @@ -607,6 +630,8 @@ enum CachedContract { /// In this case a reload is neither allowed nor possible. Please note that recursive /// calls cannot remove a contract as this is checked and denied. Terminated, + /// The frame is associated with pre-compile that has no contract info. + None, } impl Frame { @@ -705,6 +730,13 @@ impl CachedContract { self.load(account_id); get_cached_or_panic_after_load!(mem::replace(self, Self::Terminated)) } + + /// Set the status to invalidate if is cached. + fn invalidate(&mut self) { + if matches!(self, CachedContract::Cached(_)) { + *self = CachedContract::Invalidated; + } + } } impl<'a, T, E> Stack<'a, T, E> @@ -796,7 +828,7 @@ where .map(|_| (address, stack.first_frame.last_frame_output)) } - #[cfg(feature = "runtime-benchmarks")] + #[cfg(any(feature = "runtime-benchmarks", test))] pub fn bench_new_call( dest: H160, origin: Origin, @@ -804,7 +836,7 @@ where storage_meter: &'a mut storage::meter::Meter, value: BalanceOf, ) -> (Self, E) { - Self::new( + let call = Self::new( FrameArgs::Call { dest: T::AddressMapper::to_account_id(&dest), cached_info: None, @@ -817,7 +849,8 @@ where false, ) .unwrap() - .unwrap() + .unwrap(); + (call.0, call.1.into_executable().unwrap()) } /// Create a new call stack. @@ -831,7 +864,7 @@ where storage_meter: &'a mut storage::meter::Meter, value: U256, skip_transfer: bool, - ) -> Result, ExecError> { + ) -> Result)>, ExecError> { origin.ensure_mapped()?; let Some((first_frame, executable)) = Self::new_frame( args, @@ -876,84 +909,109 @@ where deposit_limit: BalanceOf, read_only: bool, origin_is_caller: bool, - ) -> Result, E)>, ExecError> { - let (account_id, contract_info, executable, delegate, entry_point, nested_gas) = - match frame_args { - FrameArgs::Call { dest, cached_info, delegated_call } => { - let contract = if let Some(contract) = cached_info { - contract - } else { - if let Some(contract) = - >::get(T::AddressMapper::to_address(&dest)) - { - contract + ) -> Result, ExecutableOrPrecompile)>, ExecError> { + let (account_id, contract_info, executable, delegate, entry_point) = match frame_args { + FrameArgs::Call { dest, cached_info, delegated_call } => { + let address = T::AddressMapper::to_address(&dest); + let precompile = >::get(address.as_fixed_bytes()); + + // which contract info to load is unaffected by the fact if this + // is a delegate call or not + let mut contract = match (cached_info, &precompile) { + (Some(info), _) => CachedContract::Cached(info), + (None, None) => + if let Some(info) = >::get(&address) { + CachedContract::Cached(info) } else { - return Ok(None); + return Ok(None) + }, + (None, Some(precompile)) if precompile.has_contract_info() => { + if let Some(info) = >::get(&address) { + CachedContract::Cached(info) + } else { + let info = ContractInfo::new(&address, 0u32.into(), H256::zero())?; + CachedContract::Cached(info) } - }; - - let mut nested_gas = gas_meter.nested(gas_limit); - let (executable, delegate_caller) = if let Some(DelegatedCall { - executable, - caller, - callee, - }) = delegated_call + }, + (None, Some(_)) => CachedContract::None, + }; + + // in case of delegate the executable is not the one at `address` + let executable = if let Some(delegated_call) = &delegated_call { + if let Some(precompile) = + >::get(delegated_call.callee.as_fixed_bytes()) { - (executable, Some(DelegateInfo { caller, callee })) + ExecutableOrPrecompile::Precompile { + instance: precompile, + _phantom: Default::default(), + } } else { - (E::from_storage(contract.code_hash, &mut nested_gas)?, None) - }; - - ( - dest, - contract, - executable, - delegate_caller, - ExportedFunction::Call, - nested_gas, - ) - }, - FrameArgs::Instantiate { sender, executable, salt, input_data } => { - let deployer = T::AddressMapper::to_address(&sender); - let account_nonce = >::account_nonce(&sender); - let address = if let Some(salt) = salt { - address::create2(&deployer, executable.code(), input_data, salt) + let Some(info) = ContractInfoOf::::get(&delegated_call.callee) else { + return Ok(None); + }; + let executable = E::from_storage(info.code_hash, gas_meter)?; + ExecutableOrPrecompile::Executable(executable) + } + } else { + if let Some(precompile) = precompile { + ExecutableOrPrecompile::Precompile { + instance: precompile, + _phantom: Default::default(), + } } else { - use sp_runtime::Saturating; - address::create1( - &deployer, - // the Nonce from the origin has been incremented pre-dispatch, so we - // need to subtract 1 to get the nonce at the time of the call. - if origin_is_caller { - account_nonce.saturating_sub(1u32.into()).saturated_into() - } else { - account_nonce.saturated_into() - }, - ) - }; - let contract = ContractInfo::new( - &address, - >::account_nonce(&sender), - *executable.code_hash(), - )?; - ( - T::AddressMapper::to_fallback_account_id(&address), - contract, - executable, - None, - ExportedFunction::Constructor, - gas_meter.nested(gas_limit), + let executable = E::from_storage( + contract + .as_contract() + .expect("When not a precompile the contract was loaded above; qed") + .code_hash, + gas_meter, + )?; + ExecutableOrPrecompile::Executable(executable) + } + }; + + (dest, contract, executable, delegated_call, ExportedFunction::Call) + }, + FrameArgs::Instantiate { sender, executable, salt, input_data } => { + let deployer = T::AddressMapper::to_address(&sender); + let account_nonce = >::account_nonce(&sender); + let address = if let Some(salt) = salt { + address::create2(&deployer, executable.code(), input_data, salt) + } else { + use sp_runtime::Saturating; + address::create1( + &deployer, + // the Nonce from the origin has been incremented pre-dispatch, so we + // need to subtract 1 to get the nonce at the time of the call. + if origin_is_caller { + account_nonce.saturating_sub(1u32.into()).saturated_into() + } else { + account_nonce.saturated_into() + }, ) - }, - }; + }; + let contract = ContractInfo::new( + &address, + >::account_nonce(&sender), + *executable.code_hash(), + )?; + ( + T::AddressMapper::to_fallback_account_id(&address), + CachedContract::Cached(contract), + ExecutableOrPrecompile::Executable(executable), + None, + ExportedFunction::Constructor, + ) + }, + }; let frame = Frame { delegate, value_transferred, - contract_info: CachedContract::Cached(contract_info), + contract_info, account_id, entry_point, - nested_gas, + nested_gas: gas_meter.nested(gas_limit), nested_storage: storage_meter.nested(deposit_limit), allows_reentry: true, read_only, @@ -971,7 +1029,7 @@ where gas_limit: Weight, deposit_limit: BalanceOf, read_only: bool, - ) -> Result, ExecError> { + ) -> Result>, ExecError> { if self.frames.len() as u32 == limits::CALL_STACK_DEPTH { return Err(Error::::MaxCallDepthReached.into()); } @@ -1013,11 +1071,13 @@ where /// Run the current (top) frame. /// /// This can be either a call or an instantiate. - fn run(&mut self, executable: E, input_data: Vec) -> Result<(), ExecError> { + fn run( + &mut self, + executable: ExecutableOrPrecompile, + input_data: Vec, + ) -> Result<(), ExecError> { let frame = self.top_frame(); let entry_point = frame.entry_point; - let delegated_code_hash = - if frame.delegate.is_some() { Some(*executable.code_hash()) } else { None }; if_tracing(|tracer| { tracer.enter_child_span( @@ -1076,25 +1136,57 @@ where >::inc_account_nonce(caller.account_id()?); // The incremented refcount should be visible to the constructor. - >::increment_refcount(*executable.code_hash())?; + >::increment_refcount( + *executable + .as_executable() + .expect("Precompiles cannot be instantiated; qed") + .code_hash(), + )?; } // Every non delegate call or instantiate also optionally transfers the balance. // If it is a delegate call, then we've already transferred tokens in the // last non-delegate frame. - if delegated_code_hash.is_none() { + if frame.delegate.is_none() { Self::transfer_from_origin( &self.origin, &caller, - &frame.account_id, + account_id, frame.value_transferred, )?; } - let code_deposit = executable.code_info().deposit(); - let output = executable - .execute(self, entry_point, input_data) - .map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?; + // We need to make sure that the pre-compiles contract exist before executing it. + // A few more conditionals: + // - Only contracts with extended API (has_contract_info) are guaranteed to have an + // account. + // - Only when not delegate calling we are executing in the context of the pre-compile. + // Pre-compiles itself cannot delegate call. + if let Some(precompile) = executable.as_precompile() { + if precompile.has_contract_info() && + frame.delegate.is_none() && + !>::account_exists(account_id) + { + // prefix matching pre-compiles cannot have a contract info + // hence we only mint once per pre-compile + T::Currency::mint_into(account_id, T::Currency::minimum_balance())?; + // make sure the pre-compile does not destroy its account by accident + >::inc_consumers(account_id)?; + } + } + + let code_deposit = executable + .as_executable() + .map(|exec| exec.code_info().deposit()) + .unwrap_or_default(); + + let output = match executable { + ExecutableOrPrecompile::Executable(executable) => + executable.execute(self, entry_point, input_data), + ExecutableOrPrecompile::Precompile { instance, .. } => + instance.call(input_data, self), + } + .map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee })?; // Avoid useless work that would be reverted anyways. if output.did_revert() { @@ -1201,8 +1293,8 @@ where } // Record the storage meter changes of the nested call into the parent meter. - // If the dropped frame's contract wasn't terminated we update the deposit counter - // in its contract info. The load is necessary to pull it from storage in case + // If the dropped frame's contract has a contract info we update the deposit + // counter in its contract info. The load is necessary to pull it from storage in case // it was invalidated. frame.contract_info.load(account_id); let mut contract = frame.contract_info.into_contract(); @@ -1225,8 +1317,8 @@ where // cache needs to be invalidated because that one will invalidate the next cache // when it is popped from the stack. >::insert(T::AddressMapper::to_address(account_id), contract); - if let Some(c) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) { - c.contract_info = CachedContract::Invalidated; + if let Some(f) = self.frames_mut().skip(1).find(|f| f.account_id == *account_id) { + f.contract_info.invalidate(); } } } else { @@ -1373,80 +1465,6 @@ where } Some(System::::block_hash(&block_number).into()) } - - fn run_precompile( - &mut self, - precompile_address: H160, - is_delegate: bool, - is_read_only: bool, - value_transferred: U256, - input_data: &[u8], - ) -> Result<(), ExecError> { - if_tracing(|tracer| { - tracer.enter_child_span( - self.caller().account_id().map(T::AddressMapper::to_address).unwrap_or_default(), - precompile_address, - is_delegate, - is_read_only, - value_transferred, - &input_data, - self.gas_meter().gas_left(), - ); - }); - - let mut do_transaction = || -> ExecResult { - if !is_delegate { - Self::transfer_from_origin( - &self.origin, - &self.caller(), - &T::AddressMapper::to_fallback_account_id(&precompile_address), - value_transferred, - )?; - } - - pure_precompiles::Precompiles::::execute( - precompile_address, - self.gas_meter_mut(), - input_data, - ) - .map_err(|e| ExecError { error: e.error, origin: ErrorOrigin::Callee }) - }; - - let transaction_outcome = - with_transaction(|| -> TransactionOutcome> { - let output = do_transaction(); - match &output { - Ok(result) if !result.did_revert() => TransactionOutcome::Commit(Ok(output)), - _ => TransactionOutcome::Rollback(Ok(output)), - } - }); - - let output = match transaction_outcome { - Ok(output) => { - if_tracing(|tracer| { - let gas_consumed = top_frame!(self).nested_gas.gas_consumed(); - match &output { - Ok(output) => tracer.exit_child_span(&output, gas_consumed), - Err(e) => tracer.exit_child_span_with_error(e.error.into(), gas_consumed), - } - }); - - output - }, - Err(error) => { - if_tracing(|tracer| { - let gas_consumed = top_frame!(self).nested_gas.gas_consumed(); - tracer.exit_child_span_with_error(error.into(), gas_consumed); - }); - - Err(error.into()) - }, - }; - - output.map(|output| { - self.top_frame_mut().last_frame_output = output; - }) - } } impl<'a, T, E> Ext for Stack<'a, T, E> @@ -1457,37 +1475,249 @@ where MomentOf: Into, T::Hash: frame_support::traits::IsType, { - type T = T; - - fn call( + fn delegate_call( &mut self, gas_limit: Weight, deposit_limit: U256, - dest_addr: &H160, - value: U256, + address: H160, input_data: Vec, - allows_reentry: bool, - read_only: bool, ) -> Result<(), ExecError> { - // Before pushing the new frame: Protect the caller contract against reentrancy attacks. - // It is important to do this before calling `allows_reentry` so that a direct recursion - // is caught by it. - self.top_frame_mut().allows_reentry = allows_reentry; - // We reset the return data now, so it is cleared out even if no new frame was executed. - // This is for example the case for balance transfers or when creating the frame fails. + // This is for example the case for unknown code hashes or creating the frame fails. *self.last_frame_output_mut() = Default::default(); - let try_call = || { - // Enable read-only access if requested; cannot disable it if already set. - let is_read_only = read_only || self.is_read_only(); - - if is_precompile(dest_addr) { - return self.run_precompile(*dest_addr, false, is_read_only, value, &input_data); - } - - let dest = T::AddressMapper::to_account_id(dest_addr); - if !self.allows_reentry(&dest) { + let top_frame = self.top_frame_mut(); + let contract_info = top_frame.contract_info().clone(); + let account_id = top_frame.account_id.clone(); + let value = top_frame.value_transferred; + if let Some(executable) = self.push_frame( + FrameArgs::Call { + dest: account_id, + cached_info: Some(contract_info), + delegated_call: Some(DelegateInfo { + caller: self.caller().clone(), + callee: address, + }), + }, + value, + gas_limit, + deposit_limit.saturated_into::>(), + self.is_read_only(), + )? { + self.run(executable, input_data) + } else { + // Delegate-calls to non-contract accounts are considered success. + Ok(()) + } + } + + fn terminate(&mut self, beneficiary: &H160) -> DispatchResult { + if self.is_recursive() { + return Err(Error::::TerminatedWhileReentrant.into()); + } + let frame = self.top_frame_mut(); + if frame.entry_point == ExportedFunction::Constructor { + return Err(Error::::TerminatedInConstructor.into()); + } + let info = frame.terminate(); + let beneficiary_account = T::AddressMapper::to_account_id(beneficiary); + frame.nested_storage.terminate(&info, beneficiary_account); + + info.queue_trie_for_deletion(); + let account_address = T::AddressMapper::to_address(&frame.account_id); + ContractInfoOf::::remove(&account_address); + ImmutableDataOf::::remove(&account_address); + >::decrement_refcount(info.code_hash)?; + + Ok(()) + } + + fn own_code_hash(&mut self) -> &H256 { + &self.top_frame_mut().contract_info().code_hash + } + + /// TODO: This should be changed to run the constructor of the supplied `hash`. + /// + /// Because the immutable data is attached to a contract and not a code, + /// we need to update the immutable data too. + /// + /// Otherwise we open a massive footgun: + /// If the immutables changed in the new code, the contract will brick. + /// + /// A possible implementation strategy is to add a flag to `FrameArgs::Instantiate`, + /// so that `fn run()` will roll back any changes if this flag is set. + /// + /// After running the constructor, the new immutable data is already stored in + /// `self.immutable_data` at the address of the (reverted) contract instantiation. + /// + /// The `set_code_hash` contract API stays disabled until this change is implemented. + fn set_code_hash(&mut self, hash: H256) -> DispatchResult { + let frame = top_frame_mut!(self); + + let info = frame.contract_info(); + + let prev_hash = info.code_hash; + info.code_hash = hash; + + let code_info = CodeInfoOf::::get(hash).ok_or(Error::::CodeNotFound)?; + + let old_base_deposit = info.storage_base_deposit(); + let new_base_deposit = info.update_base_deposit(code_info.deposit()); + let deposit = StorageDeposit::Charge(new_base_deposit) + .saturating_sub(&StorageDeposit::Charge(old_base_deposit)); + + frame.nested_storage.charge_deposit(frame.account_id.clone(), deposit); + + >::increment_refcount(hash)?; + >::decrement_refcount(prev_hash)?; + Ok(()) + } + + fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo { + let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into(); + origin.add_filter(T::CallFilter::contains); + call.dispatch(origin) + } + + fn immutable_data_len(&mut self) -> u32 { + self.top_frame_mut().contract_info().immutable_data_len() + } + + fn get_immutable_data(&mut self) -> Result { + if self.top_frame().entry_point == ExportedFunction::Constructor { + return Err(Error::::InvalidImmutableAccess.into()); + } + + // Immutable is read from contract code being executed + let address = self + .top_frame() + .delegate + .as_ref() + .map(|d| d.callee) + .unwrap_or(T::AddressMapper::to_address(self.account_id())); + Ok(>::get(address).ok_or_else(|| Error::::InvalidImmutableAccess)?) + } + + fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError> { + let frame = self.top_frame_mut(); + if frame.entry_point == ExportedFunction::Call || data.is_empty() { + return Err(Error::::InvalidImmutableAccess.into()); + } + frame.contract_info().set_immutable_data_len(data.len() as u32); + >::insert(T::AddressMapper::to_address(&frame.account_id), &data); + Ok(()) + } +} + +impl<'a, T, E> PrecompileWithInfoExt for Stack<'a, T, E> +where + T: Config, + E: Executable, + BalanceOf: Into + TryFrom, + MomentOf: Into, + T::Hash: frame_support::traits::IsType, +{ + fn get_storage(&mut self, key: &Key) -> Option> { + self.top_frame_mut().contract_info().read(key) + } + + fn get_storage_size(&mut self, key: &Key) -> Option { + self.top_frame_mut().contract_info().size(key.into()) + } + + fn set_storage( + &mut self, + key: &Key, + value: Option>, + take_old: bool, + ) -> Result { + let frame = self.top_frame_mut(); + frame.contract_info.get(&frame.account_id).write( + key.into(), + value, + Some(&mut frame.nested_storage), + take_old, + ) + } + + fn charge_storage(&mut self, diff: &Diff) { + self.top_frame_mut().nested_storage.charge(diff) + } + + fn instantiate( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + code_hash: H256, + value: U256, + input_data: Vec, + salt: Option<&[u8; 32]>, + ) -> Result { + // We reset the return data now, so it is cleared out even if no new frame was executed. + // This is for example the case when creating the frame fails. + *self.last_frame_output_mut() = Default::default(); + + let executable = E::from_storage(code_hash, self.gas_meter_mut())?; + let sender = &self.top_frame().account_id; + let executable = self.push_frame( + FrameArgs::Instantiate { + sender: sender.clone(), + executable, + salt, + input_data: input_data.as_ref(), + }, + value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, + gas_limit, + deposit_limit.saturated_into::>(), + self.is_read_only(), + )?; + let address = T::AddressMapper::to_address(&self.top_frame().account_id); + self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) + .map(|_| address) + } +} + +impl<'a, T, E> PrecompileExt for Stack<'a, T, E> +where + T: Config, + E: Executable, + BalanceOf: Into + TryFrom, + MomentOf: Into, + T::Hash: frame_support::traits::IsType, +{ + type T = T; + + fn call( + &mut self, + gas_limit: Weight, + deposit_limit: U256, + dest_addr: &H160, + value: U256, + input_data: Vec, + allows_reentry: bool, + read_only: bool, + ) -> Result<(), ExecError> { + // Before pushing the new frame: Protect the caller contract against reentrancy attacks. + // It is important to do this before calling `allows_reentry` so that a direct recursion + // is caught by it. + self.top_frame_mut().allows_reentry = allows_reentry; + + // We reset the return data now, so it is cleared out even if no new frame was executed. + // This is for example the case for balance transfers or when creating the frame fails. + *self.last_frame_output_mut() = Default::default(); + + let try_call = || { + // Enable read-only access if requested; cannot disable it if already set. + let is_read_only = read_only || self.is_read_only(); + + // We can skip the stateful lookup for pre-compiles. + let dest = if >::get::(dest_addr.as_fixed_bytes()).is_some() { + T::AddressMapper::to_fallback_account_id(dest_addr) + } else { + T::AddressMapper::to_account_id(dest_addr) + }; + + if !self.allows_reentry(&dest) { return Err(>::ReentranceDenied.into()); } @@ -1554,128 +1784,6 @@ where result } - fn delegate_call( - &mut self, - gas_limit: Weight, - deposit_limit: U256, - address: H160, - input_data: Vec, - ) -> Result<(), ExecError> { - if is_precompile(&address) { - return self.run_precompile( - address, - true, - self.is_read_only(), - 0u32.into(), - &input_data, - ); - } - - // We reset the return data now, so it is cleared out even if no new frame was executed. - // This is for example the case for unknown code hashes or creating the frame fails. - *self.last_frame_output_mut() = Default::default(); - - // Delegate-calls to non-contract accounts are considered success. - let Some(info) = ContractInfoOf::::get(&address) else { return Ok(()) }; - let executable = E::from_storage(info.code_hash, self.gas_meter_mut())?; - let top_frame = self.top_frame_mut(); - let contract_info = top_frame.contract_info().clone(); - let account_id = top_frame.account_id.clone(); - let value = top_frame.value_transferred; - let executable = self.push_frame( - FrameArgs::Call { - dest: account_id, - cached_info: Some(contract_info), - delegated_call: Some(DelegatedCall { - executable, - caller: self.caller().clone(), - callee: address, - }), - }, - value, - gas_limit, - deposit_limit.saturated_into::>(), - self.is_read_only(), - )?; - self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) - } - - fn instantiate( - &mut self, - gas_limit: Weight, - deposit_limit: U256, - code_hash: H256, - value: U256, - input_data: Vec, - salt: Option<&[u8; 32]>, - ) -> Result { - // We reset the return data now, so it is cleared out even if no new frame was executed. - // This is for example the case when creating the frame fails. - *self.last_frame_output_mut() = Default::default(); - - let executable = E::from_storage(code_hash, self.gas_meter_mut())?; - let sender = &self.top_frame().account_id; - let executable = self.push_frame( - FrameArgs::Instantiate { - sender: sender.clone(), - executable, - salt, - input_data: input_data.as_ref(), - }, - value.try_into().map_err(|_| Error::::BalanceConversionFailed)?, - gas_limit, - deposit_limit.saturated_into::>(), - self.is_read_only(), - )?; - let address = T::AddressMapper::to_address(&self.top_frame().account_id); - self.run(executable.expect(FRAME_ALWAYS_EXISTS_ON_INSTANTIATE), input_data) - .map(|_| address) - } - - fn terminate(&mut self, beneficiary: &H160) -> DispatchResult { - if self.is_recursive() { - return Err(Error::::TerminatedWhileReentrant.into()); - } - let frame = self.top_frame_mut(); - if frame.entry_point == ExportedFunction::Constructor { - return Err(Error::::TerminatedInConstructor.into()); - } - let info = frame.terminate(); - let beneficiary_account = T::AddressMapper::to_account_id(beneficiary); - frame.nested_storage.terminate(&info, beneficiary_account); - - info.queue_trie_for_deletion(); - let account_address = T::AddressMapper::to_address(&frame.account_id); - ContractInfoOf::::remove(&account_address); - ImmutableDataOf::::remove(&account_address); - >::decrement_refcount(info.code_hash)?; - - Ok(()) - } - - fn get_storage(&mut self, key: &Key) -> Option> { - self.top_frame_mut().contract_info().read(key) - } - - fn get_storage_size(&mut self, key: &Key) -> Option { - self.top_frame_mut().contract_info().size(key.into()) - } - - fn set_storage( - &mut self, - key: &Key, - value: Option>, - take_old: bool, - ) -> Result { - let frame = self.top_frame_mut(); - frame.contract_info.get(&frame.account_id).write( - key.into(), - value, - Some(&mut frame.nested_storage), - take_old, - ) - } - fn get_transient_storage(&self, key: &Key) -> Option> { self.transient_storage.read(self.account_id(), key) } @@ -1741,10 +1849,6 @@ where .unwrap_or_default() } - fn own_code_hash(&mut self) -> &H256 { - &self.top_frame_mut().contract_info().code_hash - } - fn caller_is_origin(&self) -> bool { self.origin == self.caller() } @@ -1754,35 +1858,6 @@ where self.caller_is_origin() && self.origin == Origin::Root } - fn immutable_data_len(&mut self) -> u32 { - self.top_frame_mut().contract_info().immutable_data_len() - } - - fn get_immutable_data(&mut self) -> Result { - if self.top_frame().entry_point == ExportedFunction::Constructor { - return Err(Error::::InvalidImmutableAccess.into()); - } - - // Immutable is read from contract code being executed - let address = self - .top_frame() - .delegate - .as_ref() - .map(|d| d.callee) - .unwrap_or(T::AddressMapper::to_address(self.account_id())); - Ok(>::get(address).ok_or_else(|| Error::::InvalidImmutableAccess)?) - } - - fn set_immutable_data(&mut self, data: ImmutableData) -> Result<(), DispatchError> { - let frame = self.top_frame_mut(); - if frame.entry_point == ExportedFunction::Call || data.is_empty() { - return Err(Error::::InvalidImmutableAccess.into()); - } - frame.contract_info().set_immutable_data_len(data.len() as u32); - >::insert(T::AddressMapper::to_address(&frame.account_id), &data); - Ok(()) - } - fn balance(&self) -> U256 { self.account_balance(&self.top_frame().account_id) } @@ -1842,16 +1917,6 @@ where &mut self.top_frame_mut().nested_gas } - fn charge_storage(&mut self, diff: &Diff) { - self.top_frame_mut().nested_storage.charge(diff) - } - - fn call_runtime(&self, call: ::RuntimeCall) -> DispatchResultWithPostInfo { - let mut origin: T::RuntimeOrigin = RawOrigin::Signed(self.account_id().clone()).into(); - origin.add_filter(T::CallFilter::contains); - call.dispatch(origin) - } - fn ecdsa_recover(&self, signature: &[u8; 65], message_hash: &[u8; 32]) -> Result<[u8; 33], ()> { secp256k1_ecdsa_recover_compressed(signature, message_hash).map_err(|_| ()) } @@ -1873,48 +1938,11 @@ where self.top_frame_mut().contract_info() } - #[cfg(feature = "runtime-benchmarks")] + #[cfg(any(feature = "runtime-benchmarks", test))] fn transient_storage(&mut self) -> &mut TransientStorage { &mut self.transient_storage } - /// TODO: This should be changed to run the constructor of the supplied `hash`. - /// - /// Because the immutable data is attached to a contract and not a code, - /// we need to update the immutable data too. - /// - /// Otherwise we open a massive footgun: - /// If the immutables changed in the new code, the contract will brick. - /// - /// A possible implementation strategy is to add a flag to `FrameArgs::Instantiate`, - /// so that `fn run()` will roll back any changes if this flag is set. - /// - /// After running the constructor, the new immutable data is already stored in - /// `self.immutable_data` at the address of the (reverted) contract instantiation. - /// - /// The `set_code_hash` contract API stays disabled until this change is implemented. - fn set_code_hash(&mut self, hash: H256) -> DispatchResult { - let frame = top_frame_mut!(self); - - let info = frame.contract_info(); - - let prev_hash = info.code_hash; - info.code_hash = hash; - - let code_info = CodeInfoOf::::get(hash).ok_or(Error::::CodeNotFound)?; - - let old_base_deposit = info.storage_base_deposit(); - let new_base_deposit = info.update_base_deposit(code_info.deposit()); - let deposit = StorageDeposit::Charge(new_base_deposit) - .saturating_sub(&StorageDeposit::Charge(old_base_deposit)); - - frame.nested_storage.charge_deposit(frame.account_id.clone(), deposit); - - >::increment_refcount(hash)?; - >::decrement_refcount(prev_hash)?; - Ok(()) - } - fn is_read_only(&self) -> bool { self.top_frame().read_only } diff --git a/substrate/frame/revive/src/lib.rs b/substrate/frame/revive/src/lib.rs index 7f511ca889e1..3ffb88c6b8a8 100644 --- a/substrate/frame/revive/src/lib.rs +++ b/substrate/frame/revive/src/lib.rs @@ -21,13 +21,14 @@ #![cfg_attr(feature = "runtime-benchmarks", recursion_limit = "1024")] extern crate alloc; + mod address; mod benchmarking; +mod call_builder; mod exec; mod gas; mod limits; mod primitives; -mod pure_precompiles; mod storage; mod transient_storage; mod wasm; @@ -35,8 +36,8 @@ mod wasm; #[cfg(test)] mod tests; -pub mod chain_extension; pub mod evm; +pub mod precompiles; pub mod test_utils; pub mod tracing; pub mod weights; @@ -91,9 +92,9 @@ pub use weights::WeightInfo; #[cfg(doc)] pub use crate::wasm::SyscallDoc; -type TrieId = BoundedVec>; -type BalanceOf = +pub type BalanceOf = <::Currency as Inspect<::AccountId>>::Balance; +type TrieId = BoundedVec>; type CodeVec = BoundedVec>; type ImmutableData = BoundedVec>; @@ -188,8 +189,11 @@ pub mod pallet { type WeightInfo: WeightInfo; /// Type that allows the runtime authors to add new host functions for a contract to call. + /// + /// Pass in a tuple of types that implement [`precompiles::Precompile`]. #[pallet::no_default_bounds] - type ChainExtension: chain_extension::ChainExtension + Default; + #[allow(private_bounds)] + type Precompiles: precompiles::Precompiles; /// Find the author of the current block. type FindAuthor: FindAuthor; @@ -350,7 +354,7 @@ pub mod pallet { #[inject_runtime_type] type RuntimeCall = (); type CallFilter = (); - type ChainExtension = (); + type Precompiles = (); type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; @@ -421,10 +425,6 @@ pub mod pallet { InputForwarded = 0x0E, /// The amount of topics passed to `seal_deposit_events` exceeds the limit. TooManyTopics = 0x0F, - /// The chain does not provide a chain extension. Calling the chain extension results - /// in this error. Note that this usually shouldn't happen as deploying such contracts - /// is rejected. - NoChainExtension = 0x10, /// Failed to decode the XCM program. XCMDecodeFailed = 0x11, /// A contract with the same AccountId already exists. @@ -499,8 +499,6 @@ pub mod pallet { RefcountOverOrUnderflow = 0x2E, /// Unsupported precompile address UnsupportedPrecompileAddress = 0x2F, - /// Precompile Error - PrecompileFailure = 0x30, } /// A reason for the pallet contracts placing a hold on funds. @@ -938,6 +936,7 @@ pub mod pallet { >::increment_refcount(code_hash)?; >::decrement_refcount(contract.code_hash)?; contract.code_hash = code_hash; + Ok(()) }) } diff --git a/substrate/frame/revive/src/precompiles.rs b/substrate/frame/revive/src/precompiles.rs new file mode 100644 index 000000000000..c12527cee664 --- /dev/null +++ b/substrate/frame/revive/src/precompiles.rs @@ -0,0 +1,587 @@ +// 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. + +//! Exposes types that can be used to extend `pallet_revive` with additional functionality. +//! +//! In order to add a pre-compile: +//! +//! - Implement [`Precompile`] on a type. Most likely another pallet. +//! - Add the type to a tuple passed into [`Config::Precompiles`]. +//! - Use the types inside the `run` module to test and benchmark your pre-compile. +//! +//! Use `alloy` through our re-export in this module to implement Eth ABI. + +mod builtin; + +mod tests; + +pub use crate::{ + exec::{ExecError, PrecompileExt as Ext, PrecompileWithInfoExt as ExtWithInfo}, + gas::{GasMeter, Token}, + storage::meter::Diff, +}; +pub use alloy_core as alloy; + +use crate::{ + exec::ExecResult, precompiles::builtin::Builtin, primitives::ExecReturnValue, Config, + Error as CrateError, +}; +use alloc::vec::Vec; +use alloy::sol_types::{Panic, PanicKind, Revert, SolError, SolInterface}; +use core::num::NonZero; +use pallet_revive_uapi::ReturnFlags; +use sp_runtime::DispatchError; + +#[cfg(feature = "runtime-benchmarks")] +pub(crate) use builtin::{IBenchmarking, NoInfo as BenchmarkNoInfo, WithInfo as BenchmarkWithInfo}; + +const UNIMPLEMENTED: &str = "A precompile must either implement `call` or `call_with_info`"; + +/// The composition of all available pre-compiles. +/// +/// This is how the rest of the pallet discovers and calls pre-compiles. +pub(crate) type All = (Builtin, ::Precompiles); + +/// Used by [`Precompile`] in order to declare at which addresses it will be called. +/// +/// The 2 byte integer supplied here will be interpreted as big endian and copied to +/// `address[16,17]`. Address `address[18,19]` is reserved for builtin precompiles. All other +/// bytes are set to zero. +/// +/// Big endian is chosen because it lines up with how you would invoke a pre-compile in Solidity. +/// For example writing `staticcall(..., 0x05, ...)` in Solidity sets the highest (`address[19]`) +/// byte to `5`. +pub enum AddressMatcher { + /// The pre-compile will only be called for a single address. + /// + /// This means the precompile will only be invoked for: + /// ```ignore + /// 00000000000000000000000000000000pppp0000 + /// ``` + /// + /// Where `p` is the `u16` defined here as big endian. + Fixed(NonZero), + /// The pre-compile will be called for multiple addresses. + /// + /// This is useful when some information should be encoded into the address. + /// + /// This means the precompile will be invoked for all `x`: + /// ```ignore + /// xxxxxxxx000000000000000000000000pppp0000 + /// ``` + /// + /// Where `p` is the `u16` defined here as big endian. Hence a maximum of 2 byte can be encoded + /// into the address. Allowing more bytes could lead to the situation where legitimate + /// accounts could exist at this address. Either by accident or on purpose. + Prefix(NonZero), +} + +/// Same as `AddressMatcher` but for builtin pre-compiles. +/// +/// It works in the same way as `AddressMatcher` but allows setting the full 4 byte prefix. +/// Builtin pre-compiles must only use values `<= u16::MAX` to prevent collisions with +/// external pre-compiles. +pub(crate) enum BuiltinAddressMatcher { + Fixed(NonZero), + Prefix(NonZero), +} + +/// A pre-compile can error in the same way that a real contract can. +#[derive(derive_more::From, Debug)] +pub enum Error { + /// This is the same as a contract writing `revert("I reverted")`. + /// + /// Those are the errors that are commonly caught by Solidity try-catch blocks. Encodes + /// a string onto the output buffer. + Revert(Revert), + /// An error generated by Solidity itself. + /// + /// Encodes an error code into the output buffer. + Panic(PanicKind), + /// Don't encode anything into the output buffer. Just trap. + /// + /// Commonly used for out of gas or other resource errors. + Error(ExecError), +} + +impl From for Error { + fn from(error: DispatchError) -> Self { + Self::Error(error.into()) + } +} + +impl From> for Error { + fn from(error: CrateError) -> Self { + Self::Error(DispatchError::from(error).into()) + } +} + +/// Type that can be implemented in other crates to extend the list of pre-compiles. +/// +/// Only implement exactly one function. Either `call` or `call_with_info`. +/// +/// # Warning +/// +/// Pre-compiles are unmetered code. Hence they have to charge an appropriate amount of weight +/// themselves. Generally, their first line of code should be a call to +/// `env.gas_meter_mut().charge()`. For that you need to implement [`Token`] on a type of your +/// choosing. +pub trait Precompile { + /// Your runtime. + type T: Config; + /// The Solidity ABI definition of this pre-compile. + /// + /// Use the [`self::alloy::sol`] macro to define your interface using Solidity syntax. + /// The input the caller passes to the pre-compile will be validated and parsed + /// according to this interface. + /// + /// Please note that the return value is not validated and it is the pre-compiles + /// duty to return the abi encoded bytes conformant with the interface here. + type Interface: SolInterface; + /// Defines at which addresses this pre-compile exists. + const MATCHER: AddressMatcher; + /// Defines whether this pre-compile needs a contract info data structure in storage. + /// + /// Enabling it unlocks more APIs for the pre-compile to use. Only pre-compiles with a + /// fixed matcher can set this to true. This is enforced at compile time. Reason is that + /// contract info is per address and not per pre-compile. Too many contract info structures + /// and accounts would be created otherwise. + /// + /// # When set to **true** + /// + /// - An account will be created at the pre-compiles address when it is called for the first + /// time. The ed is minted. + /// - Contract info data structure will be created in storage on first call. + /// - Only `call_with_info` should be implemented. `call` is never called. + /// + /// # When set to **false** + /// + /// - No account or any other state will be created for the address. + /// - Only `call` should be implemented. `call_with_info` is never called. + /// + /// # What to use + /// + /// Should be set to false if the additional functionality is not needed. A pre-compile with + /// contract info will incur both a storage read and write to its contract metadata when called. + /// + /// The contract info enables additional functionality: + /// - Storage deposits: Collect deposits from the origin rather than the caller. This makes it + /// easier for contracts to interact with the pre-compile as deposits + /// are paid by the transaction signer (just like gas). It also makes refunding easier. + /// - Contract storage: You can use the contracts key value child trie storage instead of + /// providing your own state. + /// The contract storage automatically takes care of deposits. + /// Providing your own storage and using pallet_revive to collect deposits is also possible, + /// though. + /// - Instantitation: Contract instantiation requires the instantiator to have an account. This + /// is because its nonce is used to derive the new contracts account id and child trie id. + /// + /// Have a look at [`ExtWithInfo`] to learn about the additional APIs that a contract info + /// unlocks. + const HAS_CONTRACT_INFO: bool; + + /// Entry point for your pre-compile when `HAS_CONTRACT_INFO = false`. + #[allow(unused_variables)] + fn call( + address: &[u8; 20], + input: &Self::Interface, + env: &mut impl Ext, + ) -> Result, Error> { + unimplemented!("{UNIMPLEMENTED}") + } + + /// Entry point for your pre-compile when `HAS_CONTRACT_INFO = true`. + #[allow(unused_variables)] + fn call_with_info( + address: &[u8; 20], + input: &Self::Interface, + env: &mut impl ExtWithInfo, + ) -> Result, Error> { + unimplemented!("{UNIMPLEMENTED}") + } +} + +/// Same as `Precompile` but meant to be used by builtin pre-compiles. +/// +/// This enabled builtin precompiles to exist at the highest bits. Those are not +/// available to external pre-compiles in order to avoid collisions. +/// +/// Automatically implemented for all types that implement `Precompile`. +pub(crate) trait BuiltinPrecompile { + type T: Config; + type Interface: SolInterface; + const MATCHER: BuiltinAddressMatcher; + const HAS_CONTRACT_INFO: bool; + + fn call( + _address: &[u8; 20], + _input: &Self::Interface, + _env: &mut impl Ext, + ) -> Result, Error> { + unimplemented!("{UNIMPLEMENTED}") + } + + fn call_with_info( + _address: &[u8; 20], + _input: &Self::Interface, + _env: &mut impl ExtWithInfo, + ) -> Result, Error> { + unimplemented!("{UNIMPLEMENTED}") + } +} + +/// A low level pre-compile that does use Solidity ABI. +/// +/// It is used to implement the original Ethereum pre-compiles which do not +/// use Solidity ABI but just encode inputs and outputs packed in memory. +/// +/// Automatically implemented for all types that implement `BuiltinPrecompile`. +/// By extension also automatically implemented for all types implementing `Precompile`. +pub(crate) trait PrimitivePrecompile { + type T: Config; + const MATCHER: BuiltinAddressMatcher; + const HAS_CONTRACT_INFO: bool; + + fn call( + _address: &[u8; 20], + _input: Vec, + _env: &mut impl Ext, + ) -> Result, Error> { + unimplemented!("{UNIMPLEMENTED}") + } + + fn call_with_info( + _address: &[u8; 20], + _input: Vec, + _env: &mut impl ExtWithInfo, + ) -> Result, Error> { + unimplemented!("{UNIMPLEMENTED}") + } +} + +/// A pre-compile ready to be called. +pub(crate) struct Instance { + has_contract_info: bool, + address: [u8; 20], + /// This is the function inside `PrimitivePrecompile` at `address`. + function: fn(&[u8; 20], Vec, &mut E) -> Result, Error>, +} + +impl Instance { + pub fn has_contract_info(&self) -> bool { + self.has_contract_info + } + + pub fn call(&self, input: Vec, env: &mut E) -> ExecResult { + let result = (self.function)(&self.address, input, env); + match result { + Ok(data) => Ok(ExecReturnValue { flags: ReturnFlags::empty(), data }), + Err(Error::Revert(msg)) => + Ok(ExecReturnValue { flags: ReturnFlags::REVERT, data: msg.abi_encode() }), + Err(Error::Panic(kind)) => Ok(ExecReturnValue { + flags: ReturnFlags::REVERT, + data: Panic::from(kind).abi_encode(), + }), + Err(Error::Error(err)) => Err(err.into()), + } + } +} + +/// A composition of pre-compiles. +/// +/// Automatically implemented for tuples of types that implement any of the +/// pre-compile traits. +pub(crate) trait Precompiles { + /// Used to generate compile time error when multiple pre-compiles use the same matcher. + const CHECK_COLLISION: (); + /// Does any of the pre-compiles use the range reserved for external pre-compiles. + /// + /// This is just used to generate a compile time error if `Builtin` is using the external + /// range by accident. + const USES_EXTERNAL_RANGE: bool; + + /// Get a reference to a specific pre-compile. + /// + /// Returns `None` if no pre-compile exists at `address`. + fn get>(address: &[u8; 20]) -> Option>; +} + +impl BuiltinPrecompile for P { + type T = ::T; + type Interface = ::Interface; + const MATCHER: BuiltinAddressMatcher = P::MATCHER.into_builtin(); + const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO; + + fn call( + address: &[u8; 20], + input: &Self::Interface, + env: &mut impl Ext, + ) -> Result, Error> { + Self::call(address, input, env) + } + + fn call_with_info( + address: &[u8; 20], + input: &Self::Interface, + env: &mut impl ExtWithInfo, + ) -> Result, Error> { + Self::call_with_info(address, input, env) + } +} + +impl PrimitivePrecompile for P { + type T = ::T; + const MATCHER: BuiltinAddressMatcher = P::MATCHER; + const HAS_CONTRACT_INFO: bool = P::HAS_CONTRACT_INFO; + + fn call( + address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + let call = ::Interface::abi_decode(&input, true) + .map_err(|_| Error::Panic(PanicKind::ResourceError))?; + ::call(address, &call, env) + } + + fn call_with_info( + address: &[u8; 20], + input: Vec, + env: &mut impl ExtWithInfo, + ) -> Result, Error> { + let call = ::Interface::abi_decode(&input, true) + .map_err(|_| Error::Panic(PanicKind::ResourceError))?; + ::call_with_info(address, &call, env) + } +} + +#[impl_trait_for_tuples::impl_for_tuples(20)] +#[tuple_types_custom_trait_bound(PrimitivePrecompile)] +impl Precompiles for Tuple { + const CHECK_COLLISION: () = { + let matchers = [for_tuples!( #( Tuple::MATCHER ),* )]; + if BuiltinAddressMatcher::has_duplicates(&matchers) { + panic!("Precompiles with duplicate matcher detected") + } + for_tuples!( + #( + let is_fixed = Tuple::MATCHER.is_fixed(); + let has_info = Tuple::HAS_CONTRACT_INFO; + assert!(is_fixed || !has_info, "Only fixed precompiles can have a contract info."); + )* + ); + }; + const USES_EXTERNAL_RANGE: bool = { + let mut uses_external = false; + for_tuples!( + #( + if Tuple::MATCHER.suffix() > u16::MAX as u32 { + uses_external = true; + } + )* + ); + uses_external + }; + + fn get>(address: &[u8; 20]) -> Option> { + let _ = >::CHECK_COLLISION; + let mut has_contract_info = false; + let mut function: Option, &mut E) -> Result, Error>> = None; + for_tuples!( + #( + if Tuple::MATCHER.matches(address) { + if Tuple::HAS_CONTRACT_INFO { + has_contract_info = true; + function = Some(Tuple::call_with_info); + } else { + has_contract_info = false; + function = Some(Tuple::call); + } + } + )* + ); + function.map(|function| Instance { has_contract_info, address: *address, function }) + } +} + +impl Precompiles for (Builtin, ::Precompiles) { + const CHECK_COLLISION: () = { + assert!( + !>::USES_EXTERNAL_RANGE, + "Builtin precompiles must not use addresses reserved for external precompiles" + ); + }; + const USES_EXTERNAL_RANGE: bool = { ::Precompiles::USES_EXTERNAL_RANGE }; + + fn get>(address: &[u8; 20]) -> Option> { + let _ = >::CHECK_COLLISION; + >::get(address).or_else(|| ::Precompiles::get(address)) + } +} + +impl AddressMatcher { + pub const fn base_address(&self) -> [u8; 20] { + self.into_builtin().base_address() + } + + pub const fn highest_address(&self) -> [u8; 20] { + self.into_builtin().highest_address() + } + + pub const fn matches(&self, address: &[u8; 20]) -> bool { + self.into_builtin().matches(address) + } + + const fn into_builtin(&self) -> BuiltinAddressMatcher { + const fn left_shift(val: NonZero) -> NonZero { + let shifted = (val.get() as u32) << 16; + NonZero::new(shifted).expect( + "Value was non zero before. + The shift is small enough to not truncate any existing bits. + Hence the value is still non zero; qed", + ) + } + + match self { + Self::Fixed(i) => BuiltinAddressMatcher::Fixed(left_shift(*i)), + Self::Prefix(i) => BuiltinAddressMatcher::Prefix(left_shift(*i)), + } + } +} + +impl BuiltinAddressMatcher { + pub const fn base_address(&self) -> [u8; 20] { + let suffix = self.suffix().to_be_bytes(); + let mut address = [0u8; 20]; + let mut i = 16; + while i < address.len() { + address[i] = suffix[i - 16]; + i = i + 1; + } + address + } + + pub const fn highest_address(&self) -> [u8; 20] { + let mut address = self.base_address(); + match self { + Self::Fixed(_) => (), + Self::Prefix(_) => { + address[0] = 0xFF; + address[1] = 0xFF; + address[2] = 0xFF; + address[3] = 0xFF; + }, + } + address + } + + pub const fn matches(&self, address: &[u8; 20]) -> bool { + let base_address = self.base_address(); + let mut i = match self { + Self::Fixed(_) => 0, + Self::Prefix(_) => 4, + }; + while i < base_address.len() { + if address[i] != base_address[i] { + return false + } + i = i + 1; + } + true + } + + const fn suffix(&self) -> u32 { + match self { + Self::Fixed(i) => i.get(), + Self::Prefix(i) => i.get(), + } + } + + const fn has_duplicates(nums: &[Self]) -> bool { + let len = nums.len(); + let mut i = 0; + while i < len { + let mut j = i + 1; + while j < len { + if nums[i].suffix() == nums[j].suffix() { + return true; + } + j += 1; + } + i += 1; + } + false + } + + const fn is_fixed(&self) -> bool { + matches!(self, Self::Fixed(_)) + } +} + +/// Types to run a pre-compile during testing or benchmarking. +/// +/// Use the types exported from this module in order to test or benchmark +/// your pre-compile. Module only exists when compiles for benchmarking +/// or tests. +#[cfg(any(test, feature = "runtime-benchmarks"))] +pub mod run { + pub use crate::{ + call_builder::{CallSetup, Contract, WasmModule}, + BalanceOf, MomentOf, + }; + pub use sp_core::{H256, U256}; + + use super::*; + + /// Convenience function to run pre-compiles for testing or benchmarking purposes. + /// + /// Use [`CallSetup`] to create an appropriate environment to pass as the `ext` parameter. + /// Panics in case the `MATCHER` of `P` does not match the passed `address`. + pub fn precompile( + ext: &mut E, + address: &[u8; 20], + input: &P::Interface, + ) -> Result, Error> + where + P: Precompile, + E: ExtWithInfo, + BalanceOf: Into + TryFrom, + MomentOf: Into, + <::T as frame_system::Config>::Hash: frame_support::traits::IsType, + { + assert!(P::MATCHER.into_builtin().matches(address)); + if P::HAS_CONTRACT_INFO { + P::call_with_info(address, input, ext) + } else { + P::call(address, input, ext) + } + } + + /// Convenience function to run builtin pre-compiles from benchmarks. + #[cfg(feature = "runtime-benchmarks")] + pub(crate) fn builtin(ext: &mut E, address: &[u8; 20], input: Vec) -> ExecResult + where + E: ExtWithInfo, + BalanceOf: Into + TryFrom, + MomentOf: Into, + <::T as frame_system::Config>::Hash: frame_support::traits::IsType, + { + let precompile = >::get(address) + .ok_or(DispatchError::from("No pre-compile at address"))?; + precompile.call(input, ext) + } +} diff --git a/substrate/frame/revive/src/precompiles/builtin.rs b/substrate/frame/revive/src/precompiles/builtin.rs new file mode 100644 index 000000000000..7cf854ca4c05 --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin.rs @@ -0,0 +1,71 @@ +// 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. + +mod blake2f; +mod bn128; +mod ecrecover; +mod identity; +mod modexp; +mod point_eval; +mod ripemd160; +mod sha256; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[cfg(feature = "runtime-benchmarks")] +use crate::{ + precompiles::{ExtWithInfo, Instance, Precompiles}, + Config, +}; + +#[cfg(feature = "runtime-benchmarks")] +pub use benchmarking::{IBenchmarking, NoInfo, WithInfo}; + +#[cfg(not(feature = "runtime-benchmarks"))] +pub type Builtin = Production; + +#[cfg(feature = "runtime-benchmarks")] +pub type Builtin = (Production, Benchmarking); + +type Production = ( + ecrecover::EcRecover, + sha256::Sha256, + ripemd160::Ripemd160, + identity::Identity, + modexp::Modexp, + bn128::Bn128Add, + bn128::Bn128Mul, + bn128::Bn128Pairing, + blake2f::Blake2F, + point_eval::PointEval, +); + +#[cfg(feature = "runtime-benchmarks")] +type Benchmarking = (benchmarking::WithInfo, benchmarking::NoInfo); + +#[cfg(feature = "runtime-benchmarks")] +impl Precompiles for (Production, Benchmarking) { + const CHECK_COLLISION: () = (); + const USES_EXTERNAL_RANGE: bool = + Production::::USES_EXTERNAL_RANGE || Benchmarking::::USES_EXTERNAL_RANGE; + + fn get>(address: &[u8; 20]) -> Option> { + let _ = >::CHECK_COLLISION; + >::get(address).or_else(|| Benchmarking::::get(address)) + } +} diff --git a/substrate/frame/revive/src/precompiles/builtin/benchmarking.rs b/substrate/frame/revive/src/precompiles/builtin/benchmarking.rs new file mode 100644 index 000000000000..f499d1a682bb --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin/benchmarking.rs @@ -0,0 +1,66 @@ +// 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. + +use crate::{ + precompiles::{BuiltinAddressMatcher, BuiltinPrecompile, Error, Ext, ExtWithInfo}, + Config, +}; +use alloc::vec::Vec; +use alloy_core::sol; +use core::{marker::PhantomData, num::NonZero}; + +sol! { + interface IBenchmarking { + function bench(bytes calldata input) external; + } +} + +pub struct WithInfo(PhantomData); + +impl BuiltinPrecompile for WithInfo { + type T = T; + type Interface = IBenchmarking::IBenchmarkingCalls; + const MATCHER: BuiltinAddressMatcher = + BuiltinAddressMatcher::Fixed(NonZero::new(0xFF_FF).unwrap()); + const HAS_CONTRACT_INFO: bool = true; + + fn call_with_info( + _address: &[u8; 20], + _input: &Self::Interface, + _env: &mut impl ExtWithInfo, + ) -> Result, Error> { + Ok(Vec::new()) + } +} + +pub struct NoInfo(PhantomData); + +impl BuiltinPrecompile for NoInfo { + type T = T; + type Interface = IBenchmarking::IBenchmarkingCalls; + const MATCHER: BuiltinAddressMatcher = + BuiltinAddressMatcher::Fixed(NonZero::new(0xEF_FF).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + _input: &Self::Interface, + _env: &mut impl Ext, + ) -> Result, Error> { + Ok(Vec::new()) + } +} diff --git a/substrate/frame/revive/src/precompiles/builtin/blake2f.rs b/substrate/frame/revive/src/precompiles/builtin/blake2f.rs new file mode 100644 index 000000000000..de8343def0de --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin/blake2f.rs @@ -0,0 +1,195 @@ +// 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. + +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + wasm::RuntimeCosts, + Config, +}; +use alloc::vec::Vec; +use core::{marker::PhantomData, num::NonZero}; +use sp_runtime::DispatchError; + +pub struct Blake2F(PhantomData); + +impl PrimitivePrecompile for Blake2F { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(9).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + const BLAKE2_F_ARG_LEN: usize = 213; + + if input.len() != BLAKE2_F_ARG_LEN { + Err(DispatchError::from("invalid input length"))?; + } + + let mut rounds_buf: [u8; 4] = [0; 4]; + rounds_buf.copy_from_slice(&input[0..4]); + let rounds: u32 = u32::from_be_bytes(rounds_buf); + + env.gas_meter_mut().charge(RuntimeCosts::Blake2F(rounds))?; + + // we use from_le_bytes below to effectively swap byte order to LE if architecture is BE + + let mut h_buf: [u8; 64] = [0; 64]; + h_buf.copy_from_slice(&input[4..68]); + let mut h = [0u64; 8]; + let mut ctr = 0; + for state_word in &mut h { + let mut temp: [u8; 8] = Default::default(); + temp.copy_from_slice(&h_buf[(ctr * 8)..(ctr + 1) * 8]); + *state_word = u64::from_le_bytes(temp); + ctr += 1; + } + + let mut m_buf: [u8; 128] = [0; 128]; + m_buf.copy_from_slice(&input[68..196]); + let mut m = [0u64; 16]; + ctr = 0; + for msg_word in &mut m { + let mut temp: [u8; 8] = Default::default(); + temp.copy_from_slice(&m_buf[(ctr * 8)..(ctr + 1) * 8]); + *msg_word = u64::from_le_bytes(temp); + ctr += 1; + } + + let mut t_0_buf: [u8; 8] = [0; 8]; + t_0_buf.copy_from_slice(&input[196..204]); + let t_0 = u64::from_le_bytes(t_0_buf); + + let mut t_1_buf: [u8; 8] = [0; 8]; + t_1_buf.copy_from_slice(&input[204..212]); + let t_1 = u64::from_le_bytes(t_1_buf); + + let f = if input[212] == 1 { + true + } else if input[212] == 0 { + false + } else { + return Err(DispatchError::from("invalid final flag").into()); + }; + + eip_152::compress(&mut h, m, [t_0, t_1], f, rounds as usize); + + let mut output_buf = [0u8; u64::BITS as usize]; + for (i, state_word) in h.iter().enumerate() { + output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes()); + } + + Ok(output_buf.to_vec()) + } +} + +mod eip_152 { + /// The precomputed values for BLAKE2b [from the spec](https://tools.ietf.org/html/rfc7693#section-2.7) + /// There are 10 16-byte arrays - one for each round + /// the entries are calculated from the sigma constants. + const SIGMA: [[usize; 16]; 10] = [ + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], + [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], + [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], + [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], + [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], + [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], + [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], + [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], + [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], + ]; + + /// IV is the initialization vector for BLAKE2b. See https://tools.ietf.org/html/rfc7693#section-2.6 + /// for details. + const IV: [u64; 8] = [ + 0x6a09e667f3bcc908, + 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, + 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, + 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, + 0x5be0cd19137e2179, + ]; + + #[inline(always)] + /// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 + fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { + v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); + v[d] = (v[d] ^ v[a]).rotate_right(32); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(24); + v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); + v[d] = (v[d] ^ v[a]).rotate_right(16); + v[c] = v[c].wrapping_add(v[d]); + v[b] = (v[b] ^ v[c]).rotate_right(63); + } + + /// The Blake2 compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 + /// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, + /// final block indicator flag `f`, and number of rounds `rounds`. The state vector provided as + /// the first parameter is modified by the function. + pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { + let mut v = [0u64; 16]; + v[..h.len()].copy_from_slice(h); // First half from state. + v[h.len()..].copy_from_slice(&IV); // Second half from IV. + + v[12] ^= t[0]; + v[13] ^= t[1]; + + if f { + v[14] = !v[14] // Invert all bits if the last-block-flag is set. + } + for i in 0..rounds { + // Message word selection permutation for this round. + let s = &SIGMA[i % 10]; + g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); + g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); + g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); + g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); + + g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); + g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); + g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); + g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); + } + + for i in 0..8 { + h[i] ^= v[i] ^ v[i + 8]; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + precompiles::tests::{run_failure_test_vectors, run_test_vectors}, + tests::Test, + }; + + #[test] + fn test_blake2f() { + run_test_vectors::>(include_str!("./testdata/9-blake2f.json")); + run_failure_test_vectors::>(include_str!( + "./testdata/9-blake2f-failures.json" + )); + } +} diff --git a/substrate/frame/revive/src/precompiles/builtin/bn128.rs b/substrate/frame/revive/src/precompiles/builtin/bn128.rs new file mode 100644 index 000000000000..a084fc2e1ffa --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin/bn128.rs @@ -0,0 +1,230 @@ +// 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. + +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + wasm::RuntimeCosts, + Config, +}; +use alloc::vec::Vec; +use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; +use core::{marker::PhantomData, num::NonZero}; +use sp_core::U256; +use sp_runtime::DispatchError; + +pub struct Bn128Add(PhantomData); + +impl PrimitivePrecompile for Bn128Add { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(6).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + env.gas_meter_mut().charge(RuntimeCosts::Bn128Add)?; + + let p1 = read_point(&input, 0)?; + let p2 = read_point(&input, 64)?; + + let mut buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { + // point not at infinity + sum.x().to_big_endian(&mut buf[0..32]).expect("0..32 is 32-byte length; qed"); + sum.y().to_big_endian(&mut buf[32..64]).expect("32..64 is 32-byte length; qed"); + } + + Ok(buf.to_vec()) + } +} + +pub struct Bn128Mul(PhantomData); + +impl PrimitivePrecompile for Bn128Mul { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(7).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + env.gas_meter_mut().charge(RuntimeCosts::Bn128Mul)?; + + let p = read_point(&input, 0)?; + let fr = read_fr(&input, 64)?; + + let mut buf = [0u8; 64]; + if let Some(sum) = AffineG1::from_jacobian(p * fr) { + // point not at infinity + sum.x().to_big_endian(&mut buf[0..32]).expect("0..32 is 32-byte length; qed"); + sum.y().to_big_endian(&mut buf[32..64]).expect("32..64 is 32-byte length; qed"); + } + + Ok(buf.to_vec()) + } +} + +pub struct Bn128Pairing(PhantomData); + +impl PrimitivePrecompile for Bn128Pairing { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(8).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + if input.len() % 192 != 0 { + Err(DispatchError::from("invalid input length"))?; + } + + let ret_val = if input.is_empty() { + env.gas_meter_mut().charge(RuntimeCosts::Bn128Pairing(0))?; + U256::one() + } else { + // (a, b_a, b_b - each 64-byte affine coordinates) + let elements = input.len() / 192; + env.gas_meter_mut().charge(RuntimeCosts::Bn128Pairing(elements as u32))?; + + let mut vals = Vec::new(); + for i in 0..elements { + let offset = i * 192; + let a_x = Fq::from_slice(&input[offset..offset + 32]) + .map_err(|_| DispatchError::from("Invalid a argument x coordinate"))?; + + let a_y = Fq::from_slice(&input[offset + 32..offset + 64]) + .map_err(|_| DispatchError::from("Invalid a argument y coordinate"))?; + + let b_a_y = Fq::from_slice(&input[offset + 64..offset + 96]).map_err(|_| { + DispatchError::from("Invalid b argument imaginary coeff x coordinate") + })?; + + let b_a_x = Fq::from_slice(&input[offset + 96..offset + 128]).map_err(|_| { + DispatchError::from("Invalid b argument imaginary coeff y coordinate") + })?; + + let b_b_y = Fq::from_slice(&input[offset + 128..offset + 160]).map_err(|_| { + DispatchError::from("Invalid b argument real coeff x coordinate") + })?; + + let b_b_x = Fq::from_slice(&input[offset + 160..offset + 192]).map_err(|_| { + DispatchError::from("Invalid b argument real coeff y coordinate") + })?; + + let b_a = Fq2::new(b_a_x, b_a_y); + let b_b = Fq2::new(b_b_x, b_b_y); + let b = + if b_a.is_zero() && b_b.is_zero() { + G2::zero() + } else { + G2::from(AffineG2::new(b_a, b_b).map_err(|_| { + DispatchError::from("Invalid b argument - not on curve") + })?) + }; + let a = + if a_x.is_zero() && a_y.is_zero() { + G1::zero() + } else { + G1::from(AffineG1::new(a_x, a_y).map_err(|_| { + DispatchError::from("Invalid a argument - not on curve") + })?) + }; + vals.push((a, b)); + } + + let mul = pairing_batch(&vals); + + if mul == Gt::one() { + U256::one() + } else { + U256::zero() + } + }; + + let buf = ret_val.to_big_endian(); + Ok(buf.to_vec()) + } +} + +fn read_point(input: &[u8], start_inx: usize) -> Result { + let mut px_buf = [0u8; 32]; + let mut py_buf = [0u8; 32]; + read_input(input, &mut px_buf, start_inx); + read_input(input, &mut py_buf, start_inx + 32); + + let px = Fq::from_slice(&px_buf).map_err(|_| "Invalid point x coordinate")?; + let py = Fq::from_slice(&py_buf).map_err(|_| "Invalid point y coordinate")?; + + Ok(if px == Fq::zero() && py == Fq::zero() { + G1::zero() + } else { + AffineG1::new(px, py).map_err(|_| "Invalid curve point")?.into() + }) +} + +fn read_fr(input: &[u8], start_inx: usize) -> Result { + let mut buf = [0u8; 32]; + read_input(input, &mut buf, start_inx); + + let r = bn::Fr::from_slice(&buf).map_err(|_| "Invalid field element")?; + Ok(r) +} + +/// Copy bytes from input to target. +fn read_input(source: &[u8], target: &mut [u8], offset: usize) { + // Out of bounds, nothing to copy. + if source.len() <= offset { + return; + } + + // Find len to copy up to target len, but not out of bounds. + let len = core::cmp::min(target.len(), source.len() - offset); + target[..len].copy_from_slice(&source[offset..][..len]); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + precompiles::tests::{run_failure_test_vectors, run_test_vectors}, + tests::Test, + }; + + #[test] + fn test_bn128add() { + run_test_vectors::>(include_str!("./testdata/6-bn128add.json")); + run_failure_test_vectors::>(include_str!( + "./testdata/6-bn128add-failure.json" + )); + } + + #[test] + fn test_bn128mul() { + run_test_vectors::>(include_str!("./testdata/7-bn128mul.json")); + } + + #[test] + fn test_bn128pairing() { + run_test_vectors::>(include_str!("./testdata/8-bn128pairing.json")); + } +} diff --git a/substrate/frame/revive/src/pure_precompiles/ecrecover.rs b/substrate/frame/revive/src/precompiles/builtin/ecrecover.rs similarity index 62% rename from substrate/frame/revive/src/pure_precompiles/ecrecover.rs rename to substrate/frame/revive/src/precompiles/builtin/ecrecover.rs index 447862dd4ed2..ea4df4c9adc3 100644 --- a/substrate/frame/revive/src/pure_precompiles/ecrecover.rs +++ b/substrate/frame/revive/src/precompiles/builtin/ecrecover.rs @@ -7,24 +7,35 @@ // 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 +// 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. -use super::Precompile; -use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts}; -use pallet_revive_uapi::ReturnFlags; -/// The ecrecover precompile. -pub struct ECRecover; +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + wasm::RuntimeCosts, + Config, +}; +use alloc::vec::Vec; +use core::{marker::PhantomData, num::NonZero}; -impl Precompile for ECRecover { - fn execute(gas_meter: &mut GasMeter, i: &[u8]) -> Result { - gas_meter.charge(RuntimeCosts::EcdsaRecovery)?; +pub struct EcRecover(PhantomData); +impl PrimitivePrecompile for EcRecover { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(1).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + i: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + env.gas_meter_mut().charge(RuntimeCosts::EcdsaRecovery)?; let mut input = [0u8; 128]; let len = i.len().min(128); input[..len].copy_from_slice(&i[..len]); @@ -40,7 +51,7 @@ impl Precompile for ECRecover { // v can only be 27 or 28 on the full 32 bytes value. // https://github.com/ethereum/go-ethereum/blob/a907d7e81aaeea15d80b2d3209ad8e08e3bf49e0/core/vm/contracts.go#L177 if input[32..63] != [0u8; 31] || ![27, 28].contains(&input[63]) { - return Ok(ExecReturnValue { data: [0u8; 0].to_vec(), flags: ReturnFlags::empty() }); + return Ok(Vec::new()); } let data = match sp_io::crypto::secp256k1_ecdsa_recover(&sig, &msg) { @@ -49,21 +60,20 @@ impl Precompile for ECRecover { address[0..12].copy_from_slice(&[0u8; 12]); address.to_vec() }, - Err(_) => [0u8; 0].to_vec(), + Err(_) => Vec::new(), }; - Ok(ExecReturnValue { data, flags: ReturnFlags::empty() }) + Ok(data) } } #[cfg(test)] mod tests { use super::*; - use crate::pure_precompiles::test::test_precompile_test_vectors; + use crate::{precompiles::tests::run_test_vectors, tests::Test}; #[test] - fn test_ecrecover() -> Result<(), String> { - test_precompile_test_vectors::(include_str!("./testdata/1-ecRecover.json"))?; - Ok(()) + fn test_ecrecover() { + run_test_vectors::>(include_str!("./testdata/1-ecRecover.json")); } } diff --git a/substrate/frame/revive/src/precompiles/builtin/identity.rs b/substrate/frame/revive/src/precompiles/builtin/identity.rs new file mode 100644 index 000000000000..80a7ea791e49 --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin/identity.rs @@ -0,0 +1,41 @@ +// 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. + +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + wasm::RuntimeCosts, + Config, +}; +use alloc::vec::Vec; +use core::{marker::PhantomData, num::NonZero}; + +pub struct Identity(PhantomData); + +impl PrimitivePrecompile for Identity { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(4).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + env.gas_meter_mut().charge(RuntimeCosts::Identity(input.len() as _))?; + Ok(input) + } +} diff --git a/substrate/frame/revive/src/pure_precompiles/modexp.rs b/substrate/frame/revive/src/precompiles/builtin/modexp.rs similarity index 75% rename from substrate/frame/revive/src/pure_precompiles/modexp.rs rename to substrate/frame/revive/src/precompiles/builtin/modexp.rs index 2e0c51af66ed..5ba239f2dafe 100644 --- a/substrate/frame/revive/src/pure_precompiles/modexp.rs +++ b/substrate/frame/revive/src/precompiles/builtin/modexp.rs @@ -7,21 +7,28 @@ // 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 +// 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 Liense. -use super::Precompile; -use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts}; +// limitations under the License. + +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + wasm::RuntimeCosts, + Config, +}; use alloc::{vec, vec::Vec}; -use core::cmp::max; +use core::{cmp::max, marker::PhantomData, num::NonZero}; use num_bigint::BigUint; use num_integer::Integer; use num_traits::{One, Zero}; -use pallet_revive_uapi::ReturnFlags; +use sp_runtime::DispatchError; + +/// See EIP-2565 +const MIN_GAS_COST: u64 = 200; /// The Modexp precompile. /// ModExp expects the following as inputs: @@ -38,115 +45,45 @@ use pallet_revive_uapi::ReturnFlags; /// that gas limits would be applied before actual computation. /// maximum stack size will also prevent abuse. /// see -pub struct Modexp; - -/// See EIP-2565 -const MIN_GAS_COST: u64 = 200; - -// Calculate gas cost according to EIP 2565: -// https://eips.ethereum.org/EIPS/eip-2565 -fn calculate_gas_cost( - base_length: u64, - mod_length: u64, - exponent: &BigUint, - exponent_bytes: &[u8], - mod_is_even: bool, -) -> u64 { - fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> u64 { - let max_length = max(base_length, mod_length); - let mut words = max_length / 8; - if max_length % 8 > 0 { - words += 1; - } - - // Note: can't overflow because we take words to be some u64 value / 8, which is - // necessarily less than sqrt(u64::MAX). - // Additionally, both base_length and mod_length are bounded to 1024, so this has - // an upper bound of roughly (1024 / 8) squared - words * words - } - - fn calculate_iteration_count(exponent: &BigUint, exponent_bytes: &[u8]) -> u64 { - let mut iteration_count: u64 = 0; - let exp_length = exponent_bytes.len() as u64; - - if exp_length <= 32 && exponent.is_zero() { - iteration_count = 0; - } else if exp_length <= 32 { - iteration_count = exponent.bits() - 1; - } else if exp_length > 32 { - // from the EIP spec: - // (8 * (exp_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) - // - // Notes: - // * exp_length is bounded to 1024 and is > 32 - // * exponent can be zero, so we subtract 1 after adding the other terms (whose sum must - // be > 0) - // * the addition can't overflow because the terms are both capped at roughly 8 * max - // size of exp_length (1024) - // * the EIP spec is written in python, in which (exponent & (2**256 - 1)) takes the - // FIRST 32 bytes. However this `BigUint` `&` operator takes the LAST 32 bytes. We - // thus instead take the bytes manually. - let exponent_head = BigUint::from_bytes_be(&exponent_bytes[..32]); - - iteration_count = (8 * (exp_length - 32)) + exponent_head.bits() - 1; - } - - max(iteration_count, 1) - } - - let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length); - let iteration_count = calculate_iteration_count(exponent, exponent_bytes); - max(MIN_GAS_COST, multiplication_complexity * iteration_count / 3) - .saturating_mul(if mod_is_even { 20 } else { 1 }) -} - -/// Copy bytes from input to target. -fn read_input(source: &[u8], target: &mut [u8], source_offset: &mut usize) { - // We move the offset by the len of the target, regardless of what we - // actually copy. - let offset = *source_offset; - *source_offset += target.len(); - - // Out of bounds, nothing to copy. - if source.len() <= offset { - return; - } - - // Find len to copy up to target len, but not out of bounds. - let len = core::cmp::min(target.len(), source.len() - offset); - target[..len].copy_from_slice(&source[offset..][..len]); -} - -impl Precompile for Modexp { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { +pub struct Modexp(PhantomData); + +impl PrimitivePrecompile for Modexp { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(5).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { let mut input_offset = 0; // Yellowpaper: whenever the input is too short, the missing bytes are // considered to be zero. let mut base_len_buf = [0u8; 32]; - read_input(input, &mut base_len_buf, &mut input_offset); + read_input(&input, &mut base_len_buf, &mut input_offset); let mut exp_len_buf = [0u8; 32]; - read_input(input, &mut exp_len_buf, &mut input_offset); + read_input(&input, &mut exp_len_buf, &mut input_offset); let mut mod_len_buf = [0u8; 32]; - read_input(input, &mut mod_len_buf, &mut input_offset); + read_input(&input, &mut mod_len_buf, &mut input_offset); // reasonable assumption: this must fit within the Ethereum EVM's max stack size let max_size_big = BigUint::from(1024u32); let base_len_big = BigUint::from_bytes_be(&base_len_buf); if base_len_big > max_size_big { - return Err("unreasonably large base length"); + Err(DispatchError::from("unreasonably large base length"))?; } let exp_len_big = BigUint::from_bytes_be(&exp_len_buf); if exp_len_big > max_size_big { - return Err("unreasonably large exponent length"); + Err(DispatchError::from("unreasonably exponent length"))?; } let mod_len_big = BigUint::from_bytes_be(&mod_len_buf); if mod_len_big > max_size_big { - return Err("unreasonably large modulus length"); + Err(DispatchError::from("unreasonably large modulus length"))?; } // bounds check handled above @@ -156,27 +93,27 @@ impl Precompile for Modexp { // if mod_len is 0 output must be empty if mod_len == 0 { - return Ok(ExecReturnValue { data: vec![], flags: ReturnFlags::empty() }) + return Ok(Vec::new()) } // Gas formula allows arbitrary large exp_len when base and modulus are empty, so we need to // handle empty base first. let r = if base_len == 0 && mod_len == 0 { - gas_meter.charge(RuntimeCosts::Modexp(MIN_GAS_COST))?; + env.gas_meter_mut().charge(RuntimeCosts::Modexp(MIN_GAS_COST))?; BigUint::zero() } else { // read the numbers themselves. let mut base_buf = vec![0u8; base_len]; - read_input(input, &mut base_buf, &mut input_offset); + read_input(&input, &mut base_buf, &mut input_offset); let base = BigUint::from_bytes_be(&base_buf); let mut exp_buf = vec![0u8; exp_len]; - read_input(input, &mut exp_buf, &mut input_offset); + read_input(&input, &mut exp_buf, &mut input_offset); let exponent = BigUint::from_bytes_be(&exp_buf); let mut mod_buf = vec![0u8; mod_len]; - read_input(input, &mut mod_buf, &mut input_offset); + read_input(&input, &mut mod_buf, &mut input_offset); let modulus = BigUint::from_bytes_be(&mod_buf); // do our gas accounting @@ -188,7 +125,7 @@ impl Precompile for Modexp { modulus.is_even(), ); - gas_meter.charge(RuntimeCosts::Modexp(gas_cost))?; + env.gas_meter_mut().charge(RuntimeCosts::Modexp(gas_cost))?; if modulus.is_zero() || modulus.is_one() { BigUint::zero() @@ -203,35 +140,112 @@ impl Precompile for Modexp { // always true except in the case of zero-length modulus, which leads to // output of length and value 1. if bytes.len() == mod_len { - Ok(ExecReturnValue { data: bytes.to_vec(), flags: ReturnFlags::empty() }) + Ok(bytes.to_vec()) } else if bytes.len() < mod_len { let mut ret = Vec::with_capacity(mod_len); ret.extend(core::iter::repeat(0).take(mod_len - bytes.len())); ret.extend_from_slice(&bytes[..]); - Ok(ExecReturnValue { data: ret.to_vec(), flags: ReturnFlags::empty() }) + Ok(ret) } else { - Err("failed") + return Err(DispatchError::from("failed").into()); + } + } +} + +// Calculate gas cost according to EIP 2565: +// https://eips.ethereum.org/EIPS/eip-2565 +fn calculate_gas_cost( + base_length: u64, + mod_length: u64, + exponent: &BigUint, + exponent_bytes: &[u8], + mod_is_even: bool, +) -> u64 { + fn calculate_multiplication_complexity(base_length: u64, mod_length: u64) -> u64 { + let max_length = max(base_length, mod_length); + let mut words = max_length / 8; + if max_length % 8 > 0 { + words += 1; + } + + // Note: can't overflow because we take words to be some u64 value / 8, which is + // necessarily less than sqrt(u64::MAX). + // Additionally, both base_length and mod_length are bounded to 1024, so this has + // an upper bound of roughly (1024 / 8) squared + words * words + } + + fn calculate_iteration_count(exponent: &BigUint, exponent_bytes: &[u8]) -> u64 { + let mut iteration_count: u64 = 0; + let exp_length = exponent_bytes.len() as u64; + + if exp_length <= 32 && exponent.is_zero() { + iteration_count = 0; + } else if exp_length <= 32 { + iteration_count = exponent.bits() - 1; + } else if exp_length > 32 { + // from the EIP spec: + // (8 * (exp_length - 32)) + ((exponent & (2**256 - 1)).bit_length() - 1) + // + // Notes: + // * exp_length is bounded to 1024 and is > 32 + // * exponent can be zero, so we subtract 1 after adding the other terms (whose sum must + // be > 0) + // * the addition can't overflow because the terms are both capped at roughly 8 * max + // size of exp_length (1024) + // * the EIP spec is written in python, in which (exponent & (2**256 - 1)) takes the + // FIRST 32 bytes. However this `BigUint` `&` operator takes the LAST 32 bytes. We + // thus instead take the bytes manually. + let exponent_head = BigUint::from_bytes_be(&exponent_bytes[..32]); + + iteration_count = (8 * (exp_length - 32)) + exponent_head.bits() - 1; } + + max(iteration_count, 1) + } + + let multiplication_complexity = calculate_multiplication_complexity(base_length, mod_length); + let iteration_count = calculate_iteration_count(exponent, exponent_bytes); + max(MIN_GAS_COST, multiplication_complexity * iteration_count / 3) + .saturating_mul(if mod_is_even { 20 } else { 1 }) +} + +/// Copy bytes from input to target. +fn read_input(source: &[u8], target: &mut [u8], source_offset: &mut usize) { + // We move the offset by the len of the target, regardless of what we + // actually copy. + let offset = *source_offset; + *source_offset += target.len(); + + // Out of bounds, nothing to copy. + if source.len() <= offset { + return; } + + // Find len to copy up to target len, but not out of bounds. + let len = core::cmp::min(target.len(), source.len() - offset); + target[..len].copy_from_slice(&source[offset..][..len]); } #[cfg(test)] mod tests { use super::*; - use crate::pure_precompiles::test::*; + use crate::{ + precompiles::tests::{run_primitive, run_test_vectors}, + tests::Test, + }; use alloy_core::hex; #[test] - fn process_consensus_tests() -> Result<(), String> { - test_precompile_test_vectors::(include_str!("./testdata/5-modexp_eip2565.json"))?; - Ok(()) + fn process_consensus_tests() { + run_test_vectors::>(include_str!("./testdata/5-modexp_eip2565.json")); } #[test] fn test_empty_input() { let input = Vec::new(); - let result = run_precompile::(input).unwrap(); - assert_eq!(result.data, Vec::::new()); + let result = run_primitive::>(input).unwrap(); + assert_eq!(result, Vec::::new()); } #[test] @@ -243,8 +257,8 @@ mod tests { ) .expect("Decode failed"); - let result = run_precompile::(input).unwrap(); - assert_eq!(result.data, vec![0x00]); + let result = run_primitive::>(input).unwrap(); + assert_eq!(result, vec![0x00]); } #[test] @@ -256,8 +270,12 @@ mod tests { ) .expect("Decode failed"); - let result = run_precompile::(input).unwrap_err(); - assert_eq!(result, "unreasonably large base length"); + let result = run_primitive::>(input).unwrap_err(); + if let Error::Error(crate::ExecError { error: DispatchError::Other(reason), .. }) = result { + assert_eq!(reason, "unreasonably large base length"); + } else { + panic!("Unexpected error"); + } } #[test] @@ -274,9 +292,9 @@ mod tests { // 3 ^ 5 % 7 == 5 - let precompile_result = run_precompile::(input).unwrap(); - assert_eq!(precompile_result.data.len(), 1); // should be same length as mod - let result = BigUint::from_bytes_be(&precompile_result.data[..]); + let precompile_result = run_primitive::>(input).unwrap(); + assert_eq!(precompile_result.len(), 1); // should be same length as mod + let result = BigUint::from_bytes_be(&precompile_result[..]); let expected = BigUint::parse_bytes(b"5", 10).unwrap(); assert_eq!(result, expected); } @@ -295,9 +313,9 @@ mod tests { // 59999 ^ 21 % 14452 = 10055 - let precompile_result = run_precompile::(input).unwrap(); - assert_eq!(precompile_result.data.len(), 32); // should be same length as mod - let result = BigUint::from_bytes_be(&precompile_result.data[..]); + let precompile_result = run_primitive::>(input).unwrap(); + assert_eq!(precompile_result.len(), 32); // should be same length as mod + let result = BigUint::from_bytes_be(&precompile_result[..]); let expected = BigUint::parse_bytes(b"10055", 10).unwrap(); assert_eq!(result, expected); } @@ -314,9 +332,9 @@ mod tests { ) .expect("Decode failed"); - let precompile_result = run_precompile::(input).unwrap(); - assert_eq!(precompile_result.data.len(), 32); // should be same length as mod - let result = BigUint::from_bytes_be(&precompile_result.data[..]); + let precompile_result = run_primitive::>(input).unwrap(); + assert_eq!(precompile_result.len(), 32); // should be same length as mod + let result = BigUint::from_bytes_be(&precompile_result[..]); let expected = BigUint::parse_bytes(b"1", 10).unwrap(); assert_eq!(result, expected); } @@ -339,16 +357,16 @@ mod tests { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ]; - let precompile_result = run_precompile::(input).unwrap(); - assert_eq!(precompile_result.data.len(), 1); // should be same length as mod - let result = BigUint::from_bytes_be(&precompile_result.data[..]); + let precompile_result = run_primitive::>(input).unwrap(); + assert_eq!(precompile_result.len(), 1); // should be same length as mod + let result = BigUint::from_bytes_be(&precompile_result[..]); let expected = BigUint::parse_bytes(b"0", 10).unwrap(); assert_eq!(result, expected); } #[test] fn test_long_exp_gas_cost_matches_specs() { - use crate::{gas::Token, tests::Test, GasMeter, Weight}; + use crate::{call_builder::CallSetup, gas::Token, tests::ExtBuilder}; let input = vec![ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -367,13 +385,16 @@ mod tests { 255, 255, 255, 249, ]; - let mut gas_meter = GasMeter::::new(Weight::MAX); - Modexp::execute(&mut gas_meter, &input).unwrap(); + ExtBuilder::default().build().execute_with(|| { + let mut call_setup = CallSetup::::default(); + let (mut ext, _) = call_setup.ext(); + + let before = ext.gas_meter().gas_consumed(); + >::call(&>::MATCHER.base_address(), input, &mut ext).unwrap(); + let after = ext.gas_meter().gas_consumed(); - // 7104 * 20 gas used when ran in geth (x20) - assert_eq!( - gas_meter.gas_consumed(), - Token::::weight(&RuntimeCosts::Modexp(7104 * 20)) - ); + // 7104 * 20 gas used when ran in geth (x20) + assert_eq!(after - before, Token::::weight(&RuntimeCosts::Modexp(7104 * 20))); + }) } } diff --git a/substrate/frame/revive/src/precompiles/builtin/point_eval.rs b/substrate/frame/revive/src/precompiles/builtin/point_eval.rs new file mode 100644 index 000000000000..69a87e7396cb --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin/point_eval.rs @@ -0,0 +1,42 @@ +// 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. + +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + Config, Error as CrateError, +}; +use alloc::vec::Vec; +use core::{marker::PhantomData, num::NonZero}; + +pub struct PointEval(PhantomData); + +impl PrimitivePrecompile for PointEval { + type T = T; + const MATCHER: BuiltinAddressMatcher = + BuiltinAddressMatcher::Fixed(NonZero::new(0x0a).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + _input: Vec, + _env: &mut impl Ext, + ) -> Result, Error> { + // Exists on Ethereum but we didn't implement it, yet. + // This fails the call instead of doing a silent balance transfer. + Err(>::UnsupportedPrecompileAddress.into()) + } +} diff --git a/substrate/frame/revive/src/precompiles/builtin/ripemd160.rs b/substrate/frame/revive/src/precompiles/builtin/ripemd160.rs new file mode 100644 index 000000000000..d8e4ea9dd273 --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin/ripemd160.rs @@ -0,0 +1,55 @@ +// 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. + +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + wasm::RuntimeCosts, + Config, +}; +use alloc::vec::Vec; +use core::{marker::PhantomData, num::NonZero}; +use ripemd::Digest; + +pub struct Ripemd160(PhantomData); + +impl PrimitivePrecompile for Ripemd160 { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(3).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + env.gas_meter_mut().charge(RuntimeCosts::Ripemd160(input.len() as _))?; + let mut ret = [0u8; 32]; + ret[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input)); + Ok(ret.to_vec()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{precompiles::tests::run_test_vectors, tests::Test}; + + #[test] + fn test_ripemd160() { + run_test_vectors::>(include_str!("./testdata/3-ripemd160.json")); + } +} diff --git a/substrate/frame/revive/src/precompiles/builtin/sha256.rs b/substrate/frame/revive/src/precompiles/builtin/sha256.rs new file mode 100644 index 000000000000..a163a99b71be --- /dev/null +++ b/substrate/frame/revive/src/precompiles/builtin/sha256.rs @@ -0,0 +1,53 @@ +// 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. + +use crate::{ + precompiles::{BuiltinAddressMatcher, Error, Ext, PrimitivePrecompile}, + wasm::RuntimeCosts, + Config, +}; +use alloc::vec::Vec; +use core::{marker::PhantomData, num::NonZero}; + +pub struct Sha256(PhantomData); + +impl PrimitivePrecompile for Sha256 { + type T = T; + const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(2).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: Vec, + env: &mut impl Ext, + ) -> Result, Error> { + env.gas_meter_mut().charge(RuntimeCosts::HashSha256(input.len() as _))?; + let data = sp_io::hashing::sha2_256(&input).to_vec(); + Ok(data) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{precompiles::tests::run_test_vectors, tests::Test}; + + #[test] + fn test_sha256() { + run_test_vectors::>(include_str!("./testdata/2-sha256.json")); + } +} diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/1-ecRecover.json b/substrate/frame/revive/src/precompiles/builtin/testdata/1-ecRecover.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/1-ecRecover.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/1-ecRecover.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/2-sha256.json b/substrate/frame/revive/src/precompiles/builtin/testdata/2-sha256.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/2-sha256.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/2-sha256.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/3-ripemd160.json b/substrate/frame/revive/src/precompiles/builtin/testdata/3-ripemd160.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/3-ripemd160.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/3-ripemd160.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/5-modexp_eip2565.json b/substrate/frame/revive/src/precompiles/builtin/testdata/5-modexp_eip2565.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/5-modexp_eip2565.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/5-modexp_eip2565.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/6-bn128add-failure.json b/substrate/frame/revive/src/precompiles/builtin/testdata/6-bn128add-failure.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/6-bn128add-failure.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/6-bn128add-failure.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/6-bn128add.json b/substrate/frame/revive/src/precompiles/builtin/testdata/6-bn128add.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/6-bn128add.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/6-bn128add.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/7-bn128mul.json b/substrate/frame/revive/src/precompiles/builtin/testdata/7-bn128mul.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/7-bn128mul.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/7-bn128mul.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/8-bn128pairing.json b/substrate/frame/revive/src/precompiles/builtin/testdata/8-bn128pairing.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/8-bn128pairing.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/8-bn128pairing.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/9-blake2f-failures.json b/substrate/frame/revive/src/precompiles/builtin/testdata/9-blake2f-failures.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/9-blake2f-failures.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/9-blake2f-failures.json diff --git a/substrate/frame/revive/src/pure_precompiles/testdata/9-blake2f.json b/substrate/frame/revive/src/precompiles/builtin/testdata/9-blake2f.json similarity index 100% rename from substrate/frame/revive/src/pure_precompiles/testdata/9-blake2f.json rename to substrate/frame/revive/src/precompiles/builtin/testdata/9-blake2f.json diff --git a/substrate/frame/revive/src/precompiles/tests.rs b/substrate/frame/revive/src/precompiles/tests.rs new file mode 100644 index 000000000000..5ca51a4176cc --- /dev/null +++ b/substrate/frame/revive/src/precompiles/tests.rs @@ -0,0 +1,256 @@ +// 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. + +#![cfg(test)] + +use super::*; +use crate::{ + call_builder::CallSetup, + exec::Stack, + tests::{ExtBuilder, Test}, + wasm::WasmBlob, +}; +use alloy_core::hex as alloy_hex; +use core::num::NonZero; +use sp_core::hex2array as hex; + +type Env<'a> = Stack<'a, Test, WasmBlob>; + +#[derive(Debug, serde::Deserialize)] +#[serde(rename_all = "PascalCase")] +struct EthConsensusTest { + input: String, + expected: String, + name: String, +} + +#[derive(Debug, serde::Deserialize)] +#[serde(rename_all = "PascalCase")] +struct EthConsensusFailureTest { + input: String, + expected_error: String, + name: String, +} + +/// Convenience function to call a primitive pre-compile for tests. +pub fn run_primitive>(input: Vec) -> Result, Error> { + ExtBuilder::default().build().execute_with(|| { + let mut call_setup = CallSetup::::default(); + let (mut ext, _) = call_setup.ext(); + assert!(P::MATCHER.is_fixed(), "All pre-compiles we are testing here are fixed"); + let address = P::MATCHER.base_address(); + if P::HAS_CONTRACT_INFO { + P::call_with_info(&address, input, &mut ext) + } else { + P::call(&address, input, &mut ext) + } + }) +} + +/// Tests a precompile against the ethereum consensus tests defined in the given json +/// The JSON format is expected to contain an array of test vectors, +/// where each vector can be deserialized into an "EthConsensusTest". +pub fn run_test_vectors>(json: &str) { + let tests: Vec = serde_json::from_str(json).expect("expected json array"); + + for test in tests { + let input: Vec = + alloy_hex::decode(test.input).expect("Could not hex-decode test input data"); + + match run_primitive::

(input) { + Ok(data) => { + assert_eq!( + alloy_hex::encode(data), + test.expected, + "test '{}' failed (different output)", + test.name + ); + }, + Err(err) => { + panic!("Test '{}' returned error: {:?}", test.name, err); + }, + } + } +} + +pub fn run_failure_test_vectors>(json: &str) { + let tests: Vec = + serde_json::from_str(json).expect("expected json array"); + + for test in tests { + let input: Vec = + alloy_hex::decode(test.input).expect("Could not hex-decode test input data"); + + match run_primitive::

(input) { + Err(Error::Error(ExecError { error: DispatchError::Other(reason), .. })) => { + assert_eq!( + test.expected_error, reason, + "Test '{}' failed (different error)", + test.name + ); + }, + Err(err) => panic!("Test {} failed with wrong error: {:?}", test.name, err), + Ok(data) => { + panic!("Test should failed, got {data:?}"); + }, + } + } +} + +#[test] +fn matching_works() { + struct Matcher1; + struct Matcher2; + + impl PrimitivePrecompile for Matcher1 { + type T = Test; + const MATCHER: BuiltinAddressMatcher = + BuiltinAddressMatcher::Fixed(NonZero::new(0x42).unwrap()); + const HAS_CONTRACT_INFO: bool = true; + + fn call( + address: &[u8; 20], + _input: Vec, + _env: &mut impl Ext, + ) -> Result, Error> { + Ok(address.to_vec()) + } + } + + impl PrimitivePrecompile for Matcher2 { + type T = Test; + const MATCHER: BuiltinAddressMatcher = + BuiltinAddressMatcher::Prefix(NonZero::new(0x88).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + address: &[u8; 20], + _input: Vec, + _env: &mut impl Ext, + ) -> Result, Error> { + Ok(address.to_vec()) + } + } + + type Col = (Matcher1, Matcher2); + + assert_eq!( + ::MATCHER.base_address(), + hex!("0000000000000000000000000000000000000042") + ); + assert_eq!( + ::MATCHER.base_address(), + ::MATCHER.highest_address() + ); + + assert_eq!( + ::MATCHER.base_address(), + hex!("0000000000000000000000000000000000000088") + ); + assert_eq!( + ::MATCHER.highest_address(), + hex!("FFFFFFFF00000000000000000000000000000088") + ); + + assert!(Col::get::(&hex!("1000000000000000000000000000000000000043")).is_none()); + assert_eq!( + Col::get::(&hex!("0000000000000000000000000000000000000042")) + .unwrap() + .has_contract_info, + true, + ); + assert!(Col::get::(&hex!("1000000000000000000000000000000000000042")).is_none()); + assert_eq!( + Col::get::(&hex!("0000000000000000000000000000000000000088")) + .unwrap() + .has_contract_info, + false, + ); + assert_eq!( + Col::get::(&hex!("2200000000000000000000000000000000000088")) + .unwrap() + .has_contract_info, + false, + ); + assert_eq!( + Col::get::(&hex!("0010000000000000000000000000000000000088")) + .unwrap() + .has_contract_info, + false, + ); + assert!(Col::get::(&hex!("0000000010000000000000000000000000000088")).is_none()); +} + +#[test] +fn builtin_matching_works() { + let _ = >::CHECK_COLLISION; + + assert_eq!( + >::get::(&hex!("0000000000000000000000000000000000000001")) + .unwrap() + .has_contract_info(), + false, + ); + + assert_eq!( + >::get::(&hex!("0000000000000000000000000000000000000002")) + .unwrap() + .has_contract_info(), + false, + ); + + assert_eq!( + >::get::(&hex!("000000000000000000000000000000000000000a")) + .unwrap() + .has_contract_info(), + false, + ); + + #[cfg(feature = "runtime-benchmarks")] + assert_eq!( + >::get::(&hex!("000000000000000000000000000000000000FFFF")) + .unwrap() + .has_contract_info(), + true, + ); + + #[cfg(feature = "runtime-benchmarks")] + assert_eq!( + >::get::(&hex!("000000000000000000000000000000000000EFFF")) + .unwrap() + .has_contract_info(), + false, + ); + assert!( + >::get::(&hex!("700000000000000000000000000000000000FFFF")).is_none() + ); + assert!( + >::get::(&hex!("700000000000000000000000000000000000EFFF")).is_none() + ); +} + +#[test] +fn public_matching_works() { + let matcher_fixed = AddressMatcher::Fixed(NonZero::new(0x42).unwrap()); + let matcher_prefix = AddressMatcher::Prefix(NonZero::new(0x8).unwrap()); + + assert_eq!(matcher_fixed.base_address(), hex!("0000000000000000000000000000000000420000")); + assert_eq!(matcher_fixed.base_address(), matcher_fixed.highest_address()); + + assert_eq!(matcher_prefix.base_address(), hex!("0000000000000000000000000000000000080000")); + assert_eq!(matcher_prefix.highest_address(), hex!("FFFFFFFF00000000000000000000000000080000")); +} diff --git a/substrate/frame/revive/src/pure_precompiles.rs b/substrate/frame/revive/src/pure_precompiles.rs deleted file mode 100644 index fda2d7a94f1c..000000000000 --- a/substrate/frame/revive/src/pure_precompiles.rs +++ /dev/null @@ -1,175 +0,0 @@ -// 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. - -use crate::{exec::ExecResult, Config, Error, ExecReturnValue, GasMeter, H160, LOG_TARGET}; - -mod ecrecover; -pub use ecrecover::*; - -mod sha256; -pub use sha256::*; - -mod ripemd160; -pub use ripemd160::*; - -mod identity; -pub use identity::*; - -mod bn128; -pub use bn128::*; - -mod modexp; -pub use modexp::*; - -mod blake2f; -pub use blake2f::*; - -/// Determine if the given address is a precompile. -/// For now, we consider that all addresses between 0x1 and 0xff are reserved for precompiles. -pub fn is_precompile(address: &H160) -> bool { - let bytes = address.as_bytes(); - bytes.starts_with(&[0u8; 19]) && bytes[19] != 0 -} - -/// The `Precompile` trait defines the functionality for executing a precompiled contract. -pub trait Precompile { - /// Executes the precompile with the provided input data. - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result; -} - -pub struct Precompiles { - _phantom: core::marker::PhantomData, -} - -impl Precompiles { - pub fn execute(addr: H160, gas_meter: &mut GasMeter, input: &[u8]) -> ExecResult { - match addr.as_bytes()[19] { - 1u8 => ECRecover::execute(gas_meter, input), - 2u8 => Sha256::execute(gas_meter, input), - 3u8 => Ripemd160::execute(gas_meter, input), - 4u8 => Identity::execute(gas_meter, input), - 5u8 => Modexp::execute(gas_meter, input), - 6u8 => Bn128Add::execute(gas_meter, input), - 7u8 => Bn128Mul::execute(gas_meter, input), - 8u8 => Bn128Pairing::execute(gas_meter, input), - 9u8 => Blake2F::execute(gas_meter, input), - _ => return Err(Error::::UnsupportedPrecompileAddress.into()), - } - .map_err(|reason| { - log::debug!(target: LOG_TARGET, "Precompile failed: {reason:?}"); - Error::::PrecompileFailure.into() - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{tests::Test, ExecReturnValue, Weight}; - use alloy_core::hex; - use pallet_revive_uapi::ReturnFlags; - - #[derive(Debug, serde::Deserialize)] - #[serde(rename_all = "PascalCase")] - struct EthConsensusTest { - input: String, - expected: String, - name: String, - } - - #[derive(Debug, serde::Deserialize)] - #[serde(rename_all = "PascalCase")] - struct EthConsensusFailureTest { - input: String, - expected_error: String, - name: String, - } - - /// Run a precompile with the given input data. - pub fn run_precompile>( - input: Vec, - ) -> Result { - let mut gas_meter = GasMeter::::new(Weight::MAX); - P::execute(&mut gas_meter, &input) - } - - /// Tests a precompile against the ethereum consensus tests defined in the given json - /// The JSON format is expected to contain an array of test vectors, - /// where each vector can be deserialized into an "EthConsensusTest". - pub fn test_precompile_test_vectors>( - json: &str, - ) -> Result<(), String> { - let tests: Vec = serde_json::from_str(json).expect("expected json array"); - - for test in tests { - let input: Vec = - hex::decode(test.input).expect("Could not hex-decode test input data"); - - let mut gas_meter = GasMeter::::new(Weight::MAX); - match P::execute(&mut gas_meter, &input) { - Ok(ExecReturnValue { data, flags }) => { - assert_eq!( - flags, - ReturnFlags::empty(), - "test '{}' failed (unexpected flags)", - test.name - ); - - assert_eq!( - hex::encode(data), - test.expected, - "test '{}' failed (different output)", - test.name - ); - }, - Err(err) => { - return Err(format!("Test '{}' returned error: {:?}", test.name, err)); - }, - } - } - - Ok(()) - } - - pub fn test_precompile_failure_test_vectors>( - json: &str, - ) -> Result<(), String> { - let tests: Vec = - serde_json::from_str(json).expect("expected json array"); - - for test in tests { - let input: Vec = - hex::decode(test.input).expect("Could not hex-decode test input data"); - - let mut gas_meter = GasMeter::::new(Weight::MAX); - match P::execute(&mut gas_meter, &input) { - Ok(ExecReturnValue { data, .. }) => { - panic!("Test should failed, got {data:?}"); - }, - Err(reason) => { - assert_eq!( - test.expected_error, reason, - "Test '{}' failed (different error)", - test.name - ); - }, - } - } - - Ok(()) - } -} diff --git a/substrate/frame/revive/src/pure_precompiles/blake2f.rs b/substrate/frame/revive/src/pure_precompiles/blake2f.rs deleted file mode 100644 index e60049e6544f..000000000000 --- a/substrate/frame/revive/src/pure_precompiles/blake2f.rs +++ /dev/null @@ -1,105 +0,0 @@ -// 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. - -use super::Precompile; -use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts}; -use pallet_revive_uapi::ReturnFlags; - -mod eip_152; - -/// The Blake2F precompile. -pub struct Blake2F; - -impl Precompile for Blake2F { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { - const BLAKE2_F_ARG_LEN: usize = 213; - - if input.len() != BLAKE2_F_ARG_LEN { - return Err("invalid input length"); - } - - let mut rounds_buf: [u8; 4] = [0; 4]; - rounds_buf.copy_from_slice(&input[0..4]); - let rounds: u32 = u32::from_be_bytes(rounds_buf); - - gas_meter.charge(RuntimeCosts::Blake2F(rounds))?; - - // we use from_le_bytes below to effectively swap byte order to LE if architecture is BE - - let mut h_buf: [u8; 64] = [0; 64]; - h_buf.copy_from_slice(&input[4..68]); - let mut h = [0u64; 8]; - let mut ctr = 0; - for state_word in &mut h { - let mut temp: [u8; 8] = Default::default(); - temp.copy_from_slice(&h_buf[(ctr * 8)..(ctr + 1) * 8]); - *state_word = u64::from_le_bytes(temp); - ctr += 1; - } - - let mut m_buf: [u8; 128] = [0; 128]; - m_buf.copy_from_slice(&input[68..196]); - let mut m = [0u64; 16]; - ctr = 0; - for msg_word in &mut m { - let mut temp: [u8; 8] = Default::default(); - temp.copy_from_slice(&m_buf[(ctr * 8)..(ctr + 1) * 8]); - *msg_word = u64::from_le_bytes(temp); - ctr += 1; - } - - let mut t_0_buf: [u8; 8] = [0; 8]; - t_0_buf.copy_from_slice(&input[196..204]); - let t_0 = u64::from_le_bytes(t_0_buf); - - let mut t_1_buf: [u8; 8] = [0; 8]; - t_1_buf.copy_from_slice(&input[204..212]); - let t_1 = u64::from_le_bytes(t_1_buf); - - let f = if input[212] == 1 { - true - } else if input[212] == 0 { - false - } else { - return Err("invalid final flag"); - }; - - eip_152::compress(&mut h, m, [t_0, t_1], f, rounds as usize); - - let mut output_buf = [0u8; u64::BITS as usize]; - for (i, state_word) in h.iter().enumerate() { - output_buf[i * 8..(i + 1) * 8].copy_from_slice(&state_word.to_le_bytes()); - } - - Ok(ExecReturnValue { data: output_buf.to_vec(), flags: ReturnFlags::empty() }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::pure_precompiles::test::*; - - #[test] - fn test_blake2f() -> Result<(), String> { - test_precompile_test_vectors::(include_str!("./testdata/9-blake2f.json"))?; - test_precompile_failure_test_vectors::(include_str!( - "./testdata/9-blake2f-failures.json" - ))?; - Ok(()) - } -} diff --git a/substrate/frame/revive/src/pure_precompiles/blake2f/eip_152.rs b/substrate/frame/revive/src/pure_precompiles/blake2f/eip_152.rs deleted file mode 100644 index 53f5d81134d4..000000000000 --- a/substrate/frame/revive/src/pure_precompiles/blake2f/eip_152.rs +++ /dev/null @@ -1,92 +0,0 @@ -// 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. - -/// The precomputed values for BLAKE2b [from the spec](https://tools.ietf.org/html/rfc7693#section-2.7) -/// There are 10 16-byte arrays - one for each round -/// the entries are calculated from the sigma constants. -const SIGMA: [[usize; 16]; 10] = [ - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - [14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3], - [11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4], - [7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8], - [9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13], - [2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9], - [12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11], - [13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10], - [6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5], - [10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0], -]; - -/// IV is the initialization vector for BLAKE2b. See https://tools.ietf.org/html/rfc7693#section-2.6 -/// for details. -const IV: [u64; 8] = [ - 0x6a09e667f3bcc908, - 0xbb67ae8584caa73b, - 0x3c6ef372fe94f82b, - 0xa54ff53a5f1d36f1, - 0x510e527fade682d1, - 0x9b05688c2b3e6c1f, - 0x1f83d9abfb41bd6b, - 0x5be0cd19137e2179, -]; - -#[inline(always)] -/// The G mixing function. See https://tools.ietf.org/html/rfc7693#section-3.1 -fn g(v: &mut [u64], a: usize, b: usize, c: usize, d: usize, x: u64, y: u64) { - v[a] = v[a].wrapping_add(v[b]).wrapping_add(x); - v[d] = (v[d] ^ v[a]).rotate_right(32); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(24); - v[a] = v[a].wrapping_add(v[b]).wrapping_add(y); - v[d] = (v[d] ^ v[a]).rotate_right(16); - v[c] = v[c].wrapping_add(v[d]); - v[b] = (v[b] ^ v[c]).rotate_right(63); -} - -/// The Blake2 compression function F. See https://tools.ietf.org/html/rfc7693#section-3.2 -/// Takes as an argument the state vector `h`, message block vector `m`, offset counter `t`, final -/// block indicator flag `f`, and number of rounds `rounds`. The state vector provided as the first -/// parameter is modified by the function. -pub fn compress(h: &mut [u64; 8], m: [u64; 16], t: [u64; 2], f: bool, rounds: usize) { - let mut v = [0u64; 16]; - v[..h.len()].copy_from_slice(h); // First half from state. - v[h.len()..].copy_from_slice(&IV); // Second half from IV. - - v[12] ^= t[0]; - v[13] ^= t[1]; - - if f { - v[14] = !v[14] // Invert all bits if the last-block-flag is set. - } - for i in 0..rounds { - // Message word selection permutation for this round. - let s = &SIGMA[i % 10]; - g(&mut v, 0, 4, 8, 12, m[s[0]], m[s[1]]); - g(&mut v, 1, 5, 9, 13, m[s[2]], m[s[3]]); - g(&mut v, 2, 6, 10, 14, m[s[4]], m[s[5]]); - g(&mut v, 3, 7, 11, 15, m[s[6]], m[s[7]]); - - g(&mut v, 0, 5, 10, 15, m[s[8]], m[s[9]]); - g(&mut v, 1, 6, 11, 12, m[s[10]], m[s[11]]); - g(&mut v, 2, 7, 8, 13, m[s[12]], m[s[13]]); - g(&mut v, 3, 4, 9, 14, m[s[14]], m[s[15]]); - } - - for i in 0..8 { - h[i] ^= v[i] ^ v[i + 8]; - } -} diff --git a/substrate/frame/revive/src/pure_precompiles/bn128.rs b/substrate/frame/revive/src/pure_precompiles/bn128.rs deleted file mode 100644 index efcd9c41293e..000000000000 --- a/substrate/frame/revive/src/pure_precompiles/bn128.rs +++ /dev/null @@ -1,235 +0,0 @@ -// 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. -use super::Precompile; -use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts}; -use alloc::vec::Vec; -use bn::{pairing_batch, AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; -use pallet_revive_uapi::ReturnFlags; -use sp_core::U256; - -/// The Bn128Add precompile. -pub struct Bn128Add; - -impl Precompile for Bn128Add { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { - gas_meter.charge(RuntimeCosts::Bn128Add)?; - - let p1 = read_point(input, 0)?; - let p2 = read_point(input, 64)?; - - let mut buf = [0u8; 64]; - if let Some(sum) = AffineG1::from_jacobian(p1 + p2) { - // point not at infinity - sum.x().to_big_endian(&mut buf[0..32]).expect("0..32 is 32-byte length; qed"); - sum.y().to_big_endian(&mut buf[32..64]).expect("32..64 is 32-byte length; qed"); - } - - Ok(ExecReturnValue { data: buf.to_vec(), flags: ReturnFlags::empty() }) - } -} - -/// The Bn128Mul builtin -pub struct Bn128Mul; - -impl Precompile for Bn128Mul { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { - gas_meter.charge(RuntimeCosts::Bn128Mul)?; - - let p = read_point(input, 0)?; - let fr = read_fr(input, 64)?; - - let mut buf = [0u8; 64]; - if let Some(sum) = AffineG1::from_jacobian(p * fr) { - // point not at infinity - sum.x().to_big_endian(&mut buf[0..32]).expect("0..32 is 32-byte length; qed"); - sum.y().to_big_endian(&mut buf[32..64]).expect("32..64 is 32-byte length; qed"); - } - - Ok(ExecReturnValue { data: buf.to_vec(), flags: ReturnFlags::empty() }) - } -} - -/// The Bn128Pairing builtin -pub struct Bn128Pairing; - -impl Precompile for Bn128Pairing { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { - if input.len() % 192 != 0 { - return Err("invalid input length"); - } - - let ret_val = if input.is_empty() { - gas_meter.charge(RuntimeCosts::Bn128Pairing(0))?; - U256::one() - } else { - // (a, b_a, b_b - each 64-byte affine coordinates) - let elements = input.len() / 192; - gas_meter.charge(RuntimeCosts::Bn128Pairing(elements as u32))?; - - let mut vals = Vec::new(); - for i in 0..elements { - let offset = i * 192; - let a_x = Fq::from_slice(&input[offset..offset + 32]) - .map_err(|_| "Invalid a argument x coordinate")?; - - let a_y = Fq::from_slice(&input[offset + 32..offset + 64]) - .map_err(|_| "Invalid a argument y coordinate")?; - - let b_a_y = Fq::from_slice(&input[offset + 64..offset + 96]) - .map_err(|_| "Invalid b argument imaginary coeff x coordinate")?; - - let b_a_x = Fq::from_slice(&input[offset + 96..offset + 128]) - .map_err(|_| "Invalid b argument imaginary coeff y coordinate")?; - - let b_b_y = Fq::from_slice(&input[offset + 128..offset + 160]) - .map_err(|_| "Invalid b argument real coeff x coordinate")?; - - let b_b_x = Fq::from_slice(&input[offset + 160..offset + 192]) - .map_err(|_| "Invalid b argument real coeff y coordinate")?; - - let b_a = Fq2::new(b_a_x, b_a_y); - let b_b = Fq2::new(b_b_x, b_b_y); - let b = if b_a.is_zero() && b_b.is_zero() { - G2::zero() - } else { - G2::from( - AffineG2::new(b_a, b_b).map_err(|_| "Invalid b argument - not on curve")?, - ) - }; - let a = if a_x.is_zero() && a_y.is_zero() { - G1::zero() - } else { - G1::from( - AffineG1::new(a_x, a_y).map_err(|_| "Invalid a argument - not on curve")?, - ) - }; - vals.push((a, b)); - } - - let mul = pairing_batch(&vals); - - if mul == Gt::one() { - U256::one() - } else { - U256::zero() - } - }; - - let buf = ret_val.to_big_endian(); - Ok(ExecReturnValue { data: buf.to_vec(), flags: ReturnFlags::empty() }) - } -} - -fn read_point(input: &[u8], start_inx: usize) -> Result { - let mut px_buf = [0u8; 32]; - let mut py_buf = [0u8; 32]; - read_input(input, &mut px_buf, start_inx); - read_input(input, &mut py_buf, start_inx + 32); - - let px = Fq::from_slice(&px_buf).map_err(|_| "Invalid point x coordinate")?; - let py = Fq::from_slice(&py_buf).map_err(|_| "Invalid point y coordinate")?; - - Ok(if px == Fq::zero() && py == Fq::zero() { - G1::zero() - } else { - AffineG1::new(px, py).map_err(|_| "Invalid curve point")?.into() - }) -} - -fn read_fr(input: &[u8], start_inx: usize) -> Result { - let mut buf = [0u8; 32]; - read_input(input, &mut buf, start_inx); - - let r = bn::Fr::from_slice(&buf).map_err(|_| "Invalid field element")?; - Ok(r) -} - -/// Copy bytes from input to target. -fn read_input(source: &[u8], target: &mut [u8], offset: usize) { - // Out of bounds, nothing to copy. - if source.len() <= offset { - return; - } - - // Find len to copy up to target len, but not out of bounds. - let len = core::cmp::min(target.len(), source.len() - offset); - target[..len].copy_from_slice(&source[offset..][..len]); -} - -#[cfg(feature = "runtime-benchmarks")] -pub fn generate_random_ecpairs(n: usize) -> Vec { - use alloc::vec; - use bn::{Fr, Group, G1, G2}; - use rand::SeedableRng; - use rand_pcg::Pcg64; - let mut rng = Pcg64::seed_from_u64(1); - - let mut buffer = vec![0u8; n * 192]; - - let mut write = |element: &bn::Fq, offset: &mut usize| { - element.to_big_endian(&mut buffer[*offset..*offset + 32]).unwrap(); - *offset += 32 - }; - - for i in 0..n { - let mut offset = i * 192; - let scalar = Fr::random(&mut rng); - - let g1 = G1::one() * scalar; - let g2 = G2::one() * scalar; - let a = AffineG1::from_jacobian(g1).expect("G1 point should be on curve"); - let b = AffineG2::from_jacobian(g2).expect("G2 point should be on curve"); - - write(&a.x(), &mut offset); - write(&a.y(), &mut offset); - write(&b.x().imaginary(), &mut offset); - write(&b.x().real(), &mut offset); - write(&b.y().imaginary(), &mut offset); - write(&b.y().real(), &mut offset); - } - - buffer -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::pure_precompiles::test::*; - - #[test] - fn test_bn128add() -> Result<(), String> { - test_precompile_test_vectors::(include_str!("./testdata/6-bn128add.json"))?; - test_precompile_failure_test_vectors::(include_str!( - "./testdata/6-bn128add-failure.json" - ))?; - Ok(()) - } - - #[test] - fn test_bn128mul() -> Result<(), String> { - test_precompile_test_vectors::(include_str!("./testdata/7-bn128mul.json"))?; - Ok(()) - } - - #[test] - fn test_bn128pairing() -> Result<(), String> { - test_precompile_test_vectors::(include_str!( - "./testdata/8-bn128pairing.json" - ))?; - Ok(()) - } -} diff --git a/substrate/frame/revive/src/pure_precompiles/identity.rs b/substrate/frame/revive/src/pure_precompiles/identity.rs deleted file mode 100644 index 59541b6c86e3..000000000000 --- a/substrate/frame/revive/src/pure_precompiles/identity.rs +++ /dev/null @@ -1,30 +0,0 @@ -// 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. -use super::Precompile; -use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts}; -use pallet_revive_uapi::ReturnFlags; - -/// The identity precompile. -pub struct Identity; - -impl Precompile for Identity { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { - gas_meter.charge(RuntimeCosts::Identity(input.len() as u32))?; - - Ok(ExecReturnValue { data: input.to_vec(), flags: ReturnFlags::empty() }) - } -} diff --git a/substrate/frame/revive/src/pure_precompiles/ripemd160.rs b/substrate/frame/revive/src/pure_precompiles/ripemd160.rs deleted file mode 100644 index 5177fa865a02..000000000000 --- a/substrate/frame/revive/src/pure_precompiles/ripemd160.rs +++ /dev/null @@ -1,45 +0,0 @@ -// 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. -use super::Precompile; -use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts}; -use pallet_revive_uapi::ReturnFlags; -use ripemd::Digest; - -/// The Ripemd160 precompile. -pub struct Ripemd160; - -impl Precompile for Ripemd160 { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { - gas_meter.charge(RuntimeCosts::Ripemd160(input.len() as _))?; - - let mut ret = [0u8; 32]; - ret[12..32].copy_from_slice(&ripemd::Ripemd160::digest(input)); - Ok(ExecReturnValue { data: ret.to_vec(), flags: ReturnFlags::empty() }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::pure_precompiles::test::test_precompile_test_vectors; - - #[test] - fn test_ripemd160() -> Result<(), String> { - test_precompile_test_vectors::(include_str!("./testdata/3-ripemd160.json"))?; - Ok(()) - } -} diff --git a/substrate/frame/revive/src/pure_precompiles/sha256.rs b/substrate/frame/revive/src/pure_precompiles/sha256.rs deleted file mode 100644 index 0b835a9d3be5..000000000000 --- a/substrate/frame/revive/src/pure_precompiles/sha256.rs +++ /dev/null @@ -1,42 +0,0 @@ -// 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. -use super::Precompile; -use crate::{Config, ExecReturnValue, GasMeter, RuntimeCosts}; -use pallet_revive_uapi::ReturnFlags; - -/// The Sha256 precompile. -pub struct Sha256; - -impl Precompile for Sha256 { - fn execute(gas_meter: &mut GasMeter, input: &[u8]) -> Result { - gas_meter.charge(RuntimeCosts::HashSha256(input.len() as u32))?; - let data = sp_io::hashing::sha2_256(input).to_vec(); - Ok(ExecReturnValue { data, flags: ReturnFlags::empty() }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::pure_precompiles::test::test_precompile_test_vectors; - - #[test] - fn test_sha256() -> Result<(), String> { - test_precompile_test_vectors::(include_str!("./testdata/2-sha256.json"))?; - Ok(()) - } -} diff --git a/substrate/frame/revive/src/test_utils.rs b/substrate/frame/revive/src/test_utils.rs index 0f5eef7a30e2..c059169c5634 100644 --- a/substrate/frame/revive/src/test_utils.rs +++ b/substrate/frame/revive/src/test_utils.rs @@ -18,8 +18,6 @@ //! Shared utilities for testing contracts. //! This is not part of the tests module because it is made public for other crates to use. -#![cfg(feature = "std")] - pub mod builder; pub use sp_runtime::AccountId32; diff --git a/substrate/frame/revive/src/test_utils/builder.rs b/substrate/frame/revive/src/test_utils/builder.rs index c9c2a3240bc4..799c633deef0 100644 --- a/substrate/frame/revive/src/test_utils/builder.rs +++ b/substrate/frame/revive/src/test_utils/builder.rs @@ -20,6 +20,7 @@ use crate::{ address::AddressMapper, AccountIdOf, BalanceOf, Code, Config, ContractResult, DepositLimit, ExecReturnValue, InstantiateReturnValue, OriginFor, Pallet, Weight, }; +use alloc::{vec, vec::Vec}; use frame_support::pallet_prelude::DispatchResultWithPostInfo; use paste::paste; use sp_core::H160; diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 811526f1b610..5572923614e8 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -16,15 +16,12 @@ // limitations under the License. mod pallet_dummy; +mod precompiles; use self::test_utils::{ensure_stored, expected_deposit}; use crate::{ self as pallet_revive, address::{create1, create2, AddressMapper}, - chain_extension::{ - ChainExtension, Environment, Ext, RegisteredChainExtension, Result as ExtensionResult, - RetVal, ReturnFlags, - }, evm::{runtime::GAS_PRICE, CallTrace, CallTracer, CallType, GenericTransaction}, exec::Key, limits, @@ -32,7 +29,6 @@ use crate::{ test_utils::*, tests::test_utils::{get_contract, get_contract_checked}, tracing::trace, - wasm::Memory, weights::WeightInfo, AccountId32Mapper, BalanceOf, Code, CodeInfoOf, Config, ContractInfo, ContractInfoOf, DeletionQueueCounter, DepositLimit, Error, EthTransactError, HoldReason, Origin, Pallet, @@ -57,7 +53,7 @@ use frame_support::{ }; use frame_system::{EventRecord, Phase}; use pallet_revive_fixtures::compile_module; -use pallet_revive_uapi::ReturnErrorCode as RuntimeReturnCode; +use pallet_revive_uapi::{ReturnErrorCode as RuntimeReturnCode, ReturnFlags}; use pallet_transaction_payment::{ConstFeeMultiplier, Multiplier}; use pretty_assertions::{assert_eq, assert_ne}; use sp_core::U256; @@ -220,159 +216,6 @@ impl Test { } } -parameter_types! { - static TestExtensionTestValue: TestExtension = Default::default(); -} - -#[derive(Clone)] -pub struct TestExtension { - enabled: bool, - last_seen_buffer: Vec, - last_seen_input_len: u32, -} - -#[derive(Default)] -pub struct RevertingExtension; - -#[derive(Default)] -pub struct DisabledExtension; - -#[derive(Default)] -pub struct TempStorageExtension { - storage: u32, -} - -impl TestExtension { - fn disable() { - TestExtensionTestValue::mutate(|e| e.enabled = false) - } - - fn last_seen_buffer() -> Vec { - TestExtensionTestValue::get().last_seen_buffer.clone() - } - - fn last_seen_input_len() -> u32 { - TestExtensionTestValue::get().last_seen_input_len - } -} - -impl Default for TestExtension { - fn default() -> Self { - Self { enabled: true, last_seen_buffer: vec![], last_seen_input_len: 0 } - } -} - -impl ChainExtension for TestExtension { - fn call(&mut self, mut env: Environment) -> ExtensionResult - where - E: Ext, - M: ?Sized + Memory, - { - let func_id = env.func_id(); - let id = env.ext_id() as u32 | func_id as u32; - match func_id { - 0 => { - let input = env.read(8)?; - env.write(&input, false, None)?; - TestExtensionTestValue::mutate(|e| e.last_seen_buffer = input); - Ok(RetVal::Converging(id)) - }, - 1 => { - TestExtensionTestValue::mutate(|e| e.last_seen_input_len = env.in_len()); - Ok(RetVal::Converging(id)) - }, - 2 => { - let mut enc = &env.read(9)?[4..8]; - let weight = Weight::from_parts( - u32::decode(&mut enc).map_err(|_| Error::::ContractTrapped)?.into(), - 0, - ); - env.charge_weight(weight)?; - Ok(RetVal::Converging(id)) - }, - 3 => Ok(RetVal::Diverging { flags: ReturnFlags::REVERT, data: vec![42, 99] }), - _ => { - panic!("Passed unknown id to test chain extension: {}", func_id); - }, - } - } - - fn enabled() -> bool { - TestExtensionTestValue::get().enabled - } -} - -impl RegisteredChainExtension for TestExtension { - const ID: u16 = 0; -} - -impl ChainExtension for RevertingExtension { - fn call(&mut self, _env: Environment) -> ExtensionResult - where - E: Ext, - M: ?Sized + Memory, - { - Ok(RetVal::Diverging { flags: ReturnFlags::REVERT, data: vec![0x4B, 0x1D] }) - } - - fn enabled() -> bool { - TestExtensionTestValue::get().enabled - } -} - -impl RegisteredChainExtension for RevertingExtension { - const ID: u16 = 1; -} - -impl ChainExtension for DisabledExtension { - fn call(&mut self, _env: Environment) -> ExtensionResult - where - E: Ext, - M: ?Sized + Memory, - { - panic!("Disabled chain extensions are never called") - } - - fn enabled() -> bool { - false - } -} - -impl RegisteredChainExtension for DisabledExtension { - const ID: u16 = 2; -} - -impl ChainExtension for TempStorageExtension { - fn call(&mut self, env: Environment) -> ExtensionResult - where - E: Ext, - M: ?Sized + Memory, - { - let func_id = env.func_id(); - match func_id { - 0 => self.storage = 42, - 1 => assert_eq!(self.storage, 42, "Storage is preserved inside the same call."), - 2 => { - assert_eq!(self.storage, 0, "Storage is different for different calls."); - self.storage = 99; - }, - 3 => assert_eq!(self.storage, 99, "Storage is preserved inside the same call."), - _ => { - panic!("Passed unknown id to test chain extension: {}", func_id); - }, - } - Ok(RetVal::Converging(0)) - } - - fn enabled() -> bool { - TestExtensionTestValue::get().enabled - } -} - -impl RegisteredChainExtension for TempStorageExtension { - const ID: u16 = 3; -} - parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = frame_system::limits::BlockWeights::simple_max( @@ -526,8 +369,6 @@ impl Config for Test { type AddressMapper = AccountId32Mapper; type Currency = Balances; type CallFilter = TestFilter; - type ChainExtension = - (TestExtension, DisabledExtension, RevertingExtension, TempStorageExtension); type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; type UnsafeUnstableInterface = UnstableInterface; @@ -536,6 +377,7 @@ impl Config for Test { type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type ChainId = ChainId; type FindAuthor = Test; + type Precompiles = (precompiles::WithInfo, precompiles::NoInfo); } impl TryFrom for crate::Call { @@ -609,29 +451,6 @@ fn initialize_block(number: u64) { System::initialize(&number, &[0u8; 32].into(), &Default::default()); } -struct ExtensionInput<'a> { - extension_id: u16, - func_id: u16, - extra: &'a [u8], -} - -impl<'a> ExtensionInput<'a> { - fn to_vec(&self) -> Vec { - (((self.extension_id as u32) << 16) | (self.func_id as u32)) - .to_le_bytes() - .iter() - .chain(self.extra) - .cloned() - .collect() - } -} - -impl<'a> From> for Vec { - fn from(input: ExtensionInput) -> Vec { - input.to_vec() - } -} - impl Default for Origin { fn default() -> Self { Self::Signed(ALICE) @@ -822,23 +641,16 @@ fn run_out_of_fuel_engine() { // Fail out of fuel (ref_time weight) in the host. #[test] fn run_out_of_fuel_host() { - let (code, _hash) = compile_module("chain_extension").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - - let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) - .value(min_balance * 100) - .build_and_unwrap_contract(); + use crate::precompiles::Precompile; + use alloy_core::sol_types::SolInterface; + use precompiles::{INoInfo, NoInfo}; - let gas_limit = Weight::from_parts(u32::MAX as u64, GAS_LIMIT.proof_size()); + let precompile_addr = H160(NoInfo::::MATCHER.base_address()); + let input = INoInfo::INoInfoCalls::consumeMaxGas(INoInfo::consumeMaxGasCall {}).abi_encode(); - // Use chain extension to charge more ref_time than it is available. - let result = builder::bare_call(addr) - .gas_limit(gas_limit) - .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &u32::MAX.encode() }.into()) - .build() - .result; + ExtBuilder::default().build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 100_000_000_000); + let result = builder::bare_call(precompile_addr).data(input).build().result; assert_err!(result, >::OutOfGas); }); } @@ -1132,6 +944,28 @@ fn delegate_call() { }); } +#[test] +fn delegate_call_non_existant_is_noop() { + let (caller_wasm, _caller_code_hash) = compile_module("delegate_call_simple").unwrap(); + + ExtBuilder::default().existential_deposit(500).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Instantiate the 'caller' + let Contract { addr: caller_addr, .. } = + builder::bare_instantiate(Code::Upload(caller_wasm)) + .value(300_000) + .build_and_unwrap_contract(); + + assert_ok!(builder::call(caller_addr) + .value(1337) + .data((BOB_ADDR, u64::MAX, u64::MAX).encode()) + .build()); + + assert_eq!(test_utils::get_balance(&BOB_FALLBACK), 0); + }); +} + #[test] fn delegate_call_with_weight_limit() { let (caller_wasm, _caller_code_hash) = compile_module("delegate_call").unwrap(); @@ -1653,113 +1487,6 @@ fn instantiate_return_code() { }); } -#[test] -fn disabled_chain_extension_errors_on_call() { - let (code, _hash) = compile_module("chain_extension").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let contract = builder::bare_instantiate(Code::Upload(code)) - .value(min_balance * 100) - .build_and_unwrap_contract(); - TestExtension::disable(); - assert_err_ignore_postinfo!( - builder::call(contract.addr).data(vec![7u8; 8]).build(), - Error::::NoChainExtension, - ); - }); -} - -#[test] -fn chain_extension_works() { - let (code, _hash) = compile_module("chain_extension").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let contract = builder::bare_instantiate(Code::Upload(code)) - .value(min_balance * 100) - .build_and_unwrap_contract(); - - // 0 = read input buffer and pass it through as output - let input: Vec = ExtensionInput { extension_id: 0, func_id: 0, extra: &[99] }.into(); - let result = builder::bare_call(contract.addr).data(input.clone()).build(); - assert_eq!(TestExtension::last_seen_buffer(), input); - assert_eq!(result.result.unwrap().data, input); - - // 1 = treat inputs as integer primitives and store the supplied integers - builder::bare_call(contract.addr) - .data(ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into()) - .build_and_unwrap_result(); - assert_eq!(TestExtension::last_seen_input_len(), 4); - - // 2 = charge some extra weight (amount supplied in the fifth byte) - let result = builder::bare_call(contract.addr) - .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &0u32.encode() }.into()) - .build(); - assert_ok!(result.result); - let gas_consumed = result.gas_consumed; - let result = builder::bare_call(contract.addr) - .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &42u32.encode() }.into()) - .build(); - assert_ok!(result.result); - assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42); - let result = builder::bare_call(contract.addr) - .data(ExtensionInput { extension_id: 0, func_id: 2, extra: &95u32.encode() }.into()) - .build(); - assert_ok!(result.result); - assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95); - - // 3 = diverging chain extension call that sets flags to 0x1 and returns a fixed buffer - let result = builder::bare_call(contract.addr) - .data(ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into()) - .build_and_unwrap_result(); - assert_eq!(result.flags, ReturnFlags::REVERT); - assert_eq!(result.data, vec![42, 99]); - - // diverging to second chain extension that sets flags to 0x1 and returns a fixed buffer - // We set the MSB part to 1 (instead of 0) which routes the request into the second - // extension - let result = builder::bare_call(contract.addr) - .data(ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into()) - .build_and_unwrap_result(); - assert_eq!(result.flags, ReturnFlags::REVERT); - assert_eq!(result.data, vec![0x4B, 0x1D]); - - // Diverging to third chain extension that is disabled - // We set the MSB part to 2 (instead of 0) which routes the request into the third - // extension - assert_err_ignore_postinfo!( - builder::call(contract.addr) - .data(ExtensionInput { extension_id: 2, func_id: 0, extra: &[] }.into()) - .build(), - Error::::NoChainExtension, - ); - }); -} - -#[test] -fn chain_extension_temp_storage_works() { - let (code, _hash) = compile_module("chain_extension_temp_storage").unwrap(); - ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let min_balance = Contracts::min_balance(); - let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); - let contract = builder::bare_instantiate(Code::Upload(code)) - .value(min_balance * 100) - .build_and_unwrap_contract(); - - // Call func 0 and func 1 back to back. - let stop_recursion = 0u8; - let mut input: Vec = ExtensionInput { extension_id: 3, func_id: 0, extra: &[] }.into(); - input.extend_from_slice( - ExtensionInput { extension_id: 3, func_id: 1, extra: &[stop_recursion] } - .to_vec() - .as_ref(), - ); - - assert_ok!(builder::bare_call(contract.addr).data(input.clone()).build().result); - }) -} - #[test] fn lazy_removal_works() { let (code, _hash) = compile_module("self_destruct").unwrap(); @@ -4489,12 +4216,15 @@ fn tracing_works() { ]; // Verify that the first trace report the same weight reported by bare_call + // TODO: fix tracing ( https://github.com/paritytech/polkadot-sdk/issues/8362 ) + /* let mut tracer = CallTracer::new(false, |w| w); let gas_used = trace(&mut tracer, || { builder::bare_call(addr).data((3u32, addr_callee).encode()).build().gas_consumed }); let traces = tracer.collect_traces(); assert_eq!(&traces[0].gas_used, &gas_used); + */ // Discarding gas usage, check that traces reported are correct for (with_logs, logs) in tracer_options { @@ -4602,26 +4332,12 @@ fn unknown_precompiles_revert() { let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); - let cases: Vec<(H160, Box)> = vec![ - ( - H160::from_low_u64_be(10), - Box::new(|result| { - assert_err!(result, >::ContractTrapped); - }), - ), - ( - H160::from_low_u64_be(0xff), - Box::new(|result| { - assert_err!(result, >::ContractTrapped); - }), - ), - ( - H160::from_low_u64_be(0x1ff), - Box::new(|result| { - assert_ok!(result); - }), - ), - ]; + let cases: Vec<(H160, Box)> = vec![( + H160::from_low_u64_be(0x0a), + Box::new(|result| { + assert_err!(result, >::UnsupportedPrecompileAddress); + }), + )]; for (callee_addr, assert_result) in cases { let result = @@ -4721,3 +4437,118 @@ fn pure_precompile_works() { }); } } + +#[test] +fn precompiles_work() { + use crate::precompiles::Precompile; + use alloy_core::sol_types::{Panic, PanicKind, Revert, SolError, SolInterface, SolValue}; + use precompiles::{INoInfo, NoInfo}; + + let precompile_addr = H160(NoInfo::::MATCHER.base_address()); + + let cases = vec![ + ( + INoInfo::INoInfoCalls::identity(INoInfo::identityCall { number: 42u64.into() }) + .abi_encode(), + 42u64.abi_encode(), + RuntimeReturnCode::Success, + ), + ( + INoInfo::INoInfoCalls::reverts(INoInfo::revertsCall { error: "panic".to_string() }) + .abi_encode(), + Revert::from("panic").abi_encode(), + RuntimeReturnCode::CalleeReverted, + ), + ( + INoInfo::INoInfoCalls::panics(INoInfo::panicsCall {}).abi_encode(), + Panic::from(PanicKind::Assert).abi_encode(), + RuntimeReturnCode::CalleeReverted, + ), + ( + INoInfo::INoInfoCalls::errors(INoInfo::errorsCall {}).abi_encode(), + Vec::new(), + RuntimeReturnCode::CalleeTrapped, + ), + // passing non decodeable input reverts with solidity panic + ( + b"invalid".to_vec(), + Panic::from(PanicKind::ResourceError).abi_encode(), + RuntimeReturnCode::CalleeReverted, + ), + ]; + + for (input, output, error_code) in cases { + let (code, _code_hash) = compile_module("call_and_returncode").unwrap(); + ExtBuilder::default().build().execute_with(|| { + let id = ::AddressMapper::to_account_id(&precompile_addr); + let _ = ::Currency::set_balance(&ALICE, 100_000_000_000); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(1000) + .build_and_unwrap_contract(); + + let result = builder::bare_call(addr) + .data( + (&precompile_addr, 0u64).encode().into_iter().chain(input).collect::>(), + ) + .build_and_unwrap_result(); + + // no account or contract info should be created for a NoInfo pre-compile + assert!(test_utils::get_contract_checked(&precompile_addr).is_none()); + assert!(!System::account_exists(&id)); + assert_eq!(test_utils::get_balance(&id), 0u64); + + assert_eq!(result.flags, ReturnFlags::empty()); + assert_eq!(u32::from_le_bytes(result.data[..4].try_into().unwrap()), error_code as u32); + assert_eq!( + &result.data[4..], + &output, + "Unexpected output for precompile: {precompile_addr:?}", + ); + }); + } +} + +#[test] +fn precompiles_with_info_creates_contract() { + use crate::precompiles::Precompile; + use alloy_core::sol_types::SolInterface; + use precompiles::{IWithInfo, WithInfo}; + + let precompile_addr = H160(WithInfo::::MATCHER.base_address()); + + let cases = vec![( + IWithInfo::IWithInfoCalls::dummy(IWithInfo::dummyCall {}).abi_encode(), + Vec::::new(), + RuntimeReturnCode::Success, + )]; + + for (input, output, error_code) in cases { + let (code, _code_hash) = compile_module("call_and_returncode").unwrap(); + ExtBuilder::default().build().execute_with(|| { + let id = ::AddressMapper::to_account_id(&precompile_addr); + let _ = ::Currency::set_balance(&ALICE, 100_000_000_000); + let Contract { addr, .. } = builder::bare_instantiate(Code::Upload(code)) + .value(1000) + .build_and_unwrap_contract(); + + let result = builder::bare_call(addr) + .data( + (&precompile_addr, 0u64).encode().into_iter().chain(input).collect::>(), + ) + .build_and_unwrap_result(); + + // a pre-compile with contract info should create an account on first call + assert!(test_utils::get_contract_checked(&precompile_addr).is_some()); + assert!(System::account_exists(&id)); + assert_eq!(test_utils::get_balance(&id), 1u64); + + assert_eq!(result.flags, ReturnFlags::empty()); + assert_eq!(u32::from_le_bytes(result.data[..4].try_into().unwrap()), error_code as u32); + assert_eq!( + &result.data[4..], + &output, + "Unexpected output for precompile: {precompile_addr:?}", + ); + }); + } +} diff --git a/substrate/frame/revive/src/tests/precompiles.rs b/substrate/frame/revive/src/tests/precompiles.rs new file mode 100644 index 000000000000..372e1c697b49 --- /dev/null +++ b/substrate/frame/revive/src/tests/precompiles.rs @@ -0,0 +1,100 @@ +// 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. + +//! Precompiles added to the test runtime. + +use crate::{ + precompiles::{AddressMatcher, Error, Ext, ExtWithInfo, Precompile, Token}, + Config, DispatchError, Weight, +}; +use alloc::vec::Vec; +use alloy_core::{ + sol, + sol_types::{PanicKind, SolValue}, +}; +use core::{marker::PhantomData, num::NonZero}; + +sol! { + interface IWithInfo { + function dummy() external; + } + + interface INoInfo { + function identity(uint64 number) external returns (uint64); + function reverts(string calldata error) external; + function panics() external; + function errors() external; + function consumeMaxGas() external; + } +} + +pub struct WithInfo(PhantomData); + +impl Precompile for WithInfo { + type T = T; + type Interface = IWithInfo::IWithInfoCalls; + const MATCHER: AddressMatcher = AddressMatcher::Fixed(NonZero::new(0xFF_FF).unwrap()); + const HAS_CONTRACT_INFO: bool = true; + + fn call_with_info( + _address: &[u8; 20], + _input: &Self::Interface, + _env: &mut impl ExtWithInfo, + ) -> Result, Error> { + Ok(Vec::new()) + } +} + +pub struct NoInfo(PhantomData); + +impl Precompile for NoInfo { + type T = T; + type Interface = INoInfo::INoInfoCalls; + const MATCHER: AddressMatcher = AddressMatcher::Fixed(NonZero::new(0xEF_FF).unwrap()); + const HAS_CONTRACT_INFO: bool = false; + + fn call( + _address: &[u8; 20], + input: &Self::Interface, + env: &mut impl Ext, + ) -> Result, Error> { + use INoInfo::INoInfoCalls; + + match input { + INoInfoCalls::identity(INoInfo::identityCall { number }) => Ok(number.abi_encode()), + INoInfoCalls::reverts(INoInfo::revertsCall { error }) => + Err(Error::Revert(error.as_str().into())), + INoInfoCalls::panics(INoInfo::panicsCall {}) => + Err(Error::Panic(PanicKind::Assert.into())), + INoInfoCalls::errors(INoInfo::errorsCall {}) => + Err(Error::Error(DispatchError::Other("precompile failed").into())), + INoInfoCalls::consumeMaxGas(INoInfo::consumeMaxGasCall {}) => { + env.gas_meter_mut().charge(MaxGasToken)?; + Ok(Vec::new()) + }, + } + } +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +struct MaxGasToken; + +impl Token for MaxGasToken { + fn weight(&self) -> Weight { + Weight::MAX + } +} diff --git a/substrate/frame/revive/src/wasm/mod.rs b/substrate/frame/revive/src/wasm/mod.rs index 650930eeaca2..0b0ba6623901 100644 --- a/substrate/frame/revive/src/wasm/mod.rs +++ b/substrate/frame/revive/src/wasm/mod.rs @@ -26,7 +26,7 @@ pub use crate::wasm::runtime::SyscallDoc; #[cfg(feature = "runtime-benchmarks")] pub use crate::wasm::runtime::{ReturnData, TrapReason}; -pub use crate::wasm::runtime::{Memory, Runtime, RuntimeCosts}; +pub use crate::wasm::runtime::{Runtime, RuntimeCosts}; use crate::{ exec::{ExecResult, Executable, ExportedFunction, Ext}, @@ -396,7 +396,7 @@ where fn from_storage(code_hash: H256, gas_meter: &mut GasMeter) -> Result { let code_info = >::get(code_hash).ok_or(Error::::CodeNotFound)?; gas_meter.charge(CodeLoadToken(code_info.code_len))?; - let code = >::get(code_hash).ok_or(Error::::CodeNotFound)?; + let code = >::get(&code_hash).ok_or(Error::::CodeNotFound)?; Ok(Self { code, code_info, code_hash }) } diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 482976f5099f..1586a0e9c1b2 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -23,8 +23,8 @@ use crate::{ exec::{ExecError, ExecResult, Ext, Key}, gas::{ChargedAmount, Token}, limits, + precompiles::{All as AllPrecompiles, Precompiles}, primitives::ExecReturnValue, - pure_precompiles::is_precompile, weights::WeightInfo, Config, Error, LOG_TARGET, SENTINEL, }; @@ -368,6 +368,12 @@ pub enum RuntimeCosts { CallBase, /// Weight of calling `seal_delegate_call` for the given input size. DelegateCallBase, + /// Weight of calling a precompile. + PrecompileBase, + /// Weight of calling a precompile that has a contract info. + PrecompileWithInfoBase, + /// Weight of reading and decoding the input to a precompile. + PrecompileDecode(u32), /// Weight of the transfer performed during a call. CallTransferSurcharge, /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. @@ -388,8 +394,6 @@ pub enum RuntimeCosts { EcdsaRecovery, /// Weight of calling `seal_sr25519_verify` for the given input size. Sr25519Verify(u32), - /// Weight charged by a chain extension through `seal_call_chain_extension`. - ChainExtension(Weight), /// Weight charged for calling into the runtime. CallRuntime(Weight), /// Weight charged for calling xcm_execute. @@ -528,6 +532,9 @@ impl Token for RuntimeCosts { }, CallBase => T::WeightInfo::seal_call(0, 0), DelegateCallBase => T::WeightInfo::seal_delegate_call(), + PrecompileBase => T::WeightInfo::seal_call_precompile(0, 0), + PrecompileWithInfoBase => T::WeightInfo::seal_call_precompile(1, 0), + PrecompileDecode(len) => cost_args!(seal_call_precompile, 0, len), CallTransferSurcharge => cost_args!(seal_call, 1, 0), CallInputCloned(len) => cost_args!(seal_call, 0, len), Instantiate { input_data_len } => T::WeightInfo::seal_instantiate(input_data_len), @@ -538,7 +545,7 @@ impl Token for RuntimeCosts { HashBlake128(len) => T::WeightInfo::seal_hash_blake2_128(len), EcdsaRecovery => T::WeightInfo::ecdsa_recover(), Sr25519Verify(len) => T::WeightInfo::seal_sr25519_verify(len), - ChainExtension(weight) | CallRuntime(weight) | CallXcmExecute(weight) => weight, + CallRuntime(weight) | CallXcmExecute(weight) => weight, SetCodeHash => T::WeightInfo::seal_set_code_hash(), EcdsaToEthAddress => T::WeightInfo::seal_ecdsa_to_eth_address(), GetImmutableData(len) => T::WeightInfo::seal_get_immutable_data(len), @@ -633,7 +640,6 @@ enum StorageReadMode { pub struct Runtime<'a, E: Ext, M: ?Sized> { ext: &'a mut E, input_data: Option>, - chain_extension: Option::ChainExtension>>, _phantom_data: PhantomData, } @@ -693,18 +699,10 @@ impl<'a, E: Ext, M: PolkaVmInstance> Runtime<'a, E, M> { impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { pub fn new(ext: &'a mut E, input_data: Vec) -> Self { - Self { - ext, - input_data: Some(input_data), - chain_extension: Some(Box::new(Default::default())), - _phantom_data: Default::default(), - } + Self { ext, input_data: Some(input_data), _phantom_data: Default::default() } } /// Get a mutable reference to the inner `Ext`. - /// - /// This is mainly for the chain extension to have access to the environment the - /// contract is executing in. pub fn ext(&mut self) -> &mut E { self.ext } @@ -712,7 +710,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { /// Charge the gas meter with the specified token. /// /// Returns `Err(HostError)` if there is not enough gas. - pub fn charge_gas(&mut self, costs: RuntimeCosts) -> Result { + fn charge_gas(&mut self, costs: RuntimeCosts) -> Result { charge_gas!(self, costs) } @@ -720,7 +718,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { /// /// This is when a maximum a priori amount was charged and then should be partially /// refunded to match the actual amount. - pub fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) { + fn adjust_gas(&mut self, charged: ChargedAmount, actual_costs: RuntimeCosts) { self.ext.gas_meter_mut().adjust_gas(charged, actual_costs); } @@ -798,7 +796,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { allow_skip: bool, create_token: impl FnOnce(u32) -> Option, ) -> Result<(), DispatchError> { - if allow_skip && out_ptr == SENTINEL { + if buf.is_empty() || (allow_skip && out_ptr == SENTINEL) { return Ok(()); } @@ -855,11 +853,13 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { let out_of_gas = Error::::OutOfGas.into(); let out_of_deposit = Error::::StorageDepositLimitExhausted.into(); let duplicate_contract = Error::::DuplicateContract.into(); + let unsupported_precompile = Error::::UnsupportedPrecompileAddress.into(); // errors in the callee do not trap the caller match (from.error, from.origin) { (err, _) if err == transfer_failed => Ok(TransferFailed), (err, _) if err == duplicate_contract => Ok(DuplicateContractAddress), + (err, _) if err == unsupported_precompile => Err(err), (err, Callee) if err == out_of_gas || err == out_of_deposit => Ok(OutOfResources), (_, Callee) => Ok(CalleeTrapped), (err, _) => Err(err), @@ -1115,16 +1115,13 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { output_ptr: u32, output_len_ptr: u32, ) -> Result { - let callee = match memory.read_h160(callee_ptr) { - Ok(callee) if is_precompile(&callee) => callee, - Ok(callee) => { - self.charge_gas(call_type.cost())?; - callee - }, - Err(err) => { - self.charge_gas(call_type.cost())?; - return Err(err.into()); - }, + let callee = memory.read_h160(callee_ptr)?; + let precompile = >::get::(&callee.as_fixed_bytes()); + match &precompile { + Some(precompile) if precompile.has_contract_info() => + self.charge_gas(RuntimeCosts::PrecompileWithInfoBase)?, + Some(_) => self.charge_gas(RuntimeCosts::PrecompileBase)?, + None => self.charge_gas(call_type.cost())?, }; let deposit_limit = memory.read_u256(deposit_ptr)?; @@ -1136,7 +1133,11 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { } else if flags.contains(CallFlags::FORWARD_INPUT) { self.input_data.take().ok_or(Error::::InputForwarded)? } else { - self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?; + if precompile.is_some() { + self.charge_gas(RuntimeCosts::PrecompileDecode(input_data_len))?; + } else { + self.charge_gas(RuntimeCosts::CopyFromContract(input_data_len))?; + } memory.read(input_data_ptr, input_data_len)? }; @@ -1194,7 +1195,11 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { write_result?; Ok(self.ext.last_frame_output().into()) }, - Err(err) => Ok(Self::exec_error_into_return_code(err)?), + Err(err) => { + let error_code = Self::exec_error_into_return_code(err)?; + memory.write(output_len_ptr, &0u32.to_le_bytes())?; + Ok(error_code) + }, } } @@ -1941,36 +1946,6 @@ pub mod env { Ok(self.ext.gas_meter().gas_left().ref_time()) } - /// Call into the chain extension provided by the chain if any. - /// See [`pallet_revive_uapi::HostFn::call_chain_extension`]. - fn call_chain_extension( - &mut self, - memory: &mut M, - id: u32, - input_ptr: u32, - input_len: u32, - output_ptr: u32, - output_len_ptr: u32, - ) -> Result { - use crate::chain_extension::{ChainExtension, Environment, RetVal}; - if !::ChainExtension::enabled() { - return Err(Error::::NoChainExtension.into()); - } - let mut chain_extension = self.chain_extension.take().expect( - "Constructor initializes with `Some`. This is the only place where it is set to `None`.\ - It is always reset to `Some` afterwards. qed", - ); - let env = - Environment::new(self, memory, id, input_ptr, input_len, output_ptr, output_len_ptr); - let ret = match chain_extension.call(env)? { - RetVal::Converging(val) => Ok(val), - RetVal::Diverging { flags, data } => - Err(TrapReason::Return(ReturnData { flags: flags.bits(), data })), - }; - self.chain_extension = Some(chain_extension); - ret - } - /// Call some dispatchable of the runtime. /// See [`frame_support::traits::call_runtime`]. #[mutating] diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 64238ebe8fa8..602fc7057b86 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -35,9 +35,9 @@ //! Autogenerated weights for `pallet_revive` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 -//! DATE: 2025-04-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2025-04-16, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `bd2de89217b1`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! HOSTNAME: `cd6bf3e6c4c6`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` //! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024` // Executed Command: @@ -139,6 +139,7 @@ pub trait WeightInfo { fn seal_contains_transient_storage(n: u32, ) -> Weight; fn seal_take_transient_storage(n: u32, ) -> Weight; fn seal_call(t: u32, i: u32, ) -> Weight; + fn seal_call_precompile(d: u32, i: u32, ) -> Weight; fn seal_delegate_call() -> Weight; fn seal_instantiate(i: u32, ) -> Weight; fn sha2_256(n: u32, ) -> Weight; @@ -166,10 +167,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `1594` - // Minimum execution time: 2_516_000 picoseconds. - Weight::from_parts(2_674_000, 1594) + // Measured: `147` + // Estimated: `1632` + // Minimum execution time: 2_971_000 picoseconds. + Weight::from_parts(3_218_000, 1632) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -179,10 +180,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `425 + k * (69 ±0)` // Estimated: `415 + k * (70 ±0)` - // Minimum execution time: 13_953_000 picoseconds. - Weight::from_parts(14_357_000, 415) - // Standard Error: 2_229 - .saturating_add(Weight::from_parts(1_231_251, 0).saturating_mul(k.into())) + // Minimum execution time: 14_042_000 picoseconds. + Weight::from_parts(14_510_000, 415) + // Standard Error: 1_047 + .saturating_add(Weight::from_parts(1_167_098, 0).saturating_mul(k.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) @@ -204,12 +205,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 104857]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `853 + c * (1 ±0)` - // Estimated: `6794 + c * (1 ±0)` - // Minimum execution time: 79_155_000 picoseconds. - Weight::from_parts(104_860_639, 6794) - // Standard Error: 13 - .saturating_add(Weight::from_parts(2_096, 0).saturating_mul(c.into())) + // Measured: `1178 + c * (1 ±0)` + // Estimated: `7119 + c * (1 ±0)` + // Minimum execution time: 81_842_000 picoseconds. + Weight::from_parts(110_091_076, 7119) + // Standard Error: 12 + .saturating_add(Weight::from_parts(2_123, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -229,12 +230,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `b` is `[0, 1]`. fn basic_block_compilation(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `4184` - // Estimated: `10124` - // Minimum execution time: 117_684_000 picoseconds. - Weight::from_parts(122_290_963, 10124) - // Standard Error: 525_579 - .saturating_add(Weight::from_parts(576_836, 0).saturating_mul(b.into())) + // Measured: `4513` + // Estimated: `10453` + // Minimum execution time: 121_768_000 picoseconds. + Weight::from_parts(126_040_712, 10453) + // Standard Error: 315_571 + .saturating_add(Weight::from_parts(961_687, 0).saturating_mul(b.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -256,14 +257,14 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn instantiate_with_code(c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `6383` - // Minimum execution time: 1_603_783_000 picoseconds. - Weight::from_parts(127_682_916, 6383) - // Standard Error: 33 - .saturating_add(Weight::from_parts(20_426, 0).saturating_mul(c.into())) + // Measured: `1122` + // Estimated: `7010` + // Minimum execution time: 1_593_697_000 picoseconds. + Weight::from_parts(150_335_332, 7010) + // Standard Error: 34 + .saturating_add(Weight::from_parts(19_710, 0).saturating_mul(c.into())) // Standard Error: 13 - .saturating_add(Weight::from_parts(5_634, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(5_466, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(6_u64)) } @@ -284,12 +285,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1316` - // Estimated: `4762` - // Minimum execution time: 149_706_000 picoseconds. - Weight::from_parts(84_227_566, 4762) + // Measured: `1886` + // Estimated: `5346` + // Minimum execution time: 155_867_000 picoseconds. + Weight::from_parts(88_055_300, 5346) // Standard Error: 25 - .saturating_add(Weight::from_parts(5_574, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(5_561, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -307,10 +308,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1483` - // Estimated: `7423` - // Minimum execution time: 81_076_000 picoseconds. - Weight::from_parts(82_876_000, 7423) + // Measured: `1812` + // Estimated: `7752` + // Minimum execution time: 85_908_000 picoseconds. + Weight::from_parts(87_953_000, 7752) .saturating_add(T::DbWeight::get().reads(7_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -323,12 +324,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `c` is `[0, 104857]`. fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `164` - // Estimated: `3629` - // Minimum execution time: 47_357_000 picoseconds. - Weight::from_parts(22_260_997, 3629) - // Standard Error: 24 - .saturating_add(Weight::from_parts(15_158, 0).saturating_mul(c.into())) + // Measured: `505` + // Estimated: `3970` + // Minimum execution time: 51_305_000 picoseconds. + Weight::from_parts(27_197_599, 3970) + // Standard Error: 20 + .saturating_add(Weight::from_parts(14_721, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -340,10 +341,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `322` - // Estimated: `3787` - // Minimum execution time: 39_242_000 picoseconds. - Weight::from_parts(40_311_000, 3787) + // Measured: `658` + // Estimated: `4123` + // Minimum execution time: 41_900_000 picoseconds. + Weight::from_parts(42_985_000, 4123) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -355,8 +356,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `528` // Estimated: `6468` - // Minimum execution time: 20_456_000 picoseconds. - Weight::from_parts(20_922_000, 6468) + // Minimum execution time: 20_254_000 picoseconds. + Weight::from_parts(21_122_000, 6468) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -366,10 +367,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(409), added: 2884, mode: `Measured`) fn map_account() -> Weight { // Proof Size summary in bytes: - // Measured: `164` - // Estimated: `3629` - // Minimum execution time: 45_077_000 picoseconds. - Weight::from_parts(46_444_000, 3629) + // Measured: `813` + // Estimated: `4278` + // Minimum execution time: 50_406_000 picoseconds. + Weight::from_parts(51_939_000, 4278) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -379,10 +380,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) fn unmap_account() -> Weight { // Proof Size summary in bytes: - // Measured: `93` - // Estimated: `3558` - // Minimum execution time: 33_860_000 picoseconds. - Weight::from_parts(35_201_000, 3558) + // Measured: `395` + // Estimated: `3860` + // Minimum execution time: 37_679_000 picoseconds. + Weight::from_parts(39_183_000, 3860) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(2_u64)) } @@ -394,8 +395,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 12_718_000 picoseconds. - Weight::from_parts(13_145_000, 3610) + // Minimum execution time: 12_684_000 picoseconds. + Weight::from_parts(13_240_000, 3610) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// The range of component `r` is `[0, 1600]`. @@ -403,24 +404,24 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_380_000 picoseconds. - Weight::from_parts(7_397_295, 0) - // Standard Error: 308 - .saturating_add(Weight::from_parts(161_735, 0).saturating_mul(r.into())) + // Minimum execution time: 6_583_000 picoseconds. + Weight::from_parts(8_416_964, 0) + // Standard Error: 170 + .saturating_add(Weight::from_parts(164_523, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_130_000, 0) + // Minimum execution time: 199_000 picoseconds. + Weight::from_parts(233_000, 0) } fn seal_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_029_000 picoseconds. - Weight::from_parts(1_095_000, 0) + // Minimum execution time: 184_000 picoseconds. + Weight::from_parts(206_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) @@ -428,18 +429,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `306` // Estimated: `3771` - // Minimum execution time: 8_388_000 picoseconds. - Weight::from_parts(8_815_000, 3771) + // Minimum execution time: 8_270_000 picoseconds. + Weight::from_parts(8_700_000, 3771) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) fn seal_to_account_id() -> Weight { // Proof Size summary in bytes: - // Measured: `269` - // Estimated: `3734` - // Minimum execution time: 6_298_000 picoseconds. - Weight::from_parts(6_675_000, 3734) + // Measured: `538` + // Estimated: `4003` + // Minimum execution time: 9_364_000 picoseconds. + Weight::from_parts(9_672_000, 4003) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) @@ -448,16 +449,16 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `402` // Estimated: `3867` - // Minimum execution time: 9_139_000 picoseconds. - Weight::from_parts(9_472_000, 3867) + // Minimum execution time: 9_183_000 picoseconds. + Weight::from_parts(9_609_000, 3867) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 257_000 picoseconds. - Weight::from_parts(305_000, 0) + // Minimum execution time: 131_000 picoseconds. + Weight::from_parts(176_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) @@ -467,51 +468,51 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `472` // Estimated: `3937` - // Minimum execution time: 12_911_000 picoseconds. - Weight::from_parts(13_397_000, 3937) + // Minimum execution time: 12_217_000 picoseconds. + Weight::from_parts(13_005_000, 3937) .saturating_add(T::DbWeight::get().reads(2_u64)) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 313_000 picoseconds. - Weight::from_parts(347_000, 0) + // Minimum execution time: 208_000 picoseconds. + Weight::from_parts(228_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(299_000, 0) + // Minimum execution time: 170_000 picoseconds. + Weight::from_parts(203_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 286_000 picoseconds. - Weight::from_parts(346_000, 0) + // Minimum execution time: 182_000 picoseconds. + Weight::from_parts(204_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 643_000 picoseconds. - Weight::from_parts(736_000, 0) + // Minimum execution time: 576_000 picoseconds. + Weight::from_parts(637_000, 0) } fn seal_ref_time_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 241_000 picoseconds. - Weight::from_parts(276_000, 0) + // Minimum execution time: 131_000 picoseconds. + Weight::from_parts(168_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `103` // Estimated: `0` - // Minimum execution time: 4_917_000 picoseconds. - Weight::from_parts(5_174_000, 0) + // Minimum execution time: 4_466_000 picoseconds. + Weight::from_parts(4_813_000, 0) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) @@ -519,10 +520,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn seal_balance_of() -> Weight { // Proof Size summary in bytes: - // Measured: `272` - // Estimated: `3737` - // Minimum execution time: 8_248_000 picoseconds. - Weight::from_parts(8_657_000, 3737) + // Measured: `517` + // Estimated: `3982` + // Minimum execution time: 8_786_000 picoseconds. + Weight::from_parts(9_450_000, 3982) .saturating_add(T::DbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) @@ -532,10 +533,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `238 + n * (1 ±0)` // Estimated: `3703 + n * (1 ±0)` - // Minimum execution time: 5_551_000 picoseconds. - Weight::from_parts(6_240_836, 3703) - // Standard Error: 5 - .saturating_add(Weight::from_parts(593, 0).saturating_mul(n.into())) + // Minimum execution time: 5_796_000 picoseconds. + Weight::from_parts(6_399_722, 3703) + // Standard Error: 4 + .saturating_add(Weight::from_parts(581, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -546,67 +547,67 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_923_000 picoseconds. - Weight::from_parts(2_163_072, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(604, 0).saturating_mul(n.into())) + // Minimum execution time: 1_950_000 picoseconds. + Weight::from_parts(2_072_690, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(633, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 243_000 picoseconds. - Weight::from_parts(292_000, 0) + // Minimum execution time: 151_000 picoseconds. + Weight::from_parts(170_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(282_000, 0) + // Minimum execution time: 127_000 picoseconds. + Weight::from_parts(151_000, 0) } fn seal_return_data_size() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 244_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 112_000 picoseconds. + Weight::from_parts(149_000, 0) } fn seal_call_data_size() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(320_000, 0) + // Minimum execution time: 124_000 picoseconds. + Weight::from_parts(154_000, 0) } fn seal_gas_limit() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 430_000 picoseconds. - Weight::from_parts(482_000, 0) + // Minimum execution time: 331_000 picoseconds. + Weight::from_parts(370_000, 0) } fn seal_gas_price() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 243_000 picoseconds. - Weight::from_parts(276_000, 0) + // Minimum execution time: 137_000 picoseconds. + Weight::from_parts(163_000, 0) } fn seal_base_fee() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(292_000, 0) + // Minimum execution time: 130_000 picoseconds. + Weight::from_parts(154_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(308_000, 0) + // Minimum execution time: 150_000 picoseconds. + Weight::from_parts(167_000, 0) } /// Storage: `Session::Validators` (r:1 w:0) /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -614,8 +615,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `141` // Estimated: `1626` - // Minimum execution time: 19_525_000 picoseconds. - Weight::from_parts(20_010_000, 1626) + // Minimum execution time: 19_205_000 picoseconds. + Weight::from_parts(19_687_000, 1626) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `System::BlockHash` (r:1 w:0) @@ -624,60 +625,60 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `30` // Estimated: `3495` - // Minimum execution time: 3_399_000 picoseconds. - Weight::from_parts(3_644_000, 3495) + // Minimum execution time: 3_333_000 picoseconds. + Weight::from_parts(3_522_000, 3495) .saturating_add(T::DbWeight::get().reads(1_u64)) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 241_000 picoseconds. - Weight::from_parts(290_000, 0) + // Minimum execution time: 126_000 picoseconds. + Weight::from_parts(156_000, 0) } fn seal_weight_to_fee() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_490_000 picoseconds. - Weight::from_parts(1_602_000, 0) + // Minimum execution time: 1_364_000 picoseconds. + Weight::from_parts(1_477_000, 0) } /// The range of component `n` is `[0, 262140]`. fn seal_copy_to_contract(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 428_000 picoseconds. - Weight::from_parts(678_026, 0) + // Minimum execution time: 309_000 picoseconds. + Weight::from_parts(603_516, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(237, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(294, 0).saturating_mul(n.into())) } fn seal_call_data_load() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(293_000, 0) + // Minimum execution time: 120_000 picoseconds. + Weight::from_parts(142_000, 0) } /// The range of component `n` is `[0, 262144]`. fn seal_call_data_copy(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 237_000 picoseconds. - Weight::from_parts(320_140, 0) + // Minimum execution time: 121_000 picoseconds. + Weight::from_parts(125_420, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(149, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 279_000 picoseconds. - Weight::from_parts(382_051, 0) + // Minimum execution time: 150_000 picoseconds. + Weight::from_parts(445_753, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(296, 0).saturating_mul(n.into())) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) @@ -691,10 +692,10 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) fn seal_terminate() -> Weight { // Proof Size summary in bytes: - // Measured: `340` - // Estimated: `3805` - // Minimum execution time: 16_658_000 picoseconds. - Weight::from_parts(17_261_000, 3805) + // Measured: `585` + // Estimated: `4050` + // Minimum execution time: 16_990_000 picoseconds. + Weight::from_parts(17_538_000, 4050) .saturating_add(T::DbWeight::get().reads(3_u64)) .saturating_add(T::DbWeight::get().writes(4_u64)) } @@ -704,12 +705,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_200_000 picoseconds. - Weight::from_parts(4_180_661, 0) - // Standard Error: 4_523 - .saturating_add(Weight::from_parts(247_995, 0).saturating_mul(t.into())) - // Standard Error: 49 - .saturating_add(Weight::from_parts(1_155, 0).saturating_mul(n.into())) + // Minimum execution time: 4_218_000 picoseconds. + Weight::from_parts(4_143_231, 0) + // Standard Error: 3_302 + .saturating_add(Weight::from_parts(235_681, 0).saturating_mul(t.into())) + // Standard Error: 36 + .saturating_add(Weight::from_parts(1_298, 0).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -717,8 +718,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `648` // Estimated: `648` - // Minimum execution time: 7_061_000 picoseconds. - Weight::from_parts(7_402_000, 648) + // Minimum execution time: 6_942_000 picoseconds. + Weight::from_parts(7_454_000, 648) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -727,8 +728,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10658` // Estimated: `10658` - // Minimum execution time: 41_498_000 picoseconds. - Weight::from_parts(42_370_000, 10658) + // Minimum execution time: 41_245_000 picoseconds. + Weight::from_parts(42_033_000, 10658) .saturating_add(T::DbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -737,8 +738,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `648` // Estimated: `648` - // Minimum execution time: 8_130_000 picoseconds. - Weight::from_parts(8_649_000, 648) + // Minimum execution time: 7_877_000 picoseconds. + Weight::from_parts(8_376_000, 648) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -748,8 +749,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `10658` // Estimated: `10658` - // Minimum execution time: 42_717_000 picoseconds. - Weight::from_parts(43_402_000, 10658) + // Minimum execution time: 42_844_000 picoseconds. + Weight::from_parts(43_778_000, 10658) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -761,12 +762,12 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 8_618_000 picoseconds. - Weight::from_parts(9_194_984, 247) - // Standard Error: 61 - .saturating_add(Weight::from_parts(596, 0).saturating_mul(n.into())) - // Standard Error: 61 - .saturating_add(Weight::from_parts(621, 0).saturating_mul(o.into())) + // Minimum execution time: 8_517_000 picoseconds. + Weight::from_parts(9_327_675, 247) + // Standard Error: 52 + .saturating_add(Weight::from_parts(407, 0).saturating_mul(n.into())) + // Standard Error: 52 + .saturating_add(Weight::from_parts(434, 0).saturating_mul(o.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -778,10 +779,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_181_000 picoseconds. - Weight::from_parts(9_094_650, 247) - // Standard Error: 73 - .saturating_add(Weight::from_parts(726, 0).saturating_mul(n.into())) + // Minimum execution time: 8_144_000 picoseconds. + Weight::from_parts(9_105_918, 247) + // Standard Error: 72 + .saturating_add(Weight::from_parts(684, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -793,10 +794,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_880_000 picoseconds. - Weight::from_parts(8_599_303, 247) - // Standard Error: 70 - .saturating_add(Weight::from_parts(1_507, 0).saturating_mul(n.into())) + // Minimum execution time: 7_914_000 picoseconds. + Weight::from_parts(8_683_792, 247) + // Standard Error: 65 + .saturating_add(Weight::from_parts(1_267, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -807,10 +808,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_309_000 picoseconds. - Weight::from_parts(8_036_934, 247) - // Standard Error: 60 - .saturating_add(Weight::from_parts(1_101, 0).saturating_mul(n.into())) + // Minimum execution time: 7_287_000 picoseconds. + Weight::from_parts(8_027_630, 247) + // Standard Error: 56 + .saturating_add(Weight::from_parts(698, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -821,10 +822,10 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_784_000 picoseconds. - Weight::from_parts(9_891_990, 247) - // Standard Error: 96 - .saturating_add(Weight::from_parts(1_730, 0).saturating_mul(n.into())) + // Minimum execution time: 8_870_000 picoseconds. + Weight::from_parts(9_939_099, 247) + // Standard Error: 225 + .saturating_add(Weight::from_parts(832, 0).saturating_mul(n.into())) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -833,36 +834,36 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_366_000 picoseconds. - Weight::from_parts(1_464_000, 0) + // Minimum execution time: 1_306_000 picoseconds. + Weight::from_parts(1_404_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_787_000 picoseconds. - Weight::from_parts(1_887_000, 0) + // Minimum execution time: 1_689_000 picoseconds. + Weight::from_parts(1_811_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_382_000 picoseconds. - Weight::from_parts(1_482_000, 0) + // Minimum execution time: 1_302_000 picoseconds. + Weight::from_parts(1_387_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_545_000 picoseconds. - Weight::from_parts(1_636_000, 0) + // Minimum execution time: 1_450_000 picoseconds. + Weight::from_parts(1_531_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_102_000 picoseconds. - Weight::from_parts(1_213_000, 0) + // Minimum execution time: 1_003_000 picoseconds. + Weight::from_parts(1_083_000, 0) } /// The range of component `n` is `[0, 416]`. /// The range of component `o` is `[0, 416]`. @@ -870,52 +871,50 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_115_000 picoseconds. - Weight::from_parts(2_395_204, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(188, 0).saturating_mul(n.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(350, 0).saturating_mul(o.into())) + // Minimum execution time: 2_147_000 picoseconds. + Weight::from_parts(2_400_128, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(305, 0).saturating_mul(n.into())) + // Standard Error: 15 + .saturating_add(Weight::from_parts(275, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 416]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_934_000 picoseconds. - Weight::from_parts(2_260_885, 0) - // Standard Error: 24 - .saturating_add(Weight::from_parts(486, 0).saturating_mul(n.into())) + // Minimum execution time: 1_941_000 picoseconds. + Weight::from_parts(2_245_468, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(407, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 416]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_772_000 picoseconds. - Weight::from_parts(1_984_688, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(396, 0).saturating_mul(n.into())) + // Minimum execution time: 1_738_000 picoseconds. + Weight::from_parts(1_944_507, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(275, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 416]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(1_859_922, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(206, 0).saturating_mul(n.into())) + // Minimum execution time: 1_521_000 picoseconds. + Weight::from_parts(1_725_877, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(184, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 416]`. - fn seal_take_transient_storage(n: u32, ) -> Weight { + fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_423_000 picoseconds. - Weight::from_parts(2_642_712, 0) - // Standard Error: 29 - .saturating_add(Weight::from_parts(82, 0).saturating_mul(n.into())) + // Minimum execution time: 2_366_000 picoseconds. + Weight::from_parts(2_590_846, 0) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) @@ -931,18 +930,38 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1291 + t * (280 ±0)` - // Estimated: `4756 + t * (2518 ±0)` - // Minimum execution time: 32_088_000 picoseconds. - Weight::from_parts(33_107_024, 4756) - // Standard Error: 69_974 - .saturating_add(Weight::from_parts(7_633_181, 0).saturating_mul(t.into())) + // Measured: `1545 + t * (206 ±0)` + // Estimated: `5010 + t * (2608 ±0)` + // Minimum execution time: 34_508_000 picoseconds. + Weight::from_parts(35_724_702, 5010) + // Standard Error: 42_504 + .saturating_add(Weight::from_parts(5_295_834, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(2, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(T::DbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 2518).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2608).saturating_mul(t.into())) + } + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `i` is `[0, 262144]`. + fn seal_call_precompile(d: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + d * (453 ±0)` + // Estimated: `1959 + d * (1959 ±0)` + // Minimum execution time: 19_412_000 picoseconds. + Weight::from_parts(3_906_222, 1959) + // Standard Error: 378_943 + .saturating_add(Weight::from_parts(16_405_804, 0).saturating_mul(d.into())) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_205, 0).saturating_mul(i.into())) + .saturating_add(T::DbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 1959).saturating_mul(d.into())) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) @@ -954,8 +973,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `1236` // Estimated: `4701` - // Minimum execution time: 27_446_000 picoseconds. - Weight::from_parts(28_490_000, 4701) + // Minimum execution time: 28_834_000 picoseconds. + Weight::from_parts(30_072_000, 4701) .saturating_add(T::DbWeight::get().reads(3_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -969,12 +988,12 @@ impl WeightInfo for SubstrateWeight { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1297` - // Estimated: `4748` - // Minimum execution time: 112_807_000 picoseconds. - Weight::from_parts(105_926_417, 4748) + // Measured: `1260` + // Estimated: `4728` + // Minimum execution time: 112_787_000 picoseconds. + Weight::from_parts(105_258_744, 4728) // Standard Error: 10 - .saturating_add(Weight::from_parts(4_131, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_163, 0).saturating_mul(i.into())) .saturating_add(T::DbWeight::get().reads(4_u64)) .saturating_add(T::DbWeight::get().writes(3_u64)) } @@ -983,18 +1002,18 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 695_000 picoseconds. - Weight::from_parts(4_119_259, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_144, 0).saturating_mul(n.into())) + // Minimum execution time: 792_000 picoseconds. + Weight::from_parts(4_505_628, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_285, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn identity(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 320_000 picoseconds. - Weight::from_parts(391_494, 0) + // Minimum execution time: 300_000 picoseconds. + Weight::from_parts(468_743, 0) // Standard Error: 0 .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) } @@ -1003,98 +1022,98 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 732_000 picoseconds. - Weight::from_parts(772_000, 0) + // Minimum execution time: 744_000 picoseconds. + Weight::from_parts(771_000, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(3_777, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_928, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_034_000 picoseconds. - Weight::from_parts(3_180_130, 0) + // Minimum execution time: 1_002_000 picoseconds. + Weight::from_parts(3_889_121, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_666, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 688_000 picoseconds. - Weight::from_parts(3_243_045, 0) + // Minimum execution time: 562_000 picoseconds. + Weight::from_parts(3_823_066, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(1_529, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_569, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 631_000 picoseconds. - Weight::from_parts(4_498_880, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_518, 0).saturating_mul(n.into())) + // Minimum execution time: 543_000 picoseconds. + Weight::from_parts(3_582_133, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_576, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_552_000 picoseconds. - Weight::from_parts(32_018_928, 0) - // Standard Error: 11 - .saturating_add(Weight::from_parts(5_112, 0).saturating_mul(n.into())) + // Minimum execution time: 42_702_000 picoseconds. + Weight::from_parts(30_839_817, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(5_086, 0).saturating_mul(n.into())) } fn ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 44_766_000 picoseconds. - Weight::from_parts(45_173_000, 0) + // Minimum execution time: 44_770_000 picoseconds. + Weight::from_parts(45_791_000, 0) } fn bn128_add() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 15_234_000 picoseconds. - Weight::from_parts(15_820_000, 0) + // Minimum execution time: 15_705_000 picoseconds. + Weight::from_parts(16_885_000, 0) } fn bn128_mul() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_016_550_000 picoseconds. - Weight::from_parts(1_020_513_000, 0) + // Minimum execution time: 1_018_103_000 picoseconds. + Weight::from_parts(1_023_315_000, 0) } /// The range of component `n` is `[0, 20]`. fn bn128_pairing(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 353_000 picoseconds. - Weight::from_parts(5_176_648_341, 0) - // Standard Error: 11_496_037 - .saturating_add(Weight::from_parts(6_221_664_944, 0).saturating_mul(n.into())) + // Minimum execution time: 378_000 picoseconds. + Weight::from_parts(5_119_381_361, 0) + // Standard Error: 10_821_771 + .saturating_add(Weight::from_parts(6_202_434_383, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1200]`. fn blake2f(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 398_000 picoseconds. - Weight::from_parts(547_765, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(22_601, 0).saturating_mul(n.into())) + // Minimum execution time: 435_000 picoseconds. + Weight::from_parts(658_540, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(22_679, 0).saturating_mul(n.into())) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_761_000 picoseconds. - Weight::from_parts(12_998_000, 0) + // Minimum execution time: 12_639_000 picoseconds. + Weight::from_parts(12_782_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -1102,8 +1121,8 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `300` // Estimated: `3765` - // Minimum execution time: 12_657_000 picoseconds. - Weight::from_parts(13_525_000, 3765) + // Minimum execution time: 12_210_000 picoseconds. + Weight::from_parts(12_747_000, 3765) .saturating_add(T::DbWeight::get().reads(1_u64)) .saturating_add(T::DbWeight::get().writes(1_u64)) } @@ -1112,20 +1131,20 @@ impl WeightInfo for SubstrateWeight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_143_000 picoseconds. - Weight::from_parts(62_111_011, 0) - // Standard Error: 949 - .saturating_add(Weight::from_parts(150_706, 0).saturating_mul(r.into())) + // Minimum execution time: 11_244_000 picoseconds. + Weight::from_parts(48_207_913, 0) + // Standard Error: 495 + .saturating_add(Weight::from_parts(125_133, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 100000]`. fn instr_empty_loop(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_764_000 picoseconds. - Weight::from_parts(5_796_070, 0) - // Standard Error: 45 - .saturating_add(Weight::from_parts(73_162, 0).saturating_mul(r.into())) + // Minimum execution time: 2_776_000 picoseconds. + Weight::from_parts(6_412_428, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(72_988, 0).saturating_mul(r.into())) } } @@ -1135,10 +1154,10 @@ impl WeightInfo for () { /// Proof: `Revive::DeletionQueueCounter` (`max_values`: Some(1), `max_size`: Some(8), added: 503, mode: `Measured`) fn on_process_deletion_queue_batch() -> Weight { // Proof Size summary in bytes: - // Measured: `109` - // Estimated: `1594` - // Minimum execution time: 2_516_000 picoseconds. - Weight::from_parts(2_674_000, 1594) + // Measured: `147` + // Estimated: `1632` + // Minimum execution time: 2_971_000 picoseconds. + Weight::from_parts(3_218_000, 1632) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1148,10 +1167,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `425 + k * (69 ±0)` // Estimated: `415 + k * (70 ±0)` - // Minimum execution time: 13_953_000 picoseconds. - Weight::from_parts(14_357_000, 415) - // Standard Error: 2_229 - .saturating_add(Weight::from_parts(1_231_251, 0).saturating_mul(k.into())) + // Minimum execution time: 14_042_000 picoseconds. + Weight::from_parts(14_510_000, 415) + // Standard Error: 1_047 + .saturating_add(Weight::from_parts(1_167_098, 0).saturating_mul(k.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(k.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) @@ -1173,12 +1192,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 104857]`. fn call_with_code_per_byte(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `853 + c * (1 ±0)` - // Estimated: `6794 + c * (1 ±0)` - // Minimum execution time: 79_155_000 picoseconds. - Weight::from_parts(104_860_639, 6794) - // Standard Error: 13 - .saturating_add(Weight::from_parts(2_096, 0).saturating_mul(c.into())) + // Measured: `1178 + c * (1 ±0)` + // Estimated: `7119 + c * (1 ±0)` + // Minimum execution time: 81_842_000 picoseconds. + Weight::from_parts(110_091_076, 7119) + // Standard Error: 12 + .saturating_add(Weight::from_parts(2_123, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(c.into())) @@ -1198,12 +1217,12 @@ impl WeightInfo for () { /// The range of component `b` is `[0, 1]`. fn basic_block_compilation(b: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `4184` - // Estimated: `10124` - // Minimum execution time: 117_684_000 picoseconds. - Weight::from_parts(122_290_963, 10124) - // Standard Error: 525_579 - .saturating_add(Weight::from_parts(576_836, 0).saturating_mul(b.into())) + // Measured: `4513` + // Estimated: `10453` + // Minimum execution time: 121_768_000 picoseconds. + Weight::from_parts(126_040_712, 10453) + // Standard Error: 315_571 + .saturating_add(Weight::from_parts(961_687, 0).saturating_mul(b.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1225,14 +1244,14 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn instantiate_with_code(c: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `474` - // Estimated: `6383` - // Minimum execution time: 1_603_783_000 picoseconds. - Weight::from_parts(127_682_916, 6383) - // Standard Error: 33 - .saturating_add(Weight::from_parts(20_426, 0).saturating_mul(c.into())) + // Measured: `1122` + // Estimated: `7010` + // Minimum execution time: 1_593_697_000 picoseconds. + Weight::from_parts(150_335_332, 7010) + // Standard Error: 34 + .saturating_add(Weight::from_parts(19_710, 0).saturating_mul(c.into())) // Standard Error: 13 - .saturating_add(Weight::from_parts(5_634, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(5_466, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(6_u64)) } @@ -1253,12 +1272,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1316` - // Estimated: `4762` - // Minimum execution time: 149_706_000 picoseconds. - Weight::from_parts(84_227_566, 4762) + // Measured: `1886` + // Estimated: `5346` + // Minimum execution time: 155_867_000 picoseconds. + Weight::from_parts(88_055_300, 5346) // Standard Error: 25 - .saturating_add(Weight::from_parts(5_574, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(5_561, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -1276,10 +1295,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn call() -> Weight { // Proof Size summary in bytes: - // Measured: `1483` - // Estimated: `7423` - // Minimum execution time: 81_076_000 picoseconds. - Weight::from_parts(82_876_000, 7423) + // Measured: `1812` + // Estimated: `7752` + // Minimum execution time: 85_908_000 picoseconds. + Weight::from_parts(87_953_000, 7752) .saturating_add(RocksDbWeight::get().reads(7_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1292,12 +1311,12 @@ impl WeightInfo for () { /// The range of component `c` is `[0, 104857]`. fn upload_code(c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `164` - // Estimated: `3629` - // Minimum execution time: 47_357_000 picoseconds. - Weight::from_parts(22_260_997, 3629) - // Standard Error: 24 - .saturating_add(Weight::from_parts(15_158, 0).saturating_mul(c.into())) + // Measured: `505` + // Estimated: `3970` + // Minimum execution time: 51_305_000 picoseconds. + Weight::from_parts(27_197_599, 3970) + // Standard Error: 20 + .saturating_add(Weight::from_parts(14_721, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1309,10 +1328,10 @@ impl WeightInfo for () { /// Proof: `Revive::PristineCode` (`max_values`: None, `max_size`: Some(262180), added: 264655, mode: `Measured`) fn remove_code() -> Weight { // Proof Size summary in bytes: - // Measured: `322` - // Estimated: `3787` - // Minimum execution time: 39_242_000 picoseconds. - Weight::from_parts(40_311_000, 3787) + // Measured: `658` + // Estimated: `4123` + // Minimum execution time: 41_900_000 picoseconds. + Weight::from_parts(42_985_000, 4123) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1324,8 +1343,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `528` // Estimated: `6468` - // Minimum execution time: 20_456_000 picoseconds. - Weight::from_parts(20_922_000, 6468) + // Minimum execution time: 20_254_000 picoseconds. + Weight::from_parts(21_122_000, 6468) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1335,10 +1354,10 @@ impl WeightInfo for () { /// Proof: `Balances::Holds` (`max_values`: None, `max_size`: Some(409), added: 2884, mode: `Measured`) fn map_account() -> Weight { // Proof Size summary in bytes: - // Measured: `164` - // Estimated: `3629` - // Minimum execution time: 45_077_000 picoseconds. - Weight::from_parts(46_444_000, 3629) + // Measured: `813` + // Estimated: `4278` + // Minimum execution time: 50_406_000 picoseconds. + Weight::from_parts(51_939_000, 4278) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1348,10 +1367,10 @@ impl WeightInfo for () { /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) fn unmap_account() -> Weight { // Proof Size summary in bytes: - // Measured: `93` - // Estimated: `3558` - // Minimum execution time: 33_860_000 picoseconds. - Weight::from_parts(35_201_000, 3558) + // Measured: `395` + // Estimated: `3860` + // Minimum execution time: 37_679_000 picoseconds. + Weight::from_parts(39_183_000, 3860) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(2_u64)) } @@ -1363,8 +1382,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `145` // Estimated: `3610` - // Minimum execution time: 12_718_000 picoseconds. - Weight::from_parts(13_145_000, 3610) + // Minimum execution time: 12_684_000 picoseconds. + Weight::from_parts(13_240_000, 3610) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// The range of component `r` is `[0, 1600]`. @@ -1372,24 +1391,24 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 6_380_000 picoseconds. - Weight::from_parts(7_397_295, 0) - // Standard Error: 308 - .saturating_add(Weight::from_parts(161_735, 0).saturating_mul(r.into())) + // Minimum execution time: 6_583_000 picoseconds. + Weight::from_parts(8_416_964, 0) + // Standard Error: 170 + .saturating_add(Weight::from_parts(164_523, 0).saturating_mul(r.into())) } fn seal_caller() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_056_000 picoseconds. - Weight::from_parts(1_130_000, 0) + // Minimum execution time: 199_000 picoseconds. + Weight::from_parts(233_000, 0) } fn seal_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_029_000 picoseconds. - Weight::from_parts(1_095_000, 0) + // Minimum execution time: 184_000 picoseconds. + Weight::from_parts(206_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) @@ -1397,18 +1416,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `306` // Estimated: `3771` - // Minimum execution time: 8_388_000 picoseconds. - Weight::from_parts(8_815_000, 3771) + // Minimum execution time: 8_270_000 picoseconds. + Weight::from_parts(8_700_000, 3771) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) fn seal_to_account_id() -> Weight { // Proof Size summary in bytes: - // Measured: `269` - // Estimated: `3734` - // Minimum execution time: 6_298_000 picoseconds. - Weight::from_parts(6_675_000, 3734) + // Measured: `538` + // Estimated: `4003` + // Minimum execution time: 9_364_000 picoseconds. + Weight::from_parts(9_672_000, 4003) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) @@ -1417,16 +1436,16 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `402` // Estimated: `3867` - // Minimum execution time: 9_139_000 picoseconds. - Weight::from_parts(9_472_000, 3867) + // Minimum execution time: 9_183_000 picoseconds. + Weight::from_parts(9_609_000, 3867) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 257_000 picoseconds. - Weight::from_parts(305_000, 0) + // Minimum execution time: 131_000 picoseconds. + Weight::from_parts(176_000, 0) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) @@ -1436,51 +1455,51 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `472` // Estimated: `3937` - // Minimum execution time: 12_911_000 picoseconds. - Weight::from_parts(13_397_000, 3937) + // Minimum execution time: 12_217_000 picoseconds. + Weight::from_parts(13_005_000, 3937) .saturating_add(RocksDbWeight::get().reads(2_u64)) } fn seal_caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 313_000 picoseconds. - Weight::from_parts(347_000, 0) + // Minimum execution time: 208_000 picoseconds. + Weight::from_parts(228_000, 0) } fn seal_caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(299_000, 0) + // Minimum execution time: 170_000 picoseconds. + Weight::from_parts(203_000, 0) } fn seal_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 286_000 picoseconds. - Weight::from_parts(346_000, 0) + // Minimum execution time: 182_000 picoseconds. + Weight::from_parts(204_000, 0) } fn seal_weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 643_000 picoseconds. - Weight::from_parts(736_000, 0) + // Minimum execution time: 576_000 picoseconds. + Weight::from_parts(637_000, 0) } fn seal_ref_time_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 241_000 picoseconds. - Weight::from_parts(276_000, 0) + // Minimum execution time: 131_000 picoseconds. + Weight::from_parts(168_000, 0) } fn seal_balance() -> Weight { // Proof Size summary in bytes: - // Measured: `140` + // Measured: `103` // Estimated: `0` - // Minimum execution time: 4_917_000 picoseconds. - Weight::from_parts(5_174_000, 0) + // Minimum execution time: 4_466_000 picoseconds. + Weight::from_parts(4_813_000, 0) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) @@ -1488,10 +1507,10 @@ impl WeightInfo for () { /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) fn seal_balance_of() -> Weight { // Proof Size summary in bytes: - // Measured: `272` - // Estimated: `3737` - // Minimum execution time: 8_248_000 picoseconds. - Weight::from_parts(8_657_000, 3737) + // Measured: `517` + // Estimated: `3982` + // Minimum execution time: 8_786_000 picoseconds. + Weight::from_parts(9_450_000, 3982) .saturating_add(RocksDbWeight::get().reads(2_u64)) } /// Storage: `Revive::ImmutableDataOf` (r:1 w:0) @@ -1501,10 +1520,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `238 + n * (1 ±0)` // Estimated: `3703 + n * (1 ±0)` - // Minimum execution time: 5_551_000 picoseconds. - Weight::from_parts(6_240_836, 3703) - // Standard Error: 5 - .saturating_add(Weight::from_parts(593, 0).saturating_mul(n.into())) + // Minimum execution time: 5_796_000 picoseconds. + Weight::from_parts(6_399_722, 3703) + // Standard Error: 4 + .saturating_add(Weight::from_parts(581, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1515,67 +1534,67 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_923_000 picoseconds. - Weight::from_parts(2_163_072, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(604, 0).saturating_mul(n.into())) + // Minimum execution time: 1_950_000 picoseconds. + Weight::from_parts(2_072_690, 0) + // Standard Error: 1 + .saturating_add(Weight::from_parts(633, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().writes(1_u64)) } fn seal_value_transferred() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 243_000 picoseconds. - Weight::from_parts(292_000, 0) + // Minimum execution time: 151_000 picoseconds. + Weight::from_parts(170_000, 0) } fn seal_minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(282_000, 0) + // Minimum execution time: 127_000 picoseconds. + Weight::from_parts(151_000, 0) } fn seal_return_data_size() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 244_000 picoseconds. - Weight::from_parts(285_000, 0) + // Minimum execution time: 112_000 picoseconds. + Weight::from_parts(149_000, 0) } fn seal_call_data_size() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(320_000, 0) + // Minimum execution time: 124_000 picoseconds. + Weight::from_parts(154_000, 0) } fn seal_gas_limit() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 430_000 picoseconds. - Weight::from_parts(482_000, 0) + // Minimum execution time: 331_000 picoseconds. + Weight::from_parts(370_000, 0) } fn seal_gas_price() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 243_000 picoseconds. - Weight::from_parts(276_000, 0) + // Minimum execution time: 137_000 picoseconds. + Weight::from_parts(163_000, 0) } fn seal_base_fee() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 259_000 picoseconds. - Weight::from_parts(292_000, 0) + // Minimum execution time: 130_000 picoseconds. + Weight::from_parts(154_000, 0) } fn seal_block_number() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 255_000 picoseconds. - Weight::from_parts(308_000, 0) + // Minimum execution time: 150_000 picoseconds. + Weight::from_parts(167_000, 0) } /// Storage: `Session::Validators` (r:1 w:0) /// Proof: `Session::Validators` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) @@ -1583,8 +1602,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `141` // Estimated: `1626` - // Minimum execution time: 19_525_000 picoseconds. - Weight::from_parts(20_010_000, 1626) + // Minimum execution time: 19_205_000 picoseconds. + Weight::from_parts(19_687_000, 1626) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `System::BlockHash` (r:1 w:0) @@ -1593,60 +1612,60 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `30` // Estimated: `3495` - // Minimum execution time: 3_399_000 picoseconds. - Weight::from_parts(3_644_000, 3495) + // Minimum execution time: 3_333_000 picoseconds. + Weight::from_parts(3_522_000, 3495) .saturating_add(RocksDbWeight::get().reads(1_u64)) } fn seal_now() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 241_000 picoseconds. - Weight::from_parts(290_000, 0) + // Minimum execution time: 126_000 picoseconds. + Weight::from_parts(156_000, 0) } fn seal_weight_to_fee() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_490_000 picoseconds. - Weight::from_parts(1_602_000, 0) + // Minimum execution time: 1_364_000 picoseconds. + Weight::from_parts(1_477_000, 0) } /// The range of component `n` is `[0, 262140]`. fn seal_copy_to_contract(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 428_000 picoseconds. - Weight::from_parts(678_026, 0) + // Minimum execution time: 309_000 picoseconds. + Weight::from_parts(603_516, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(237, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(294, 0).saturating_mul(n.into())) } fn seal_call_data_load() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 252_000 picoseconds. - Weight::from_parts(293_000, 0) + // Minimum execution time: 120_000 picoseconds. + Weight::from_parts(142_000, 0) } /// The range of component `n` is `[0, 262144]`. fn seal_call_data_copy(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 237_000 picoseconds. - Weight::from_parts(320_140, 0) + // Minimum execution time: 121_000 picoseconds. + Weight::from_parts(125_420, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(149, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262140]`. fn seal_return(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 279_000 picoseconds. - Weight::from_parts(382_051, 0) + // Minimum execution time: 150_000 picoseconds. + Weight::from_parts(445_753, 0) // Standard Error: 0 - .saturating_add(Weight::from_parts(239, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(296, 0).saturating_mul(n.into())) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) @@ -1660,10 +1679,10 @@ impl WeightInfo for () { /// Proof: `Revive::ImmutableDataOf` (`max_values`: None, `max_size`: Some(4118), added: 6593, mode: `Measured`) fn seal_terminate() -> Weight { // Proof Size summary in bytes: - // Measured: `340` - // Estimated: `3805` - // Minimum execution time: 16_658_000 picoseconds. - Weight::from_parts(17_261_000, 3805) + // Measured: `585` + // Estimated: `4050` + // Minimum execution time: 16_990_000 picoseconds. + Weight::from_parts(17_538_000, 4050) .saturating_add(RocksDbWeight::get().reads(3_u64)) .saturating_add(RocksDbWeight::get().writes(4_u64)) } @@ -1673,12 +1692,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 4_200_000 picoseconds. - Weight::from_parts(4_180_661, 0) - // Standard Error: 4_523 - .saturating_add(Weight::from_parts(247_995, 0).saturating_mul(t.into())) - // Standard Error: 49 - .saturating_add(Weight::from_parts(1_155, 0).saturating_mul(n.into())) + // Minimum execution time: 4_218_000 picoseconds. + Weight::from_parts(4_143_231, 0) + // Standard Error: 3_302 + .saturating_add(Weight::from_parts(235_681, 0).saturating_mul(t.into())) + // Standard Error: 36 + .saturating_add(Weight::from_parts(1_298, 0).saturating_mul(n.into())) } /// Storage: `Skipped::Metadata` (r:0 w:0) /// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`) @@ -1686,8 +1705,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `648` // Estimated: `648` - // Minimum execution time: 7_061_000 picoseconds. - Weight::from_parts(7_402_000, 648) + // Minimum execution time: 6_942_000 picoseconds. + Weight::from_parts(7_454_000, 648) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1696,8 +1715,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10658` // Estimated: `10658` - // Minimum execution time: 41_498_000 picoseconds. - Weight::from_parts(42_370_000, 10658) + // Minimum execution time: 41_245_000 picoseconds. + Weight::from_parts(42_033_000, 10658) .saturating_add(RocksDbWeight::get().reads(1_u64)) } /// Storage: `Skipped::Metadata` (r:0 w:0) @@ -1706,8 +1725,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `648` // Estimated: `648` - // Minimum execution time: 8_130_000 picoseconds. - Weight::from_parts(8_649_000, 648) + // Minimum execution time: 7_877_000 picoseconds. + Weight::from_parts(8_376_000, 648) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1717,8 +1736,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `10658` // Estimated: `10658` - // Minimum execution time: 42_717_000 picoseconds. - Weight::from_parts(43_402_000, 10658) + // Minimum execution time: 42_844_000 picoseconds. + Weight::from_parts(43_778_000, 10658) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -1730,12 +1749,12 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + o * (1 ±0)` // Estimated: `247 + o * (1 ±0)` - // Minimum execution time: 8_618_000 picoseconds. - Weight::from_parts(9_194_984, 247) - // Standard Error: 61 - .saturating_add(Weight::from_parts(596, 0).saturating_mul(n.into())) - // Standard Error: 61 - .saturating_add(Weight::from_parts(621, 0).saturating_mul(o.into())) + // Minimum execution time: 8_517_000 picoseconds. + Weight::from_parts(9_327_675, 247) + // Standard Error: 52 + .saturating_add(Weight::from_parts(407, 0).saturating_mul(n.into())) + // Standard Error: 52 + .saturating_add(Weight::from_parts(434, 0).saturating_mul(o.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(o.into())) @@ -1747,10 +1766,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_181_000 picoseconds. - Weight::from_parts(9_094_650, 247) - // Standard Error: 73 - .saturating_add(Weight::from_parts(726, 0).saturating_mul(n.into())) + // Minimum execution time: 8_144_000 picoseconds. + Weight::from_parts(9_105_918, 247) + // Standard Error: 72 + .saturating_add(Weight::from_parts(684, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1762,10 +1781,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_880_000 picoseconds. - Weight::from_parts(8_599_303, 247) - // Standard Error: 70 - .saturating_add(Weight::from_parts(1_507, 0).saturating_mul(n.into())) + // Minimum execution time: 7_914_000 picoseconds. + Weight::from_parts(8_683_792, 247) + // Standard Error: 65 + .saturating_add(Weight::from_parts(1_267, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1776,10 +1795,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 7_309_000 picoseconds. - Weight::from_parts(8_036_934, 247) - // Standard Error: 60 - .saturating_add(Weight::from_parts(1_101, 0).saturating_mul(n.into())) + // Minimum execution time: 7_287_000 picoseconds. + Weight::from_parts(8_027_630, 247) + // Standard Error: 56 + .saturating_add(Weight::from_parts(698, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) } @@ -1790,10 +1809,10 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `248 + n * (1 ±0)` // Estimated: `247 + n * (1 ±0)` - // Minimum execution time: 8_784_000 picoseconds. - Weight::from_parts(9_891_990, 247) - // Standard Error: 96 - .saturating_add(Weight::from_parts(1_730, 0).saturating_mul(n.into())) + // Minimum execution time: 8_870_000 picoseconds. + Weight::from_parts(9_939_099, 247) + // Standard Error: 225 + .saturating_add(Weight::from_parts(832, 0).saturating_mul(n.into())) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) .saturating_add(Weight::from_parts(0, 1).saturating_mul(n.into())) @@ -1802,36 +1821,36 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_366_000 picoseconds. - Weight::from_parts(1_464_000, 0) + // Minimum execution time: 1_306_000 picoseconds. + Weight::from_parts(1_404_000, 0) } fn set_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_787_000 picoseconds. - Weight::from_parts(1_887_000, 0) + // Minimum execution time: 1_689_000 picoseconds. + Weight::from_parts(1_811_000, 0) } fn get_transient_storage_empty() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_382_000 picoseconds. - Weight::from_parts(1_482_000, 0) + // Minimum execution time: 1_302_000 picoseconds. + Weight::from_parts(1_387_000, 0) } fn get_transient_storage_full() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_545_000 picoseconds. - Weight::from_parts(1_636_000, 0) + // Minimum execution time: 1_450_000 picoseconds. + Weight::from_parts(1_531_000, 0) } fn rollback_transient_storage() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_102_000 picoseconds. - Weight::from_parts(1_213_000, 0) + // Minimum execution time: 1_003_000 picoseconds. + Weight::from_parts(1_083_000, 0) } /// The range of component `n` is `[0, 416]`. /// The range of component `o` is `[0, 416]`. @@ -1839,52 +1858,50 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_115_000 picoseconds. - Weight::from_parts(2_395_204, 0) - // Standard Error: 17 - .saturating_add(Weight::from_parts(188, 0).saturating_mul(n.into())) - // Standard Error: 17 - .saturating_add(Weight::from_parts(350, 0).saturating_mul(o.into())) + // Minimum execution time: 2_147_000 picoseconds. + Weight::from_parts(2_400_128, 0) + // Standard Error: 15 + .saturating_add(Weight::from_parts(305, 0).saturating_mul(n.into())) + // Standard Error: 15 + .saturating_add(Weight::from_parts(275, 0).saturating_mul(o.into())) } /// The range of component `n` is `[0, 416]`. fn seal_clear_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_934_000 picoseconds. - Weight::from_parts(2_260_885, 0) - // Standard Error: 24 - .saturating_add(Weight::from_parts(486, 0).saturating_mul(n.into())) + // Minimum execution time: 1_941_000 picoseconds. + Weight::from_parts(2_245_468, 0) + // Standard Error: 18 + .saturating_add(Weight::from_parts(407, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 416]`. fn seal_get_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_772_000 picoseconds. - Weight::from_parts(1_984_688, 0) - // Standard Error: 20 - .saturating_add(Weight::from_parts(396, 0).saturating_mul(n.into())) + // Minimum execution time: 1_738_000 picoseconds. + Weight::from_parts(1_944_507, 0) + // Standard Error: 16 + .saturating_add(Weight::from_parts(275, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 416]`. fn seal_contains_transient_storage(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_670_000 picoseconds. - Weight::from_parts(1_859_922, 0) - // Standard Error: 16 - .saturating_add(Weight::from_parts(206, 0).saturating_mul(n.into())) + // Minimum execution time: 1_521_000 picoseconds. + Weight::from_parts(1_725_877, 0) + // Standard Error: 13 + .saturating_add(Weight::from_parts(184, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 416]`. - fn seal_take_transient_storage(n: u32, ) -> Weight { + fn seal_take_transient_storage(_n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_423_000 picoseconds. - Weight::from_parts(2_642_712, 0) - // Standard Error: 29 - .saturating_add(Weight::from_parts(82, 0).saturating_mul(n.into())) + // Minimum execution time: 2_366_000 picoseconds. + Weight::from_parts(2_590_846, 0) } /// Storage: `Revive::OriginalAccount` (r:1 w:0) /// Proof: `Revive::OriginalAccount` (`max_values`: None, `max_size`: Some(52), added: 2527, mode: `Measured`) @@ -1900,18 +1917,38 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_call(t: u32, i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1291 + t * (280 ±0)` - // Estimated: `4756 + t * (2518 ±0)` - // Minimum execution time: 32_088_000 picoseconds. - Weight::from_parts(33_107_024, 4756) - // Standard Error: 69_974 - .saturating_add(Weight::from_parts(7_633_181, 0).saturating_mul(t.into())) + // Measured: `1545 + t * (206 ±0)` + // Estimated: `5010 + t * (2608 ±0)` + // Minimum execution time: 34_508_000 picoseconds. + Weight::from_parts(35_724_702, 5010) + // Standard Error: 42_504 + .saturating_add(Weight::from_parts(5_295_834, 0).saturating_mul(t.into())) // Standard Error: 0 - .saturating_add(Weight::from_parts(3, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(2, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(t.into()))) .saturating_add(RocksDbWeight::get().writes(1_u64)) - .saturating_add(Weight::from_parts(0, 2518).saturating_mul(t.into())) + .saturating_add(Weight::from_parts(0, 2608).saturating_mul(t.into())) + } + /// Storage: `Revive::ContractInfoOf` (r:1 w:1) + /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + /// The range of component `d` is `[0, 1]`. + /// The range of component `i` is `[0, 262144]`. + fn seal_call_precompile(d: u32, i: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `0 + d * (453 ±0)` + // Estimated: `1959 + d * (1959 ±0)` + // Minimum execution time: 19_412_000 picoseconds. + Weight::from_parts(3_906_222, 1959) + // Standard Error: 378_943 + .saturating_add(Weight::from_parts(16_405_804, 0).saturating_mul(d.into())) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_205, 0).saturating_mul(i.into())) + .saturating_add(RocksDbWeight::get().reads((2_u64).saturating_mul(d.into()))) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(d.into()))) + .saturating_add(Weight::from_parts(0, 1959).saturating_mul(d.into())) } /// Storage: `Revive::ContractInfoOf` (r:1 w:0) /// Proof: `Revive::ContractInfoOf` (`max_values`: None, `max_size`: Some(242), added: 2717, mode: `Measured`) @@ -1923,8 +1960,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `1236` // Estimated: `4701` - // Minimum execution time: 27_446_000 picoseconds. - Weight::from_parts(28_490_000, 4701) + // Minimum execution time: 28_834_000 picoseconds. + Weight::from_parts(30_072_000, 4701) .saturating_add(RocksDbWeight::get().reads(3_u64)) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) @@ -1938,12 +1975,12 @@ impl WeightInfo for () { /// The range of component `i` is `[0, 262144]`. fn seal_instantiate(i: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `1297` - // Estimated: `4748` - // Minimum execution time: 112_807_000 picoseconds. - Weight::from_parts(105_926_417, 4748) + // Measured: `1260` + // Estimated: `4728` + // Minimum execution time: 112_787_000 picoseconds. + Weight::from_parts(105_258_744, 4728) // Standard Error: 10 - .saturating_add(Weight::from_parts(4_131, 0).saturating_mul(i.into())) + .saturating_add(Weight::from_parts(4_163, 0).saturating_mul(i.into())) .saturating_add(RocksDbWeight::get().reads(4_u64)) .saturating_add(RocksDbWeight::get().writes(3_u64)) } @@ -1952,18 +1989,18 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 695_000 picoseconds. - Weight::from_parts(4_119_259, 0) - // Standard Error: 2 - .saturating_add(Weight::from_parts(1_144, 0).saturating_mul(n.into())) + // Minimum execution time: 792_000 picoseconds. + Weight::from_parts(4_505_628, 0) + // Standard Error: 3 + .saturating_add(Weight::from_parts(1_285, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn identity(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 320_000 picoseconds. - Weight::from_parts(391_494, 0) + // Minimum execution time: 300_000 picoseconds. + Weight::from_parts(468_743, 0) // Standard Error: 0 .saturating_add(Weight::from_parts(148, 0).saturating_mul(n.into())) } @@ -1972,98 +2009,98 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 732_000 picoseconds. - Weight::from_parts(772_000, 0) + // Minimum execution time: 744_000 picoseconds. + Weight::from_parts(771_000, 0) // Standard Error: 1 - .saturating_add(Weight::from_parts(3_777, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_928, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_keccak_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_034_000 picoseconds. - Weight::from_parts(3_180_130, 0) + // Minimum execution time: 1_002_000 picoseconds. + Weight::from_parts(3_889_121, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(3_627, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(3_666, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_256(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 688_000 picoseconds. - Weight::from_parts(3_243_045, 0) + // Minimum execution time: 562_000 picoseconds. + Weight::from_parts(3_823_066, 0) // Standard Error: 3 - .saturating_add(Weight::from_parts(1_529, 0).saturating_mul(n.into())) + .saturating_add(Weight::from_parts(1_569, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 262144]`. fn seal_hash_blake2_128(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 631_000 picoseconds. - Weight::from_parts(4_498_880, 0) - // Standard Error: 3 - .saturating_add(Weight::from_parts(1_518, 0).saturating_mul(n.into())) + // Minimum execution time: 543_000 picoseconds. + Weight::from_parts(3_582_133, 0) + // Standard Error: 2 + .saturating_add(Weight::from_parts(1_576, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 261889]`. fn seal_sr25519_verify(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 43_552_000 picoseconds. - Weight::from_parts(32_018_928, 0) - // Standard Error: 11 - .saturating_add(Weight::from_parts(5_112, 0).saturating_mul(n.into())) + // Minimum execution time: 42_702_000 picoseconds. + Weight::from_parts(30_839_817, 0) + // Standard Error: 10 + .saturating_add(Weight::from_parts(5_086, 0).saturating_mul(n.into())) } fn ecdsa_recover() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 44_766_000 picoseconds. - Weight::from_parts(45_173_000, 0) + // Minimum execution time: 44_770_000 picoseconds. + Weight::from_parts(45_791_000, 0) } fn bn128_add() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 15_234_000 picoseconds. - Weight::from_parts(15_820_000, 0) + // Minimum execution time: 15_705_000 picoseconds. + Weight::from_parts(16_885_000, 0) } fn bn128_mul() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 1_016_550_000 picoseconds. - Weight::from_parts(1_020_513_000, 0) + // Minimum execution time: 1_018_103_000 picoseconds. + Weight::from_parts(1_023_315_000, 0) } /// The range of component `n` is `[0, 20]`. fn bn128_pairing(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 353_000 picoseconds. - Weight::from_parts(5_176_648_341, 0) - // Standard Error: 11_496_037 - .saturating_add(Weight::from_parts(6_221_664_944, 0).saturating_mul(n.into())) + // Minimum execution time: 378_000 picoseconds. + Weight::from_parts(5_119_381_361, 0) + // Standard Error: 10_821_771 + .saturating_add(Weight::from_parts(6_202_434_383, 0).saturating_mul(n.into())) } /// The range of component `n` is `[0, 1200]`. fn blake2f(n: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 398_000 picoseconds. - Weight::from_parts(547_765, 0) - // Standard Error: 4 - .saturating_add(Weight::from_parts(22_601, 0).saturating_mul(n.into())) + // Minimum execution time: 435_000 picoseconds. + Weight::from_parts(658_540, 0) + // Standard Error: 6 + .saturating_add(Weight::from_parts(22_679, 0).saturating_mul(n.into())) } fn seal_ecdsa_to_eth_address() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 12_761_000 picoseconds. - Weight::from_parts(12_998_000, 0) + // Minimum execution time: 12_639_000 picoseconds. + Weight::from_parts(12_782_000, 0) } /// Storage: `Revive::CodeInfoOf` (r:1 w:1) /// Proof: `Revive::CodeInfoOf` (`max_values`: None, `max_size`: Some(96), added: 2571, mode: `Measured`) @@ -2071,8 +2108,8 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `300` // Estimated: `3765` - // Minimum execution time: 12_657_000 picoseconds. - Weight::from_parts(13_525_000, 3765) + // Minimum execution time: 12_210_000 picoseconds. + Weight::from_parts(12_747_000, 3765) .saturating_add(RocksDbWeight::get().reads(1_u64)) .saturating_add(RocksDbWeight::get().writes(1_u64)) } @@ -2081,19 +2118,19 @@ impl WeightInfo for () { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 11_143_000 picoseconds. - Weight::from_parts(62_111_011, 0) - // Standard Error: 949 - .saturating_add(Weight::from_parts(150_706, 0).saturating_mul(r.into())) + // Minimum execution time: 11_244_000 picoseconds. + Weight::from_parts(48_207_913, 0) + // Standard Error: 495 + .saturating_add(Weight::from_parts(125_133, 0).saturating_mul(r.into())) } /// The range of component `r` is `[0, 100000]`. fn instr_empty_loop(r: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 2_764_000 picoseconds. - Weight::from_parts(5_796_070, 0) - // Standard Error: 45 - .saturating_add(Weight::from_parts(73_162, 0).saturating_mul(r.into())) + // Minimum execution time: 2_776_000 picoseconds. + Weight::from_parts(6_412_428, 0) + // Standard Error: 17 + .saturating_add(Weight::from_parts(72_988, 0).saturating_mul(r.into())) } }