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
5 changes: 5 additions & 0 deletions ibc.ts/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[src/**.{ts,json,js}]
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = space
indent_size = 4
6 changes: 6 additions & 0 deletions ibc.ts/.env.default
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CHAIN_A_RPC_URL="http://localhost:8080"
CHAIN_A_NETWORK_ID="tc"
CHAIN_A_FAUCET_ADDRESS="tccqym6znlgc48qeelrzccehkcaut7yz39wwq96q3y7"
CHAIN_B_RPC_URL="http://localhost:8081"
CHAIN_B_NETWORK_ID="fc"
CHAIN_B_FAUCET_ADDRESS="fccqyd6clszl2aeq4agrk8sgq8whkty6ktljuemc9y3"
5 changes: 5 additions & 0 deletions ibc.ts/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
yarn-error.log
yarn.lock
build/
.env
4 changes: 4 additions & 0 deletions ibc.ts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
IBC relayer and scenario
=========================

This directory contains IBC relayer implementation and IBC demo scenario script.
26 changes: 26 additions & 0 deletions ibc.ts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "ibc",
"version": "0.0.0",
"scripts": {
"build": "tsc -p .",
"fmt": "tslint -p . --fix && prettier 'src/**/*.{ts, json}' --write",
"lint": "tsc -p . --noEmit && tslint -p . && prettier 'src/**/*.{ts, json}' -l",
"scenario": "yarn build && node build/scenario/index.js",
"relayer": "yarn build && node build/relayer/index.js"
},
"devDependencies": {
"@types/debug": "^4.1.5",
"@types/node": "^13.7.4",
"prettier": "^1.19.1",
"tslint": "^6.0.0",
"tslint-config-prettier": "^1.18.0",
"typescript": "^3.8.2"
},
"dependencies": {
"codechain-primitives": "^1.0.4",
"codechain-sdk": "^2.0.1",
"debug": "^4.1.1",
"dotenv": "^8.2.0",
"rlp": "^2.0.0"
}
}
60 changes: 60 additions & 0 deletions ibc.ts/src/common/chain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { Datagram } from "./datagram/index";
import { SDK } from "codechain-sdk";
import { H256, PlatformAddress } from "codechain-primitives";
import { IBC } from "./foundry/transaction";
import { delay } from "./util";
import Debug from "debug";

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

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

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

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

public async submitDatagram(datagram: Datagram): Promise<void> {
const ibcAction = new IBC(this.sdk.networkId, datagram.rlpBytes());

const seq = await this.sdk.rpc.chain.getSeq(this.faucetAddress);
const signedTx = await this.sdk.key.signTransaction(ibcAction, {
account: this.faucetAddress,
fee: 100,
seq
});

const txHash = await this.sdk.rpc.chain.sendSignedTransaction(signedTx);
waitForTx(this.sdk, txHash);
}
}

async function waitForTx(sdk: SDK, txHash: H256) {
const timeout = delay(10 * 1000).then(() => {
throw new Error("Timeout");
});
const wait = (async () => {
while (true) {
debug(`wait tx: ${txHash.toString()}`);
if (sdk.rpc.chain.containsTransaction(txHash)) {
return;
}
await delay(500);
}
})();
return Promise.race([timeout, wait]);
}
33 changes: 33 additions & 0 deletions ibc.ts/src/common/datagram/createClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const RLP = require("rlp");

export class CreateClientDatagram {
private id: string;
private kind: number;
private consensusState: Buffer;
private data: Buffer;

public constructor({
id,
kind,
consensusState,
data
}: {
id: string;
kind: number;
consensusState: Buffer;
data: Buffer;
}) {
this.id = id;
this.kind = kind;
this.consensusState = consensusState;
this.data = data;
}

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

public toEncodeObject(): any[] {
return [this.id, this.kind, this.consensusState, this.data];
}
}
4 changes: 4 additions & 0 deletions ibc.ts/src/common/datagram/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Datagram {
rlpBytes(): Buffer;
toEncodeObject(): any[];
}
1 change: 1 addition & 0 deletions ibc.ts/src/common/example.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export let HelloWorld = "HelloWorld";
30 changes: 30 additions & 0 deletions ibc.ts/src/common/foundry/transaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Transaction } from "codechain-sdk/lib/core/classes";
import { NetworkId } from "codechain-sdk/lib/core/types";

