1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
use core::time::Duration;

use ibc_core_host_types::identifiers::ClientId;
use ibc_primitives::prelude::*;
use ibc_primitives::Signer;
use ibc_proto::ibc::core::connection::v1::MsgConnectionOpenInit as RawMsgConnectionOpenInit;
use ibc_proto::Protobuf;

use crate::connection::Counterparty;
use crate::error::ConnectionError;
use crate::version::Version;

pub const CONN_OPEN_INIT_TYPE_URL: &str = "/ibc.core.connection.v1.MsgConnectionOpenInit";

/// Per our convention, this message is sent to chain A.
/// The handler will check proofs of chain B.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct MsgConnectionOpenInit {
    /// ClientId on chain A that the connection is being opened for
    pub client_id_on_a: ClientId,
    pub counterparty: Counterparty,
    pub version: Option<Version>,
    pub delay_period: Duration,
    pub signer: Signer,
}

/// This module encapsulates the workarounds we need to do to implement
/// `BorshSerialize` and `BorshDeserialize` on `MsgConnectionOpenInit`
#[cfg(feature = "borsh")]
mod borsh_impls {
    use borsh::maybestd::io::{self, Read};
    use borsh::{BorshDeserialize, BorshSerialize};

    use super::*;

    #[derive(BorshSerialize, BorshDeserialize)]
    pub struct InnerMsgConnectionOpenInit {
        /// ClientId on chain A that the connection is being opened for
        pub client_id_on_a: ClientId,
        pub counterparty: Counterparty,
        pub version: Option<Version>,
        pub delay_period_nanos: u64,
        pub signer: Signer,
    }

    impl BorshSerialize for MsgConnectionOpenInit {
        fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
            let delay_period_nanos: u64 =
                self.delay_period.as_nanos().try_into().map_err(|_| {
                    io::Error::new(
                        io::ErrorKind::Other,
                        format!("Duration too long: {} nanos", self.delay_period.as_nanos()),
                    )
                })?;

            let inner = InnerMsgConnectionOpenInit {
                client_id_on_a: self.client_id_on_a.clone(),
                counterparty: self.counterparty.clone(),
                version: self.version.clone(),
                delay_period_nanos,
                signer: self.signer.clone(),
            };

            inner.serialize(writer)
        }
    }

    impl BorshDeserialize for MsgConnectionOpenInit {
        fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
            let inner = InnerMsgConnectionOpenInit::deserialize_reader(reader)?;

            Ok(MsgConnectionOpenInit {
                client_id_on_a: inner.client_id_on_a,
                counterparty: inner.counterparty,
                version: inner.version,
                delay_period: Duration::from_nanos(inner.delay_period_nanos),
                signer: inner.signer,
            })
        }
    }
}

impl Protobuf<RawMsgConnectionOpenInit> for MsgConnectionOpenInit {}

impl TryFrom<RawMsgConnectionOpenInit> for MsgConnectionOpenInit {
    type Error = ConnectionError;

    fn try_from(msg: RawMsgConnectionOpenInit) -> Result<Self, Self::Error> {
        let counterparty: Counterparty = msg
            .counterparty
            .ok_or(ConnectionError::MissingCounterparty)?
            .try_into()?;

        counterparty.verify_empty_connection_id()?;

        Ok(Self {
            client_id_on_a: msg
                .client_id
                .parse()
                .map_err(ConnectionError::InvalidIdentifier)?,
            counterparty,
            version: msg.version.map(TryInto::try_into).transpose()?,
            delay_period: Duration::from_nanos(msg.delay_period),
            signer: msg.signer.into(),
        })
    }
}

impl From<MsgConnectionOpenInit> for RawMsgConnectionOpenInit {
    fn from(ics_msg: MsgConnectionOpenInit) -> Self {
        RawMsgConnectionOpenInit {
            client_id: ics_msg.client_id_on_a.as_str().to_string(),
            counterparty: Some(ics_msg.counterparty.into()),
            version: ics_msg.version.map(Into::into),
            delay_period: ics_msg.delay_period.as_nanos() as u64,
            signer: ics_msg.signer.to_string(),
        }
    }
}