use crate::core::ics24_host::identifier::ClientId;
use crate::core::ContextError;
use crate::mock::context::AnyClientState;
use crate::prelude::*;
use crate::signer::Signer;
use crate::Height;
pub trait RelayerContext {
fn query_latest_height(&self) -> Result<Height, ContextError>;
fn query_client_full_state(&self, client_id: &ClientId) -> Option<AnyClientState>;
fn signer(&self) -> Signer;
}
#[cfg(test)]
mod tests {
use test_log::test;
use tracing::debug;
use crate::clients::ics07_tendermint::client_type as tm_client_type;
use crate::core::ics02_client::client_state::ClientStateCommon;
use crate::core::ics02_client::msgs::update_client::MsgUpdateClient;
use crate::core::ics02_client::msgs::ClientMsg;
use crate::core::ics24_host::identifier::{ChainId, ClientId};
use crate::core::MsgEnvelope;
use crate::mock::client_state::client_type as mock_client_type;
use crate::mock::context::MockContext;
use crate::mock::host::{HostBlock, HostType};
use crate::mock::ics18_relayer::context::RelayerContext;
use crate::mock::ics18_relayer::error::RelayerError;
use crate::mock::router::MockRouter;
use crate::prelude::*;
use crate::Height;
pub(crate) fn build_client_update_datagram<Ctx>(
dest: &Ctx,
client_id: &ClientId,
src_header: &HostBlock,
) -> Result<ClientMsg, RelayerError>
where
Ctx: RelayerContext,
{
let dest_client_state = dest.query_client_full_state(client_id).ok_or_else(|| {
RelayerError::ClientStateNotFound {
client_id: client_id.clone(),
}
})?;
let dest_client_latest_height = dest_client_state.latest_height();
if src_header.height() == dest_client_latest_height {
return Err(RelayerError::ClientAlreadyUpToDate {
client_id: client_id.clone(),
source_height: src_header.height(),
destination_height: dest_client_latest_height,
});
};
if dest_client_latest_height > src_header.height() {
return Err(RelayerError::ClientAtHigherHeight {
client_id: client_id.clone(),
source_height: src_header.height(),
destination_height: dest_client_latest_height,
});
};
Ok(ClientMsg::UpdateClient(MsgUpdateClient {
client_id: client_id.clone(),
client_message: (*src_header).clone().into(),
signer: dest.signer(),
}))
}
#[test]
fn client_update_ping_pong() {
let chain_a_start_height = Height::new(1, 11).unwrap();
let chain_b_start_height = Height::new(1, 20).unwrap();
let client_on_b_for_a_height = Height::new(1, 10).unwrap(); let client_on_a_for_b_height = Height::new(1, 20).unwrap(); let num_iterations = 4;
let client_on_a_for_b = ClientId::new(tm_client_type(), 0).unwrap();
let client_on_b_for_a = ClientId::new(mock_client_type(), 0).unwrap();
let chain_id_a = ChainId::new("mockgaiaA", 1).unwrap();
let chain_id_b = ChainId::new("mockgaiaB", 1).unwrap();
let mut ctx_a =
MockContext::new(chain_id_a.clone(), HostType::Mock, 5, chain_a_start_height)
.with_client_parametrized_with_chain_id(
chain_id_b.clone(),
&client_on_a_for_b,
client_on_a_for_b_height,
Some(tm_client_type()), Some(client_on_a_for_b_height),
);
let mut router_a = MockRouter::default();
let mut ctx_b = MockContext::new(
chain_id_b,
HostType::SyntheticTendermint,
5,
chain_b_start_height,
)
.with_client_parametrized_with_chain_id(
chain_id_a,
&client_on_b_for_a,
client_on_b_for_a_height,
Some(mock_client_type()), Some(client_on_b_for_a_height),
);
let mut router_b = MockRouter::default();
for _i in 0..num_iterations {
let a_latest_header = ctx_a.query_latest_header().unwrap();
let client_msg_b_res =
build_client_update_datagram(&ctx_b, &client_on_b_for_a, &a_latest_header);
assert!(
client_msg_b_res.is_ok(),
"create_client_update failed for context destination {ctx_b:?}, error: {client_msg_b_res:?}",
);
let client_msg_b = client_msg_b_res.unwrap();
let dispatch_res_b = ctx_b.deliver(&mut router_b, MsgEnvelope::Client(client_msg_b));
let validation_res = ctx_b.validate();
assert!(
validation_res.is_ok(),
"context validation failed with error {validation_res:?} for context {ctx_b:?}",
);
assert!(
dispatch_res_b.is_ok(),
"Dispatch failed for host chain b with error: {dispatch_res_b:?}"
);
let client_height_b = ctx_b
.query_client_full_state(&client_on_b_for_a)
.unwrap()
.latest_height();
assert_eq!(client_height_b, ctx_a.query_latest_height().unwrap());
let mut b_latest_header = ctx_b.query_latest_header().unwrap();
let th = b_latest_header.height();
b_latest_header.set_trusted_height(th.decrement().unwrap());
let client_msg_a_res =
build_client_update_datagram(&ctx_a, &client_on_a_for_b, &b_latest_header);
assert!(
client_msg_a_res.is_ok(),
"create_client_update failed for context destination {ctx_a:?}, error: {client_msg_a_res:?}",
);
let client_msg_a = client_msg_a_res.unwrap();
debug!("client_msg_a = {:?}", client_msg_a);
let dispatch_res_a = ctx_a.deliver(&mut router_a, MsgEnvelope::Client(client_msg_a));
let validation_res = ctx_a.validate();
assert!(
validation_res.is_ok(),
"context validation failed with error {validation_res:?} for context {ctx_a:?}",
);
assert!(
dispatch_res_a.is_ok(),
"Dispatch failed for host chain a with error: {dispatch_res_a:?}"
);
let client_height_a = ctx_a
.query_client_full_state(&client_on_a_for_b)
.unwrap()
.latest_height();
assert_eq!(client_height_a, ctx_b.query_latest_height().unwrap());
}
}
}