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 bb94459549..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, )?; @@ -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); 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.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/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.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" 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/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/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/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/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/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; +} diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index 85b3180dbf..36c6284262 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(); @@ -16,7 +19,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 +30,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, @@ -74,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[] = []; @@ -97,6 +100,25 @@ 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 + } = await buildConnection({ + chain, + counterpartyChain, + height, + counterpartyChainHeight + }); + + localDatagrams = localDatagrams.concat(localDatagramsForConnection); + counterpartyDatagrams = counterpartyDatagrams.concat( + counterpartyDatagramsForConnection + ); + return { localDatagrams, counterpartyDatagrams }; } @@ -120,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 ))!; @@ -136,3 +159,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 c33e2944d9..de794d23c3 100644 --- a/ibc.ts/src/scenario/index.ts +++ b/ibc.ts/src/scenario/index.ts @@ -4,6 +4,8 @@ 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(); @@ -14,7 +16,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 +29,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, @@ -34,10 +40,61 @@ 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 }); + } + + 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 }); + } + + 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); @@ -85,3 +142,36 @@ 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 + }) + ); +} + +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)}`); +} diff --git a/ibc.ts/src/scenario/runChains.ts b/ibc.ts/src/scenario/runChains.ts index 51f04003e8..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,12 +107,10 @@ async function runChainB() { } ); streamOutToDebug(foundryProcess); + return foundryProcess; } 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 +123,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", []); @@ -144,7 +186,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 () => { 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()) } }