use crate::{
error::Error,
events::{Event, RequestResponseHandled},
handlers::{validate_state_machine, MessageResult},
host::IsmpHost,
messaging::{dedup_requests, hash_request, ResponseMessage},
router::{GetResponse, Request, StorageValue},
};
use alloc::vec::Vec;
use sp_weights::Weight;
pub fn handle<H>(host: &H, msg: ResponseMessage) -> Result<MessageResult, anyhow::Error>
where
H: IsmpHost,
{
if msg.requests.is_empty() {
Err(Error::EmptyBatch)?
}
let proof = msg.proof();
let state_machine = validate_state_machine(host, proof.height)?;
let state = host.state_machine_commitment(proof.height)?;
let mut total_weights = Weight::zero();
dedup_requests::<H>(&msg.requests())?;
for get in &msg.requests {
let req = Request::Get(get.clone());
if req.timed_out(host.timestamp()) {
Err(Error::RequestTimeout { meta: (&req).into() })?
}
if req.dest_chain() != proof.height.id.state_id {
Err(Error::RequestProofMetadataNotValid { meta: (&req).into() })?
}
let commitment = hash_request::<H>(&req);
if host.request_commitment(commitment).is_err() {
Err(Error::UnknownRequest { meta: (&req).into() })?
}
let res = GetResponse { get: get.clone(), values: Default::default() };
if host.response_receipt(&res).is_some() {
Err(Error::DuplicateResponse { meta: (&res).into() })?
}
}
if !msg.requests.iter().all(|get| get.height == proof.height.height) {
Err(Error::InsufficientProofHeight)?
}
let result = msg
.requests
.iter()
.cloned()
.map(|request| {
let wrapped_req = Request::Get(request.clone());
let keys = request.keys.clone();
let values = state_machine
.verify_state_proof(host, keys, state.state_root, &proof)?
.into_iter()
.map(|(key, value)| StorageValue { key, value })
.collect();
let router = host.ismp_router();
let cb = router.module_for_id(request.from.clone())?;
let response = GetResponse { get: request.clone(), values: Default::default() };
if host.response_receipt(&response).is_some() {
Err(Error::DuplicateResponse { meta: (&response).into() })?
}
let signer = host.store_response_receipt(&response, &msg.signer)?;
let res = cb.on_response(GetResponse { get: request.clone(), values }).map(|weight| {
total_weights.saturating_accrue(weight);
let commitment = hash_request::<H>(&wrapped_req);
Event::GetRequestHandled(RequestResponseHandled { commitment, relayer: signer })
});
if res.is_err() {
host.delete_response_receipt(&response)?;
}
Ok::<_, anyhow::Error>(res)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(MessageResult::Response { events: result, weight: total_weights })
}