@instadapp/interop-x 0.0.0-dev.0ee2ee3

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 (44) hide show
  1. package/.env.example +1 -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/config/index.js +17 -0
  6. package/dist/constants/addresses.js +13 -0
  7. package/dist/constants/index.js +17 -0
  8. package/dist/db/index.js +17 -0
  9. package/dist/db/models/execution.js +38 -0
  10. package/dist/db/models/index.js +17 -0
  11. package/dist/db/sequelize.js +22 -0
  12. package/dist/index.js +34 -0
  13. package/dist/logger/index.js +138 -0
  14. package/dist/net/index.js +19 -0
  15. package/dist/net/peer/index.js +100 -0
  16. package/dist/net/pool/index.js +107 -0
  17. package/dist/net/protocol/dial/BaseDialProtocol.js +106 -0
  18. package/dist/net/protocol/dial/SignatureDialProtocol.js +48 -0
  19. package/dist/net/protocol/index.js +92 -0
  20. package/dist/tasks/BaseTask.js +61 -0
  21. package/dist/tasks/index.js +19 -0
  22. package/dist/types.js +21 -0
  23. package/dist/utils/index.js +89 -0
  24. package/package.json +59 -0
  25. package/src/config/index.ts +24 -0
  26. package/src/constants/addresses.ts +10 -0
  27. package/src/constants/index.ts +1 -0
  28. package/src/db/index.ts +1 -0
  29. package/src/db/models/execution.ts +57 -0
  30. package/src/db/models/index.ts +1 -0
  31. package/src/db/sequelize.ts +21 -0
  32. package/src/index.ts +39 -0
  33. package/src/logger/index.ts +157 -0
  34. package/src/net/index.ts +3 -0
  35. package/src/net/peer/index.ts +113 -0
  36. package/src/net/pool/index.ts +128 -0
  37. package/src/net/protocol/dial/BaseDialProtocol.ts +104 -0
  38. package/src/net/protocol/dial/SignatureDialProtocol.ts +62 -0
  39. package/src/net/protocol/index.ts +138 -0
  40. package/src/tasks/BaseTask.ts +76 -0
  41. package/src/tasks/index.ts +18 -0
  42. package/src/types.ts +39 -0
  43. package/src/utils/index.ts +116 -0
  44. package/tsconfig.json +27 -0
