Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions core/src/ibc/transaction_handler/datagrams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ impl Decodable for Datagram {
match tag {
DatagramTag::CreateClient => {
let item_count = rlp.item_count()?;
if item_count != 4 {
if item_count != 5 {
return Err(DecoderError::RlpInvalidLength {
expected: 4,
expected: 5,
got: item_count,
})
}
Expand Down
5 changes: 5 additions & 0 deletions ibc.ts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ This directory contains IBC relayer implementation and IBC demo scenario script.

Run `yarn run runChains`

## How to run a scenario

Run `yarn run scenario`. It will create light clients, a connection, and a channel.
Finally, it will send a packet.

## Print debug log

Please use `DEBUG` environment variable.
Expand Down
19 changes: 16 additions & 3 deletions ibc.ts/src/common/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface ChainConfig {
networkId: string;
faucetAddress: PlatformAddress;
counterpartyIdentifiers: CounterpartyIdentifiers;
keystorePath: string;
}

export class Chain {
Expand All @@ -42,7 +43,11 @@ export class Chain {
public constructor(config: ChainConfig) {
this.sdk = new SDK({
server: config.server,
networkId: config.networkId
networkId: config.networkId,
keyStoreType: {
type: "local",
path: config.keystorePath
}
});
this.faucetAddress = config.faucetAddress;
this.counterpartyIdentifiers = config.counterpartyIdentifiers;
Expand All @@ -67,17 +72,25 @@ export class Chain {
}

public async queryClient(
blockNumber: number
blockNumber?: number
): Promise<IBCQueryResult<ClientState> | null> {
return this.sdk.rpc.sendRpcRequest("ibc_query_client_state", [
this.counterpartyIdentifiers.client,
blockNumber
]);
}

public async queryHeader(blockNumber: number): Promise<IBCHeader | null> {
public async queryIBCHeader(
blockNumber: number
): Promise<IBCHeader | null> {
return this.sdk.rpc.sendRpcRequest("ibc_compose_header", [blockNumber]);
}

public async queryChainHeader(blockNumber: number): Promise<string | null> {
return this.sdk.rpc.sendRpcRequest("chain_getRawHeaderByNumber", [
blockNumber
]);
}
}

async function waitForTx(sdk: SDK, txHash: H256) {
Expand Down
7 changes: 5 additions & 2 deletions ibc.ts/src/relayer/config.ts → ibc.ts/src/common/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface FoundryChainConfig {
counterpartyClientId: string;
counterpartyConnectionId: string;
counterpartyChannelId: string;
keystorePath: string;
}

export function getConfig(): Config {
Expand All @@ -26,7 +27,8 @@ export function getConfig(): Config {
counterpartyConnectionId: getEnv(
"CHAIN_A_COUNTERPARTY_CONNECTION_ID"
),
counterpartyChannelId: getEnv("CHAIN_A_COUNTERPARTY_CHANNEL_ID")
counterpartyChannelId: getEnv("CHAIN_A_COUNTERPARTY_CHANNEL_ID"),
keystorePath: "./chainA/keystore.db"
},
chainB: {
rpcURL: getEnv("CHAIN_B_RPC_URL"),
Expand All @@ -36,7 +38,8 @@ export function getConfig(): Config {
counterpartyConnectionId: getEnv(
"CHAIN_B_COUNTERPARTY_CONNECTION_ID"
),
counterpartyChannelId: getEnv("CHAIN_B_COUNTERPARTY_CHANNEL_ID")
counterpartyChannelId: getEnv("CHAIN_B_COUNTERPARTY_CHANNEL_ID"),
keystorePath: "./chainB/keystore.db"
}
};
}
Expand Down
1 change: 0 additions & 1 deletion ibc.ts/src/common/example.ts

This file was deleted.

10 changes: 6 additions & 4 deletions ibc.ts/src/relayer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Debug from "debug";
import { Chain } from "../common/chain";
import { Datagram } from "../common/datagram/index";
import { delay } from "../common/util";
import { getConfig } from "./config";
import { getConfig } from "../common/config";
import { PlatformAddress } from "codechain-primitives/lib";
import { UpdateClientDatagram } from "../common/datagram/updateClient";

Expand All @@ -20,7 +20,8 @@ async function main() {
client: config.chainA.counterpartyClientId,
connection: config.chainA.counterpartyConnectionId,
channel: config.chainA.counterpartyChannelId
}
},
keystorePath: config.chainA.keystorePath
});
const chainB = new Chain({
server: config.chainB.rpcURL,
Expand All @@ -30,7 +31,8 @@ async function main() {
client: config.chainB.counterpartyClientId,
connection: config.chainB.counterpartyConnectionId,
channel: config.chainB.counterpartyChannelId
}
},
keystorePath: config.chainB.keystorePath
});

while (true) {
Expand Down Expand Up @@ -122,7 +124,7 @@ async function updateLightClient({
}
let currentBlockNumber = clientState!.data!.number;
while (currentBlockNumber < counterpartyChainHeight) {
const header = (await counterpartyChain.queryHeader(
const header = (await counterpartyChain.queryIBCHeader(
currentBlockNumber + 1
))!;
datagrams.push(
Expand Down
84 changes: 82 additions & 2 deletions ibc.ts/src/scenario/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,87 @@
import { HelloWorld } from "../common/example";
import Debug from "debug";
import { getConfig } from "../common/config";
import { Chain } from "../common/chain";
import { PlatformAddress } from "codechain-primitives/lib";
import { CreateClientDatagram } from "../common/datagram/createClient";
import { strict as assert } from "assert";

require("dotenv").config();

const debug = Debug("scenario:main");

async function main() {
console.log(HelloWorld);
const config = getConfig();
const chainA = new Chain({
server: config.chainA.rpcURL,
networkId: config.chainA.networkId,
faucetAddress: PlatformAddress.fromString(config.chainA.faucetAddress),
counterpartyIdentifiers: {
client: config.chainA.counterpartyClientId,
connection: config.chainA.counterpartyConnectionId,
channel: config.chainA.counterpartyChannelId
},
keystorePath: config.chainA.keystorePath
});
const chainB = new Chain({
server: config.chainB.rpcURL,
networkId: config.chainB.networkId,
faucetAddress: PlatformAddress.fromString(config.chainB.faucetAddress),
counterpartyIdentifiers: {
client: config.chainB.counterpartyClientId,
connection: config.chainB.counterpartyConnectionId,
channel: config.chainB.counterpartyChannelId
},
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 });
}

main().catch(console.error);

async function createLightClient({
chain,
counterpartyChain
}: {
chain: Chain;
counterpartyChain: Chain;
}) {
debug("Create light client");
const counterpartyBlockNumber = await counterpartyChain.latestHeight();
const blockNumber = await chain.latestHeight();
debug(`height is ${counterpartyBlockNumber}`);
const counterpartyRawHeader = await counterpartyChain.queryChainHeader(
counterpartyBlockNumber
);
debug(`rawHeader is ${counterpartyRawHeader}`);

assert(counterpartyRawHeader, "header should not be empty");
assert.notStrictEqual(
counterpartyRawHeader!.substr(0, 2),
"0x",
"should not start with 0x"
);

debug(`Get queryClient`);
const clientStateBefore = await chain.queryClient(blockNumber);
assert.notEqual(clientStateBefore, null, "querying on the best block");
assert.equal(clientStateBefore!.data, null, "client is not initialized");

const createClient = new CreateClientDatagram({
id: chain.counterpartyIdentifiers.client,
kind: 0,
consensusState: Buffer.alloc(0),
data: Buffer.from(counterpartyRawHeader!, "hex")
});

debug(`Submit datagram`);
await chain.submitDatagram(createClient);

const clientStateAfter = await chain.queryClient();
assert.notEqual(clientStateAfter, null, "querying on the best block");
assert.notEqual(clientStateAfter!.data, null, "client is initialized");
debug(`Create client is ${JSON.stringify(clientStateAfter)}`);
}
6 changes: 6 additions & 0 deletions rpc/src/v1/impls/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use ctypes::transaction::Action;
use ctypes::{BlockHash, BlockNumber, ShardId, Tracker, TxHash};
use jsonrpc_core::Result;
use primitives::H256;
use rustc_hex::ToHex;
use std::convert::TryFrom;
use std::sync::Arc;

Expand Down Expand Up @@ -172,6 +173,11 @@ where
}))
}

fn get_raw_header_by_number(&self, block_number: u64) -> Result<Option<String>> {
let id = BlockId::Number(block_number);
Ok(self.client.block_header(&id).map(|header| header.into_inner().as_slice().to_hex()))
}

fn get_block_transaction_count_by_hash(&self, block_hash: BlockHash) -> Result<Option<usize>> {
Ok(self.client.block(&BlockId::Hash(block_hash)).map(|block| block.transactions_count()))
}
Expand Down
9 changes: 5 additions & 4 deletions rpc/src/v1/impls/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use ccore::{BlockChainClient, BlockId, StateInfo};
use ibc::client_02::types::Header;
use jsonrpc_core::Result;
use primitives::Bytes;
use rustc_hex::ToHex;
use std::sync::Arc;

#[allow(dead_code)]
Expand Down Expand Up @@ -69,7 +70,7 @@ where
Ok(Some(IBCQuery {
number: block_number,
data: client_state.map(|x| ClientState::from_core(&x)),
proof: querier::make_proof(&context, &path),
proof: querier::make_proof(&context, &path).to_hex(),
}))
}

Expand All @@ -96,7 +97,7 @@ where
Ok(Some(IBCQuery {
number: block_number,
data: consensus_state.map(|x| ConsensusState::from_core(&x)),
proof: querier::make_proof(&context, &path),
proof: querier::make_proof(&context, &path).to_hex(),
}))
}

Expand Down Expand Up @@ -147,7 +148,7 @@ where
let response = IBCQuery {
number: block_number,
data: connection_end.map(ConnectionEnd::from_core),
proof: querier::make_proof(&context, &path),
proof: querier::make_proof(&context, &path).to_hex(),
};
Ok(Some(response))
}
Expand All @@ -172,7 +173,7 @@ where
let response = IBCQuery {
number: block_number,
data: connections_in_client.map(|from_core| from_core.into_vec()),
proof: querier::make_proof(&context, &path),
proof: querier::make_proof(&context, &path).to_hex(),
};
Ok(Some(response))
}
Expand Down
4 changes: 4 additions & 0 deletions rpc/src/v1/traits/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ pub trait Chain {
#[rpc(name = "chain_getBlockTransactionCountByHash")]
fn get_block_transaction_count_by_hash(&self, block_hash: BlockHash) -> Result<Option<usize>>;

///Gets the raw bytes of a header with given number.
#[rpc(name = "chain_getRawHeaderByNumber")]
fn get_raw_header_by_number(&self, block_number: u64) -> Result<Option<String>>;

///Gets the minimum transaction fee of the given name.
#[rpc(name = "chain_getMinTransactionFee")]
fn get_min_transaction_fee(&self, action_type: String, block_number: Option<u64>) -> Result<Option<u64>>;
Expand Down
4 changes: 2 additions & 2 deletions rpc/src/v1/types/ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use codechain_core::ibc::client_02::types::{ClientState as CoreClientState, Cons
use codechain_core::ibc::connection_03::types::{
ConnectionEnd as CoreConnectionEnd, ConnectionState as CoreConnectionState,
};
use primitives::{Bytes, H256};
use primitives::H256;
use serde::Serialize;

type Identifier = String;
Expand All @@ -36,7 +36,7 @@ type CommitmentPrefix = String;
pub struct IBCQuery<T: Serialize> {
pub number: u64,
pub data: Option<T>,
pub proof: Bytes,
pub proof: String,
}

/// Client 02 related types
Expand Down
1 change: 1 addition & 0 deletions types/src/transaction/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ impl Decodable for ActionTag {
0x05 => Ok(Self::SetShardOwners),
0x06 => Ok(Self::SetShardUsers),
0x19 => Ok(Self::ShardStore),
0x20 => Ok(Self::IBC),
0xFF => Ok(Self::Custom),
_ => Err(DecoderError::Custom("Unexpected action prefix")),
}
Expand Down