diff --git a/core/src/ibc/channel_04/log.rs b/core/src/ibc/channel_04/log.rs index b8ec7a086e..1d6f4c03e4 100644 --- a/core/src/ibc/channel_04/log.rs +++ b/core/src/ibc/channel_04/log.rs @@ -23,16 +23,21 @@ use super::types::Packet; use crate::ibc; use ibc::IdentifierSlice; -pub fn set_packet<'a>(ctx: &'a mut dyn ibc::Context, packet: &Packet, tag: &str) { +pub fn set_packet<'a>( + ctx: &'a mut dyn ibc::Context, + port: IdentifierSlice, + channel: IdentifierSlice, + packet: &Packet, + tag: &str, +) { let value = rlp::encode(packet); - let path = format!("nastylogs/{}/{}/{}/latest", packet.source_port, packet.source_channel, tag); + let path = format!("nastylogs/{}/{}/{}/latest", port, channel, tag); let result = ctx.get_kv_store_mut().insert(&path, &value); if result.is_some() { panic!("Packet already exists."); } } -#[allow(dead_code)] pub fn get_packet<'a>( ctx: &'a mut dyn ibc::Context, port: IdentifierSlice, diff --git a/core/src/ibc/channel_04/manager.rs b/core/src/ibc/channel_04/manager.rs index dae07f3d79..efc2f62594 100644 --- a/core/src/ibc/channel_04/manager.rs +++ b/core/src/ibc/channel_04/manager.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -use super::log::{remove_packet, set_packet}; +use super::log::{get_packet, remove_packet, set_packet}; use super::types::{ Acknowledgement, ChannelEnd, ChannelOrder, ChannelState, Packet, PacketCommitment, PacketCommitmentHash, Sequence, }; @@ -445,7 +445,10 @@ impl<'a> Manager<'a> { let mut next_sequence_send = self.get_sequence_send(&packet.source_port, &packet.source_channel)?; if packet.sequence != next_sequence_send { - return Err("Packet carries invalid sequence".to_owned()) + return Err(format!( + "Packet carries invalid sequence. expected: {:?}, actual: {:?}", + next_sequence_send, packet.sequence + )) } next_sequence_send.raw += 1; @@ -467,7 +470,7 @@ impl<'a> Manager<'a> { ); // check log.rs to understand this statement - set_packet(self.ctx, &packet, "send"); + set_packet(self.ctx, &packet.source_port, &packet.source_channel, &packet, "send"); Ok(()) } @@ -522,7 +525,10 @@ impl<'a> Manager<'a> { ); if packet.sequence != next_sequence_recv { - return Err("Packet carries invalid sequence".to_owned()) + return Err(format!( + "Packet carries invalid sequence. expected: {:?} actal: {:?}", + next_sequence_recv, packet.sequence + )) } next_sequence_recv.raw += 1; kv_store.insert( @@ -535,8 +541,10 @@ impl<'a> Manager<'a> { // check log.rs to understand this statement // Unlike send, we just overwrite an old event. - remove_packet(self.ctx, &packet.dest_port, &packet.dest_channel, "recv"); - set_packet(self.ctx, &packet, "recv"); + if get_packet(self.ctx, &packet.dest_port, &packet.dest_channel, "recv").is_some() { + remove_packet(self.ctx, &packet.dest_port, &packet.dest_channel, "recv"); + } + set_packet(self.ctx, &packet.dest_port, &packet.dest_channel, &packet, "recv"); Ok(packet) } diff --git a/core/src/ibc/channel_04/types.rs b/core/src/ibc/channel_04/types.rs index b7613e9c53..617ad42adc 100644 --- a/core/src/ibc/channel_04/types.rs +++ b/core/src/ibc/channel_04/types.rs @@ -20,11 +20,25 @@ use primitives::{Bytes, H256}; use rlp; use rlp::{DecoderError, Rlp, RlpStream}; -#[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] +#[derive(PartialEq, Debug)] pub struct Sequence { pub raw: u64, } +impl rlp::Encodable for Sequence { + fn rlp_append(&self, s: &mut RlpStream) { + s.append_single_value(&self.raw); + } +} + +impl rlp::Decodable for Sequence { + fn decode(rlp: &Rlp) -> Result { + Ok(Self { + raw: rlp.as_val()?, + }) + } +} + #[repr(u8)] #[derive(Copy, Clone, PartialEq, Debug)] pub enum ChannelState { @@ -100,12 +114,12 @@ pub struct Packet { /// Acknowledgement and PacketCommitment's behaviors are somewhat different from other ICS data: /// They are not saved directly in the state, but the hash PacketCommitmentHash will be. -#[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] +#[derive(RlpEncodableWrapper, RlpDecodableWrapper, PartialEq, Debug)] pub struct AcknowledgementHash { pub raw: H256, } -#[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] +#[derive(RlpEncodableWrapper, RlpDecodableWrapper, PartialEq, Debug)] pub struct Acknowledgement { pub raw: Bytes, } @@ -118,7 +132,7 @@ impl Acknowledgement { } } -#[derive(RlpEncodable, RlpDecodable, PartialEq, Debug)] +#[derive(RlpEncodableWrapper, RlpDecodableWrapper, PartialEq, Debug)] pub struct PacketCommitmentHash { pub raw: H256, } diff --git a/core/src/ibc/transaction_handler/datagrams.rs b/core/src/ibc/transaction_handler/datagrams.rs index fdf5be6d9b..a8bd8ae634 100644 --- a/core/src/ibc/transaction_handler/datagrams.rs +++ b/core/src/ibc/transaction_handler/datagrams.rs @@ -476,6 +476,7 @@ impl Decodable for Datagram { #[cfg(test)] mod tests { use super::*; + use crate::ibc::channel_04::types::Sequence; use rlp::{self, rlp_encode_and_decode_test}; #[test] @@ -542,4 +543,25 @@ mod tests { }; rlp_encode_and_decode_test!(chan_open_init); } + + #[test] + fn send_packet() { + let send_packet = Datagram::SendPacket { + raw: SendPacket { + tag: DatagramTag::SendPacket, + packet: Packet { + sequence: Sequence { + raw: 1, + }, + timeout_height: 32, + source_port: "source_port".to_owned(), + source_channel: "source_channel".to_owned(), + dest_port: "dest_port".to_owned(), + dest_channel: "dest_channel".to_owned(), + data: b"data".to_vec(), + }, + }, + }; + rlp_encode_and_decode_test!(send_packet); + } } diff --git a/ibc.ts/chainA/chainA.schem.json b/ibc.ts/chainA/chainA.schem.json index 0b8a19425d..5846ba0b5e 100644 --- a/ibc.ts/chainA/chainA.schem.json +++ b/ibc.ts/chainA/chainA.schem.json @@ -6,7 +6,7 @@ "validators": [ "0xc7db8f558a87a45d3b4d2e7422abb48a27e6eeee00a0f884ca244d1d783d6ead2ca445e9ac8f08b311d78b4b4b69d48a9daf0c98d2a19e87e482f8060138ba88" ], - "timeoutPropose": 10000, + "timeoutPropose": 3000, "timeoutProposeDelta": 500, "timeoutPrevote": 1000, "timeoutPrevoteDelta": 500, diff --git a/ibc.ts/chainB/chainB.schem.json b/ibc.ts/chainB/chainB.schem.json index c33c47f003..d05a72993d 100644 --- a/ibc.ts/chainB/chainB.schem.json +++ b/ibc.ts/chainB/chainB.schem.json @@ -6,7 +6,7 @@ "validators": [ "0x521d103d95b5b684f9be089ccc5cffd6b4ff2997ce535a7053e44d811b84f38abf2f35ebfeb5458e86a0b7d7293e71bcb3a59eddeb8b63801c2c4a8081fc67ab" ], - "timeoutPropose": 10000, + "timeoutPropose": 3000, "timeoutProposeDelta": 500, "timeoutPrevote": 1000, "timeoutPrevoteDelta": 500, diff --git a/ibc.ts/src/common/chain.ts b/ibc.ts/src/common/chain.ts index 99025b967a..ec1f2fe472 100644 --- a/ibc.ts/src/common/chain.ts +++ b/ibc.ts/src/common/chain.ts @@ -1,4 +1,4 @@ -import { Datagram } from "./datagram/index"; +import { Datagram, PacketJSON } from "./datagram/index"; import { SDK } from "codechain-sdk"; import { H256, PlatformAddress } from "codechain-primitives"; import { IBC } from "./foundry/transaction"; @@ -136,6 +136,60 @@ export class Chain { blockNumber ]); } + + public async queryCommitment( + sequence: number, + blockNumber?: number + ): Promise | null> { + return this.sdk.rpc.sendRpcRequest("ibc_query_packet_commitment", [ + "DEFAULT_PORT", + this.counterpartyIdentifiers.channel, + sequence, + blockNumber + ]); + } + + public async queryAcknowledgement( + sequence: number, + blockNumber?: number + ): Promise | null> { + return this.sdk.rpc.sendRpcRequest("ibc_query_packet_acknowledgement", [ + "DEFAULT_PORT", + this.counterpartyIdentifiers.channel, + sequence, + blockNumber + ]); + } + + public async queryLatestSendPacket( + blockNumber?: number + ): Promise { + return this.sdk.rpc.sendRpcRequest("ibc_query_latest_send_packet", [ + "DEFAULT_PORT", + this.counterpartyIdentifiers.channel, + blockNumber + ]); + } + + public async queryLatestRecvPacket( + blockNumber?: number + ): Promise { + return this.sdk.rpc.sendRpcRequest("ibc_query_latest_recv_packet", [ + "DEFAULT_PORT", + this.counterpartyIdentifiers.channel, + blockNumber + ]); + } + + public async queryNextSequenceRecv( + blockNumber?: number + ): Promise | null> { + return this.sdk.rpc.sendRpcRequest("ibc_query_next_sequence_recv", [ + "DEFAULT_PORT", + this.counterpartyIdentifiers.channel, + blockNumber + ]); + } } async function waitForTx(sdk: SDK, txHash: H256) { diff --git a/ibc.ts/src/common/datagram/acknowledgePacket.ts b/ibc.ts/src/common/datagram/acknowledgePacket.ts new file mode 100644 index 0000000000..7e475213f5 --- /dev/null +++ b/ibc.ts/src/common/datagram/acknowledgePacket.ts @@ -0,0 +1,41 @@ +import { Packet } from "."; + +const RLP = require("rlp"); + +export class AcknowledgePacketDatagram { + private packet: Packet; + private ack: Buffer; + private proof: Buffer; + private proofHeight: number; + + public constructor({ + packet, + ack, + proof, + proofHeight + }: { + packet: Packet; + ack: Buffer; + proof: Buffer; + proofHeight: number; + }) { + this.packet = packet; + this.ack = ack; + this.proof = proof; + this.proofHeight = proofHeight; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 15, + this.packet.toEncodeObject(), + this.ack, + this.proof, + this.proofHeight + ]; + } +} diff --git a/ibc.ts/src/common/datagram/index.ts b/ibc.ts/src/common/datagram/index.ts index e84d0d1847..b6d87e8f98 100644 --- a/ibc.ts/src/common/datagram/index.ts +++ b/ibc.ts/src/common/datagram/index.ts @@ -5,3 +5,53 @@ export interface Datagram { export const ChannelOrdered = 0; export const ChannelUnordered = 1; + +export interface PacketJSON { + sequence: number; + timeoutHeight: number; + sourcePort: string; + sourceChannel: string; + destPort: string; + destChannel: string; + data: string; +} + +export class Packet { + public readonly sequence: number; + public readonly timeoutHeight: number; + public readonly sourcePort: string; + public readonly sourceChannel: string; + public readonly destPort: string; + public readonly destChannel: string; + public readonly data: Buffer; + + public constructor({ + sequence, + timeoutHeight, + sourcePort, + sourceChannel, + destPort, + destChannel, + data + }: PacketJSON) { + this.sequence = sequence; + this.timeoutHeight = timeoutHeight; + this.sourcePort = sourcePort; + this.sourceChannel = sourceChannel; + this.destPort = destPort; + this.destChannel = destChannel; + this.data = Buffer.from(data, "hex"); + } + + public toEncodeObject(): any[] { + return [ + this.sequence, + this.timeoutHeight, + this.sourcePort, + this.sourceChannel, + this.destPort, + this.destChannel, + this.data + ]; + } +} diff --git a/ibc.ts/src/common/datagram/recvPacket.ts b/ibc.ts/src/common/datagram/recvPacket.ts new file mode 100644 index 0000000000..9d192f1c02 --- /dev/null +++ b/ibc.ts/src/common/datagram/recvPacket.ts @@ -0,0 +1,41 @@ +import { Packet } from "."; + +const RLP = require("rlp"); + +export class RecvPacketDatagram { + private packet: Packet; + private proof: Buffer; + private proofHeight: number; + private ack: Buffer; + + public constructor({ + packet, + proof, + proofHeight, + ack + }: { + packet: Packet; + proof: Buffer; + proofHeight: number; + ack: Buffer; + }) { + this.packet = packet; + this.proof = proof; + this.proofHeight = proofHeight; + this.ack = ack; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [ + 14, + this.packet.toEncodeObject(), + this.proof, + this.proofHeight, + this.ack + ]; + } +} diff --git a/ibc.ts/src/common/datagram/sendPacket.ts b/ibc.ts/src/common/datagram/sendPacket.ts new file mode 100644 index 0000000000..6d5637e764 --- /dev/null +++ b/ibc.ts/src/common/datagram/sendPacket.ts @@ -0,0 +1,19 @@ +import { Packet } from "."; + +const RLP = require("rlp"); + +export class SendPacketDatagram { + private packet: Packet; + + public constructor({ packet }: { packet: Packet }) { + this.packet = packet; + } + + public rlpBytes(): Buffer { + return RLP.encode(this.toEncodeObject()); + } + + public toEncodeObject(): any[] { + return [13, this.packet.toEncodeObject()]; + } +} diff --git a/ibc.ts/src/relayer/index.ts b/ibc.ts/src/relayer/index.ts index 201c0b588e..c1957073f4 100644 --- a/ibc.ts/src/relayer/index.ts +++ b/ibc.ts/src/relayer/index.ts @@ -1,9 +1,9 @@ import Debug from "debug"; import { Chain } from "../common/chain"; -import { Datagram, ChannelOrdered } from "../common/datagram/index"; +import { Datagram, ChannelOrdered, Packet } from "../common/datagram/index"; import { delay } from "../common/util"; import { getConfig } from "../common/config"; -import { PlatformAddress } from "codechain-primitives/lib"; +import { PlatformAddress, blake256 } from "codechain-primitives/lib"; import { UpdateClientDatagram } from "../common/datagram/updateClient"; import { strict as assert } from "assert"; import { ConnOpenTryDatagram } from "../common/datagram/connOpenTry"; @@ -12,6 +12,8 @@ import { ConnOpenConfirmDatagram } from "../common/datagram/connOpenConfirm"; import { ChanOpenTryDatagram } from "../common/datagram/chanOpenTry"; import { ChanOpenAckDatagram } from "../common/datagram/chanOpenAck"; import { ChanOpenConfirmDatagram } from "../common/datagram/chanOpenConfirm"; +import { RecvPacketDatagram } from "../common/datagram/recvPacket"; +import { AcknowledgePacketDatagram } from "../common/datagram/acknowledgePacket"; require("dotenv").config(); @@ -145,6 +147,15 @@ async function pendingDatagrams({ counterpartyDatagramsForChannel ); + counterpartyDatagrams = counterpartyDatagrams.concat( + await relayPacket({ + chain, + counterpartyChain, + height, + counterpartyChainHeight + }) + ); + return { localDatagrams, counterpartyDatagrams }; } @@ -345,3 +356,95 @@ async function buildChannel({ counterpartyDatagrams }; } + +async function relayPacket({ + chain, + counterpartyChain, + height, + counterpartyChainHeight +}: { + chain: Chain; + counterpartyChain: Chain; + height: number; + counterpartyChainHeight: number; +}): Promise { + const counterpartyDatagrams = []; + + // FIXME: what will be acknowledgement? + const acknowledgementValue = Buffer.from("acknowledgement", "utf8"); + + const sendPacket = await chain.queryLatestSendPacket(height); + if (sendPacket != null) { + const counterpartyAcknowledgement = await counterpartyChain.queryAcknowledgement( + sendPacket.sequence, + counterpartyChainHeight + ); + assert.notEqual( + counterpartyAcknowledgement, + null, + `block at ${counterpartyChainHeight} exists` + ); + if (counterpartyAcknowledgement!.data == null) { + const commitment = await chain.queryCommitment( + sendPacket.sequence, + height + ); + assert.notEqual( + commitment?.data, + null, + `block at ${height} exists` + ); + + counterpartyDatagrams.push( + new RecvPacketDatagram({ + packet: new Packet(sendPacket), + proof: Buffer.from(commitment!.proof, "hex"), + proofHeight: height, + ack: acknowledgementValue + }) + ); + } + } + + const recvPacket = await chain.queryLatestRecvPacket(height); + if (recvPacket != null) { + const acknowledgement = await chain.queryAcknowledgement( + recvPacket.sequence, + height + ); + assert.notEqual( + acknowledgement?.data, + null, + "(Recivecd packet != null) -> (ack !== null)" + ); + assert.strictEqual( + acknowledgement!.data!, + "0x" + blake256(acknowledgementValue), + "Acknowledgement should be the same" + ); + const counterpartyCommitment = await counterpartyChain.queryCommitment( + recvPacket.sequence, + counterpartyChainHeight + ); + assert.notEqual( + counterpartyCommitment, + null, + `block at ${counterpartyChainHeight} exists` + ); + if (counterpartyCommitment!.data == null) { + debug("Counterparty commitment does not exist"); + } + if (counterpartyCommitment!.data != null) { + counterpartyDatagrams.push( + new AcknowledgePacketDatagram({ + packet: new Packet(recvPacket), + ack: acknowledgementValue, + proof: Buffer.from(acknowledgement!.proof, "hex"), + proofHeight: height + }) + ); + } + } + + return counterpartyDatagrams; +} diff --git a/ibc.ts/src/scenario/index.ts b/ibc.ts/src/scenario/index.ts index c2041ac92b..9df09c8a58 100644 --- a/ibc.ts/src/scenario/index.ts +++ b/ibc.ts/src/scenario/index.ts @@ -6,13 +6,17 @@ 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"; +import { ChannelOrdered, Packet } from "../common/datagram"; +import { SendPacketDatagram } from "../common/datagram/sendPacket"; const { Select } = require("enquirer"); require("dotenv").config(); const debug = Debug("scenario:main"); +// Fix the packet's sequence in the PoC scenario. +const SEQUENCE = 1; + async function main() { const config = getConfig(); const chainA = new Chain({ @@ -42,96 +46,45 @@ async function main() { keystorePath: config.chainB.keystorePath }); - 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") { + if ("exit" === (await runLightClientCreationPrompt({ chainA, chainB }))) { 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") { + if ("exit" === (await runConnectionCreationPrompt({ chainA, chainB }))) { 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") { + const result = await runConnectionCheckPrompt({ chainA, chainB }); + if (result === "exit") { return; - } - - if (connectionCheckAnswer === "yes") { - console.log("Check a connection"); - await checkConnections({ chainA, chainB }); - } - - if (connectionCheckAnswer === "skip") { + } else if (result === "break") { 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") { + if ("exit" === (await runChannelCreationPrompt({ chainA, chainB }))) { 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") { + const result = await runChannelCheckPrompt({ chainA, chainB }); + if (result === "exit") { return; + } else if (result === "break") { + break; } + } - if (channelCheckAnsser === "yes") { - console.log("Check a channel"); - await checkChannels({ chainA, chainB }); - } + if ("exit" === (await runSendPacketPrompt({ chainA, chainB }))) { + return; + } - if (channelCheckAnsser === "skip") { + while (true) { + const result = await runPacketCheckPrompt({ chainA, chainB }); + if (result === "exit") { + return; + } else if (result === "break") { break; } } @@ -247,3 +200,245 @@ async function checkChannels({ const channelB = await chainB.queryChannel(); console.log(`Channel in B ${JSON.stringify(channelB)}`); } + +async function sendPacket({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}) { + const chainBHeight = await chainB.latestHeight(); + await chainA.submitDatagram( + new SendPacketDatagram({ + packet: new Packet({ + sequence: SEQUENCE, + timeoutHeight: chainBHeight + 1000, + sourcePort: "DEFAULT_PORT", + sourceChannel: chainA.counterpartyIdentifiers.channel, + destPort: "DEFAULT_PORT", + destChannel: chainB.counterpartyIdentifiers.channel, + data: Buffer.from("PING", "utf8").toString("hex") + }) + }) + ); +} + +async function checkPackets({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}) { + const commitment = await chainA.queryCommitment(SEQUENCE); + console.log(`Chain A's commitment: ${JSON.stringify(commitment)}`); + const recvSequence = await chainB.queryNextSequenceRecv(); + console.log(`Chain B's recvSequence ${JSON.stringify(recvSequence)}`); + const acknowledgement = await chainB.queryAcknowledgement(SEQUENCE); + console.log( + `Chain B's acknowledgement: ${JSON.stringify(acknowledgement)}` + ); + const chainASendPacket = await chainA.queryLatestSendPacket(); + console.log(`Chain A's sendPacket: ${JSON.stringify(chainASendPacket)}`); + const chainBRecvPacket = await chainB.queryLatestRecvPacket(); + console.log(`Chain B's recvPacket: ${JSON.stringify(chainBRecvPacket)}`); +} + +async function runLightClientCreationPrompt({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}): Promise<"exit" | null> { + 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 "exit"; + } + + 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 }); + } + + return null; +} + +async function runConnectionCreationPrompt({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}): Promise<"exit" | null> { + const connectionPrompt = new Select({ + name: "connection", + message: "Will you create connection?", + choices: ["yes", "skip", "exit"] + }); + const connectionAnswer = await connectionPrompt.run(); + + if (connectionAnswer === "exit") { + return "exit"; + } + + if (connectionAnswer === "yes") { + console.log("Create a connection"); + await createConnection({ chainA, chainB }); + } + + return null; +} + +async function runConnectionCheckPrompt({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}): Promise<"exit" | "break" | null> { + 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 "exit"; + } + + if (connectionCheckAnswer === "yes") { + console.log("Check a connection"); + await checkConnections({ chainA, chainB }); + } + + if (connectionCheckAnswer === "skip") { + return "break"; + } + + return null; +} + +async function runChannelCreationPrompt({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}): Promise<"exit" | null> { + 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 "exit"; + } + + if (channelAnswer === "yes") { + console.log("Create a channel"); + await createChannel({ chainA, chainB }); + } + + return null; +} + +async function runChannelCheckPrompt({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}): Promise<"exit" | "break" | null> { + 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 "exit"; + } + + if (channelCheckAnsser === "yes") { + console.log("Check a channel"); + await checkChannels({ chainA, chainB }); + } + + if (channelCheckAnsser === "skip") { + return "break"; + } + + return null; +} + +async function runSendPacketPrompt({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}): Promise<"exit" | null> { + const packetPrompt = new Select({ + name: "packet", + message: "Will you send a packet?", + choices: ["yes", "skip", "exit"] + }); + + const packetAnswer = await packetPrompt.run(); + + if (packetAnswer === "exit") { + return "exit"; + } + + if (packetAnswer === "yes") { + console.log("Send a packet"); + await sendPacket({ chainA, chainB }); + } + + return null; +} + +async function runPacketCheckPrompt({ + chainA, + chainB +}: { + chainA: Chain; + chainB: Chain; +}): Promise<"exit" | "break" | null> { + const packetCheckPrompt = new Select({ + name: "packet check", + message: "Will you check the packet status?", + choices: ["yes", "skip", "exit"] + }); + + const packetCheckAnswer = await packetCheckPrompt.run(); + + if (packetCheckAnswer === "exit") { + return "exit"; + } + + if (packetCheckAnswer === "yes") { + console.log("Check packets"); + await checkPackets({ chainA, chainB }); + } + + if (packetCheckAnswer === "skip") { + return "break"; + } + + return null; +} diff --git a/rpc/src/v1/impls/ibc.rs b/rpc/src/v1/impls/ibc.rs index 1c77d7fa27..16462017f3 100644 --- a/rpc/src/v1/impls/ibc.rs +++ b/rpc/src/v1/impls/ibc.rs @@ -198,12 +198,18 @@ where query_common(&self.client, &path, block_number) } - fn query_latest_send_packet(&self, port_id: String, channel_id: String) -> Result> { - if self.client.state_at(BlockId::Latest).is_none() { + fn query_latest_send_packet( + &self, + port_id: String, + channel_id: String, + block_number: Option, + ) -> Result> { + let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest); + if self.client.state_at(block_id).is_none() { return Ok(None) } - let mut state = self.client.state_at(BlockId::Latest).unwrap(); - let block_number = match self.client.block_number(&BlockId::Latest) { + let mut state = self.client.state_at(block_id).unwrap(); + let block_number = match self.client.block_number(&block_id) { None => return Ok(None), Some(block_number) => block_number, }; @@ -211,12 +217,18 @@ where Ok(ibc::channel_04::log::get_packet(&mut context, &port_id, &channel_id, "send").map(Packet::from_core)) } - fn query_latest_recv_packet(&self, port_id: String, channel_id: String) -> Result> { - if self.client.state_at(BlockId::Latest).is_none() { + fn query_latest_recv_packet( + &self, + port_id: String, + channel_id: String, + block_number: Option, + ) -> Result> { + let block_id = block_number.map(BlockId::Number).unwrap_or(BlockId::Latest); + if self.client.state_at(block_id).is_none() { return Ok(None) } - let mut state = self.client.state_at(BlockId::Latest).unwrap(); - let block_number = match self.client.block_number(&BlockId::Latest) { + let mut state = self.client.state_at(block_id).unwrap(); + let block_number = match self.client.block_number(&block_id) { None => return Ok(None), Some(block_number) => block_number, }; diff --git a/rpc/src/v1/traits/ibc.rs b/rpc/src/v1/traits/ibc.rs index 85b97363dd..24acca6e5d 100644 --- a/rpc/src/v1/traits/ibc.rs +++ b/rpc/src/v1/traits/ibc.rs @@ -91,8 +91,18 @@ pub trait IBC { ) -> Result>>; #[rpc(name = "ibc_query_latest_send_packet")] - fn query_latest_send_packet(&self, port_id: String, channel_id: String) -> Result>; + fn query_latest_send_packet( + &self, + port_id: String, + channel_id: String, + block_number: Option, + ) -> Result>; #[rpc(name = "ibc_query_latest_recv_packet")] - fn query_latest_recv_packet(&self, port_id: String, channel_id: String) -> Result>; + fn query_latest_recv_packet( + &self, + port_id: String, + channel_id: String, + block_number: Option, + ) -> Result>; } diff --git a/rpc/src/v1/types/ibc.rs b/rpc/src/v1/types/ibc.rs index 55cb5fd48d..0b3a3a0f0b 100644 --- a/rpc/src/v1/types/ibc.rs +++ b/rpc/src/v1/types/ibc.rs @@ -218,42 +218,30 @@ impl Packet { #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] -pub struct Sequence { - pub raw: u64, -} +pub struct Sequence(u64); impl FromCore for Sequence { fn from_core(core: CoreSequence) -> Self { - Sequence { - raw: core.raw, - } + Sequence(core.raw) } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] -pub struct PacketCommitmentHash { - pub raw: H256, -} +pub struct PacketCommitmentHash(H256); impl FromCore for PacketCommitmentHash { fn from_core(core: CorePacketCommitmentHash) -> Self { - PacketCommitmentHash { - raw: core.raw, - } + PacketCommitmentHash(core.raw) } } #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] -pub struct AcknowledgementHash { - pub raw: H256, -} +pub struct AcknowledgementHash(H256); impl FromCore for AcknowledgementHash { fn from_core(core: CoreAcknowledgementHash) -> Self { - AcknowledgementHash { - raw: core.raw, - } + AcknowledgementHash(core.raw) } }