'use strict'; const assert = require('node:assert'); const utils = require('./utils'); /** * Represent kademlia protocol handlers */ class KademliaRules { /** * Constructs a kademlia rules instance in the context of a * {@link KademliaNode} * @constructor * @param {KademliaNode} node */ constructor(node) { this.node = node; } /** * This RPC involves one node sending a PING message to another, which * presumably replies with a PONG. This has a two-fold effect: the * recipient of the PING must update the bucket corresponding to the * sender; and, if there is a reply, the sender must update the bucket * appropriate to the recipient. * @param {AbstractNode~request} request * @param {AbstractNode~response} response */ ping(request, response) { response.send([]); } /** * The sender of the STORE RPC provides a key and a block of data and * requires that the recipient store the data and make it available for * later retrieval by that key. * @param {AbstractNode~request} request * @param {AbstractNode~response} response * @param {AbstractNode~next} next */ store(request, response, next) { const [key, item] = request.params; try { assert(typeof item === 'object', 'Invalid storage item supplied'); assert(typeof item.timestamp === 'number', 'Invalid timestamp supplied'); assert(utils.keyStringIsValid(item.publisher), 'Invalid publisher identity supplied'); assert(utils.keyStringIsValid(key), 'Invalid item key supplied'); assert(typeof item.value !== 'undefined', 'Invalid item value supplied'); } catch (err) { return next(err); } this.node.storage.put(key, item, { valueEncoding: 'json' }, (err) => { if (err) { return next(err); } response.send([key, item]); // NB: Echo back what was stored }); } /** * The FIND_NODE RPC includes a 160-bit key. The recipient of the RPC returns * up to K contacts that it knows to be closest to the key. The recipient * must return K contacts if at all possible. It may only return fewer than K * if it is returning all of the contacts that it has knowledge of. * @param {AbstractNode~request} request * @param {AbstractNode~response} response * @param {AbstractNode~next} next */ findNode(request, response, next) { const [key] = request.params; if (!utils.keyStringIsValid(key)) { return next(new Error('Invalid lookup key supplied')); } response.send([...this.node.router.getClosestContactsToKey(key).entries()]); } /** * A FIND_VALUE RPC includes a B=160-bit key. If a corresponding value is * present on the recipient, the associated data is returned. Otherwise the * RPC is equivalent to a FIND_NODE and a set of K contacts is returned. * @param {AbstractNode~request} request * @param {AbstractNode~response} response * @param {AbstractNode~next} next */ findValue(request, response, next) { const [key] = request.params; if (!utils.keyStringIsValid(key)) { return next(new Error('Invalid lookup key supplied')); } this.node.storage.get(key, { valueEncoding: 'json' }, (err, item) => { if (err) { return this.findNode(request, response, next); } response.send(item); }); } } module.exports = KademliaRules;