@instadapp/interop-x 0.0.0-dev.9b1fcb8 → 0.0.0-dev.a846f65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/package.json +5 -5
  2. package/dist/src/api/index.js +3 -3
  3. package/dist/src/config/index.js +1 -0
  4. package/dist/src/index.js +44 -5
  5. package/dist/src/net/peer/index.js +2 -1
  6. package/dist/src/net/pool/index.js +18 -2
  7. package/dist/src/net/protocol/dial/SignatureDialProtocol.1.js +28 -0
  8. package/dist/src/net/protocol/index.js +41 -1
  9. package/dist/src/tasks/AutoUpdateTask.js +67 -0
  10. package/dist/src/tasks/BaseTask.js +7 -3
  11. package/dist/src/tasks/InteropBridge/ProcessWithdrawEvents.js +146 -0
  12. package/dist/src/tasks/InteropBridge/SyncWithdrawEvents.js +2 -1
  13. package/dist/src/tasks/InteropXGateway/ProcessDepositEvents.js +3 -1
  14. package/dist/src/tasks/InteropXGateway/SyncDepositEvents.js +0 -1
  15. package/dist/src/tasks/Transactions/SyncTransactionStatusTask.js +53 -0
  16. package/dist/src/tasks/index.js +8 -0
  17. package/dist/src/utils/index.js +68 -8
  18. package/package.json +5 -5
  19. package/src/api/index.ts +2 -2
  20. package/src/config/index.ts +2 -0
  21. package/src/index.ts +56 -7
  22. package/src/net/peer/index.ts +2 -1
  23. package/src/net/pool/index.ts +25 -5
  24. package/src/net/protocol/dial/SignatureDialProtocol.1.ts +31 -0
  25. package/src/net/protocol/index.ts +57 -1
  26. package/src/tasks/AutoUpdateTask.ts +81 -0
  27. package/src/tasks/BaseTask.ts +8 -3
  28. package/src/tasks/InteropBridge/ProcessWithdrawEvents.ts +231 -0
  29. package/src/tasks/InteropBridge/SyncWithdrawEvents.ts +3 -3
  30. package/src/tasks/InteropXGateway/ProcessDepositEvents.ts +4 -2
  31. package/src/tasks/InteropXGateway/SyncDepositEvents.ts +0 -2
  32. package/src/tasks/Transactions/SyncTransactionStatusTask.ts +65 -0
  33. package/src/tasks/index.ts +11 -0
  34. package/src/utils/index.ts +88 -7
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const BaseTask_1 = require("../BaseTask");
7
+ const logger_1 = __importDefault(require("@/logger"));
8
+ const net_1 = require("@/net");
9
+ const db_1 = require("@/db");
10
+ const sequelize_1 = require("sequelize");
11
+ class SyncTransactionStatusTask extends BaseTask_1.BaseTask {
12
+ constructor() {
13
+ super({
14
+ logger: new logger_1.default("SyncTransactionStatusTask"),
15
+ });
16
+ this.pollIntervalMs = 60 * 1000;
17
+ this.exceptLeadNode = true;
18
+ }
19
+ async pollHandler() {
20
+ // if transaction is pending for more than 1 hour, check lead node for status
21
+ const leadNode = net_1.peerPool.getLeadPeer();
22
+ if (!leadNode) {
23
+ return;
24
+ }
25
+ const transaction = await db_1.Transaction.findOne({
26
+ where: {
27
+ status: 'pending',
28
+ sourceCreatedAt: {
29
+ [sequelize_1.Op.gte]: new Date(Date.now() - 60 * 60 * 1000),
30
+ },
31
+ }
32
+ });
33
+ if (!transaction) {
34
+ return;
35
+ }
36
+ this.logger.info(`Requesting transaction status for ${transaction.transactionHash}`);
37
+ const transactionStatus = await net_1.protocol.requestTransactionStatus(transaction.transactionHash, leadNode.id);
38
+ if (!transactionStatus) {
39
+ return;
40
+ }
41
+ this.logger.info(`Received transaction status for ${transaction.transactionHash}`);
42
+ transaction.sourceStatus = transactionStatus.sourceStatus;
43
+ transaction.sourceTransactionHash = transactionStatus.sourceTransactionHash;
44
+ transaction.sourceErrors = transactionStatus.sourceErrors;
45
+ transaction.targetStatus = transactionStatus.targetStatus;
46
+ transaction.targetTransactionHash = transactionStatus.targetTransactionHash;
47
+ transaction.targetErrors = transactionStatus.targetErrors;
48
+ transaction.status = transactionStatus.status;
49
+ await transaction.save();
50
+ this.logger.info(`Updated transaction status for ${transaction.transactionHash}`);
51
+ }
52
+ }
53
+ exports.default = SyncTransactionStatusTask;
@@ -7,9 +7,14 @@ exports.Tasks = void 0;
7
7
  const ProcessDepositEvents_1 = __importDefault(require("./InteropXGateway/ProcessDepositEvents"));
