implment fast merkel proof
MODIFY
package-lock.json
package-lock.json
"resolved": "https://registry.npmjs.org/@types/node/-/node-15.6.0.tgz",
"integrity": "sha512-gCYSfQpy+LYhOFTKAeE8BkyGqaxmlFxe+n4DKM6DR0wzw/HISUE/hAmkC/KT8Sw5PCJblqg062b3z9gucv3k0A=="
},
+ "@types/node-fetch": {
+ "version": "2.5.12",
+ "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz",
+ "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "form-data": "^3.0.0"
+ },
+ "dependencies": {
+ "form-data": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
+ "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
+ "dev": true,
+ "requires": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ }
+ }
+ }
+ },
"@types/normalize-package-data": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
"dev": true,
"requires": {
"node-fetch": "~1.7.1"
+ },
+ "dependencies": {
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "dev": true,
+ "requires": {
+ "encoding": "^0.1.11",
+ "is-stream": "^1.0.1"
+ }
+ }
}
},
"figgy-pudding": {
"integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
},
"node-fetch": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
- "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
- "dev": true,
- "requires": {
- "encoding": "^0.1.11",
- "is-stream": "^1.0.1"
- }
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
+ "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-gyp-build": {
"version": "4.2.3",

MODIFY
package.json
package.json
"ethereumjs-block": "2.2.0",
"ethereumjs-tx": "1.3.7",
"ethereumjs-util": "^5.2.0",
- "merkle-patricia-tree": "2.3.2"
+ "merkle-patricia-tree": "2.3.2",
+ "node-fetch": "^2.6.1"
},
"devDependencies": {
"@truffle/hdwallet-provider": "^1.2.0",
"@types/bn.js": "4.11.5",
"@types/ethereumjs-util": "^6.1.0",
+ "@types/node-fetch": "^2.5.12",
"@types/web3": "1.0.19",
"@typescript-eslint/eslint-plugin": "^2.16.0",
"@typescript-eslint/parser": "^2.16.0",

ADD
src/config.ts
+import MetaNetwork from '@maticnetwork/meta/network';
+
+export const config = {
+ networkApiUrl:MetaNetwork
+}
\ No newline at end of file

MODIFY
src/pos/erc20.ts
src/pos/erc20.ts
@@ -92,7 +92,21 @@ export class ERC20 extends POSToken {
);
}
+ /**
+ * complete withdraw process after checkpoint has been submitted for the block containing burn tx.
+ *
+ * Note:- It create the proof in api call for fast exit.
+ *
+ * @param {string} burnTransactionHash
+ * @param {ITransactionOption} [option]
+ * @returns
+ * @memberof ERC20
+ */
withdrawExitFaster(burnTransactionHash: string, option?: ITransactionOption) {
- // this.exitManager.
+ return this.exitManager.exitFast(
+ burnTransactionHash,
+ Log_Event_Signature.Erc20Transfer,
+ option
+ );
}
}
\ No newline at end of file

