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	precompiles::{BuiltinAddressMatcher, BuiltinPrecompile, Error, Ext},
20	vm::RuntimeCosts,
21	Config,
22};
23use alloc::vec::Vec;
24use alloy_core::sol;
25use core::{marker::PhantomData, num::NonZero};
26use sp_core::hexdisplay::AsBytesRef;
27
28pub struct System<T>(PhantomData<T>);
29
30sol! {
31	interface ISystem {
32		/// Computes the BLAKE2 256-bit hash on the given input.
33		function hashBlake256(bytes memory input) external pure returns (bytes32 digest);
34		/// Computes the BLAKE2 128-bit hash on the given input.
35		function hashBlake128(bytes memory input) external pure returns (bytes32 digest);
36		/// Retrieve the account id for a specified `H160` address.
37		///
38		/// Calling this function on a native `H160` chain (`type AccountId = H160`)
39		/// does not make sense, as it would just return the `address` that it was
40		/// called with.
41		///
42		/// # Note
43		///
44		/// If no mapping exists for `addr`, the fallback account id will be returned.
45		function toAccountId(address input) external view returns (bytes memory account_id);
46	}
47}
48
49impl<T: Config> BuiltinPrecompile for System<T> {
50	type T = T;
51	type Interface = ISystem::ISystemCalls;
52	const MATCHER: BuiltinAddressMatcher =
53		BuiltinAddressMatcher::Fixed(NonZero::new(0x900).unwrap());
54	const HAS_CONTRACT_INFO: bool = false;
55
56	fn call(
57		_address: &[u8; 20],
58		input: &Self::Interface,
59		env: &mut impl Ext<T = Self::T>,
60	) -> Result<Vec<u8>, Error> {
61		use ISystem::ISystemCalls;
62		match input {
63			ISystemCalls::hashBlake256(ISystem::hashBlake256Call { input }) => {
64				env.gas_meter_mut().charge(RuntimeCosts::HashBlake256(input.len() as u32))?;
65				let output = sp_io::hashing::blake2_256(input.as_bytes_ref());
66				Ok(output.to_vec())
67			},
68			ISystemCalls::hashBlake128(ISystem::hashBlake128Call { input }) => {
69				env.gas_meter_mut().charge(RuntimeCosts::HashBlake128(input.len() as u32))?;
70				let output = sp_io::hashing::blake2_128(input.as_bytes_ref());
71				Ok(output.to_vec())
72			},
73			ISystemCalls::toAccountId(ISystem::toAccountIdCall { input }) => {
74				use crate::address::AddressMapper;
75				use codec::Encode;
76				env.gas_meter_mut().charge(RuntimeCosts::ToAccountId)?;
77				let account_id =
78					T::AddressMapper::to_account_id(&crate::H160::from_slice(input.as_slice()));
79				Ok(account_id.encode())
80			},
81		}
82	}
83}
84
85#[cfg(test)]
86mod tests {
87	use super::{ISystem, *};
88	use crate::{
89		address::AddressMapper,
90		call_builder::{caller_funding, CallSetup},
91		pallet,
92		precompiles::{tests::run_test_vectors, BuiltinPrecompile},
93		tests::{ExtBuilder, Test},
94		H160,
95	};
96	use codec::Decode;
97	use frame_support::traits::fungible::Mutate;
98
99	#[test]
100	fn test_system_precompile() {
101		run_test_vectors::<System<Test>>(include_str!("testdata/900-blake2_256.json"));
102		run_test_vectors::<System<Test>>(include_str!("testdata/900-blake2_128.json"));
103		run_test_vectors::<System<Test>>(include_str!("testdata/900-to_account_id.json"));
104	}
105
106	#[test]
107	fn test_system_precompile_unmapped_account() {
108		ExtBuilder::default().build().execute_with(|| {
109			// given
110			let mut call_setup = CallSetup::<Test>::default();
111			let (mut ext, _) = call_setup.ext();
112			let unmapped_address = H160::zero();
113
114			// when
115			let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
116				input: unmapped_address.0.into(),
117			});
118			let expected_fallback_account_id =
119				<System<Test>>::call(&<System<Test>>::MATCHER.base_address(), &input, &mut ext)
120					.unwrap();
121
122			// then
123			assert_eq!(
124				expected_fallback_account_id[20..32],
125				[0xEE; 12],
126				"no fallback suffix found where one should be"
127			);
128		})
129	}
130
131	#[test]
132	fn test_system_precompile_mapped_account() {
133		use crate::test_utils::EVE;
134		ExtBuilder::default().build().execute_with(|| {
135			// given
136			let mapped_address = {
137				<Test as pallet::Config>::Currency::set_balance(&EVE, caller_funding::<Test>());
138				let _ = <Test as pallet::Config>::AddressMapper::map(&EVE);
139				<Test as pallet::Config>::AddressMapper::to_address(&EVE)
140			};
141
142			let mut call_setup = CallSetup::<Test>::default();
143			let (mut ext, _) = call_setup.ext();
144
145			// when
146			let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall {
147				input: mapped_address.0.into(),
148			});
149			let data =
150				<System<Test>>::call(&<System<Test>>::MATCHER.base_address(), &input, &mut ext)
151					.unwrap();
152
153			// then
154			assert_ne!(
155				data.as_slice()[20..32],
156				[0xEE; 12],
157				"fallback suffix found where none should be"
158			);
159			assert_eq!(
160				<Test as frame_system::Config>::AccountId::decode(&mut data.as_slice()),
161				Ok(EVE),
162			);
163		})
164	}
165}