// This file is part of Bizinikiwi. // 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, precompiles::{BuiltinAddressMatcher, BuiltinPrecompile, Error, Ext}, vm::RuntimeCosts, Config, H160, }; use alloc::vec::Vec; use alloy_core::sol_types::SolValue; use codec::Encode; use core::{marker::PhantomData, num::NonZero}; use pezpallet_revive_uapi::precompiles::system::ISystem; use pezsp_core::hexdisplay::AsBytesRef; pub struct System(PhantomData); impl BuiltinPrecompile for System { type T = T; type Interface = ISystem::ISystemCalls; const MATCHER: BuiltinAddressMatcher = BuiltinAddressMatcher::Fixed(NonZero::new(0x900).unwrap()); const HAS_CONTRACT_INFO: bool = false; fn call( _address: &[u8; 20], input: &Self::Interface, env: &mut impl Ext, ) -> Result, Error> { use ISystem::ISystemCalls; match input { ISystemCalls::terminate(_) if env.is_read_only() => Err(crate::Error::::StateChangeDenied.into()), ISystemCalls::hashBlake256(ISystem::hashBlake256Call { input }) => { env.gas_meter_mut().charge(RuntimeCosts::HashBlake256(input.len() as u32))?; let output = pezsp_io::hashing::blake2_256(input.as_bytes_ref()); Ok(output.abi_encode()) }, ISystemCalls::hashBlake128(ISystem::hashBlake128Call { input }) => { env.gas_meter_mut().charge(RuntimeCosts::HashBlake128(input.len() as u32))?; let output = pezsp_io::hashing::blake2_128(input.as_bytes_ref()); Ok(output.abi_encode()) }, ISystemCalls::toAccountId(ISystem::toAccountIdCall { input }) => { env.gas_meter_mut().charge(RuntimeCosts::ToAccountId)?; 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()) }, ISystemCalls::terminate(ISystem::terminateCall { beneficiary }) => { // no need to adjust gas because this always deletes code env.gas_meter_mut().charge(RuntimeCosts::Terminate { code_removed: true })?; let h160 = H160::from_slice(beneficiary.as_slice()); env.terminate_caller(&h160).map_err(Error::try_to_revert::)?; Ok(Vec::new()) }, } } } #[cfg(test)] mod tests { use super::*; use crate::{ address::AddressMapper, call_builder::{caller_funding, CallSetup}, pezpallet, precompiles::{ alloy::sol_types::{sol_data::Bytes, SolType}, tests::run_test_vectors, BuiltinPrecompile, }, tests::{ExtBuilder, Test}, }; use codec::Decode; use pezframe_support::traits::fungible::Mutate; #[test] fn test_system_precompile() { run_test_vectors::>(include_str!("testdata/900-blake2_256.json")); run_test_vectors::>(include_str!("testdata/900-blake2_128.json")); run_test_vectors::>(include_str!("testdata/900-to_account_id.json")); } #[test] fn test_system_precompile_unmapped_account() { ExtBuilder::default().build().execute_with(|| { // given let mut call_setup = CallSetup::::default(); let (mut ext, _) = call_setup.ext(); let unmapped_address = H160::zero(); // when let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall { input: unmapped_address.0.into(), }); 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.0.as_ref()[20..32], [0xEE; 12], "no fallback suffix found where one should be" ); }) } #[test] fn test_system_precompile_mapped_account() { use crate::test_utils::EVE; ExtBuilder::default().build().execute_with(|| { // given let mapped_address = { ::Currency::set_balance(&EVE, caller_funding::()); let _ = ::AddressMapper::map(&EVE); ::AddressMapper::to_address(&EVE) }; let mut call_setup = CallSetup::::default(); let (mut ext, _) = call_setup.ext(); // when let input = ISystem::ISystemCalls::toAccountId(ISystem::toAccountIdCall { input: mapped_address.0.into(), }); 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.0.as_ref()[20..32], [0xEE; 12], "fallback suffix found where none should be" ); assert_eq!( ::AccountId::decode(&mut data.as_ref()), Ok(EVE), ); }) } }