From 75bd8d06ab109251bac80f682c62bac6c8a7123d Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 5 Mar 2020 16:10:35 +0900 Subject: [PATCH 01/13] Make the serialization format of ConnectionIdentifiers intuitively Before: `` { result: { raw: ["a", "b"] } } ``` After: ``` { result: ["a", "b"] } ``` Change the response of ConnectionIdentifiers --- rpc/src/v1/types/ibc.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/rpc/src/v1/types/ibc.rs b/rpc/src/v1/types/ibc.rs index b004918e93..55cb5fd48d 100644 --- a/rpc/src/v1/types/ibc.rs +++ b/rpc/src/v1/types/ibc.rs @@ -123,16 +123,11 @@ impl FromCore for ConnectionEnd { } #[derive(Debug, Serialize)] -#[serde(rename_all = "camelCase")] -pub struct ConnectionIdentifiersInClient { - raw: Vec, -} +pub struct ConnectionIdentifiersInClient(Vec); impl FromCore for ConnectionIdentifiersInClient { fn from_core(core: CoreConnectionIdentifiersInClient) -> Self { - ConnectionIdentifiersInClient { - raw: core.into_vec(), - } + ConnectionIdentifiersInClient(core.into_vec()) } } From dab11dba9fbd37106ef76e8ddb4e28082e4af96a Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 5 Mar 2020 16:09:43 +0900 Subject: [PATCH 02/13] Fix a wrong condition in the add_connection_to_client When add_connection_to_client is called, the connection should exist. --- core/src/ibc/connection_03/manager.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/ibc/connection_03/manager.rs b/core/src/ibc/connection_03/manager.rs index bb94459549..9109127a7e 100644 --- a/core/src/ibc/connection_03/manager.rs +++ b/core/src/ibc/connection_03/manager.rs @@ -233,8 +233,8 @@ impl<'a> Manager<'a> { connection_identifier: Identifier, ) -> Result<(), String> { let kv_store = self.ctx.get_kv_store_mut(); - if kv_store.contains_key(&connection_path(&connection_identifier)) { - return Err("Connection exist".to_owned()) + if !kv_store.contains_key(&connection_path(&connection_identifier)) { + return Err("Connection does not exist".to_owned()) } let path = client_connections_path(&client_identifier); From b4caab793cad57d86a19d91f1940ebab44c26df9 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Mon, 9 Mar 2020 18:30:48 +0900 Subject: [PATCH 03/13] Rename confusing arguments names in the connection code and fix misuse --- core/src/ibc/client_02/manager.rs | 22 +++++++++++++--------- core/src/ibc/connection_03/manager.rs | 10 +++++----- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/core/src/ibc/client_02/manager.rs b/core/src/ibc/client_02/manager.rs index 33d2df1bcd..d642d10dbc 100644 --- a/core/src/ibc/client_02/manager.rs +++ b/core/src/ibc/client_02/manager.rs @@ -151,15 +151,15 @@ impl<'a> Manager<'a> { pub fn verify_connection_state( &self, - id: IdentifierSlice, + client_identifier: IdentifierSlice, proof_height: BlockNumber, proof: Bytes, - connection_identifier: IdentifierSlice, + counterparty_connection_identifier: IdentifierSlice, connection_end: &ConnectionEnd, ) -> Result<(), String> { - let path = ibc::connection_03::path(connection_identifier); + let path = ibc::connection_03::path(counterparty_connection_identifier); let value_enc = rlp::encode(connection_end); - self.verify_common_presence(id, proof_height, proof, path, value_enc) + self.verify_common_presence(client_identifier, proof_height, proof, path, value_enc) .map_err(|e| format!("{} : connection_state", e)) } @@ -169,10 +169,10 @@ impl<'a> Manager<'a> { proof_height: BlockNumber, proof: Bytes, port_identifier: IdentifierSlice, - channel_identifier: IdentifierSlice, + counterparty_channel_identifier: IdentifierSlice, channel_end: &ChannelEnd, ) -> Result<(), String> { - let path = ibc::channel_04::channel_path(port_identifier, channel_identifier); + let path = ibc::channel_04::channel_path(port_identifier, counterparty_channel_identifier); let value_enc = rlp::encode(channel_end); self.verify_common_presence(id, proof_height, proof, path, value_enc) .map_err(|e| format!("{} : channel_state", e)) @@ -183,12 +183,16 @@ impl<'a> Manager<'a> { id: IdentifierSlice, proof_height: BlockNumber, proof: Bytes, - port_identifier: IdentifierSlice, - channel_identifier: IdentifierSlice, + counterparty_port_identifier: IdentifierSlice, + counterparty_channel_identifier: IdentifierSlice, sequence: &Sequence, packet_commitment: &PacketCommitment, ) -> Result<(), String> { - let path = ibc::channel_04::packet_commitment_path(port_identifier, channel_identifier, sequence); + let path = ibc::channel_04::packet_commitment_path( + counterparty_port_identifier, + counterparty_channel_identifier, + sequence, + ); let value_enc = rlp::encode(&packet_commitment.hash()); self.verify_common_presence(id, proof_height, proof, path, value_enc) .map_err(|e| format!("{} : packet_data", e)) diff --git a/core/src/ibc/connection_03/manager.rs b/core/src/ibc/connection_03/manager.rs index 9109127a7e..cfabd97689 100644 --- a/core/src/ibc/connection_03/manager.rs +++ b/core/src/ibc/connection_03/manager.rs @@ -91,7 +91,7 @@ impl<'a> Manager<'a> { let connection = ConnectionEnd { state: ConnectionState::TRYOPEN, - counterparty_connection_identifier: counterparty_client_identifier.clone(), + counterparty_connection_identifier: counterparty_connection_identifier.clone(), counterparty_prefix: counterparty_prefix.clone(), client_identifier: client_identifier.clone(), counterparty_client_identifier: counterparty_client_identifier.clone(), @@ -99,10 +99,10 @@ impl<'a> Manager<'a> { let client_manager = ClientManager::new(self.ctx); client_manager.verify_connection_state( - &counterparty_client_identifier, + &client_identifier, proof_height, proof_init, - &desired_identifier, + &counterparty_connection_identifier, &expected, )?; @@ -162,7 +162,7 @@ impl<'a> Manager<'a> { &connection.client_identifier, proof_height, proof_try, - &identifier, + &connection.counterparty_connection_identifier, &expected, )?; @@ -200,7 +200,7 @@ impl<'a> Manager<'a> { &connection.client_identifier, proof_height, proof_ack, - &identifier, + &connection.counterparty_connection_identifier, &expected, )?; From d3154178e1bcf40046d22560312432befbc821fb Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 5 Mar 2020 15:38:04 +0900 Subject: [PATCH 04/13] Add a scenario address The relayer and scenario script sends transactions concurrently. To make sequence management easier, I prepared a new account for the scenario script. --- ibc.ts/.env.default | 6 ++++-- ibc.ts/chainA/chainA.schem.json | 4 ++++ ibc.ts/chainA/keystore.db | 21 +++++++++++++++++++++ ibc.ts/chainB/chainB.schem.json | 4 ++++ ibc.ts/chainB/keystore.db | 21 +++++++++++++++++++++ ibc.ts/src/common/config.ts | 9 ++++++--- ibc.ts/src/relayer/index.ts | 4 ++-- ibc.ts/src/scenario/index.ts | 8 ++++++-- 8 files changed, 68 insertions(+), 9 deletions(-) diff --git a/ibc.ts/.env.default b/ibc.ts/.env.default index 7b90570c51..461d6d0ec0 100644 --- a/ibc.ts/.env.default +++ b/ibc.ts/.env.default @@ -1,12 +1,14 @@ CHAIN_A_RPC_URL="http://localhost:18080" CHAIN_A_NETWORK_ID="ac" -CHAIN_A_FAUCET_ADDRESS="accqym7qmn5yj29cdl405xlmx6awd3f3yz07g7vq2c9" +CHAIN_A_RELAYER_ADDRESS="accqym7qmn5yj29cdl405xlmx6awd3f3yz07g7vq2c9" +CHAIN_A_SCENARIO_ADDRESS="accq97692hdn2t0nhfdzzjz8znf0wlav8tq5clzzfts" CHAIN_A_COUNTERPARTY_CLIENT_ID="BClient" CHAIN_A_COUNTERPARTY_CONNECTION_ID="BConnection" CHAIN_A_COUNTERPARTY_CHANNEL_ID="BChannel" CHAIN_B_RPC_URL="http://localhost:18081" CHAIN_B_NETWORK_ID="bc" -CHAIN_B_FAUCET_ADDRESS="bccqygjwzj8wupc9m7du9ccef4j6k2u3erjuv2w8pt0" +CHAIN_B_RELAYER_ADDRESS="bccqygjwzj8wupc9m7du9ccef4j6k2u3erjuv2w8pt0" +CHAIN_B_SCENARIO_ADDRESS="bccq8rd3qky8xqvzjrwwkz3ytgr34fvq40gfysdpk6d" CHAIN_B_COUNTERPARTY_CLIENT_ID="AClient" CHAIN_B_COUNTERPARTY_CONNECTION_ID="AConnection" CHAIN_B_COUNTERPARTY_CHANNEL_ID="AChannel" diff --git a/ibc.ts/chainA/chainA.schem.json b/ibc.ts/chainA/chainA.schem.json index 930507087e..0b8a19425d 100644 --- a/ibc.ts/chainA/chainA.schem.json +++ b/ibc.ts/chainA/chainA.schem.json @@ -56,6 +56,10 @@ "accqym7qmn5yj29cdl405xlmx6awd3f3yz07g7vq2c9": { "balance": "10000000000000000000", "seq": "0" + }, + "accq97692hdn2t0nhfdzzjz8znf0wlav8tq5clzzfts": { + "balance": "10000000000000000000", + "seq": "0" } }, "shards": { diff --git a/ibc.ts/chainA/keystore.db b/ibc.ts/chainA/keystore.db index 6f4feb0a6f..313ccd13bc 100644 --- a/ibc.ts/chainA/keystore.db +++ b/ibc.ts/chainA/keystore.db @@ -21,6 +21,27 @@ "version": 3, "address": "37e06e7424945c37f57d0dfd9b5d736298904ff2", "meta": "{}" + }, + { + "crypto": { + "ciphertext": "49cd2f6d5c6a61d1b554efe959e1b34b54fce2f3d228487fca7f9c7a86464840", + "cipherparams": { + "iv": "f8a7b8e199f110b6cbcf51c0d6beaa64" + }, + "cipher": "aes-128-ctr", + "kdf": "pbkdf2", + "kdfparams": { + "dklen": 32, + "salt": "a3e115ac72241325719152fb3ecd6e0b3155d9ea26f532d45631c42ca293ea63", + "c": 262144, + "prf": "hmac-sha256" + }, + "mac": "20bfd9d4bfb3fe34a94d1b48c7c3126f5ba7b982ad36fe1c45e2397534d906cf" + }, + "id": "a3eca2fc-2cbe-4f08-a13e-b6732c01855c", + "version": 3, + "address": "7da2aaed9a96f9dd2d10a4238a697bbfd61d60a6", + "meta": "{}" } ], "asset": [], diff --git a/ibc.ts/chainB/chainB.schem.json b/ibc.ts/chainB/chainB.schem.json index da9ae5b140..c33c47f003 100644 --- a/ibc.ts/chainB/chainB.schem.json +++ b/ibc.ts/chainB/chainB.schem.json @@ -56,6 +56,10 @@ "bccqygjwzj8wupc9m7du9ccef4j6k2u3erjuv2w8pt0": { "balance": "10000000000000000000", "seq": "0" + }, + "bccq8rd3qky8xqvzjrwwkz3ytgr34fvq40gfysdpk6d": { + "balance": "10000000000000000000", + "seq": "0" } }, "shards": { diff --git a/ibc.ts/chainB/keystore.db b/ibc.ts/chainB/keystore.db index 87ff792fbc..392b61beb1 100644 --- a/ibc.ts/chainB/keystore.db +++ b/ibc.ts/chainB/keystore.db @@ -21,6 +21,27 @@ "version": 3, "address": "11270a47770382efcde1718ca6b2d595c8e472e3", "meta": "{}" + }, + { + "crypto": { + "ciphertext": "985c9bf8e7162cb63af2f57619b136026e94298eda856e62f3118be453eebb3b", + "cipherparams": { + "iv": "4e01dc90d10b76d7a78992e6943fc2b0" + }, + "cipher": "aes-128-ctr", + "kdf": "pbkdf2", + "kdfparams": { + "dklen": 32, + "salt": "d8df814d95379734f387a11fb0630ef5e462828c2f637d7059e10dba426e8b24", + "c": 262144, + "prf": "hmac-sha256" + }, + "mac": "2c5cab0c0712c0cfde538083d2863e1c17cb04c61a1c973bf7c7f2632735afa7" + }, + "id": "5d9fa12c-3307-4e59-bfa1-44dd007fd75d", + "version": 3, + "address": "c6d882c43980c1486e7585122d038d52c055e849", + "meta": "{}" } ], "asset": [], diff --git a/ibc.ts/src/common/config.ts b/ibc.ts/src/common/config.ts index 7d9744f28f..a8e02a2e46 100644 --- a/ibc.ts/src/common/config.ts +++ b/ibc.ts/src/common/config.ts @@ -10,7 +10,8 @@ interface FoundryChainConfig { */ rpcURL: string; networkId: string; - faucetAddress: string; + relayerAddress: string; + scenarioAddress: string; counterpartyClientId: string; counterpartyConnectionId: string; counterpartyChannelId: string; @@ -22,7 +23,8 @@ export function getConfig(): Config { chainA: { rpcURL: getEnv("CHAIN_A_RPC_URL"), networkId: getEnv("CHAIN_A_NETWORK_ID"), - faucetAddress: getEnv("CHAIN_A_FAUCET_ADDRESS"), + relayerAddress: getEnv("CHAIN_A_RELAYER_ADDRESS"), + scenarioAddress: getEnv("CHAIN_A_SCENARIO_ADDRESS"), counterpartyClientId: getEnv("CHAIN_A_COUNTERPARTY_CLIENT_ID"), counterpartyConnectionId: getEnv( "CHAIN_A_COUNTERPARTY_CONNECTION_ID" @@ -33,7 +35,8 @@ export function getConfig(): Config { chainB: { rpcURL: getEnv("CHAIN_B_RPC_URL"), networkId: getEnv("CHAIN_B_NETWORK_ID"), - faucetAddress: getEnv("CHAIN_B_FAUCET_ADDRESS"), + relayerAddress: getEnv("CHAIN_B_RELAYER_ADDRESS"), + scenarioAddress: getEnv("CHAIN_B_SCENARIO_ADDRESS"), counterpartyClientId: getEnv("CHAIN_B_COUNTERPARTY_CLIENT_ID"), counterpartyConnectionId: getEnv( "CHAIN_B_COUNTERPARTY_CONNECTION_ID" diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index 85b3180dbf..f6bf168404 100644 --- a/ibc.ts/src/relayer/index.ts +++ b/ibc.ts/src/relayer/index.ts @@ -16,7 +16,7 @@ async function main() { const chainA = new Chain({ server: config.chainA.rpcURL, networkId: config.chainA.networkId, - faucetAddress: PlatformAddress.fromString(config.chainA.faucetAddress), + faucetAddress: PlatformAddress.fromString(config.chainA.relayerAddress), counterpartyIdentifiers: { client: config.chainA.counterpartyClientId, connection: config.chainA.counterpartyConnectionId, @@ -27,7 +27,7 @@ async function main() { const chainB = new Chain({ server: config.chainB.rpcURL, networkId: config.chainB.networkId, - faucetAddress: PlatformAddress.fromString(config.chainB.faucetAddress), + faucetAddress: PlatformAddress.fromString(config.chainB.relayerAddress), counterpartyIdentifiers: { client: config.chainB.counterpartyClientId, connection: config.chainB.counterpartyConnectionId, diff --git a/ibc.ts/src/scenario/index.ts b/ibc.ts/src/scenario/index.ts index c33e2944d9..cb850ea5bc 100644 --- a/ibc.ts/src/scenario/index.ts +++ b/ibc.ts/src/scenario/index.ts @@ -14,7 +14,9 @@ async function main() { const chainA = new Chain({ server: config.chainA.rpcURL, networkId: config.chainA.networkId, - faucetAddress: PlatformAddress.fromString(config.chainA.faucetAddress), + faucetAddress: PlatformAddress.fromString( + config.chainA.scenarioAddress + ), counterpartyIdentifiers: { client: config.chainA.counterpartyClientId, connection: config.chainA.counterpartyConnectionId, @@ -25,7 +27,9 @@ async function main() { const chainB = new Chain({ server: config.chainB.rpcURL, networkId: config.chainB.networkId, - faucetAddress: PlatformAddress.fromString(config.chainB.faucetAddress), + faucetAddress: PlatformAddress.fromString( + config.chainB.scenarioAddress + ), counterpartyIdentifiers: { client: config.chainB.counterpartyClientId, connection: config.chainB.counterpartyConnectionId, From f9a3fb3a7137de23bf1afd945649ef06a6853896 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Mon, 9 Mar 2020 18:31:40 +0900 Subject: [PATCH 05/13] Add instance_id in the chainA and chainB `instance_id` is used in the Foundry log. It helps debugging. --- ibc.ts/chainA/chainA.config.toml | 1 + ibc.ts/chainB/chainB.config.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/ibc.ts/chainA/chainA.config.toml b/ibc.ts/chainA/chainA.config.toml index c98e0ad99b..2069b669fa 100644 --- a/ibc.ts/chainA/chainA.config.toml +++ b/ibc.ts/chainA/chainA.config.toml @@ -2,6 +2,7 @@ quiet = false base_path = "." password_path = "./password.json" +instance_id = 1 [mining] engine_signer = "accqym7qmn5yj29cdl405xlmx6awd3f3yz07g7vq2c9" diff --git a/ibc.ts/chainB/chainB.config.toml b/ibc.ts/chainB/chainB.config.toml index 53bce50b09..e23a6db195 100644 --- a/ibc.ts/chainB/chainB.config.toml +++ b/ibc.ts/chainB/chainB.config.toml @@ -2,6 +2,7 @@ quiet = false base_path = "." password_path = "./password.json" +instance_id = 2 [mining] engine_signer = "bccqygjwzj8wupc9m7du9ccef4j6k2u3erjuv2w8pt0" From 58e1c471dc51a747ae2630edae83559fc51192d8 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Mon, 9 Mar 2020 18:35:03 +0900 Subject: [PATCH 06/13] Make chain initialize timeout bigger in the IBC test scenario --- ibc.ts/src/scenario/runChains.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ibc.ts/src/scenario/runChains.ts b/ibc.ts/src/scenario/runChains.ts index 51f04003e8..9893b9ccf1 100644 --- a/ibc.ts/src/scenario/runChains.ts +++ b/ibc.ts/src/scenario/runChains.ts @@ -144,7 +144,7 @@ async function sendPayTx({ } async function waitForTx(sdk: SDK, txHash: H256) { - const timeout = delay(10 * 1000).then(() => { + const timeout = delay(30 * 1000).then(() => { throw new Error("Timeout"); }); const wait = (async () => { From 789ae66e9161d00cdc8a3405711a62fd2c7f9de9 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 5 Mar 2020 13:40:57 +0900 Subject: [PATCH 07/13] Wait for Foundry using a smart way Before: Always wait 3 seconds. Sometimes it takes more than 3 seconds. After: Send ping every second for 10 seconds. If a chain is initlaized, we can check it in a second. --- ibc.ts/src/scenario/runChains.ts | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/ibc.ts/src/scenario/runChains.ts b/ibc.ts/src/scenario/runChains.ts index 9893b9ccf1..0a02037031 100644 --- a/ibc.ts/src/scenario/runChains.ts +++ b/ibc.ts/src/scenario/runChains.ts @@ -81,9 +81,6 @@ async function runChainB() { } async function checkChainAAndBAreRunning() { - // Wait for Foundry to listen on the port, three seconds is an arbitrary value. - await delay(3000); - // FIXME: read values from config const sdkA = new SDK({ server: "http://localhost:18080", @@ -96,10 +93,25 @@ async function checkChainAAndBAreRunning() { keyStoreType: { type: "local", path: "./chainB/keystore.db" } }); - debug("Send ping to A"); - await sdkA.rpc.node.ping(); - debug("Send ping to B"); - await sdkB.rpc.node.ping(); + // Wait for Foundry to listen on the port, three seconds is an arbitrary value. + let retryCount = 0; + while (true) { + try { + debug("Send ping to A"); + await sdkA.rpc.node.ping(); + debug("Send ping to B"); + await sdkB.rpc.node.ping(); + break; + } catch (err) { + if (retryCount < 10) { + retryCount += 1; + debug("Failed to send ping. I will retry"); + await delay(1000); + } else { + throw err; + } + } + } debug("Delete pending Txs in A"); await sdkA.rpc.sendRpcRequest("mempool_deleteAllPendingTransactions", []); From 92d5ab5339abdc021e25ce98aab75c27e70b7d8d Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Mon, 9 Mar 2020 18:34:46 +0900 Subject: [PATCH 08/13] Make runChain script kill Foundry gracefully --- ibc.ts/src/scenario/runChains.ts | 34 ++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/ibc.ts/src/scenario/runChains.ts b/ibc.ts/src/scenario/runChains.ts index 0a02037031..63adfeb845 100644 --- a/ibc.ts/src/scenario/runChains.ts +++ b/ibc.ts/src/scenario/runChains.ts @@ -18,9 +18,37 @@ async function main() { console.log("Reset DB"); await resetDB(); console.log("Run Chain A"); - await runChainA(); + const chainA = await runChainA(); console.log("Run Chain B"); - await runChainB(); + const chainB = await runChainB(); + + process.stdin.resume(); + let sentKillSignal = false; + process.on("SIGINT", () => { + if (sentKillSignal === false) { + sentKillSignal = true; + chainA.kill("SIGINT"); + chainB.kill("SIGINT"); + console.log("Sent kill signal to Foundry"); + } else if (!chainA.killed || !chainB.killed) { + console.log("Waiting for foundry is killed"); + } else { + process.exit(); + } + }); + chainA.on("close", () => { + console.log("Chain A is killed"); + if (chainA.killed && chainB.killed && sentKillSignal) { + process.exit(); + } + }); + chainA.on("close", () => { + console.log("Chain B is killed"); + if (chainA.killed && chainB.killed && sentKillSignal) { + process.exit(); + } + }); + console.log("Check the chains"); await checkChainAAndBAreRunning(); console.log("Chains are running!"); @@ -62,6 +90,7 @@ async function runChainA() { } ); streamOutToDebug(foundryProcess); + return foundryProcess; } async function runChainB() { @@ -78,6 +107,7 @@ async function runChainB() { } ); streamOutToDebug(foundryProcess); + return foundryProcess; } async function checkChainAAndBAreRunning() { From d520b8f651c3384572d42f49a7d147ee2de02e9c Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 4 Mar 2020 18:12:40 +0900 Subject: [PATCH 09/13] Make creating client skippable in the IBC scenario --- ibc.ts/package.json | 1 + ibc.ts/src/scenario/index.ts | 22 ++++++++++++++++++---- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/ibc.ts/package.json b/ibc.ts/package.json index ed1157d160..9165a221e6 100644 --- a/ibc.ts/package.json +++ b/ibc.ts/package.json @@ -22,6 +22,7 @@ "codechain-sdk": "^2.0.1", "debug": "^4.1.1", "dotenv": "^8.2.0", + "enquirer": "^2.3.4", "rlp": "^2.0.0" } } diff --git a/ibc.ts/src/scenario/index.ts b/ibc.ts/src/scenario/index.ts index cb850ea5bc..accf6fc156 100644 --- a/ibc.ts/src/scenario/index.ts +++ b/ibc.ts/src/scenario/index.ts @@ -4,6 +4,7 @@ import { Chain } from "../common/chain"; import { PlatformAddress } from "codechain-primitives/lib"; import { CreateClientDatagram } from "../common/datagram/createClient"; import { strict as assert } from "assert"; +const { Select } = require("enquirer"); require("dotenv").config(); @@ -38,10 +39,23 @@ async function main() { keystorePath: config.chainB.keystorePath }); - console.log("Create a light client in chain A"); - await createLightClient({ chain: chainA, counterpartyChain: chainB }); - console.log("Create a light client in chain B"); - await createLightClient({ chain: chainB, counterpartyChain: chainA }); + const lightclientPrompt = new Select({ + name: "light client", + message: "Will you create light clients?", + choices: ["yes", "skip", "exit"] + }); + const lightclientAnswer = await lightclientPrompt.run(); + + if (lightclientAnswer === "exit") { + return; + } + + if (lightclientAnswer === "yes") { + console.log("Create a light client in chain A"); + await createLightClient({ chain: chainA, counterpartyChain: chainB }); + console.log("Create a light client in chain B"); + await createLightClient({ chain: chainB, counterpartyChain: chainA }); + } } main().catch(console.error); From 608b6ec8cc8715d4193b3db63ab8fcbe02bac9c2 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 4 Mar 2020 18:36:20 +0900 Subject: [PATCH 10/13] Implement queryConnection and queryClientConnections in ibc.ts --- ibc.ts/src/common/chain.ts | 20 +++++++++++++++++++- ibc.ts/src/common/types.ts | 8 ++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/ibc.ts/src/common/chain.ts b/ibc.ts/src/common/chain.ts index e2354ba36c..95f52f124a 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 } from "./types"; +import { IBCHeader, IBCQueryResult, ConnectionEnd } from "./types"; const debug = Debug("common:tx"); @@ -108,6 +108,24 @@ export class Chain { blockNumber ]); } + + public async queryConnection( + blockNumber?: number + ): Promise | null> { + return this.sdk.rpc.sendRpcRequest("ibc_query_connection", [ + this.counterpartyIdentifiers.connection, + blockNumber + ]); + } + + public async queryClientConnections( + blockNumber?: number + ): Promise | null> { + return this.sdk.rpc.sendRpcRequest("ibc_query_client_connections", [ + this.counterpartyIdentifiers.client, + 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 a969385519..256937212c 100644 --- a/ibc.ts/src/common/types.ts +++ b/ibc.ts/src/common/types.ts @@ -4,3 +4,11 @@ export interface IBCQueryResult { } export type IBCHeader = string; + +export interface ConnectionEnd { + state: "INIT" | "TRYOPEN" | "OPEN"; + counterpartyConnectionIdentifier: string; + counterpartyPrefix: string; + clientIdentifier: string; + counterpartyClientIdentifier: string; +} From 6caf8ed39e75e344dfa350922bd357ad9a3995f1 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Wed, 4 Mar 2020 18:24:22 +0900 Subject: [PATCH 11/13] Send connection datagrams in the scenario script and the relayer * connOpenInit * connOpenTry * connOpenAck * connOpenConfirm --- ibc.ts/src/common/datagram/connOpenAck.ts | 44 +++++++ ibc.ts/src/common/datagram/connOpenConfirm.ts | 29 +++++ ibc.ts/src/common/datagram/connOpenInit.ts | 44 +++++++ ibc.ts/src/common/datagram/connOpenTry.ts | 64 ++++++++++ ibc.ts/src/relayer/index.ts | 109 ++++++++++++++++++ ibc.ts/src/scenario/index.ts | 36 ++++++ 6 files changed, 326 insertions(+) create mode 100644 ibc.ts/src/common/datagram/connOpenAck.ts create mode 100644 ibc.ts/src/common/datagram/connOpenConfirm.ts create mode 100644 ibc.ts/src/common/datagram/connOpenInit.ts create mode 100644 ibc.ts/src/common/datagram/connOpenTry.ts diff --git a/ibc.ts/src/common/datagram/connOpenAck.ts b/ibc.ts/src/common/datagram/connOpenAck.ts new file mode 100644 index 0000000000..0975e68f29 --- /dev/null +++ b/ibc.ts/src/common/datagram/connOpenAck.ts @@ -0,0 +1,44 @@ +const RLP = require("rlp"); + +export class ConnOpenAckDatagram { + private identifier: string; + private proofTry: Buffer; + private proofConsensus: Buffer; + private proofHeight: number; + private consensusHeight: number; + + public constructor({ + identifier, + proofTry, + proofConsensus, + proofHeight, + consensusHeight + }: { + identifier: string; + proofTry: Buffer; + proofConsensus: Buffer; + proofHeight: number; + consensusHeight: number; + }) { + this.identifier = identifier; + this.proofTry = proofTry; + this.proofConsensus = proofConsensus; + this.proofHeight = proofHeight; + this.consensusHeight = consensusHeight; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 5, + this.identifier, + this.proofTry, + this.proofConsensus, + this.proofHeight, + this.consensusHeight + ]; + } +} diff --git a/ibc.ts/src/common/datagram/connOpenConfirm.ts b/ibc.ts/src/common/datagram/connOpenConfirm.ts new file mode 100644 index 0000000000..a8d2cab271 --- /dev/null +++ b/ibc.ts/src/common/datagram/connOpenConfirm.ts @@ -0,0 +1,29 @@ +const RLP = require("rlp"); + +export class ConnOpenConfirmDatagram { + private identifier: string; + private proofAck: Buffer; + private proofHeight: number; + + public constructor({ + identifier, + proofAck, + proofHeight + }: { + identifier: string; + proofAck: Buffer; + proofHeight: number; + }) { + this.identifier = identifier; + this.proofAck = proofAck; + this.proofHeight = proofHeight; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [6, this.identifier, this.proofAck, this.proofHeight]; + } +} diff --git a/ibc.ts/src/common/datagram/connOpenInit.ts b/ibc.ts/src/common/datagram/connOpenInit.ts new file mode 100644 index 0000000000..12190771ca --- /dev/null +++ b/ibc.ts/src/common/datagram/connOpenInit.ts @@ -0,0 +1,44 @@ +const RLP = require("rlp"); + +export class ConnOpenInitDatagram { + private id: string; + private desiredCounterpartyConnectionIdentifier: string; + private counterpartyPrefix: string; + private clientIdentifier: string; + private counterpartyClientIdentifier: string; + + public constructor({ + id, + desiredCounterpartyConnectionIdentifier, + counterpartyPrefix, + clientIdentifier, + counterpartyClientIdentifier + }: { + id: string; + desiredCounterpartyConnectionIdentifier: string; + counterpartyPrefix: string; + clientIdentifier: string; + counterpartyClientIdentifier: string; + }) { + this.id = id; + this.desiredCounterpartyConnectionIdentifier = desiredCounterpartyConnectionIdentifier; + this.counterpartyPrefix = counterpartyPrefix; + this.clientIdentifier = clientIdentifier; + this.counterpartyClientIdentifier = counterpartyClientIdentifier; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 3, + this.id, + this.desiredCounterpartyConnectionIdentifier, + this.counterpartyPrefix, + this.clientIdentifier, + this.counterpartyClientIdentifier + ]; + } +} diff --git a/ibc.ts/src/common/datagram/connOpenTry.ts b/ibc.ts/src/common/datagram/connOpenTry.ts new file mode 100644 index 0000000000..080214874d --- /dev/null +++ b/ibc.ts/src/common/datagram/connOpenTry.ts @@ -0,0 +1,64 @@ +const RLP = require("rlp"); + +export class ConnOpenTryDatagram { + private desiredIdentifier: string; + private counterpartyConnectionIdentifier: string; + private counterpartyPrefix: string; + private counterpartyClientIdentifier: string; + private clientIdentifier: string; + private proofInit: Buffer; + private proofConsensus: Buffer; + private proofHeight: number; + private consensusHeight: number; + + public constructor({ + desiredIdentifier, + counterpartyConnectionIdentifier, + counterpartyPrefix, + counterpartyClientIdentifier, + clientIdentifier, + proofInit, + proofConsensus, + proofHeight, + consensusHeight + }: { + desiredIdentifier: string; + counterpartyConnectionIdentifier: string; + counterpartyPrefix: string; + counterpartyClientIdentifier: string; + clientIdentifier: string; + proofInit: Buffer; + proofConsensus: Buffer; + proofHeight: number; + consensusHeight: number; + }) { + this.desiredIdentifier = desiredIdentifier; + this.counterpartyConnectionIdentifier = counterpartyConnectionIdentifier; + this.counterpartyPrefix = counterpartyPrefix; + this.counterpartyClientIdentifier = counterpartyClientIdentifier; + this.clientIdentifier = clientIdentifier; + this.proofInit = proofInit; + this.proofConsensus = proofConsensus; + this.proofHeight = proofHeight; + this.consensusHeight = consensusHeight; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 4, + this.desiredIdentifier, + this.counterpartyConnectionIdentifier, + this.counterpartyPrefix, + this.counterpartyClientIdentifier, + this.clientIdentifier, + this.proofInit, + this.proofConsensus, + this.proofHeight, + this.consensusHeight + ]; + } +} diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index f6bf168404..c9b76fa546 100644 --- a/ibc.ts/src/relayer/index.ts +++ b/ibc.ts/src/relayer/index.ts @@ -6,6 +6,9 @@ import { getConfig } from "../common/config"; import { PlatformAddress } from "codechain-primitives/lib"; import { UpdateClientDatagram } from "../common/datagram/updateClient"; import { strict as assert } from "assert"; +import { ConnOpenTryDatagram } from "../common/datagram/connOpenTry"; +import { ConnOpenAckDatagram } from "../common/datagram/connOpenAck"; +import { ConnOpenConfirmDatagram } from "../common/datagram/connOpenConfirm"; require("dotenv").config(); @@ -97,6 +100,21 @@ async function pendingDatagrams({ }) ); + const { + localDatagrams: localDatagramsForConnection, + counterpartyDatagrams: counterpartyDatagramsForConnection + } = await buildConnection({ + chain, + counterpartyChain, + height, + counterpartyChainHeight + }); + + localDatagrams = localDatagrams.concat(localDatagramsForConnection); + counterpartyDatagrams = counterpartyDatagrams.concat( + counterpartyDatagramsForConnection + ); + return { localDatagrams, counterpartyDatagrams }; } @@ -136,3 +154,94 @@ async function updateLightClient({ return datagrams; } + +async function buildConnection({ + chain, + counterpartyChain, + height, + counterpartyChainHeight +}: { + chain: Chain; + counterpartyChain: Chain; + height: number; + counterpartyChainHeight: number; +}) { + const localDatagrams: Datagram[] = []; + const counterpartyDatagrams = []; + const connectionIdentifiers = await chain.queryClientConnections(height); + + assert.notEqual(connectionIdentifiers, null, "Client should be exist"); + for (const connectionIdentifier of connectionIdentifiers!.data || []) { + const client = await chain.queryClient(height); + const counterpartyClient = await counterpartyChain.queryClient( + counterpartyChainHeight + ); + + assert.strictEqual( + connectionIdentifier, + chain.counterpartyIdentifiers.connection, + "PoC supports only one connection" + ); + const connectionEnd = await chain.queryConnection(height); + const counterpartyConnectionEnd = await counterpartyChain.queryConnection( + counterpartyChainHeight + ); + assert.notEqual( + connectionEnd!.data, + null, + "Connection exists because we acquired the identifier from RPC" + ); + if ( + connectionEnd!.data!.state === "INIT" && + counterpartyConnectionEnd!.data == null + ) { + counterpartyDatagrams.push( + new ConnOpenTryDatagram({ + desiredIdentifier: + counterpartyChain.counterpartyIdentifiers.connection, + counterpartyConnectionIdentifier: + chain.counterpartyIdentifiers.connection, + counterpartyClientIdentifier: + chain.counterpartyIdentifiers.client, + clientIdentifier: + counterpartyChain.counterpartyIdentifiers.client, + proofInit: Buffer.from(connectionEnd!.proof, "hex"), + proofConsensus: Buffer.alloc(0), + proofHeight: height, + consensusHeight: client!.data!.number, + counterpartyPrefix: "" + }) + ); + } else if ( + connectionEnd!.data!.state === "INIT" && + counterpartyConnectionEnd!.data!.state === "TRYOPEN" + ) { + localDatagrams.push( + new ConnOpenAckDatagram({ + identifier: chain.counterpartyIdentifiers.connection, + proofTry: Buffer.from( + counterpartyConnectionEnd!.proof, + "hex" + ), + proofConsensus: Buffer.alloc(0), + proofHeight: counterpartyChainHeight, + consensusHeight: counterpartyClient!.data!.number + }) + ); + } else if ( + connectionEnd!.data!.state === "OPEN" && + counterpartyConnectionEnd!.data!.state === "TRYOPEN" + ) { + counterpartyDatagrams.push( + new ConnOpenConfirmDatagram({ + identifier: + counterpartyChain.counterpartyIdentifiers.connection, + proofAck: Buffer.from(connectionEnd!.proof, "hex"), + proofHeight: height + }) + ); + } + } + + return { localDatagrams, counterpartyDatagrams }; +} diff --git a/ibc.ts/src/scenario/index.ts b/ibc.ts/src/scenario/index.ts index accf6fc156..6b35891fe9 100644 --- a/ibc.ts/src/scenario/index.ts +++ b/ibc.ts/src/scenario/index.ts @@ -4,6 +4,7 @@ import { Chain } from "../common/chain"; import { PlatformAddress } from "codechain-primitives/lib"; import { CreateClientDatagram } from "../common/datagram/createClient"; import { strict as assert } from "assert"; +import { ConnOpenInitDatagram } from "../common/datagram/connOpenInit"; const { Select } = require("enquirer"); require("dotenv").config(); @@ -56,6 +57,22 @@ async function main() { console.log("Create a light client in chain B"); await createLightClient({ chain: chainB, counterpartyChain: chainA }); } + + const connectionPrompt = new Select({ + name: "connection", + message: "Will you create connection?", + choices: ["yes", "skip", "exit"] + }); + const connectionAnswer = await connectionPrompt.run(); + + if (connectionAnswer === "exit") { + return; + } + + if (connectionAnswer === "yes") { + console.log("Create a connection"); + await createConnection({ chainA, chainB }); + } } main().catch(console.error); @@ -103,3 +120,22 @@ async function createLightClient({ assert.notEqual(clientStateAfter!.data, null, "client is initialized"); debug(`Create client is ${JSON.stringify(clientStateAfter)}`); } + +async function createConnection({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}) { + await chainA.submitDatagram( + new ConnOpenInitDatagram({ + id: chainA.counterpartyIdentifiers.connection, + desiredCounterpartyConnectionIdentifier: + chainB.counterpartyIdentifiers.connection, + counterpartyPrefix: "", + clientIdentifier: chainA.counterpartyIdentifiers.client, + counterpartyClientIdentifier: chainB.counterpartyIdentifiers.client + }) + ); +} From e6db3a1379576e759bfd60e0edf3ab11774ab437 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Mon, 9 Mar 2020 18:15:10 +0900 Subject: [PATCH 12/13] Make indexer update light client until the parent of the best block Since querying the current best block's seal is difficult, our PoC implementation does not implement it. Make the relayer use the information. --- ibc.ts/src/relayer/index.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index c9b76fa546..36c6284262 100644 --- a/ibc.ts/src/relayer/index.ts +++ b/ibc.ts/src/relayer/index.ts @@ -77,8 +77,8 @@ async function pendingDatagrams({ chain: Chain; counterpartyChain: Chain; }): Promise<{ localDatagrams: Datagram[]; counterpartyDatagrams: Datagram[] }> { - const height = await chain.latestHeight(); - const counterpartyChainHeight = await counterpartyChain.latestHeight(); + let height = await chain.latestHeight(); + let counterpartyChainHeight = await counterpartyChain.latestHeight(); let localDatagrams: Datagram[] = []; let counterpartyDatagrams: Datagram[] = []; @@ -100,6 +100,10 @@ async function pendingDatagrams({ }) ); + // FIXME: We can't update light client upto the best block. + height = height - 1; + counterpartyChainHeight = counterpartyChainHeight - 1; + const { localDatagrams: localDatagramsForConnection, counterpartyDatagrams: counterpartyDatagramsForConnection @@ -138,7 +142,8 @@ async function updateLightClient({ ); } let currentBlockNumber = clientState!.data!.number; - while (currentBlockNumber < counterpartyChainHeight) { + // FIXME: We can't get the best block's IBC header + while (currentBlockNumber < counterpartyChainHeight - 1) { const header = (await counterpartyChain.queryIBCHeader( currentBlockNumber + 1 ))!; From e927201ff6ee2b4aa0e54903b726c9b5b576d049 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Mon, 9 Mar 2020 18:32:24 +0900 Subject: [PATCH 13/13] Add connection inspection code in the scenario script --- ibc.ts/src/scenario/index.ts | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/ibc.ts/src/scenario/index.ts b/ibc.ts/src/scenario/index.ts index 6b35891fe9..de794d23c3 100644 --- a/ibc.ts/src/scenario/index.ts +++ b/ibc.ts/src/scenario/index.ts @@ -73,6 +73,28 @@ async function main() { console.log("Create a connection"); await createConnection({ chainA, chainB }); } + + while (true) { + const connectionCheckPrompt = new Select({ + name: "connection check", + message: "Will you check connection?", + choices: ["yes", "skip", "exit"] + }); + const connectionCheckAnswer = await connectionCheckPrompt.run(); + + if (connectionCheckAnswer === "exit") { + return; + } + + if (connectionCheckAnswer === "yes") { + console.log("Check a connection"); + await checkConnections({ chainA, chainB }); + } + + if (connectionCheckAnswer === "skip") { + break; + } + } } main().catch(console.error); @@ -139,3 +161,17 @@ async function createConnection({ }) ); } + +async function checkConnections({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}) { + const connectionA = await chainA.queryConnection(); + console.log(`Connection in A ${JSON.stringify(connectionA)}`); + + const connectionB = await chainB.queryConnection(); + console.log(`Connection in B ${JSON.stringify(connectionB)}`); +}