Skip to content

Commit

Permalink
feat(p2p): add reputation events for errors (#634)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ImmanuelSegol authored and sangaman committed Nov 11, 2018
1 parent 320143e commit 8b1199f
Show file tree
Hide file tree
Showing 4 changed files with 15 additions and 24 deletions.
3 changes: 3 additions & 0 deletions lib/p2p/NodeList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
24 changes: 6 additions & 18 deletions lib/p2p/Peer.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;
Expand All @@ -53,7 +54,6 @@ class Peer extends EventEmitter {
private pingTimer?: NodeJS.Timer;
private responseMap: Map<string, PendingResponseEntry> = new Map();
private connectTime!: number;
private banScore = 0;
private lastRecv = 0;
private lastSend = 0;
private handshakeState?: HandshakeState;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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);
}
});
}
Expand Down
9 changes: 3 additions & 6 deletions lib/p2p/Pool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
});
}
Expand Down
3 changes: 3 additions & 0 deletions lib/types/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ export enum ReputationEvent {
PacketTimeout = 2,
SwapFailure = 3,
SwapSuccess = 4,
UnparseableMessage = 5,
InvalidMessage = 6,
UnknownPacketType = 7,
}

export enum SwapFailureReason {
Expand Down

0 comments on commit 8b1199f

Please sign in to comment.