diff --git a/core/src/ibc/connection_03/manager.rs b/core/src/ibc/connection_03/manager.rs
new file mode 100644
index 0000000000..44a5487f45
--- /dev/null
+++ b/core/src/ibc/connection_03/manager.rs
@@ -0,0 +1,77 @@
+// Copyright 2020 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use super::path as connection_path;
+use super::types::{CommitmentPrefix, Identifier};
+use crate::ibc;
+use crate::ibc::connection_03::client_connections_path;
+use crate::ibc::connection_03::types::{ConnectionEnd, ConnectionIdentifiersInClient, ConnectionState};
+use rlp::{Encodable, Rlp};
+
+#[derive(Default)]
+pub struct Manager {}
+
+impl Manager {
+ pub fn new() -> Self {
+ Manager {}
+ }
+
+ pub fn handle_open_init(
+ &self,
+ ctx: &mut dyn ibc::Context,
+ identifier: Identifier,
+ desired_counterparty_connection_identifier: Identifier,
+ counterparty_prefix: CommitmentPrefix,
+ client_identifier: Identifier,
+ counterparty_client_identifier: Identifier,
+ ) -> Result<(), String> {
+ let kv_store = ctx.get_kv_store();
+ if kv_store.has(&connection_path(&identifier)) {
+ return Err("Connection exist".to_owned())
+ }
+ let state = ConnectionState::INIT;
+ let connection = ConnectionEnd {
+ state,
+ counterparty_connection_identifier: desired_counterparty_connection_identifier,
+ counterparty_prefix,
+ client_identifier: client_identifier.clone(),
+ counterparty_client_identifier,
+ };
+ kv_store.set(&connection_path(&identifier), &connection.rlp_bytes());
+ self.add_connection_to_client(ctx, client_identifier, identifier)?;
+ Ok(())
+ }
+
+ fn add_connection_to_client(
+ &self,
+ ctx: &mut dyn ibc::Context,
+ client_identifier: Identifier,
+ connection_identifier: Identifier,
+ ) -> Result<(), String> {
+ let kv_store = ctx.get_kv_store();
+ if kv_store.has(&connection_path(&connection_identifier)) {
+ return Err("Connection exist".to_owned())
+ }
+ let bytes = kv_store.get(&client_connections_path(&client_identifier));
+ let rlp = Rlp::new(&bytes);
+ let mut conns: ConnectionIdentifiersInClient = rlp.as_val().expect("data from DB");
+
+ conns.add(connection_identifier);
+
+ kv_store.set(&client_connections_path(&client_identifier), &rlp::encode(&conns));
+ Ok(())
+ }
+}
diff --git a/core/src/ibc/connection_03/mod.rs b/core/src/ibc/connection_03/mod.rs
new file mode 100644
index 0000000000..d376d8f7eb
--- /dev/null
+++ b/core/src/ibc/connection_03/mod.rs
@@ -0,0 +1,28 @@
+// Copyright 2020 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+mod manager;
+mod types;
+
+pub fn path(id: &str) -> String {
+ format!("connections/{}", id)
+}
+
+pub fn client_connections_path(client_id: &str) -> String {
+ format!("clients/{}/connections", client_id)
+}
+
+pub use manager::Manager;
diff --git a/core/src/ibc/connection_03/types.rs b/core/src/ibc/connection_03/types.rs
new file mode 100644
index 0000000000..6622242f3f
--- /dev/null
+++ b/core/src/ibc/connection_03/types.rs
@@ -0,0 +1,97 @@
+// Copyright 2020 Kodebox, Inc.
+// This file is part of CodeChain.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see .
+
+use rlp;
+use rlp::{DecoderError, Rlp, RlpStream};
+
+#[repr(u8)]
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum ConnectionState {
+ INIT = 0,
+ TRYOPEN = 1,
+ OPEN = 2,
+}
+
+impl rlp::Encodable for ConnectionState {
+ fn rlp_append(&self, s: &mut RlpStream) {
+ s.append_single_value(&(*self as u8));
+ }
+}
+
+impl rlp::Decodable for ConnectionState {
+ fn decode(rlp: &Rlp) -> Result {
+ let byte: u8 = rlp.as_val()?;
+ match byte {
+ 0 => Ok(ConnectionState::INIT),
+ 1 => Ok(ConnectionState::TRYOPEN),
+ 2 => Ok(ConnectionState::OPEN),
+ _ => Err(DecoderError::Custom("Unexpected ConsensusState Value")),
+ }
+ }
+}
+
+// FIXME: current commitment_23::Prefix is too generic.
+pub type CommitmentPrefix = String;
+pub type Identifier = String;
+
+#[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)]
+pub struct ConnectionEnd {
+ pub state: ConnectionState,
+ pub counterparty_connection_identifier: Identifier,
+ pub counterparty_prefix: CommitmentPrefix,
+ pub client_identifier: Identifier,
+ pub counterparty_client_identifier: Identifier,
+ // FIXME: implement version
+}
+
+#[derive(RlpEncodableWrapper, RlpDecodableWrapper, PartialEq, Debug)]
+pub struct ConnectionIdentifiersInClient(Vec);
+
+impl ConnectionIdentifiersInClient {
+ pub fn add(&mut self, identifier: Identifier) {
+ self.0.push(identifier);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use rlp::{self, rlp_encode_and_decode_test};
+
+ use super::*;
+
+ #[test]
+ fn connection_state() {
+ rlp_encode_and_decode_test!(ConnectionState::INIT);
+ }
+
+ #[test]
+ fn connection_end() {
+ let connection_end = ConnectionEnd {
+ state: ConnectionState::INIT,
+ counterparty_connection_identifier: "counterparty_connection_identifier".to_owned(),
+ counterparty_prefix: "counterparty_prefix".to_owned(),
+ client_identifier: "client_identifier".to_owned(),
+ counterparty_client_identifier: "counterparty_client_identifier".to_owned(),
+ };
+ rlp_encode_and_decode_test!(connection_end);
+ }
+
+ #[test]
+ fn connection_identifiers_in_client() {
+ let identifiers = ConnectionIdentifiersInClient(vec!["a".to_owned(), "b".to_owned()]);
+ rlp_encode_and_decode_test!(identifiers);
+ }
+}
diff --git a/core/src/ibc/mod.rs b/core/src/ibc/mod.rs
index 1952c23347..5eadfc86a4 100644
--- a/core/src/ibc/mod.rs
+++ b/core/src/ibc/mod.rs
@@ -18,6 +18,9 @@ pub mod client_02;
#[allow(dead_code)]
#[allow(unused_variables)]
mod commitment_23;
+#[allow(dead_code)]
+#[allow(unused_variables)]
+mod connection_03;
pub mod context;
mod kv_store;
mod transaction_handler;
diff --git a/core/src/ibc/transaction_handler/datagrams.rs b/core/src/ibc/transaction_handler/datagrams.rs
index a0012b118f..cd013524b3 100644
--- a/core/src/ibc/transaction_handler/datagrams.rs
+++ b/core/src/ibc/transaction_handler/datagrams.rs
@@ -16,8 +16,31 @@
use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream};
-const DATAGRAM_CREATE_CLIENT: u8 = 1;
-const DATAGRAM_UPDATE_CLIENT: u8 = 2;
+#[repr(u8)]
+#[derive(Clone, Copy)]
+enum DatagramTag {
+ CreateClient = 1,
+ UpdateClient = 2,
+ ConnOpenInit = 3,
+}
+
+impl Encodable for DatagramTag {
+ fn rlp_append(&self, s: &mut RlpStream) {
+ s.append_single_value(&(*self as u8));
+ }
+}
+
+impl Decodable for DatagramTag {
+ fn decode(rlp: &Rlp) -> Result {
+ let byte: u8 = rlp.as_val()?;
+ match byte {
+ 1 => Ok(DatagramTag::CreateClient),
+ 2 => Ok(DatagramTag::UpdateClient),
+ 3 => Ok(DatagramTag::ConnOpenInit),
+ _ => Err(DecoderError::Custom("Unexpected DatagramTag Value")),
+ }
+ }
+}
#[derive(Debug, PartialEq)]
pub enum Datagram {
@@ -30,6 +53,13 @@ pub enum Datagram {
id: String,
header: Vec,
},
+ ConnOpenInit {
+ identifier: String,
+ desired_counterparty_connection_identifier: String,
+ counterparty_prefix: String,
+ client_identifier: String,
+ counterparty_client_identifier: String,
+ },
}
impl Encodable for Datagram {
@@ -40,13 +70,28 @@ impl Encodable for Datagram {
kind,
consensus_state,
} => {
- s.begin_list(4).append(&DATAGRAM_CREATE_CLIENT).append(id).append(kind).append(consensus_state);
+ s.begin_list(4).append(&DatagramTag::CreateClient).append(id).append(kind).append(consensus_state);
}
Datagram::UpdateClient {
id,
header,
} => {
- s.begin_list(3).append(&DATAGRAM_UPDATE_CLIENT).append(id).append(header);
+ s.begin_list(3).append(&DatagramTag::UpdateClient).append(id).append(header);
+ }
+ Datagram::ConnOpenInit {
+ identifier,
+ desired_counterparty_connection_identifier,
+ counterparty_prefix,
+ client_identifier,
+ counterparty_client_identifier,
+ } => {
+ s.begin_list(6);
+ s.append(&DatagramTag::ConnOpenInit)
+ .append(identifier)
+ .append(desired_counterparty_connection_identifier)
+ .append(counterparty_prefix)
+ .append(client_identifier)
+ .append(counterparty_client_identifier);
}
};
}
@@ -56,7 +101,7 @@ impl Decodable for Datagram {
fn decode(rlp: &Rlp) -> Result {
let tag = rlp.val_at(0)?;
match tag {
- DATAGRAM_CREATE_CLIENT => {
+ DatagramTag::CreateClient => {
let item_count = rlp.item_count()?;
if item_count != 4 {
return Err(DecoderError::RlpInvalidLength {
@@ -70,7 +115,7 @@ impl Decodable for Datagram {
consensus_state: rlp.val_at(3)?,
})
}
- DATAGRAM_UPDATE_CLIENT => {
+ DatagramTag::UpdateClient => {
let item_count = rlp.item_count()?;
if item_count != 3 {
return Err(DecoderError::RlpInvalidLength {
@@ -83,7 +128,22 @@ impl Decodable for Datagram {
header: rlp.val_at(2)?,
})
}
- _ => Err(DecoderError::Custom("Unexpected IBC Datagram Type")),
+ DatagramTag::ConnOpenInit => {
+ let item_count = rlp.item_count()?;
+ if item_count != 6 {
+ return Err(DecoderError::RlpInvalidLength {
+ expected: 6,
+ got: item_count,
+ })
+ }
+ Ok(Datagram::ConnOpenInit {
+ identifier: rlp.val_at(1)?,
+ desired_counterparty_connection_identifier: rlp.val_at(2)?,
+ counterparty_prefix: rlp.val_at(3)?,
+ client_identifier: rlp.val_at(4)?,
+ counterparty_client_identifier: rlp.val_at(5)?,
+ })
+ }
}
}
}
diff --git a/core/src/ibc/transaction_handler/mod.rs b/core/src/ibc/transaction_handler/mod.rs
index 0eb348f13f..ed345e8706 100644
--- a/core/src/ibc/transaction_handler/mod.rs
+++ b/core/src/ibc/transaction_handler/mod.rs
@@ -23,6 +23,7 @@ use cstate::{StateResult, TopLevelState};
use ctypes::errors::RuntimeError;
use ibc::client_02 as ibc_client;
use ibc::client_02::foundry as ibc_foundry;
+use ibc::connection_03 as ibc_connection;
use ibc::context as ibc_context;
use rlp::{Decodable, Rlp};
@@ -43,6 +44,26 @@ pub fn execute(
id,
header,
} => update_client(state, &id, &header),
+ Datagram::ConnOpenInit {
+ identifier,
+ desired_counterparty_connection_identifier,
+ counterparty_prefix,
+ client_identifier,
+ counterparty_client_identifier,
+ } => {
+ let mut context = ibc_context::TopLevelContext::new(state);
+ let connection_manager = ibc_connection::Manager::new();
+ connection_manager
+ .handle_open_init(
+ &mut context,
+ identifier,
+ desired_counterparty_connection_identifier,
+ counterparty_prefix,
+ client_identifier,
+ counterparty_client_identifier,
+ )
+ .map_err(|err| RuntimeError::IBC(format!("ConnOpenInit: {}", err)).into())
+ }
}
}