@instadapp/interop-x 0.0.0-dev.051cd4e

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 (94) hide show
  1. package/.env.example +2 -0
  2. package/.github/workflows/publish-dev.yml +30 -0
  3. package/README.md +21 -0
  4. package/bin/interop-x +2 -0
  5. package/dist/package.json +72 -0
  6. package/dist/src/abi/erc20.json +350 -0
  7. package/dist/src/abi/gnosisSafe.json +747 -0
  8. package/dist/src/abi/index.js +15 -0
  9. package/dist/src/abi/interopBridgeToken.json +286 -0
  10. package/dist/src/abi/interopXGateway.json +184 -0
  11. package/dist/src/api/index.js +33 -0
  12. package/dist/src/config/index.js +22 -0
  13. package/dist/src/constants/addresses.js +20 -0
  14. package/dist/src/constants/index.js +19 -0
  15. package/dist/src/constants/itokens.js +13 -0
  16. package/dist/src/constants/tokens.js +107 -0
  17. package/dist/src/db/index.js +17 -0
  18. package/dist/src/db/models/index.js +17 -0
  19. package/dist/src/db/models/transaction.js +54 -0
  20. package/dist/src/db/sequelize.js +23 -0
  21. package/dist/src/index.js +103 -0
  22. package/dist/src/logger/index.js +138 -0
  23. package/dist/src/net/index.js +19 -0
  24. package/dist/src/net/peer/index.js +108 -0
  25. package/dist/src/net/pool/index.js +125 -0
  26. package/dist/src/net/protocol/dial/BaseDialProtocol.js +106 -0
  27. package/dist/src/net/protocol/dial/SignatureDialProtocol.js +56 -0
  28. package/dist/src/net/protocol/index.js +121 -0
  29. package/dist/src/tasks/AutoUpdateTask.js +45 -0
  30. package/dist/src/tasks/BaseTask.js +61 -0
  31. package/dist/src/tasks/InteropBridge/ProcessWithdrawEvents.js +147 -0
  32. package/dist/src/tasks/InteropBridge/SyncWithdrawEvents.js +70 -0
  33. package/dist/src/tasks/InteropXGateway/ProcessDepositEvents.js +150 -0
  34. package/dist/src/tasks/InteropXGateway/SyncDepositEvents.js +75 -0
  35. package/dist/src/tasks/index.js +42 -0
  36. package/dist/src/typechain/Erc20.js +2 -0
  37. package/dist/src/typechain/GnosisSafe.js +2 -0
  38. package/dist/src/typechain/InteropBridgeToken.js +2 -0
  39. package/dist/src/typechain/InteropXGateway.js +2 -0
  40. package/dist/src/typechain/common.js +2 -0
  41. package/dist/src/typechain/factories/Erc20__factory.js +367 -0
  42. package/dist/src/typechain/factories/GnosisSafe__factory.js +1174 -0
  43. package/dist/src/typechain/factories/InteropBridgeToken__factory.js +459 -0
  44. package/dist/src/typechain/factories/InteropXGateway__factory.js +265 -0
  45. package/dist/src/typechain/factories/index.js +14 -0
  46. package/dist/src/typechain/index.js +35 -0
  47. package/dist/src/types.js +21 -0
  48. package/dist/src/utils/index.js +228 -0
  49. package/package.json +72 -0
  50. package/patches/@ethersproject+properties+5.6.0.patch +13 -0
  51. package/src/abi/erc20.json +350 -0
  52. package/src/abi/gnosisSafe.json +747 -0
  53. package/src/abi/index.ts +11 -0
  54. package/src/abi/interopBridgeToken.json +286 -0
  55. package/src/abi/interopXGateway.json +184 -0
  56. package/src/api/index.ts +33 -0
  57. package/src/config/index.ts +32 -0
  58. package/src/constants/addresses.ts +17 -0
  59. package/src/constants/index.ts +3 -0
  60. package/src/constants/itokens.ts +10 -0
  61. package/src/constants/tokens.ts +104 -0
  62. package/src/db/index.ts +1 -0
  63. package/src/db/models/index.ts +1 -0
  64. package/src/db/models/transaction.ts +96 -0
  65. package/src/db/sequelize.ts +22 -0
  66. package/src/index.ts +123 -0
  67. package/src/logger/index.ts +157 -0
  68. package/src/net/index.ts +3 -0
  69. package/src/net/peer/index.ts +120 -0
  70. package/src/net/pool/index.ts +154 -0
  71. package/src/net/protocol/dial/BaseDialProtocol.ts +104 -0
  72. package/src/net/protocol/dial/SignatureDialProtocol.ts +71 -0
  73. package/src/net/protocol/index.ts +182 -0
  74. package/src/tasks/AutoUpdateTask.ts +54 -0
  75. package/src/tasks/BaseTask.ts +75 -0
  76. package/src/tasks/InteropBridge/ProcessWithdrawEvents.ts +233 -0
  77. package/src/tasks/InteropBridge/SyncWithdrawEvents.ts +121 -0
  78. package/src/tasks/InteropXGateway/ProcessDepositEvents.ts +245 -0
  79. package/src/tasks/InteropXGateway/SyncDepositEvents.ts +126 -0
  80. package/src/tasks/index.ts +41 -0
  81. package/src/typechain/Erc20.ts +491 -0
  82. package/src/typechain/GnosisSafe.ts +1728 -0
  83. package/src/typechain/InteropBridgeToken.ts +686 -0
  84. package/src/typechain/InteropXGateway.ts +407 -0
  85. package/src/typechain/common.ts +44 -0
  86. package/src/typechain/factories/Erc20__factory.ts +368 -0
  87. package/src/typechain/factories/GnosisSafe__factory.ts +1178 -0
  88. package/src/typechain/factories/InteropBridgeToken__factory.ts +466 -0
  89. package/src/typechain/factories/InteropXGateway__factory.ts +272 -0
  90. package/src/typechain/factories/index.ts +7 -0
  91. package/src/typechain/index.ts +12 -0
  92. package/src/types.ts +39 -0
  93. package/src/utils/index.ts +307 -0
  94. package/tsconfig.json +30 -0