@@ -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,113 @@
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 "../protocol";
19
+
20
+ const logger = new Logger("Peer");
21
+
22
+ let node: Libp2p;
23
+
24
+ // Known peers addresses
25
+ const bootstrapMultiaddrs = [
26
+ '/ip4/164.92.249.133/tcp/15001/ws/p2p/QmVGmkqRn8FEw4PQz83L8Mun24ALxXB2fEyS5aEfT2gGqz',
27
+ '/ip4/164.92.249.133/tcp/15000/p2p/QmVGmkqRn8FEw4PQz83L8Mun24ALxXB2fEyS5aEfT2gGqz',
28
+ ];
29
+
30
+ interface IPeerOptions {
31
+ }
32
+
33
+ export const startPeer = async ({ }: IPeerOptions) => {
34
+ node = await Libp2p.create({
35
+ addresses: {
36
+ listen: ["/ip4/0.0.0.0/tcp/0", "/ip4/0.0.0.0/tcp/0/ws"],
37
+ },
38
+ modules: {
39
+ transport: [TCP, WS],
40
+ streamMuxer: [Mplex],
41
+ connEncryption: [NOISE],
42
+ peerDiscovery: [MulticastDNS, Bootstrap, PubsubPeerDiscovery],
43
+ pubsub: Gossipsub,
44
+ dht: KadDHT,
45
+ },
46
+
47
+ config: {
48
+ dht: {
49
+ enabled: true,
50
+ },
51
+ peerDiscovery: {
52
+ autoDial: true, // Auto connect to discovered peers (limited by ConnectionManager minConnections)
53
+ // The `tag` property will be searched when creating the instance of your Peer Discovery service.
54
+ // The associated object, will be passed to the service when it is instantiated.
55
+ [Bootstrap.tag]: {
56
+ enabled: true,
57
+ interval: 60e3,
58
+ list: bootstrapMultiaddrs, // provide array of multiaddrs
59
+ },
60
+ [MulticastDNS.tag]: {
61
+ interval: 20e3,
62
+ enabled: true,
63
+ },
64
+ [PubsubPeerDiscovery.tag]: {
65
+ interval: 1000,
66
+ enabled: true,
67
+ },
68
+ },
69
+ relay: {
70
+ enabled: true,
71
+ autoRelay: {
72
+ enabled: true,
73
+ maxListeners: 10,
74
+ },
75
+ },
76
+ },
77
+ peerStore: {
78
+ persistence: true,
79
+ },
80
+ });
81
+
82
+ logger.info("Peer ID:", node.peerId.toB58String());
83
+
84
+ await node.start();
85
+
86
+ protocol.start({
87
+ libp2p: node
88
+ })
89
+
90
+ node.on("peer:discovery", (peer) =>
91
+ logger.log(`Discovered peer ${peer}`)
92
+ ); // peer disc.
93
+ node.connectionManager.on("peer:connect", (connection) =>
94
+ logger.log(`Connected to ${connection.remotePeer.toB58String()}`)
95
+ );
96
+
97
+ logger.log("Peer discovery started");
98
+
99
+ await wait(1000);
100
+ };
101
+
102
+ export const stopPeer = async () => {
103
+ if (node) {
104
+ // stop libp2p
105
+ await node.stop();
106
+ logger.log("libp2p has stopped");
107
+ }
108
+
109
+ process.exit(0);
110
+ };
111
+
112
+ process.on("SIGTERM", stopPeer);
113
+ process.on("SIGINT", stopPeer);
@@ -0,0 +1,128 @@
1
+ import { Event } from "../../types";
2
+ import config from "../../config";
3
+
4
+ export interface IPeerInfo {
5
+ id: string;
6
+ publicAddress: string;
7
+ pooled: boolean;
8
+ updated: Date;
9
+ }
10
+
11
+ export class PeerPool {
12
+ public PEERS_CLEANUP_TIME_LIMIT = 1
13
+ private pool: Map<string, IPeerInfo>
14
+
15
+
16
+ constructor() {
17
+ this.pool = new Map<string, IPeerInfo>()
18
+
19
+ config.events.on(Event.PEER_CONNECTED, (peer) => {
20
+ this.connected(peer)
21
+ })
22
+
23
+ config.events.on(Event.PEER_DISCONNECTED, (peer) => {
24
+ this.disconnected(peer)
25
+ })
26
+ }
27
+
28
+
29
+ /**
30
+ * Connected peers
31
+ */
32
+ get peers(): IPeerInfo[] {
33
+ const connectedPeers: IPeerInfo[] = Array.from(this.pool.values())
34
+ return connectedPeers
35
+ }
36
+
37
+ /**
38
+ * Number of peers in pool
39
+ */
40
+ get size() {
41
+ return this.peers.length
42
+ }
43
+
44
+ /**
45
+ * Return true if pool contains the specified peer
46
+ * @param peer peer object or id
47
+ */
48
+ contains(peer: IPeerInfo | string): boolean {
49
+ if (typeof peer !== 'string') {
50
+ peer = peer.id
51
+ }
52
+ return !!this.pool.get(peer)
53
+ }
54
+
55
+ /**
56
+ * Handler for peer connections
57
+ * @param peer peer
58
+ */
59
+ private connected(peer: IPeerInfo) {
60
+ if (this.size >= config.maxPeers) return
61
+ this.add(peer)
62
+ }
63
+
64
+ /**
65
+ * Handler for peer disconnections
66
+ * @param peer peer
67
+ */
68
+ private disconnected(peer: IPeerInfo) {
69
+ this.remove(peer)
70
+ }
71
+
72
+ /**
73
+ * Add peer to pool
74
+ * @param peer peer
75
+ * @emits {@link Event.POOL_PEER_ADDED}
76
+ */
77
+ add(peer?: IPeerInfo) {
78
+ if (peer && peer.id && !this.pool.get(peer.id)) {
79
+ this.pool.set(peer.id, peer)
80
+ peer.pooled = true
81
+ config.events.emit(Event.POOL_PEER_ADDED, peer)
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Remove peer from pool
87
+ * @param peer peer
88
+ * @emits {@link Event.POOL_PEER_REMOVED}
89
+ */
90
+ remove(peer?: IPeerInfo) {
91
+ if (peer && peer.id) {
92
+ if (this.pool.delete(peer.id)) {
93
+ peer.pooled = false
94
+ config.events.emit(Event.POOL_PEER_REMOVED, peer)
95
+ }
96
+ }
97
+ }
98
+
99
+ get activePeers() {
100
+ this.cleanup()
101
+
102
+ return this.peers.filter((p) => {
103
+ if(!p.pooled) return false;
104
+
105
+ const now = new Date()
106
+
107
+ return (now.getTime() - p.updated.getTime()) < 1000 * 10 // 10 seconds
108
+ })
109
+ }
110
+
111
+ get activePeerIds() {
112
+ return this.activePeers.map((p) => p.id)
113
+ }
114
+
115
+
116
+ cleanup() {
117
+ let compDate = Date.now() - this.PEERS_CLEANUP_TIME_LIMIT * 60
118
+
119
+ this.peers.forEach((peerInfo) => {
120
+ if (peerInfo.updated.getTime() < compDate) {
121
+ console.log(`Peer ${peerInfo.id} idle for ${this.PEERS_CLEANUP_TIME_LIMIT} minutes`)
122
+ this.remove(peerInfo)
123
+ }
124
+ })
125
+ }
126
+ }
127
+
128
+ export const peerPool = new PeerPool()
@@ -0,0 +1,104 @@
1
+ import pipe from 'it-pipe';
2
+ import Libp2p from 'libp2p';
3
+ import PeerId from 'peer-id';
4
+ import { asyncCallWithTimeout } from '../../../utils';
5
+ import wait from 'waait';
6
+
7
+ export class BaseDialProtocol<TRequest extends any, TResponse extends any> {
8
+ protected libp2p: Libp2p;
9
+ protected protocol: string;
10
+ protected timeout = 20000;
11
+
12
+ constructor(libp2p: Libp2p, protocol: string) {
13
+ this.libp2p = libp2p
14
+ this.protocol = protocol
15
+
16
+ this.libp2p.handle(this.protocol, this.handle.bind(this))
17
+ }
18
+
19
+ encode(data: TRequest) {
20
+ return JSON.stringify(data)
21
+ }
22
+
23
+ encodeResponse(data: TResponse): String | Buffer {
24
+ return JSON.stringify(data)
25
+ }
26
+
27
+ decode(data: Buffer) {
28
+ return JSON.parse(data.toString())
29
+ }
30
+
31
+ decodeResponse(data: Buffer): any {
32
+ return JSON.parse(data.toString())
33
+ }
34
+
35
+ async response(data: TRequest): Promise<TResponse> {
36
+ return data as TResponse;
37
+ }
38
+
39
+ async handle({ connection, stream }) {
40
+ const instance = this;
41
+
42
+ try {
43
+ await pipe(stream, async function (source) {
44
+ for await (const message of source) {
45
+ const response = instance.encodeResponse(
46
+ await instance.response(
47
+ instance.decode(message)
48
+ )
49
+ )
50
+
51
+ await pipe(
52
+ [response],
53
+ stream
54
+ );
55
+ }
56
+ });
57
+ } catch (err) {
58
+ console.error(err);
59
+ }
60
+ }
61
+
62
+ async send(data: TRequest, peerId: string) {
63
+ return await asyncCallWithTimeout<TResponse>(new Promise(async (resolve, reject) => {
64
+ try {
65
+ let connection = this.libp2p.connectionManager.get(PeerId.createFromB58String(peerId))
66
+
67
+ if (!connection) {
68
+
69
+ await wait(5000)
70
+
71
+ connection = this.libp2p.connectionManager.get(PeerId.createFromB58String(peerId))
72
+
73
+
74
+ if (!connection) {
75
+
76
+ await wait(3000)
77
+
78
+ connection = this.libp2p.connectionManager.get(PeerId.createFromB58String(peerId))
79
+
80
+ if (!connection) {
81
+ throw new Error('No connection available')
82
+ }
83
+ }
84
+ }
85
+
86
+
87
+
88
+ const { stream } = await connection.newStream([this.protocol])
89
+
90
+ const instance = this;
91
+
92
+ pipe([this.encode(data)], stream, async function (source) {
93
+ for await (const message of source) {
94
+ resolve(instance.decodeResponse(message))
95
+ stream.close()
96
+ }
97
+ });
98
+ } catch (error) {
99
+ reject(error)
100
+ }
101
+ }), this.timeout)
102
+
103
+ }
104
+ }
@@ -0,0 +1,62 @@
1
+ import { signGnosisSafeTx } from "../../../utils";
2
+ import { BaseDialProtocol } from "./BaseDialProtocol";
3
+ import wait from "waait";
4
+ import { ChainId } from "../../../types";
5
+ import config from "config";
6
+ import { addresses } from "../../../constants";
7
+ import { Execution } from "db";
8
+
9
+ export interface ISignatureRequest {
10
+ type: string,
11
+ vnonce: string
12
+ safeTxGas: string
13
+ safeNonce: string
14
+ }
15
+ export interface ISignatureResponse {
16
+ signer: string,
17
+ data?: string | null,
18
+ error?: string | null,
19
+ }
20
+ export class SignatureDialProtocol extends BaseDialProtocol<ISignatureRequest, ISignatureResponse> {
21
+ protected timeout = 30000;
22
+
23
+ constructor(libp2p) {
24
+ super(libp2p, '/signatures')
25
+ }
26
+
27
+ async response(data: ISignatureRequest): Promise<ISignatureResponse> {
28
+ const signer = config.wallet;
29
+
30
+ let event: Execution | null;
31
+ let maxTimeout = 20000;
32
+
33
+ do {
34
+ event = await Execution.findOne({ where: { vnonce: data.vnonce.toString() } })
35
+
36
+ if (!event) {
37
+ await wait(1000);
38
+ maxTimeout -= 1000;
39
+ }
40
+ } while (!event && maxTimeout > 0)
41
+
42
+ if (!event) {
43
+ return {
44
+ signer: signer.address,
45
+ data: null,
46
+ error: 'Event not found'
47
+ };
48
+ }
49
+
50
+ const signedData = await signGnosisSafeTx({
51
+ to: addresses[event.chainId as ChainId].multisend,
52
+ data: 'TODO',
53
+ chainId: event.chainId as ChainId,
54
+ safeTxGas: data.safeTxGas,
55
+ }, { signer });
56
+
57
+ return {
58
+ signer: signer.address,
59
+ data: signedData,
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,138 @@
1
+ import { EventEmitter } from "stream";
2
+ import Libp2p from 'libp2p';
3
+ import { bufferToInt, rlp } from 'ethereumjs-util'
4
+ import { SignatureDialProtocol, ISignatureRequest, ISignatureResponse } from "./dial/SignatureDialProtocol";
5
+ import { IPeerInfo, peerPool } from "..";
6
+ import config from "config";
7
+ import { Event } from "types";
8
+
9
+ export interface ProtocolOptions {
10
+ /* Handshake timeout in ms (default: 8000) */
11
+ timeout?: number
12
+ }
13
+
14
+
15
+ export type Message = {
16
+ name: string
17
+ code: number
18
+ payload?: any
19
+ response?: number
20
+ encode: Function
21
+ decode: Function
22
+ }
23
+
24
+ interface BaseMessageEvent {
25
+ peerId: string
26
+ name: string
27
+ code: number
28
+ data: any
29
+ }
30
+
31
+
32
+ interface PeerInfoEvent extends BaseMessageEvent {
33
+ data: Omit<IPeerInfo, 'id' | 'updated' | 'idle' | 'pooled'>
34
+ }
35
+
36
+ declare interface Protocol {
37
+ on(event: 'PeerInfo', listener: (payload: PeerInfoEvent) => void): this;
38
+ on(event: string, listener: (payload: BaseMessageEvent) => void): this;
39
+ }
40
+
41
+ class Protocol extends EventEmitter {
42
+ private libp2p: Libp2p;
43
+ private topic: string;
44
+ private protocolMessages: Message[] = [
45
+ {
46
+ name: 'PeerInfo',
47
+ code: 0x09,
48
+ encode: (info: Pick<IPeerInfo, 'publicAddress'>) => [
49
+ Buffer.from(info.publicAddress),
50
+ ],
51
+ decode: ([publicAddress]: [Buffer]) => ({
52
+ publicAddress: publicAddress.toString(),
53
+ }),
54
+ },
55
+ ];
56
+ private signature: SignatureDialProtocol;
57
+
58
+
59
+ start({ libp2p, topic = null, }) {
60
+ this.libp2p = libp2p
61
+ this.topic = topic || 'protocol'
62
+
63
+ if (this.libp2p.isStarted()) this.init()
64
+
65
+ this.on('PeerInfo', (payload) => {
66
+
67
+ config.events.emit(Event.PEER_CONNECTED, {
68
+ id: payload.peerId,
69
+ publicAddress: payload.data.publicAddress,
70
+ pooled: false,
71
+ updated: new Date(),
72
+ })
73
+ })
74
+
75
+ this.signature = new SignatureDialProtocol(this.libp2p);
76
+ }
77
+
78
+
79
+ init() {
80
+ this.libp2p.pubsub.subscribe(this.topic)
81
+
82
+ this.libp2p.pubsub.on(this.topic, (message) => {
83
+ try {
84
+ const [codeBuf, payload]: any = rlp.decode(message.data);
85
+
86
+ const code = bufferToInt(codeBuf);
87
+
88
+ const protocolMessage = this.protocolMessages.find((m) => m.code === code);
89
+ if (!protocolMessage) {
90
+ return;
91
+ }
92
+ const decoded = protocolMessage.decode(payload);
93
+ this.emit(
94
+ protocolMessage.name,
95
+ {
96
+ peerId: message.from,
97
+ name: protocolMessage.name,
98
+ code: protocolMessage.code,
99
+ data: decoded
100
+ }
101
+ )
102
+
103
+ this.emit('all', {
104
+ peerId: message.from,
105
+ name: protocolMessage.name,
106
+ code: protocolMessage.code,
107
+ data: decoded,
108
+ })
109
+ } catch (err) {
110
+ console.error(err)
111
+ }
112
+ })
113
+ }
114
+
115
+
116
+ public sendPeerInfo(data: Omit<IPeerInfo, 'peerId' | 'updated' | 'idle'>) {
117
+ const message = this.protocolMessages.find((m) => m.name === 'PeerInfo')!
118
+
119
+ const encoded = rlp.encode([message.code, message.encode(data)]);
120
+
121
+ this.libp2p.pubsub.publish(this.topic, encoded)
122
+ }
123
+
124
+ async requestSignatures(data: ISignatureRequest, peerIds?: string[]) {
125
+ try {
126
+ peerIds = peerIds || peerPool.activePeerIds;
127
+ const promises = peerIds.map((peerId) => this.signature.send(data, peerId))
128
+ return (await Promise.allSettled(promises))
129
+ .map((p) => p.status === 'fulfilled' ? p.value : null)
130
+ .filter(Boolean) as ISignatureResponse[]
131
+ } catch (error) {
132
+ console.log(error);
133
+ return []
134
+ }
135
+ }
136
+ }
137
+
138
+ export const protocol = new Protocol();