MODIFY
src/pos/exit_manager.ts
src/pos/exit_manager.ts
@@ -5,7 +5,8 @@ import { LOGGER } from "../constant";
import assert from "assert";
import BN from "bn.js";
import ethUtils from "ethereumjs-util";
-import { ITransactionOption } from "src/interfaces";
+import { ITransactionOption, ITransactionReceipt } from "src/interfaces";
+import { NetworkService } from "../services";
export class ExitManager {
private maticClient_: BaseWeb3Client;
@@ -34,43 +35,96 @@ export class ExitManager {
);
}
- async buildPayloadForExit(burnTxHash: string, logEventSig: string, requestConcurrency?) {
- // check checkpoint
- const [lastChildBlock, burnTx, receipt] = await Promise.all([
- this.rootChainManager.getLastChildBlock(),
- this.maticClient_.getTransaction(burnTxHash),
- this.maticClient_.getTransactionReceipt(burnTxHash)
- ]);
+ async exitFast(burnTxHash: string, logSignature: string, option: ITransactionOption) {
+ const payload = await this.buildPayloadForFastExit(
+ burnTxHash,
+ logSignature,
+ );
+ const method = this.rootChainManager.method("exit", payload);
- const block = await this.maticClient_.getBlockWithTransaction(burnTx.blockNumber);
+ return this.rootChainManager['processWrite'](
+ method,
+ option
+ );
+ }
- LOGGER.log({ 'burnTx.blockNumber': burnTx.blockNumber, lastCheckPointedBlockNumber: lastChildBlock });
+ private buildPayloadForExitFastMerkle_(start, end, blockNumber) {
+ // build block proof
+ return ProofUtil.buildBlockProof(
+ this.maticClient_,
+ parseInt(start, 10),
+ parseInt(end, 10),
+ parseInt(blockNumber + '', 10)
+ );
+ }
+
+ async buildPayloadForFastExit(burnTxHash, logEventSig) {
+ // check checkpoint
+ const lastChildBlock = await this.rootChainManager.getLastChildBlock();
+ const receipt = await this.maticClient_.getTransactionReceipt(burnTxHash);
+ const block: any = await this.maticClient_.getBlock(receipt.blockNumber);
assert.ok(
- new BN(lastChildBlock).gte(new BN(burnTx.blockNumber)),
+ new BN(lastChildBlock).gte(new BN(receipt.blockNumber)),
'Burn transaction has not been checkpointed as yet'
);
- const rootBlockNumber = await this.rootChainManager.findRootBlockFromChild(burnTx.blockNumber);
- const blockLimit = await this.rootChainManager.method(
+ let headerBlock;
+ const service = new NetworkService(
+ this.rootChainManager['client'].metaNetwork.Matic.NetworkAPI
+ );
+ try {
+ headerBlock = await service.getBlockIncluded(receipt.blockNumber as any);
+ if (!headerBlock || !headerBlock.start || !headerBlock.end || !headerBlock.headerBlockNumber) {
+ throw Error('Network API Error');
+ }
+ } catch (err) {
+ const rootBlockNumber = await this.rootChainManager.findRootBlockFromChild(receipt.blockNumber);
+ headerBlock = await this.rootChainManager.method(
"headerBlocks", formatAmount(rootBlockNumber)
).read<{ start: string, end: string }>();
+ }
// build block proof
- const blockProof = await ProofUtil.buildBlockProof(
- this.maticClient_,
- parseInt(blockLimit.start, 10),
- parseInt(blockLimit.end, 10),
- parseInt(burnTx.blockNumber + '', 10)
+
+ const start = parseInt(headerBlock.start, 10);
+ const end = parseInt(headerBlock.end, 10);
+ const receiptBlockNumber = parseInt(receipt.blockNumber + '', 10);
+ let blockProof;
+
+ try {
+ blockProof = await service.getProof(
+ start, end, receiptBlockNumber
);
+ if (!blockProof) {
+ throw Error('Network API Error');
+ }
+ } catch (err) {
+ blockProof = await this.buildPayloadForExitFastMerkle_(start, end, receiptBlockNumber);
+ }
const receiptProof: any = await ProofUtil.getReceiptProof(
- receipt,
- block,
- this.maticClient_,
- requestConcurrency
+ receipt, block, this.maticClient_
+ );
+ const logIndex = this.getLogIndex_(
+ logEventSig, receipt
+ );
+
+ return this._encodePayload(
+ headerBlock.headerBlockNumber,
+ blockProof,
+ receipt.blockNumber,
+ block.timestamp,
+ Buffer.from(block.transactionsRoot.slice(2), 'hex'),
+ Buffer.from(block.receiptsRoot.slice(2), 'hex'),
+ ProofUtil.getReceiptBytes(receipt), // rlp encoded
+ receiptProof.parentNodes,
+ receiptProof.path,
+ logIndex
);
+ }
+ private getLogIndex_(logEventSig: string, receipt: ITransactionReceipt) {
let logIndex = -1;
switch (logEventSig) {
@@ -97,6 +151,49 @@ export class ExitManager {
}
assert.ok(logIndex > -1, 'Log not found in receipt');
+ return logIndex;
+ }
+
+ async buildPayloadForExit(burnTxHash: string, logEventSig: string, requestConcurrency?) {
+ // check checkpoint
+ const [lastChildBlock, burnTx, receipt] = await Promise.all([
+ this.rootChainManager.getLastChildBlock(),
+ this.maticClient_.getTransaction(burnTxHash),
+ this.maticClient_.getTransactionReceipt(burnTxHash)
+ ]);
+
+ const block = await this.maticClient_.getBlockWithTransaction(burnTx.blockNumber);
+
+ LOGGER.log({ 'burnTx.blockNumber': burnTx.blockNumber, lastCheckPointedBlockNumber: lastChildBlock });
+
+ assert.ok(
+ new BN(lastChildBlock).gte(new BN(burnTx.blockNumber)),
+ 'Burn transaction has not been checkpointed as yet'
+ );
+ const rootBlockNumber = await this.rootChainManager.findRootBlockFromChild(burnTx.blockNumber);
+
+ const rootBlockInfo = await this.rootChainManager.method(
+ "headerBlocks", formatAmount(rootBlockNumber)
+ ).read<{ start: string, end: string }>();
+
+ // build block proof
+ const blockProof = await ProofUtil.buildBlockProof(
+ this.maticClient_,
+ parseInt(rootBlockInfo.start, 10),
+ parseInt(rootBlockInfo.end, 10),
+ parseInt(burnTx.blockNumber + '', 10)
+ );
+
+ const receiptProof: any = await ProofUtil.getReceiptProof(
+ receipt,
+ block,
+ this.maticClient_,
+ requestConcurrency
+ );
+
+ const logIndex = this.getLogIndex_(
+ logEventSig, receipt
+ );
return this._encodePayload(
rootBlockNumber,

ADD
src/services/index.ts
+import { HttpRequest } from "../utils";
+
+export class NetworkService {
+ httpRequest: HttpRequest;
+
+ constructor(baseUrl: string) {
+ this.httpRequest = new HttpRequest(baseUrl);
+ }
+
+ getBlockIncluded(blockNumber: string) {
+ const url = `block-included/${blockNumber}`;
+ return this.httpRequest.get<{
+ start: string;
+ end: string;
+ headerBlockNumber: string;
+ }>(url);
+ }
+
+ getProof(start, end, blockNumber) {
+ const url = `fast-merkle-proof?start=${start}&end=${end}&number=${blockNumber}`;
+ return this.httpRequest.get<any>(url).then(result => {
+ return result.proof;
+ });
+ }
+}
+
+

ADD
src/utils/http_request.ts
+export class HttpRequest {
+ baseUrl = "";
+ private fetch_: (input: RequestInfo, init?: RequestInit) => Promise<Response> = window ? window.fetch : require('node-fetch');
+
+ constructor(option: { baseUrl: string } | string = {} as any) {
+ option = typeof option === "string" ? {
+ baseUrl: option
+ } : option;
+
+ if (option.baseUrl) {
+ this.baseUrl = option.baseUrl;
+ }
+ }
+
+ get<T>(url = "", query = {}): Promise<T> {
+ url = this.baseUrl + url + Object.keys(query).
+ map(key => `${encodeURIComponent(key)}=${encodeURIComponent(query[key])}`).join('&');
+
+ return this.fetch_(url, {
+ method: 'GET',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ }
+ }).then(res => {
+ return res.json();
+ });
+ }
+
+ post(url = "", body) {
+ url = this.baseUrl + url;
+
+ return this.fetch_(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ },
+ body: body ? JSON.stringify(body) : null
+ }).then(res => {
+ return res.json();
+ });
+ }
+}
\ No newline at end of file

MODIFY
src/utils/index.ts
src/utils/index.ts
@@ -7,6 +7,7 @@ export * from "./logger";
export * from "./merge";
export * from "./map_promise";
export * from "./proof_util";
+export * from "./http_request";
export const util = {
proof: ProofUtil

implment fast merkel proof