@@ -0,0 +1,96 @@
1
+ import { sequelize } from '@/db/sequelize'
2
+ import { CreationOptional, InferAttributes, InferCreationAttributes, Model, DataTypes } from 'sequelize';
3
+
4
+ export class Transaction extends Model<InferAttributes<Transaction>, InferCreationAttributes<Transaction>> {
5
+ declare id: CreationOptional<number>;
6
+
7
+ declare transactionHash: string;
8
+ declare action: string;
9
+ declare from: string;
10
+ declare to: string;
11
+
12
+ declare submitTransactionHash: string;
13
+ declare submitBlockNumber: number;
14
+
15
+ declare sourceChainId: number;
16
+ declare sourceTransactionHash: CreationOptional<string>;
17
+ declare sourceBlockNumber: CreationOptional<number>;
18
+ declare sourceStatus: string;
19
+ declare sourceErrors: CreationOptional<string[]>;
20
+ declare sourceCreatedAt: CreationOptional<Date>;
21
+ declare sourceDelayUntil: CreationOptional<Date>;
22
+
23
+ declare targetChainId: number;
24
+ declare targetTransactionHash: CreationOptional<string>;
25
+ declare targetBlockNumber: CreationOptional<number>;
26
+ declare targetStatus: string;
27
+ declare targetErrors: CreationOptional<string[]>;
28
+ declare targetCreatedAt: CreationOptional<Date>;
29
+ declare targetDelayUntil: CreationOptional<Date>;
30
+
31
+ declare submitEvent: any;
32
+ declare sourceEvent: CreationOptional<any>;
33
+ declare targetEvent: CreationOptional<any>;
34
+
35
+ declare metadata: CreationOptional<any>;
36
+
37
+ declare status: string;
38
+
39
+ declare createdAt: CreationOptional<Date>;
40
+ declare updatedAt: CreationOptional<Date>;
41
+ }
42
+
43
+ Transaction.init({
44
+ id: {
45
+ type: DataTypes.INTEGER,
46
+ autoIncrement: true,
47
+ primaryKey: true
48
+ },
49
+
50
+ submitTransactionHash: DataTypes.NUMBER,
51
+ submitBlockNumber: DataTypes.NUMBER,
52
+
53
+ transactionHash: DataTypes.STRING,
54
+ action: DataTypes.STRING,
55
+
56
+ from: DataTypes.STRING,
57
+ to: DataTypes.STRING,
58
+
59
+ sourceChainId: DataTypes.NUMBER,
60
+ sourceTransactionHash: DataTypes.STRING,
61
+ sourceBlockNumber: DataTypes.NUMBER,
62
+ sourceStatus: DataTypes.STRING,
63
+ sourceErrors: {
64
+ type: DataTypes.JSON,
65
+ // defaultValue: [],
66
+ },
67
+ sourceCreatedAt: {
68
+ type: DataTypes.DATE,
69
+ defaultValue: Date.now()
70
+ },
71
+ sourceDelayUntil: DataTypes.STRING,
72
+
73
+ targetChainId: DataTypes.NUMBER,
74
+ targetTransactionHash: DataTypes.STRING,
75
+ targetBlockNumber: DataTypes.NUMBER,
76
+ targetStatus: DataTypes.STRING,
77
+ targetErrors: {
78
+ type: DataTypes.JSON,
79
+ // defaultValue: [],
80
+ },
81
+ targetCreatedAt: DataTypes.DATE,
82
+ targetDelayUntil: DataTypes.DATE,
83
+
84
+ submitEvent: DataTypes.JSON,
85
+ sourceEvent: DataTypes.JSON,
86
+ targetEvent: DataTypes.JSON,
87
+
88
+ metadata: DataTypes.JSON,
89
+
90
+ status: {
91
+ type: DataTypes.STRING,
92
+ defaultValue: 'pending'
93
+ },
94
+ createdAt: DataTypes.DATE,
95
+ updatedAt: DataTypes.DATE,
96
+ }, { sequelize, tableName: 'transactions' });
@@ -0,0 +1,22 @@
1
+ //@ts-ignore
2
+ import config from "@/config";
3
+ import expandHomeDir from "expand-home-dir";
4
+
5
+ import { Sequelize } from 'sequelize';
6
+
7
+ const basePath = expandHomeDir(`~/.interop-x/data/${config.publicAddress}/${config.staging ? 'staging' : ''}`);
8
+
9
+ export const sequelize = new Sequelize({
10
+ dialect: 'sqlite',
11
+ storage: `${basePath}/localDB.sqlite`,
12
+ // dialectModulePath: '@journeyapps/sqlcipher',
13
+ logging: false,
14
+ });
15
+
16
+ sequelize.sync({
17
+ alter: true
18
+ }).then(() => {
19
+ // sequelize.query('PRAGMA cipher_compatibility = 3');
20
+ // sequelize.query("PRAGMA key = 'your-encryption-key'");
21
+ })
22
+
package/src/index.ts ADDED
@@ -0,0 +1,123 @@
1
+ import moduleAlias from 'module-alias';
2
+
3
+ moduleAlias.addAliases({
4
+ "@/": __dirname + "/",
5
+ "@/logger": __dirname + "/logger",
6
+ "@/tasks": __dirname + "/tasks",
7
+ "@/utils": __dirname + "/utils",
8
+ "@/api": __dirname + "/api",
9
+ "@/net": __dirname + "/net",
10
+ "@/db": __dirname + "/db",
11
+ "@/config": __dirname + "/config",
12
+ "@/types": __dirname + "/types",
13
+ "@/abi": __dirname + "/abi",
14
+ "@/constants": __dirname + "/constants",
15
+ "@/typechain": __dirname + "/typechain"
16
+ })
17
+
18
+ moduleAlias();
19
+ import dotenv from "dotenv";
20
+ import chalk from 'chalk';
21
+ import { ethers } from "ethers";
22
+ import packageJson from '../package.json'
23
+ dotenv.config();
24
+
25
+ import Logger from "@/logger";
26
+ const logger = new Logger('Process')
27
+
28
+ const printUsage = () => {
29
+ console.log('Usage:')
30
+ console.log(' PRIVATE_KEY=abcd1234 interop-x')
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()
38
+ process.exit(0)
39
+ }
40
+
41
+ const GIT_SHORT_HASH = '@GIT_SHORT_HASH@';
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
+ }
53
+ try {
54
+ new ethers.Wallet(process.env.PRIVATE_KEY!)
55
+ } catch (e) {
56
+ console.error(chalk.bgRed.white('Invalid private key\n'))
57
+ printUsage()
58
+ process.exit(1)
59
+ }
60
+
61
+ logger.debug(`Starting Interop X Node (v${packageJson.version} - rev.${GIT_SHORT_HASH})`)
62
+
63
+ import { Tasks } from "@/tasks";
64
+ import { startPeer, protocol, peerPool } from "@/net";
65
+ import { startApiServer } from '@/api';
66
+ import { Transaction } from './db';
67
+
68
+ async function main() {
69
+
70
+ startPeer({})
71
+
72
+ const tasks = new Tasks()
73
+
74
+ tasks.start();
75
+
76
+ startApiServer()
77
+
78
+ protocol.on('TransactionStatus', async (payload) => {
79
+ if (!peerPool.isLeadNode(payload.peerId)) {
80
+ const peer = peerPool.getPeer(payload.peerId)
81
+ logger.info(`ignored transaction status from ${payload.peerId} ${peer?.publicAddress} `)
82
+ return;
83
+ }
84
+
85
+ const transaction = await Transaction.findOne({ where: { transactionHash: payload.data.transactionHash } })
86
+
87
+ if (!transaction) {
88
+ return;
89
+ }
90
+
91
+ transaction.sourceStatus = payload.data.sourceStatus
92
+ transaction.sourceTransactionHash = payload.data.sourceTransactionHash
93
+ transaction.sourceErrors = payload.data.sourceErrors
94
+
95
+ transaction.targetStatus = payload.data.targetStatus
96
+ transaction.targetTransactionHash = payload.data.targetTransactionHash
97
+ transaction.targetErrors = payload.data.targetErrors
98
+
99
+ transaction.status = payload.data.status
100
+
101
+ await transaction.save()
102
+ })
103
+ }
104
+
105
+ main()
106
+ .then(() => {
107
+ }).catch(err => {
108
+ console.error(err);
109
+ });
110
+
111
+ process.on('SIGINT', () => {
112
+ logger.debug('received SIGINT signal. exiting.')
113
+ process.exit(0)
114
+ })
115
+
116
+ process.on('SIGTERM', () => {
117
+ logger.debug('received SIGTERM signal. exiting.')
118
+ process.exit(0)
119
+ })
120
+
121
+ process.on('unhandledRejection', (reason: Error, p: Promise<any>) => {
122
+ logger.error('unhandled rejection: promise:', p, 'reason:', reason)
123
+ })
@@ -0,0 +1,157 @@
1
+ import chalk from 'chalk'
2
+ import { DateTime } from 'luxon'
3
+
4
+ export type Options = {
5
+ tag?: string
6
+ prefix?: string
7
+ color: string
8
+ }
9
+
10
+ type AdditionalDataLabel = {
11
+ id?: string
12
+ root?: string
13
+ }
14
+
15
+ export enum LogLevels {
16
+ Critical,
17
+ Error,
18
+ Warn,
19
+ Info,
20
+ Log,
21
+ Debug
22
+ }
23
+
24
+ const logLevelColors: { [key: string]: string } = {
25
+ [LogLevels.Critical]: 'red',
26
+ [LogLevels.Error]: 'red',
27
+ [LogLevels.Warn]: 'yellow',
28
+ [LogLevels.Info]: 'blue',
29
+ [LogLevels.Log]: 'white',
30
+ [LogLevels.Debug]: 'white'
31
+ }
32
+
33
+ let logLevel = LogLevels.Debug
34
+ export const setLogLevel = (_logLevel: LogLevels | string) => {
35
+ if (typeof _logLevel === 'string') {
36
+ const mapping: { [key: string]: number } = {
37
+ error: LogLevels.Error,
38
+ warn: LogLevels.Warn,
39
+ info: LogLevels.Info,
40
+ debug: LogLevels.Debug
41
+ }
42
+ _logLevel = mapping[_logLevel]
43
+ }
44
+ logLevel = _logLevel
45
+ }
46
+
47
+ class Logger {
48
+ private readonly tag: string = ''
49
+ private readonly prefix: string = ''
50
+ private readonly options: any = {}
51
+ enabled: boolean = true
52
+
53
+ setEnabled(enabled: boolean) {
54
+ this.enabled = enabled
55
+ }
56
+
57
+ constructor(
58
+ tag: Partial<Options> | string = '',
59
+ opts: Partial<Options> = {
60
+ color: 'white'
61
+ }
62
+ ) {
63
+ if (tag instanceof Object) {
64
+ opts = tag
65
+ tag = opts.tag ?? ''
66
+ }
67
+ if (opts.prefix) {
68
+ this.prefix = `<${opts.prefix}>`
69
+ }
70
+ if (tag) {
71
+ if (opts.color) {
72
+ //@ts-ignore
73
+ this.tag = chalk[opts.color](`[${tag}]`)
74
+ } else {
75
+ this.tag = `[${tag}]`
76
+ }
77
+ }
78
+ if (process.env.DISABLE_LOGGER) {
79
+ this.enabled = false
80
+ }
81
+ this.options = opts
82
+ }
83
+
84
+ create(additionalDataLabel: AdditionalDataLabel): Logger {
85
+ let label: string
86
+ if (additionalDataLabel.id) {
87
+ label = `id: ${additionalDataLabel.id}`
88
+ } else {
89
+ label = `root: ${additionalDataLabel.root}`
90
+ }
91
+
92
+ return new Logger(
93
+ this.options.tag,
94
+ Object.assign({}, this.options, {
95
+ prefix: `${this.options.prefix ? `${this.options.prefix} ` : ''}${label}`
96
+ })
97
+ )
98
+ }
99
+
100
+ get timestamp(): string {
101
+ return DateTime.now().toISO()
102
+ }
103
+
104
+ headers(logLevelEnum: LogLevels): string[] {
105
+ const keys = Object.keys(LogLevels)
106
+ const logLevelName = keys[logLevelEnum + keys.length / 2].toUpperCase()
107
+ //@ts-ignore
108
+ const coloredLogLevel = chalk[logLevelColors[logLevelEnum]](
109
+ logLevelName.padEnd(5, ' ')
110
+ )
111
+ return [this.timestamp, coloredLogLevel, this.tag, this.prefix]
112
+ }
113
+
114
+ critical = (...input: any[]) => {
115
+ if (!this.enabled) return
116
+ console.error(...this.headers(LogLevels.Critical), ...input)
117
+ }
118
+
119
+ debug = (...input: any[]) => {
120
+ if (!this.enabled) return
121
+ if (logLevel !== LogLevels.Debug) {
122
+ return
123
+ }
124
+ console.debug(...this.headers(LogLevels.Debug), ...input)
125
+ }
126
+
127
+ error = (...input: any[]) => {
128
+ if (!this.enabled) return
129
+ console.error(...this.headers(LogLevels.Error), ...input)
130
+ }
131
+
132
+ info = (...input: any[]) => {
133
+ if (!this.enabled) return
134
+ if (!(logLevel === LogLevels.Debug || logLevel === LogLevels.Info)) {
135
+ return
136
+ }
137
+ console.info(...this.headers(LogLevels.Info), ...input)
138
+ }
139
+
140
+ log = (...input: any[]) => {
141
+ if (!this.enabled) return
142
+ if (logLevel < LogLevels.Info) {
143
+ return
144
+ }
145
+ console.log(...this.headers(LogLevels.Log), ...input)
146
+ }
147
+
148
+ warn = (...input: any[]) => {
149
+ if (!this.enabled) return
150
+ if (logLevel < LogLevels.Warn) {
151
+ return
152
+ }
153
+ console.warn(...this.headers(LogLevels.Warn), ...input)
154
+ }
155
+ }
156
+
157
+ export default Logger
@@ -0,0 +1,3 @@
1
+ export * from './peer';
2
+ export * from './pool';
3
+ export * from './protocol';
@@ -0,0 +1,120 @@
1
+ import Libp2p from "libp2p";
2
+ import TCP from "libp2p-tcp";
3
+ //@ts-ignore
4
+ import WS from "libp2p-websockets";
5
+ //@ts-ignore
6
+ import Mplex from "libp2p-mplex";
7
+ import { NOISE } from "libp2p-noise";
8
+ import Logger from "@/logger";
9
+ import Bootstrap from "libp2p-bootstrap";
10
+ import wait from "waait";
11
+ import Gossipsub from "@achingbrain/libp2p-gossipsub";
12
+ //@ts-ignore
13
+ import MulticastDNS from "libp2p-mdns";
14
+ //@ts-ignore
15
+ import KadDHT from "libp2p-kad-dht";
16
+ //@ts-ignore
17
+ import PubsubPeerDiscovery from "libp2p-pubsub-peer-discovery";
18
+ import { protocol } from "@/net";
19
+ import config from "@/config";
20
+
21
+ const logger = new Logger("Peer");
22
+
23
+ let node: Libp2p;
24
+
25
+ // Known peers addresses
26
+ const bootstrapMultiaddrs = [
27
+ '/ip4/164.92.249.133/tcp/15001/ws/p2p/QmVGmkqRn8FEw4PQz83L8Mun24ALxXB2fEyS5aEfT2gGqz',
28
+ '/ip4/164.92.249.133/tcp/15000/p2p/QmVGmkqRn8FEw4PQz83L8Mun24ALxXB2fEyS5aEfT2gGqz',
29
+ ];
30
+
31
+ interface IPeerOptions {
32
+ }
33
+
34
+ export const startPeer = async ({ }: IPeerOptions) => {
35
+ node = await Libp2p.create({
36
+ addresses: {
37
+ listen: ["/ip4/0.0.0.0/tcp/0", "/ip4/0.0.0.0/tcp/0/ws"],
38
+ },
39
+ modules: {
40
+ transport: [TCP, WS],
41
+ streamMuxer: [Mplex],
42
+ connEncryption: [NOISE],
43
+ peerDiscovery: [MulticastDNS, Bootstrap, PubsubPeerDiscovery],
44
+ pubsub: Gossipsub,
45
+ dht: KadDHT,
46
+ },
47
+
48
+ config: {
49
+ dht: {
50
+ enabled: true,
51
+ },
52
+ peerDiscovery: {
53
+ autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minConnections)
54
+ // The `tag` property will be searched when creating the instance of your Peer Discovery service.
55
+ // The associated object, will be passed to the service when it is instantiated.
56
+ [Bootstrap.tag]: {
57
+ enabled: true,
58
+ interval: 60e3,
59
+ list: bootstrapMultiaddrs, // provide array of multiaddrs
60
+ },
61
+ [MulticastDNS.tag]: {
62
+ interval: 20e3,
63
+ enabled: true,
64
+ },
65
+ [PubsubPeerDiscovery.tag]: {
66
+ interval: 1000,
67
+ enabled: true,
68
+ },
69
+ },
70
+ relay: {
71
+ enabled: true,
72
+ autoRelay: {
73
+ enabled: true,
74
+ maxListeners: 10,
75
+ },
76
+ },
77
+ },
78
+ peerStore: {
79
+ persistence: true,
80
+ },
81
+ });
82
+
83
+ logger.info("Peer ID:", node.peerId.toB58String());
84
+
85
+ await node.start();
86
+
87
+ protocol.start({
88
+ libp2p: node
89
+ })
90
+
91
+ node.on("peer:discovery", (peer) => {
92
+ // logger.log(`Discovered peer ${peer}`)
93
+ }); // peer disc.
94
+
95
+ node.connectionManager.on("peer:connect", (connection) => {
96
+ // logger.log(`Connected to ${connection.remotePeer.toB58String()}`)
97
+ });
98
+
99
+ logger.log("Peer discovery started");
100
+
101
+ await wait(1000);
102
+
103
+
104
+ setInterval(() => protocol.sendPeerInfo({
105
+ publicAddress: config.wallet.address,
106
+ }), 5000)
107
+ };
108
+
109
+ export const stopPeer = async () => {
110
+ if (node) {
111
+ // stop libp2p
112
+ await node.stop();
113
+ logger.log("libp2p has stopped");
114
+ }
115
+
116
+ process.exit(0);
117
+ };
118
+
119
+ process.on("SIGTERM", stopPeer);
120
+ process.on("SIGINT", stopPeer);
@@ -0,0 +1,154 @@
1
+ import { Event } from "@/types";
2
+ import config from "@/config";
3
+ import Logger from "@/logger";
4
+ import { getAddress } from "ethers/lib/utils";
5
+
6
+
7
+ const logger = new Logger('PeerPool')
8
+
9
+ export interface IPeerInfo {
10
+ id: string;
11
+ publicAddress: string;
12
+ pooled: boolean;
13
+ updated: Date;
14
+ }
15
+
16
+ export class PeerPool {
17
+ public PEERS_CLEANUP_TIME_LIMIT = 1
18
+ private pool: Map<string, IPeerInfo>
19
+
20
+
21
+ constructor() {
22
+ this.pool = new Map<string, IPeerInfo>()
23
+
24
+ config.events.on(Event.PEER_CONNECTED, (peer) => {
25
+ this.connected(peer)
26
+ })
27
+
28
+ config.events.on(Event.PEER_DISCONNECTED, (peer) => {
29
+ this.disconnected(peer)
30
+ })
31
+ }
32
+
33
+
34
+ /**
35
+ * Connected peers
36
+ */
37
+ get peers(): IPeerInfo[] {
38
+ const connectedPeers: IPeerInfo[] = Array.from(this.pool.values())
39
+ return connectedPeers
40
+ }
41
+
42
+ /**
43
+ * Number of peers in pool
44
+ */
45
+ get size() {
46
+ return this.peers.length
47
+ }
48
+
49
+ /**
50
+ * Return true if pool contains the specified peer
51
+ * @param peer peer object or id
52
+ */
53
+ contains(peer: IPeerInfo | string): boolean {
54
+ if (typeof peer !== 'string') {
55
+ peer = peer.id
56
+ }
57
+ return !!this.pool.get(peer)
58
+ }
59
+
60
+ /**
61
+ * Handler for peer connections
62
+ * @param peer peer
63
+ */
64
+ private connected(peer: IPeerInfo) {
65
+ if (this.size >= config.maxPeers) return
66
+ this.add(peer)
67
+ }
68
+
69
+ /**
70
+ * Handler for peer disconnections
71
+ * @param peer peer
72
+ */
73
+ private disconnected(peer: IPeerInfo) {
74
+ this.remove(peer)
75
+ }
76
+
77
+ /**
78
+ * Add peer to pool
79
+ * @param peer peer
80
+ * @emits {@link Event.POOL_PEER_ADDED}
81
+ */
82
+ add(peer?: IPeerInfo) {
83
+ if (peer && peer.id) {
84
+ const newPeer = !this.pool.get(peer.id);
85
+ this.pool.set(peer.id, peer)
86
+ peer.pooled = true
87
+
88
+ if (newPeer) {
89
+ config.events.emit(Event.POOL_PEER_ADDED, peer)
90
+ logger.info(`Peer ${peer.id} with address ${peer.publicAddress} added to pool`)
91
+ }
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Remove peer from pool
97
+ * @param peer peer
98
+ * @emits {@link Event.POOL_PEER_REMOVED}
99
+ */
100
+ remove(peer?: IPeerInfo) {
101
+ if (peer && peer.id) {
102
+ if (this.pool.delete(peer.id)) {
103
+ peer.pooled = false
104
+ config.events.emit(Event.POOL_PEER_REMOVED, peer)
105
+ logger.info(`Peer ${peer.id} with address ${peer.publicAddress} removed from pool`)
106
+ }
107
+ }
108
+ }
109
+
110
+ get activePeers() {
111
+ this.cleanup()
112
+
113
+ return this.peers.filter((p) => {
114
+ if (!p.pooled) return false;
115
+
116
+ const now = new Date()
117
+
118
+ return (now.getTime() - p.updated.getTime()) < 1000 * 10 // 10 seconds
119
+ })
120
+ }
121
+
122
+ get activePeerIds() {
123
+ return this.activePeers.map((p) => p.id)
124
+ }
125
+
126
+
127
+ getPeer(id: string){
128
+ return this.pool.get(id);
129
+ }
130
+
131
+ isLeadNode(id: string) {
132
+ const peer = this.pool.get(id);
133
+
134
+ if (!peer) {
135
+ return false;
136
+ }
137
+
138
+ return getAddress(peer.publicAddress) === getAddress(config.leadNodeAddress)
139
+ }
140
+
141
+
142
+ cleanup() {
143
+ // let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
144
+
145
+ // this.peers.forEach((peerInfo) => {
146
+ // if (peerInfo.updated.getTime() < compDate) {
147
+ // console.log(`Peer ${peerInfo.id} idle for ${this.PEERS_CLEANUP_TIME_LIMIT} minutes`)
148
+ // this.remove(peerInfo)
149
+ // }
150
+ // })
151
+ }
152
+ }
153
+
154
+ export const peerPool = new PeerPool()