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: 4 additions & 0 deletions ibc.ts/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,7 @@ trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4

[.env.default]
trim_trailing_whitespace = true
insert_final_newline = true
6 changes: 6 additions & 0 deletions ibc.ts/.env.default
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHAIN_A_RPC_URL="http://localhost:8080"
CHAIN_A_NETWORK_ID="tc"
CHAIN_A_FAUCET_ADDRESS="tccqym6znlgc48qeelrzccehkcaut7yz39wwq96q3y7"
CHAIN_A_COUNTERPARTY_CLIENT_ID="BClient"
CHAIN_A_COUNTERPARTY_CONNECTION_ID="BConnection"
CHAIN_A_COUNTERPARTY_CHANNEL_ID="BChannel"
CHAIN_B_RPC_URL="http://localhost:8081"
CHAIN_B_NETWORK_ID="fc"
CHAIN_B_FAUCET_ADDRESS="fccqyd6clszl2aeq4agrk8sgq8whkty6ktljuemc9y3"
CHAIN_B_COUNTERPARTY_CLIENT_ID="AClient"
CHAIN_B_COUNTERPARTY_CONNECTION_ID="AConnection"
CHAIN_B_COUNTERPARTY_CHANNEL_ID="AChannel"
37 changes: 37 additions & 0 deletions ibc.ts/src/common/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,48 @@ import { H256, PlatformAddress } from "codechain-primitives";
import { IBC } from "./foundry/transaction";
import { delay } from "./util";
import Debug from "debug";
import { ClientState } from "./foundry/types";
import { IBCHeader, IBCQueryResult } from "./types";

const debug = Debug("common:tx");

export interface CounterpartyIdentifiers {
/**
* Identifier for counter party chain's light client saved in this chain
*/
client: string;
/**
* Identifier for connection with counterparty chain
*/
connection: string;
/**
* Identifier for channel with counterparty chain
*/
channel: string;
}

export interface ChainConfig {
/**
* Example: "http://localhost:8080"
*/
server: string;
networkId: string;
faucetAddress: PlatformAddress;
counterpartyIdentifiers: CounterpartyIdentifiers;
}

