UNPKG

xud

Version:
253 lines 12.3 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.reputationEventWeight = void 0; const events_1 = require("events"); const enums_1 = require("../constants/enums"); const addressUtils_1 = __importDefault(require("../utils/addressUtils")); const aliasUtils_1 = require("../utils/aliasUtils"); const errors_1 = __importDefault(require("./errors")); exports.reputationEventWeight = { [enums_1.ReputationEvent.ManualBan]: Number.NEGATIVE_INFINITY, [enums_1.ReputationEvent.ManualUnban]: 0, [enums_1.ReputationEvent.PacketTimeout]: -1, [enums_1.ReputationEvent.SwapFailure]: -10, [enums_1.ReputationEvent.SwapSuccess]: 1, [enums_1.ReputationEvent.WireProtocolErr]: -5, [enums_1.ReputationEvent.InvalidAuth]: -20, [enums_1.ReputationEvent.SwapTimeout]: -15, [enums_1.ReputationEvent.SwapMisbehavior]: -20, [enums_1.ReputationEvent.SwapAbuse]: Number.NEGATIVE_INFINITY, [enums_1.ReputationEvent.SwapDelay]: -25, }; /** Represents a list of nodes for managing network peers activity */ let NodeList = /** @class */ (() => { class NodeList extends events_1.EventEmitter { constructor(repository) { super(); this.repository = repository; /** A map of node pub keys to node instances. */ this.nodes = new Map(); /** A map of node ids to node instances. */ this.nodeIdMap = new Map(); /** A map of node pub keys to aliases. */ this.pubKeyToAliasMap = new Map(); /** A map of aliases to node pub keys. */ this.aliasToPubKeyMap = new Map(); /** * Check if a node with a given nodePubKey exists. */ this.has = (nodePubKey) => { return this.nodes.has(nodePubKey); }; this.forEach = (callback) => { this.nodes.forEach(callback); }; /** * Get the internal node id for a given nodePubKey. */ this.getNodeById = (nodeId) => { return this.nodeIdMap.get(nodeId); }; /** * Get the alias for a given nodePubKey. */ this.getAlias = (nodePubKey) => { return this.pubKeyToAliasMap.get(nodePubKey); }; this.getId = (nodePubKey) => { var _a; return (_a = this.nodes.get(nodePubKey)) === null || _a === void 0 ? void 0 : _a.id; }; this.get = (nodePubKey) => { return this.nodes.get(nodePubKey); }; this.getPubKeyForAlias = (alias) => { const nodePubKey = this.aliasToPubKeyMap.get(alias); if (!nodePubKey) { throw errors_1.default.ALIAS_NOT_FOUND(alias); } if (nodePubKey === 'CONFLICT') { throw errors_1.default.ALIAS_CONFLICT(alias); } return nodePubKey; }; /** * Ban a node by nodePubKey. * @returns true if the node was banned, false otherwise */ this.ban = (nodePubKey) => __awaiter(this, void 0, void 0, function* () { return yield this.addReputationEvent(nodePubKey, enums_1.ReputationEvent.ManualBan); }); /** * Remove ban from node by nodePubKey. * @returns true if ban was removed, false otherwise */ this.unBan = (nodePubKey) => __awaiter(this, void 0, void 0, function* () { return yield this.addReputationEvent(nodePubKey, enums_1.ReputationEvent.ManualUnban); }); this.isBanned = (nodePubKey) => { var _a; return ((_a = this.nodes.get(nodePubKey)) === null || _a === void 0 ? void 0 : _a.banned) || false; }; /** * Load this NodeList from the database. */ this.load = () => __awaiter(this, void 0, void 0, function* () { const nodes = yield this.repository.getNodes(); const reputationLoadPromises = []; nodes.forEach((node) => { this.addNode(node); const reputationLoadPromise = this.repository.getReputationEvents(node).then((events) => { node.reputationScore = 0; events.forEach(({ event }) => { NodeList.updateReputationScore(node, event); }); }); reputationLoadPromises.push(reputationLoadPromise); }); yield Promise.all(reputationLoadPromises); }); /** * Persists a node to the database and adds it to the node list. */ this.createNode = (nodeCreationAttributes) => __awaiter(this, void 0, void 0, function* () { const node = yield this.repository.addNodeIfNotExists(nodeCreationAttributes); if (node) { node.reputationScore = 0; this.addNode(node); } }); /** * Update a node's addresses. * @return true if the specified node exists and was updated, false otherwise */ this.updateAddresses = (nodePubKey, addresses = [], lastAddress) => __awaiter(this, void 0, void 0, function* () { const node = this.nodes.get(nodePubKey); if (node) { // avoid overriding the `lastConnected` field for existing matching addresses unless a new value was set node.addresses = addresses.map((newAddress) => { const oldAddress = node.addresses.find(address => addressUtils_1.default.areEqual(address, newAddress)); if (oldAddress && !newAddress.lastConnected) { return oldAddress; } else { return newAddress; } }); if (lastAddress) { node.lastAddress = lastAddress; } yield node.save(); return true; } return false; }); /** * Retrieves up to 10 of the most recent negative reputation events for a node * from the repository. * @param node the node for which to retrieve events * @param newEvent a reputation event that hasn't been added to the repository yet */ this.getNegativeReputationEvents = (node, newEvent) => __awaiter(this, void 0, void 0, function* () { const reputationEvents = yield this.repository.getReputationEvents(node); const negativeReputationEvents = reputationEvents .filter(e => exports.reputationEventWeight[e.event] < 0).slice(0, 9) .map(e => e.event); if (newEvent) { negativeReputationEvents.unshift(newEvent); } return negativeReputationEvents; }); /** * Add a reputation event to the node's history * @return true if the specified node exists and the event was added, false otherwise */ this.addReputationEvent = (nodePubKey, event) => __awaiter(this, void 0, void 0, function* () { const node = this.nodes.get(nodePubKey); if (node) { const promises = []; NodeList.updateReputationScore(node, event); if (node.reputationScore < NodeList.BAN_THRESHOLD && !node.banned) { promises.push(this.setBanStatus(node, true)); const negativeReputationEvents = yield this.getNegativeReputationEvents(node); this.emit('node.ban', nodePubKey, negativeReputationEvents); } else if (node.reputationScore >= NodeList.BAN_THRESHOLD && node.banned) { // If the reputationScore is not below the banThreshold but node.banned // is true that means that the node was unbanned promises.push(this.setBanStatus(node, false)); } promises.push(this.repository.addReputationEvent({ event, nodeId: node.id })); yield Promise.all(promises); return true; } return false; }); this.removeAddress = (nodePubKey, address) => __awaiter(this, void 0, void 0, function* () { const node = this.nodes.get(nodePubKey); if (node) { const index = node.addresses.findIndex(existingAddress => addressUtils_1.default.areEqual(address, existingAddress)); if (index > -1) { node.addresses = [...node.addresses.slice(0, index), ...node.addresses.slice(index + 1)]; yield node.save(); return true; } // if the lastAddress is removed, then re-assigning lastAddress with the latest connected advertised address if (node.lastAddress && addressUtils_1.default.areEqual(address, node.lastAddress)) { node.lastAddress = addressUtils_1.default.sortByLastConnected(node.addresses)[0]; } } return false; }); this.setBanStatus = (node, status) => { node.banned = status; return node.save(); }; this.addNode = (node) => { const { nodePubKey } = node; const alias = aliasUtils_1.pubKeyToAlias(nodePubKey); if (this.aliasToPubKeyMap.has(alias)) { this.aliasToPubKeyMap.set(alias, 'CONFLICT'); } else { this.aliasToPubKeyMap.set(alias, nodePubKey); } this.nodes.set(nodePubKey, node); this.nodeIdMap.set(node.id, node); this.pubKeyToAliasMap.set(nodePubKey, alias); }; } get count() { return this.nodes.size; } } NodeList.BAN_THRESHOLD = -50; NodeList.MAX_REPUTATION_SCORE = 100; NodeList.updateReputationScore = (node, event) => { if (event === enums_1.ReputationEvent.ManualUnban) { node.reputationScore = exports.reputationEventWeight[event]; } else { // events that carry a negative infinity weight will set the // reputationScore to negative infinity and result in a ban node.reputationScore += exports.reputationEventWeight[event]; // reputation score for a node cannot exceed the maximum node.reputationScore = Math.min(node.reputationScore, NodeList.MAX_REPUTATION_SCORE); } }; return NodeList; })(); exports.default = NodeList; //# sourceMappingURL=NodeList.js.map