From de41a053e7f19d73b8df67d196edb02eeb284439 Mon Sep 17 00:00:00 2001 From: Tin Chung <56880684+chungquantin@users.noreply.github.com> Date: Mon, 12 Aug 2024 23:23:09 +0700 Subject: [PATCH] refactor: remove config and add generic extension type (#176) --- extension/src/lib.rs | 88 +++++++++++++++----------- runtime/devnet/src/config/api.rs | 49 +++++++------- runtime/devnet/src/config/contracts.rs | 10 +-- 3 files changed, 76 insertions(+), 71 deletions(-) diff --git a/extension/src/lib.rs b/extension/src/lib.rs index 51671139..afad2afa 100644 --- a/extension/src/lib.rs +++ b/extension/src/lib.rs @@ -10,7 +10,7 @@ use constants::*; use frame_support::{ dispatch::{GetDispatchInfo, PostDispatchInfo}, pallet_prelude::*, - traits::{Contains, OriginTrait}, + traits::OriginTrait, }; use frame_system::RawOrigin; use pallet_contracts::chain_extension::{ @@ -22,30 +22,42 @@ use sp_std::vec::Vec; type ContractSchedule = ::Schedule; -/// Trait for the Pop API chain extension configuration. -pub trait Config: - frame_system::Config> -{ - /// A query of runtime state. - type RuntimeRead: Decode; - /// Something to read runtime states. - type StateReader: ReadState; - /// Allowlisted runtime calls and read state calls. - type AllowedApiCalls: Contains + Contains; +/// Handles the query from the chain extension environment for state reads. +pub trait ReadState { + type StateQuery: Decode; + + /// Allowed state queries from the API. + fn contains(c: &Self::StateQuery) -> bool; + + /// Reads state using the provided query, returning the result as a byte vector. + fn read(read: Self::StateQuery) -> Vec; + + /// Decodes parameters into state query. + fn decode(params: &mut &[u8]) -> Result { + decode_checked(params) + } } -/// Trait for handling parameters from the chain extension environment during state read operations. -pub trait ReadState { - fn read(read: T::RuntimeRead) -> Vec; +/// Handles the query from the chain extension environment for dispatch calls. +pub trait CallFilter { + type Call: Decode; + + /// Allowed runtime calls from the API. + fn contains(t: &Self::Call) -> bool; } #[derive(Default)] -pub struct ApiExtension; +pub struct ApiExtension(PhantomData); -impl ChainExtension for ApiExtension +impl ChainExtension for ApiExtension where - T: Config + pallet_contracts::Config, + T: pallet_contracts::Config + + frame_system::Config< + RuntimeCall: GetDispatchInfo + Dispatchable, + >, T::AccountId: UncheckedFrom + AsRef<[u8]>, + // Bound the type by the two traits which need to be implemented by the runtime. + I: ReadState + CallFilter::RuntimeCall> + 'static, { fn call>( &mut self, @@ -72,10 +84,10 @@ where log::debug!(target: LOG_TARGET, "Read input successfully"); match function_id { FuncId::Dispatch => { - dispatch::(&mut env, version, pallet_index, call_index, params) + dispatch::(&mut env, version, pallet_index, call_index, params) }, FuncId::ReadState => { - read_state::(&mut env, version, pallet_index, call_index, params) + read_state::(&mut env, version, pallet_index, call_index, params) }, } }, @@ -105,17 +117,13 @@ fn extract_env>(env: &Environment) -> (u8, (version, function_id, pallet_index, call_index) } -fn read_state( +fn read_state, StateReader: ReadState>( env: &mut Environment, version: u8, pallet_index: u8, call_index: u8, mut params: Vec, -) -> Result<(), DispatchError> -where - T: Config, - E: Ext, -{ +) -> Result<(), DispatchError> { const LOG_PREFIX: &str = " read_state |"; // Prefix params with version, pallet, index to simplify decoding. @@ -129,9 +137,9 @@ where env.charge_weight(T::DbWeight::get().reads(1_u64))?; let result = match version { VersionedStateRead::V0 => { - let read = decode_checked::(&mut encoded_read)?; - ensure!(T::AllowedApiCalls::contains(&read), UNKNOWN_CALL_ERROR); - T::StateReader::read(read) + let read = StateReader::decode(&mut encoded_read)?; + ensure!(StateReader::contains(&read), UNKNOWN_CALL_ERROR); + StateReader::read(read) }, }; log::trace!( @@ -141,7 +149,7 @@ where env.write(&result, false, None) } -fn dispatch( +fn dispatch( env: &mut Environment, version: u8, pallet_index: u8, @@ -149,8 +157,11 @@ fn dispatch( mut params: Vec, ) -> Result<(), DispatchError> where - T: Config, + T: frame_system::Config< + RuntimeCall: GetDispatchInfo + Dispatchable, + >, E: Ext, + Filter: CallFilter::RuntimeCall> + 'static, { const LOG_PREFIX: &str = " dispatch |"; @@ -158,11 +169,11 @@ where params.insert(0, version); params.insert(1, pallet_index); params.insert(2, call_index); - let call = decode_checked::>(&mut ¶ms[..])?; + let call = decode_checked::>(&mut ¶ms[..])?; // Contract is the origin by default. let origin: T::RuntimeOrigin = RawOrigin::Signed(env.ext().address().clone()).into(); match call { - VersionedDispatch::V0(call) => dispatch_call::(env, call, origin, LOG_PREFIX), + VersionedDispatch::V0(call) => dispatch_call::(env, call, origin, LOG_PREFIX), } } @@ -171,19 +182,22 @@ fn decode_checked(params: &mut &[u8]) -> Result { T::decode(params).map_err(|_| DECODING_FAILED_ERROR) } -fn dispatch_call( +fn dispatch_call( env: &mut Environment, call: T::RuntimeCall, mut origin: T::RuntimeOrigin, log_prefix: &str, ) -> Result<(), DispatchError> where - T: Config, + T: frame_system::Config< + RuntimeCall: GetDispatchInfo + Dispatchable, + >, E: Ext, + Filter: CallFilter::RuntimeCall> + 'static, { let charged_dispatch_weight = env.charge_weight(call.get_dispatch_info().weight)?; log::debug!(target:LOG_TARGET, "{} Inputted RuntimeCall: {:?}", log_prefix, call); - origin.add_filter(T::AllowedApiCalls::contains); + origin.add_filter(Filter::contains); match call.dispatch(origin) { Ok(info) => { log::debug!(target:LOG_TARGET, "{} success, actual weight: {:?}", log_prefix, info.actual_weight); @@ -210,10 +224,10 @@ enum VersionedStateRead { /// Wrapper to enable versioning of runtime calls. #[derive(Decode, Debug)] -enum VersionedDispatch { +enum VersionedDispatch { /// Version zero of dispatch calls. #[codec(index = 0)] - V0(T::RuntimeCall), + V0(RuntimeCall), } /// Function identifiers used in the Pop API. diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 29e61f55..5f234cd4 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -1,7 +1,6 @@ use crate::{config::assets::TrustBackedAssetsInstance, fungibles, Runtime, RuntimeCall}; use codec::{Decode, Encode, MaxEncodedLen}; -use frame_support::traits::Contains; -use pop_chain_extension::ReadState; +use pop_chain_extension::{CallFilter, ReadState}; use sp_std::vec::Vec; /// A query of runtime state. @@ -13,9 +12,25 @@ pub enum RuntimeRead { Fungibles(fungibles::Read), } -/// A struct that provides a state reading implementation for the Runtime. -pub struct StateReader; -impl ReadState for StateReader { +/// A struct that implement requirements for the Pop API chain extension. +#[derive(Default)] +pub struct Extension; +impl ReadState for Extension { + type StateQuery = RuntimeRead; + + fn contains(c: &Self::StateQuery) -> bool { + use fungibles::Read::*; + matches!( + c, + RuntimeRead::Fungibles( + TotalSupply(..) + | BalanceOf { .. } | Allowance { .. } + | TokenName(..) | TokenSymbol(..) + | TokenDecimals(..) | AssetExists(..) + ) + ) + } + fn read(read: RuntimeRead) -> Vec { match read { RuntimeRead::Fungibles(key) => fungibles::Pallet::read_state(key), @@ -23,12 +38,10 @@ impl ReadState for StateReader { } } -/// A type to identify allowed calls to the Runtime from the API. -pub struct AllowedApiCalls; +impl CallFilter for Extension { + type Call = RuntimeCall; -impl Contains for AllowedApiCalls { - /// Allowed runtime calls from the API. - fn contains(c: &RuntimeCall) -> bool { + fn contains(c: &Self::Call) -> bool { use fungibles::Call::*; matches!( c, @@ -46,22 +59,6 @@ impl Contains for AllowedApiCalls { } } -impl Contains for AllowedApiCalls { - /// Allowed state queries from the API. - fn contains(c: &RuntimeRead) -> bool { - use fungibles::Read::*; - matches!( - c, - RuntimeRead::Fungibles( - TotalSupply(..) - | BalanceOf { .. } | Allowance { .. } - | TokenName(..) | TokenSymbol(..) - | TokenDecimals(..) | AssetExists(..) - ) - ) - } -} - impl fungibles::Config for Runtime { type AssetsInstance = TrustBackedAssetsInstance; type WeightInfo = fungibles::weights::SubstrateWeight; diff --git a/runtime/devnet/src/config/contracts.rs b/runtime/devnet/src/config/contracts.rs index fa48970d..5dc613b5 100644 --- a/runtime/devnet/src/config/contracts.rs +++ b/runtime/devnet/src/config/contracts.rs @@ -1,4 +1,4 @@ -use super::api::{AllowedApiCalls, RuntimeRead, StateReader}; +use super::api::Extension; use crate::{ deposit, Balance, Balances, BalancesCall, Perbill, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason, Timestamp, @@ -45,12 +45,6 @@ parameter_types! { pub const CodeHashLockupDepositPercent: Perbill = Perbill::from_percent(0); } -impl pop_chain_extension::Config for Runtime { - type RuntimeRead = RuntimeRead; - type StateReader = StateReader; - type AllowedApiCalls = AllowedApiCalls; -} - impl pallet_contracts::Config for Runtime { type Time = Timestamp; type Randomness = DummyRandomness; @@ -70,7 +64,7 @@ impl pallet_contracts::Config for Runtime { type CallStack = [pallet_contracts::Frame; 23]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; - type ChainExtension = pop_chain_extension::ApiExtension; + type ChainExtension = pop_chain_extension::ApiExtension; type Schedule = Schedule; type AddressGenerator = pallet_contracts::DefaultAddressGenerator; // This node is geared towards development and testing of contracts.