Skip to content
This repository has been archived by the owner on Jul 8, 2021. It is now read-only.

Commit

Permalink
Add verify command and optimize relay check (#85)
Browse files Browse the repository at this point in the history
* feat(cmd): add redeem debug tool

* chore(cmd): init dj-verify to binary list

* fix(relay): return submitted proposals

* feat(proposal): add several pre-check for submit proposals

* fix(relay): the check of pendingHeaders

* feat(relay): complete proposal check

* chore(deps): update the version of util-crypto

* fix(lint): the object access of relay check

* feat(postinsall): remove old types.json after install

* fix(relay): add lastConfirmed check
  • Loading branch information
clearloop authored Sep 17, 2020
1 parent f1fb407 commit 961afa7
Show file tree
Hide file tree
Showing 14 changed files with 159 additions and 96 deletions.
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"private": false,
"name": "@darwinia/dj",
"version": "0.2.4-alpha.2",
"version": "0.2.5-alpha.3",
"description": "Darwinia bridge relayer tool",
"homepage": "https://github.com/darwinia-network/dj",
"repository": {
Expand All @@ -14,13 +14,14 @@
"bin": {
"dj": "lib/index.js",
"dj-proposal": "lib/src/bin/proposal.js",
"dj-confirm": "lib/src/bin/confirm.js"
"dj-confirm": "lib/src/bin/confirm.js",
"dj-verify": "lib/src/bin/verify.js"
},
"files": ["lib/**/*"],
"dependencies": {
"@polkadot/api": "1.32.1",
"@polkadot/keyring": "3.4.1",
"@polkadot/util-crypto": "3.3.1",
"@polkadot/util-crypto": "3.4.1",
"axios": "^0.19.2",
"prompts": "^2.3.2",
"web3": "^1.2.11",
Expand All @@ -38,6 +39,7 @@
},
"scripts": {
"build": "tsc --strict",
"lint": "tsc --noEmit --strict && tslint --project ./tsconfig.json"
"lint": "tsc --noEmit --strict && tslint --project ./tsconfig.json",
"postinstall": "rm -f ~/.darwinia/types.json"
}
}
64 changes: 56 additions & 8 deletions src/api/darwinia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { DispatchError, EventRecord } from "@polkadot/types/interfaces/types";
import { cryptoWaitReady } from "@polkadot/util-crypto";
import {
IEthereumHeaderThingWithProof,
IEthereumHeaderThingWithConfirmation,
IReceiptWithProof,
} from "../types";

Expand Down Expand Up @@ -196,6 +197,7 @@ export class API {
} else {
return new ExResult(false, "", "");
}

log.event(`Approve block ${block}`);
return await this.blockFinalized(ex, true);
}
Expand All @@ -212,31 +214,77 @@ export class API {
} else {
return new ExResult(false, "", "");
}

log.event(`Reject block ${block}`);
return await this.blockFinalized(ex);
}

