ibc_relayer_types/core/ics03_connection/msgs/
conn_open_ack.rs1use ibc_proto::google::protobuf::Any;
2use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck;
3use ibc_proto::Protobuf;
4
5use crate::core::ics03_connection::error::Error;
6use crate::core::ics03_connection::version::Version;
7use crate::core::ics23_commitment::commitment::CommitmentProofBytes;
8use crate::core::ics24_host::identifier::ConnectionId;
9use crate::proofs::{ConsensusProof, Proofs};
10use crate::signer::Signer;
11use crate::tx_msg::Msg;
12use crate::Height;
13
14pub const TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenAck";
15
16#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct MsgConnectionOpenAck {
19 pub connection_id: ConnectionId,
20 pub counterparty_connection_id: ConnectionId,
21 pub client_state: Option<Any>,
22 pub proofs: Proofs,
23 pub version: Version,
24 pub signer: Signer,
25}
26
27impl MsgConnectionOpenAck {
28 pub fn consensus_height(&self) -> Option<Height> {
31 self.proofs.consensus_proof().map(|proof| proof.height())
32 }
33}
34
35impl Msg for MsgConnectionOpenAck {
36 type ValidationError = Error;
37 type Raw = RawMsgConnectionOpenAck;
38
39 fn route(&self) -> String {
40 crate::keys::ROUTER_KEY.to_string()
41 }
42
43 fn type_url(&self) -> String {
44 TYPE_URL.to_string()
45 }
46}
47
48impl Protobuf<RawMsgConnectionOpenAck> for MsgConnectionOpenAck {}
49
50impl TryFrom<RawMsgConnectionOpenAck> for MsgConnectionOpenAck {
51 type Error = Error;
52
53 fn try_from(msg: RawMsgConnectionOpenAck) -> Result<Self, Self::Error> {
54 let consensus_height = msg
55 .consensus_height
56 .and_then(|raw_height| raw_height.try_into().ok())
57 .ok_or_else(Error::missing_consensus_height)?;
58
59 let consensus_proof = ConsensusProof::new(
60 msg.proof_consensus
61 .try_into()
62 .map_err(Error::invalid_proof)?,
63 consensus_height,
64 )
65 .map_err(Error::invalid_proof)?;
66
67 let proof_height = msg
68 .proof_height
69 .and_then(|raw_height| raw_height.try_into().ok())
70 .ok_or_else(Error::missing_proof_height)?;
71
72 let client_proof =
73 CommitmentProofBytes::try_from(msg.proof_client).map_err(Error::invalid_proof)?;
74
75 let consensus_state_proof =
77 CommitmentProofBytes::try_from(msg.host_consensus_state_proof).ok();
78
79 Ok(Self {
80 connection_id: msg
81 .connection_id
82 .parse()
83 .map_err(Error::invalid_identifier)?,
84 counterparty_connection_id: msg
85 .counterparty_connection_id
86 .parse()
87 .map_err(Error::invalid_identifier)?,
88 client_state: msg.client_state,
89 version: msg.version.ok_or_else(Error::empty_versions)?.try_into()?,
90 proofs: Proofs::new(
91 msg.proof_try.try_into().map_err(Error::invalid_proof)?,
92 Some(client_proof),
93 Some(consensus_proof),
94 consensus_state_proof,
95 None,
96 proof_height,
97 )
98 .map_err(Error::invalid_proof)?,
99 signer: msg.signer.parse().map_err(Error::signer)?,
100 })
101 }
102}
103
104impl From<MsgConnectionOpenAck> for RawMsgConnectionOpenAck {
105 fn from(ics_msg: MsgConnectionOpenAck) -> Self {
106 RawMsgConnectionOpenAck {
107 connection_id: ics_msg.connection_id.as_str().to_string(),
108 counterparty_connection_id: ics_msg.counterparty_connection_id.as_str().to_string(),
109 client_state: ics_msg.client_state,
110 proof_height: Some(ics_msg.proofs.height().into()),
111 proof_try: ics_msg.proofs.object_proof().clone().into(),
112 proof_client: ics_msg
113 .proofs
114 .client_proof()
115 .map_or_else(Vec::new, |v| v.to_bytes()),
116 proof_consensus: ics_msg
117 .proofs
118 .consensus_proof()
119 .map_or_else(Vec::new, |v| v.proof().to_bytes()),
120 consensus_height: ics_msg
121 .proofs
122 .consensus_proof()
123 .map_or_else(|| None, |h| Some(h.height().into())),
124 host_consensus_state_proof: ics_msg
125 .proofs
126 .host_consensus_state_proof()
127 .map_or_else(Vec::new, |v| v.to_bytes()),
128 version: Some(ics_msg.version.into()),
129 signer: ics_msg.signer.to_string(),
130 }
131 }
132}
133
134#[cfg(test)]
135pub mod test_util {
136
137 use ibc_proto::ibc::core::client::v1::Height;
138 use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck;
139
140 use crate::core::ics03_connection::version::Version;
141 use crate::core::ics24_host::identifier::ConnectionId;
142 use crate::test_utils::{get_dummy_bech32_account, get_dummy_proof};
143
144 pub fn get_dummy_raw_msg_conn_open_ack(
145 proof_height: u64,
146 consensus_height: u64,
147 ) -> RawMsgConnectionOpenAck {
148 RawMsgConnectionOpenAck {
149 connection_id: ConnectionId::new(0).to_string(),
150 counterparty_connection_id: ConnectionId::new(1).to_string(),
151 proof_try: get_dummy_proof(),
152 proof_height: Some(Height {
153 revision_number: 0,
154 revision_height: proof_height,
155 }),
156 proof_consensus: get_dummy_proof(),
157 host_consensus_state_proof: get_dummy_proof(),
158 consensus_height: Some(Height {
159 revision_number: 0,
160 revision_height: consensus_height,
161 }),
162 client_state: None,
163 proof_client: get_dummy_proof(),
164 version: Some(Version::default().into()),
165 signer: get_dummy_bech32_account(),
166 }
167 }
168}
169
170#[cfg(test)]
171mod tests {
172
173 use test_log::test;
174
175 use ibc_proto::ibc::core::client::v1::Height;
176 use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenAck as RawMsgConnectionOpenAck;
177
178 use crate::core::ics03_connection::msgs::conn_open_ack::test_util::get_dummy_raw_msg_conn_open_ack;
179 use crate::core::ics03_connection::msgs::conn_open_ack::MsgConnectionOpenAck;
180
181 #[test]
182 fn parse_connection_open_ack_msg() {
183 #[derive(Clone, Debug, PartialEq)]
184 struct Test {
185 name: String,
186 raw: RawMsgConnectionOpenAck,
187 want_pass: bool,
188 }
189
190 let default_ack_msg = get_dummy_raw_msg_conn_open_ack(5, 5);
191
192 let tests: Vec<Test> = vec![
193 Test {
194 name: "Good parameters".to_string(),
195 raw: default_ack_msg.clone(),
196 want_pass: true,
197 },
198 Test {
199 name: "Bad connection id, non-alpha".to_string(),
200 raw: RawMsgConnectionOpenAck {
201 connection_id: "con007".to_string(),
202 ..default_ack_msg.clone()
203 },
204 want_pass: false,
205 },
206 Test {
207 name: "Bad version, missing version".to_string(),
208 raw: RawMsgConnectionOpenAck {
209 version: None,
210 ..default_ack_msg.clone()
211 },
212 want_pass: false,
213 },
214 Test {
215 name: "Bad proof height, height is 0".to_string(),
216 raw: RawMsgConnectionOpenAck {
217 proof_height: Some(Height {
218 revision_number: 1,
219 revision_height: 0,
220 }),
221 ..default_ack_msg.clone()
222 },
223 want_pass: false,
224 },
225 Test {
226 name: "Bad consensus height, height is 0".to_string(),
227 raw: RawMsgConnectionOpenAck {
228 consensus_height: Some(Height {
229 revision_number: 1,
230 revision_height: 0,
231 }),
232 ..default_ack_msg
233 },
234 want_pass: false,
235 },
236 ]
237 .into_iter()
238 .collect();
239
240 for test in tests {
241 let msg = MsgConnectionOpenAck::try_from(test.raw.clone());
242
243 assert_eq!(
244 test.want_pass,
245 msg.is_ok(),
246 "MsgConnOpenAck::new failed for test {}, \nmsg {:?} with error {:?}",
247 test.name,
248 test.raw,
249 msg.err(),
250 );
251 }
252 }
253
254 #[test]
255 fn to_and_from() {
256 let raw = get_dummy_raw_msg_conn_open_ack(5, 6);
257 let msg = MsgConnectionOpenAck::try_from(raw.clone()).unwrap();
258 let raw_back = RawMsgConnectionOpenAck::from(msg.clone());
259 let msg_back = MsgConnectionOpenAck::try_from(raw_back.clone()).unwrap();
260 assert_eq!(raw, raw_back);
261 assert_eq!(msg, msg_back);
262 }
263}