libp2p-gossipsub
Version:
A typescript implementation of gossipsub
105 lines (104 loc) • 4 kB
JavaScript
;
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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IWantTracer = void 0;
const constants_1 = require("./constants");
const utils_1 = require("./utils");
const pubsubErrors = require("libp2p-interfaces/src/pubsub/errors");
const { ERR_INVALID_SIGNATURE, ERR_MISSING_SIGNATURE } = pubsubErrors.codes;
/**
* IWantTracer is an internal tracer that tracks IWANT requests in order to penalize
* peers who don't follow up on IWANT requests after an IHAVE advertisement.
* The tracking of promises is probabilistic to avoid using too much memory.
*
* Note: Do not confuse these 'promises' with JS Promise objects.
* These 'promises' are merely expectations of a peer's behavior.
*/
class IWantTracer {
constructor() {
this.promises = new Map();
}
/**
* Track a promise to deliver a message from a list of msgIds we are requesting
* @param {string} p peer id
* @param {string[]} msgIds
* @returns {void}
*/
addPromise(p, msgIds) {
// pick msgId randomly from the list
const ix = Math.floor(Math.random() * msgIds.length);
const msgId = msgIds[ix];
const msgIdStr = utils_1.messageIdToString(msgId);
let peers = this.promises.get(msgIdStr);
if (!peers) {
peers = new Map();
this.promises.set(msgIdStr, peers);
}
if (!peers.has(p)) {
peers.set(p, Date.now() + constants_1.GossipsubIWantFollowupTime);
}
}
/**
* Returns the number of broken promises for each peer who didn't follow up on an IWANT request.
* @returns {Map<string, number>}
*/
getBrokenPromises() {
const now = Date.now();
const result = new Map();
this.promises.forEach((peers, msgId) => {
peers.forEach((expire, p) => {
// the promise has been broken
if (expire < now) {
// add 1 to result
result.set(p, (result.get(p) || 0) + 1);
// delete from tracked promises
peers.delete(p);
}
});
// clean up empty promises for a msgId
if (!peers.size) {
this.promises.delete(msgId);
}
});
return result;
}
/**
* Someone delivered a message, stop tracking promises for it
* @param {string} msgIdStr
* @returns {Promise<void>}
*/
deliverMessage(msgIdStr) {
return __awaiter(this, void 0, void 0, function* () {
this.promises.delete(msgIdStr);
});
}
/**
* A message got rejected, so we can stop tracking promises and let the score penalty apply from invalid message delivery,
* unless its an obviously invalid message.
* @param {string} msgIdStr
* @param {string} reason
* @returns {Promise<void>}
*/
rejectMessage(msgIdStr, reason) {
return __awaiter(this, void 0, void 0, function* () {
switch (reason) {
case ERR_INVALID_SIGNATURE:
case ERR_MISSING_SIGNATURE:
return;
}
this.promises.delete(msgIdStr);
});
}
clear() {
this.promises.clear();
}
}
exports.IWantTracer = IWantTracer;