Skip to content

Instantly share code, notes, and snippets.

@thEpisode
Last active October 8, 2018 18:51
Show Gist options
  • Save thEpisode/31adfd8d14576c0c3ac898a3bfd8fd30 to your computer and use it in GitHub Desktop.
Save thEpisode/31adfd8d14576c0c3ac898a3bfd8fd30 to your computer and use it in GitHub Desktop.
'use strict';
// Import dependencies
const crypto = require('crypto');
const bunyan = require('bunyan');
const levelup = require('levelup');
const encoding = require('encoding-down');
const leveldown = require('leveldown');
const kadence = require('@kadenceproject/kadence');
// Prepare required options
const storage = levelup(encoding(leveldown(`${__dirname}/db`)));
const logger = bunyan.createLogger({ name: 'kadence example' });
const transport = new kadence.HTTPTransport();
const identity = kadence.utils.getRandomKeyBuffer();
const contact = { hostname: 'localhost', port: 8080 };
// Construct a kademlia node interface; the returned `Node` object exposes:
// - router
// - rpc
// - storage
// - identity
const node = new kadence.KademliaNode({
transport,
storage,
logger,
identity,
contact
});
// Use rule "extensions" from plugins to add additional functionality.
// Plugins can also extend the `KademliaNode` object with additional methods
node.plugin(kadence.quasar());
// Use "global" rules for preprocessing *all* incoming messages
// This is useful for things like blacklisting certain nodes
node.use((request, response, next) => {
let [identityString] = request.contact
if ([/* identity blacklist */].includes(identityString)) {
return next(new Error('You have been blacklisted'));
}
next();
});
// Use existing "base" rules to add additional logic to the base kad routes
// This is useful for things like validating key/value pairs
node.use('STORE', (request, response, next) => {
let [key, val] = request.params;
let hash = crypto.createHash('rmd160').update(val).digest('hex');
// Ensure values are content-addressable
if (key !== hash) {
return next(new Error('Key must be the RMD-160 hash of value'));
}
next();
});
// Use "userland" (that's you!) rules to create your own protocols
node.use('ECHO', (request, response, next) => {
if ([/* some naughty words */].includes(request.params.message)) {
return next(new Error(
`Oh goodness, I dare not say "${request.params.message}"`
));
}
response.send(request.params);
});
// Define a global custom error handler rule, simply by including the `err`
// argument in the handler
node.use((err, request, response, next) => {
response.send({ error: err.message });
});
// Define error handlers for specific rules the same way, but including the
// rule name as the first argument
node.use('ECHO', (err, request, response, next) => {
response.send({
error: err.message.replace(request.params.message, '[redacted]')
});
});
// Extend the Node interface with your own plugins
// In many cases, you probably want parity with any userland message routes
// you have defined - in this case for the ECHO method
node.plugin(function(node) {
node.sendNeighborEcho = (text, callback) => {
const neighbor = [
...node.router.getClosestContactsToKey(node.identity).entries(),
].shift();
node.send('ECHO', {
message: text
}, neighbor, callback);
};
});
// When you are ready, start listening for messages and join the network
// The Node#listen method takes different arguments based on the transport
// adapter being used
node.listen(contact.port)
console.log(`Listening on ${contact.port}`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment