use crate::{
error::Error,
events::{Event, RequestResponseHandled},
handlers::{validate_state_machine, MessageResult},
host::{IsmpHost, StateMachine},
messaging::{dedup_requests, hash_request, RequestMessage},
router::Request,
};
use alloc::vec::Vec;
use sp_weights::Weight;
pub fn handle<H>(host: &H, msg: RequestMessage) -> Result<MessageResult, anyhow::Error>
where
H: IsmpHost,
{
if msg.requests.is_empty() {
Err(Error::EmptyBatch)?
}
let state_machine = validate_state_machine(host, msg.proof.height)?;
let consensus_clients = host.consensus_clients();
let check_state_machine_client = |state_machine: StateMachine| {
consensus_clients
.iter()
.find_map(|client| client.state_machine(state_machine).ok())
.is_none()
};
let router = host.ismp_router();
let wrapped: Vec<Request> = msg.requests.iter().cloned().map(Request::Post).collect();
dedup_requests::<H>(&wrapped)?;
for req in msg.requests.iter() {
let req = Request::Post(req.clone());
if host.request_receipt(&req).is_some() {
Err(Error::DuplicateRequest { meta: req.clone().into() })?
}
if req.timed_out(host.timestamp()) {
Err(Error::RequestTimeout { meta: req.clone().into() })?
}
if req.dest_chain() != host.host_state_machine() && !host.is_router() {
Err(Error::InvalidRequestDestination { meta: req.clone().into() })?
}
let source_chain = req.source_chain();
let allow_proxy = host.is_allowed_proxy(&msg.proof.height.id.state_id) &&
check_state_machine_client(source_chain);
if source_chain != msg.proof.height.id.state_id && !allow_proxy {
Err(Error::RequestProxyProhibited { meta: req.clone().into() })?
}
}
let state = host.state_machine_commitment(msg.proof.height)?;
let commitments = msg
.requests
.iter()
.map(|post| hash_request::<H>(&Request::Post(post.clone())))
.collect();
state_machine.verify_membership(host, commitments, state, &msg.proof)?;
let mut total_weights = Weight::zero();
let result = msg
.requests
.into_iter()
.map(|request| {
let wrapped_req = Request::Post(request.clone());
let mut lambda = || {
let cb = router.module_for_id(request.to.clone())?;
if host.request_receipt(&wrapped_req).is_some() {
Err(Error::DuplicateRequest { meta: wrapped_req.clone().into() })?
}
let signer = host.store_request_receipt(&wrapped_req, &msg.signer)?;
let res = cb.on_accept(request.clone()).map(|weight| {
total_weights.saturating_accrue(weight);
let commitment = hash_request::<H>(&wrapped_req);
Event::PostRequestHandled(RequestResponseHandled {
commitment,
relayer: signer,
})
});
if res.is_err() {
host.delete_request_receipt(&wrapped_req)?;
}
Ok(res)
};
let res = lambda().and_then(|res| res);
res
})
.collect::<Vec<_>>();
Ok(MessageResult::Request { events: result, weight: total_weights })
}