From 8b1199fd28f933c279d4b2837a595cf99f254750 Mon Sep 17 00:00:00 2001 From: Immanuel <3ditds@gmail.com> Date: Mon, 12 Nov 2018 01:46:56 +0200 Subject: [PATCH] feat(p2p): add reputation events for errors (#634) This adds three new reputation event types for transmission and communication errors with peers. Peers that send invalid or unparseable packets will be penalized. It also refactors some of the code around banning peers and tracking reputation scores to centralize it in Pool.ts Closes #566. --- lib/p2p/NodeList.ts | 3 +++ lib/p2p/Peer.ts | 24 ++++++------------------ lib/p2p/Pool.ts | 9 +++------ lib/types/enums.ts | 3 +++ 4 files changed, 15 insertions(+), 24 deletions(-) diff --git a/lib/p2p/NodeList.ts b/lib/p2p/NodeList.ts index 0cf494ef6..2cf119669 100644 --- a/lib/p2p/NodeList.ts +++ b/lib/p2p/NodeList.ts @@ -12,6 +12,9 @@ const reputationEventWeight = { [ReputationEvent.PacketTimeout]: -1, [ReputationEvent.SwapFailure]: -10, [ReputationEvent.SwapSuccess]: 1, + [ReputationEvent.InvalidMessage]: -10, + [ReputationEvent.UnknownPacketType]: -20, + [ReputationEvent.UnparseableMessage]: -10, }; // TODO: inform node about getting banned diff --git a/lib/p2p/Peer.ts b/lib/p2p/Peer.ts index 8bf3c0008..8be9ef19d 100644 --- a/lib/p2p/Peer.ts +++ b/lib/p2p/Peer.ts @@ -1,6 +1,7 @@ import assert from 'assert'; import net, { Socket } from 'net'; import { EventEmitter } from 'events'; +import { ReputationEvent } from '../types/enums'; import Parser, { ParserError, ParserErrorType } from './Parser'; import * as packets from './packets/types'; import Logger from '../Logger'; @@ -29,9 +30,9 @@ interface Peer { on(event: 'handshake', listener: () => void): this; once(event: 'open', listener: () => void): this; once(event: 'close', listener: () => void): this; - once(event: 'ban', listener: () => void): this; + once(event: 'reputation', listener: (event: ReputationEvent) => void): this; emit(event: 'connect'): boolean; - emit(event: 'ban'): boolean; + emit(event: 'reputation', reputationEvent: ReputationEvent): boolean; emit(event: 'open'): boolean; emit(event: 'close'): boolean; emit(event: 'error', err: Error): boolean; @@ -53,7 +54,6 @@ class Peer extends EventEmitter { private pingTimer?: NodeJS.Timer; private responseMap: Map = new Map(); private connectTime!: number; - private banScore = 0; private lastRecv = 0; private lastSend = 0; private handshakeState?: HandshakeState; @@ -256,18 +256,6 @@ class Peer extends EventEmitter { } } - private increaseBan = (score: number): boolean => { - this.banScore += score; - - if (this.banScore >= 100) { // TODO: make configurable - this.logger.debug(`Ban threshold exceeded (${this.nodePubKey})`); - this.emit('ban'); - return true; - } - - return false; - } - /** * Ensure we are connected (for inbound connections) or listen for the `connect` socket event (for outbound connections) * and set the [[connectTime]] timestamp. If an outbound connection attempt errors or times out, throw an error. @@ -487,15 +475,15 @@ class Peer extends EventEmitter { switch (err.type) { case ParserErrorType.UnparseableMessage: this.logger.warn(`Unparsable peer message: ${err.payload}`); - this.increaseBan(10); + this.emit('reputation', ReputationEvent.UnparseableMessage); break; case ParserErrorType.InvalidMessage: this.logger.warn(`Invalid peer message: ${err.payload}`); - this.increaseBan(10); + this.emit('reputation', ReputationEvent.InvalidMessage); break; case ParserErrorType.UnknownPacketType: this.logger.warn(`Unknown peer message type: ${err.payload}`); - this.increaseBan(20); + this.emit('reputation', ReputationEvent.UnknownPacketType); } }); } diff --git a/lib/p2p/Pool.ts b/lib/p2p/Pool.ts index f80a83e21..ffb102da8 100644 --- a/lib/p2p/Pool.ts +++ b/lib/p2p/Pool.ts @@ -647,13 +647,10 @@ class Pool extends EventEmitter { this.emit('peer.close', peer); }); - peer.once('ban', async () => { - this.logger.debug(`Banning peer (${peer.nodePubKey})`); + peer.once('reputation', async (event) => { + this.logger.debug(`Peer (${peer.nodePubKey}), received reputation event: ${ReputationEvent[event]}`); if (peer.nodePubKey) { - await this.nodes.ban(peer.nodePubKey); - } - if (peer.connected) { - peer.close(); + await this.nodes.addReputationEvent(peer.nodePubKey, event); } }); } diff --git a/lib/types/enums.ts b/lib/types/enums.ts index a8890a2ad..c29c0af58 100644 --- a/lib/types/enums.ts +++ b/lib/types/enums.ts @@ -47,6 +47,9 @@ export enum ReputationEvent { PacketTimeout = 2, SwapFailure = 3, SwapSuccess = 4, + UnparseableMessage = 5, + InvalidMessage = 6, + UnknownPacketType = 7, } export enum SwapFailureReason {