export class Chain {
private readonly sdk: SDK;
private readonly faucetAddress: PlatformAddress;
public readonly counterpartyIdentifiers: CounterpartyIdentifiers;

public constructor(config: ChainConfig) {
this.sdk = new SDK({
server: config.server,
networkId: config.networkId
});
this.faucetAddress = config.faucetAddress;
this.counterpartyIdentifiers = config.counterpartyIdentifiers;
}

public async submitDatagram(datagram: Datagram): Promise<void> {
Expand All @@ -41,6 +61,23 @@ export class Chain {
const txHash = await this.sdk.rpc.chain.sendSignedTransaction(signedTx);
waitForTx(this.sdk, txHash);
}

public async latestHeight(): Promise<number> {
return await this.sdk.rpc.chain.getBestBlockNumber();
}

public async queryClient(
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> {
return this.sdk.rpc.sendRpcRequest("ibc_compose_header", [blockNumber]);
}
}

async function waitForTx(sdk: SDK, txHash: H256) {
Expand Down
2 changes: 1 addition & 1 deletion ibc.ts/src/common/datagram/createClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,6 @@ export class CreateClientDatagram {
}

public toEncodeObject(): any[] {
return [this.id, this.kind, this.consensusState, this.data];
return [1, this.id, this.kind, this.consensusState, this.data];
}
}
19 changes: 19 additions & 0 deletions ibc.ts/src/common/datagram/updateClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const RLP = require("rlp");

export class UpdateClientDatagram {
private id: string;
private header: Buffer;

public constructor({ id, header }: { id: string; header: Buffer }) {
this.id = id;
this.header = header;
}

public rlpBytes(): Buffer {
return RLP.encode(this.toEncodeObject());
}

public toEncodeObject(): any[] {
return [2, this.id, this.header];
}
}
4 changes: 4 additions & 0 deletions ibc.ts/src/common/foundry/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface ClientState {
number: number;
next_validator_set_hash: string;
}
6 changes: 6 additions & 0 deletions ibc.ts/src/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface IBCQueryResult<T> {
data: T | null;
proof: string;
}

export type IBCHeader = string;
17 changes: 15 additions & 2 deletions ibc.ts/src/relayer/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,32 @@ interface FoundryChainConfig {
rpcURL: string;
networkId: string;
faucetAddress: string;
counterpartyClientId: string;
counterpartyConnectionId: string;
counterpartyChannelId: string;
}

export function getConfig(): Config {
return {
chainA: {
rpcURL: getEnv("CHAIN_A_RPC_URL"),
networkId: getEnv("CHAIN_A_NETWORK_ID"),
faucetAddress: getEnv("CHAIN_A_FAUCET_ADDRESS")
faucetAddress: getEnv("CHAIN_A_FAUCET_ADDRESS"),
counterpartyClientId: getEnv("CHAIN_A_COUNTERPARTY_CLIENT_ID"),
counterpartyConnectionId: getEnv(
"CHAIN_A_COUNTERPARTY_CONNECTION_ID"
),
counterpartyChannelId: getEnv("CHAIN_A_COUNTERPARTY_CHANNEL_ID")
},
chainB: {
rpcURL: getEnv("CHAIN_B_RPC_URL"),
networkId: getEnv("CHAIN_B_NETWORK_ID"),
faucetAddress: getEnv("CHAIN_B_FAUCET_ADDRESS")
faucetAddress: getEnv("CHAIN_B_FAUCET_ADDRESS"),
counterpartyClientId: getEnv("CHAIN_B_COUNTERPARTY_CLIENT_ID"),
counterpartyConnectionId: getEnv(
"CHAIN_B_COUNTERPARTY_CONNECTION_ID"
),
counterpartyChannelId: getEnv("CHAIN_B_COUNTERPARTY_CHANNEL_ID")
}
};
}
Expand Down
86 changes: 80 additions & 6 deletions ibc.ts/src/relayer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Datagram } from "../common/datagram/index";
import { delay } from "../common/util";
import { getConfig } from "./config";
import { PlatformAddress } from "codechain-primitives/lib";
import { UpdateClientDatagram } from "../common/datagram/updateClient";

require("dotenv").config();

Expand All @@ -14,12 +15,22 @@ 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.faucetAddress),
counterpartyIdentifiers: {
client: config.chainA.counterpartyClientId,
connection: config.chainA.counterpartyConnectionId,
channel: config.chainA.counterpartyChannelId
}
});
const chainB = new Chain({
server: config.chainB.rpcURL,
networkId: config.chainB.networkId,
faucetAddress: PlatformAddress.fromString(config.chainB.faucetAddress)
faucetAddress: PlatformAddress.fromString(config.chainB.faucetAddress),
counterpartyIdentifiers: {
client: config.chainB.counterpartyClientId,
connection: config.chainB.counterpartyConnectionId,
channel: config.chainB.counterpartyChannelId
}
});

while (true) {
Expand Down Expand Up @@ -57,8 +68,71 @@ async function relayFromTo({
}
}

async function pendingDatagrams(
args: any
): Promise<{ localDatagrams: Datagram[]; counterpartyDatagrams: Datagram[] }> {
return { localDatagrams: [], counterpartyDatagrams: [] };
async function pendingDatagrams({
chain,
counterpartyChain
}: {
chain: Chain;
counterpartyChain: Chain;
}): Promise<{ localDatagrams: Datagram[]; counterpartyDatagrams: Datagram[] }> {
const height = await chain.latestHeight();
const counterpartyChainHeight = await chain.latestHeight();
let localDatagrams: Datagram[] = [];
let counterpartyDatagrams: Datagram[] = [];

localDatagrams = localDatagrams.concat(
await updateLightClient({
chain,
counterpartyChain,
height,
counterpartyChainHeight
})
);

counterpartyDatagrams = counterpartyDatagrams.concat(
await updateLightClient({
chain: counterpartyChain,
counterpartyChain: chain,
height: counterpartyChainHeight,
counterpartyChainHeight: height
})
);

return { localDatagrams, counterpartyDatagrams };
}

async function updateLightClient({
chain,
counterpartyChain,
height,
counterpartyChainHeight
}: {
chain: Chain;
counterpartyChain: Chain;
height: number;
counterpartyChainHeight: number;
}): Promise<Datagram[]> {
const datagrams = [];
const clientState = await chain.queryClient(height);

if (clientState!.data == null) {
throw new Error(
`No client state found. Please create a light client with identifier: ${chain.counterpartyIdentifiers.client}`
);
}
let currentBlockNumber = clientState!.data!.number;
while (currentBlockNumber < counterpartyChainHeight) {
const header = (await counterpartyChain.queryHeader(
currentBlockNumber + 1
))!;
datagrams.push(
new UpdateClientDatagram({
id: chain.counterpartyIdentifiers.client,
header: Buffer.from(header, "hex")
})
);
currentBlockNumber += 1;
}

return datagrams;
}