From d132ca5cf654b15ef80e9ca2279e4ee0fea2a385 Mon Sep 17 00:00:00 2001 From: kku9706 Date: Sat, 4 Jan 2025 22:32:05 +0800 Subject: [PATCH 1/3] FeeHandler --- modules/ismp/pallets/pallet/src/fee.rs | 32 ++++++++++++++++++++++++ modules/ismp/pallets/pallet/src/impls.rs | 10 +++----- modules/ismp/pallets/pallet/src/lib.rs | 20 ++++++++++++--- 3 files changed, 51 insertions(+), 11 deletions(-) create mode 100644 modules/ismp/pallets/pallet/src/fee.rs diff --git a/modules/ismp/pallets/pallet/src/fee.rs b/modules/ismp/pallets/pallet/src/fee.rs new file mode 100644 index 000000000..b05edd88a --- /dev/null +++ b/modules/ismp/pallets/pallet/src/fee.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2024 Polytope Labs. +// 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::{weights::get_weight, Config}; +use frame_support::dispatch::{DispatchResultWithPostInfo, Pays, PostDispatchInfo}; +use ismp::messaging::Message; + +pub trait HandleFee { + fn handle_fee(messages: &[Message], signer: T::AccountId) -> DispatchResultWithPostInfo; +} + +impl HandleFee for () { + fn handle_fee(messages: &[Message], _signer: T::AccountId) -> DispatchResultWithPostInfo { + //Todo: reward the signer + Ok(PostDispatchInfo { + actual_weight: Some(get_weight::(&messages)), + pays_fee: Pays::Yes, + }) + } +} diff --git a/modules/ismp/pallets/pallet/src/impls.rs b/modules/ismp/pallets/pallet/src/impls.rs index c19a37901..6030e342e 100644 --- a/modules/ismp/pallets/pallet/src/impls.rs +++ b/modules/ismp/pallets/pallet/src/impls.rs @@ -19,12 +19,11 @@ use crate::{ child_trie::{RequestCommitments, ResponseCommitments}, dispatcher::{FeeMetadata, RequestMetadata}, offchain::{self, ForkIdentifier, Leaf, LeafIndexAndPos, OffchainDBProvider, Proof, ProofKeys}, - weights::get_weight, Config, Error, Event, Pallet, Responded, }; use alloc::{string::ToString, vec, vec::Vec}; use codec::Decode; -use frame_support::dispatch::{DispatchResultWithPostInfo, Pays, PostDispatchInfo}; +use frame_support::dispatch::DispatchResult; use frame_system::Phase; use ismp::{ handlers::{handle_incoming_message, MessageResult}, @@ -80,7 +79,7 @@ impl Pallet { } /// Execute the provided ISMP datagrams, this will short circuit if any messages are invalid. - pub fn execute(messages: Vec) -> DispatchResultWithPostInfo { + pub fn execute(messages: Vec) -> DispatchResult { // Define a host let host = Pallet::::default(); let events = messages @@ -116,10 +115,7 @@ impl Pallet { Pallet::::deposit_event(event.into()) } - Ok(PostDispatchInfo { - actual_weight: Some(get_weight::(&messages)), - pays_fee: Pays::Yes, - }) + Ok(()) } /// Dispatch an outgoing request, returns the request commitment diff --git a/modules/ismp/pallets/pallet/src/lib.rs b/modules/ismp/pallets/pallet/src/lib.rs index dd4aa7d8c..fae4d46f7 100644 --- a/modules/ismp/pallets/pallet/src/lib.rs +++ b/modules/ismp/pallets/pallet/src/lib.rs @@ -178,6 +178,7 @@ pub mod child_trie; pub mod dispatcher; pub mod errors; pub mod events; +mod fee; pub mod host; mod impls; pub mod offchain; @@ -197,12 +198,13 @@ pub mod pallet { use crate::{ child_trie::{RequestCommitments, ResponseCommitments, CHILD_TRIE_PREFIX}, errors::HandlingError, + fee::HandleFee, weights::{get_weight, WeightProvider}, }; use codec::{Codec, Encode}; use core::fmt::Debug; use frame_support::{ - dispatch::{DispatchResult, DispatchResultWithPostInfo}, + dispatch::{DispatchResult, DispatchResultWithPostInfo, PostDispatchInfo}, pallet_prelude::*, traits::{fungible::Mutate, tokens::Preservation, Get, UnixTime}, PalletId, @@ -304,6 +306,9 @@ pub mod pallet { /// This offchain DB is also allowed to "merkelize" and "generate proofs" for messages. /// Most state machines will likey not need this and can just provide `()` type OffchainDB: OffchainDBProvider; + + /// FeeHandler with custom fee logic + type FeeHandler: HandleFee; } // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and @@ -437,7 +442,12 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { ensure_none(origin)?; - Self::execute(messages) + Self::execute(messages.clone())?; + + Ok(PostDispatchInfo { + actual_weight: Some(get_weight::(&messages)), + pays_fee: Pays::No, + }) } /// Execute the provided batch of ISMP messages. This call will short-circuit and revert if @@ -453,9 +463,11 @@ pub mod pallet { #[pallet::call_index(1)] #[frame_support::transactional] pub fn handle(origin: OriginFor, messages: Vec) -> DispatchResultWithPostInfo { - ensure_signed(origin)?; + let signer = ensure_signed(origin)?; + + Self::execute(messages.clone())?; - Self::execute(messages) + T::FeeHandler::handle_fee(messages.as_slice(), signer) } /// Create a consensus client, using a subjectively chosen consensus state. This can also From de740159508c5014285284a774dd0a52e3209b56 Mon Sep 17 00:00:00 2001 From: kku9706 Date: Sun, 5 Jan 2025 00:52:01 +0800 Subject: [PATCH 2/3] Update runtime --- modules/ismp/pallets/pallet/src/lib.rs | 12 ++++-------- parachain/runtimes/nexus/Cargo.toml | 2 +- parachain/runtimes/nexus/src/ismp.rs | 1 + 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/modules/ismp/pallets/pallet/src/lib.rs b/modules/ismp/pallets/pallet/src/lib.rs index fae4d46f7..7db4c2f32 100644 --- a/modules/ismp/pallets/pallet/src/lib.rs +++ b/modules/ismp/pallets/pallet/src/lib.rs @@ -222,13 +222,12 @@ pub mod pallet { router::IsmpRouter, }; use sp_core::{storage::ChildInfo, H256}; - #[cfg(feature = "unsigned")] - use sp_runtime::transaction_validity::{ - InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, - ValidTransaction, - }; use sp_runtime::{ traits::{AccountIdConversion, AtLeast32BitUnsigned}, + transaction_validity::{ + InvalidTransaction, TransactionSource, TransactionValidity, TransactionValidityError, + ValidTransaction, + }, FixedPointOperand, }; use sp_std::prelude::*; @@ -432,7 +431,6 @@ pub mod pallet { /// - `messages`: the messages to handle or process. /// /// Emits different message events based on the Message received if successful. - #[cfg(feature = "unsigned")] #[pallet::weight(get_weight::(&messages))] #[pallet::call_index(0)] #[frame_support::transactional] @@ -458,7 +456,6 @@ pub mod pallet { /// - `messages`: A set of ISMP [`Message`]s to handle or process. /// /// Emits different message events based on the Message received if successful. - #[cfg(not(feature = "unsigned"))] #[pallet::weight(get_weight::(&messages))] #[pallet::call_index(1)] #[frame_support::transactional] @@ -656,7 +653,6 @@ pub mod pallet { } /// This allows users execute ISMP datagrams for free. Use with caution. - #[cfg(feature = "unsigned")] #[pallet::validate_unsigned] impl ValidateUnsigned for Pallet { type Call = Call; diff --git a/parachain/runtimes/nexus/Cargo.toml b/parachain/runtimes/nexus/Cargo.toml index b2ba60ba6..0856d44bf 100644 --- a/parachain/runtimes/nexus/Cargo.toml +++ b/parachain/runtimes/nexus/Cargo.toml @@ -91,7 +91,7 @@ parachains-common = { workspace = true } # ismp modules ismp = { workspace = true } -pallet-ismp = { workspace = true, features = ["unsigned"] } +pallet-ismp = { workspace = true } pallet-ismp-demo = { workspace = true } pallet-ismp-runtime-api = { workspace = true } ismp-parachain = { workspace = true } diff --git a/parachain/runtimes/nexus/src/ismp.rs b/parachain/runtimes/nexus/src/ismp.rs index 463117652..b04ab2028 100644 --- a/parachain/runtimes/nexus/src/ismp.rs +++ b/parachain/runtimes/nexus/src/ismp.rs @@ -112,6 +112,7 @@ impl pallet_ismp::Config for Runtime { ); type OffchainDB = Mmr; type WeightProvider = (); + type FeeHandler = (); } impl pallet_ismp_relayer::Config for Runtime { From d03c72b1a47f6e08b273cb40e170486ee146898b Mon Sep 17 00:00:00 2001 From: kku9706 Date: Tue, 7 Jan 2025 21:55:28 +0800 Subject: [PATCH 3/3] Remove WeightRouter --- modules/ismp/pallets/pallet/src/lib.rs | 10 +- modules/ismp/pallets/pallet/src/weights.rs | 113 ++++++--------------- 2 files changed, 38 insertions(+), 85 deletions(-) diff --git a/modules/ismp/pallets/pallet/src/lib.rs b/modules/ismp/pallets/pallet/src/lib.rs index 7db4c2f32..f4813b86a 100644 --- a/modules/ismp/pallets/pallet/src/lib.rs +++ b/modules/ismp/pallets/pallet/src/lib.rs @@ -199,7 +199,7 @@ pub mod pallet { child_trie::{RequestCommitments, ResponseCommitments, CHILD_TRIE_PREFIX}, errors::HandlingError, fee::HandleFee, - weights::{get_weight, WeightProvider}, + weights::{get_weight, IsmpModuleWeight}, }; use codec::{Codec, Encode}; use core::fmt::Debug; @@ -295,10 +295,6 @@ pub mod pallet { /// subsystems. type ConsensusClients: ConsensusClientProvider; - /// This implementation should provide the weight consumed by `IsmpModule` callbacks from - /// their benchmarks. - type WeightProvider: WeightProvider; - /// Offchain database implementation. Outgoing requests and responses are /// inserted in this database, while their commitments are stored onchain. /// @@ -308,6 +304,10 @@ pub mod pallet { /// FeeHandler with custom fee logic type FeeHandler: HandleFee; + + /// This implementation should provide the weight consumed by `IsmpModuleWeight` from + /// their benchmarks. + type WeightProvider: IsmpModuleWeight; } // Simple declaration of the `Pallet` type. It is placeholder we use to implement traits and diff --git a/modules/ismp/pallets/pallet/src/weights.rs b/modules/ismp/pallets/pallet/src/weights.rs index 1b156ae15..326618fe2 100644 --- a/modules/ismp/pallets/pallet/src/weights.rs +++ b/modules/ismp/pallets/pallet/src/weights.rs @@ -15,47 +15,43 @@ //! Utilities for providing the static weights for module callbacks -use crate::{utils::ModuleId, Config}; -use alloc::boxed::Box; -use frame_support::weights::Weight; +use crate::Config; +use frame_support::weights::{constants::RocksDbWeight, Weight}; use ismp::{ messaging::{Message, TimeoutMessage}, - router::{GetResponse, PostRequest, Request, RequestResponse, Response, Timeout}, + router::{GetResponse, PostRequest, RequestResponse, Response, Timeout}, }; /// Interface for providing the weight information about [`IsmpModule`](ismp::module::IsmpModule) /// callbacks pub trait IsmpModuleWeight { /// Should return the weight used in processing this request - fn on_accept(&self, request: &PostRequest) -> Weight; + fn on_accept(request: &PostRequest) -> Weight; /// Should return the weight used in processing this timeout - fn on_timeout(&self, request: &Timeout) -> Weight; + fn on_timeout(request: &Timeout) -> Weight; /// Should return the weight used in processing this response - fn on_response(&self, response: &Response) -> Weight; + fn on_response(response: &Response) -> Weight; } +/// Just by estimation, require benchmark to generate weight for production in runtimes impl IsmpModuleWeight for () { - fn on_accept(&self, _request: &PostRequest) -> Weight { - Weight::zero() + fn on_accept(_request: &PostRequest) -> Weight { + Weight::from_parts(63_891_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(1)) } - fn on_timeout(&self, _request: &Timeout) -> Weight { - Weight::zero() + fn on_timeout(_request: &Timeout) -> Weight { + Weight::from_parts(63_891_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(1)) } - fn on_response(&self, _response: &Response) -> Weight { - Weight::zero() - } -} - -/// An interface for querying the [`IsmpModuleWeight`] for a given -/// [`IsmpModule`](ismp::module::IsmpModule) -pub trait WeightProvider { - /// Returns a reference to the weight provider for a module - fn module_callback(dest_module: ModuleId) -> Option>; -} - -impl WeightProvider for () { - fn module_callback(_dest_module: ModuleId) -> Option> { - None + fn on_response(_response: &Response) -> Weight { + Weight::from_parts(63_891_000, 0) + .saturating_add(Weight::from_parts(0, 52674)) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(1)) } } @@ -63,45 +59,23 @@ impl WeightProvider for () { pub(crate) fn get_weight(messages: &[Message]) -> Weight { messages.into_iter().fold(Weight::zero(), |acc, msg| match msg { Message::Request(msg) => { - let cb_weight = msg.requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = ModuleId::from_bytes(req.to.as_slice()).ok(); - let handle = dest_module - .map(|id| ::WeightProvider::module_callback(id)) - .flatten() - .unwrap_or(Box::new(())); - acc + handle.on_accept(&req) - }); + let cb_weight = msg + .requests + .iter() + .fold(Weight::zero(), |acc, req| acc + T::WeightProvider::on_accept(&req)); acc + cb_weight }, Message::Response(msg) => match &msg.datagram { RequestResponse::Response(responses) => { - let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { - let dest_module = match res { - Response::Post(ref post) => - ModuleId::from_bytes(post.post.from.as_slice()).ok(), - _ => return acc, - }; - - let handle = dest_module - .map(|id| ::WeightProvider::module_callback(id)) - .flatten() - .unwrap_or(Box::new(())); - acc + handle.on_response(&res) - }); + let cb_weight = responses + .iter() + .fold(Weight::zero(), |acc, res| acc + T::WeightProvider::on_response(&res)); acc + cb_weight }, RequestResponse::Request(requests) => { let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = match req { - Request::Get(ref get) => ModuleId::from_bytes(get.from.as_slice()).ok(), - _ => return acc, - }; - let handle = dest_module - .map(|id| ::WeightProvider::module_callback(id)) - .flatten() - .unwrap_or(Box::new(())); - acc + handle.on_response(&Response::Get(GetResponse { + acc + T::WeightProvider::on_response(&Response::Get(GetResponse { get: req.get_request().expect("Infallible"), values: Default::default(), })) @@ -113,42 +87,21 @@ pub(crate) fn get_weight(messages: &[Message]) -> Weight { Message::Timeout(msg) => match msg { TimeoutMessage::Post { requests, .. } => { let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = match req { - Request::Post(ref post) => ModuleId::from_bytes(post.from.as_slice()).ok(), - _ => return acc, - }; - let handle = dest_module - .map(|id| ::WeightProvider::module_callback(id)) - .flatten() - .unwrap_or(Box::new(())); - acc + handle.on_timeout(&Timeout::Request(req.clone())) + acc + T::WeightProvider::on_timeout(&Timeout::Request(req.clone())) }); acc + cb_weight }, TimeoutMessage::PostResponse { responses, .. } => { let cb_weight = responses.iter().fold(Weight::zero(), |acc, res| { - let dest_module = ModuleId::from_bytes(&res.post.to).ok(); - let handle = dest_module - .map(|id| ::WeightProvider::module_callback(id)) - .flatten() - .unwrap_or(Box::new(())); - acc + handle.on_timeout(&Timeout::Response(res.clone())) + acc + T::WeightProvider::on_timeout(&Timeout::Response(res.clone())) }); acc + cb_weight }, TimeoutMessage::Get { requests } => { let cb_weight = requests.iter().fold(Weight::zero(), |acc, req| { - let dest_module = match req { - Request::Get(ref get) => ModuleId::from_bytes(get.from.as_slice()).ok(), - _ => return acc, - }; - let handle = dest_module - .map(|id| ::WeightProvider::module_callback(id)) - .flatten() - .unwrap_or(Box::new(())); - acc + handle.on_timeout(&Timeout::Request(req.clone())) + acc + T::WeightProvider::on_timeout(&Timeout::Request(req.clone())) }); acc + cb_weight },