diff --git a/modules/Cargo.toml b/modules/Cargo.toml index 58b834a8ba..7445e05f5f 100644 --- a/modules/Cargo.toml +++ b/modules/Cargo.toml @@ -16,10 +16,10 @@ description = """ [dependencies] tendermint = "0.15.0" -tendermint-rpc = { version = "0.15.0", features=["client"] } +tendermint-rpc = { version = "0.15.0", features = ["client"] } # Proto definitions for all IBC-related interfaces, e.g., connections or channels. -ibc-proto = "0.1.0" +ibc-proto = "0.3.0" anomaly = "0.2.0" thiserror = "1.0.11" @@ -28,6 +28,8 @@ serde = "1.0.104" serde_json = "1" tracing = "0.1.13" prost = "0.6.1" +prost-types = { version = "0.6.1" } + bytes = "0.5.5" dyn-clonable = "0.9.0" diff --git a/modules/src/address.rs b/modules/src/address.rs index 276562d4ed..a578b3cbc7 100644 --- a/modules/src/address.rs +++ b/modules/src/address.rs @@ -1 +1 @@ -pub struct AccAddress(Vec; +pub struct AccAddress(Vec); diff --git a/modules/src/context.rs b/modules/src/context.rs index 7360a387ca..97878c2150 100644 --- a/modules/src/context.rs +++ b/modules/src/context.rs @@ -3,19 +3,27 @@ use tendermint::block::Height; #[cfg(test)] use crate::ics02_client::client_def::AnyConsensusState; +use crate::ics07_tendermint; #[cfg(test)] use crate::mock_client::header::MockHeader; #[cfg(test)] use crate::mock_client::state::MockConsensusState; #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum SelfChainType { + Tendermint, + #[cfg(test)] + Mock, +} + +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum SelfHeader { - // Tendermint(tendermint::header::Header), + Tendermint(ics07_tendermint::header::Header), #[cfg(test)] Mock(MockHeader), } -#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct HistoricalInfo { pub header: SelfHeader, } @@ -28,7 +36,8 @@ impl From for AnyConsensusState { } pub trait ChainReader { - fn self_historical_info(&self, height: Height) -> Option<&HistoricalInfo>; + fn chain_type(&self) -> SelfChainType; + fn self_historical_info(&self, height: Height) -> Option; } pub trait ChainKeeper { diff --git a/modules/src/context_mock.rs b/modules/src/context_mock.rs index 5b6db01848..a9b31a2ec1 100644 --- a/modules/src/context_mock.rs +++ b/modules/src/context_mock.rs @@ -1,14 +1,14 @@ -use crate::context::{ChainKeeper, ChainReader, HistoricalInfo, SelfHeader}; +use crate::context::{ChainKeeper, ChainReader, HistoricalInfo, SelfChainType, SelfHeader}; use crate::mock_client::header::MockHeader; use serde_derive::{Deserialize, Serialize}; use std::error::Error; use tendermint::block::Height; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)] pub struct MockChainContext { pub max_size: usize, pub latest: Height, - pub history: Vec, + pub history: Vec, } impl MockChainContext { @@ -17,9 +17,7 @@ impl MockChainContext { max_size, latest: n, history: (0..n.value()) - .map(|i| HistoricalInfo { - header: SelfHeader::Mock(MockHeader(Height(i).increment())), - }) + .map(|i| MockHeader(Height(i).increment())) .collect(), } } @@ -40,33 +38,38 @@ impl MockChainContext { } } - /// Used for testing pub fn validate(&self) -> Result<(), Box> { - // 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()); - } - } + // TODO 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(()) } } impl ChainReader for MockChainContext { - fn self_historical_info(&self, height: Height) -> Option<&HistoricalInfo> { + fn chain_type(&self) -> SelfChainType { + SelfChainType::Mock + } + + fn self_historical_info(&self, height: Height) -> Option { let l = height.value() as usize; let h = self.latest.value() as usize; @@ -74,7 +77,9 @@ impl ChainReader for MockChainContext { // header with height not in the history None } else { - Some(&self.history[h - l]) + Some(HistoricalInfo { + header: SelfHeader::Mock(self.history[h - l]), + }) } } } @@ -84,16 +89,17 @@ impl ChainKeeper for MockChainContext { if height != self.latest.increment() { return; } - let mut history = self.history.clone(); - if history.len() >= self.max_size { - history.rotate_left(1); - history[self.max_size - 1] = info; - } else { - history.push(info); + if let SelfHeader::Mock(header) = info.header { + let mut history = self.history.clone(); + if history.len() >= self.max_size { + history.rotate_left(1); + history[self.max_size - 1] = header; + } else { + history.push(header); + } + self.history = history; + self.latest = height; } - //history.insert(height, info); - self.history = history; - self.latest = height; } } diff --git a/modules/src/ics02_client/client_def.rs b/modules/src/ics02_client/client_def.rs index 415e7036bb..b6d4aa3d59 100644 --- a/modules/src/ics02_client/client_def.rs +++ b/modules/src/ics02_client/client_def.rs @@ -1,24 +1,30 @@ +use prost::Message; use serde_derive::{Deserialize, Serialize}; use crate::downcast; use crate::ics02_client::client_type::ClientType; -use crate::ics02_client::error; +use crate::ics02_client::error::{self, Error}; use crate::ics02_client::header::Header; use crate::ics02_client::state::{ClientState, ConsensusState}; use crate::ics03_connection::connection::ConnectionEnd; use crate::ics07_tendermint as tendermint; use crate::ics07_tendermint::client_def::TendermintClient; +use crate::ics07_tendermint::client_state::ClientState as TendermintClientState; use crate::ics23_commitment::commitment::{CommitmentPrefix, CommitmentProof, CommitmentRoot}; use crate::ics24_host::identifier::{ClientId, ConnectionId}; +use crate::try_from_raw::TryFromRaw; + +use ibc_proto::ibc::tendermint::ClientState as RawTendermintClientState; use ::tendermint::block::Height; #[cfg(test)] -use crate::mock_client::client_def::MockClient; -#[cfg(test)] -use crate::mock_client::header::MockHeader; -#[cfg(test)] -use crate::mock_client::state::{MockClientState, MockConsensusState}; +use { + crate::mock_client::client_def::MockClient, + crate::mock_client::header::MockHeader, + crate::mock_client::state::{MockClientState, MockConsensusState}, + ibc_proto::ibc::mock::ClientState as RawMockClientState, +}; pub trait ClientDef: Clone { type Header: Header; @@ -48,7 +54,7 @@ pub trait ClientDef: Clone { proof: &CommitmentProof, client_id: &ClientId, consensus_height: Height, - expected_consensus_state: &Self::ConsensusState, + expected_consensus_state: &AnyConsensusState, ) -> Result<(), Box>; /// Verify a `proof` that a connection state matches that of the input `connection_end`. @@ -61,6 +67,19 @@ pub trait ClientDef: Clone { connection_id: &ConnectionId, expected_connection_end: &ConnectionEnd, ) -> Result<(), Box>; + + /// Verify the client state for this chain that it is stored on the counterparty chain. + #[allow(clippy::too_many_arguments)] + fn verify_client_full_state( + &self, + _client_state: &Self::ClientState, + height: Height, + root: &CommitmentRoot, + prefix: &CommitmentPrefix, + client_id: &ClientId, + proof: &CommitmentProof, + client_state: &AnyClientState, + ) -> Result<(), Box>; } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] // TODO: Add Eq @@ -92,14 +111,41 @@ impl Header for AnyHeader { } } -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum AnyClientState { - Tendermint(crate::ics07_tendermint::client_state::ClientState), + Tendermint(TendermintClientState), #[cfg(test)] Mock(MockClientState), } +impl AnyClientState { + pub fn from_any(any: prost_types::Any) -> Result { + match any.type_url.as_str() { + "ibc.tendermint.ClientState" => { + let raw = RawTendermintClientState::decode(any.value.as_ref()) + .map_err(|e| error::Kind::ProtoDecodingFailure.context(e))?; + let client_state = TendermintClientState::try_from(raw) + .map_err(|e| error::Kind::InvalidRawClientState.context(e))?; + + Ok(AnyClientState::Tendermint(client_state)) + } + + #[cfg(test)] + "ibc.mock.ClientState" => { + let raw = RawMockClientState::decode(any.value.as_ref()) + .map_err(|e| error::Kind::ProtoDecodingFailure.context(e))?; + let client_state = MockClientState::try_from(raw) + .map_err(|e| error::Kind::InvalidRawClientState.context(e))?; + + Ok(AnyClientState::Mock(client_state)) + } + + _ => Err(error::Kind::UnknownClientStateType(any.type_url).into()), + } + } +} + impl ClientState for AnyClientState { fn chain_id(&self) -> String { todo!() @@ -114,21 +160,21 @@ impl ClientState for AnyClientState { } } - fn latest_height(&self) -> Height { + fn is_frozen(&self) -> bool { match self { - Self::Tendermint(tm_state) => tm_state.latest_height(), + AnyClientState::Tendermint(tm_state) => tm_state.is_frozen(), #[cfg(test)] - Self::Mock(mock_state) => mock_state.latest_height(), + AnyClientState::Mock(mock_state) => mock_state.is_frozen(), } } - fn is_frozen(&self) -> bool { + fn latest_height(&self) -> Height { match self { - Self::Tendermint(tm_state) => tm_state.is_frozen(), + Self::Tendermint(tm_state) => tm_state.latest_height(), #[cfg(test)] - Self::Mock(mock_state) => mock_state.is_frozen(), + Self::Mock(mock_state) => mock_state.latest_height(), } } } @@ -237,9 +283,8 @@ impl ClientDef for AnyClient { ) -> Result<(), Box> { match self { Self::Tendermint(client) => { - let (client_state, expected_consensus_state) = downcast!( - client_state => AnyClientState::Tendermint, - expected_consensus_state => AnyConsensusState::Tendermint, + let client_state = downcast!( + client_state => AnyClientState::Tendermint ) .ok_or_else(|| error::Kind::ClientArgsTypeMismatch(ClientType::Tendermint))?; @@ -256,9 +301,8 @@ impl ClientDef for AnyClient { #[cfg(test)] Self::Mock(client) => { - let (client_state, expected_consensus_state) = downcast!( - client_state => AnyClientState::Mock, - expected_consensus_state => AnyConsensusState::Mock, + let client_state = downcast!( + client_state => AnyClientState::Mock ) .ok_or_else(|| error::Kind::ClientArgsTypeMismatch(ClientType::Mock))?; @@ -277,7 +321,7 @@ impl ClientDef for AnyClient { fn verify_connection_state( &self, - client_state: &Self::ClientState, + client_state: &AnyClientState, height: Height, prefix: &CommitmentPrefix, proof: &CommitmentProof, @@ -315,4 +359,52 @@ impl ClientDef for AnyClient { } } } + + fn verify_client_full_state( + &self, + client_state: &Self::ClientState, + height: Height, + root: &CommitmentRoot, + prefix: &CommitmentPrefix, + client_id: &ClientId, + proof: &CommitmentProof, + client_state_on_counterparty: &AnyClientState, + ) -> Result<(), Box> { + match self { + Self::Tendermint(client) => { + let client_state = downcast!( + client_state => AnyClientState::Tendermint + ) + .ok_or_else(|| error::Kind::ClientArgsTypeMismatch(ClientType::Tendermint))?; + + client.verify_client_full_state( + client_state, + height, + root, + prefix, + client_id, + proof, + client_state_on_counterparty, + ) + } + + #[cfg(test)] + Self::Mock(client) => { + let client_state = downcast!( + client_state => AnyClientState::Mock + ) + .ok_or_else(|| error::Kind::ClientArgsTypeMismatch(ClientType::Mock))?; + + client.verify_client_full_state( + client_state, + height, + root, + prefix, + client_id, + proof, + client_state_on_counterparty, + ) + } + } + } } diff --git a/modules/src/ics02_client/error.rs b/modules/src/ics02_client/error.rs index 1434aec119..f6f19ffe23 100644 --- a/modules/src/ics02_client/error.rs +++ b/modules/src/ics02_client/error.rs @@ -28,6 +28,18 @@ pub enum Kind { #[error("header verification failed")] HeaderVerificationFailure, + #[error("unknown client state type: {0}")] + UnknownClientStateType(String), + + #[error("invalid raw client state")] + InvalidRawClientState, + + #[error("invalid raw header")] + InvalidRawHeader, + + #[error("Protobuf decoding failure")] + ProtoDecodingFailure, + #[error("mismatch between client and arguments types, expected: {0:?}")] ClientArgsTypeMismatch(ClientType), } diff --git a/modules/src/ics02_client/state.rs b/modules/src/ics02_client/state.rs index 97cd68777d..1af84df7d0 100644 --- a/modules/src/ics02_client/state.rs +++ b/modules/src/ics02_client/state.rs @@ -1,6 +1,6 @@ -use crate::ics02_client::client_type::ClientType; +use super::client_type::ClientType; use crate::ics23_commitment::commitment::CommitmentRoot; -use tendermint::block::Height; +use crate::Height; #[dyn_clonable::clonable] pub trait ConsensusState: Clone + std::fmt::Debug { diff --git a/modules/src/ics03_connection/connection.rs b/modules/src/ics03_connection/connection.rs index dc535fc0c1..225e4d6f36 100644 --- a/modules/src/ics03_connection/connection.rs +++ b/modules/src/ics03_connection/connection.rs @@ -1,12 +1,14 @@ use crate::ics03_connection::error::{Error, Kind}; use crate::ics23_commitment::commitment::CommitmentPrefix; +use crate::ics24_host::error::ValidationError; use crate::ics24_host::identifier::{ClientId, ConnectionId}; use crate::try_from_raw::TryFromRaw; -use serde_derive::{Deserialize, Serialize}; -// Import proto declarations. -use crate::ics24_host::error::ValidationError; -use ibc_proto::connection::{ConnectionEnd as RawConnectionEnd, Counterparty as RawCounterparty}; +use ibc_proto::ibc::connection::{ + ConnectionEnd as RawConnectionEnd, Counterparty as RawCounterparty, +}; + +use serde_derive::{Deserialize, Serialize}; use std::convert::{TryFrom, TryInto}; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -21,10 +23,6 @@ impl TryFromRaw for ConnectionEnd { type RawType = RawConnectionEnd; type Error = anomaly::Error; fn try_from(value: RawConnectionEnd) -> Result { - if value.id == "" { - return Err(Kind::ConnectionNotFound.into()); - } - Ok(Self::new( State::from_i32(value.state), value diff --git a/modules/src/ics03_connection/context.rs b/modules/src/ics03_connection/context.rs index ad8f33777a..42ef404531 100644 --- a/modules/src/ics03_connection/context.rs +++ b/modules/src/ics03_connection/context.rs @@ -1,3 +1,4 @@ +use crate::context::SelfChainType; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics03_connection::connection::ConnectionEnd; use crate::ics03_connection::error::Error; @@ -19,6 +20,8 @@ pub trait ConnectionReader { /// Returns the number of consensus state historical entries for the local chain. fn chain_consensus_states_history_size(&self) -> usize; + fn chain_type(&self) -> SelfChainType; + /// Returns the prefix that the local chain uses in the KV store. fn commitment_prefix(&self) -> CommitmentPrefix; diff --git a/modules/src/ics03_connection/context_mock.rs b/modules/src/ics03_connection/context_mock.rs index c859bfe061..886f4af982 100644 --- a/modules/src/ics03_connection/context_mock.rs +++ b/modules/src/ics03_connection/context_mock.rs @@ -1,4 +1,4 @@ -use crate::context::{ChainReader, SelfHeader}; +use crate::context::{ChainReader, SelfChainType, SelfHeader}; use crate::context_mock::MockChainContext; use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; use crate::ics02_client::context::ClientReader; @@ -82,11 +82,16 @@ impl ConnectionReader for MockConnectionContext { self.client_context.consensus_state(client_id, height) } + fn chain_type(&self) -> SelfChainType { + SelfChainType::Mock + } + fn fetch_self_consensus_state(&self, height: Height) -> Option { let hi = self.chain_context.self_historical_info(height)?.header; match hi { #[cfg(test)] SelfHeader::Mock(h) => Some(h.into()), + _ => None, } } } diff --git a/modules/src/ics03_connection/error.rs b/modules/src/ics03_connection/error.rs index 16eb441890..cd9eee3e8c 100644 --- a/modules/src/ics03_connection/error.rs +++ b/modules/src/ics03_connection/error.rs @@ -35,6 +35,12 @@ pub enum Kind { #[error("invalid address")] InvalidAddress, + #[error("missing consensus proof height")] + MissingProofHeight, + + #[error("missing consensus proof height")] + MissingConsensusHeight, + #[error("invalid connection proof")] InvalidProof, @@ -56,6 +62,9 @@ pub enum Kind { #[error("the client id does not match any client state: {0}")] MissingClient(ClientId), + #[error("client proof must be present")] + NullClientProof, + #[error("the client is frozen")] FrozenClient, @@ -68,6 +77,9 @@ pub enum Kind { #[error("the local consensus state could not be retrieved")] MissingLocalConsensusState, + #[error("the client state proof verification failed")] + ClientStateVerificationFailure, + #[error("the consensus proof verification failed")] ConsensusStateVerificationFailure, } diff --git a/modules/src/ics03_connection/handler.rs b/modules/src/ics03_connection/handler.rs index 93bf9048b5..29cfb3e0e2 100644 --- a/modules/src/ics03_connection/handler.rs +++ b/modules/src/ics03_connection/handler.rs @@ -95,7 +95,7 @@ where result, log, events, - } = conn_open_try::process(ctx, msg)?; + } = conn_open_try::process(ctx, *msg)?; keep(ctx, result.clone())?; Ok(HandlerOutput::builder() diff --git a/modules/src/ics03_connection/handler/conn_open_try.rs b/modules/src/ics03_connection/handler/conn_open_try.rs index e1d48eefd2..e3a76c3043 100644 --- a/modules/src/ics03_connection/handler/conn_open_try.rs +++ b/modules/src/ics03_connection/handler/conn_open_try.rs @@ -60,6 +60,7 @@ pub(crate) fn process( verify_proofs( ctx, msg.connection_id(), + msg.client_state(), &new_connection_end, &expected_conn, msg.proofs(), @@ -125,20 +126,20 @@ mod tests { ctx: default_context .clone() .with_client_state(dummy_msg.client_id(), 10), - msg: ConnectionMsg::ConnectionOpenTry(dummy_msg.clone()), + msg: ConnectionMsg::ConnectionOpenTry(Box::new(dummy_msg.clone())), want_pass: true, }, Test { name: "Processing fails because no client exists".to_string(), ctx: default_context.clone(), - msg: ConnectionMsg::ConnectionOpenTry(dummy_msg.clone()), + msg: ConnectionMsg::ConnectionOpenTry(Box::new(dummy_msg.clone())), want_pass: false, }, Test { name: "Processing fails because connection exists in the store already".to_string(), ctx: default_context .add_connection(dummy_msg.connection_id().clone(), try_conn_end.clone()), - msg: ConnectionMsg::ConnectionOpenTry(dummy_msg.clone()), + msg: ConnectionMsg::ConnectionOpenTry(Box::new(dummy_msg.clone())), want_pass: false, }, ] diff --git a/modules/src/ics03_connection/handler/verify.rs b/modules/src/ics03_connection/handler/verify.rs index ff19f8f663..1de5fd143c 100644 --- a/modules/src/ics03_connection/handler/verify.rs +++ b/modules/src/ics03_connection/handler/verify.rs @@ -1,4 +1,6 @@ -use crate::ics02_client::{client_def::AnyClient, client_def::ClientDef, state::ClientState}; +use crate::ics02_client::client_def::AnyClientState; +use crate::ics02_client::state::{ClientState, ConsensusState}; +use crate::ics02_client::{client_def::AnyClient, client_def::ClientDef}; use crate::ics03_connection::connection::ConnectionEnd; use crate::ics03_connection::context::ConnectionReader; use crate::ics03_connection::error::{Error, Kind}; @@ -10,6 +12,7 @@ use tendermint::block::Height; pub fn verify_proofs( ctx: &dyn ConnectionReader, id: &ConnectionId, + client_state: Option, connection_end: &ConnectionEnd, expected_conn: &ConnectionEnd, proofs: &Proofs, @@ -23,6 +26,20 @@ pub fn verify_proofs( proofs.object_proof(), )?; + // If a client proof is present verify it. + if let Some(state) = client_state { + verify_client_proof( + ctx, + connection_end, + state, + proofs.height(), + proofs + .client_proof() + .as_ref() + .ok_or_else(|| Kind::NullClientProof)?, + )?; + } + // If a consensus proof is present verify it. match proofs.consensus_proof() { None => Ok(()), @@ -69,6 +86,40 @@ pub fn verify_connection_proof( .map_err(|_| Kind::InvalidProof.context(id.to_string()))?) } +pub fn verify_client_proof( + ctx: &dyn ConnectionReader, + connection_end: &ConnectionEnd, + expected_client_state: AnyClientState, + proof_height: Height, + proof: &CommitmentProof, +) -> Result<(), Error> { + let client_state = ctx + .fetch_client_state(connection_end.client_id()) + .ok_or_else(|| Kind::MissingClient(connection_end.client_id().clone()))?; + + let consensus_state = ctx + .fetch_client_consensus_state(connection_end.client_id(), proof_height) + .ok_or_else(|| { + Kind::MissingClientConsensusState.context(connection_end.client_id().to_string()) + })?; + + let client_def = AnyClient::from_client_type(client_state.client_type()); + + Ok(client_def + .verify_client_full_state( + &client_state, + proof_height, + consensus_state.root(), + connection_end.counterparty().prefix(), + connection_end.counterparty().client_id(), + proof, + &expected_client_state, + ) + .map_err(|_| { + Kind::ClientStateVerificationFailure.context(connection_end.client_id().to_string()) + })?) +} + pub fn verify_consensus_proof( ctx: &dyn ConnectionReader, connection_end: &ConnectionEnd, diff --git a/modules/src/ics03_connection/msgs.rs b/modules/src/ics03_connection/msgs.rs index 1703914ba0..569d4217bb 100644 --- a/modules/src/ics03_connection/msgs.rs +++ b/modules/src/ics03_connection/msgs.rs @@ -14,22 +14,26 @@ //! TODO: Separate the Cosmos-SDK specific functionality from canonical ICS types. Decorators? #![allow(clippy::too_many_arguments)] + +use crate::ics02_client::client_def::AnyClientState; use crate::ics03_connection::connection::{validate_version, validate_versions, Counterparty}; use crate::ics03_connection::error::{Error, Kind}; use crate::ics24_host::identifier::{ClientId, ConnectionId}; use crate::proofs::{ConsensusProof, Proofs}; use crate::try_from_raw::TryFromRaw; use crate::tx_msg::Msg; -use ibc_proto::connection::MsgConnectionOpenAck as RawMsgConnectionOpenAck; -use ibc_proto::connection::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; -use ibc_proto::connection::MsgConnectionOpenInit as RawMsgConnectionOpenInit; -use ibc_proto::connection::MsgConnectionOpenTry as RawMsgConnectionOpenTry; + +use ibc_proto::ibc::connection::MsgConnectionOpenAck as RawMsgConnectionOpenAck; +use ibc_proto::ibc::connection::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; +use ibc_proto::ibc::connection::MsgConnectionOpenInit as RawMsgConnectionOpenInit; +use ibc_proto::ibc::connection::MsgConnectionOpenTry as RawMsgConnectionOpenTry; + +use tendermint::account::Id as AccountId; +use tendermint::block::Height; use serde_derive::{Deserialize, Serialize}; use std::convert::TryInto; use std::str::{from_utf8, FromStr}; -use tendermint::account::Id as AccountId; -use tendermint::block::Height; /// Message type for the `MsgConnectionOpenInit` message. pub const TYPE_MSG_CONNECTION_OPEN_INIT: &str = "connection_open_init"; @@ -47,7 +51,7 @@ pub const TYPE_MSG_CONNECTION_OPEN_CONFIRM: &str = "connection_open_confirm"; #[derive(Clone, Debug)] pub enum ConnectionMsg { ConnectionOpenInit(MsgConnectionOpenInit), - ConnectionOpenTry(MsgConnectionOpenTry), + ConnectionOpenTry(Box), // ConnectionOpenAck(MsgConnectionOpenAck), // ConnectionOpenConfirm(MsgConnectionOpenConfirm), } @@ -139,6 +143,7 @@ impl Msg for MsgConnectionOpenInit { pub struct MsgConnectionOpenTry { connection_id: ConnectionId, client_id: ClientId, + client_state: Option, counterparty: Counterparty, counterparty_versions: Vec, proofs: Proofs, @@ -146,18 +151,9 @@ pub struct MsgConnectionOpenTry { } impl MsgConnectionOpenTry { - /// Getter for accessing the `consensus_height` field from this message. Returns the special - /// value `0` if this field is not set. - pub fn consensus_height(&self) -> Height { - match self.proofs.consensus_proof() { - None => Height(0), - Some(p) => p.height(), - } - } - - /// Getter for accesing the whole counterparty of this message. Returns a `clone()`. - pub fn counterparty(&self) -> Counterparty { - self.counterparty.clone() + /// Getter for accessing the connection identifier of this message. + pub fn connection_id(&self) -> &ConnectionId { + &self.connection_id } /// Getter for accessing the client identifier from this message. @@ -165,20 +161,34 @@ impl MsgConnectionOpenTry { &self.client_id } - /// Getter for accessing the connection identifier of this message. - pub fn connection_id(&self) -> &ConnectionId { - &self.connection_id + /// Getter for accessing the client state. + pub fn client_state(&self) -> Option { + self.client_state.clone() } - /// Getter for accessing the proofs in this message. - pub fn proofs(&self) -> &Proofs { - &self.proofs + /// Getter for accesing the whole counterparty of this message. Returns a `clone()`. + pub fn counterparty(&self) -> Counterparty { + self.counterparty.clone() } /// Getter for accessing the versions from this message. Returns a `clone()`. pub fn counterparty_versions(&self) -> Vec { self.counterparty_versions.clone() } + + /// Getter for accessing the proofs in this message. + pub fn proofs(&self) -> &Proofs { + &self.proofs + } + + /// Getter for accessing the `consensus_height` field from this message. Returns the special + /// value `0` if this field is not set. + pub fn consensus_height(&self) -> Height { + match self.proofs.consensus_proof() { + None => Height(0), + Some(p) => p.height(), + } + } } impl Msg for MsgConnectionOpenTry { @@ -208,12 +218,25 @@ impl Msg for MsgConnectionOpenTry { } impl TryFromRaw for MsgConnectionOpenTry { + type Error = Error; type RawType = RawMsgConnectionOpenTry; - type Error = anomaly::Error; + fn try_from(msg: RawMsgConnectionOpenTry) -> Result { - let consensus_proof_obj = - ConsensusProof::new(msg.proof_consensus.into(), msg.consensus_height) - .map_err(|e| Kind::InvalidProof.context(e))?; + let proof_height = msg + .proof_height + .ok_or_else(|| Kind::MissingProofHeight)? + .epoch_height; // FIXME: This is wrong as it does not take the epoch number into account + let consensus_height = msg + .consensus_height + .ok_or_else(|| Kind::MissingConsensusHeight)? + .epoch_height; // FIXME: This is wrong as it does not take the epoch number into account + let consensus_proof_obj = ConsensusProof::new(msg.proof_consensus.into(), consensus_height) + .map_err(|e| Kind::InvalidProof.context(e))?; + + let client_proof = match msg.client_state { + None => None, + Some(_) => Some(msg.proof_client.into()), + }; Ok(Self { connection_id: msg @@ -224,6 +247,11 @@ impl TryFromRaw for MsgConnectionOpenTry { .client_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, + client_state: msg + .client_state + .map(AnyClientState::from_any) + .transpose() + .map_err(|e| Kind::InvalidProof.context(e))?, counterparty: msg .counterparty .ok_or_else(|| Kind::MissingCounterparty)? @@ -232,8 +260,9 @@ impl TryFromRaw for MsgConnectionOpenTry { .map_err(|e| Kind::InvalidVersion.context(e))?, proofs: Proofs::new( msg.proof_init.into(), + client_proof, Some(consensus_proof_obj), - msg.proof_height, + proof_height, ) .map_err(|e| Kind::InvalidProof.context(e))?, signer: AccountId::from_str( @@ -250,35 +279,41 @@ impl TryFromRaw for MsgConnectionOpenTry { #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct MsgConnectionOpenAck { connection_id: ConnectionId, + client_state: Option, proofs: Proofs, version: String, signer: AccountId, } impl MsgConnectionOpenAck { - /// Getter for accessing the `consensus_height` field from this message. Returns the special - /// value `0` if this field is not set. - pub fn consensus_height(&self) -> Height { - match self.proofs.consensus_proof() { - None => Height(0), - Some(p) => p.height(), - } - } - /// Getter for accessing the connection identifier of this message. pub fn connection_id(&self) -> &ConnectionId { &self.connection_id } - /// Getter for the version field. - pub fn version(&self) -> &String { - &self.version + /// Getter for accessing the client state. + pub fn client_state(&self) -> Option { + self.client_state.clone() } /// Getter for accessing (borrow) the proofs in this message. pub fn proofs(&self) -> &Proofs { &self.proofs } + + /// Getter for the version field. + pub fn version(&self) -> &String { + &self.version + } + + /// Getter for accessing the `consensus_height` field from this message. Returns the special + /// value `0` if this field is not set. + pub fn consensus_height(&self) -> Height { + match self.proofs.consensus_proof() { + None => Height(0), + Some(p) => p.height(), + } + } } impl Msg for MsgConnectionOpenAck { @@ -310,20 +345,38 @@ impl TryFromRaw for MsgConnectionOpenAck { type Error = anomaly::Error; fn try_from(msg: RawMsgConnectionOpenAck) -> Result { - let consensus_proof_obj = - ConsensusProof::new(msg.proof_consensus.into(), msg.consensus_height) - .map_err(|e| Kind::InvalidProof.context(e))?; + let proof_height = msg + .proof_height + .ok_or_else(|| Kind::MissingProofHeight)? + .epoch_height; // FIXME: This is wrong as it does not take the epoch number into account + let consensus_height = msg + .consensus_height + .ok_or_else(|| Kind::MissingConsensusHeight)? + .epoch_height; // FIXME: This is wrong as it does not take the epoch number into account + let consensus_proof_obj = ConsensusProof::new(msg.proof_consensus.into(), consensus_height) + .map_err(|e| Kind::InvalidProof.context(e))?; + + let client_proof = match msg.client_state { + None => None, + Some(_) => Some(msg.proof_client.into()), + }; Ok(Self { connection_id: msg .connection_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, + client_state: msg + .client_state + .map(AnyClientState::from_any) + .transpose() + .map_err(|e| Kind::InvalidProof.context(e))?, version: validate_version(msg.version).map_err(|e| Kind::InvalidVersion.context(e))?, proofs: Proofs::new( msg.proof_try.into(), + client_proof, Option::from(consensus_proof_obj), - msg.proof_height, + proof_height, ) .map_err(|e| Kind::InvalidProof.context(e))?, signer: AccountId::from_str( @@ -385,12 +438,16 @@ impl TryFromRaw for MsgConnectionOpenConfirm { type Error = anomaly::Error; fn try_from(msg: RawMsgConnectionOpenConfirm) -> Result { + let proof_height = msg + .proof_height + .ok_or_else(|| Kind::MissingProofHeight)? + .epoch_height; // FIXME: This is wrong as it does not take the epoch number into account Ok(Self { connection_id: msg .connection_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, - proofs: Proofs::new(msg.proof_ack.into(), None, msg.proof_height) + proofs: Proofs::new(msg.proof_ack.into(), None, None, proof_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer: AccountId::from_str( from_utf8(&msg.signer).map_err(|e| Kind::InvalidSigner.context(e))?, @@ -402,13 +459,14 @@ impl TryFromRaw for MsgConnectionOpenConfirm { #[cfg(test)] pub mod test_util { - use ibc_proto::connection::Counterparty as RawCounterparty; - use ibc_proto::connection::MsgConnectionOpenAck as RawMsgConnectionOpenAck; - use ibc_proto::connection::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; - use ibc_proto::connection::MsgConnectionOpenInit as RawMsgConnectionOpenInit; - use ibc_proto::connection::MsgConnectionOpenTry as RawMsgConnectionOpenTry; + use ibc_proto::ibc::connection::Counterparty as RawCounterparty; + use ibc_proto::ibc::connection::MsgConnectionOpenAck as RawMsgConnectionOpenAck; + use ibc_proto::ibc::connection::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; + use ibc_proto::ibc::connection::MsgConnectionOpenInit as RawMsgConnectionOpenInit; + use ibc_proto::ibc::connection::MsgConnectionOpenTry as RawMsgConnectionOpenTry; - use ibc_proto::commitment::MerklePrefix; + use ibc_proto::ibc::client::Height; + use ibc_proto::ibc::commitment::MerklePrefix; pub fn get_dummy_proof() -> Vec { "Y29uc2Vuc3VzU3RhdGUvaWJjb25lY2xpZW50LzIy" @@ -450,13 +508,21 @@ pub mod test_util { RawMsgConnectionOpenTry { client_id: "srcclient".to_string(), connection_id: "srcconnection".to_string(), + client_state: None, counterparty: Some(get_dummy_counterparty()), counterparty_versions: vec!["1.0.0".to_string()], proof_init: get_dummy_proof(), - proof_height, + proof_height: Some(Height { + epoch_number: 1, + epoch_height: proof_height, + }), proof_consensus: get_dummy_proof(), - consensus_height, + consensus_height: Some(Height { + epoch_number: 1, + epoch_height: consensus_height, + }), signer: get_dummy_account_id(), + proof_client: vec![], } } @@ -465,10 +531,18 @@ pub mod test_util { connection_id: "srcconnection".to_string(), version: "1.0.0".to_string(), proof_try: get_dummy_proof(), - proof_height: 10, + proof_height: Some(Height { + epoch_number: 1, + epoch_height: 10, + }), proof_consensus: get_dummy_proof(), - consensus_height: 10, + consensus_height: Some(Height { + epoch_number: 1, + epoch_height: 10, + }), signer: get_dummy_account_id(), + client_state: None, + proof_client: vec![], } } @@ -476,7 +550,10 @@ pub mod test_util { RawMsgConnectionOpenConfirm { connection_id: "srcconnection".to_string(), proof_ack: get_dummy_proof(), - proof_height: 10, + proof_height: Some(Height { + epoch_number: 1, + epoch_height: 10, + }), signer: get_dummy_account_id(), } } @@ -493,11 +570,12 @@ mod tests { MsgConnectionOpenAck, MsgConnectionOpenConfirm, MsgConnectionOpenTry, }; use crate::try_from_raw::TryFromRaw; - use ibc_proto::connection::Counterparty as RawCounterparty; - use ibc_proto::connection::MsgConnectionOpenAck as RawMsgConnectionOpenAck; - use ibc_proto::connection::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; - use ibc_proto::connection::MsgConnectionOpenInit as RawMsgConnectionOpenInit; - use ibc_proto::connection::MsgConnectionOpenTry as RawMsgConnectionOpenTry; + use ibc_proto::ibc::client::Height; + use ibc_proto::ibc::connection::Counterparty as RawCounterparty; + use ibc_proto::ibc::connection::MsgConnectionOpenAck as RawMsgConnectionOpenAck; + use ibc_proto::ibc::connection::MsgConnectionOpenConfirm as RawMsgConnectionOpenConfirm; + use ibc_proto::ibc::connection::MsgConnectionOpenInit as RawMsgConnectionOpenInit; + use ibc_proto::ibc::connection::MsgConnectionOpenTry as RawMsgConnectionOpenTry; #[test] fn parse_connection_open_init_msg() { @@ -574,7 +652,8 @@ mod tests { let default_try_msg = get_dummy_msg_conn_open_try(10, 34); - let tests: Vec = vec![ + let tests: Vec = + vec![ Test { name: "Good parameters".to_string(), raw: default_try_msg.clone(), @@ -640,7 +719,7 @@ mod tests { Test { name: "Bad proof height, height is 0".to_string(), raw: RawMsgConnectionOpenTry { - proof_height: 0, + proof_height: Some(Height{ epoch_number: 1, epoch_height: 0 }), ..default_try_msg.clone() }, want_pass: false, @@ -648,7 +727,7 @@ mod tests { Test { name: "Bad consensus height, height is 0".to_string(), raw: RawMsgConnectionOpenTry { - consensus_height: 0, + proof_height: Some(Height{ epoch_number: 1, epoch_height: 0 }), ..default_try_msg.clone() }, want_pass: false, @@ -660,10 +739,10 @@ mod tests { ..default_try_msg }, want_pass: false, - }, - ] - .into_iter() - .collect(); + } + ] + .into_iter() + .collect(); for test in tests { let msg = MsgConnectionOpenTry::try_from(test.raw.clone()); @@ -715,7 +794,10 @@ mod tests { Test { name: "Bad proof height, height is 0".to_string(), raw: RawMsgConnectionOpenAck { - proof_height: 0, + proof_height: Some(Height { + epoch_number: 1, + epoch_height: 0, + }), ..default_ack_msg.clone() }, want_pass: false, @@ -723,7 +805,10 @@ mod tests { Test { name: "Bad consensus height, height is 0".to_string(), raw: RawMsgConnectionOpenAck { - consensus_height: 0, + consensus_height: Some(Height { + epoch_number: 1, + epoch_height: 0, + }), ..default_ack_msg }, want_pass: false, @@ -773,7 +858,10 @@ mod tests { Test { name: "Bad proof height, height is 0".to_string(), raw: RawMsgConnectionOpenConfirm { - proof_height: 0, + proof_height: Some(Height { + epoch_number: 1, + epoch_height: 0, + }), ..default_ack_msg }, want_pass: false, diff --git a/modules/src/ics04_channel/channel.rs b/modules/src/ics04_channel/channel.rs index 321f5e7605..d7fb41a016 100644 --- a/modules/src/ics04_channel/channel.rs +++ b/modules/src/ics04_channel/channel.rs @@ -1,10 +1,12 @@ use crate::ics04_channel::error::{self, Error, Kind}; use crate::ics24_host::identifier::{ChannelId, ConnectionId, PortId}; use crate::try_from_raw::TryFromRaw; + +use ibc_proto::ibc::channel::Channel as RawChannel; + use anomaly::fail; -use core::str::FromStr; -use ibc_proto::channel::Channel as RawChannel; use serde_derive::{Deserialize, Serialize}; +use std::str::FromStr; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ChannelEnd { @@ -221,11 +223,13 @@ impl State { #[cfg(test)] mod tests { + use std::str::FromStr; + use crate::ics04_channel::channel::ChannelEnd; use crate::try_from_raw::TryFromRaw; - use core::str::FromStr; - use ibc_proto::channel::Channel as RawChannel; - use ibc_proto::channel::Counterparty as RawCounterparty; + + use ibc_proto::ibc::channel::Channel as RawChannel; + use ibc_proto::ibc::channel::Counterparty as RawCounterparty; #[test] fn channel_end_try_from_raw() { diff --git a/modules/src/ics04_channel/msgs.rs b/modules/src/ics04_channel/msgs.rs index 93107d1246..2ea94b3807 100644 --- a/modules/src/ics04_channel/msgs.rs +++ b/modules/src/ics04_channel/msgs.rs @@ -130,7 +130,7 @@ impl MsgChannelOpenTry { ), counterparty_version: validate_version(counterparty_version) .map_err(|e| Kind::InvalidVersion.context(e))?, - proofs: Proofs::new(proof_init, None, proofs_height) + proofs: Proofs::new(proof_init, None, None, proofs_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, }) @@ -190,7 +190,7 @@ impl MsgChannelOpenAck { .map_err(|e| Kind::IdentifierError.context(e))?, counterparty_version: validate_version(counterparty_version) .map_err(|e| Kind::InvalidVersion.context(e))?, - proofs: Proofs::new(proof_try, None, proofs_height) + proofs: Proofs::new(proof_try, None, None, proofs_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, }) @@ -248,7 +248,7 @@ impl MsgChannelOpenConfirm { channel_id: channel_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, - proofs: Proofs::new(proof_ack, None, proofs_height) + proofs: Proofs::new(proof_ack, None, None, proofs_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, }) @@ -359,7 +359,7 @@ impl MsgChannelCloseConfirm { channel_id: channel_id .parse() .map_err(|e| Kind::IdentifierError.context(e))?, - proofs: Proofs::new(proof_init, None, proofs_height) + proofs: Proofs::new(proof_init, None, None, proofs_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, }) @@ -412,7 +412,7 @@ impl MsgPacket { packet: packet .validate() .map_err(|e| Kind::InvalidPacket.context(e))?, - proofs: Proofs::new(proof, None, proof_height) + proofs: Proofs::new(proof, None, None, proof_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, }) @@ -474,7 +474,7 @@ impl MsgTimeout { .validate() .map_err(|e| Kind::InvalidPacket.context(e))?, next_sequence_recv, - proofs: Proofs::new(proof, None, proof_height) + proofs: Proofs::new(proof, None, None, proof_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, }) @@ -534,7 +534,7 @@ impl MsgAcknowledgement { .validate() .map_err(|e| Kind::InvalidPacket.context(e))?, acknowledgement, - proofs: Proofs::new(proof, None, proof_height) + proofs: Proofs::new(proof, None, None, proof_height) .map_err(|e| Kind::InvalidProof.context(e))?, signer, }) diff --git a/modules/src/ics07_tendermint/client_def.rs b/modules/src/ics07_tendermint/client_def.rs index 3cc14b20eb..2e75be6607 100644 --- a/modules/src/ics07_tendermint/client_def.rs +++ b/modules/src/ics07_tendermint/client_def.rs @@ -1,9 +1,9 @@ -use crate::ics02_client::client_def::ClientDef; +use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState, ClientDef}; use crate::ics03_connection::connection::ConnectionEnd; use crate::ics07_tendermint::client_state::ClientState; use crate::ics07_tendermint::consensus_state::ConsensusState; use crate::ics07_tendermint::header::Header; -use crate::ics23_commitment::commitment::{CommitmentPrefix, CommitmentProof}; +use crate::ics23_commitment::commitment::{CommitmentPrefix, CommitmentProof, CommitmentRoot}; use crate::ics24_host::identifier::ClientId; use crate::ics24_host::identifier::ConnectionId; use tendermint::block::Height; @@ -32,7 +32,7 @@ impl ClientDef for TendermintClient { _proof: &CommitmentProof, _client_id: &ClientId, _consensus_height: Height, - _expected_consensus_state: &Self::ConsensusState, + _expected_consensus_state: &AnyConsensusState, ) -> Result<(), Box> { todo!() } @@ -48,4 +48,17 @@ impl ClientDef for TendermintClient { ) -> Result<(), Box> { todo!() } + + fn verify_client_full_state( + &self, + _client_state: &Self::ClientState, + _height: Height, + _root: &CommitmentRoot, + _prefix: &CommitmentPrefix, + _client_id: &ClientId, + _proof: &CommitmentProof, + _expected_client_state: &AnyClientState, + ) -> Result<(), Box> { + unimplemented!() + } } diff --git a/modules/src/ics07_tendermint/client_state.rs b/modules/src/ics07_tendermint/client_state.rs index f1b6ff7600..fa83165efd 100644 --- a/modules/src/ics07_tendermint/client_state.rs +++ b/modules/src/ics07_tendermint/client_state.rs @@ -1,9 +1,11 @@ use crate::ics02_client::client_type::ClientType; use crate::ics07_tendermint::error::{Error, Kind}; -use tendermint::block::Height; +use crate::try_from_raw::TryFromRaw; use serde_derive::{Deserialize, Serialize}; -use std::time::Duration; +use std::{convert::TryInto, time::Duration}; + +use tendermint::block::Height; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct ClientState { @@ -89,6 +91,44 @@ impl crate::ics02_client::state::ClientState for ClientState { } } +impl TryFromRaw for ClientState { + type Error = Error; + type RawType = ibc_proto::ibc::tendermint::ClientState; + + fn try_from(raw: Self::RawType) -> Result { + Ok(Self { + chain_id: raw.chain_id, + trusting_period: raw + .trusting_period + .ok_or_else(|| Kind::InvalidRawClientState.context("missing trusting period"))? + .try_into() + .map_err(|_| Kind::InvalidRawClientState.context("negative trusting period"))?, + unbonding_period: raw + .unbonding_period + .ok_or_else(|| Kind::InvalidRawClientState.context("missing unbonding period"))? + .try_into() + .map_err(|_| Kind::InvalidRawClientState.context("negative unbonding period"))?, + max_clock_drift: raw + .max_clock_drift + .ok_or_else(|| Kind::InvalidRawClientState.context("missing max clock drift"))? + .try_into() + .map_err(|_| Kind::InvalidRawClientState.context("negative max clock drift"))?, + latest_height: decode_height( + raw.latest_height + .ok_or_else(|| Kind::InvalidRawClientState.context("missing latest height"))?, + ), + frozen_height: decode_height( + raw.frozen_height + .ok_or_else(|| Kind::InvalidRawClientState.context("missing frozen height"))?, + ), + }) + } +} + +fn decode_height(height: ibc_proto::ibc::client::Height) -> Height { + Height(height.epoch_height) // FIXME: This is wrong as it does not take the epoch into account +} + #[cfg(test)] mod tests { use crate::ics07_tendermint::client_state::ClientState; diff --git a/modules/src/ics07_tendermint/error.rs b/modules/src/ics07_tendermint/error.rs index 4be7f96122..af9e13cf10 100644 --- a/modules/src/ics07_tendermint/error.rs +++ b/modules/src/ics07_tendermint/error.rs @@ -19,6 +19,9 @@ pub enum Kind { #[error("validation error")] ValidationError, + + #[error("invalid raw client state")] + InvalidRawClientState, } impl Kind { diff --git a/modules/src/ics23_commitment/merkle.rs b/modules/src/ics23_commitment/merkle.rs index aab52d43d3..845f2ea494 100644 --- a/modules/src/ics23_commitment/merkle.rs +++ b/modules/src/ics23_commitment/merkle.rs @@ -1,5 +1,6 @@ use crate::ics23_commitment::commitment::CommitmentPrefix; -use ibc_proto::commitment::MerklePath; + +use ibc_proto::ibc::commitment::MerklePath; pub fn apply_prefix( prefix: &CommitmentPrefix, diff --git a/modules/src/mock_client/client_def.rs b/modules/src/mock_client/client_def.rs index 2208fcddf1..c78697c0fb 100644 --- a/modules/src/mock_client/client_def.rs +++ b/modules/src/mock_client/client_def.rs @@ -7,6 +7,8 @@ use crate::{ics03_connection::connection::ConnectionEnd, ics24_host::Path}; use crate::mock_client::header::MockHeader; use crate::mock_client::state::{MockClientState, MockConsensusState}; +use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState}; +use crate::ics23_commitment::commitment::CommitmentRoot; use tendermint::block::Height; #[derive(Clone, Debug, PartialEq, Eq)] @@ -37,7 +39,7 @@ impl ClientDef for MockClient { _proof: &CommitmentProof, client_id: &ClientId, _consensus_height: Height, - _expected_consensus_state: &Self::ConsensusState, + _expected_consensus_state: &AnyConsensusState, ) -> Result<(), Box> { let client_prefixed_path = Path::ConsensusState(client_id.clone(), height.value()).to_string(); @@ -63,4 +65,17 @@ impl ClientDef for MockClient { ) -> Result<(), Box> { Ok(()) } + + fn verify_client_full_state( + &self, + _client_state: &Self::ClientState, + _height: Height, + _root: &CommitmentRoot, + _prefix: &CommitmentPrefix, + _client_id: &ClientId, + _proof: &CommitmentProof, + _expected_client_state: &AnyClientState, + ) -> Result<(), Box> { + Ok(()) + } } diff --git a/modules/src/mock_client/header.rs b/modules/src/mock_client/header.rs index 22d173346a..0388be7b7d 100644 --- a/modules/src/mock_client/header.rs +++ b/modules/src/mock_client/header.rs @@ -1,6 +1,9 @@ use crate::ics02_client::client_def::AnyHeader; use crate::ics02_client::client_type::ClientType; +use crate::ics02_client::error::{self, Error}; use crate::ics02_client::header::Header; +use crate::try_from_raw::TryFromRaw; + use serde_derive::{Deserialize, Serialize}; use tendermint::block::Height; @@ -28,3 +31,17 @@ impl Header for MockHeader { todo!() } } + +impl TryFromRaw for MockHeader { + type Error = Error; + type RawType = ibc_proto::ibc::mock::Header; + + fn try_from(raw: Self::RawType) -> Result { + let proto_height = raw.height.ok_or_else(|| error::Kind::InvalidRawHeader)?; + Ok(Self(decode_height(proto_height))) + } +} + +fn decode_height(height: ibc_proto::ibc::client::Height) -> Height { + Height(height.epoch_height) // FIXME: This is wrong as it does not take the epoch into account +} diff --git a/modules/src/mock_client/state.rs b/modules/src/mock_client/state.rs index 9711680d57..28c5be8303 100644 --- a/modules/src/mock_client/state.rs +++ b/modules/src/mock_client/state.rs @@ -2,13 +2,18 @@ use crate::ics02_client::client_def::{AnyClientState, AnyConsensusState, AnyHeader}; use crate::ics02_client::client_type::ClientType; +use crate::ics02_client::error::Error; +use crate::ics02_client::error::Kind; use crate::ics02_client::header::Header; use crate::ics02_client::state::{ClientState, ConsensusState}; use crate::ics23_commitment::commitment::CommitmentRoot; use crate::mock_client::header::MockHeader; +use crate::try_from_raw::TryFromRaw; + +use tendermint::block::Height; + use serde_derive::{Deserialize, Serialize}; use std::collections::HashMap; -use tendermint::block::Height; /// A mock of an IBC client record as it is stored in a mock context. /// For testing ICS02 handlers mostly, cf. `MockClientContext`. @@ -50,13 +55,25 @@ impl MockClientState { } } -#[cfg(test)] impl From for AnyClientState { fn from(mcs: MockClientState) -> Self { Self::Mock(mcs) } } +impl TryFromRaw for MockClientState { + type Error = Error; + type RawType = ibc_proto::ibc::mock::ClientState; + + fn try_from(raw: Self::RawType) -> Result { + let raw_header = raw + .header + .ok_or_else(|| Kind::InvalidRawClientState.context("missing header"))?; + + Ok(Self(MockHeader::try_from(raw_header)?)) + } +} + impl ClientState for MockClientState { fn chain_id(&self) -> String { todo!() diff --git a/modules/src/proofs.rs b/modules/src/proofs.rs index 6e9df14d22..6983261cf7 100644 --- a/modules/src/proofs.rs +++ b/modules/src/proofs.rs @@ -5,6 +5,7 @@ use tendermint::block::Height; #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Proofs { object_proof: CommitmentProof, + client_proof: Option, consensus_proof: Option, /// Height for both the above proofs height: Height, @@ -13,6 +14,7 @@ pub struct Proofs { impl Proofs { pub fn new( object_proof: CommitmentProof, + client_proof: Option, consensus_proof: Option, height: u64, ) -> Result { @@ -26,6 +28,7 @@ impl Proofs { Ok(Self { object_proof, + client_proof, consensus_proof, height: Height(height), }) @@ -46,6 +49,11 @@ impl Proofs { pub fn object_proof(&self) -> &CommitmentProof { &self.object_proof } + + /// Getter for the client_proof + pub fn client_proof(&self) -> &Option { + &self.client_proof + } } #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]