diff --git a/libs/mocks/src/router_message.rs b/libs/mocks/src/router_message.rs index 58e7166d38..be2b61c9d1 100644 --- a/libs/mocks/src/router_message.rs +++ b/libs/mocks/src/router_message.rs @@ -40,10 +40,11 @@ pub mod pallet { } impl MessageSender for Pallet { + type Message = Vec; type Middleware = T::Middleware; type Origin = T::Origin; - fn send(a: Self::Middleware, b: Self::Origin, c: Vec) -> DispatchResult { + fn send(a: Self::Middleware, b: Self::Origin, c: Self::Message) -> DispatchResult { execute_call!((a, b, c)) } } diff --git a/libs/traits/src/liquidity_pools.rs b/libs/traits/src/liquidity_pools.rs index 7e4edf0a80..bc07c483e0 100644 --- a/libs/traits/src/liquidity_pools.rs +++ b/libs/traits/src/liquidity_pools.rs @@ -21,7 +21,6 @@ pub type Proof = [u8; 32]; /// LiquidityPools General Message Passing Format pub trait LpMessage: Sized { type Domain; - type SerializableDomain; fn serialize(&self) -> Vec; fn deserialize(input: &[u8]) -> Result; @@ -43,10 +42,10 @@ pub trait LpMessage: Sized { /// Converts the message into a message proof type. fn to_proof_message(&self) -> Self; - /// Unwraps a forwarded message + /// Unwraps a forwarded message. fn unwrap_forwarded(self) -> Option<(Self::Domain, H160, Self)>; - /// Attempts to wrap into a forwarded message + /// Attempts to wrap into a forwarded message. fn try_wrap_forward( domain: Self::Domain, forwarding_contract: H160, @@ -70,9 +69,15 @@ pub trait MessageSender { /// The originator of the message to be sent type Origin; + /// The type of the message + type Message; + /// Sends a message for origin to destination - fn send(middleware: Self::Middleware, origin: Self::Origin, message: Vec) - -> DispatchResult; + fn send( + middleware: Self::Middleware, + origin: Self::Origin, + message: Self::Message, + ) -> DispatchResult; } /// The behavior of an entity that can receive messages @@ -142,38 +147,3 @@ pub trait InboundMessageHandler { /// Handle an inbound message. fn handle(sender: Self::Sender, msg: Self::Message) -> DispatchResult; } - -/// The behavior of an entity that can forward outbound messages -pub trait ForwardMessageSender { - /// The middleware by where this message is sent - type Middleware; - - /// The originator of the message to be sent - type Origin; - - /// The message that will be forwarded - type Message; - - /// Forwards a message for origin to destination - fn forward( - middleware: Self::Middleware, - origin: Self::Origin, - message: Self::Message, - ) -> DispatchResult; -} - -/// The behavior of an entity that can forward received messages -pub trait ForwardMessageReceiver { - /// The middleware by where this message is received - type Middleware; - - /// The originator of the received message - type Origin; - - /// Forwards a received message from origin - fn forward( - middleware: Self::Middleware, - origin: Self::Origin, - message: Vec, - ) -> DispatchResult; -} diff --git a/pallets/axelar-router/src/lib.rs b/pallets/axelar-router/src/lib.rs index 50c621cc1f..58df759b44 100644 --- a/pallets/axelar-router/src/lib.rs +++ b/pallets/axelar-router/src/lib.rs @@ -319,10 +319,15 @@ pub mod pallet { } impl MessageSender for Pallet { + type Message = Vec; type Middleware = AxelarId; type Origin = DomainAddress; - fn send(axelar_id: AxelarId, origin: Self::Origin, message: Vec) -> DispatchResult { + fn send( + axelar_id: AxelarId, + origin: Self::Origin, + message: Self::Message, + ) -> DispatchResult { let chain_name = ChainNameById::::get(axelar_id) .ok_or(Error::::RouterConfigurationNotFound)?; let config = Configuration::::get(&chain_name) diff --git a/pallets/liquidity-pools-forwarder/src/lib.rs b/pallets/liquidity-pools-forwarder/src/lib.rs index bd2ea98c22..1e3801d92e 100644 --- a/pallets/liquidity-pools-forwarder/src/lib.rs +++ b/pallets/liquidity-pools-forwarder/src/lib.rs @@ -11,12 +11,22 @@ // GNU General Public License for more details. // //! # Liquidity Pools Forwarder Pallet. +//! +//! The Forwarder pallet acts as middleware for incoming and outgoing Liquidity +//! Pools messages by wrapping them, if they are forwarded ones. +//! +//! For incoming messages, it extracts the payload from forwarded messages. +//! +//! For outgoing messages, it wraps the payload based on the configured router +//! info. +//! +//! Assumptions: The EVM side ensures that incoming forwarded messages are +//! valid. + #![cfg_attr(not(feature = "std"), no_std)] -// TODO: Docs use cfg_traits::liquidity_pools::{ - ForwardMessageReceiver, ForwardMessageSender, LpMessage as LpMessageT, MessageReceiver, - MessageSender, RouterProvider, + LpMessage as LpMessageT, MessageReceiver, MessageSender, RouterProvider, }; use cfg_types::domain_address::{Domain, DomainAddress}; use frame_support::{dispatch::DispatchResult, pallet_prelude::*}; @@ -70,13 +80,17 @@ pub mod pallet { + TypeInfo + FullCodec; - /// The target of the messages coming from this chain - type Sender: MessageSender; + /// The entity of the messages coming from this chain. + type Sender: MessageSender< + Middleware = Self::RouterId, + Origin = DomainAddress, + Message = Vec, + >; - /// The target of the messages coming from other chains + /// The entity which acts on unwrapped messages. type Receiver: MessageReceiver; - /// An identification of a router + /// An identification of a router. type RouterId: Parameter + MaxEncodedLen; /// The type that provides the router available for a domain. @@ -88,11 +102,13 @@ pub mod pallet { #[pallet::event] #[pallet::generate_deposit(pub (super) fn deposit_event)] pub enum Event { + /// Forwarding info was set ForwarderSet { router_id: T::RouterId, source_domain: Domain, forwarding_contract: H160, }, + /// Forwarding info was removed ForwarderRemoved { router_id: T::RouterId, source_domain: Domain, @@ -100,21 +116,27 @@ pub mod pallet { }, } + /// Maps a router id to its forwarding info. + /// + /// Can only be mutated via admin origin. #[pallet::storage] - pub type Forwarding = + pub type RouterForwarding = StorageMap<_, Blake2_128Concat, T::RouterId, ForwardInfo, OptionQuery>; #[pallet::error] pub enum Error { /// The router id does not have any forwarder info stored ForwardInfoNotFound, - /// The forwarder origin of the message is invalid - InvalidForwarderOrigin, } #[pallet::call] impl Pallet { - #[pallet::weight(0)] + /// Set forwarding info for the given router id. + /// + /// Origin: Admin. + /// + /// NOTE: Simple weight due to origin requirement. + #[pallet::weight(T::DbWeight::get().writes(1))] #[pallet::call_index(0)] pub fn set_forwarder( origin: OriginFor, @@ -124,7 +146,7 @@ pub mod pallet { ) -> DispatchResult { T::AdminOrigin::ensure_origin(origin)?; - Forwarding::::insert( + RouterForwarding::::insert( &router_id, ForwardInfo { source_domain: source_domain.clone(), @@ -141,12 +163,17 @@ pub mod pallet { Ok(()) } - #[pallet::weight(0)] + /// Remove the forwarding info for the given router id. + /// + /// Origin: Admin. + /// + /// NOTE: Simple weight due to origin requirement. + #[pallet::weight(T::DbWeight::get().writes(1))] #[pallet::call_index(1)] pub fn remove_forwarder(origin: OriginFor, router_id: T::RouterId) -> DispatchResult { T::AdminOrigin::ensure_origin(origin)?; - if let Some(info) = Forwarding::::take(&router_id) { + if let Some(info) = RouterForwarding::::take(&router_id) { Self::deposit_event(Event::::ForwarderRemoved { router_id, source_domain: info.source_domain, @@ -158,17 +185,17 @@ pub mod pallet { } } - impl ForwardMessageSender for Pallet { + impl MessageSender for Pallet { type Message = T::Message; type Middleware = T::RouterId; type Origin = DomainAddress; - fn forward( + fn send( router_id: T::RouterId, origin: DomainAddress, message: T::Message, ) -> DispatchResult { - let payload = if let Some(info) = Forwarding::::get(&router_id) { + let payload = if let Some(info) = RouterForwarding::::get(&router_id) { let wrapped = T::Message::try_wrap_forward(info.source_domain, info.contract, message)?; wrapped.serialize() @@ -180,27 +207,24 @@ pub mod pallet { } } - impl ForwardMessageReceiver for Pallet { + impl MessageReceiver for Pallet { type Middleware = T::RouterId; type Origin = DomainAddress; - fn forward( + fn receive( router_id: T::RouterId, domain_address: DomainAddress, payload: Vec, ) -> DispatchResult { let message = T::Message::deserialize(&payload)?; + // If message can be unwrapped, it was forwarded if let Some((source_domain, _, lp_message)) = message.unwrap_forwarded() { let router_ids = T::RouterProvider::routers_for_domain(source_domain); for router_id in router_ids { - if let Some(info) = Forwarding::::get(&router_id) { - ensure!( - // TODO: Ensure this condition can be assumed, info.contract could be - // Forwarder != Adapter which could be domain_address.h160() - info.contract == domain_address.h160(), - Error::::InvalidForwarderOrigin - ); + // NOTE: We can rely on EVM side to ensure forwarded messages are valid such + // that it suffices to filter for the existence of forwarding info + if RouterForwarding::::get(&router_id).is_some() { return T::Receiver::receive( router_id, domain_address.domain(), diff --git a/pallets/liquidity-pools/src/message.rs b/pallets/liquidity-pools/src/message.rs index c7de61df75..a1ed535070 100644 --- a/pallets/liquidity-pools/src/message.rs +++ b/pallets/liquidity-pools/src/message.rs @@ -193,7 +193,7 @@ impl BatchMessages { } } -/// A message type that can not be forwarded. +/// A message type that cannot be forwarded. #[derive(Encode, Decode, Serialize, Deserialize, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] @@ -565,6 +565,10 @@ pub enum Message EVM Domain. Forwarded { source_domain: SerializableDomain, forwarding_contract: H160, @@ -574,7 +578,6 @@ pub enum Message Vec { gmpf::to_vec(self).unwrap_or_default() diff --git a/runtime/common/src/routing.rs b/runtime/common/src/routing.rs index ab20fba17e..bd794800f3 100644 --- a/runtime/common/src/routing.rs +++ b/runtime/common/src/routing.rs @@ -57,10 +57,11 @@ impl MessageSender for RouterDispatcher where Routers: pallet_axelar_router::Config, { + type Message = Vec; type Middleware = RouterId; type Origin = DomainAddress; - fn send(router_id: RouterId, origin: Self::Origin, message: Vec) -> DispatchResult { + fn send(router_id: RouterId, origin: Self::Origin, message: Self::Message) -> DispatchResult { match router_id { RouterId::Axelar(axelar_id) => { pallet_axelar_router::Pallet::::send(axelar_id, origin, message)