export interface IBCActionJSON {
bytes: Buffer;
}

export class IBC extends Transaction {
private readonly bytes: Buffer;

public constructor(networkId: NetworkId, bytes: Buffer) {
super(networkId);
this.bytes = bytes;
}

public type(): string {
return "ibc";
}

protected actionToEncodeObject(): any[] {
return [0x20, this.bytes];
}

// Since the result type is hard-coded in the SDK, we should use any type here.
protected actionToJSON(): any {
return {
bytes: this.bytes
};
}
}
7 changes: 7 additions & 0 deletions ibc.ts/src/common/util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export async function delay(ms: number): Promise<void> {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, ms);
});
}
38 changes: 38 additions & 0 deletions ibc.ts/src/relayer/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
export interface Config {
chainA: FoundryChainConfig;
chainB: FoundryChainConfig;
}

interface FoundryChainConfig {
/**
* Foundry RPC URL
* ex) http://localhost:8080
*/
rpcURL: string;
networkId: string;
faucetAddress: 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")
},
chainB: {
rpcURL: getEnv("CHAIN_B_RPC_URL"),
networkId: getEnv("CHAIN_B_NETWORK_ID"),
faucetAddress: getEnv("CHAIN_B_FAUCET_ADDRESS")
}
};
}

function getEnv(key: string): string {
const result = process.env[key];
if (result) {
return result;
} else {
throw new Error(`Environment variable ${key} is not set`);
}
}
64 changes: 64 additions & 0 deletions ibc.ts/src/relayer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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 { PlatformAddress } from "codechain-primitives/lib";

require("dotenv").config();

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

async function main() {
const config = getConfig();
const chainA = new Chain({
server: config.chainA.rpcURL,
networkId: config.chainA.networkId,
faucetAddress: PlatformAddress.fromString(config.chainA.faucetAddress)
});
const chainB = new Chain({
server: config.chainB.rpcURL,
networkId: config.chainB.networkId,
faucetAddress: PlatformAddress.fromString(config.chainB.faucetAddress)
});

while (true) {
debug("Run relay");
await relay(chainA, chainB);
await delay(1000);
}
}

main().catch(console.error);

async function relay(chainA: Chain, chainB: Chain) {
await relayFromTo({ chain: chainA, counterpartyChain: chainB });
await relayFromTo({ chain: chainB, counterpartyChain: chainA });
}

async function relayFromTo({
chain,
counterpartyChain
}: {
chain: Chain;
counterpartyChain: Chain;
}) {
const { localDatagrams, counterpartyDatagrams } = await pendingDatagrams({
chain,
counterpartyChain
});

for (const localDiagram of localDatagrams) {
await chain.submitDatagram(localDiagram);
}

for (const counterpartyDatagram of counterpartyDatagrams) {
await counterpartyChain.submitDatagram(counterpartyDatagram);
}
}

async function pendingDatagrams(
args: any
): Promise<{ localDatagrams: Datagram[]; counterpartyDatagrams: Datagram[] }> {
return { localDatagrams: [], counterpartyDatagrams: [] };
}
7 changes: 7 additions & 0 deletions ibc.ts/src/scenario/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { HelloWorld } from "../common/example";

async function main() {
console.log(HelloWorld);
}

main().catch(console.error);
13 changes: 13 additions & 0 deletions ibc.ts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "es2018",
"types": ["node"],
"module": "commonjs",
"lib": ["es2018"],
"declaration": true,
"outDir": "build",
"strict": true
},
"include": ["./src/**/*"],
"exclude": ["./src/**/*.test.ts"]
}
27 changes: 27 additions & 0 deletions ibc.ts/tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"extends": ["tslint:recommended", "tslint-config-prettier"],
"rules": {
"interface-name": false,
"variable-name": [
true,
"check-format",
"allow-leading-underscore",
"allow-pascal-case"
],
"no-console": false,
"object-literal-sort-keys": false,
"only-arrow-functions": false,
"no-var-requires": false,
"max-classes-per-file": false,
"triple-equals": [true, "allow-null-check", "allow-undefined-check"],
"no-bitwise": false,
"array-type": false
},
"jsRules": {
"no-console": false,
"object-literal-sort-keys": false
},
"linterOptions": {
"exclude": ["node_modules/**/*.ts", "/build/*"]
}
}