From 8a492db593118350fa1a21349b5df7b724777634 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Mon, 28 Sep 2020 12:27:11 +0200 Subject: [PATCH 1/8] merge master --- modules/src/context_mock.rs | 13 ++++++ modules/src/ics02_client/client_def.rs | 8 +++- modules/src/ics02_client/context_mock.rs | 29 ++++++++++++ modules/src/ics02_client/handler.rs | 1 + .../src/ics02_client/handler/create_client.rs | 2 +- .../src/ics02_client/handler/update_client.rs | 45 +++++++++++++++++-- modules/src/ics26_routing/handler.rs | 2 + modules/src/mock_client/state.rs | 2 +- 8 files changed, 96 insertions(+), 6 deletions(-) diff --git a/modules/src/context_mock.rs b/modules/src/context_mock.rs index a9b31a2ec1..ded8438dce 100644 --- a/modules/src/context_mock.rs +++ b/modules/src/context_mock.rs @@ -38,6 +38,19 @@ impl MockChainContext { } } + pub fn add_header(&mut self, h: u64) { + let mut new_h = h; + if h == 0 { + new_h = h+1; + } + self.store_historical_info( + Height(new_h), + HistoricalInfo { + header: SelfHeader::Mock(MockHeader(Height(new_h))), + }, + ); + } + pub fn validate(&self) -> Result<(), Box> { // TODO Ok(()) diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index 0842204cb8..bd249f28dd 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -249,7 +249,13 @@ impl ConsensusState for AnyConsensusState { } fn height(&self) -> Height { - todo!() + match self { + AnyConsensusState::Tendermint(cs) => cs.height(), + + #[cfg(test)] + AnyConsensusState::Mock(cs) => cs.height(), + + } } fn root(&self) -> &CommitmentRoot { diff --git a/modules/src/ics02_client/context_mock.rs b/modules/src/ics02_client/context_mock.rs index d8399d5064..914f352bea 100644 --- a/modules/src/ics02_client/context_mock.rs +++ b/modules/src/ics02_client/context_mock.rs @@ -1,3 +1,4 @@ +use crate::context_mock::MockChainContext; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::client_type::ClientType; use crate::ics02_client::context::{ClientKeeper, ClientReader}; @@ -7,6 +8,9 @@ use crate::mock_client::header::MockHeader; use crate::mock_client::state::{MockClientRecord, MockClientState, MockConsensusState}; use std::collections::HashMap; use tendermint::block::Height; +use crate::ics02_client::handler::ClientResult::{CreateResult, UpdateResult}; +use crate::ics02_client::handler::ClientResult; +use crate::ics02_client::state::ConsensusState; /// A mock implementation of client context. This mocks (i.e., replaces) the functionality of /// a KV-store holding information related to the various IBC clients running on a chain. @@ -15,13 +19,24 @@ use tendermint::block::Height; /// the ICS02 handlers (create, update client) and other dependent ICS handlers (e.g., ICS03). #[derive(Clone, Debug)] pub struct MockClientContext { + pub chain_context: MockChainContext, /// The set of all clients, indexed by their id. pub clients: HashMap, } +impl MockClientContext { + pub fn new(chain_height: u64, max_history_size: usize) -> Self { + MockClientContext { + chain_context: MockChainContext::new(max_history_size, Height(chain_height)), + clients: Default::default(), + } + } +} + impl Default for MockClientContext { fn default() -> Self { MockClientContext { + chain_context: Default::default(), clients: Default::default(), } } @@ -99,6 +114,20 @@ impl ClientReader for MockClientContext { } impl ClientKeeper for MockClientContext { + fn store_client_result(&mut self, handler_res: ClientResult) -> Result<(), Error> { + match handler_res { + CreateResult(res) => { + self.with_client_type(&res.client_id, ClientType::Mock, res.consensus_state.height()); + } + UpdateResult(res) => { + self.with_client_consensus_state(&res.client_id, res.consensus_state.height()); + // Create a new block. + self.chain_context.add_header(0); + } + } + Ok(()) + } + fn store_client_type( &mut self, _client_id: ClientId, diff --git a/modules/src/ics02_client/handler.rs b/modules/src/ics02_client/handler.rs index e5fe050a44..a151db88d9 100644 --- a/modules/src/ics02_client/handler.rs +++ b/modules/src/ics02_client/handler.rs @@ -17,6 +17,7 @@ pub enum ClientEvent { ClientUpdated(ClientId), } +#[derive(Clone, Debug, PartialEq, Eq)] pub enum ClientResult { CreateResult(CreateClientResult), UpdateResult(UpdateClientResult), diff --git a/modules/src/ics02_client/handler/create_client.rs b/modules/src/ics02_client/handler/create_client.rs index 8f5c156578..4971ff8706 100644 --- a/modules/src/ics02_client/handler/create_client.rs +++ b/modules/src/ics02_client/handler/create_client.rs @@ -7,7 +7,7 @@ use crate::ics02_client::handler::ClientEvent; use crate::ics02_client::msgs::MsgCreateAnyClient; use crate::ics24_host::identifier::ClientId; -#[derive(Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct CreateClientResult { pub client_id: ClientId, pub client_type: ClientType, diff --git a/modules/src/ics02_client/handler/update_client.rs b/modules/src/ics02_client/handler/update_client.rs index 5ce05623ba..4613287cad 100644 --- a/modules/src/ics02_client/handler/update_client.rs +++ b/modules/src/ics02_client/handler/update_client.rs @@ -1,14 +1,14 @@ use crate::handler::{HandlerOutput, HandlerResult}; use crate::ics02_client::client_def::{AnyClient, AnyClientState, AnyConsensusState, ClientDef}; -use crate::ics02_client::context::ClientReader; +use crate::ics02_client::context::{ClientReader, ClientKeeper}; use crate::ics02_client::error::{Error, Kind}; -use crate::ics02_client::handler::ClientEvent; +use crate::ics02_client::handler::{ClientEvent, ClientResult}; use crate::ics02_client::msgs::MsgUpdateAnyClient; use crate::ics02_client::state::ClientState; use crate::ics24_host::identifier::ClientId; -#[derive(Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct UpdateClientResult { pub client_id: ClientId, pub client_state: AnyClientState, @@ -57,6 +57,11 @@ pub fn process( })) } + +pub fn keep(keeper: &mut dyn ClientKeeper, result: UpdateClientResult) -> Result<(), Error> { + keeper.store_client_result(ClientResult::UpdateResult(result)) +} + #[cfg(test)] mod tests { use tendermint::block::Height; @@ -66,6 +71,8 @@ mod tests { use crate::ics02_client::context_mock::MockClientContext; use crate::ics03_connection::msgs::test_util::get_dummy_account_id; use crate::mock_client::header::MockHeader; + use crate::ics02_client::handler::dispatch; + use crate::ics02_client::msgs::ClientMsg; #[test] fn test_update_client_ok() { @@ -172,4 +179,36 @@ mod tests { } } } + #[test] + fn test_update_two_chains() { + let client_ibc1_on_ibc0: ClientId = "ibconeclient".parse().unwrap(); + let client_ibc0_on_ibc1: ClientId = "ibczeroclient".parse().unwrap(); + let signer = get_dummy_account_id(); + + let ibc0_start_height = 10; + let ibc1_start_height = 20; + let max_history_size = 3; + let num_iterations = 4; + + let mut ctx_ibc0 = MockClientContext::new(ibc0_start_height, max_history_size); + let mut ctx_ibc1 = MockClientContext::new(ibc1_start_height, max_history_size); + ctx_ibc0.with_client(&client_ibc1_on_ibc0, ClientType::Mock, Height(ibc1_start_height)); + ctx_ibc1.with_client(&client_ibc0_on_ibc1, ClientType::Mock, Height(ibc0_start_height)); + + ctx_ibc0.chain_context.add_header(ibc0_start_height+1); + + for _i in 0..num_iterations { + let ibc0_latest_height = ctx_ibc0.chain_context.latest; + let record_ibc0_on_ibc1 = ctx_ibc1.clients.get(&client_ibc0_on_ibc1).unwrap(); + if record_ibc0_on_ibc1.consensus_states.get(&ibc0_latest_height).is_none() { + let msg = MsgUpdateAnyClient { + client_id: client_ibc0_on_ibc1.clone(), + header: MockHeader(Height(u64::from(ibc0_latest_height))).into(), + signer, + }; + let res = dispatch(&mut ctx_ibc1, ClientMsg::UpdateClient(msg.clone())); + assert_eq!(res.is_ok(), true); + } + } + } } diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index dd931e1787..a38ab7bb9a 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -70,6 +70,7 @@ mod tests { use crate::mock_client::state::{MockClientState, MockConsensusState}; use std::str::FromStr; use tendermint::block::Height; + use crate::ics03_connection::msgs::test_util::get_dummy_account_id; #[test] fn routing_dispatch() { @@ -88,6 +89,7 @@ mod tests { client_type: ClientType::Mock, client_state: AnyClientState::from(MockClientState(MockHeader(Height(42)))), consensus_state: AnyConsensusState::from(MockConsensusState(MockHeader(Height(42)))), + signer: get_dummy_account_id(), }; let envelope = ICS26Envelope::ICS2Msg(ClientMsg::CreateClient(msg)); diff --git a/modules/src/mock_client/state.rs b/modules/src/mock_client/state.rs index 2f9fbf6394..a652ca2bfb 100644 --- a/modules/src/mock_client/state.rs +++ b/modules/src/mock_client/state.rs @@ -156,7 +156,7 @@ impl ConsensusState for MockConsensusState { } fn height(&self) -> Height { - todo!() + self.0.height() } fn root(&self) -> &CommitmentRoot { From 53d21475b14968c1e529e13da29deada5d84175c Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Tue, 29 Sep 2020 08:30:28 +0200 Subject: [PATCH 2/8] Move and adapt the two chain client update test to ics26 --- modules/src/context_mock.rs | 23 +++- modules/src/ics02_client/client_def.rs | 1 - modules/src/ics02_client/context_mock.rs | 76 ++++++++---- modules/src/ics02_client/error.rs | 3 + .../src/ics02_client/handler/update_client.rs | 37 +----- modules/src/ics03_connection/context_mock.rs | 26 ++-- modules/src/ics26_routing/context_mock.rs | 48 ++++++-- modules/src/ics26_routing/handler.rs | 115 +++++++++++++++++- modules/src/mock_client/header.rs | 2 +- modules/src/mock_client/state.rs | 2 +- 10 files changed, 237 insertions(+), 96 deletions(-) diff --git a/modules/src/context_mock.rs b/modules/src/context_mock.rs index ded8438dce..e92050669d 100644 --- a/modules/src/context_mock.rs +++ b/modules/src/context_mock.rs @@ -1,23 +1,31 @@ use crate::context::{ChainKeeper, ChainReader, HistoricalInfo, SelfChainType, SelfHeader}; use crate::mock_client::header::MockHeader; use serde_derive::{Deserialize, Serialize}; +use std::cmp::min; use std::error::Error; use tendermint::block::Height; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)] pub struct MockChainContext { + /// Maximum size of the history pub max_size: usize, + /// Heighest height of the headers in the history pub latest: Height, + /// A list of `max_size` headers ordered by height pub history: Vec, } impl MockChainContext { - pub fn new(max_size: usize, n: Height) -> Self { + /// Creates a new mock chain with max_size number of headers up to height h + pub fn new(max_size: usize, h: Height) -> Self { + // number of headers to store, if h is 0 nothing is stored + let n = min(max_size as u64, h.value()); Self { max_size, - latest: n, - history: (0..n.value()) - .map(|i| MockHeader(Height(i).increment())) + latest: h, + history: (0..n) + .rev() + .map(|i| MockHeader(Height(h.value() - i))) .collect(), } } @@ -41,7 +49,7 @@ impl MockChainContext { pub fn add_header(&mut self, h: u64) { let mut new_h = h; if h == 0 { - new_h = h+1; + new_h = u64::from(self.latest.increment()); } self.store_historical_info( Height(new_h), @@ -146,6 +154,11 @@ mod tests { ctx: MockChainContext::new(3, Height(2)), args: [3, 4].to_vec(), }, + Test { + name: "Add with initial prune".to_string(), + ctx: MockChainContext::new(3, Height(10)), + args: [11].to_vec(), + }, Test { name: "Attempt to add non sequential headers".to_string(), ctx: MockChainContext::new(3, Height(2)), diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index bd249f28dd..39377a88f6 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -254,7 +254,6 @@ impl ConsensusState for AnyConsensusState { #[cfg(test)] AnyConsensusState::Mock(cs) => cs.height(), - } } diff --git a/modules/src/ics02_client/context_mock.rs b/modules/src/ics02_client/context_mock.rs index 914f352bea..48d6065f50 100644 --- a/modules/src/ics02_client/context_mock.rs +++ b/modules/src/ics02_client/context_mock.rs @@ -2,15 +2,13 @@ use crate::context_mock::MockChainContext; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::client_type::ClientType; use crate::ics02_client::context::{ClientKeeper, ClientReader}; -use crate::ics02_client::error::Error; +use crate::ics02_client::error::{Error, Kind}; +use crate::ics02_client::state::ConsensusState; use crate::ics24_host::identifier::ClientId; use crate::mock_client::header::MockHeader; use crate::mock_client::state::{MockClientRecord, MockClientState, MockConsensusState}; use std::collections::HashMap; use tendermint::block::Height; -use crate::ics02_client::handler::ClientResult::{CreateResult, UpdateResult}; -use crate::ics02_client::handler::ClientResult; -use crate::ics02_client::state::ConsensusState; /// A mock implementation of client context. This mocks (i.e., replaces) the functionality of /// a KV-store holding information related to the various IBC clients running on a chain. @@ -31,6 +29,13 @@ impl MockClientContext { clients: Default::default(), } } + pub fn chain_context(&self) -> &MockChainContext { + &self.chain_context + } + + pub fn set_chain_context(&mut self, chain_context: MockChainContext) { + self.chain_context = chain_context + } } impl Default for MockClientContext { @@ -114,41 +119,58 @@ impl ClientReader for MockClientContext { } impl ClientKeeper for MockClientContext { - fn store_client_result(&mut self, handler_res: ClientResult) -> Result<(), Error> { - match handler_res { - CreateResult(res) => { - self.with_client_type(&res.client_id, ClientType::Mock, res.consensus_state.height()); - } - UpdateResult(res) => { - self.with_client_consensus_state(&res.client_id, res.consensus_state.height()); - // Create a new block. - self.chain_context.add_header(0); - } - } - Ok(()) - } - fn store_client_type( &mut self, - _client_id: ClientId, - _client_type: ClientType, + client_id: ClientId, + client_type: ClientType, ) -> Result<(), Error> { - todo!() + let mut client_record = self.clients.entry(client_id).or_insert(MockClientRecord { + client_type, + consensus_states: Default::default(), + client_state: Default::default(), + }); + + client_record.client_type = client_type; + Ok(()) } fn store_client_state( &mut self, - _client_id: ClientId, - _client_state: AnyClientState, + client_id: ClientId, + client_state: AnyClientState, ) -> Result<(), Error> { - todo!() + match client_state { + AnyClientState::Mock(client_state) => { + let mut client_record = self.clients.entry(client_id).or_insert(MockClientRecord { + client_type: ClientType::Mock, + consensus_states: Default::default(), + client_state, + }); + client_record.client_state = client_state; + Ok(()) + } + _ => Err(Kind::BadClientState.into()), + } } fn store_consensus_state( &mut self, - _client_id: ClientId, - _consensus_state: AnyConsensusState, + client_id: ClientId, + consensus_state: AnyConsensusState, ) -> Result<(), Error> { - todo!() + match consensus_state { + AnyConsensusState::Mock(consensus_state) => { + let client_record = self.clients.entry(client_id).or_insert(MockClientRecord { + client_type: ClientType::Mock, + consensus_states: Default::default(), + client_state: Default::default(), + }); + client_record + .consensus_states + .insert(consensus_state.height(), consensus_state); + Ok(()) + } + _ => Err(Kind::BadClientState.into()), + } } } diff --git a/modules/src/ics02_client/error.rs b/modules/src/ics02_client/error.rs index 3be580296f..c2b6bfb9f6 100644 --- a/modules/src/ics02_client/error.rs +++ b/modules/src/ics02_client/error.rs @@ -48,6 +48,9 @@ pub enum Kind { #[error("mismatch between client and arguments types, expected: {0:?}")] ClientArgsTypeMismatch(ClientType), + + #[error("bad client state")] + BadClientState, } impl Kind { diff --git a/modules/src/ics02_client/handler/update_client.rs b/modules/src/ics02_client/handler/update_client.rs index 4613287cad..bac2842acc 100644 --- a/modules/src/ics02_client/handler/update_client.rs +++ b/modules/src/ics02_client/handler/update_client.rs @@ -1,6 +1,6 @@ use crate::handler::{HandlerOutput, HandlerResult}; use crate::ics02_client::client_def::{AnyClient, AnyClientState, AnyConsensusState, ClientDef}; -use crate::ics02_client::context::{ClientReader, ClientKeeper}; +use crate::ics02_client::context::{ClientKeeper, ClientReader}; use crate::ics02_client::error::{Error, Kind}; use crate::ics02_client::handler::{ClientEvent, ClientResult}; @@ -57,7 +57,6 @@ pub fn process( })) } - pub fn keep(keeper: &mut dyn ClientKeeper, result: UpdateClientResult) -> Result<(), Error> { keeper.store_client_result(ClientResult::UpdateResult(result)) } @@ -71,8 +70,6 @@ mod tests { use crate::ics02_client::context_mock::MockClientContext; use crate::ics03_connection::msgs::test_util::get_dummy_account_id; use crate::mock_client::header::MockHeader; - use crate::ics02_client::handler::dispatch; - use crate::ics02_client::msgs::ClientMsg; #[test] fn test_update_client_ok() { @@ -179,36 +176,4 @@ mod tests { } } } - #[test] - fn test_update_two_chains() { - let client_ibc1_on_ibc0: ClientId = "ibconeclient".parse().unwrap(); - let client_ibc0_on_ibc1: ClientId = "ibczeroclient".parse().unwrap(); - let signer = get_dummy_account_id(); - - let ibc0_start_height = 10; - let ibc1_start_height = 20; - let max_history_size = 3; - let num_iterations = 4; - - let mut ctx_ibc0 = MockClientContext::new(ibc0_start_height, max_history_size); - let mut ctx_ibc1 = MockClientContext::new(ibc1_start_height, max_history_size); - ctx_ibc0.with_client(&client_ibc1_on_ibc0, ClientType::Mock, Height(ibc1_start_height)); - ctx_ibc1.with_client(&client_ibc0_on_ibc1, ClientType::Mock, Height(ibc0_start_height)); - - ctx_ibc0.chain_context.add_header(ibc0_start_height+1); - - for _i in 0..num_iterations { - let ibc0_latest_height = ctx_ibc0.chain_context.latest; - let record_ibc0_on_ibc1 = ctx_ibc1.clients.get(&client_ibc0_on_ibc1).unwrap(); - if record_ibc0_on_ibc1.consensus_states.get(&ibc0_latest_height).is_none() { - let msg = MsgUpdateAnyClient { - client_id: client_ibc0_on_ibc1.clone(), - header: MockHeader(Height(u64::from(ibc0_latest_height))).into(), - signer, - }; - let res = dispatch(&mut ctx_ibc1, ClientMsg::UpdateClient(msg.clone())); - assert_eq!(res.is_ok(), true); - } - } - } } diff --git a/modules/src/ics03_connection/context_mock.rs b/modules/src/ics03_connection/context_mock.rs index 697d045e33..f54aaee3b9 100644 --- a/modules/src/ics03_connection/context_mock.rs +++ b/modules/src/ics03_connection/context_mock.rs @@ -13,7 +13,6 @@ use tendermint::block::Height; #[derive(Clone, Debug, Default)] pub struct MockConnectionContext { - chain_context: MockChainContext, client_context: MockClientContext, connections: HashMap, client_connections: HashMap, @@ -22,15 +21,22 @@ pub struct MockConnectionContext { impl MockConnectionContext { pub fn new(chain_height: u64, max_history_size: usize) -> Self { MockConnectionContext { - chain_context: MockChainContext::new(max_history_size, Height(chain_height)), - client_context: Default::default(), + client_context: MockClientContext::new(chain_height, max_history_size), connections: Default::default(), client_connections: Default::default(), } } + pub fn chain_context(&self) -> &MockChainContext { + &self.client_context.chain_context + } + + pub fn client_context(&self) -> &MockClientContext { + &self.client_context + } + pub fn with_client_state(self, client_id: &ClientId, latest_client_height: u64) -> Self { - let mut client_context = self.client_context.clone(); + let mut client_context = self.client_context().clone(); client_context.with_client_consensus_state(client_id, Height(latest_client_height)); Self { client_context, @@ -39,7 +45,7 @@ impl MockConnectionContext { } pub fn max_size(&self) -> usize { - self.chain_context.max_size() + self.chain_context().max_size() } pub fn add_connection(self, id: ConnectionId, end: ConnectionEnd) -> Self { @@ -58,16 +64,16 @@ impl ConnectionReader for MockConnectionContext { } fn fetch_client_state(&self, client_id: &ClientId) -> Option { - self.client_context.client_state(client_id) + self.client_context().client_state(client_id) } fn chain_current_height(&self) -> Height { - self.chain_context.latest + self.chain_context().latest } /// Returns the number of consensus state historical entries for the local chain. fn chain_consensus_states_history_size(&self) -> usize { - self.chain_context.max_size() + self.chain_context().max_size() } fn chain_type(&self) -> SelfChainType { @@ -83,11 +89,11 @@ impl ConnectionReader for MockConnectionContext { client_id: &ClientId, height: Height, ) -> Option { - self.client_context.consensus_state(client_id, height) + self.client_context().consensus_state(client_id, height) } fn fetch_self_consensus_state(&self, height: Height) -> Option { - let hi = self.chain_context.self_historical_info(height)?.header; + let hi = self.chain_context().self_historical_info(height)?.header; match hi { #[cfg(test)] SelfHeader::Mock(h) => Some(h.into()), diff --git a/modules/src/ics26_routing/context_mock.rs b/modules/src/ics26_routing/context_mock.rs index 9ba07c9566..e466634faa 100644 --- a/modules/src/ics26_routing/context_mock.rs +++ b/modules/src/ics26_routing/context_mock.rs @@ -1,4 +1,5 @@ use crate::context::SelfChainType; +use crate::context_mock::MockChainContext; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::client_type::ClientType; use crate::ics02_client::context::{ClientKeeper, ClientReader}; @@ -20,8 +21,20 @@ use tendermint::block::Height; /// of this mock type. #[derive(Clone, Debug, Default)] pub struct MockICS26Context { - client_context: MockClientContext, - connection_context: MockConnectionContext, + pub client_context: MockClientContext, + pub connection_context: MockConnectionContext, +} + +impl MockICS26Context { + pub fn client_context(&self) -> &MockClientContext { + &self.client_context + } + pub fn chain_context(&self) -> &MockChainContext { + &self.client_context.chain_context + } + pub fn set_chain_context(&mut self, chain_context: MockChainContext) { + self.client_context.chain_context = chain_context; + } } impl ICS26Context for MockICS26Context {} @@ -32,11 +45,11 @@ impl ConnectionReader for MockICS26Context { } fn fetch_client_state(&self, client_id: &ClientId) -> Option { - self.connection_context.fetch_client_state(client_id) + self.client_context().client_state(client_id) } fn chain_current_height(&self) -> Height { - self.connection_context.chain_current_height() + self.chain_context().latest } fn chain_consensus_states_history_size(&self) -> usize { @@ -77,15 +90,15 @@ impl ConnectionReader for MockICS26Context { impl ClientReader for MockICS26Context { fn client_type(&self, client_id: &ClientId) -> Option { - self.client_context.client_type(client_id) + self.client_context().client_type(client_id) } fn client_state(&self, client_id: &ClientId) -> Option { - self.client_context.client_state(client_id) + self.client_context().client_state(client_id) } fn consensus_state(&self, client_id: &ClientId, height: Height) -> Option { - self.client_context.consensus_state(client_id, height) + self.client_context().consensus_state(client_id, height) } } @@ -110,25 +123,34 @@ impl ConnectionKeeper for MockICS26Context { impl ClientKeeper for MockICS26Context { fn store_client_type( &mut self, - _client_id: ClientId, - _client_type: ClientType, + client_id: ClientId, + client_type: ClientType, ) -> Result<(), ICS2Error> { + let mut client_store = self.client_context().clone(); + client_store.store_client_type(client_id, client_type)?; + self.client_context = client_store; Ok(()) } fn store_client_state( &mut self, - _client_id: ClientId, - _client_state: AnyClientState, + client_id: ClientId, + client_state: AnyClientState, ) -> Result<(), ICS2Error> { + let mut client_store = self.client_context().clone(); + client_store.store_client_state(client_id, client_state)?; + self.client_context = client_store; Ok(()) } fn store_consensus_state( &mut self, - _client_id: ClientId, - _consensus_state: AnyConsensusState, + client_id: ClientId, + consensus_state: AnyConsensusState, ) -> Result<(), ICS2Error> { + let mut client_store = self.client_context().clone(); + client_store.store_consensus_state(client_id, consensus_state)?; + self.client_context = client_store; Ok(()) } } diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index a38ab7bb9a..7981f0d150 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -59,18 +59,129 @@ where #[cfg(test)] mod tests { + use crate::handler::HandlerOutput; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::client_type::ClientType; - use crate::ics02_client::msgs::{ClientMsg, MsgCreateAnyClient}; + use crate::ics02_client::context_mock::MockClientContext; + use crate::ics02_client::msgs::{ClientMsg, MsgCreateAnyClient, MsgUpdateAnyClient}; + use crate::ics02_client::state::ClientState; + use crate::ics03_connection::msgs::test_util::get_dummy_account_id; use crate::ics24_host::identifier::ClientId; use crate::ics26_routing::context_mock::MockICS26Context; + use crate::ics26_routing::error::{Error, Kind}; use crate::ics26_routing::handler::dispatch; use crate::ics26_routing::msgs::ICS26Envelope; use crate::mock_client::header::MockHeader; use crate::mock_client::state::{MockClientState, MockConsensusState}; use std::str::FromStr; use tendermint::block::Height; - use crate::ics03_connection::msgs::test_util::get_dummy_account_id; + + fn update_client_to_latest( + dest: &mut MockICS26Context, + src: &MockICS26Context, + client_id: &ClientId, + ) -> Result, Error> { + // Check if client for ibc0 on ibc1 has been updated to latest height: + // - query latest height on source chain + let src_latest_height = src.chain_context().latest; + // - query client state on destination chain + // (TODO - simulate relayer by "querying" the client state and get the latest height from there + // then check if that is smaller than the source latest height) + let dest_client = dest.client_context.clients.get(&client_id).unwrap(); + + // Does the client have a consensus state for the latest height? + if dest_client + .consensus_states + .get(&src_latest_height) + .is_none() + { + // No, client needs update. + let msg = MsgUpdateAnyClient { + client_id: client_id.clone(), + header: MockHeader(Height(u64::from(src_latest_height))).into(), + signer: get_dummy_account_id(), + }; + dispatch(dest, ICS26Envelope::ICS2Msg(ClientMsg::UpdateClient(msg))) + } else { + Err(Into::::into(Kind::HandlerRaisedError)) + } + } + + #[test] + fn test_update_two_chains() { + let client_on_ibc0_for_ibc1: ClientId = "ibconeclient".parse().unwrap(); + let client_on_ibc1_for_ibc0: ClientId = "ibczeroclient".parse().unwrap(); + + let ibc0_start_height = 11; + let ibc1_start_height = 20; + let max_history_size = 3; + let num_iterations = 4; + + // Create ibc0 context + let mut ctx_ibc0_clients = MockClientContext::new(ibc0_start_height, max_history_size); + ctx_ibc0_clients.with_client( + &client_on_ibc0_for_ibc1, + ClientType::Mock, + Height(ibc1_start_height), + ); + let mut ctx_ibc0 = MockICS26Context { + client_context: ctx_ibc0_clients, + connection_context: Default::default(), + }; + + // Create ibc1 context + let mut ctx_ibc1_clients = MockClientContext::new(ibc1_start_height, max_history_size); + ctx_ibc1_clients.with_client( + &client_on_ibc1_for_ibc0, + ClientType::Mock, + Height(ibc0_start_height - 1), + ); + let mut ctx_ibc1 = MockICS26Context { + client_context: ctx_ibc1_clients, + connection_context: Default::default(), + }; + + // Loop a number of times, create new blocks and ensure clients on chain are updated to latest height. + for _i in 0..num_iterations { + // Update client on ibc1 + let res = update_client_to_latest(&mut ctx_ibc1, &ctx_ibc0, &client_on_ibc1_for_ibc0); + assert!(res.is_ok()); + assert_eq!( + ctx_ibc1 + .client_context() + .clients + .get(&client_on_ibc1_for_ibc0) + .unwrap() + .client_state + .latest_height(), + ctx_ibc0.chain_context().latest + ); + + // Advance height on ibc1 + let mut chain_context = ctx_ibc1.chain_context().clone(); + chain_context.add_header(0); + ctx_ibc1.set_chain_context(chain_context); + + // Update client on ibc0 + let res = update_client_to_latest(&mut ctx_ibc0, &ctx_ibc1, &client_on_ibc0_for_ibc1); + assert!(res.is_ok()); + assert_eq!( + ctx_ibc0 + .client_context() + .clients + .get(&client_on_ibc0_for_ibc1) + .unwrap() + .client_state + .latest_height(), + ctx_ibc1.chain_context().latest + ); + + // Advance height on ibc0 + let mut chain_context = ctx_ibc0.chain_context().clone(); + chain_context.add_header(0); + ctx_ibc0.set_chain_context(chain_context); + } + } #[test] fn routing_dispatch() { diff --git a/modules/src/mock_client/header.rs b/modules/src/mock_client/header.rs index 0b6e1b1461..665d13ec35 100644 --- a/modules/src/mock_client/header.rs +++ b/modules/src/mock_client/header.rs @@ -9,7 +9,7 @@ use std::convert::TryFrom; use tendermint::block::Height; use tendermint_proto::DomainType; -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct MockHeader(pub Height); impl DomainType for MockHeader {} diff --git a/modules/src/mock_client/state.rs b/modules/src/mock_client/state.rs index a652ca2bfb..fe0f123565 100644 --- a/modules/src/mock_client/state.rs +++ b/modules/src/mock_client/state.rs @@ -34,7 +34,7 @@ pub struct MockClientRecord { /// A mock of a client state. For an example of a real structure that this mocks, you can see /// `ClientState` of ics07_tendermint/client_state.rs. /// TODO: `MockClientState` should evolve, at the very least needs a `is_frozen` boolean field. -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct MockClientState(pub MockHeader); impl DomainType for MockClientState {} From ad86205cc60a322f20914cbe37a6028f30695ff2 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Tue, 29 Sep 2020 23:16:04 +0200 Subject: [PATCH 3/8] Added basic ICS18 method + context mock for testing. Should fix for #216 (local part). --- modules/src/context_mock.rs | 2 +- modules/src/ics18_relayer/context.rs | 14 +++ modules/src/ics18_relayer/context_mock.rs | 63 ++++++++++ modules/src/ics18_relayer/error.rs | 21 ++++ modules/src/ics18_relayer/mod.rs | 10 ++ modules/src/ics18_relayer/utils.rs | 147 ++++++++++++++++++++++ modules/src/ics26_routing/context_mock.rs | 8 +- modules/src/ics26_routing/handler.rs | 16 ++- modules/src/ics26_routing/mod.rs | 2 +- modules/src/lib.rs | 2 + 10 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 modules/src/ics18_relayer/context.rs create mode 100644 modules/src/ics18_relayer/context_mock.rs create mode 100644 modules/src/ics18_relayer/error.rs create mode 100644 modules/src/ics18_relayer/mod.rs create mode 100644 modules/src/ics18_relayer/utils.rs diff --git a/modules/src/context_mock.rs b/modules/src/context_mock.rs index e92050669d..364bb3740a 100644 --- a/modules/src/context_mock.rs +++ b/modules/src/context_mock.rs @@ -9,7 +9,7 @@ use tendermint::block::Height; pub struct MockChainContext { /// Maximum size of the history pub max_size: usize, - /// Heighest height of the headers in the history + /// Highest height of the headers in the history pub latest: Height, /// A list of `max_size` headers ordered by height pub history: Vec, diff --git a/modules/src/ics18_relayer/context.rs b/modules/src/ics18_relayer/context.rs new file mode 100644 index 0000000000..041b750b8b --- /dev/null +++ b/modules/src/ics18_relayer/context.rs @@ -0,0 +1,14 @@ +use crate::ics02_client::client_def::AnyClientState; +use crate::ics24_host::identifier::ClientId; +use crate::Height; + +/// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to +/// relay packets between chains. This trait comprises the dependencies towards a single chain. +/// TODO -- eventually this trait should mirror the `Chain` trait. +pub trait ICS18Context { + /// Returns the latest height of the chain. + fn query_latest_height(&self) -> Height; + + /// MockClientState->latest_height() of this client + fn query_client_full_state(&self, client_id: &ClientId) -> Option; +} diff --git a/modules/src/ics18_relayer/context_mock.rs b/modules/src/ics18_relayer/context_mock.rs new file mode 100644 index 0000000000..b0761158f6 --- /dev/null +++ b/modules/src/ics18_relayer/context_mock.rs @@ -0,0 +1,63 @@ +use crate::ics02_client::client_def::AnyClientState; +use crate::ics02_client::client_type::ClientType; +use crate::ics02_client::context_mock::MockClientContext; +use crate::ics03_connection::context::ConnectionReader; +use crate::ics18_relayer::context::ICS18Context; +use crate::ics24_host::identifier::ClientId; +use crate::ics26_routing::context_mock::MockICS26Context; +use crate::Height; + +#[derive(Clone, Debug, Default)] +pub struct MockICS18Context { + /// Comprises an ICS26 context. Needed to be able to mock the routing of datagrams to a chain. + pub chain_routing_context: MockICS26Context, +} + +impl MockICS18Context { + /// Create a new ICS18 mock context. This function initializes the context to comprise a chain + /// with current height corresponding to `chain_height`, contain + pub fn new( + chain_height: Height, + max_hist_size: usize, + client_id: &ClientId, + client_height: Height, + ) -> MockICS18Context { + // Create the mock client context. + let mut client_ctx = MockClientContext::new(u64::from(chain_height), max_hist_size); + client_ctx.with_client(client_id, ClientType::Mock, client_height); + + Self { + // Wrap the client context in a ICS26 context, which we wrap in the ICS18 context. + chain_routing_context: MockICS26Context { + client_context: client_ctx, + connection_context: Default::default(), // This parameter is ignored for the purpose of this mock. + }, + } + } + + pub fn routing_context(&self) -> &MockICS26Context { + &self.chain_routing_context + } + + pub fn set_routing_context(&mut self, new_rc: MockICS26Context) { + self.chain_routing_context = new_rc + } + + pub fn advance_chain_height(&mut self) { + let mut underlying_chain_ctx = self.chain_routing_context.chain_context().clone(); + underlying_chain_ctx.add_header(0); + // Replace the chain context with the newer one. + self.chain_routing_context + .set_chain_context(underlying_chain_ctx) + } +} + +impl ICS18Context for MockICS18Context { + fn query_latest_height(&self) -> Height { + self.chain_routing_context.chain_current_height() + } + + fn query_client_full_state(&self, client_id: &ClientId) -> Option { + self.chain_routing_context.fetch_client_state(client_id) + } +} diff --git a/modules/src/ics18_relayer/error.rs b/modules/src/ics18_relayer/error.rs new file mode 100644 index 0000000000..6c7a419344 --- /dev/null +++ b/modules/src/ics18_relayer/error.rs @@ -0,0 +1,21 @@ +use crate::ics24_host::identifier::ClientId; +use crate::Height; +use anomaly::{BoxError, Context}; +use thiserror::Error; + +pub type Error = anomaly::Error; + +#[derive(Clone, Debug, Error, PartialEq, Eq)] +pub enum Kind { + #[error("client state on destination chain not found, (client id: {0})")] + ClientStateNotFound(ClientId), + + #[error("the client on destination chain is already up-to-date (client id: {0}, source height: {1}, dest height: {2})")] + ClientAlreadyUpToDate(ClientId, Height, Height), +} + +impl Kind { + pub fn context(self, source: impl Into) -> Context { + Context::new(self, Some(source.into())) + } +} diff --git a/modules/src/ics18_relayer/mod.rs b/modules/src/ics18_relayer/mod.rs new file mode 100644 index 0000000000..5de23b5ade --- /dev/null +++ b/modules/src/ics18_relayer/mod.rs @@ -0,0 +1,10 @@ +//! - ICS 18: Implementation of basic relayer functions. + +pub mod context; +pub mod error; + +#[cfg(test)] +pub mod utils; // Currently only used in tests. + +#[cfg(test)] +pub mod context_mock; diff --git a/modules/src/ics18_relayer/utils.rs b/modules/src/ics18_relayer/utils.rs new file mode 100644 index 0000000000..6ee71690d0 --- /dev/null +++ b/modules/src/ics18_relayer/utils.rs @@ -0,0 +1,147 @@ +use crate::ics02_client::msgs::{ClientMsg, MsgUpdateAnyClient}; +use crate::ics02_client::state::ClientState; +use crate::ics03_connection::msgs::test_util::get_dummy_account_id; +use crate::ics18_relayer::context::ICS18Context; +use crate::ics18_relayer::error::{Error, Kind}; +use crate::ics24_host::identifier::ClientId; +use crate::mock_client::header::MockHeader; + +pub fn create_client_update_datagram( + dest: &Ctx, + src: &Ctx, + client_id: &ClientId, +) -> Result +where + Ctx: ICS18Context, +{ + // Check if client for ibc0 on ibc1 has been updated to latest height: + // - query latest height on source chain + let src_latest_height = src.query_latest_height(); + // - query client state on destination chain + let dest_client_state = dest + .query_client_full_state(&client_id) + .ok_or_else(|| Kind::ClientStateNotFound(client_id.clone()))?; + + let dest_client_latest_height = dest_client_state.latest_height(); + + if dest_client_latest_height < src_latest_height { + // Client on destination chain needs an update. + let msg = MsgUpdateAnyClient { + client_id: client_id.clone(), + header: MockHeader(src_latest_height).into(), + signer: get_dummy_account_id(), + }; + Ok(ClientMsg::UpdateClient(msg)) + } else { + Err(Kind::ClientAlreadyUpToDate( + client_id.clone(), + src_latest_height, + dest_client_latest_height, + ) + .into()) + } +} + +#[cfg(test)] +mod tests { + use crate::ics02_client::state::ClientState; + use crate::ics18_relayer::context::ICS18Context; + use crate::ics18_relayer::context_mock::MockICS18Context; + use crate::ics18_relayer::utils::create_client_update_datagram; + use crate::ics24_host::identifier::ClientId; + use crate::ics26_routing::handler::dispatch; + use crate::ics26_routing::msgs::ICS26Envelope; + use std::str::FromStr; + use tendermint::block::Height; + + #[test] + /// This test was moved here from ICS 26. + /// Serves to test both ICS 26 `dispatch` & `create_client_update` function. + fn client_update_ping_pong() { + let chain_a_start_height = Height(11); + let chain_b_start_height = Height(20); + let client_on_b_for_a_height = Height(10); // Should be smaller than `chain_a_start_height` + let client_on_a_for_b_height = Height(19); // Should be smaller than `chain_b_start_height` + let max_history_size = 3; + let num_iterations = 4; + + let client_on_a_for_b = ClientId::from_str("ibconeclient").unwrap(); + let client_on_b_for_a = ClientId::from_str("ibczeroclient").unwrap(); + + // Create two mock contexts, one for each chain. + let mut ctx_a = MockICS18Context::new( + chain_a_start_height, + max_history_size, + &client_on_a_for_b, + client_on_a_for_b_height, + ); + let mut ctx_b = MockICS18Context::new( + chain_b_start_height, + max_history_size, + &client_on_b_for_a, + client_on_b_for_a_height, + ); + + for _i in 0..num_iterations { + // Update client on chain B. + let client_msg_b_res = + create_client_update_datagram(&ctx_b, &ctx_a, &client_on_b_for_a); + assert_eq!( + client_msg_b_res.is_ok(), + true, + "create_client_update failed for context destination {:?}, error: {:?}", + ctx_b, + client_msg_b_res + ); + let mut routing_context_b = ctx_b.routing_context().clone(); + let client_msg_b = client_msg_b_res.unwrap(); + let dispatch_res_b = + dispatch(&mut routing_context_b, ICS26Envelope::ICS2Msg(client_msg_b)); + assert!(dispatch_res_b.is_ok()); + ctx_b.set_routing_context(routing_context_b); // Replace the original routing context with the updated one. + + // Check if the update succeeded. + 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()); + + // Advance height on chain B. + ctx_b.advance_chain_height(); + + // Update client on chain A. + let client_msg_a_res = + create_client_update_datagram(&ctx_a, &ctx_b, &client_on_a_for_b); + assert_eq!( + client_msg_a_res.is_ok(), + true, + "create_client_update failed for context destination {:?}, error: {:?}", + ctx_a, + client_msg_a_res + ); + let mut routing_context_a = ctx_a.routing_context().clone(); + let client_msg_a = client_msg_a_res.unwrap(); + let dispatch_res_a = + dispatch(&mut routing_context_a, ICS26Envelope::ICS2Msg(client_msg_a)); + assert!(dispatch_res_a.is_ok()); + ctx_a.set_routing_context(routing_context_a); + + // Check if the update in the other direction succeeded. + 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()); + + // Advance height for chain A. + ctx_a.advance_chain_height(); + } + + // let msg = create_client_update(dest_chain_ctx); + // + // let res = update_client_to_latest(&mut ctx_ibc1, &ctx_a, &client_on_b_for_a); + } +} diff --git a/modules/src/ics26_routing/context_mock.rs b/modules/src/ics26_routing/context_mock.rs index e466634faa..1b1033e7cf 100644 --- a/modules/src/ics26_routing/context_mock.rs +++ b/modules/src/ics26_routing/context_mock.rs @@ -126,9 +126,11 @@ impl ClientKeeper for MockICS26Context { client_id: ClientId, client_type: ClientType, ) -> Result<(), ICS2Error> { - let mut client_store = self.client_context().clone(); - client_store.store_client_type(client_id, client_type)?; - self.client_context = client_store; + self.client_context + .store_client_type(client_id, client_type)?; + // let mut client_store = self.client_context().clone(); + // client_store.store_client_type(client_id, client_type)?; + // self.client_context = client_store; Ok(()) } diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index 7981f0d150..3151b97dad 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -83,12 +83,22 @@ mod tests { ) -> Result, Error> { // Check if client for ibc0 on ibc1 has been updated to latest height: // - query latest height on source chain + // TODO maybe: src.get_latest_height() let src_latest_height = src.chain_context().latest; // - query client state on destination chain // (TODO - simulate relayer by "querying" the client state and get the latest height from there - // then check if that is smaller than the source latest height) + // then check if that is smaller than the source latest height) + // TODO maybe: dest.query_client_state(client_id) let dest_client = dest.client_context.clients.get(&client_id).unwrap(); + // let dest_height = dest.query_client_full_state(client_id).latest_height()?; + // if dest_height > src_latest_height { + // weird + // } + // if dest_height < src_latest_height { + // return new Envelope + // } + // Does the client have a consensus state for the latest height? if dest_client .consensus_states @@ -109,8 +119,8 @@ mod tests { #[test] fn test_update_two_chains() { - let client_on_ibc0_for_ibc1: ClientId = "ibconeclient".parse().unwrap(); - let client_on_ibc1_for_ibc0: ClientId = "ibczeroclient".parse().unwrap(); + let client_on_ibc0_for_ibc1 = ClientId::from_str("ibconeclient").unwrap(); + let client_on_ibc1_for_ibc0 = ClientId::from_str("ibczeroclient").unwrap(); let ibc0_start_height = 11; let ibc1_start_height = 20; diff --git a/modules/src/ics26_routing/mod.rs b/modules/src/ics26_routing/mod.rs index 087b6a6d47..60e2dbd641 100644 --- a/modules/src/ics26_routing/mod.rs +++ b/modules/src/ics26_routing/mod.rs @@ -6,4 +6,4 @@ pub mod handler; pub mod msgs; #[cfg(test)] -mod context_mock; +pub mod context_mock; diff --git a/modules/src/lib.rs b/modules/src/lib.rs index 3b7c51747e..f8438cb4fe 100644 --- a/modules/src/lib.rs +++ b/modules/src/lib.rs @@ -17,6 +17,7 @@ //! - ICS 03: Connection //! - ICS 04: Channel //! - ICS 07: Tendermint Client +//! - ICS 18: Basic relayer functions //! - ICS 20: Fungible Token //! - ICS 23: Vector Commitment Scheme //! - ICS 24: Host Requirements @@ -29,6 +30,7 @@ pub mod ics02_client; pub mod ics03_connection; pub mod ics04_channel; pub mod ics07_tendermint; +pub mod ics18_relayer; pub mod ics20_fungible_token_transfer; pub mod ics23_commitment; pub mod ics24_host; From 11cad7a7afa4ee82e71dbb0838e9e1dc41d4ef87 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Wed, 30 Sep 2020 10:09:27 +0200 Subject: [PATCH 4/8] change the APIs to look closer to what the realyer will use --- modules/src/context.rs | 2 + modules/src/context_mock.rs | 12 +++- modules/src/ics18_relayer/context.rs | 9 ++- modules/src/ics18_relayer/context_mock.rs | 21 +++++- modules/src/ics18_relayer/error.rs | 6 ++ modules/src/ics18_relayer/utils.rs | 82 ++++++++++++----------- 6 files changed, 89 insertions(+), 43 deletions(-) diff --git a/modules/src/context.rs b/modules/src/context.rs index 49b3527169..2866d4c496 100644 --- a/modules/src/context.rs +++ b/modules/src/context.rs @@ -2,6 +2,7 @@ use crate::ics07_tendermint; use serde_derive::{Deserialize, Serialize}; use tendermint::block::Height; +use crate::ics02_client::client_def::AnyHeader; #[cfg(test)] use { crate::ics02_client::client_def::AnyConsensusState, crate::mock_client::header::MockHeader, @@ -37,6 +38,7 @@ impl From for AnyConsensusState { pub trait ChainReader { fn chain_type(&self) -> SelfChainType; fn self_historical_info(&self, height: Height) -> Option; + fn header(&self, height: Height) -> Option; } pub trait ChainKeeper { diff --git a/modules/src/context_mock.rs b/modules/src/context_mock.rs index 364bb3740a..b640aab223 100644 --- a/modules/src/context_mock.rs +++ b/modules/src/context_mock.rs @@ -1,4 +1,5 @@ use crate::context::{ChainKeeper, ChainReader, HistoricalInfo, SelfChainType, SelfHeader}; +use crate::ics02_client::client_def::AnyHeader; use crate::mock_client::header::MockHeader; use serde_derive::{Deserialize, Serialize}; use std::cmp::min; @@ -99,10 +100,19 @@ impl ChainReader for MockChainContext { None } else { Some(HistoricalInfo { - header: SelfHeader::Mock(self.history[h - l]), + header: SelfHeader::Mock(self.history[self.max_size + l - h - 1]), }) } } + + fn header(&self, height: Height) -> Option { + let hi = self.self_historical_info(height)?.header; + match hi { + #[cfg(test)] + SelfHeader::Mock(h) => Some(h.into()), + _ => None, + } + } } impl ChainKeeper for MockChainContext { diff --git a/modules/src/ics18_relayer/context.rs b/modules/src/ics18_relayer/context.rs index 041b750b8b..314016dbd7 100644 --- a/modules/src/ics18_relayer/context.rs +++ b/modules/src/ics18_relayer/context.rs @@ -1,5 +1,8 @@ -use crate::ics02_client::client_def::AnyClientState; +use crate::handler::HandlerOutput; +use crate::ics02_client::client_def::{AnyClientState, AnyHeader}; +use crate::ics18_relayer::error::Error; use crate::ics24_host::identifier::ClientId; +use crate::ics26_routing::msgs::ICS26Envelope; use crate::Height; /// Trait capturing all dependencies (i.e., the context) which algorithms in ICS18 require to @@ -11,4 +14,8 @@ pub trait ICS18Context { /// MockClientState->latest_height() of this client fn query_client_full_state(&self, client_id: &ClientId) -> Option; + + fn query_latest_header(&self) -> Option; + + fn send(&mut self, msg: ICS26Envelope) -> Result, Error>; } diff --git a/modules/src/ics18_relayer/context_mock.rs b/modules/src/ics18_relayer/context_mock.rs index b0761158f6..ab5cedf447 100644 --- a/modules/src/ics18_relayer/context_mock.rs +++ b/modules/src/ics18_relayer/context_mock.rs @@ -1,10 +1,15 @@ -use crate::ics02_client::client_def::AnyClientState; +use crate::context::ChainReader; +use crate::handler::HandlerOutput; +use crate::ics02_client::client_def::{AnyClientState, AnyHeader}; use crate::ics02_client::client_type::ClientType; use crate::ics02_client::context_mock::MockClientContext; use crate::ics03_connection::context::ConnectionReader; use crate::ics18_relayer::context::ICS18Context; +use crate::ics18_relayer::error::{Error, Kind}; use crate::ics24_host::identifier::ClientId; use crate::ics26_routing::context_mock::MockICS26Context; +use crate::ics26_routing::handler::dispatch; +use crate::ics26_routing::msgs::ICS26Envelope; use crate::Height; #[derive(Clone, Debug, Default)] @@ -57,7 +62,21 @@ impl ICS18Context for MockICS18Context { self.chain_routing_context.chain_current_height() } + fn query_latest_header(&self) -> Option { + let latest_height = self.chain_routing_context.chain_current_height(); + self.chain_routing_context + .chain_context() + .header(latest_height) + } + fn query_client_full_state(&self, client_id: &ClientId) -> Option { self.chain_routing_context.fetch_client_state(client_id) } + + fn send(&mut self, msg: ICS26Envelope) -> Result, Error> { + let mut rctx = self.routing_context().clone(); + let res = dispatch(&mut rctx, msg).map_err(|e| Kind::TransactionFailed.context(e))?; + self.set_routing_context(rctx); + Ok(res) + } } diff --git a/modules/src/ics18_relayer/error.rs b/modules/src/ics18_relayer/error.rs index 6c7a419344..ca51ee5fed 100644 --- a/modules/src/ics18_relayer/error.rs +++ b/modules/src/ics18_relayer/error.rs @@ -12,6 +12,12 @@ pub enum Kind { #[error("the client on destination chain is already up-to-date (client id: {0}, source height: {1}, dest height: {2})")] ClientAlreadyUpToDate(ClientId, Height, Height), + + #[error("the client on destination chain is at a higher height (client id: {0}, source height: {1}, dest height: {2})")] + ClientAtHeigherHeight(ClientId, Height, Height), + + #[error("transaction failed")] + TransactionFailed, } impl Kind { diff --git a/modules/src/ics18_relayer/utils.rs b/modules/src/ics18_relayer/utils.rs index 6ee71690d0..598951a98f 100644 --- a/modules/src/ics18_relayer/utils.rs +++ b/modules/src/ics18_relayer/utils.rs @@ -1,22 +1,21 @@ +use crate::ics02_client::client_def::AnyHeader; +use crate::ics02_client::header::Header; use crate::ics02_client::msgs::{ClientMsg, MsgUpdateAnyClient}; use crate::ics02_client::state::ClientState; use crate::ics03_connection::msgs::test_util::get_dummy_account_id; use crate::ics18_relayer::context::ICS18Context; use crate::ics18_relayer::error::{Error, Kind}; use crate::ics24_host::identifier::ClientId; -use crate::mock_client::header::MockHeader; pub fn create_client_update_datagram( dest: &Ctx, - src: &Ctx, client_id: &ClientId, + header: AnyHeader, ) -> Result where Ctx: ICS18Context, { // Check if client for ibc0 on ibc1 has been updated to latest height: - // - query latest height on source chain - let src_latest_height = src.query_latest_height(); // - query client state on destination chain let dest_client_state = dest .query_client_full_state(&client_id) @@ -24,22 +23,30 @@ where let dest_client_latest_height = dest_client_state.latest_height(); - if dest_client_latest_height < src_latest_height { - // Client on destination chain needs an update. - let msg = MsgUpdateAnyClient { - client_id: client_id.clone(), - header: MockHeader(src_latest_height).into(), - signer: get_dummy_account_id(), - }; - Ok(ClientMsg::UpdateClient(msg)) - } else { - Err(Kind::ClientAlreadyUpToDate( + if header.height() == dest_client_latest_height { + return Err(Kind::ClientAlreadyUpToDate( client_id.clone(), - src_latest_height, + header.height(), dest_client_latest_height, ) - .into()) - } + .into()); + }; + + if dest_client_latest_height > header.height() { + return Err(Kind::ClientAtHeigherHeight( + client_id.clone(), + header.height(), + dest_client_latest_height, + ) + .into()); + }; + + // Client on destination chain can be updated. + Ok(ClientMsg::UpdateClient(MsgUpdateAnyClient { + client_id: client_id.clone(), + header, + signer: get_dummy_account_id(), + })) } #[cfg(test)] @@ -49,7 +56,6 @@ mod tests { use crate::ics18_relayer::context_mock::MockICS18Context; use crate::ics18_relayer::utils::create_client_update_datagram; use crate::ics24_host::identifier::ClientId; - use crate::ics26_routing::handler::dispatch; use crate::ics26_routing::msgs::ICS26Envelope; use std::str::FromStr; use tendermint::block::Height; @@ -61,7 +67,7 @@ mod tests { let chain_a_start_height = Height(11); let chain_b_start_height = Height(20); let client_on_b_for_a_height = Height(10); // Should be smaller than `chain_a_start_height` - let client_on_a_for_b_height = Height(19); // Should be smaller than `chain_b_start_height` + let client_on_a_for_b_height = Height(20); // Should be smaller than `chain_b_start_height` let max_history_size = 3; let num_iterations = 4; @@ -83,9 +89,11 @@ mod tests { ); for _i in 0..num_iterations { - // Update client on chain B. + // Update client on chain B to latest height of A. + // - create the client update message with the latest header from A + let a_latest_header = ctx_a.query_latest_header().unwrap(); let client_msg_b_res = - create_client_update_datagram(&ctx_b, &ctx_a, &client_on_b_for_a); + create_client_update_datagram(&ctx_b, &client_on_b_for_a, a_latest_header); assert_eq!( client_msg_b_res.is_ok(), true, @@ -93,27 +101,27 @@ mod tests { ctx_b, client_msg_b_res ); - let mut routing_context_b = ctx_b.routing_context().clone(); let client_msg_b = client_msg_b_res.unwrap(); - let dispatch_res_b = - dispatch(&mut routing_context_b, ICS26Envelope::ICS2Msg(client_msg_b)); - assert!(dispatch_res_b.is_ok()); - ctx_b.set_routing_context(routing_context_b); // Replace the original routing context with the updated one. + + // - send the message to B + let dispatch_res_b = ctx_b.send(ICS26Envelope::ICS2Msg(client_msg_b)); // Check if the update succeeded. + assert!(dispatch_res_b.is_ok()); 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()); // Advance height on chain B. ctx_b.advance_chain_height(); - // Update client on chain A. + // Update client on chain B to latest height of B. + // - create the client update message with the latest header from B + let b_latest_header = ctx_b.query_latest_header().unwrap(); let client_msg_a_res = - create_client_update_datagram(&ctx_a, &ctx_b, &client_on_a_for_b); + create_client_update_datagram(&ctx_a, &client_on_a_for_b, b_latest_header); assert_eq!( client_msg_a_res.is_ok(), true, @@ -121,27 +129,21 @@ mod tests { ctx_a, client_msg_a_res ); - let mut routing_context_a = ctx_a.routing_context().clone(); let client_msg_a = client_msg_a_res.unwrap(); - let dispatch_res_a = - dispatch(&mut routing_context_a, ICS26Envelope::ICS2Msg(client_msg_a)); - assert!(dispatch_res_a.is_ok()); - ctx_a.set_routing_context(routing_context_a); - // Check if the update in the other direction succeeded. + // - send the message to A + let dispatch_res_a = ctx_a.send(ICS26Envelope::ICS2Msg(client_msg_a)); + + // Check if the update succeeded. + assert!(dispatch_res_a.is_ok()); 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()); // Advance height for chain A. ctx_a.advance_chain_height(); } - - // let msg = create_client_update(dest_chain_ctx); - // - // let res = update_client_to_latest(&mut ctx_ibc1, &ctx_a, &client_on_b_for_a); } } From fa0556e7d43ca9bdccef29b33735f97773e800d3 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Wed, 30 Sep 2020 11:37:07 +0200 Subject: [PATCH 5/8] move the context update and chain advance in new .recv() --- modules/src/context.rs | 8 ++++---- modules/src/context_mock.rs | 11 ++++++++++- modules/src/ics18_relayer/context_mock.rs | 14 ++++++++++---- modules/src/ics18_relayer/utils.rs | 6 ------ modules/src/ics26_routing/context_mock.rs | 6 ++---- 5 files changed, 26 insertions(+), 19 deletions(-) diff --git a/modules/src/context.rs b/modules/src/context.rs index 2866d4c496..05fa2645fa 100644 --- a/modules/src/context.rs +++ b/modules/src/context.rs @@ -2,12 +2,11 @@ use crate::ics07_tendermint; use serde_derive::{Deserialize, Serialize}; use tendermint::block::Height; +use crate::ics02_client::client_def::AnyConsensusState; use crate::ics02_client::client_def::AnyHeader; + #[cfg(test)] -use { - crate::ics02_client::client_def::AnyConsensusState, crate::mock_client::header::MockHeader, - crate::mock_client::state::MockConsensusState, -}; +use {crate::mock_client::header::MockHeader, crate::mock_client::state::MockConsensusState}; #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum SelfChainType { @@ -39,6 +38,7 @@ pub trait ChainReader { fn chain_type(&self) -> SelfChainType; fn self_historical_info(&self, height: Height) -> Option; fn header(&self, height: Height) -> Option; + fn fetch_self_consensus_state(&self, height: Height) -> Option; } pub trait ChainKeeper { diff --git a/modules/src/context_mock.rs b/modules/src/context_mock.rs index b640aab223..c249e5ff9b 100644 --- a/modules/src/context_mock.rs +++ b/modules/src/context_mock.rs @@ -1,5 +1,5 @@ use crate::context::{ChainKeeper, ChainReader, HistoricalInfo, SelfChainType, SelfHeader}; -use crate::ics02_client::client_def::AnyHeader; +use crate::ics02_client::client_def::{AnyConsensusState, AnyHeader}; use crate::mock_client::header::MockHeader; use serde_derive::{Deserialize, Serialize}; use std::cmp::min; @@ -113,6 +113,15 @@ impl ChainReader for MockChainContext { _ => None, } } + + fn fetch_self_consensus_state(&self, height: Height) -> Option { + let hi = self.self_historical_info(height)?.header; + match hi { + #[cfg(test)] + SelfHeader::Mock(h) => Some(h.into()), + _ => None, + } + } } impl ChainKeeper for MockChainContext { diff --git a/modules/src/ics18_relayer/context_mock.rs b/modules/src/ics18_relayer/context_mock.rs index ab5cedf447..2d52cdb241 100644 --- a/modules/src/ics18_relayer/context_mock.rs +++ b/modules/src/ics18_relayer/context_mock.rs @@ -55,6 +55,15 @@ impl MockICS18Context { self.chain_routing_context .set_chain_context(underlying_chain_ctx) } + + fn recv(&mut self, msg: ICS26Envelope) -> Result, Error> { + let mut rctx = self.routing_context().clone(); + let res = dispatch(&mut rctx, msg).map_err(|e| Kind::TransactionFailed.context(e))?; + self.set_routing_context(rctx); + // Create new block + self.advance_chain_height(); + Ok(res) + } } impl ICS18Context for MockICS18Context { @@ -74,9 +83,6 @@ impl ICS18Context for MockICS18Context { } fn send(&mut self, msg: ICS26Envelope) -> Result, Error> { - let mut rctx = self.routing_context().clone(); - let res = dispatch(&mut rctx, msg).map_err(|e| Kind::TransactionFailed.context(e))?; - self.set_routing_context(rctx); - Ok(res) + self.recv(msg) } } diff --git a/modules/src/ics18_relayer/utils.rs b/modules/src/ics18_relayer/utils.rs index 598951a98f..53324ab893 100644 --- a/modules/src/ics18_relayer/utils.rs +++ b/modules/src/ics18_relayer/utils.rs @@ -114,9 +114,6 @@ mod tests { .latest_height(); assert_eq!(client_height_b, ctx_a.query_latest_height()); - // Advance height on chain B. - ctx_b.advance_chain_height(); - // Update client on chain B to latest height of B. // - create the client update message with the latest header from B let b_latest_header = ctx_b.query_latest_header().unwrap(); @@ -141,9 +138,6 @@ mod tests { .unwrap() .latest_height(); assert_eq!(client_height_a, ctx_b.query_latest_height()); - - // Advance height for chain A. - ctx_a.advance_chain_height(); } } } diff --git a/modules/src/ics26_routing/context_mock.rs b/modules/src/ics26_routing/context_mock.rs index 1b1033e7cf..ee9322a3cd 100644 --- a/modules/src/ics26_routing/context_mock.rs +++ b/modules/src/ics26_routing/context_mock.rs @@ -53,8 +53,7 @@ impl ConnectionReader for MockICS26Context { } fn chain_consensus_states_history_size(&self) -> usize { - self.connection_context - .chain_consensus_states_history_size() + self.chain_context().max_size() } fn chain_type(&self) -> SelfChainType { @@ -70,8 +69,7 @@ impl ConnectionReader for MockICS26Context { client_id: &ClientId, height: Height, ) -> Option { - self.connection_context - .fetch_client_consensus_state(client_id, height) + self.client_context().consensus_state(client_id, height) } fn fetch_self_consensus_state(&self, height: Height) -> Option { From 31d76b3a132ce709be97a7d7302378a2d87d5f6a Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 30 Sep 2020 12:36:04 +0200 Subject: [PATCH 6/8] Cleanup for ICS26 and ICS18 --- modules/src/ics02_client/error.rs | 2 +- modules/src/ics18_relayer/context.rs | 5 +- modules/src/ics18_relayer/context_mock.rs | 9 +- modules/src/ics18_relayer/error.rs | 2 +- modules/src/ics18_relayer/utils.rs | 19 ++-- modules/src/ics26_routing/handler.rs | 123 +--------------------- 6 files changed, 23 insertions(+), 137 deletions(-) diff --git a/modules/src/ics02_client/error.rs b/modules/src/ics02_client/error.rs index c2b6bfb9f6..6bb89d2f79 100644 --- a/modules/src/ics02_client/error.rs +++ b/modules/src/ics02_client/error.rs @@ -49,7 +49,7 @@ pub enum Kind { #[error("mismatch between client and arguments types, expected: {0:?}")] ClientArgsTypeMismatch(ClientType), - #[error("bad client state")] + #[error("bad client state (expected mock client)")] BadClientState, } diff --git a/modules/src/ics18_relayer/context.rs b/modules/src/ics18_relayer/context.rs index 314016dbd7..b2ff2e5179 100644 --- a/modules/src/ics18_relayer/context.rs +++ b/modules/src/ics18_relayer/context.rs @@ -12,10 +12,13 @@ pub trait ICS18Context { /// Returns the latest height of the chain. fn query_latest_height(&self) -> Height; - /// MockClientState->latest_height() of this client + /// Returns this client state for the given `client_id` on this chain. fn query_client_full_state(&self, client_id: &ClientId) -> Option; + /// Returns the most advanced header of this chain. fn query_latest_header(&self) -> Option; + /// Interface that the relayer uses to submit a datagram to this chain. + /// TODO: Unclear what should be the return type of this. fn send(&mut self, msg: ICS26Envelope) -> Result, Error>; } diff --git a/modules/src/ics18_relayer/context_mock.rs b/modules/src/ics18_relayer/context_mock.rs index 2d52cdb241..62151a9aaf 100644 --- a/modules/src/ics18_relayer/context_mock.rs +++ b/modules/src/ics18_relayer/context_mock.rs @@ -56,6 +56,7 @@ impl MockICS18Context { .set_chain_context(underlying_chain_ctx) } + /// Internal interface of the context, for consuming (on the modules side) a datagram. fn recv(&mut self, msg: ICS26Envelope) -> Result, Error> { let mut rctx = self.routing_context().clone(); let res = dispatch(&mut rctx, msg).map_err(|e| Kind::TransactionFailed.context(e))?; @@ -71,6 +72,10 @@ impl ICS18Context for MockICS18Context { self.chain_routing_context.chain_current_height() } + fn query_client_full_state(&self, client_id: &ClientId) -> Option { + self.chain_routing_context.fetch_client_state(client_id) + } + fn query_latest_header(&self) -> Option { let latest_height = self.chain_routing_context.chain_current_height(); self.chain_routing_context @@ -78,10 +83,6 @@ impl ICS18Context for MockICS18Context { .header(latest_height) } - fn query_client_full_state(&self, client_id: &ClientId) -> Option { - self.chain_routing_context.fetch_client_state(client_id) - } - fn send(&mut self, msg: ICS26Envelope) -> Result, Error> { self.recv(msg) } diff --git a/modules/src/ics18_relayer/error.rs b/modules/src/ics18_relayer/error.rs index ca51ee5fed..78f18a03fd 100644 --- a/modules/src/ics18_relayer/error.rs +++ b/modules/src/ics18_relayer/error.rs @@ -16,7 +16,7 @@ pub enum Kind { #[error("the client on destination chain is at a higher height (client id: {0}, source height: {1}, dest height: {2})")] ClientAtHeigherHeight(ClientId, Height, Height), - #[error("transaction failed")] + #[error("transaction processing by modules failed")] TransactionFailed, } diff --git a/modules/src/ics18_relayer/utils.rs b/modules/src/ics18_relayer/utils.rs index 53324ab893..db1ce23c98 100644 --- a/modules/src/ics18_relayer/utils.rs +++ b/modules/src/ics18_relayer/utils.rs @@ -7,10 +7,12 @@ use crate::ics18_relayer::context::ICS18Context; use crate::ics18_relayer::error::{Error, Kind}; use crate::ics24_host::identifier::ClientId; +/// Creates a `ClientMsg::UpdateClient` for a client with id `client_id` running on the `dest` +/// context, assuming that the latest header on the source context is `src_header`. pub fn create_client_update_datagram( dest: &Ctx, client_id: &ClientId, - header: AnyHeader, + src_header: AnyHeader, ) -> Result where Ctx: ICS18Context, @@ -23,19 +25,19 @@ where let dest_client_latest_height = dest_client_state.latest_height(); - if header.height() == dest_client_latest_height { + if src_header.height() == dest_client_latest_height { return Err(Kind::ClientAlreadyUpToDate( client_id.clone(), - header.height(), + src_header.height(), dest_client_latest_height, ) .into()); }; - if dest_client_latest_height > header.height() { + if dest_client_latest_height > src_header.height() { return Err(Kind::ClientAtHeigherHeight( client_id.clone(), - header.height(), + src_header.height(), dest_client_latest_height, ) .into()); @@ -44,7 +46,7 @@ where // Client on destination chain can be updated. Ok(ClientMsg::UpdateClient(MsgUpdateAnyClient { client_id: client_id.clone(), - header, + header: src_header, signer: get_dummy_account_id(), })) } @@ -61,8 +63,9 @@ mod tests { use tendermint::block::Height; #[test] - /// This test was moved here from ICS 26. - /// Serves to test both ICS 26 `dispatch` & `create_client_update` function. + /// Serves to test both ICS 26 `dispatch` & `create_client_update_datagram` function. + /// Implements a "ping pong" of client update messages, so that two chains repeatedly + /// process a client update message and update their height in succession. fn client_update_ping_pong() { let chain_a_start_height = Height(11); let chain_b_start_height = Height(20); diff --git a/modules/src/ics26_routing/handler.rs b/modules/src/ics26_routing/handler.rs index 3151b97dad..90b62eaa26 100644 --- a/modules/src/ics26_routing/handler.rs +++ b/modules/src/ics26_routing/handler.rs @@ -59,16 +59,12 @@ where #[cfg(test)] mod tests { - use crate::handler::HandlerOutput; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::client_type::ClientType; - use crate::ics02_client::context_mock::MockClientContext; - use crate::ics02_client::msgs::{ClientMsg, MsgCreateAnyClient, MsgUpdateAnyClient}; - use crate::ics02_client::state::ClientState; + use crate::ics02_client::msgs::{ClientMsg, MsgCreateAnyClient}; use crate::ics03_connection::msgs::test_util::get_dummy_account_id; use crate::ics24_host::identifier::ClientId; use crate::ics26_routing::context_mock::MockICS26Context; - use crate::ics26_routing::error::{Error, Kind}; use crate::ics26_routing::handler::dispatch; use crate::ics26_routing::msgs::ICS26Envelope; use crate::mock_client::header::MockHeader; @@ -76,123 +72,6 @@ mod tests { use std::str::FromStr; use tendermint::block::Height; - fn update_client_to_latest( - dest: &mut MockICS26Context, - src: &MockICS26Context, - client_id: &ClientId, - ) -> Result, Error> { - // Check if client for ibc0 on ibc1 has been updated to latest height: - // - query latest height on source chain - // TODO maybe: src.get_latest_height() - let src_latest_height = src.chain_context().latest; - // - query client state on destination chain - // (TODO - simulate relayer by "querying" the client state and get the latest height from there - // then check if that is smaller than the source latest height) - // TODO maybe: dest.query_client_state(client_id) - let dest_client = dest.client_context.clients.get(&client_id).unwrap(); - - // let dest_height = dest.query_client_full_state(client_id).latest_height()?; - // if dest_height > src_latest_height { - // weird - // } - // if dest_height < src_latest_height { - // return new Envelope - // } - - // Does the client have a consensus state for the latest height? - if dest_client - .consensus_states - .get(&src_latest_height) - .is_none() - { - // No, client needs update. - let msg = MsgUpdateAnyClient { - client_id: client_id.clone(), - header: MockHeader(Height(u64::from(src_latest_height))).into(), - signer: get_dummy_account_id(), - }; - dispatch(dest, ICS26Envelope::ICS2Msg(ClientMsg::UpdateClient(msg))) - } else { - Err(Into::::into(Kind::HandlerRaisedError)) - } - } - - #[test] - fn test_update_two_chains() { - let client_on_ibc0_for_ibc1 = ClientId::from_str("ibconeclient").unwrap(); - let client_on_ibc1_for_ibc0 = ClientId::from_str("ibczeroclient").unwrap(); - - let ibc0_start_height = 11; - let ibc1_start_height = 20; - let max_history_size = 3; - let num_iterations = 4; - - // Create ibc0 context - let mut ctx_ibc0_clients = MockClientContext::new(ibc0_start_height, max_history_size); - ctx_ibc0_clients.with_client( - &client_on_ibc0_for_ibc1, - ClientType::Mock, - Height(ibc1_start_height), - ); - let mut ctx_ibc0 = MockICS26Context { - client_context: ctx_ibc0_clients, - connection_context: Default::default(), - }; - - // Create ibc1 context - let mut ctx_ibc1_clients = MockClientContext::new(ibc1_start_height, max_history_size); - ctx_ibc1_clients.with_client( - &client_on_ibc1_for_ibc0, - ClientType::Mock, - Height(ibc0_start_height - 1), - ); - let mut ctx_ibc1 = MockICS26Context { - client_context: ctx_ibc1_clients, - connection_context: Default::default(), - }; - - // Loop a number of times, create new blocks and ensure clients on chain are updated to latest height. - for _i in 0..num_iterations { - // Update client on ibc1 - let res = update_client_to_latest(&mut ctx_ibc1, &ctx_ibc0, &client_on_ibc1_for_ibc0); - assert!(res.is_ok()); - assert_eq!( - ctx_ibc1 - .client_context() - .clients - .get(&client_on_ibc1_for_ibc0) - .unwrap() - .client_state - .latest_height(), - ctx_ibc0.chain_context().latest - ); - - // Advance height on ibc1 - let mut chain_context = ctx_ibc1.chain_context().clone(); - chain_context.add_header(0); - ctx_ibc1.set_chain_context(chain_context); - - // Update client on ibc0 - let res = update_client_to_latest(&mut ctx_ibc0, &ctx_ibc1, &client_on_ibc0_for_ibc1); - assert!(res.is_ok()); - assert_eq!( - ctx_ibc0 - .client_context() - .clients - .get(&client_on_ibc0_for_ibc1) - .unwrap() - .client_state - .latest_height(), - ctx_ibc1.chain_context().latest - ); - - // Advance height on ibc0 - let mut chain_context = ctx_ibc0.chain_context().clone(); - chain_context.add_header(0); - ctx_ibc0.set_chain_context(chain_context); - } - } - #[test] fn routing_dispatch() { struct DispatchParams { From 5489c2bbdf57a9f1cdad3fe9c3fd7da57e948412 Mon Sep 17 00:00:00 2001 From: Adi Seredinschi Date: Wed, 30 Sep 2020 12:43:31 +0200 Subject: [PATCH 7/8] Fix for awkward ClientKeeper setters --- modules/src/ics26_routing/context_mock.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/modules/src/ics26_routing/context_mock.rs b/modules/src/ics26_routing/context_mock.rs index ee9322a3cd..5ab77348c2 100644 --- a/modules/src/ics26_routing/context_mock.rs +++ b/modules/src/ics26_routing/context_mock.rs @@ -126,9 +126,6 @@ impl ClientKeeper for MockICS26Context { ) -> Result<(), ICS2Error> { self.client_context .store_client_type(client_id, client_type)?; - // let mut client_store = self.client_context().clone(); - // client_store.store_client_type(client_id, client_type)?; - // self.client_context = client_store; Ok(()) } @@ -137,9 +134,8 @@ impl ClientKeeper for MockICS26Context { client_id: ClientId, client_state: AnyClientState, ) -> Result<(), ICS2Error> { - let mut client_store = self.client_context().clone(); - client_store.store_client_state(client_id, client_state)?; - self.client_context = client_store; + self.client_context + .store_client_state(client_id, client_state)?; Ok(()) } @@ -148,9 +144,8 @@ impl ClientKeeper for MockICS26Context { client_id: ClientId, consensus_state: AnyConsensusState, ) -> Result<(), ICS2Error> { - let mut client_store = self.client_context().clone(); - client_store.store_consensus_state(client_id, consensus_state)?; - self.client_context = client_store; + self.client_context + .store_consensus_state(client_id, consensus_state)?; Ok(()) } } From 1a133132d3a189b99d3aa357d86c4b1a436655f0 Mon Sep 17 00:00:00 2001 From: Anca Zamfir Date: Wed, 30 Sep 2020 13:35:36 +0200 Subject: [PATCH 8/8] fix the chain mock context tests --- modules/src/context_mock.rs | 41 ++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/modules/src/context_mock.rs b/modules/src/context_mock.rs index c249e5ff9b..dd4c695417 100644 --- a/modules/src/context_mock.rs +++ b/modules/src/context_mock.rs @@ -61,28 +61,27 @@ impl MockChainContext { } pub fn validate(&self) -> Result<(), Box> { - // TODO + // check that the number of entries is not higher than max_size + if self.history.len() > self.max_size { + return Err("too many entries".to_string().into()); + } + + // get the highers header + let lh = self.history[self.history.len() - 1]; + // check latest is properly updated with highest header height + if lh.height() != self.latest { + return Err("latest height is not updated".to_string().into()); + } + + // check that all headers are in sequential order + for i in 1..self.history.len() { + let ph = self.history[i - 1]; + let h = self.history[i]; + if ph.height().increment() != h.height() { + return Err("headers in history not sequential".to_string().into()); + } + } Ok(()) - // // check that the number of entries is not higher than max_size - // if self.history.len() > self.max_size { - // return Err("too many entries".to_string().into()); - // } - // - // // check latest is properly updated with highest header height - // let SelfHeader::Mock(lh) = self.history[self.history.len() - 1].header; - // if lh.height() != self.latest { - // return Err("latest height is not updated".to_string().into()); - // } - // - // // check that all headers are in sequential order - // for i in 1..self.history.len() { - // let SelfHeader::Mock(ph) = self.history[i - 1].header; - // let SelfHeader::Mock(h) = self.history[i].header; - // if ph.height().increment() != h.height() { - // return Err("headers in history not sequential".to_string().into()); - // } - // } - // Ok(()) } }