-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathindex.ts
140 lines (106 loc) · 4.43 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
import fs from 'fs';
import * as async from 'async';
import { log } from './log';
import { NetworkScannerOptions, parseOptions } from './options';
import { Peer } from './peer';
import { PeerConnection } from './PeerConnection';
class ChiaNetworkScanner {
private readonly queue = async.queue(this.processPeer.bind(this), this.options.concurrency);
private peers = new Map<string, Peer>();
private scanInProgress = false;
public constructor(private readonly options: NetworkScannerOptions) {
this.options = parseOptions(options);
}
/**
* The scan is started from the full node provided in the options.
*/
public async scan(): Promise<Peer[]> {
if (this.scanInProgress) {
throw new Error('Only one scan be be performed at a time');
}
log.info('Starting scan of Chia Network');
// Prevents caller from executing async scan more than once at a time
this.scanInProgress = true;
const { startNodes } = this.options;
// Reset peers from any previous scans
this.peers = new Map<string, Peer>();
// The network scan is started from passed in start nodes
startNodes.forEach(node => {
this.queue.push(new Peer({
hostname: node.hostname,
port: node.port,
timestamp: Math.floor(Date.now() / 1000)
}), (e) => { console.log(`fin ${node.hostname}:${node.port}`, e); });
});
await this.queue.drain();
// Async network scan has finished, another could now be performed
this.scanInProgress = false;
return [
...this.peers.values()
];
}
/**
* Peers are added to the async queue and a graph traversal of the network is performed.
*
* The concurrency parameter passed in the constructor specifies how many of these are executed concurrently via the event loop.
*/
private async processPeer(proposedPeer: Peer): Promise<void> {
const peerLogger = log.child({
...proposedPeer
});
const ipv6 = proposedPeer.hostname.includes(':');
// Only scan ipv4 because ipv6 nodes can appear with both their ipv4 address and their ipv6 address
if (ipv6) {
return;
}
const { connectionTimeout, network, peer: peerOptions, certPath, keyPath } = this.options;
const peerHash = proposedPeer.hash();
if (!this.peers.has(peerHash)) {
peerLogger.debug('First time seeing peer');
this.peers.set(peerHash, proposedPeer);
}
// Ensures we get the visited value of peer from previous traversal
const peer = this.peers.get(peerHash) as Peer;
// We only visit each peer once
if (peer.visited) {
peerLogger.debug('Skipping already visited peer');
if (proposedPeer.timestamp > peer.timestamp) {
peerLogger.debug(`Updating visited peer to more recent timestamp`);
proposedPeer.visit();
this.peers.set(peerHash, proposedPeer);
}
return;
}
peerLogger.info('Visiting peer');
// Set to visited immediately to prevent async processing of the same peer
peer.visit();
// Opens a websocket connection with the peer we are processing
const peerConnection = new PeerConnection({
networkId: network.networkId,
protocolVersion: network.protocolVersion,
softwareVersion: network.softwareVersion,
nodeType: peerOptions.nodeType,
hostname: peer.hostname,
port: peer.port,
connectionTimeout,
cert: fs.readFileSync(certPath),
key: fs.readFileSync(keyPath)
});
peerLogger.info('Establishing websocket connection');
try {
// Establish websocket connection
await peerConnection.connect();
peerLogger.info('Websocket connection established');
// Performs application level handshake with peer
await peerConnection.handshake();
const peers = await peerConnection.getPeers();
// Enqueue peers of peer for async processing
this.queue.push(peers);
// Close websocket connection
await peerConnection.close();
} catch (err) {
log.error(err);
}
}
}
export { ChiaNetworkScanner };