/**
* Set confirmed block with sudo privilege
*/
public async setConfirmed(headerThing: IEthereumHeaderThingWithProof): Promise<ExResult> {
log.event(`Set confirmed block ${headerThing.header.number}`);
const ex = this._.tx.ethereumRelay.setConfirmed(headerThing);
public async setConfirmed(headerThing: IEthereumHeaderThingWithConfirmation): Promise<ExResult> {
log.event(`Set confirmed block ${headerThing.header_thing.header.number}`);
const ex = this._.tx.ethereumRelay.setConfirmed(headerThing.header_thing);
return await this.blockFinalized(this._.tx.sudo.sudo(ex));
}

/**
* Check if should relay target
*
* @param {number} target - target header
*/
public async shouldRelay(target: number): Promise<boolean> {
log.trace("Check if target block less than the last confirmed block");
const lastConfirmed = await this.lastConfirm();
if (target < lastConfirmed) {
return false;
}
// Check if has confirmed
log.trace("Check if proposal has been confirmed");
const confirmed = await this._.query.ethereumRelay.confirmedHeaders(target);
if (confirmed.toJSON()) {
log.event(`Proposal ${target} has been submitted yet`);
return false;
}

// Check if is pendding
log.trace("Check if proposal is pending");
const pendingHeaders = (
await this._.query.ethereumRelayerGame.pendingHeaders()
).toJSON() as string[][];
if (pendingHeaders.filter((h: any) => Number.parseInt(h[1], 10) === target).length > 0) {
log.event(`Proposal ${target} has been submitted yet`);
// return new ExResult(true, "", "");
return false;
}

// Check if target contains in the current Game
//
// Storage Key: `0xcdacb51c37fcd27f3b87230d9a1c265088c2f7188c6fdd1dffae2fa0d171f440`
log.trace("Check if proposal is in the relayer game");
for (const key of (await this._.rpc.state.getKeysPaged(
"0xcdacb51c37fcd27f3b87230d9a1c265088c2f7188c6fdd1dffae2fa0d171f440",
32,
)).toJSON() as any[]) {
const codec: string = (await this._.rpc.state.getStorage(key) as any).toJSON();
const proposal = this._.createType("Vec<RelayProposalT>" as any, codec);
for (const bonded of proposal.toHuman()[0].bonded_proposal) {
if (Number.parseInt(bonded[1].header.number.replace(/,/g, ""), 10) >= target) {
return false;
}
}
};

return true;
}

/**
* get the specify block
*
* @param {IEthHeaderThing} headerThings - Eth Header Things
*/
public async submitProposal(headerThings: IEthereumHeaderThingWithProof[]): Promise<ExResult> {
const latest = headerThings[headerThings.length - 1].header.number;
const confirmed = await this._.query.ethereumRelay.confirmedHeaders(latest);
if (confirmed.toJSON()) {
log.event(`Proposal ${latest} has been submitted yet`);
return new ExResult(true, "", "");
}

// Submit new proposal
log.event(`Submit proposal contains block ${latest}`);
Expand Down
9 changes: 2 additions & 7 deletions src/bin/confirm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { API, ShadowAPI } from "../api";
(async () => {
const args = process.argv.slice(2);
if (args.length !== 1) {
log.warn("Usage: dj-confirm <number>");
console.log("Usage: dj-confirm <number>");
return;
}

Expand All @@ -21,12 +21,7 @@ import { API, ShadowAPI } from "../api";
// Trigger relay
const lastConfirmed = await api.lastConfirm();
try {
await api.setConfirmed(await shadow.getProposal(
lastConfirmed,
target,
target - 1,
));

await api.setConfirmed(await shadow.getHeaderThing(target));
log.ox(`Set confirmed block ${target} succeed!`);
} catch (e) {
log.ex(e);
Expand Down
2 changes: 1 addition & 1 deletion src/bin/proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { API, ShadowAPI } from "../api";
(async () => {
const args = process.argv.slice(2);
if (args.length !== 1) {
log.warn("Usage: dj-proposal <number>");
console.log("Usage: dj-proposal <number>");
return;
}

Expand Down
33 changes: 33 additions & 0 deletions src/bin/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env node
import { Config, log } from "../util";
import { API, ShadowAPI } from "../api";

(async () => {
const args = process.argv.slice(2);
if (args.length !== 1) {
console.log("Usage: dj-verify <tx>/<confirmed> [-d deposit]");
console.log("Example: dj-verify 0x1d3ef601b9fa4a7f1d6259c658d0a10c77940fa5db9e10ab55397eb0ce88807d/8694126");
console.log("Example: dj-verify 0x1d3ef601b9fa4a7f1d6259c658d0a10c77940fa5db9e10ab55397eb0ce88807d/8694126 -d");
return;
}

/// Init logs
process.env.LOGGER = "ALL";

/// Init API
const conf = new Config();
const api = await API.auto();
const shadow = new ShadowAPI(conf.shadow);
const target = args[0].split("/");

// Trigger relay
try {
await api.redeem(
args.indexOf("-d") > -1? "Deposit": "Token",
await shadow.getReceipt(target[0], Number.parseInt(target[1], 10))
);
log.ox(`Redeem tx ${target} succeed!`);
} catch (e) {
log.ex(e);
}
})();
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export default async function main() {
// Start proposal linstener
Listener.guard(api, shadow);
Listener.relay(api, shadow, QUEUE);
Listener.redeem(api, shadow, QUEUE);
Listener.ethereum(conf.eth, QUEUE);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { IEthereumHeaderThingWithProof } from "../../types";
import { Cache } from "../"

/// NewRound handler
export default async function newRound(
export default async function game(
event: any,
phase: any,
types: any,
Expand Down
26 changes: 26 additions & 0 deletions src/listener/chain/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { ShadowAPI, API } from "../../api";
import { log } from "../../util";
import { DispatchError } from "@polkadot/types/interfaces/types";
import game from "./game";

export function listen(api: API, shadow: ShadowAPI) {
// Subscribe to system events via storage
api._.query.system.events((events: any) => {
events.forEach(async (record: any) => {
const { event, phase } = record;
const types = event.typeDef;

// Chain events
switch (event.method) {
case "NewRound":
await game(event, phase, types, api, shadow);
}

if (event.data[0] && (event.data[0] as DispatchError).isModule) {
log.err(api._.registry.findMetaError(
(event.data[0] as DispatchError).asModule.toU8a(),
));
}
});
});
}
4 changes: 3 additions & 1 deletion src/listener/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ export async function listen(api: API, shadow: ShadowAPI) {
// start listening
const handled: number[] = [];
setInterval(async () => {
const headers = (await api._.query.ethereumRelayerGame.pendingHeaders()).toJSON() as string[][];
const headers = (
await api._.query.ethereumRelayerGame.pendingHeaders()
).toJSON() as string[][];
if (headers.length === 0) {
return;
}
Expand Down
1 change: 1 addition & 0 deletions src/listener/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export * as Cache from "./cache";
export { listen as guard } from "./guard";
export { listen as relay } from "./relay";
export { listen as redeem } from "./redeem";
export { listen as ethereum } from "./eth";
19 changes: 19 additions & 0 deletions src/listener/redeem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ShadowAPI, API } from "../api";
import { delay } from "../util";
import { ITx } from "../types";

/// Approved handler
export async function listen(
api: API,
shadow: ShadowAPI,
queue: ITx[],
) {
setInterval(async () => {
const lastConfirmed = await api.lastConfirm();
for (const tx of queue.filter((t) => t.blockNumber < lastConfirmed)) {
await api.redeem(tx.ty, await shadow.getReceipt(tx.tx, lastConfirmed));
await delay(10000);
};
queue = queue.filter((t) => t.blockNumber >= lastConfirmed);
}, 30000);
}
21 changes: 10 additions & 11 deletions src/listener/relay/relay.ts → src/listener/relay.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,31 @@
import { ShadowAPI, API } from "../../api";
import { log } from "../../util";
import { ITx } from "../../types";
import { ShadowAPI, API } from "../api";
import { log } from "../util";
import { ITx } from "../types";

// Listen and submit proposals
export default function relay(api: API, shadow: ShadowAPI, queue: ITx[]) {
const submitted: number[] = [];

// Trigger relay every 180s
export function listen(api: API, shadow: ShadowAPI, queue: ITx[]) {
setInterval(async () => {
if (queue.length < 1) return;

// Check last confirm
const lastConfirmed = await api.lastConfirm();
const maxBlock = queue.sort((p, q) => q.blockNumber - p.blockNumber)[0].blockNumber;
if (lastConfirmed === maxBlock + 1) {
if (lastConfirmed >= maxBlock + 1) {
return;
}

// Submit new proposal
const target = Math.max(lastConfirmed, maxBlock) + 1;
if (!(await api.shouldRelay(target))) {
return;
};

// Relay txs
log(`Currently we have ${queue.length} txs are waiting to be redeemed`);
await api.submitProposal([await shadow.getProposal(
lastConfirmed,
target,
target - 1,
)]).catch(log.err);

// Refresh target
submitted.push(target);
}, 60000);
}
21 changes: 0 additions & 21 deletions src/listener/relay/approve.ts

This file was deleted.

42 changes: 0 additions & 42 deletions src/listener/relay/index.ts

This file was deleted.

0 comments on commit 961afa7

Please sign in to comment.