UNPKG

chia-network-scanner

Version:
181 lines (148 loc) 5.92 kB
import { log } from './log'; import { MessageChannel } from './MessageChannel'; import { Peer } from './peer'; import { decodeMessage, encodeMessage, ProtocolMessageTypes } from './encoder'; interface PeerConnectionOptions { networkId: string; protocolVersion: string; softwareVersion: string; nodeType: number; hostname: string; port: number; connectionTimeout: number; cert: Buffer; key: Buffer; } class PeerConnection { private readonly messageChannel: MessageChannel; private readonly messageHandlers: Map<number, Function> = new Map(); public constructor({ networkId, protocolVersion, softwareVersion, nodeType, hostname, port, connectionTimeout, cert, key }: PeerConnectionOptions) { this.messageChannel = new MessageChannel({ networkId, protocolVersion, softwareVersion, nodeType, hostname, port, connectionTimeout, onMessage: data => this.onMessage(data), cert, key }); } public async connect(): Promise<void> { return new Promise(async(resolve, reject) => { const timeout = setTimeout(() => reject(new Error('Failed to connect to peer within 2 seconds')), 2000); try { await this.messageChannel.connect(); // Connected within timeout clearTimeout(timeout); resolve(); } catch (err) { clearTimeout(timeout); reject(err); } }); } /** * Chia application level handshake required before using the peer protocol. */ public async handshake(): Promise<this> { const { hostname, port, connectionTimeout, networkId, protocolVersion, softwareVersion, nodeType } = this.messageChannel; return new Promise(async(resolve, reject) => { const timeout = setTimeout(() => reject(new Error(`${hostname}:${port} did not respond to handshake within ${(connectionTimeout / 1000).toFixed(2)} seconds. Bailing.`)), connectionTimeout); // Handle handshake response messages const handshakeResponse = [ this.expectMessage(ProtocolMessageTypes.handshake), this.expectMessage(ProtocolMessageTypes.handshake_ack) ]; // Initiate handshake this.sendMessage(ProtocolMessageTypes.handshake, { network_id: networkId, protocol_version: protocolVersion, software_version: softwareVersion, server_port: port, node_type: nodeType }); try { // Wait for handshake response await Promise.race(handshakeResponse); } catch (err) { return reject(err); } // Handshake completed within timeout clearTimeout(timeout); // We can now use the peer protocol resolve(this); }); } public sendMessage(messageType: number, data: any) { const message = encodeMessage(messageType, data); log.info(`Sending ${messageType} message`); this.messageChannel.sendMessage(message); } /** * Get the peers of this peer. */ public getPeers(): Promise<Peer[]> { const { hostname, port, connectionTimeout } = this.messageChannel; return new Promise((resolve, reject) => { const timeout = setTimeout(() => reject(new Error(`${hostname}:${port} did not respond after ${(connectionTimeout / 1000).toFixed(2)} seconds. Bailing.`)), connectionTimeout); this.addMessageHandler(ProtocolMessageTypes.respond_peers, (respondPeers: any) => { clearTimeout(timeout); resolve(respondPeers.peer_list); }); this.sendMessage(ProtocolMessageTypes.request_peers, {}); }); } public close(): void { return this.messageChannel.close(); } private addMessageHandler(messageType: number, handler: Function) { log.debug(`Adding message handler for ${messageType} messages`); this.messageHandlers.set(messageType, handler); } private onMessage(data: Buffer): void { try { const messageType = data[0]; log.info(`Received message of type ${messageType}`); const message = decodeMessage(data); log.info(`Decoded message to ${JSON.stringify(message)}`) const handler = this.messageHandlers.get(messageType); if (handler) { return handler(message); } log.warn(`No handler for ${messageType} message. Discarding it.`); } catch (err) { // Anybody could send any old rubbish down the wire and we don't want that to crash our process log.error(err, 'Error handling inbound message'); log.warn(`Hex of message that could not be decoded: ${data.toString('hex')}`); } } /** * Expects a message of a messageType to be received within a timeout. * * @param messageType expected */ private expectMessage(messageType: number): Promise<void> { const { hostname, port, connectionTimeout } = this.messageChannel; return new Promise((resolve, reject) => { const timeout = setTimeout(() => reject(new Error(`${hostname}:${port} did not receive ${messageType} message within ${(connectionTimeout / 1000).toFixed(2)} seconds. Bailing.`)), connectionTimeout); this.addMessageHandler(messageType, () => { clearTimeout(timeout); resolve(); }); }); } } export { PeerConnection };