8
8
  const SyncDepositEvents_1 = __importDefault(require("./InteropXGateway/SyncDepositEvents"));
9
9
  const SyncWithdrawEvents_1 = __importDefault(require("./InteropBridge/SyncWithdrawEvents"));
10
+ const ProcessWithdrawEvents_1 = __importDefault(require("./InteropBridge/ProcessWithdrawEvents"));
11
+ const AutoUpdateTask_1 = __importDefault(require("./AutoUpdateTask"));
12
+ const SyncTransactionStatusTask_1 = __importDefault(require("./Transactions/SyncTransactionStatusTask"));
10
13
  class Tasks {
11
14
  constructor() {
12
15
  this.tasks = [
16
+ new SyncTransactionStatusTask_1.default(),
17
+ new AutoUpdateTask_1.default(),
13
18
  new SyncDepositEvents_1.default({
14
19
  chainId: 43114
15
20
  }),
@@ -19,6 +24,9 @@ class Tasks {
19
24
  new SyncWithdrawEvents_1.default({
20
25
  chainId: 137,
21
26
  itokenAddress: '0xEab02fe1F016eE3e4106c1C6aad35FeEe657268E',
27
+ }),
28
+ new ProcessWithdrawEvents_1.default({
29
+ chainId: 137,
22
30
  })
23
31
  ];
24
32
  }
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getContract = exports.buildDataForTransaction = exports.generateInteropTransactionHash = exports.asyncCallWithTimeout = exports.buildSignatureBytes = exports.getRpcProviderUrl = exports.signGnosisSafeTx = exports.short = exports.http = void 0;
6
+ exports.getContract = exports.buildWithdrawDataForTransaction = exports.buildDepositDataForTransaction = exports.buildDataForTransaction = exports.generateInteropTransactionHash = exports.asyncCallWithTimeout = exports.buildSignatureBytes = exports.getRpcProviderUrl = exports.signGnosisSafeTx = exports.short = exports.shortenHash = exports.http = void 0;
7
7
  /**
8
8
  * @module util
9
9
  */
@@ -16,6 +16,16 @@ const config_1 = __importDefault(require("@/config"));
16
16
  const abi_1 = __importDefault(require("@/abi"));
17
17
  exports.http = axios_1.default.create();
18
18
  (0, axios_retry_1.default)(exports.http, { retries: 3, retryDelay: axios_retry_1.default.exponentialDelay });
19
+ function shortenHash(hash, length = 4) {
20
+ if (!hash)
21
+ return;
22
+ if (hash.length < 12)
23
+ return hash;
24
+ const beginningChars = hash.startsWith("0x") ? length + 2 : length;
25
+ const shortened = hash.substr(0, beginningChars) + "…" + hash.substr(-length);
26
+ return shortened;
27
+ }
28
+ exports.shortenHash = shortenHash;
19
29
  function short(buffer) {
20
30
  return buffer.toString('hex').slice(0, 8) + '...';
21
31
  }
@@ -59,11 +69,11 @@ exports.signGnosisSafeTx = signGnosisSafeTx;
59
69
  const getRpcProviderUrl = (chainId) => {
60
70
  switch (chainId) {
61
71
  case 1:
62
- return 'https://rpc.instadapp.io/mainnet';
72
+ return 'https://rpc.ankr.com/eth';
63
73
  case 137:
64
- return 'https://rpc.instadapp.io/polygon';
74
+ return 'https://rpc.ankr.com/polygon';
65
75
  case 43114:
66
- return 'https://rpc.instadapp.io/avalanche';
76
+ return 'https://rpc.ankr.com/avalanche';
67
77
  default:
68
78
  throw new Error(`Unknown chainId: ${chainId}`);
69
79
  }
@@ -104,9 +114,20 @@ const generateInteropTransactionHash = (data) => {
104
114
  exports.generateInteropTransactionHash = generateInteropTransactionHash;
105
115
  const buildDataForTransaction = async (transaction, type) => {
106
116
  type = type || transaction.sourceStatus === 'pending' ? 'source' : 'target';
117
+ switch (transaction.action) {
118
+ case "deposit":
119
+ return await (0, exports.buildDepositDataForTransaction)(transaction, type);
120
+ case "withdraw":
121
+ return await (0, exports.buildWithdrawDataForTransaction)(transaction, type);
122
+ default:
123
+ throw new Error(`Unknown action: ${transaction.action}`);
124
+ }
125
+ };
126
+ exports.buildDataForTransaction = buildDataForTransaction;
127
+ const buildDepositDataForTransaction = async (transaction, type) => {
107
128
  const transactions = [];
108
- if (transaction.action != 'deposit') {
109
- throw new Error('Invalid action');
129
+ if (transaction.action !== 'deposit') {
130
+ throw new Error(`Invalid action: ${transaction.action}`);
110
131
  }
111
132
  if (transaction.action === 'deposit' && transaction.sourceStatus === 'pending') {
112
133
  throw Error('Cannot build data for pending deposit transaction');
@@ -125,7 +146,7 @@ const buildDataForTransaction = async (transaction, type) => {
125
146
  const targetChainProvider = new ethers_1.ethers.providers.JsonRpcProvider((0, exports.getRpcProviderUrl)(transaction.targetChainId));
126
147
  const targetWallet = new ethers_1.ethers.Wallet(config_1.default.privateKey, targetChainProvider);
127
148
  const interopBridgeContract = getContract(itoken.address, abi_1.default.interopBridgeToken, targetWallet);
128
- const { data } = await interopBridgeContract.populateTransaction.mint(transaction.submitEvent.user, ethers_1.ethers.BigNumber.from(transaction.submitEvent.amount.toString()), ethers_1.ethers.BigNumber.from(transaction.submitEvent.sourceChainId.toString()), transaction.sourceTransactionHash);
149
+ const { data } = await interopBridgeContract.populateTransaction.mint(transaction.submitEvent.user, ethers_1.ethers.BigNumber.from(transaction.submitEvent.amount.toString()), ethers_1.ethers.BigNumber.from(transaction.submitEvent.sourceChainId.toString()), transaction.submitTransactionHash);
129
150
  transactions.push({
130
151
  to: itoken.address,
131
152
  data: data,
@@ -134,12 +155,51 @@ const buildDataForTransaction = async (transaction, type) => {
134
155
  });
135
156
  return (0, ethers_multisend_1.encodeMulti)(transactions).data;
136
157
  };
137
- exports.buildDataForTransaction = buildDataForTransaction;
158
+ exports.buildDepositDataForTransaction = buildDepositDataForTransaction;
159
+ const buildWithdrawDataForTransaction = async (transaction, type) => {
160
+ const transactions = [];
161
+ if (transaction.action !== 'withdraw') {
162
+ throw new Error(`Invalid action: ${transaction.action}`);
163
+ }
164
+ if (transaction.action === 'withdraw' && transaction.sourceStatus === 'pending') {
165
+ throw Error('Cannot build data for pending withdraw transaction');
166
+ }
167
+ if (!transaction.submitEvent) {
168
+ throw Error('Cannot build data for transaction without submitEvent');
169
+ }
170
+ const { to, amount, chainId, itoken: itokenAddress } = transaction.submitEvent;
171
+ const itoken = constants_1.itokens[transaction.sourceChainId].find(token => token.address.toLowerCase() === itokenAddress.toLowerCase());
172
+ if (!itoken) {
173
+ throw Error('Cannot build data for transaction without itoken');
174
+ }
175
+ const token = constants_1.tokens[chainId].find(t => t.symbol.toLowerCase() === itoken.symbol.toLowerCase());
176
+ if (!token) {
177
+ throw Error('Cannot build data for transaction without token');
178
+ }
179
+ const targetChainProvider = new ethers_1.ethers.providers.JsonRpcProvider((0, exports.getRpcProviderUrl)(transaction.targetChainId));
180
+ const targetWallet = new ethers_1.ethers.Wallet(config_1.default.privateKey, targetChainProvider);
181
+ const gatewayAddress = constants_1.addresses[chainId].interopXGateway;
182
+ const interopBridgeContract = getContract(gatewayAddress, abi_1.default.interopXGateway, targetWallet);
183
+ const { data } = await interopBridgeContract.populateTransaction.systemWithdraw(ethers_1.ethers.BigNumber.from(amount.toString()), to, token.address, ethers_1.ethers.BigNumber.from(transaction.sourceChainId.toString()), transaction.submitTransactionHash);
184
+ transactions.push({
185
+ to: gatewayAddress,
186
+ data: data,
187
+ value: '0',
188
+ operation: ethers_multisend_1.OperationType.Call,
189
+ });
190
+ return (0, ethers_multisend_1.encodeMulti)(transactions).data;
191
+ };
192
+ exports.buildWithdrawDataForTransaction = buildWithdrawDataForTransaction;
138
193
  function getContract(address, contractInterface, signerOrProvider) {
139
194
  if (!ethers_1.ethers.utils.getAddress(address) || address === ethers_1.ethers.constants.AddressZero) {
140
195
  throw Error(`Invalid 'address' parameter '${address}'.`);
141
196
  }
142
197
  const contract = new ethers_1.ethers.Contract(address, contractInterface, signerOrProvider);
198
+ // Make sure the contract properties is writable
199
+ const desc = Object.getOwnPropertyDescriptor(contract, 'functions');
200
+ if (!desc || desc.writable !== true) {
201
+ return contract;
202
+ }
143
203
  return new Proxy(contract, {
144
204
  get(target, prop, receiver) {
145
205
  const value = Reflect.get(target, prop, receiver);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@instadapp/interop-x",
3
- "version": "0.0.0-dev.9b1fcb8",
3
+ "version": "0.0.0-dev.a846f65",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -24,9 +24,10 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@achingbrain/libp2p-gossipsub": "^0.12.2",
27
+ "@fastify/cors": "^7.0.0",
28
+ "await-spawn": "^4.0.2",
27
29
  "axios": "^0.27.1",
28
30
  "axios-retry": "^3.2.4",
29
- "bignumber.js": "^9.0.2",
30
31
  "chalk": "4.1.2",
31
32
  "dotenv": "^16.0.0",
32
33
  "ethereumjs-util": "^7.1.4",
@@ -34,7 +35,6 @@
34
35
  "ethers-multisend": "^2.1.1",
35
36
  "expand-home-dir": "^0.0.3",
36
37
  "fastify": "^3.28.0",
37
- "fastify-cors": "^6.0.3",
38
38
  "libp2p": "^0.36.2",
39
39
  "libp2p-bootstrap": "^0.14.0",
40
40
  "libp2p-kad-dht": "^0.28.6",
@@ -46,6 +46,8 @@
46
46
  "libp2p-websockets": "^0.16.2",
47
47
  "luxon": "^2.3.2",
48
48
  "module-alias": "^2.2.2",
49
+ "patch-package": "^6.4.7",
50
+ "postinstall-postinstall": "^2.1.0",
49
51
  "sequelize": "6.18.0",
50
52
  "sqlite3": "^5.0.5",
51
53
  "waait": "^1.0.5"
@@ -60,8 +62,6 @@
60
62
  "@types/fs-extra": "^9.0.13",
61
63
  "@types/node": "^17.0.17",
62
64
  "nodemon": "^2.0.15",
63
- "patch-package": "^6.4.7",
64
- "postinstall-postinstall": "^2.1.0",
65
65
  "replace-in-file": "^6.3.2",
66
66
  "rimraf": "^3.0.2",
67
67
  "ts-node": "^10.5.0",
package/src/api/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import fastify from "fastify"
2
- import cors from 'fastify-cors'
2
+ import cors from '@fastify/cors'
3
3
  import Logger from "@/logger"
4
4
  import { Transaction } from "@/db";
5
5
 
@@ -27,7 +27,7 @@ export const startApiServer = async () => {
27
27
 
28
28
  logger.log(`RPC Server listening at http://${HOST}:${PORT}`)
29
29
  } catch (err) {
30
- logger.error(err)
30
+ logger.error(err.message)
31
31
  process.exit(1)
32
32
  }
33
33
  }
@@ -8,12 +8,14 @@ class Config {
8
8
  public readonly privateKey: string
9
9
  public readonly wallet: Wallet
10
10
  public readonly staging: boolean
11
+ public readonly autoUpdate: boolean
11
12
 
12
13
  constructor() {
13
14
  this.events = new EventBus() as EventBusType
14
15
  this.maxPeers = 10
15
16
  this.privateKey = process.env.PRIVATE_KEY as string;
16
17
  this.staging = !! process.env.STAGING && process.env.STAGING === 'true';
18
+ this.autoUpdate = !! process.env.AUTO_UPDATE && process.env.AUTO_UPDATE === 'true';
17
19
  this.wallet = new Wallet(this.privateKey);
18
20
  this.leadNodeAddress = '0x910E413DBF3F6276Fe8213fF656726bDc142E08E'
19
21
  }
package/src/index.ts CHANGED
@@ -16,8 +16,8 @@ moduleAlias.addAliases({
16
16
  })
17
17
 
18
18
  moduleAlias();
19
- import assert from "assert";
20
19
  import dotenv from "dotenv";
20
+ import chalk from 'chalk';
21
21
  import { ethers } from "ethers";
22
22
  import packageJson from '../package.json'
23
23
  dotenv.config();
@@ -25,28 +25,46 @@ dotenv.config();
25
25
  import Logger from "@/logger";
26
26
  const logger = new Logger('Process')
27
27
 
28
-
29
- if (process.argv.at(-1) === 'help') {
28
+ const printUsage = () => {
30
29
  console.log('Usage:')
31
30
  console.log(' PRIVATE_KEY=abcd1234 interop-x')
32
31
  console.log(' PRIVATE_KEY=abcd1234 STAGING=true interop-x')
32
+ console.log(' PRIVATE_KEY=abcd1234 AUTO_UPDATE=true interop-x')
33
+ console.log(' PRIVATE_KEY=abcd1234 API_HOST=0.0.0.0 API_PORT=8080 interop-x')
34
+ }
35
+
36
+ if (process.argv.at(-1) === 'help') {
37
+ printUsage()
33
38
  process.exit(0)
34
39
  }
35
40
 
36
- assert(process.env.PRIVATE_KEY, "PRIVATE_KEY is not defined");
41
+ const GIT_SHORT_HASH = '@GIT_SHORT_HASH@';
37
42
 
43
+ if (process.argv.at(-1) === 'version') {
44
+ console.log(`Interop X Node (v${packageJson.version} - rev.${GIT_SHORT_HASH})`)
45
+ process.exit(0)
46
+ }
47
+
48
+ if(! process.env.PRIVATE_KEY) {
49
+ console.error(chalk.bgRed.white.bold('Please provide a private key\n'))
50
+ printUsage()
51
+ process.exit(1)
52
+ }
38
53
  try {
39
54
  new ethers.Wallet(process.env.PRIVATE_KEY!)
40
55
  } catch (e) {
41
- logger.error('Invalid private key')
56
+ console.error(chalk.bgRed.white('Invalid private key\n'))
57
+ printUsage()
42
58
  process.exit(1)
43
59
  }
44
60
 
45
- logger.debug(`Starting Interop X Node (v${packageJson.version} - rev.@GIT_SHORT_HASH@)`)
61
+ logger.debug(`Starting Interop X Node (v${packageJson.version} - rev.${GIT_SHORT_HASH})`)
46
62
 
47
63
  import { Tasks } from "@/tasks";
48
- import { startPeer } from "@/net";
64
+ import { startPeer, protocol, peerPool } from "@/net";
49
65
  import { startApiServer } from '@/api';
66
+ import { Transaction } from './db';
67
+ import { shortenHash } from './utils';
50
68
 
51
69
  async function main() {
52
70
 
@@ -57,6 +75,37 @@ async function main() {
57
75
  tasks.start();
58
76
 
59
77
  startApiServer()
78
+
79
+ protocol.on('TransactionStatus', async (payload) => {
80
+ if (!peerPool.isLeadNode(payload.peerId)) {
81
+ const peer = peerPool.getPeer(payload.peerId)
82
+
83
+ if(! peer) {
84
+ return;
85
+ }
86
+
87
+ logger.info(`ignored transaction status from ${payload.peerId} ${shortenHash(peer.publicAddress)} `)
88
+ return;
89
+ }
90
+
91
+ const transaction = await Transaction.findOne({ where: { transactionHash: payload.data.transactionHash } })
92
+
93
+ if (!transaction) {
94
+ return;
95
+ }
96
+
97
+ transaction.sourceStatus = payload.data.sourceStatus
98
+ transaction.sourceTransactionHash = payload.data.sourceTransactionHash
99
+ transaction.sourceErrors = payload.data.sourceErrors
100
+
101
+ transaction.targetStatus = payload.data.targetStatus
102
+ transaction.targetTransactionHash = payload.data.targetTransactionHash
103
+ transaction.targetErrors = payload.data.targetErrors
104
+
105
+ transaction.status = payload.data.status
106
+
107
+ await transaction.save()
108
+ })
60
109
  }
61
110
 
62
111
  main()
@@ -17,6 +17,7 @@ import KadDHT from "libp2p-kad-dht";
17
17
  import PubsubPeerDiscovery from "libp2p-pubsub-peer-discovery";
18
18
  import { protocol } from "@/net";
19
19
  import config from "@/config";
20
+ import chalk from "chalk";
20
21
 
21
22
  const logger = new Logger("Peer");
22
23
 
@@ -80,7 +81,7 @@ export const startPeer = async ({ }: IPeerOptions) => {
80
81
  },
81
82
  });
82
83
 
83
- logger.info("Peer ID:", node.peerId.toB58String());
84
+ logger.info("Peer ID:", chalk.bold(node.peerId.toB58String()));
84
85
 
85
86
  await node.start();
86
87
 
@@ -1,6 +1,9 @@
1
1
  import { Event } from "@/types";
2
2
  import config from "@/config";
3
3
  import Logger from "@/logger";
4
+ import { getAddress } from "ethers/lib/utils";
5
+ import { shortenHash } from "@/utils";
6
+ import chalk from "chalk";
4
7
 
5
8
 
6
9
  const logger = new Logger('PeerPool')
@@ -83,10 +86,10 @@ export class PeerPool {
83
86
  const newPeer = !this.pool.get(peer.id);
84
87
  this.pool.set(peer.id, peer)
85
88
  peer.pooled = true
86
-
87
- if(newPeer) {
89
+
90
+ if (newPeer) {
88
91
  config.events.emit(Event.POOL_PEER_ADDED, peer)
89
- logger.info(`Peer ${peer.id} with address ${peer.publicAddress} added to pool`)
92
+ logger.info(`Peer ${chalk.bold(shortenHash(peer.id, 16))} with address ${chalk.bold(shortenHash(peer.publicAddress))} added to pool`)
90
93
  }
91
94
  }
92
95
  }
@@ -101,7 +104,7 @@ export class PeerPool {
101
104
  if (this.pool.delete(peer.id)) {
102
105
  peer.pooled = false
103
106
  config.events.emit(Event.POOL_PEER_REMOVED, peer)
104
- logger.info(`Peer ${peer.id} with address ${peer.publicAddress} removed from pool`)
107
+ logger.info(`Peer ${chalk.bold(shortenHash(peer.id, 16))} with address ${chalk.bold(shortenHash(peer.publicAddress))} removed from pool`)
105
108
  }
106
109
  }
107
110
  }
@@ -110,7 +113,7 @@ export class PeerPool {
110
113
  this.cleanup()
111
114
 
112
115
  return this.peers.filter((p) => {
113
- if(!p.pooled) return false;
116
+ if (!p.pooled) return false;
114
117
 
115
118
  const now = new Date()
116
119
 
@@ -122,6 +125,23 @@ export class PeerPool {
122
125
  return this.activePeers.map((p) => p.id)
123
126
  }
124
127
 
128
+ getPeer(id: string){
129
+ return this.pool.get(id);
130
+ }
131
+
132
+ isLeadNode(id: string) {
133
+ const peer = this.pool.get(id);
134
+
135
+ if (!peer) {
136
+ return false;
137
+ }
138
+
139
+ return getAddress(peer.publicAddress) === getAddress(config.leadNodeAddress)
140
+ }
141
+
142
+ getLeadPeer() {
143
+ return this.peers.find((p) => this.isLeadNode(p.id))
144
+ }
125
145
 
126
146
  cleanup() {
127
147
  // let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
@@ -0,0 +1,31 @@
1
+ import { BaseDialProtocol } from "./BaseDialProtocol";
2
+ import { Transaction } from "@/db";
3
+
4
+ export class TransactionStatusDialProtocol extends BaseDialProtocol<string, Pick<Transaction, 'transactionHash' | 'sourceStatus' | 'sourceTransactionHash' | 'sourceErrors' | 'targetStatus' | 'targetTransactionHash' | 'targetErrors' | 'status'> | null> {
5
+ protected timeout = 30000;
6
+
7
+ constructor(libp2p) {
8
+ super(libp2p, '/interop-x/transaction-status')
9
+ }
10
+
11
+ async response(transactionHash: string){
12
+ const transaction = await Transaction.findOne({ where: { transactionHash } })
13
+
14
+ if(! transaction){
15
+ return null
16
+ }
17
+ return {
18
+ transactionHash: transaction.transactionHash,
19
+
20
+ sourceStatus: transaction.sourceStatus,
21
+ sourceTransactionHash: transaction.sourceTransactionHash,
22
+ sourceErrors: transaction.sourceErrors,
23
+
24
+ targetStatus: transaction.targetStatus,
25
+ targetTransactionHash: transaction.targetTransactionHash,
26
+ targetErrors: transaction.targetErrors,
27
+
28
+ status: transaction.status,
29
+ }
30
+ }
31
+ }
@@ -5,6 +5,8 @@ import { SignatureDialProtocol, ISignatureRequest, ISignatureResponse } from "./
5
5
  import { IPeerInfo, peerPool } from "..";
6
6
  import config from "@/config";
7
7
  import { Event } from "@/types";
8
+ import { Transaction } from "@/db";
9
+ import { TransactionStatusDialProtocol } from "./dial/SignatureDialProtocol.1";
8
10
 
9
11
  export interface ProtocolOptions {
10
12
  /* Handshake timeout in ms (default: 8000) */
@@ -33,7 +35,12 @@ interface PeerInfoEvent extends BaseMessageEvent {
33
35
  data: Omit<IPeerInfo, 'id' | 'updated' | 'idle' | 'pooled'>
34
36
  }
35
37
 
38
+ interface TransactionStatusEvent extends BaseMessageEvent {
39
+ data: Pick<Transaction, 'transactionHash' | 'sourceStatus' | 'sourceTransactionHash' | 'sourceErrors' | 'targetStatus' | 'targetTransactionHash' | 'targetErrors' | 'status'>
40
+ }
41
+
36
42
  declare interface Protocol {
43
+ on(event: 'TransactionStatus', listener: (payload: TransactionStatusEvent) => void): this;
37
44
  on(event: 'PeerInfo', listener: (payload: PeerInfoEvent) => void): this;
38
45
  on(event: string, listener: (payload: BaseMessageEvent) => void): this;
39
46
  }
@@ -44,7 +51,7 @@ class Protocol extends EventEmitter {
44
51
  private protocolMessages: Message[] = [
45
52
  {
46
53
  name: 'PeerInfo',
47
- code: 0x09,
54
+ code: 0x01,
48
55
  encode: (info: Pick<IPeerInfo, 'publicAddress'>) => [
49
56
  Buffer.from(info.publicAddress),
50
57
  ],
@@ -52,8 +59,39 @@ class Protocol extends EventEmitter {
52
59
  publicAddress: publicAddress.toString(),
53
60
  }),
54
61
  },
62
+ {
63
+ name: 'TransactionStatus',
64
+ code: 0x02,
65
+ encode: (transaction: Transaction) => [
66
+ Buffer.from(transaction.transactionHash),
67
+
68
+ Buffer.from(transaction.sourceStatus),
69
+ Buffer.from(transaction.sourceTransactionHash),
70
+ transaction.sourceErrors ? transaction.sourceErrors.map((e) => Buffer.from(e)) : [],
71
+
72
+ Buffer.from(transaction.targetStatus),
73
+ Buffer.from(transaction.targetTransactionHash),
74
+ transaction.targetErrors ? transaction.targetErrors.map((e) => Buffer.from(e)) : [],
75
+
76
+ Buffer.from(transaction.status),
77
+ ],
78
+ decode: ([transactionHash, sourceStatus, sourceTransactionHash, sourceErrors, targetStatus, targetTransactionHash, targetErrors, status]: [Buffer, Buffer, Buffer, Buffer[], Buffer, Buffer, Buffer[], Buffer]) => ({
79
+ transactionHash: transactionHash.toString(),
80
+
81
+ sourceStatus: sourceStatus.toString(),
82
+ sourceTransactionHash: sourceTransactionHash.toString(),
83
+ sourceErrors: sourceErrors.map((e) => e.toString()),
84
+
85
+ targetStatus: targetStatus.toString(),
86
+ targetTransactionHash: targetTransactionHash.toString(),
87
+ targetErrors: targetErrors.map((e) => e.toString()),
88
+
89
+ status: status.toString(),
90
+ }),
91
+ },
55
92
  ];
56
93
  private signature: SignatureDialProtocol;
94
+ private transactionStatus: TransactionStatusDialProtocol;
57
95
 
58
96
 
59
97
  start({ libp2p, topic = null, }) {
@@ -73,6 +111,7 @@ class Protocol extends EventEmitter {
73
111
  })
74
112
 
75
113
  this.signature = new SignatureDialProtocol(this.libp2p);
114
+ this.transactionStatus = new TransactionStatusDialProtocol(this.libp2p);
76
115
  }
77
116
 
78
117
 
@@ -121,6 +160,14 @@ class Protocol extends EventEmitter {
121
160
  this.libp2p.pubsub.publish(this.topic, encoded)
122
161
  }
123
162
 
163
+ public sendTransaction(transaction: Transaction) {
164
+ const message = this.protocolMessages.find((m) => m.name === 'TransactionStatus')!
165
+
166
+ const encoded = rlp.encode([message.code, message.encode(transaction)]);
167
+
168
+ this.libp2p.pubsub.publish(this.topic, encoded)
169
+ }
170
+
124
171
  async requestSignatures(data: ISignatureRequest, peerIds?: string[]) {
125
172
  try {
126
173
  peerIds = peerIds || peerPool.activePeerIds;
@@ -133,6 +180,15 @@ class Protocol extends EventEmitter {
133
180
  return []
134
181
  }
135
182
  }
183
+
184
+ async requestTransactionStatus(transactionHash: string, peerId: string) {
185
+ try {
186
+ return await this.transactionStatus.send(transactionHash, peerId);
187
+ } catch (error) {
188
+ console.log(error);
189
+ return null
190
+ }
191
+ }
136
192
  }
137
193
 
138
194
  export const protocol = new Protocol();