pallet_revive/precompiles/builtin/
system.rs1use 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 let mut call_setup = CallSetup::<Test>::default();
124 let (mut ext, _) = call_setup.ext();
125 let unmapped_address = H160::zero();
126
127 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 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 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 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 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}