From 01965c582fbca4124f613f76dbed5e1652da70c1 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 18:38:12 +0900 Subject: [PATCH 01/10] Correct the return value of the `insert` function in the IBC's KVStore The insert function should return the previous value. If the value does not exist, it should return None. However, it is returning rlp([]) instead. Since this is the PoC branch, I fixed this temporarily. --- core/src/ibc/context.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/ibc/context.rs b/core/src/ibc/context.rs index 49404b8de0..e8a807aa59 100644 --- a/core/src/ibc/context.rs +++ b/core/src/ibc/context.rs @@ -81,8 +81,13 @@ impl<'a> KVStore for TopLevelKVStore<'a> { } fn insert(&mut self, path: Path, value: &[u8]) -> Option { + // FIXME: the update_ibc_data returns the previous data. + // When the previous data is empty, it should return None. + // Currently it is returning an empty RLP array. + let prev = self.get(path); let key = TopLevelKVStore::key(path); - self.state.update_ibc_data(&key, value.to_vec()).expect("Set in IBC KVStore").map(Bytes::from) + self.state.update_ibc_data(&key, value.to_vec()).expect("Set in IBC KVStore"); + prev } fn remove(&mut self, path: Path) -> Option { From 5aa3577b0c628ee6e6bdb2bc9cbb0fa94a9e2a03 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 15:28:29 +0900 Subject: [PATCH 02/10] Flatten channel datagrams' rlp serialization format Before this commit, serialization/deserialization does not work together. This commit fix it by moving datagram tag in the inner type and flattening it. --- core/src/ibc/transaction_handler/datagrams.rs | 46 ++++++++++++++----- 1 file changed, 35 insertions(+), 11 deletions(-) diff --git a/core/src/ibc/transaction_handler/datagrams.rs b/core/src/ibc/transaction_handler/datagrams.rs index a236b41a76..ee331cf7d5 100644 --- a/core/src/ibc/transaction_handler/datagrams.rs +++ b/core/src/ibc/transaction_handler/datagrams.rs @@ -19,8 +19,8 @@ use primitives::Bytes; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; #[repr(u8)] -#[derive(Clone, Copy)] -enum DatagramTag { +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum DatagramTag { CreateClient = 1, UpdateClient = 2, ConnOpenInit = 3, @@ -72,6 +72,7 @@ impl Decodable for DatagramTag { // This is because of RLP macro's weak support. #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanOpenInit { + pub tag: DatagramTag, pub order: u8, pub connection: String, pub channel_identifier: String, @@ -80,6 +81,7 @@ pub struct ChanOpenInit { } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanOpenTry { + pub tag: DatagramTag, pub order: u8, pub connection: String, pub channel_identifier: String, @@ -91,6 +93,7 @@ pub struct ChanOpenTry { } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanOpenAck { + pub tag: DatagramTag, pub channel_identifier: String, pub counterparty_version: String, pub proof_try: Bytes, @@ -98,26 +101,31 @@ pub struct ChanOpenAck { } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanOpenConfirm { + pub tag: DatagramTag, pub channel_identifier: String, pub proof_ack: Bytes, pub proof_height: u64, } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanCloseInit { + pub tag: DatagramTag, pub channel_identifier: String, } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanCloseConfirm { + pub tag: DatagramTag, pub channel_identifier: String, pub proof_init: Bytes, pub proof_height: u64, } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct SendPacket { + pub tag: DatagramTag, pub packet: Packet, } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct RecvPacket { + pub tag: DatagramTag, pub packet: Packet, pub proof: Bytes, pub proof_height: u64, @@ -125,6 +133,7 @@ pub struct RecvPacket { } #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct AcknowledgePacket { + pub tag: DatagramTag, pub packet: Packet, pub ack: Bytes, pub proof: Bytes, @@ -288,47 +297,47 @@ impl Encodable for Datagram { Datagram::ChanOpenInit { raw, } => { - s.append(&DatagramTag::ChanOpenInit).append(raw); + s.append_single_value(raw); } Datagram::ChanOpenTry { raw, } => { - s.append(&DatagramTag::ChanOpenTry).append(raw); + s.append_single_value(raw); } Datagram::ChanOpenAck { raw, } => { - s.append(&DatagramTag::ChanOpenAck).append(raw); + s.append_single_value(raw); } Datagram::ChanOpenConfirm { raw, } => { - s.append(&DatagramTag::ChanOpenConfirm).append(raw); + s.append_single_value(raw); } Datagram::ChanCloseInit { raw, } => { - s.append(&DatagramTag::ChanCloseInit).append(raw); + s.append_single_value(raw); } Datagram::ChanCloseConfirm { raw, } => { - s.append(&DatagramTag::ChanCloseConfirm).append(raw); + s.append_single_value(raw); } Datagram::SendPacket { raw, } => { - s.append(&DatagramTag::SendPacket).append(raw); + s.append_single_value(raw); } Datagram::RecvPacket { raw, } => { - s.append(&DatagramTag::RecvPacket).append(raw); + s.append_single_value(raw); } Datagram::AcknowledgePacket { raw, } => { - s.append(&DatagramTag::AcknowledgePacket).append(raw); + s.append_single_value(raw); } }; } @@ -517,4 +526,19 @@ mod tests { }; rlp_encode_and_decode_test!(conn_open_confirm); } + + #[test] + fn chann_open_init() { + let chan_open_init = Datagram::ChanOpenInit { + raw: ChanOpenInit { + tag: DatagramTag::ChanOpenInit, + order: 1, + connection: "connection".to_owned(), + channel_identifier: "channel".to_owned(), + counterparty_channel_identifier: "counterparty_channel".to_owned(), + version: "version".to_owned(), + }, + }; + rlp_encode_and_decode_test!(chan_open_init); + } } From 46e2c8cf155fff505629bdb390000634d5c42376 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 16:20:38 +0900 Subject: [PATCH 03/10] Fix ChannelOrder deserialize code In the datagram code, ORDERED is 1 and UNORDERED is 0. In the ChannelOrder type, ORDERED is 0 and UNORDERED is 1. I made the datagram code to use the ChannelOrder type. --- core/src/ibc/transaction_handler/datagrams.rs | 7 ++++--- core/src/ibc/transaction_handler/mod.rs | 16 ++-------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/core/src/ibc/transaction_handler/datagrams.rs b/core/src/ibc/transaction_handler/datagrams.rs index ee331cf7d5..fdf5be6d9b 100644 --- a/core/src/ibc/transaction_handler/datagrams.rs +++ b/core/src/ibc/transaction_handler/datagrams.rs @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +use crate::ibc::channel_04::types::ChannelOrder; use crate::ibc::channel_04::types::Packet; use primitives::Bytes; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; @@ -73,7 +74,7 @@ impl Decodable for DatagramTag { #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanOpenInit { pub tag: DatagramTag, - pub order: u8, + pub order: ChannelOrder, pub connection: String, pub channel_identifier: String, pub counterparty_channel_identifier: String, @@ -82,7 +83,7 @@ pub struct ChanOpenInit { #[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] pub struct ChanOpenTry { pub tag: DatagramTag, - pub order: u8, + pub order: ChannelOrder, pub connection: String, pub channel_identifier: String, pub counterparty_channel_identifier: String, @@ -532,7 +533,7 @@ mod tests { let chan_open_init = Datagram::ChanOpenInit { raw: ChanOpenInit { tag: DatagramTag::ChanOpenInit, - order: 1, + order: ChannelOrder::ORDERED, connection: "connection".to_owned(), channel_identifier: "channel".to_owned(), counterparty_channel_identifier: "counterparty_channel".to_owned(), diff --git a/core/src/ibc/transaction_handler/mod.rs b/core/src/ibc/transaction_handler/mod.rs index bd1f20347a..1131d9b50f 100644 --- a/core/src/ibc/transaction_handler/mod.rs +++ b/core/src/ibc/transaction_handler/mod.rs @@ -151,13 +151,7 @@ pub fn execute( let mut channel_manager = ibc_channel::Manager::new(&mut context); channel_manager .chan_open_init( - { - if raw.order == 1 { - ibc::channel_04::types::ChannelOrder::ORDERED - } else { - ibc::channel_04::types::ChannelOrder::UNORDERED - } - }, + raw.order, raw.connection, raw.channel_identifier, raw.counterparty_channel_identifier, @@ -172,13 +166,7 @@ pub fn execute( let mut channel_manager = ibc_channel::Manager::new(&mut context); channel_manager .chan_open_try( - { - if raw.order == 1 { - ibc::channel_04::types::ChannelOrder::ORDERED - } else { - ibc::channel_04::types::ChannelOrder::UNORDERED - } - }, + raw.order, raw.connection, raw.channel_identifier, raw.counterparty_channel_identifier, From 2dc4fe42d3cc171a47cd4e77f1f7404bdc007149 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 11 Mar 2020 14:13:27 +0900 Subject: [PATCH 04/10] Fix connection hop to use the same chain's identifier There was a problem with the ICS spec. The spec was not distinguishing the connection's identifier in the two chains. Since a connection's identifier is different in each chain, we should use the identifier carefully. --- core/src/ibc/channel_04/manager.rs | 31 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/core/src/ibc/channel_04/manager.rs b/core/src/ibc/channel_04/manager.rs index 9530d2039b..dae07f3d79 100644 --- a/core/src/ibc/channel_04/manager.rs +++ b/core/src/ibc/channel_04/manager.rs @@ -54,12 +54,17 @@ impl<'a> Manager<'a> { } // Utility functions - fn check_connection_opened(&self, id: IdentifierSlice) -> Result { + fn query_connection(&self, connection_id: IdentifierSlice) -> Result { let kv_store = self.ctx.get_kv_store(); - let connection_end: ConnectionEnd = - rlp::decode(&kv_store.get(&connection_path(&id)).ok_or_else(|| "Connection doesn't exist".to_owned())?) - .expect("Illformed ConnectionEnd stored in the DB"); + let connection_end: ConnectionEnd = rlp::decode( + &kv_store.get(&connection_path(&connection_id)).ok_or_else(|| "Connection doesn't exist".to_owned())?, + ) + .expect("Illformed ConnectionEnd stored in the DB"); + Ok(connection_end) + } + fn check_connection_opened(&self, id: IdentifierSlice) -> Result { + let connection_end = self.query_connection(id)?; if connection_end.state != ConnectionState::OPEN { return Err("Connection not opened".to_owned()) } @@ -203,6 +208,7 @@ impl<'a> Manager<'a> { } let client_identifier = self.check_connection_opened(&connection)?; + let connection_end = self.query_connection(&connection)?; let expected = ChannelEnd { state: ChannelState::INIT, @@ -210,7 +216,7 @@ impl<'a> Manager<'a> { counterparty_port_identifier: DEFAULT_PORT.to_string(), counterparty_channel_identifier: channel_identifier.clone(), // Note: the array should be reversed in the future where `connection` becomes an array. - connection_hops: vec![connection.clone()], + connection_hops: vec![connection_end.counterparty_connection_identifier], version: counterparty_version, }; @@ -284,9 +290,8 @@ impl<'a> Manager<'a> { counterparty_port_identifier: DEFAULT_PORT.to_string(), counterparty_channel_identifier: channel_identifier.clone(), connection_hops: { - let mut x = previous.connection_hops.clone(); - x.reverse(); - x + let connection_end = self.query_connection(&previous.connection_hops[0])?; + vec![connection_end.counterparty_connection_identifier] }, version: counterparty_version.clone(), }; @@ -330,9 +335,8 @@ impl<'a> Manager<'a> { counterparty_port_identifier: DEFAULT_PORT.to_string(), counterparty_channel_identifier: channel_identifier.clone(), connection_hops: { - let mut x = previous.connection_hops.clone(); - x.reverse(); - x + let connection_end = self.query_connection(&previous.connection_hops[0])?; + vec![connection_end.counterparty_connection_identifier] }, version: previous.version.clone(), }; @@ -392,9 +396,8 @@ impl<'a> Manager<'a> { counterparty_port_identifier: DEFAULT_PORT.to_string(), counterparty_channel_identifier: channel_identifier.clone(), connection_hops: { - let mut x = previous.connection_hops.clone(); - x.reverse(); - x + let connection_end = self.query_connection(&previous.connection_hops[0])?; + vec![connection_end.counterparty_connection_identifier] }, version: previous.version.clone(), }; From c3a9661a07ae1e237bec8fd07082cf41cded9774 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 18:27:03 +0900 Subject: [PATCH 05/10] Log all IBC transaction error Before this commit, we tried to log errors from executing datagram by checking the return value. The '?' operator, however, returns an error immediately. So I made a wrapper function and check errors in the wrapper function. --- core/src/ibc/transaction_handler/mod.rs | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/core/src/ibc/transaction_handler/mod.rs b/core/src/ibc/transaction_handler/mod.rs index 1131d9b50f..7c8638342a 100644 --- a/core/src/ibc/transaction_handler/mod.rs +++ b/core/src/ibc/transaction_handler/mod.rs @@ -29,6 +29,22 @@ use ibc::context as ibc_context; use rlp::{Decodable, Rlp}; pub fn execute( + bytes: &[u8], + state: &mut TopLevelState, + fee_payer: &Address, + sender_public: &Public, + current_block_number: u64, +) -> StateResult<()> { + let result = execute_inner(bytes, state, fee_payer, sender_public, current_block_number); + + if let Err(err) = &result { + cwarn!(IBC, "Executing datagram failed: {:?}", err); + } + + result +} + +fn execute_inner( bytes: &[u8], state: &mut TopLevelState, _fee_payer: &Address, @@ -49,7 +65,7 @@ pub fn execute( } } - let result = match datagram { + match datagram { Datagram::CreateClient { id, kind, @@ -248,11 +264,5 @@ pub fn execute( .map_err(|err| RuntimeError::IBC(format!("AcknowledgePacket: {}", err)))?; Ok(()) } - }; - - if let Err(err) = &result { - cwarn!(IBC, "Executing datagram failed: {:?}", err); } - - result } From ef95b1a65a5a627d0f31fdef3634ce8f65dff784 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 12:10:50 +0900 Subject: [PATCH 06/10] Make relayer wait for light client creation Before this commit, we should run the relayer after creating light clients. After this commit, we can run the relayer before creating light clients. It will make us to run tests easier. --- ibc.ts/src/relayer/index.ts | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index 36c6284262..5358b78932 100644 --- a/ibc.ts/src/relayer/index.ts +++ b/ibc.ts/src/relayer/index.ts @@ -41,7 +41,15 @@ async function main() { while (true) { debug("Run relay"); - await relay(chainA, chainB); + try { + await relay(chainA, chainB); + } catch (err) { + if (err.message === "NoClient") { + console.log("Light client does not exist"); + } else { + throw err; + } + } await delay(1000); } } @@ -137,9 +145,7 @@ async function updateLightClient({ const clientState = await chain.queryClient(height); if (clientState!.data == null) { - throw new Error( - `No client state found. Please create a light client with identifier: ${chain.counterpartyIdentifiers.client}` - ); + throw new Error("NoClient"); } let currentBlockNumber = clientState!.data!.number; // FIXME: We can't get the best block's IBC header From 2dcd31692770f6985adcdd9302d808d1ed53aea7 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 12:28:47 +0900 Subject: [PATCH 07/10] Add queryChannel method in the ibc.ts --- ibc.ts/src/common/chain.ts | 11 ++++++++++- ibc.ts/src/common/types.ts | 9 +++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/ibc.ts/src/common/chain.ts b/ibc.ts/src/common/chain.ts index 95f52f124a..978c0d5450 100644 --- a/ibc.ts/src/common/chain.ts +++ b/ibc.ts/src/common/chain.ts @@ -5,7 +5,7 @@ import { IBC } from "./foundry/transaction"; import { delay } from "./util"; import Debug from "debug"; import { ClientState } from "./foundry/types"; -import { IBCHeader, IBCQueryResult, ConnectionEnd } from "./types"; +import { IBCHeader, IBCQueryResult, ConnectionEnd, ChannelEnd } from "./types"; const debug = Debug("common:tx"); @@ -126,6 +126,15 @@ export class Chain { blockNumber ]); } + + public async queryChannel( + blockNumber?: number + ): Promise | null> { + return this.sdk.rpc.sendRpcRequest("", [ + this.counterpartyIdentifiers.channel, + blockNumber + ]); + } } async function waitForTx(sdk: SDK, txHash: H256) { diff --git a/ibc.ts/src/common/types.ts b/ibc.ts/src/common/types.ts index 256937212c..9023607a81 100644 --- a/ibc.ts/src/common/types.ts +++ b/ibc.ts/src/common/types.ts @@ -12,3 +12,12 @@ export interface ConnectionEnd { clientIdentifier: string; counterpartyClientIdentifier: string; } + +export interface ChannelEnd { + state: "INIT" | "TRYOPEN" | "OPEN" | "CLOSED"; + ordering: "ORDERED" | "UNORDERED"; + counterpartyPortIdentifier: string; + counterpartyChannelIdentifier: string; + connectionHops: string[]; + version: string; +} From 5e7d8ede8430a3e9e47fc6d9a1465ed2af8a44ab Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 15:59:21 +0900 Subject: [PATCH 08/10] Add channel establishing datagrams in the ibc.ts --- ibc.ts/src/common/datagram/chanOpenAck.ts | 39 ++++++++++++ ibc.ts/src/common/datagram/chanOpenConfirm.ts | 29 +++++++++ ibc.ts/src/common/datagram/chanOpenInit.ts | 44 ++++++++++++++ ibc.ts/src/common/datagram/chanOpenTry.ts | 59 +++++++++++++++++++ 4 files changed, 171 insertions(+) create mode 100644 ibc.ts/src/common/datagram/chanOpenAck.ts create mode 100644 ibc.ts/src/common/datagram/chanOpenConfirm.ts create mode 100644 ibc.ts/src/common/datagram/chanOpenInit.ts create mode 100644 ibc.ts/src/common/datagram/chanOpenTry.ts diff --git a/ibc.ts/src/common/datagram/chanOpenAck.ts b/ibc.ts/src/common/datagram/chanOpenAck.ts new file mode 100644 index 0000000000..9d8f90f121 --- /dev/null +++ b/ibc.ts/src/common/datagram/chanOpenAck.ts @@ -0,0 +1,39 @@ +const RLP = require("rlp"); + +export class ChanOpenAckDatagram { + private channelIdentifier: string; + private counterpartyVersion: string; + private proofTry: Buffer; + private proofHeight: number; + + public constructor({ + channelIdentifier, + counterpartyVersion, + proofTry, + proofHeight + }: { + channelIdentifier: string; + counterpartyVersion: string; + proofTry: Buffer; + proofHeight: number; + }) { + this.channelIdentifier = channelIdentifier; + this.counterpartyVersion = counterpartyVersion; + this.proofTry = proofTry; + this.proofHeight = proofHeight; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 9, + this.channelIdentifier, + this.counterpartyVersion, + this.proofTry, + this.proofHeight + ]; + } +} diff --git a/ibc.ts/src/common/datagram/chanOpenConfirm.ts b/ibc.ts/src/common/datagram/chanOpenConfirm.ts new file mode 100644 index 0000000000..8632745794 --- /dev/null +++ b/ibc.ts/src/common/datagram/chanOpenConfirm.ts @@ -0,0 +1,29 @@ +const RLP = require("rlp"); + +export class ChanOpenConfirmDatagram { + private channelIdentifier: string; + private proofAck: Buffer; + private proofHeight: number; + + public constructor({ + channelIdentifier, + proofAck, + proofHeight + }: { + channelIdentifier: string; + proofAck: Buffer; + proofHeight: number; + }) { + this.channelIdentifier = channelIdentifier; + this.proofAck = proofAck; + this.proofHeight = proofHeight; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [10, this.channelIdentifier, this.proofAck, this.proofHeight]; + } +} diff --git a/ibc.ts/src/common/datagram/chanOpenInit.ts b/ibc.ts/src/common/datagram/chanOpenInit.ts new file mode 100644 index 0000000000..259242e2e3 --- /dev/null +++ b/ibc.ts/src/common/datagram/chanOpenInit.ts @@ -0,0 +1,44 @@ +const RLP = require("rlp"); + +export class ChanOpenInitDatagram { + private order: number; + private connection: string; + private channelIdentifier: string; + private counterpartyChannelIdentifier: string; + private version: string; + + public constructor({ + order, + connection, + channelIdentifier, + counterpartyChannelIdentifier, + version + }: { + order: number; + connection: string; + channelIdentifier: string; + counterpartyChannelIdentifier: string; + version: string; + }) { + this.order = order; + this.connection = connection; + this.channelIdentifier = channelIdentifier; + this.counterpartyChannelIdentifier = counterpartyChannelIdentifier; + this.version = version; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 7, + this.order, + this.connection, + this.channelIdentifier, + this.counterpartyChannelIdentifier, + this.version + ]; + } +} diff --git a/ibc.ts/src/common/datagram/chanOpenTry.ts b/ibc.ts/src/common/datagram/chanOpenTry.ts new file mode 100644 index 0000000000..2fa91bd14a --- /dev/null +++ b/ibc.ts/src/common/datagram/chanOpenTry.ts @@ -0,0 +1,59 @@ +const RLP = require("rlp"); + +export class ChanOpenTryDatagram { + private order: number; + private connection: string; + private channelIdentifier: string; + private counterpartyChannelIdentifier: string; + private version: string; + private counterpartyVersion: string; + private proofInit: Buffer; + private proofHeight: number; + + public constructor({ + order, + connection, + channelIdentifier, + counterpartyChannelIdentifier, + version, + counterpartyVersion, + proofInit, + proofHeight + }: { + order: number; + connection: string; + channelIdentifier: string; + counterpartyChannelIdentifier: string; + version: string; + counterpartyVersion: string; + proofInit: Buffer; + proofHeight: number; + }) { + this.order = order; + this.connection = connection; + this.channelIdentifier = channelIdentifier; + this.counterpartyChannelIdentifier = counterpartyChannelIdentifier; + this.version = version; + this.counterpartyVersion = counterpartyVersion; + this.proofInit = proofInit; + this.proofHeight = proofHeight; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 8, + this.order, + this.connection, + this.channelIdentifier, + this.counterpartyChannelIdentifier, + this.version, + this.counterpartyVersion, + this.proofInit, + this.proofHeight + ]; + } +} From 7434879d52cf08dfc810ce9ff11a40febb48f438 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 17:47:22 +0900 Subject: [PATCH 09/10] Make the IBC scenario script initialize the channel creation --- ibc.ts/src/scenario/index.ts | 72 ++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/ibc.ts/src/scenario/index.ts b/ibc.ts/src/scenario/index.ts index de794d23c3..c2041ac92b 100644 --- a/ibc.ts/src/scenario/index.ts +++ b/ibc.ts/src/scenario/index.ts @@ -5,6 +5,8 @@ import { PlatformAddress } from "codechain-primitives/lib"; import { CreateClientDatagram } from "../common/datagram/createClient"; import { strict as assert } from "assert"; import { ConnOpenInitDatagram } from "../common/datagram/connOpenInit"; +import { ChanOpenInitDatagram } from "../common/datagram/chanOpenInit"; +import { ChannelOrdered } from "../common/datagram"; const { Select } = require("enquirer"); require("dotenv").config(); @@ -95,6 +97,44 @@ async function main() { break; } } + + const channelPrompt = new Select({ + name: "channel", + message: "Will you create a channel?", + choices: ["yes", "skip", "exit"] + }); + const channelAnswer = await channelPrompt.run(); + + if (channelAnswer === "exit") { + return; + } + + if (channelAnswer === "yes") { + console.log("Create a channel"); + await createChannel({ chainA, chainB }); + } + + while (true) { + const channelCheckPrompt = new Select({ + name: "channel check", + message: "Will you check the channel?", + choices: ["yes", "skip", "exit"] + }); + const channelCheckAnsser = await channelCheckPrompt.run(); + + if (channelCheckAnsser === "exit") { + return; + } + + if (channelCheckAnsser === "yes") { + console.log("Check a channel"); + await checkChannels({ chainA, chainB }); + } + + if (channelCheckAnsser === "skip") { + break; + } + } } main().catch(console.error); @@ -175,3 +215,35 @@ async function checkConnections({ const connectionB = await chainB.queryConnection(); console.log(`Connection in B ${JSON.stringify(connectionB)}`); } + +async function createChannel({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}) { + await chainA.submitDatagram( + new ChanOpenInitDatagram({ + order: ChannelOrdered, + connection: chainA.counterpartyIdentifiers.connection, + channelIdentifier: chainA.counterpartyIdentifiers.channel, + counterpartyChannelIdentifier: + chainB.counterpartyIdentifiers.channel, + version: "" + }) + ); +} + +async function checkChannels({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}) { + const channelA = await chainA.queryChannel(); + console.log(`Channel in A ${JSON.stringify(channelA)}`); + const channelB = await chainB.queryChannel(); + console.log(`Channel in B ${JSON.stringify(channelB)}`); +} From 56eb3908d008a9b034ade3459c1c23b891482046 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Tue, 10 Mar 2020 18:00:00 +0900 Subject: [PATCH 10/10] Make the relayer to relay channel creation datagrams --- ibc.ts/src/common/chain.ts | 3 +- ibc.ts/src/common/datagram/index.ts | 3 + ibc.ts/src/relayer/index.ts | 91 ++++++++++++++++++++++++++++- 3 files changed, 95 insertions(+), 2 deletions(-) diff --git a/ibc.ts/src/common/chain.ts b/ibc.ts/src/common/chain.ts index 978c0d5450..99025b967a 100644 --- a/ibc.ts/src/common/chain.ts +++ b/ibc.ts/src/common/chain.ts @@ -130,7 +130,8 @@ export class Chain { public async queryChannel( blockNumber?: number ): Promise | null> { - return this.sdk.rpc.sendRpcRequest("", [ + return this.sdk.rpc.sendRpcRequest("ibc_query_channel_end", [ + "DEFAULT_PORT", this.counterpartyIdentifiers.channel, blockNumber ]); diff --git a/ibc.ts/src/common/datagram/index.ts b/ibc.ts/src/common/datagram/index.ts index 4b10241349..e84d0d1847 100644 --- a/ibc.ts/src/common/datagram/index.ts +++ b/ibc.ts/src/common/datagram/index.ts @@ -2,3 +2,6 @@ export interface Datagram { rlpBytes(): Buffer; toEncodeObject(): any[]; } + +export const ChannelOrdered = 0; +export const ChannelUnordered = 1; diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index 5358b78932..201c0b588e 100644 --- a/ibc.ts/src/relayer/index.ts +++ b/ibc.ts/src/relayer/index.ts @@ -1,6 +1,6 @@ import Debug from "debug"; import { Chain } from "../common/chain"; -import { Datagram } from "../common/datagram/index"; +import { Datagram, ChannelOrdered } from "../common/datagram/index"; import { delay } from "../common/util"; import { getConfig } from "../common/config"; import { PlatformAddress } from "codechain-primitives/lib"; @@ -9,6 +9,9 @@ import { strict as assert } from "assert"; import { ConnOpenTryDatagram } from "../common/datagram/connOpenTry"; import { ConnOpenAckDatagram } from "../common/datagram/connOpenAck"; import { ConnOpenConfirmDatagram } from "../common/datagram/connOpenConfirm"; +import { ChanOpenTryDatagram } from "../common/datagram/chanOpenTry"; +import { ChanOpenAckDatagram } from "../common/datagram/chanOpenAck"; +import { ChanOpenConfirmDatagram } from "../common/datagram/chanOpenConfirm"; require("dotenv").config(); @@ -127,6 +130,21 @@ async function pendingDatagrams({ counterpartyDatagramsForConnection ); + const { + localDatagrams: localDatagramsForChannel, + counterpartyDatagrams: counterpartyDatagramsForChannel + } = await buildChannel({ + chain, + counterpartyChain, + height, + counterpartyChainHeight + }); + + localDatagrams = localDatagrams.concat(localDatagramsForChannel); + counterpartyDatagrams = counterpartyDatagrams.concat( + counterpartyDatagramsForChannel + ); + return { localDatagrams, counterpartyDatagrams }; } @@ -256,3 +274,74 @@ async function buildConnection({ return { localDatagrams, counterpartyDatagrams }; } + +async function buildChannel({ + chain, + counterpartyChain, + height, + counterpartyChainHeight +}: { + chain: Chain; + counterpartyChain: Chain; + height: number; + counterpartyChainHeight: number; +}) { + const localDatagrams: Datagram[] = []; + const counterpartyDatagrams = []; + + const channel = await chain.queryChannel(height); + assert.notEqual(channel, null, "Block at height exists"); + + const counterpartyChannel = await counterpartyChain.queryChannel( + counterpartyChainHeight + ); + assert.notEqual(counterpartyChannel, null, "Block at height exists"); + + if (channel!.data?.state === "INIT" && counterpartyChannel!.data == null) { + counterpartyDatagrams.push( + new ChanOpenTryDatagram({ + order: ChannelOrdered, + connection: + counterpartyChain.counterpartyIdentifiers.connection, + channelIdentifier: + counterpartyChain.counterpartyIdentifiers.channel, + counterpartyChannelIdentifier: + chain.counterpartyIdentifiers.channel, + version: "", + counterpartyVersion: "", + proofInit: Buffer.from(channel!.proof, "hex"), + proofHeight: height + }) + ); + } + if ( + channel!.data?.state === "INIT" && + counterpartyChannel!.data?.state === "TRYOPEN" + ) { + localDatagrams.push( + new ChanOpenAckDatagram({ + channelIdentifier: chain.counterpartyIdentifiers.channel, + counterpartyVersion: "", + proofTry: Buffer.from(counterpartyChannel!.proof, "hex"), + proofHeight: counterpartyChainHeight + }) + ); + } + if ( + channel!.data?.state === "OPEN" && + counterpartyChannel!.data?.state === "TRYOPEN" + ) { + counterpartyDatagrams.push( + new ChanOpenConfirmDatagram({ + channelIdentifier: + counterpartyChain.counterpartyIdentifiers.channel, + proofAck: Buffer.from(channel!.proof, "hex"), + proofHeight: height + }) + ); + } + return { + localDatagrams, + counterpartyDatagrams + }; +}