diff --git a/Cargo.lock b/Cargo.lock index 687935c21fd6d..d49cee5b65ad5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3580,6 +3580,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "const-crypto" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c06f1eb05f06cf2e380fdded278fbf056a38974299d77960555a311dcf91a52" +dependencies = [ + "keccak-const", + "sha2-const-stable", +] + [[package]] name = "const-hex" version = "1.14.0" @@ -8973,6 +8983,12 @@ dependencies = [ "sha3-asm", ] +[[package]] +name = "keccak-const" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d8d8ce877200136358e0bbff3a77965875db3af755a11e1fa6b1b3e2df13ea" + [[package]] name = "keccak-hash" version = "0.11.0" @@ -13138,7 +13154,10 @@ dependencies = [ name = "pallet-revive-uapi" version = "0.1.0" dependencies = [ + "alloy-core", "bitflags 1.3.2", + "const-crypto", + "hex-literal", "pallet-revive-proc-macro", "parity-scale-codec", "polkavm-derive 0.27.0", @@ -21551,6 +21570,12 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha2-const-stable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f179d4e11094a893b82fff208f74d448a7512f99f5a0acbd5c679b705f83ed9" + [[package]] name = "sha3" version = "0.10.8" diff --git a/prdoc/pr_9517.prdoc b/prdoc/pr_9517.prdoc new file mode 100644 index 0000000000000..d6bab909344be --- /dev/null +++ b/prdoc/pr_9517.prdoc @@ -0,0 +1,13 @@ +title: '[pallet-revive] Migrate various getters to `System` pre-compile' +doc: +- audience: Runtime Dev + description: Migrates `own_code_hash`, `caller_is_origin`, `caller_is_root`, + `weight_left`, `minimum_balance`. Contains minor other fixes (removing leftovers + from deprecating chain extensions, stabilizing `block_hash` in overlooked crates, …). +crates: +- name: pallet-revive + bump: patch +- name: pallet-revive-fixtures + bump: major +- name: pallet-revive-uapi + bump: major diff --git a/substrate/frame/contracts/src/chain_extension.rs b/substrate/frame/contracts/src/chain_extension.rs index b9bb451fd734a..b2f7408917c97 100644 --- a/substrate/frame/contracts/src/chain_extension.rs +++ b/substrate/frame/contracts/src/chain_extension.rs @@ -67,7 +67,7 @@ //! # Example //! //! The ink-examples repository maintains an -//! [end-to-end example](https://github.com/paritytech/ink-examples/tree/main/rand-extension) +//! [end-to-end example](https://github.com/use-ink/ink-examples/tree/v5.x.x/rand-extension) //! on how to use a chain extension in order to provide new features to ink! contracts. use crate::{ diff --git a/substrate/frame/revive/Cargo.toml b/substrate/frame/revive/Cargo.toml index 6a2078dd50c43..15b929e043287 100644 --- a/substrate/frame/revive/Cargo.toml +++ b/substrate/frame/revive/Cargo.toml @@ -47,7 +47,7 @@ frame-support = { workspace = true } frame-system = { workspace = true } pallet-revive-fixtures = { workspace = true, optional = true } pallet-revive-proc-macro = { workspace = true } -pallet-revive-uapi = { workspace = true, features = ["scale"] } +pallet-revive-uapi = { workspace = true, features = ["precompiles-sol-interfaces", "scale"] } pallet-transaction-payment = { workspace = true } ripemd = { workspace = true } sp-api = { workspace = true } diff --git a/substrate/frame/revive/fixtures/contracts/call_caller_is_origin.rs b/substrate/frame/revive/fixtures/contracts/call_caller_is_origin.rs new file mode 100644 index 0000000000000..b8fc11b4a3463 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_caller_is_origin.rs @@ -0,0 +1,47 @@ +// 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 fixture calls the `callerIsOrigin` function on the +//! `System` pre-compile. + +#![no_std] +#![no_main] +include!("../panic_handler.rs"); + +use uapi::{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() { + let mut output = [0u8; 32]; + let _ = api::call( + uapi::CallFlags::READ_ONLY, + &uapi::SYSTEM_PRECOMPILE_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. + &[0u8; 32], // Value transferred to the contract. + &uapi::solidity_selector("callerIsOrigin()"), + Some(&mut &mut output[..]), + ).unwrap(); + + api::return_value(uapi::ReturnFlags::empty(), &output); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_caller_is_root.rs b/substrate/frame/revive/fixtures/contracts/call_caller_is_root.rs new file mode 100644 index 0000000000000..5a50abaac13fc --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_caller_is_root.rs @@ -0,0 +1,47 @@ +// 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 fixture calls the `callerIsRoot` function on the +//! `System` pre-compile. + +#![no_std] +#![no_main] +include!("../panic_handler.rs"); + +use uapi::{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() { + let mut output = [0u8; 32]; + let _ = api::call( + uapi::CallFlags::READ_ONLY, + &uapi::SYSTEM_PRECOMPILE_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. + &[0u8; 32], // Value transferred to the contract. + &uapi::solidity_selector("callerIsRoot()"), + Some(&mut &mut output[..]), + ).unwrap(); + + api::return_value(uapi::ReturnFlags::empty(), &output); +} diff --git a/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs index 9e208845c3362..6b9c7af292dd7 100644 --- a/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs +++ b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs @@ -27,7 +27,7 @@ #![no_main] include!("../panic_handler.rs"); -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, u256_bytes}; const BUF_SIZE: usize = 8; static DATA: [u8; BUF_SIZE] = [1, 2, 3, 4, 5, 6, 7, 8]; @@ -58,8 +58,19 @@ fn assert_call(callee_address: &[u8; 20], expected_output: [u8; /// Instantiate this contract with an output buf of size `N` /// and expect the instantiate output to match `expected_output`. fn assert_instantiate(expected_output: [u8; BUF_SIZE]) { - let mut code_hash = [0; 32]; - api::own_code_hash(&mut code_hash); + let mut output_buf1 = [0u8; 32]; + let output1 = &mut &mut output_buf1[..]; + let _ = api::call( + uapi::CallFlags::READ_ONLY, + &uapi::SYSTEM_PRECOMPILE_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. + &[0u8; 32], // Value transferred to the contract. + &uapi::solidity_selector("ownCodeHash()"), + Some(output1), + ).unwrap(); + assert_ne!(output_buf1, [0u8; 32]); let mut output_buf = [0u8; BUF_SIZE]; let output_buf_capped = &mut &mut output_buf[..N]; @@ -68,8 +79,8 @@ fn assert_instantiate(expected_output: [u8; BUF_SIZE]) { u64::MAX, u64::MAX, &[u8::MAX; 32], - &[0; 32], - &code_hash, + &u256_bytes(0), + output_buf1.clone().as_slice(), None, Some(output_buf_capped), None, diff --git a/substrate/frame/revive/fixtures/contracts/call_own_code_hash.rs b/substrate/frame/revive/fixtures/contracts/call_own_code_hash.rs new file mode 100644 index 0000000000000..7d215b0c19ba2 --- /dev/null +++ b/substrate/frame/revive/fixtures/contracts/call_own_code_hash.rs @@ -0,0 +1,51 @@ +// 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 fixture calls the `ownCodeHash` function on the +//! `System` pre-compile. + +#![allow(unused_imports)] +#![no_std] +#![no_main] +include!("../panic_handler.rs"); + +use core::num::NonZero; +use uapi::{HostFn, HostFnImpl as api, u256_bytes}; +use hex_literal::hex; + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn deploy() { } + +#[no_mangle] +#[polkavm_derive::polkavm_export] +pub extern "C" fn call() { + let mut output_buf = [0u8; 32]; + let output = &mut &mut output_buf[..]; + let _ = api::call( + uapi::CallFlags::READ_ONLY, + &uapi::SYSTEM_PRECOMPILE_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. + &[0u8; 32], // Value transferred to the contract. + &uapi::solidity_selector("ownCodeHash()"), + Some(output), + ).unwrap(); + assert_ne!(output_buf, [0u8; 32]); + api::return_value(uapi::ReturnFlags::empty(), &output_buf); +} diff --git a/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs b/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs index f176dae12b8de..e9f124de2eafe 100644 --- a/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs +++ b/substrate/frame/revive/fixtures/contracts/caller_is_origin_n.rs @@ -33,6 +33,15 @@ pub extern "C" fn call() { input!(n: u32, ); for _ in 0..n { - let _ = api::caller_is_origin(); + let _ = api::call( + uapi::CallFlags::READ_ONLY, + &uapi::SYSTEM_PRECOMPILE_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. + &[0u8; 32], // Value transferred to the contract. + &uapi::solidity_selector("callerIsOrigin()"), + None, + ).unwrap(); } } diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs deleted file mode 100644 index 70aedf35fea56..0000000000000 --- a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs +++ /dev/null @@ -1,66 +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. - -//! Call chain extension two times with the specified func_ids -//! It then calls itself once -#![no_std] -#![no_main] -include!("../panic_handler.rs"); - -use uapi::{input, 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!( - input, - func_id1: u32, - func_id2: u32, - stop_recurse: u8, - ); - - api::call_chain_extension(func_id1, input, None); - api::call_chain_extension(func_id2, input, None); - - if stop_recurse == 0 { - // Setup next call - input[0..4].copy_from_slice(&((3 << 16) | 2u32).to_le_bytes()); - input[4..8].copy_from_slice(&((3 << 16) | 3u32).to_le_bytes()); - input[8] = 1u8; - - // Read the contract address. - let mut addr = [0u8; 20]; - api::address(&mut addr); - - // call self - api::call( - uapi::CallFlags::ALLOW_REENTRY, - &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. - &[0u8; 32], // Value transferred to the contract. - input, - None, - ) - .unwrap(); - } -} diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs index 13698205eb20a..df86587eb6aa1 100644 --- a/substrate/frame/revive/fixtures/contracts/drain.rs +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -29,7 +29,24 @@ pub extern "C" fn deploy() {} #[polkavm_derive::polkavm_export] pub extern "C" fn call() { let balance = u64_output!(api::balance,); - let minimum_balance = u64_output!(api::minimum_balance,); + + let mut output_buf = [0u8; 32]; + let output = &mut &mut output_buf[..]; + let _ = api::call( + uapi::CallFlags::READ_ONLY, + &uapi::SYSTEM_PRECOMPILE_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. + &[0u8; 32], // Value transferred to the contract. + &uapi::solidity_selector("minimumBalance()"), + Some(output), + ).unwrap(); + assert_ne!(output_buf, [0u8; 32]); + + let mut u64_buf = [0u8; 8]; + u64_buf[..8].copy_from_slice(&output_buf[24..32]); + let minimum_balance = u64::from_be_bytes(u64_buf); // Make the transferred value exceed the balance by adding the minimum balance. let balance = balance + minimum_balance; diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension.rs b/substrate/frame/revive/fixtures/contracts/gas_price_n.rs similarity index 68% rename from substrate/frame/revive/fixtures/contracts/chain_extension.rs rename to substrate/frame/revive/fixtures/contracts/gas_price_n.rs index bd142b321c01f..51cfc3a6aea85 100644 --- a/substrate/frame/revive/fixtures/contracts/chain_extension.rs +++ b/substrate/frame/revive/fixtures/contracts/gas_price_n.rs @@ -15,7 +15,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Call chain extension by passing through input and output of this contract. +//! This fixture calls `gas_price` `n` times. + #![no_std] #![no_main] include!("../panic_handler.rs"); @@ -29,14 +30,9 @@ pub extern "C" fn deploy() {} #[no_mangle] #[polkavm_derive::polkavm_export] pub extern "C" fn call() { - input!(input, 8, func_id: u32,); - - // the chain extension passes through the input and returns it as output - let mut output_buffer = [0u8; 32]; - let output = &mut &mut output_buffer[0..input.len()]; - - let ret_id = api::call_chain_extension(func_id, input, Some(output)); - assert_eq!(ret_id, func_id); + input!(n: u32, ); - api::return_value(uapi::ReturnFlags::empty(), output); + for _ in 0..n { + let _ = api::gas_price(); + } } diff --git a/substrate/frame/revive/proc-macro/src/lib.rs b/substrate/frame/revive/proc-macro/src/lib.rs index 42b34a628476f..87f7c699f461d 100644 --- a/substrate/frame/revive/proc-macro/src/lib.rs +++ b/substrate/frame/revive/proc-macro/src/lib.rs @@ -15,7 +15,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Procedural macros used in the contracts module. +//! Procedural macros used in `pallet-revive`. //! //! Most likely you should use the [`#[define_env]`][`macro@define_env`] attribute macro which hides //! boilerplate of defining external environment for a polkavm module. diff --git a/substrate/frame/revive/src/benchmarking.rs b/substrate/frame/revive/src/benchmarking.rs index 2505aecd8abcb..691477539f181 100644 --- a/substrate/frame/revive/src/benchmarking.rs +++ b/substrate/frame/revive/src/benchmarking.rs @@ -24,7 +24,13 @@ use crate::{ exec::{Key, MomentOf, PrecompileExt}, limits, precompiles::{ - self, run::builtin as run_builtin_precompile, BenchmarkSystem, BuiltinPrecompile, ISystem, + self, + alloy::sol_types::{ + sol_data::{Bool, Bytes, FixedBytes, Uint}, + SolType, + }, + run::builtin as run_builtin_precompile, + BenchmarkSystem, BuiltinPrecompile, }, storage::WriteOutcome, vm::{ @@ -35,7 +41,7 @@ use crate::{ Pallet as Contracts, *, }; use alloc::{vec, vec::Vec}; -use alloy_core::sol_types::SolInterface; +use alloy_core::sol_types::{SolInterface, SolValue}; use codec::{Encode, MaxEncodedLen}; use frame_benchmarking::v2::*; use frame_support::{ @@ -46,7 +52,9 @@ use frame_support::{ weights::{Weight, WeightMeter}, }; use frame_system::RawOrigin; -use pallet_revive_uapi::{pack_hi_lo, CallFlags, ReturnErrorCode, StorageFlags}; +use pallet_revive_uapi::{ + pack_hi_lo, precompiles::system::ISystem, CallFlags, ReturnErrorCode, StorageFlags, +}; use revm::{ bytecode::{opcode::EXTCODECOPY, Bytecode}, interpreter::{ @@ -634,13 +642,14 @@ mod benchmarks { input_bytes, ); } - let data = result.unwrap().data; + let raw_data = result.unwrap().data; + let data = Bytes::abi_decode(&raw_data).expect("decoding failed"); assert_ne!( - data.as_slice()[20..32], + data.0.as_ref()[20..32], [0xEE; 12], "fallback suffix found where none should be" ); - assert_eq!(T::AccountId::decode(&mut data.as_slice()), Ok(account_id),); + assert_eq!(T::AccountId::decode(&mut data.as_ref()), Ok(account_id),); } #[benchmark(pov_mode = Measured)] @@ -663,20 +672,27 @@ mod benchmarks { } #[benchmark(pov_mode = Measured)] - fn seal_own_code_hash() { - let len = ::max_encoded_len() as u32; - build_runtime!(runtime, contract, memory: [vec![0u8; len as _], ]); + fn own_code_hash() { + let input_bytes = + ISystem::ISystemCalls::ownCodeHash(ISystem::ownCodeHashCall {}).abi_encode(); + let mut call_setup = CallSetup::::default(); + let contract_acc = call_setup.contract().account_id.clone(); + let caller = call_setup.contract().address; + call_setup.set_origin(Origin::from_account_id(contract_acc)); + let (mut ext, _) = call_setup.ext(); + let result; #[block] { - result = runtime.bench_own_code_hash(memory.as_mut_slice(), 0); + result = run_builtin_precompile( + &mut ext, + H160(BenchmarkSystem::::MATCHER.base_address()).as_fixed_bytes(), + input_bytes, + ); } - - assert_ok!(result); - assert_eq!( - ::decode(&mut &memory[..]).unwrap(), - contract.info().unwrap().code_hash - ); + assert!(result.is_ok()); + let caller_code_hash = ext.code_hash(&caller); + assert_eq!(caller_code_hash.0.to_vec(), result.unwrap().data); } #[benchmark(pov_mode = Measured)] @@ -694,30 +710,48 @@ mod benchmarks { } #[benchmark(pov_mode = Measured)] - fn seal_caller_is_origin() { - build_runtime!(runtime, memory: []); + fn caller_is_origin() { + let input_bytes = + ISystem::ISystemCalls::callerIsOrigin(ISystem::callerIsOriginCall {}).abi_encode(); + + let mut call_setup = CallSetup::::default(); + let (mut ext, _) = call_setup.ext(); let result; #[block] { - result = runtime.bench_caller_is_origin(memory.as_mut_slice()); + result = run_builtin_precompile( + &mut ext, + H160(BenchmarkSystem::::MATCHER.base_address()).as_fixed_bytes(), + input_bytes, + ); } - assert_eq!(result.unwrap(), 1u32); + let raw_data = result.unwrap().data; + let is_origin = Bool::abi_decode(&raw_data[..]).expect("decoding failed"); + assert!(is_origin); } #[benchmark(pov_mode = Measured)] - fn seal_caller_is_root() { + fn caller_is_root() { + let input_bytes = + ISystem::ISystemCalls::callerIsRoot(ISystem::callerIsRootCall {}).abi_encode(); + let mut setup = CallSetup::::default(); setup.set_origin(Origin::Root); let (mut ext, _) = setup.ext(); - let mut runtime = pvm::Runtime::new(&mut ext, vec![]); let result; #[block] { - result = runtime.bench_caller_is_root([0u8; 0].as_mut_slice()); + result = run_builtin_precompile( + &mut ext, + H160(BenchmarkSystem::::MATCHER.base_address()).as_fixed_bytes(), + input_bytes, + ); } - assert_eq!(result.unwrap(), 1u32); + let raw_data = result.unwrap().data; + let is_root = Bool::abi_decode(&raw_data).expect("decoding failed"); + assert!(is_root); } #[benchmark(pov_mode = Measured)] @@ -735,22 +769,31 @@ mod benchmarks { } #[benchmark(pov_mode = Measured)] - fn seal_weight_left() { - // use correct max_encoded_len when new version of parity-scale-codec is released - let len = 18u32; - assert!(::max_encoded_len() as u32 != len); - build_runtime!(runtime, memory: [32u32.to_le_bytes(), vec![0u8; len as _], ]); + fn weight_left() { + let input_bytes = + ISystem::ISystemCalls::weightLeft(ISystem::weightLeftCall {}).abi_encode(); + + let mut call_setup = CallSetup::::default(); + let (mut ext, _) = call_setup.ext(); + let weight_left_before = ext.gas_meter().gas_left(); let result; #[block] { - result = runtime.bench_weight_left(memory.as_mut_slice(), 4, 0); + result = run_builtin_precompile( + &mut ext, + H160(BenchmarkSystem::::MATCHER.base_address()).as_fixed_bytes(), + input_bytes, + ); } - assert_ok!(result); - assert_eq!( - ::decode(&mut &memory[4..]).unwrap(), - runtime.ext().gas_meter().gas_left() - ); + let weight_left_after = ext.gas_meter().gas_left(); + assert_ne!(weight_left_after.ref_time(), 0); + assert!(weight_left_before.ref_time() > weight_left_after.ref_time()); + + let raw_data = result.unwrap().data; + type MyTy = (Uint<64>, Uint<64>); + let foo = MyTy::abi_decode(&raw_data[..]).unwrap(); + assert_eq!(weight_left_after.ref_time(), foo.0); } #[benchmark(pov_mode = Measured)] @@ -874,15 +917,32 @@ mod benchmarks { } #[benchmark(pov_mode = Measured)] - fn seal_minimum_balance() { - build_runtime!(runtime, memory: [[0u8;32], ]); + fn minimum_balance() { + let input_bytes = + ISystem::ISystemCalls::minimumBalance(ISystem::minimumBalanceCall {}).abi_encode(); + + let mut call_setup = CallSetup::::default(); + let (mut ext, _) = call_setup.ext(); + let result; #[block] { - result = runtime.bench_minimum_balance(memory.as_mut_slice(), 0); + result = run_builtin_precompile( + &mut ext, + H160(BenchmarkSystem::::MATCHER.base_address()).as_fixed_bytes(), + input_bytes, + ); } - assert_ok!(result); - assert_eq!(U256::from_little_endian(&memory[..]), runtime.ext().minimum_balance()); + let min: U256 = crate::Pallet::::convert_native_to_evm(T::Currency::minimum_balance()); + let min = + crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&min.to_big_endian()) + .unwrap(); + + let raw_data = result.unwrap().data; + let returned_min = + crate::precompiles::alloy::primitives::aliases::U256::abi_decode(&raw_data) + .expect("decoding failed"); + assert_eq!(returned_min, min); } #[benchmark(pov_mode = Measured)] @@ -2065,7 +2125,13 @@ mod benchmarks { input_bytes, ); } - assert_eq!(sp_io::hashing::blake2_256(&input).to_vec(), result.unwrap().data); + let truth: [u8; 32] = sp_io::hashing::blake2_256(&input); + let truth = FixedBytes::<32>::abi_encode(&truth); + let truth = FixedBytes::<32>::abi_decode(&truth[..]).expect("decoding failed"); + + let raw_data = result.unwrap().data; + let ret_hash = FixedBytes::<32>::abi_decode(&raw_data[..]).expect("decoding failed"); + assert_eq!(truth, ret_hash); } // `n`: Input to hash in bytes @@ -2089,7 +2155,13 @@ mod benchmarks { input_bytes, ); } - assert_eq!(sp_io::hashing::blake2_128(&input).to_vec(), result.unwrap().data); + let truth: [u8; 16] = sp_io::hashing::blake2_128(&input); + let truth = FixedBytes::<16>::abi_encode(&truth); + let truth = FixedBytes::<16>::abi_decode(&truth[..]).expect("decoding failed"); + + let raw_data = result.unwrap().data; + let ret_hash = FixedBytes::<16>::abi_decode(&raw_data[..]).expect("decoding failed"); + assert_eq!(truth, ret_hash); } // `n`: Message input length to verify in bytes. diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index a74f8ecc695f1..f44ca68f40573 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -155,6 +155,7 @@ impl Origin { pub fn from_account_id(account_id: T::AccountId) -> Self { Origin::Signed(account_id) } + /// Creates a new Origin from a `RuntimeOrigin`. pub fn from_runtime_origin(o: OriginFor) -> Result { match o.into() { @@ -163,6 +164,7 @@ impl Origin { _ => Err(BadOrigin.into()), } } + /// Returns the AccountId of a Signed Origin or an error if the origin is Root. pub fn account_id(&self) -> Result<&T::AccountId, DispatchError> { match self { @@ -206,6 +208,7 @@ pub trait Ext: PrecompileWithInfoExt { fn terminate(&mut self, beneficiary: &H160) -> Result; /// Returns the code hash of the contract being executed. + #[allow(dead_code)] fn own_code_hash(&mut self) -> &H256; /// Sets new code hash and immutable data for an existing contract. @@ -288,8 +291,6 @@ pub trait PrecompileExt: sealing::Sealed { } /// 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, @@ -325,9 +326,15 @@ pub trait PrecompileExt: sealing::Sealed { /// Returns the caller. fn caller(&self) -> Origin; + /// Returns the caller of the caller. + fn caller_of_caller(&self) -> Origin; + /// Return the origin of the whole call stack. fn origin(&self) -> &Origin; + /// Returns the account id for the given `address`. + fn to_account_id(&self, address: &H160) -> AccountIdOf; + /// Returns the code hash of the contract for the given `address`. /// If not a contract but account exists then `keccak_256([])` is returned, otherwise `zero`. fn code_hash(&self, address: &H160) -> H256; @@ -336,10 +343,10 @@ pub trait PrecompileExt: sealing::Sealed { fn code_size(&self, address: &H160) -> u64; /// Check if the caller of the current contract is the origin of the whole call stack. - fn caller_is_origin(&self) -> bool; + fn caller_is_origin(&self, use_caller_of_caller: bool) -> bool; /// Check if the caller is origin, and this origin is root. - fn caller_is_root(&self) -> bool; + fn caller_is_root(&self, use_caller_of_caller: bool) -> bool; /// Returns a reference to the account id of the current contract. fn account_id(&self) -> &AccountIdOf; @@ -1998,10 +2005,27 @@ where } } + fn caller_of_caller(&self) -> Origin { + // fetch top frame of top frame + let caller_of_caller_frame = match self.frames().nth(2) { + None => return self.origin.clone(), + Some(frame) => frame, + }; + if let Some(DelegateInfo { caller, .. }) = &caller_of_caller_frame.delegate { + caller.clone() + } else { + Origin::from_account_id(caller_of_caller_frame.account_id.clone()) + } + } + fn origin(&self) -> &Origin { &self.origin } + fn to_account_id(&self, address: &H160) -> T::AccountId { + T::AddressMapper::to_account_id(address) + } + fn code_hash(&self, address: &H160) -> H256 { if let Some(code) = >::code(address.as_fixed_bytes()) { return sp_io::hashing::keccak_256(code).into() @@ -2028,13 +2052,14 @@ where .unwrap_or_default() } - fn caller_is_origin(&self) -> bool { - self.origin == self.caller() + fn caller_is_origin(&self, use_caller_of_caller: bool) -> bool { + let caller = if use_caller_of_caller { self.caller_of_caller() } else { self.caller() }; + self.origin == caller } - fn caller_is_root(&self) -> bool { + fn caller_is_root(&self, use_caller_of_caller: bool) -> bool { // if the caller isn't origin, then it can't be root. - self.caller_is_origin() && self.origin == Origin::Root + self.caller_is_origin(use_caller_of_caller) && self.origin == Origin::Root } fn balance(&self) -> U256 { diff --git a/substrate/frame/revive/src/exec/mock_ext.rs b/substrate/frame/revive/src/exec/mock_ext.rs index 399fd534edd96..91a3e1c1c43f5 100644 --- a/substrate/frame/revive/src/exec/mock_ext.rs +++ b/substrate/frame/revive/src/exec/mock_ext.rs @@ -80,6 +80,10 @@ impl PrecompileExt for MockExt { panic!("MockExt::caller") } + fn caller_of_caller(&self) -> Origin { + panic!("MockExt::caller_of_caller") + } + fn origin(&self) -> &Origin { panic!("MockExt::origin") } @@ -92,11 +96,11 @@ impl PrecompileExt for MockExt { panic!("MockExt::code_size") } - fn caller_is_origin(&self) -> bool { + fn caller_is_origin(&self, _use_caller_of_caller: bool) -> bool { panic!("MockExt::caller_is_origin") } - fn caller_is_root(&self) -> bool { + fn caller_is_root(&self, _use_caller_of_caller: bool) -> bool { panic!("MockExt::caller_is_root") } @@ -205,6 +209,10 @@ impl PrecompileExt for MockExt { fn copy_code_slice(&mut self, _buf: &mut [u8], _address: &H160, _code_offset: usize) { panic!("MockExt::copy_code_slice") } + + fn to_account_id(&self, _address: &H160) -> AccountIdOf { + panic!("MockExt::to_account_id") + } } impl PrecompileWithInfoExt for MockExt { diff --git a/substrate/frame/revive/src/exec/tests.rs b/substrate/frame/revive/src/exec/tests.rs index 407ad5e7fda9b..3be69bc619cc6 100644 --- a/substrate/frame/revive/src/exec/tests.rs +++ b/substrate/frame/revive/src/exec/tests.rs @@ -800,6 +800,40 @@ fn origin_returns_proper_values() { assert_eq!(WitnessedCallerCharlie::get(), Some(ALICE_ADDR)); } +#[test] +fn to_account_id_returns_proper_values() { + let bob_code_hash = MockLoader::insert(Call, |ctx, _| { + let alice_account_id = ::AddressMapper::to_account_id(&ALICE_ADDR); + assert_eq!(ctx.ext.to_account_id(&ALICE_ADDR), alice_account_id); + + const UNMAPPED_ADDR: H160 = H160([99u8; 20]); + let mut unmapped_fallback_account_id = [0xEE; 32]; + unmapped_fallback_account_id[..20].copy_from_slice(UNMAPPED_ADDR.as_bytes()); + assert_eq!( + ctx.ext.to_account_id(&UNMAPPED_ADDR), + AccountId32::new(unmapped_fallback_account_id) + ); + + exec_success() + }); + + ExtBuilder::default().build().execute_with(|| { + place_contract(&BOB, bob_code_hash); + let origin = Origin::from_account_id(ALICE); + let mut storage_meter = storage::meter::Meter::new(0); + let result = MockStack::run_call( + origin, + BOB_ADDR, + &mut GasMeter::::new(GAS_LIMIT), + &mut storage_meter, + U256::zero(), + vec![0], + false, + ); + assert_matches!(result, Ok(_)); + }); +} + #[test] fn code_hash_returns_proper_values() { let bob_code_hash = MockLoader::insert(Call, |ctx, _| { @@ -867,13 +901,13 @@ fn own_code_hash_returns_proper_values() { fn caller_is_origin_returns_proper_values() { let code_charlie = MockLoader::insert(Call, |ctx, _| { // BOB is not the origin of the stack call - assert!(!ctx.ext.caller_is_origin()); + assert!(!ctx.ext.caller_is_origin(false)); exec_success() }); let code_bob = MockLoader::insert(Call, |ctx, _| { // ALICE is the origin of the call stack - assert!(ctx.ext.caller_is_origin()); + assert!(ctx.ext.caller_is_origin(false)); // BOB calls CHARLIE ctx.ext .call(Weight::zero(), U256::zero(), &CHARLIE_ADDR, U256::zero(), vec![], true, false) @@ -903,7 +937,7 @@ fn caller_is_origin_returns_proper_values() { fn root_caller_succeeds() { let code_bob = MockLoader::insert(Call, |ctx, _| { // root is the origin of the call stack. - assert!(ctx.ext.caller_is_root()); + assert!(ctx.ext.caller_is_root(false)); exec_success() }); @@ -929,7 +963,7 @@ fn root_caller_succeeds() { fn root_caller_does_not_succeed_when_value_not_zero() { let code_bob = MockLoader::insert(Call, |ctx, _| { // root is the origin of the call stack. - assert!(ctx.ext.caller_is_root()); + assert!(ctx.ext.caller_is_root(false)); exec_success() }); @@ -955,13 +989,13 @@ fn root_caller_does_not_succeed_when_value_not_zero() { fn root_caller_succeeds_with_consecutive_calls() { let code_charlie = MockLoader::insert(Call, |ctx, _| { // BOB is not root, even though the origin is root. - assert!(!ctx.ext.caller_is_root()); + assert!(!ctx.ext.caller_is_root(false)); exec_success() }); let code_bob = MockLoader::insert(Call, |ctx, _| { // root is the origin of the call stack. - assert!(ctx.ext.caller_is_root()); + assert!(ctx.ext.caller_is_root(false)); // BOB calls CHARLIE. ctx.ext .call(Weight::zero(), U256::zero(), &CHARLIE_ADDR, U256::zero(), vec![], true, false) diff --git a/substrate/frame/revive/src/precompiles.rs b/substrate/frame/revive/src/precompiles.rs index b827caa27c09c..83b8b760e16a1 100644 --- a/substrate/frame/revive/src/precompiles.rs +++ b/substrate/frame/revive/src/precompiles.rs @@ -41,7 +41,7 @@ pub use sp_core::{H160, H256, U256}; use crate::{ exec::ExecResult, precompiles::builtin::Builtin, primitives::ExecReturnValue, Config, - Error as CrateError, + Error as CrateError, LOG_TARGET, }; use alloc::vec::Vec; use alloy::sol_types::{Panic, PanicKind, Revert, SolError, SolInterface}; @@ -51,7 +51,7 @@ use sp_runtime::DispatchError; #[cfg(feature = "runtime-benchmarks")] pub(crate) use builtin::{ - IBenchmarking, ISystem, NoInfo as BenchmarkNoInfo, System as BenchmarkSystem, + IBenchmarking, NoInfo as BenchmarkNoInfo, System as BenchmarkSystem, WithInfo as BenchmarkWithInfo, }; @@ -370,8 +370,11 @@ impl PrimitivePrecompile for P { input: Vec, env: &mut impl Ext, ) -> Result, Error> { - let call = ::Interface::abi_decode_validate(&input) - .map_err(|_| Error::Panic(PanicKind::ResourceError))?; + let call = + ::Interface::abi_decode_validate(&input).map_err(|err| { + log::debug!(target: LOG_TARGET, "`abi_decode_validate` for pre-compile failed: {err:?}"); + Error::Panic(PanicKind::ResourceError) + })?; ::call(address, &call, env) } @@ -381,7 +384,10 @@ impl PrimitivePrecompile for P { env: &mut impl ExtWithInfo, ) -> Result, Error> { let call = ::Interface::abi_decode_validate(&input) - .map_err(|_| Error::Panic(PanicKind::ResourceError))?; + .map_err(|err| { + log::debug!(target: LOG_TARGET, "`abi_decode_validate` for pre-compile (with info) failed: {err:?}"); + Error::Panic(PanicKind::ResourceError) + })?; ::call_with_info(address, &call, env) } } diff --git a/substrate/frame/revive/src/precompiles/builtin.rs b/substrate/frame/revive/src/precompiles/builtin.rs index 34fab30802096..abec334768e8b 100644 --- a/substrate/frame/revive/src/precompiles/builtin.rs +++ b/substrate/frame/revive/src/precompiles/builtin.rs @@ -37,7 +37,7 @@ use crate::{ #[cfg(feature = "runtime-benchmarks")] pub use self::{ benchmarking::{IBenchmarking, NoInfo, WithInfo}, - system::{ISystem, System}, + system::System, }; #[cfg(not(feature = "runtime-benchmarks"))] diff --git a/substrate/frame/revive/src/precompiles/builtin/system.rs b/substrate/frame/revive/src/precompiles/builtin/system.rs index aa8b537110461..9fce7a54a849d 100644 --- a/substrate/frame/revive/src/precompiles/builtin/system.rs +++ b/substrate/frame/revive/src/precompiles/builtin/system.rs @@ -16,36 +16,20 @@ // limitations under the License. use crate::{ + address::AddressMapper, precompiles::{BuiltinAddressMatcher, BuiltinPrecompile, Error, Ext}, vm::RuntimeCosts, - Config, + Config, H160, }; use alloc::vec::Vec; -use alloy_core::sol; +use alloy_core::sol_types::SolValue; +use codec::Encode; use core::{marker::PhantomData, num::NonZero}; +use pallet_revive_uapi::precompiles::system::ISystem; use sp_core::hexdisplay::AsBytesRef; pub struct System(PhantomData); -sol! { - interface ISystem { - /// Computes the BLAKE2 256-bit hash on the given input. - function hashBlake256(bytes memory input) external pure returns (bytes32 digest); - /// Computes the BLAKE2 128-bit hash on the given input. - function hashBlake128(bytes memory input) external pure returns (bytes32 digest); - /// Retrieve the account id for a specified `H160` address. - /// - /// Calling this function on a native `H160` chain (`type AccountId = H160`) - /// does not make sense, as it would just return the `address` that it was - /// called with. - /// - /// # Note - /// - /// If no mapping exists for `addr`, the fallback account id will be returned. - function toAccountId(address input) external view returns (bytes memory account_id); - } -} - impl BuiltinPrecompile for System { type T = T; type Interface = ISystem::ISystemCalls; @@ -63,20 +47,46 @@ impl BuiltinPrecompile for System { ISystemCalls::hashBlake256(ISystem::hashBlake256Call { input }) => { env.gas_meter_mut().charge(RuntimeCosts::HashBlake256(input.len() as u32))?; let output = sp_io::hashing::blake2_256(input.as_bytes_ref()); - Ok(output.to_vec()) + Ok(output.abi_encode()) }, ISystemCalls::hashBlake128(ISystem::hashBlake128Call { input }) => { env.gas_meter_mut().charge(RuntimeCosts::HashBlake128(input.len() as u32))?; let output = sp_io::hashing::blake2_128(input.as_bytes_ref()); - Ok(output.to_vec()) + Ok(output.abi_encode()) }, ISystemCalls::toAccountId(ISystem::toAccountIdCall { input }) => { - use crate::address::AddressMapper; - use codec::Encode; env.gas_meter_mut().charge(RuntimeCosts::ToAccountId)?; - let account_id = - T::AddressMapper::to_account_id(&crate::H160::from_slice(input.as_slice())); - Ok(account_id.encode()) + let account_id = env.to_account_id(&H160::from_slice(input.as_slice())); + Ok(account_id.encode().abi_encode()) + }, + ISystemCalls::callerIsOrigin(ISystem::callerIsOriginCall {}) => { + env.gas_meter_mut().charge(RuntimeCosts::CallerIsOrigin)?; + let is_origin = env.caller_is_origin(true); + Ok(is_origin.abi_encode()) + }, + ISystemCalls::callerIsRoot(ISystem::callerIsRootCall {}) => { + env.gas_meter_mut().charge(RuntimeCosts::CallerIsRoot)?; + let is_root = env.caller_is_root(true); + Ok(is_root.abi_encode()) + }, + ISystemCalls::ownCodeHash(ISystem::ownCodeHashCall {}) => { + env.gas_meter_mut().charge(RuntimeCosts::OwnCodeHash)?; + let caller = env.caller(); + let addr = T::AddressMapper::to_address(caller.account_id()?); + let output = env.code_hash(&addr.into()).0.abi_encode(); + Ok(output) + }, + ISystemCalls::minimumBalance(ISystem::minimumBalanceCall {}) => { + env.gas_meter_mut().charge(RuntimeCosts::MinimumBalance)?; + let minimum_balance = env.minimum_balance(); + Ok(minimum_balance.to_big_endian().abi_encode()) + }, + ISystemCalls::weightLeft(ISystem::weightLeftCall {}) => { + env.gas_meter_mut().charge(RuntimeCosts::WeightLeft)?; + let ref_time = env.gas_meter().gas_left().ref_time(); + let proof_size = env.gas_meter().gas_left().proof_size(); + let res = (ref_time, proof_size); + Ok(res.abi_encode()) }, } } @@ -84,14 +94,17 @@ impl BuiltinPrecompile for System { #[cfg(test)] mod tests { - use super::{ISystem, *}; + use super::*; use crate::{ address::AddressMapper, call_builder::{caller_funding, CallSetup}, pallet, - precompiles::{tests::run_test_vectors, BuiltinPrecompile}, + precompiles::{ + alloy::sol_types::{sol_data::Bytes, SolType}, + tests::run_test_vectors, + BuiltinPrecompile, + }, tests::{ExtBuilder, Test}, - H160, }; use codec::Decode; use frame_support::traits::fungible::Mutate; @@ -115,13 +128,15 @@ mod tests { let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall { input: unmapped_address.0.into(), }); - let expected_fallback_account_id = + let raw_data = >::call(&>::MATCHER.base_address(), &input, &mut ext) .unwrap(); // then + let expected_fallback_account_id = + Bytes::abi_decode(&raw_data).expect("decoding failed"); assert_eq!( - expected_fallback_account_id[20..32], + expected_fallback_account_id.0.as_ref()[20..32], [0xEE; 12], "no fallback suffix found where one should be" ); @@ -146,18 +161,19 @@ mod tests { let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall { input: mapped_address.0.into(), }); - let data = + let raw_data = >::call(&>::MATCHER.base_address(), &input, &mut ext) .unwrap(); // then + let data = Bytes::abi_decode(&raw_data).expect("decoding failed"); assert_ne!( - data.as_slice()[20..32], + data.0.as_ref()[20..32], [0xEE; 12], "fallback suffix found where none should be" ); assert_eq!( - ::AccountId::decode(&mut data.as_slice()), + ::AccountId::decode(&mut data.as_ref()), Ok(EVE), ); }) diff --git a/substrate/frame/revive/src/precompiles/builtin/testdata/900-to_account_id.json b/substrate/frame/revive/src/precompiles/builtin/testdata/900-to_account_id.json index b88909e923b33..a87e2d60f146a 100644 --- a/substrate/frame/revive/src/precompiles/builtin/testdata/900-to_account_id.json +++ b/substrate/frame/revive/src/precompiles/builtin/testdata/900-to_account_id.json @@ -1,7 +1,7 @@ [ { "Input": "cf5231cc00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000", - "Expected": "0000000000000000000000000000000000000020eeeeeeeeeeeeeeeeeeeeeeee", + "Expected": "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000020eeeeeeeeeeeeeeeeeeeeeeee", "Name": "vector 1", "Gas": 8000000, "NoBenchmark": false diff --git a/substrate/frame/revive/src/tests/pvm.rs b/substrate/frame/revive/src/tests/pvm.rs index d25060322699a..8108956d82203 100644 --- a/substrate/frame/revive/src/tests/pvm.rs +++ b/substrate/frame/revive/src/tests/pvm.rs @@ -27,6 +27,10 @@ use crate::{ evm::{runtime::GAS_PRICE, CallTrace, CallTracer, CallType, GenericTransaction}, exec::Key, limits, + precompiles::alloy::sol_types::{ + sol_data::{Bool, FixedBytes}, + SolType, + }, storage::DeletionQueueManager, test_utils::builder::Contract, tests::{ @@ -471,7 +475,7 @@ fn run_out_of_fuel_host() { #[test] fn gas_syncs_work() { - let (code, _code_hash) = compile_module("caller_is_origin_n").unwrap(); + let (code, _code_hash) = compile_module("gas_price_n").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { let _ = ::Currency::set_balance(&ALICE, 1_000_000); let contract = builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); @@ -483,7 +487,7 @@ fn gas_syncs_work() { let result = builder::bare_call(contract.addr).data(1u32.encode()).build(); assert_ok!(result.result); let gas_consumed_once = result.gas_consumed.ref_time(); - let host_consumed_once = ::WeightInfo::seal_caller_is_origin().ref_time(); + let host_consumed_once = ::WeightInfo::seal_gas_price().ref_time(); let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop; let result = builder::bare_call(contract.addr).data(2u32.encode()).build(); @@ -3469,6 +3473,74 @@ fn call_diverging_out_len_works() { }); } +#[test] +fn call_own_code_hash_works() { + let (code, code_hash) = compile_module("call_own_code_hash").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create the contract: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + let ret = builder::bare_call(addr).build_and_unwrap_result(); + let ret_hash = FixedBytes::<32>::abi_decode(&ret.data).unwrap(); + assert_eq!(ret_hash, code_hash.0); + }); +} + +#[test] +fn call_caller_is_root() { + let (code, _) = compile_module("call_caller_is_root").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create the contract: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + let ret = builder::bare_call(addr).origin(RuntimeOrigin::root()).build_and_unwrap_result(); + let is_root = Bool::abi_decode(&ret.data).expect("decoding failed"); + assert!(is_root); + }); +} + +#[test] +fn call_caller_is_root_from_non_root() { + let (code, _) = compile_module("call_caller_is_root").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create the contract: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + let ret = builder::bare_call(addr).build_and_unwrap_result(); + let is_root = Bool::abi_decode(&ret.data).expect("decoding failed"); + assert!(!is_root); + }); +} + +#[test] +fn call_caller_is_origin() { + let (code, _) = compile_module("call_caller_is_origin").unwrap(); + + ExtBuilder::default().existential_deposit(100).build().execute_with(|| { + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + + // Create the contract: Constructor does nothing + let Contract { addr, .. } = + builder::bare_instantiate(Code::Upload(code)).build_and_unwrap_contract(); + + let ret = builder::bare_call(addr).build_and_unwrap_result(); + let is_origin = Bool::abi_decode(&ret.data).expect("decoding failed"); + assert!(is_origin); + }); +} + #[test] fn chain_id_works() { let (code, _) = compile_module("chain_id").unwrap(); diff --git a/substrate/frame/revive/src/vm/pvm/env.rs b/substrate/frame/revive/src/vm/pvm/env.rs index fe0572126856f..3ed466041a18a 100644 --- a/substrate/frame/revive/src/vm/pvm/env.rs +++ b/substrate/frame/revive/src/vm/pvm/env.rs @@ -861,20 +861,6 @@ pub mod env { Ok(self.ext.gas_meter().gas_left().ref_time()) } - /// Checks whether the caller of the current contract is the origin of the whole call stack. - /// See [`pallet_revive_uapi::HostFn::caller_is_origin`]. - fn caller_is_origin(&mut self, _memory: &mut M) -> Result { - self.charge_gas(RuntimeCosts::CallerIsOrigin)?; - Ok(self.ext.caller_is_origin() as u32) - } - - /// Checks whether the caller of the current contract is root. - /// See [`pallet_revive_uapi::HostFn::caller_is_root`]. - fn caller_is_root(&mut self, _memory: &mut M) -> Result { - self.charge_gas(RuntimeCosts::CallerIsRoot)?; - Ok(self.ext.caller_is_root() as u32) - } - /// Clear the value at the given key in the contract storage. /// See [`pallet_revive_uapi::HostFn::clear_storage`] #[mutating] @@ -921,33 +907,6 @@ pub mod env { } } - /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// See [`pallet_revive_uapi::HostFn::minimum_balance`]. - fn minimum_balance(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { - self.charge_gas(RuntimeCosts::MinimumBalance)?; - Ok(self.write_fixed_sandbox_output( - memory, - out_ptr, - &self.ext.minimum_balance().to_little_endian(), - false, - already_charged, - )?) - } - - /// Retrieve the code hash of the currently executing contract. - /// See [`pallet_revive_uapi::HostFn::own_code_hash`]. - fn own_code_hash(&mut self, memory: &mut M, out_ptr: u32) -> Result<(), TrapReason> { - self.charge_gas(RuntimeCosts::OwnCodeHash)?; - let code_hash = *self.ext.own_code_hash(); - Ok(self.write_fixed_sandbox_output( - memory, - out_ptr, - code_hash.as_bytes(), - false, - already_charged, - )?) - } - /// Replace the contract code at the specified address with new code. /// See [`pallet_revive_uapi::HostFn::set_code_hash`]. /// @@ -1016,24 +975,4 @@ pub mod env { } Err(TrapReason::Termination) } - - /// Stores the amount of weight left into the supplied buffer. - /// See [`pallet_revive_uapi::HostFn::weight_left`]. - fn weight_left( - &mut self, - memory: &mut M, - out_ptr: u32, - out_len_ptr: u32, - ) -> Result<(), TrapReason> { - self.charge_gas(RuntimeCosts::WeightLeft)?; - let gas_left = &self.ext.gas_meter().gas_left().encode(); - Ok(self.write_sandbox_output( - memory, - out_ptr, - out_len_ptr, - gas_left, - false, - already_charged, - )?) - } } diff --git a/substrate/frame/revive/src/vm/runtime_costs.rs b/substrate/frame/revive/src/vm/runtime_costs.rs index 18e9b622f82fc..f96c9467e00b0 100644 --- a/substrate/frame/revive/src/vm/runtime_costs.rs +++ b/substrate/frame/revive/src/vm/runtime_costs.rs @@ -50,25 +50,25 @@ pub enum RuntimeCosts { CallDataSize, /// Weight of calling `seal_return_data_size`. ReturnDataSize, - /// Weight of calling `to_account_id`. + /// Weight of calling `toAccountId` on the `System` pre-compile. ToAccountId, /// Weight of calling `seal_origin`. Origin, /// Weight of calling `seal_code_hash`. CodeHash, - /// Weight of calling `seal_own_code_hash`. + /// Weight of calling `ownCodeHash` on the `System` pre-compile. OwnCodeHash, /// Weight of calling `seal_code_size`. CodeSize, - /// Weight of calling `seal_caller_is_origin`. + /// Weight of calling `callerIsOrigin` on the `System` pre-compile. CallerIsOrigin, - /// Weight of calling `caller_is_root`. + /// Weight of calling `callerIsRoot` on the `System` pre-compile. CallerIsRoot, /// Weight of calling `seal_address`. Address, /// Weight of calling `seal_ref_time_left`. RefTimeLeft, - /// Weight of calling `seal_weight_left`. + /// Weight of calling `weightLeft` on the `System` pre-compile. WeightLeft, /// Weight of calling `seal_balance`. Balance, @@ -76,7 +76,7 @@ pub enum RuntimeCosts { BalanceOf, /// Weight of calling `seal_value_transferred`. ValueTransferred, - /// Weight of calling `seal_minimum_balance`. + /// Weight of calling `minimumBalance` on the `System` pre-compile. MinimumBalance, /// Weight of calling `seal_block_number`. BlockNumber, @@ -239,16 +239,16 @@ impl Token for RuntimeCosts { ToAccountId => T::WeightInfo::to_account_id(), CodeHash => T::WeightInfo::seal_code_hash(), CodeSize => T::WeightInfo::seal_code_size(), - OwnCodeHash => T::WeightInfo::seal_own_code_hash(), - CallerIsOrigin => T::WeightInfo::seal_caller_is_origin(), - CallerIsRoot => T::WeightInfo::seal_caller_is_root(), + OwnCodeHash => T::WeightInfo::own_code_hash(), + CallerIsOrigin => T::WeightInfo::caller_is_origin(), + CallerIsRoot => T::WeightInfo::caller_is_root(), Address => T::WeightInfo::seal_address(), RefTimeLeft => T::WeightInfo::seal_ref_time_left(), - WeightLeft => T::WeightInfo::seal_weight_left(), + WeightLeft => T::WeightInfo::weight_left(), Balance => T::WeightInfo::seal_balance(), BalanceOf => T::WeightInfo::seal_balance_of(), ValueTransferred => T::WeightInfo::seal_value_transferred(), - MinimumBalance => T::WeightInfo::seal_minimum_balance(), + MinimumBalance => T::WeightInfo::minimum_balance(), BlockNumber => T::WeightInfo::seal_block_number(), BlockHash => T::WeightInfo::seal_block_hash(), BlockAuthor => T::WeightInfo::seal_block_author(), diff --git a/substrate/frame/revive/src/weights.rs b/substrate/frame/revive/src/weights.rs index 8ae133fb2b7be..1f1cdc3095691 100644 --- a/substrate/frame/revive/src/weights.rs +++ b/substrate/frame/revive/src/weights.rs @@ -92,19 +92,19 @@ pub trait WeightInfo { fn seal_origin() -> Weight; fn to_account_id() -> Weight; fn seal_code_hash() -> Weight; - fn seal_own_code_hash() -> Weight; + fn own_code_hash() -> Weight; fn seal_code_size() -> Weight; - fn seal_caller_is_origin() -> Weight; - fn seal_caller_is_root() -> Weight; + fn caller_is_origin() -> Weight; + fn caller_is_root() -> Weight; fn seal_address() -> Weight; - fn seal_weight_left() -> Weight; + fn weight_left() -> Weight; fn seal_ref_time_left() -> Weight; fn seal_balance() -> Weight; fn seal_balance_of() -> Weight; fn seal_get_immutable_data(n: u32, ) -> Weight; fn seal_set_immutable_data(n: u32, ) -> Weight; fn seal_value_transferred() -> Weight; - fn seal_minimum_balance() -> Weight; + fn minimum_balance() -> Weight; fn seal_return_data_size() -> Weight; fn seal_call_data_size() -> Weight; fn seal_gas_limit() -> Weight; @@ -537,7 +537,7 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(9_975_000, 3868) .saturating_add(T::DbWeight::get().reads(1_u64)) } - fn seal_own_code_hash() -> Weight { + fn own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -556,14 +556,14 @@ impl WeightInfo for SubstrateWeight { Weight::from_parts(13_527_000, 3940) .saturating_add(T::DbWeight::get().reads(2_u64)) } - fn seal_caller_is_origin() -> Weight { + fn caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 337_000 picoseconds. Weight::from_parts(375_000, 0) } - fn seal_caller_is_root() -> Weight { + fn caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -577,7 +577,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 308_000 picoseconds. Weight::from_parts(352_000, 0) } - fn seal_weight_left() -> Weight { + fn weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -646,7 +646,7 @@ impl WeightInfo for SubstrateWeight { // Minimum execution time: 260_000 picoseconds. Weight::from_parts(293_000, 0) } - fn seal_minimum_balance() -> Weight { + fn minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -1690,7 +1690,7 @@ impl WeightInfo for () { Weight::from_parts(9_975_000, 3868) .saturating_add(RocksDbWeight::get().reads(1_u64)) } - fn seal_own_code_hash() -> Weight { + fn own_code_hash() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -1709,14 +1709,14 @@ impl WeightInfo for () { Weight::from_parts(13_527_000, 3940) .saturating_add(RocksDbWeight::get().reads(2_u64)) } - fn seal_caller_is_origin() -> Weight { + fn caller_is_origin() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` // Minimum execution time: 337_000 picoseconds. Weight::from_parts(375_000, 0) } - fn seal_caller_is_root() -> Weight { + fn caller_is_root() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -1730,7 +1730,7 @@ impl WeightInfo for () { // Minimum execution time: 308_000 picoseconds. Weight::from_parts(352_000, 0) } - fn seal_weight_left() -> Weight { + fn weight_left() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` @@ -1799,7 +1799,7 @@ impl WeightInfo for () { // Minimum execution time: 260_000 picoseconds. Weight::from_parts(293_000, 0) } - fn seal_minimum_balance() -> Weight { + fn minimum_balance() -> Weight { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` diff --git a/substrate/frame/revive/uapi/Cargo.toml b/substrate/frame/revive/uapi/Cargo.toml index f4221ca5ce313..dafbd96642621 100644 --- a/substrate/frame/revive/uapi/Cargo.toml +++ b/substrate/frame/revive/uapi/Cargo.toml @@ -16,8 +16,14 @@ features = ["unstable-hostfn"] targets = ["riscv64imac-unknown-none-elf"] [dependencies] +alloy-core = { workspace = true, optional = true, features = ["sol-types"] } bitflags = { workspace = true } -codec = { features = ["derive", "max-encoded-len"], optional = true, workspace = true } +codec = { features = [ + "derive", + "max-encoded-len", +], optional = true, workspace = true } +const-crypto = { version = "0.3.0", default-features = false } +hex-literal = { version = "0.4.1", default-features = false } pallet-revive-proc-macro = { workspace = true } scale-info = { features = ["derive"], optional = true, workspace = true } @@ -27,4 +33,5 @@ polkavm-derive = { version = "0.27.0" } [features] default = ["scale"] scale = ["dep:codec", "scale-info"] +precompiles-sol-interfaces = ["alloy-core"] unstable-hostfn = [] diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index c7684bd8cece4..71da29d815fa6 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -219,7 +219,7 @@ pub trait HostFn: private::Sealed { /// Retrieve the value under the given key from storage. /// - /// The key length must not exceed the maximum defined by the contracts module parameter. + /// The key length must not exceed the maximum defined by the `pallet-revive` parameter. /// /// # Parameters /// - `key`: The storage key. @@ -355,7 +355,7 @@ pub trait HostFn: private::Sealed { /// Set the value at the given key in the contract storage. /// - /// The key and value lengths must not exceed the maximums defined by the contracts module + /// The key and value lengths must not exceed the maximums defined by the `pallet-revive` /// parameters. /// /// # Parameters @@ -439,54 +439,8 @@ pub trait HostFn: private::Sealed { /// /// - `block_number`: A reference to the block number buffer. /// - `output`: A reference to the output data buffer to write the block number. - #[unstable_hostfn] fn block_hash(block_number: &[u8; 32], output: &mut [u8; 32]); - /// Call into the chain extension provided by the chain if any. - /// - /// Handling of the input values is up to the specific chain extension and so is the - /// return value. The extension can decide to use the inputs as primitive inputs or as - /// in/out arguments by interpreting them as pointers. Any caller of this function - /// must therefore coordinate with the chain that it targets. - /// - /// # Note - /// - /// If no chain extension exists the contract will trap with the `NoChainExtension` - /// module error. - /// - /// # Parameters - /// - /// - `func_id`: The function id of the chain extension. - /// - `input`: The input data buffer. - /// - `output`: A reference to the output data buffer to write the call output buffer. If `None` - /// is provided then the output buffer is not copied. - /// - /// # Return - /// - /// The chain extension returned value, if executed successfully. - #[unstable_hostfn] - fn call_chain_extension(func_id: u32, input: &[u8], output: Option<&mut &mut [u8]>) -> u32; - - /// Checks whether the caller of the current contract is the origin of the whole call stack. - /// - /// - /// # Return - /// - /// A return value of `true` indicates that this contract is being called by a plain account - /// and `false` indicates that the caller is another contract. - #[unstable_hostfn] - fn caller_is_origin() -> bool; - - /// Checks whether the caller of the current contract is root. - /// - /// Note that only the origin of the call stack can be root. Hence this function returning - /// `true` implies that the contract is being called by the origin. - /// - /// A return value of `true` indicates that this contract is being called by a root origin, - /// and `false` indicates that the caller is a signed origin. - #[unstable_hostfn] - fn caller_is_root() -> bool; - /// Clear the value at the given key in the contract storage. /// /// # Parameters @@ -501,7 +455,7 @@ pub trait HostFn: private::Sealed { /// Checks whether there is a value stored under the given key. /// - /// The key length must not exceed the maximum defined by the contracts module parameter. + /// The key length must not exceed the maximum defined by the `pallet-revive` parameter. /// /// # Parameters /// - `key`: The storage key. @@ -526,22 +480,6 @@ pub trait HostFn: private::Sealed { #[unstable_hostfn] fn ecdsa_to_eth_address(pubkey: &[u8; 33], output: &mut [u8; 20]) -> Result; - /// Stores the minimum balance (a.k.a. existential deposit) into the supplied buffer. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the minimum balance. - #[unstable_hostfn] - fn minimum_balance(output: &mut [u8; 32]); - - /// Retrieve the code hash of the currently executing contract. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the code hash. - #[unstable_hostfn] - fn own_code_hash(output: &mut [u8; 32]); - /// Replace the contract code at the specified address with new code. /// /// # Note @@ -615,17 +553,6 @@ pub trait HostFn: private::Sealed { /// - The deletion queue is full. #[unstable_hostfn] fn terminate(beneficiary: &[u8; 20]) -> !; - - /// Stores the amount of weight left into the supplied buffer. - /// The data is encoded as Weight. - /// - /// If the available space in `output` is less than the size of the value a trap is triggered. - /// - /// # Parameters - /// - /// - `output`: A reference to the output data buffer to write the weight left. - #[unstable_hostfn] - fn weight_left(output: &mut &mut [u8]); } mod private { diff --git a/substrate/frame/revive/uapi/src/host/riscv64.rs b/substrate/frame/revive/uapi/src/host/riscv64.rs index b0cc4987e2cb7..27d1c79bb856c 100644 --- a/substrate/frame/revive/uapi/src/host/riscv64.rs +++ b/substrate/frame/revive/uapi/src/host/riscv64.rs @@ -97,12 +97,8 @@ mod sys { pub fn origin(out_ptr: *mut u8); pub fn code_hash(address_ptr: *const u8, out_ptr: *mut u8); pub fn code_size(address_ptr: *const u8) -> u64; - pub fn own_code_hash(out_ptr: *mut u8); - pub fn caller_is_origin() -> ReturnCode; - pub fn caller_is_root() -> ReturnCode; pub fn address(out_ptr: *mut u8); pub fn weight_to_fee(ref_time: u64, proof_size: u64, out_ptr: *mut u8); - pub fn weight_left(out_ptr: *mut u8, out_len_ptr: *mut u32); pub fn ref_time_left() -> u64; pub fn get_immutable_data(out_ptr: *mut u8, out_len_ptr: *mut u32); pub fn set_immutable_data(ptr: *const u8, len: u32); @@ -112,7 +108,6 @@ mod sys { pub fn value_transferred(out_ptr: *mut u8); pub fn now(out_ptr: *mut u8); pub fn gas_limit() -> u64; - pub fn minimum_balance(out_ptr: *mut u8); pub fn deposit_event( topics_ptr: *const [u8; 32], num_topic: u32, @@ -126,13 +121,6 @@ mod sys { pub fn block_hash(block_number_ptr: *const u8, out_ptr: *mut u8); pub fn block_author(out_ptr: *mut u8); pub fn hash_keccak_256(input_ptr: *const u8, input_len: u32, out_ptr: *mut u8); - pub fn call_chain_extension( - id: u32, - input_ptr: *const u8, - input_len: u32, - out_ptr: *mut u8, - out_len_ptr: *mut u32, - ) -> ReturnCode; pub fn sr25519_verify( signature_ptr: *const u8, pub_key_ptr: *const u8, @@ -445,49 +433,15 @@ impl HostFn for HostFnImpl { unsafe { sys::ref_time_left() } } - #[unstable_hostfn] fn block_hash(block_number_ptr: &[u8; 32], output: &mut [u8; 32]) { unsafe { sys::block_hash(block_number_ptr.as_ptr(), output.as_mut_ptr()) }; } - #[unstable_hostfn] - fn call_chain_extension(func_id: u32, input: &[u8], mut output: Option<&mut &mut [u8]>) -> u32 { - let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); - let ret_code = { - unsafe { - sys::call_chain_extension( - func_id, - input.as_ptr(), - input.len() as u32, - output_ptr, - &mut output_len, - ) - } - }; - - if let Some(ref mut output) = output { - extract_from_slice(output, output_len as usize); - } - ret_code.into_u32() - } - fn call_data_copy(output: &mut [u8], offset: u32) { let len = output.len() as u32; unsafe { sys::call_data_copy(output.as_mut_ptr(), len, offset) }; } - #[unstable_hostfn] - fn caller_is_origin() -> bool { - let ret_val = unsafe { sys::caller_is_origin() }; - ret_val.into_bool() - } - - #[unstable_hostfn] - fn caller_is_root() -> bool { - let ret_val = unsafe { sys::caller_is_root() }; - ret_val.into_bool() - } - #[unstable_hostfn] fn clear_storage(flags: StorageFlags, key: &[u8]) -> Option { let ret_code = unsafe { sys::clear_storage(flags.bits(), key.as_ptr(), key.len() as u32) }; @@ -507,16 +461,6 @@ impl HostFn for HostFnImpl { ret_code.into() } - #[unstable_hostfn] - fn minimum_balance(output: &mut [u8; 32]) { - unsafe { sys::minimum_balance(output.as_mut_ptr()) } - } - - #[unstable_hostfn] - fn own_code_hash(output: &mut [u8; 32]) { - unsafe { sys::own_code_hash(output.as_mut_ptr()) } - } - #[unstable_hostfn] fn set_code_hash(code_hash: &[u8; 32]) { unsafe { sys::set_code_hash(code_hash.as_ptr()) } @@ -558,11 +502,4 @@ impl HostFn for HostFnImpl { unsafe { sys::terminate(beneficiary.as_ptr()) } panic!("terminate does not return"); } - - #[unstable_hostfn] - fn weight_left(output: &mut &mut [u8]) { - let mut output_len = output.len() as u32; - unsafe { sys::weight_left(output.as_mut_ptr(), &mut output_len) } - extract_from_slice(output, output_len as usize) - } } diff --git a/substrate/frame/revive/uapi/src/lib.rs b/substrate/frame/revive/uapi/src/lib.rs index 66cf4ffcd6ded..c2a98c5410add 100644 --- a/substrate/frame/revive/uapi/src/lib.rs +++ b/substrate/frame/revive/uapi/src/lib.rs @@ -12,9 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! External C API to communicate with substrate contracts runtime module. +//! External C API to communicate with Polkadot SDK's `pallet-revive` module. //! -//! Refer to substrate FRAME contract module for more documentation. +//! Refer to the FRAME `pallet-revive` module for more documentation. #![no_std] #![cfg_attr(docsrs, feature(doc_cfg))] @@ -24,6 +24,9 @@ pub use flags::*; mod host; mod macros; +pub mod precompiles; +pub use precompiles::{system::SYSTEM_PRECOMPILE_ADDR, utils::solidity_selector}; + pub use host::{HostFn, HostFnImpl}; /// Convert a u64 into a [u8; 32]. diff --git a/substrate/frame/revive/uapi/src/precompiles/mod.rs b/substrate/frame/revive/uapi/src/precompiles/mod.rs new file mode 100644 index 0000000000000..0df51720ad9a4 --- /dev/null +++ b/substrate/frame/revive/uapi/src/precompiles/mod.rs @@ -0,0 +1,16 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// 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. + +pub mod system; +pub mod utils; diff --git a/substrate/frame/revive/uapi/src/precompiles/system.rs b/substrate/frame/revive/uapi/src/precompiles/system.rs new file mode 100644 index 0000000000000..e73c9ca05fa29 --- /dev/null +++ b/substrate/frame/revive/uapi/src/precompiles/system.rs @@ -0,0 +1,70 @@ +// 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. + +//! Information around the `System` pre-compile. + +#[cfg(feature = "precompiles-sol-interfaces")] +use alloy_core::sol; + +/// Address for the System pre-compile. +pub const SYSTEM_PRECOMPILE_ADDR: [u8; 20] = + hex_literal::hex!("0000000000000000000000000000000000000900"); + +#[cfg(feature = "precompiles-sol-interfaces")] +sol! { + interface ISystem { + /// Computes the BLAKE2 256-bit hash on the given input. + function hashBlake256(bytes memory input) external pure returns (bytes32 digest); + + /// Computes the BLAKE2 128-bit hash on the given input. + function hashBlake128(bytes memory input) external pure returns (bytes32 digest); + + /// Retrieve the account id for a specified `H160` address. + /// + /// Calling this function on a native `H160` chain (`type AccountId = H160`) + /// does not make sense, as it would just return the `address` that it was + /// called with. + /// + /// # Note + /// + /// If no mapping exists for `addr`, the fallback account id will be returned. + function toAccountId(address input) external view returns (bytes memory account_id); + + /// Checks whether the caller of the contract calling this function is the origin + /// of the whole call stack. + function callerIsOrigin() external view returns (bool); + + /// Checks whether the caller of the contract calling this function is root. + /// + /// Note that only the origin of the call stack can be root. Hence this + /// function returning `true` implies that the contract is being called by the origin. + /// + /// A return value of `true` indicates that this contract is being called by a root origin, + /// and `false` indicates that the caller is a signed origin. + function callerIsRoot() external view returns (bool); + + /// Returns the minimum balance that is required for creating an account + /// (the existential deposit). + function minimumBalance() external view returns (uint); + + /// Returns the code hash of the caller. + function ownCodeHash() external view returns (bytes32); + + /// Returns the amount of `Weight` left. + function weightLeft() external view returns (uint64 refTime, uint64 proofSize); + } +} diff --git a/substrate/frame/revive/uapi/src/precompiles/utils.rs b/substrate/frame/revive/uapi/src/precompiles/utils.rs new file mode 100644 index 0000000000000..95ae966db0a2d --- /dev/null +++ b/substrate/frame/revive/uapi/src/precompiles/utils.rs @@ -0,0 +1,35 @@ +// 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. + +//! Helper utilities around pre-compiles. + +/// Returns the Solidity selector for `fn_sig`. +/// +/// Note that this is a const function, it is evaluated at compile time. +/// +/// # Usage +/// +/// ``` +/// # use pallet_revive_uapi::solidity_selector; +/// let sel = solidity_selector("ownCodeHash()"); +/// assert_eq!(sel, [219, 107, 220, 138]); +/// ``` +pub const fn solidity_selector(fn_sig: &str) -> [u8; 4] { + let output: [u8; 32] = + const_crypto::sha3::Keccak256::new().update(fn_sig.as_bytes()).finalize(); + [output[0], output[1], output[2], output[3]] +}