diff --git a/ismp-testsuite/src/mocks.rs b/ismp-testsuite/src/mocks.rs index ac68c9a9f..944df1574 100644 --- a/ismp-testsuite/src/mocks.rs +++ b/ismp-testsuite/src/mocks.rs @@ -6,9 +6,10 @@ use ismp::{ error::Error, host::{IsmpHost, StateMachine}, messaging::{Proof, StateCommitmentHeight}, + module::{DispatchError, DispatchResult, DispatchSuccess, IsmpModule}, router::{ - DispatchError, DispatchRequest, DispatchResult, DispatchSuccess, Get, IsmpDispatcher, - IsmpRouter, Post, PostResponse, Request, RequestResponse, Response, + DispatchRequest, Get, IsmpDispatcher, IsmpRouter, Post, PostResponse, Request, + RequestResponse, Response, }, util::{hash_request, hash_response}, }; @@ -266,38 +267,40 @@ impl IsmpHost for Host { } } -pub struct MockRouter(pub Host); +#[derive(Default)] +pub struct MockModule; -impl IsmpRouter for MockRouter { - fn handle_request(&self, request: Post) -> DispatchResult { - let host = &self.0.clone(); - let request = Request::Post(request); - if request.dest_chain() != host.host_state_machine() { - } else { - host.store_request_receipt(&request).unwrap(); - } +impl IsmpModule for MockModule { + fn on_accept(&self, request: Post) -> DispatchResult { + Ok(DispatchSuccess { + dest_chain: request.dest_chain, + source_chain: request.source_chain, + nonce: request.nonce, + }) + } + fn on_response(&self, response: Response) -> DispatchResult { Ok(DispatchSuccess { - dest_chain: request.dest_chain(), - source_chain: request.source_chain(), - nonce: request.nonce(), + dest_chain: response.dest_chain(), + source_chain: response.source_chain(), + nonce: response.nonce(), }) } - fn handle_timeout(&self, request: Request) -> DispatchResult { + fn on_timeout(&self, request: Request) -> DispatchResult { Ok(DispatchSuccess { dest_chain: request.dest_chain(), source_chain: request.source_chain(), nonce: request.nonce(), }) } +} - fn handle_response(&self, response: Response) -> DispatchResult { - Ok(DispatchSuccess { - dest_chain: response.dest_chain(), - source_chain: response.source_chain(), - nonce: response.nonce(), - }) +pub struct MockRouter(pub Host); + +impl IsmpRouter for MockRouter { + fn module_for_id(&self, _bytes: Vec) -> Result, Error> { + Ok(Box::new(MockModule)) } } diff --git a/ismp/src/error.rs b/ismp/src/error.rs index 72b01f3f6..aa6fa1af3 100644 --- a/ismp/src/error.rs +++ b/ismp/src/error.rs @@ -19,7 +19,7 @@ use crate::{ consensus::{ConsensusClientId, StateMachineHeight}, host::StateMachine, }; -use alloc::string::String; +use alloc::{string::String, vec::Vec}; use core::time::Duration; /// Errors that may be encountered by the ISMP module @@ -135,4 +135,6 @@ pub enum Error { }, /// Supplied proof height is invalid InsufficientProofHeight, + /// An Ismp Module was not found for the given raw id + ModuleNotFound(Vec), } diff --git a/ismp/src/handlers.rs b/ismp/src/handlers.rs index 33fcb7818..4808fff19 100644 --- a/ismp/src/handlers.rs +++ b/ismp/src/handlers.rs @@ -19,9 +19,9 @@ use crate::{ error::Error, host::IsmpHost, messaging::Message, - router::DispatchResult, }; +use crate::module::DispatchResult; use alloc::{boxed::Box, collections::BTreeSet, vec::Vec}; pub use consensus::create_client; diff --git a/ismp/src/handlers/request.rs b/ismp/src/handlers/request.rs index 8d85c1ebc..12d23b1c6 100644 --- a/ismp/src/handlers/request.rs +++ b/ismp/src/handlers/request.rs @@ -50,7 +50,8 @@ where host.request_receipt(&req).is_none() && !req.timed_out(state.timestamp()) }) .map(|request| { - let res = router.handle_request(request.clone()); + let cb = router.module_for_id(request.to.clone())?; + let res = cb.on_accept(request.clone()); host.store_request_receipt(&Request::Post(request))?; Ok(res) }) diff --git a/ismp/src/handlers/response.rs b/ismp/src/handlers/response.rs index 9632c2afb..ada0dcca5 100644 --- a/ismp/src/handlers/response.rs +++ b/ismp/src/handlers/response.rs @@ -62,7 +62,8 @@ where .into_iter() .filter(|res| host.response_receipt(res).is_none()) .map(|response| { - let res = router.handle_response(response.clone()); + let cb = router.module_for_id(response.destination_module())?; + let res = cb.on_response(response.clone()); host.store_response_receipt(&response)?; Ok(res) }) @@ -84,7 +85,8 @@ where let values = state_machine.verify_state_proof(host, keys, state, &proof)?; let router = host.ismp_router(); - let res = router.handle_response(Response::Get(GetResponse { + let cb = router.module_for_id(request.source_module())?; + let res = cb.on_response(Response::Get(GetResponse { get: request.get_request()?, values, })); diff --git a/ismp/src/handlers/timeout.rs b/ismp/src/handlers/timeout.rs index 7a9dcc97b..7a1790f42 100644 --- a/ismp/src/handlers/timeout.rs +++ b/ismp/src/handlers/timeout.rs @@ -67,7 +67,8 @@ where requests .into_iter() .map(|request| { - let res = router.handle_timeout(request.clone()); + let cb = router.module_for_id(request.source_module())?; + let res = cb.on_timeout(request.clone()); host.delete_request_commitment(&request)?; Ok(res) }) @@ -99,7 +100,8 @@ where requests .into_iter() .map(|request| { - let res = router.handle_timeout(request.clone()); + let cb = router.module_for_id(request.source_module())?; + let res = cb.on_timeout(request.clone()); host.delete_request_commitment(&request)?; Ok(res) }) diff --git a/ismp/src/module.rs b/ismp/src/module.rs index b943816b7..8e6901817 100644 --- a/ismp/src/module.rs +++ b/ismp/src/module.rs @@ -16,22 +16,50 @@ //! ISMPModule definition use crate::{ - error::Error, + host::StateMachine, router::{Post as PostRequest, Request, Response}, }; +use alloc::string::String; + +/// The result of successfully dispatching a request or response +#[derive(Debug, PartialEq, Eq)] +pub struct DispatchSuccess { + /// Destination chain for request or response + pub dest_chain: StateMachine, + /// Source chain for request or response + pub source_chain: StateMachine, + /// Request nonce + pub nonce: u64, +} + +/// The result of unsuccessfully dispatching a request or response +#[derive(Debug, PartialEq, Eq)] +pub struct DispatchError { + /// Descriptive error message + pub msg: String, + /// Request nonce + pub nonce: u64, + /// Source chain for request or response + pub source: StateMachine, + /// Destination chain for request or response + pub dest: StateMachine, +} + +/// A type alias for dispatch results +pub type DispatchResult = Result; /// Individual modules which live on a state machine must conform to this interface in order to send -/// and receive ISMP requests and reponses +/// and receive ISMP requests and responses pub trait IsmpModule { - /// Called by the local ISMP router on a module, to notify module of a new POST request + /// Called by the message handler on a module, to notify module of a new POST request /// the module may choose to respond immediately, or in a later block - fn on_accept(request: PostRequest) -> Result<(), Error>; + fn on_accept(&self, request: PostRequest) -> DispatchResult; - /// Called by the router on a module, to notify module of a response to a previously sent out - /// request - fn on_response(response: Response) -> Result<(), Error>; + /// Called by the message handler on a module, to notify module of a response to a previously + /// sent out request + fn on_response(&self, response: Response) -> DispatchResult; - /// Called by the router on a module, to notify module of requests that were previously sent but - /// have now timed-out - fn on_timeout(request: Request) -> Result<(), Error>; + /// Called by the message handler on a module, to notify module of requests that were previously + /// sent but have now timed-out + fn on_timeout(&self, request: Request) -> DispatchResult; } diff --git a/ismp/src/router.rs b/ismp/src/router.rs index b8cae3f3f..e02264556 100644 --- a/ismp/src/router.rs +++ b/ismp/src/router.rs @@ -15,11 +15,13 @@ //! IsmpRouter definition -use crate::{error::Error, host::StateMachine, prelude::Vec}; -use alloc::{ - collections::BTreeMap, - string::{String, ToString}, +use crate::{ + error::Error, + host::StateMachine, + module::{DispatchResult, IsmpModule}, + prelude::Vec, }; +use alloc::{boxed::Box, collections::BTreeMap, string::ToString}; use codec::{Decode, Encode}; use core::time::Duration; @@ -92,6 +94,22 @@ impl Request { } } + /// Module where this request originated on source chain + pub fn source_module(&self) -> Vec { + match self { + Request::Get(get) => get.from.clone(), + Request::Post(post) => post.from.clone(), + } + } + + /// Module that this request will be routed to on destination chain + pub fn destination_module(&self) -> Vec { + match self { + Request::Get(get) => get.from.clone(), + Request::Post(post) => post.to.clone(), + } + } + /// Get the destination chain pub fn dest_chain(&self) -> StateMachine { match self { @@ -202,6 +220,14 @@ impl Response { } } + /// Module that this response will be routed to on destination chain + pub fn destination_module(&self) -> Vec { + match self { + Response::Get(get) => get.get.from.clone(), + Response::Post(post) => post.post.from.clone(), + } + } + /// Get the source chain for this response pub fn source_chain(&self) -> StateMachine { match self { @@ -235,43 +261,12 @@ pub enum RequestResponse { Response(Vec), } -/// The result of successfully dispatching a request or response -#[derive(Debug, PartialEq, Eq)] -pub struct DispatchSuccess { - /// Destination chain for request or response - pub dest_chain: StateMachine, - /// Source chain for request or response - pub source_chain: StateMachine, - /// Request nonce - pub nonce: u64, -} - -/// The result of unsuccessfully dispatching a request or response -#[derive(Debug, PartialEq, Eq)] -pub struct DispatchError { - /// Descriptive error message - pub msg: String, - /// Request nonce - pub nonce: u64, - /// Source chain for request or response - pub source: StateMachine, - /// Destination chain for request or response - pub dest: StateMachine, -} - -/// A type alias for dispatch results -pub type DispatchResult = Result; - /// The Ismp router dictates how messsages are routed to [`IsmpModules`] pub trait IsmpRouter { - /// Dispatch the incoming request to destination modules - fn handle_request(&self, request: Post) -> DispatchResult; - - /// Dispatch the request timeouts to destination modules - fn handle_timeout(&self, request: Request) -> DispatchResult; - - /// Dispatch the incoming response to destination modules - fn handle_response(&self, response: Response) -> DispatchResult; + /// Get module handler by id + /// Should decode the module id and return a handler to the appropriate `IsmpModule` + /// implementation + fn module_for_id(&self, bytes: Vec) -> Result, Error>; } /// Simplified POST request, intended to be used for sending outgoing requests