referrerpolicy=no-referrer-when-downgrade

pallet_revive/precompiles/builtin/
system.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::{
19	address::AddressMapper,
20	precompiles::{BuiltinAddressMatcher, BuiltinPrecompile, Error, Ext},
21	vm::RuntimeCosts,
22	Config, H160,
23};
24use alloc::vec::Vec;
25use alloy_core::sol_types::SolValue;
26use codec::Encode;
27use core::{marker::PhantomData, num::NonZero};
28use pallet_revive_uapi::precompiles::system::ISystem;
29use sp_core::hexdisplay::AsBytesRef;
30
31pub struct System<T>(PhantomData<T>);
32
33impl<T: Config> BuiltinPrecompile for System<T> {
34	type T = T;
35	type Interface = ISystem::ISystemCalls;
36	const MATCHER: BuiltinAddressMatcher =
37		BuiltinAddressMatcher::Fixed(NonZero::new(0x900).unwrap());
38	const HAS_CONTRACT_INFO: bool = false;
39
40	fn call(
41		_address: &[u8; 20],
42		input: &Self::Interface,
43		env: &mut impl Ext<T = Self::T>,
44	) -> Result<Vec<u8>, Error> {
45		use ISystem::ISystemCalls;
46		match input {
47			ISystemCalls::hashBlake256(ISystem::hashBlake256Call { input }) => {
48				env.gas_meter_mut().charge(RuntimeCosts::HashBlake256(input.len() as u32))?;
49				let output = sp_io::hashing::blake2_256(input.as_bytes_ref());
50				Ok(output.abi_encode())
51			},
52			ISystemCalls::hashBlake128(ISystem::hashBlake128Call { input }) => {
53				env.gas_meter_mut().charge(RuntimeCosts::HashBlake128(input.len() as u32))?;
54				let output = sp_io::hashing::blake2_128(input.as_bytes_ref());
55				Ok(output.abi_encode())
56			},
57			ISystemCalls::toAccountId(ISystem::toAccountIdCall { input }) => {
58				env.gas_meter_mut().charge(RuntimeCosts::ToAccountId)?;
59				let account_id = env.to_account_id(&H160::from_slice(input.as_slice()));
60				Ok(account_id.encode().abi_encode())
61			},
62			ISystemCalls::callerIsOrigin(ISystem::callerIsOriginCall {}) => {
63				env.gas_meter_mut().charge(RuntimeCosts::CallerIsOrigin)?;
64				let is_origin = env.caller_is_origin(true);
65				Ok(is_origin.abi_encode())
66			},
67			ISystemCalls::callerIsRoot(ISystem::callerIsRootCall {}) => {
68				env.gas_meter_mut().charge(RuntimeCosts::CallerIsRoot)?;
69				let is_root = env.caller_is_root(true);
70				Ok(is_root.abi_encode())
71			},
72			ISystemCalls::ownCodeHash(ISystem::ownCodeHashCall {}) => {
73				env.gas_meter_mut().charge(RuntimeCosts::OwnCodeHash)?;
74				let caller = env.caller();
75				let addr = T::AddressMapper::to_address(caller.account_id()?);
76				let output = env.code_hash(&addr.into()).0.abi_encode();
77				Ok(output)
78			},
79			ISystemCalls::minimumBalance(ISystem::minimumBalanceCall {}) => {
80				env.gas_meter_mut().charge(RuntimeCosts::MinimumBalance)?;
81				let minimum_balance = env.minimum_balance();
82				Ok(minimum_balance.to_big_endian().abi_encode())
83			},
84			ISystemCalls::weightLeft(ISystem::weightLeftCall {}) => {
85				env.gas_meter_mut().charge(RuntimeCosts::WeightLeft)?;
86				let ref_time = env.gas_meter().gas_left().ref_time();
87				let proof_size = env.gas_meter().gas_left().proof_size();
88				let res = (ref_time, proof_size);
89				Ok(res.abi_encode())
90			},
91		}
92	}
93}
94
95#[cfg(test)]
96mod tests {
97	use super::*;
98	use crate::{
99		address::AddressMapper,
100		call_builder::{caller_funding, CallSetup},
101		pallet,
102		precompiles::{
103			alloy::sol_types::{sol_data::Bytes, SolType},
104			tests::run_test_vectors,
105			BuiltinPrecompile,
106		},
107		tests::{ExtBuilder, Test},
108	};
109	use codec::Decode;
110	use frame_support::traits::fungible::Mutate;
111
112	#[test]
113	fn test_system_precompile() {
114		run_test_vectors::<System<Test>>(include_str!("testdata/900-blake2_256.json"));
115		run_test_vectors::<System<Test>>(include_str!("testdata/900-blake2_128.json"));
116		run_test_vectors::<System<Test>>(include_str!("testdata/900-to_account_id.json"));
117	}
118
119	#[test]
120	fn test_system_precompile_unmapped_account() {
121		ExtBuilder::default().build().execute_with(|| {
122			// given
123			let mut call_setup = CallSetup::<Test>::default();
124			let (mut ext, _) = call_setup.ext();
125			let unmapped_address = H160::zero();
126
127			// when
128			let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
129				input: unmapped_address.0.into(),
130			});
131			let raw_data =
132				<System<Test>>::call(&<System<Test>>::MATCHER.base_address(), &input, &mut ext)
133					.unwrap();
134
135			// then
136			let expected_fallback_account_id =
137				Bytes::abi_decode(&raw_data).expect("decoding failed");
138			assert_eq!(
139				expected_fallback_account_id.0.as_ref()[20..32],
140				[0xEE; 12],
141				"no fallback suffix found where one should be"
142			);
143		})
144	}
145
146	#[test]
147	fn test_system_precompile_mapped_account() {
148		use crate::test_utils::EVE;
149		ExtBuilder::default().build().execute_with(|| {
150			// given
151			let mapped_address = {
152				<Test as pallet::Config>::Currency::set_balance(&EVE, caller_funding::<Test>());
153				let _ = <Test as pallet::Config>::AddressMapper::map(&EVE);
154				<Test as pallet::Config>::AddressMapper::to_address(&EVE)
155			};
156
157			let mut call_setup = CallSetup::<Test>::default();
158			let (mut ext, _) = call_setup.ext();
159
160			// when
161			let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
162				input: mapped_address.0.into(),
163			});
164			let raw_data =
165				<System<Test>>::call(&<System<Test>>::MATCHER.base_address(), &input, &mut ext)
166					.unwrap();
167
168			// then
169			let data = Bytes::abi_decode(&raw_data).expect("decoding failed");
170			assert_ne!(
171				data.0.as_ref()[20..32],
172				[0xEE; 12],
173				"fallback suffix found where none should be"
174			);
175			assert_eq!(
176				<Test as frame_system::Config>::AccountId::decode(&mut data.as_ref()),
177				Ok(EVE),
178			);
179		})
180	}
181}