diff --git a/.jsdoc.json b/.jsdoc.json new file mode 100644 index 0000000..1003590 --- /dev/null +++ b/.jsdoc.json @@ -0,0 +1,19 @@ +{ + "opts": { + "template": "node_modules/minami" + }, + "templates": { + "systemName": "Kadence", + "copyright": "Copyright 2019 Lily Anne Hall.", + "includeDate": false, + "navType": "vertical", + "linenums": false, + "collapseSymbols": false, + "inverseNav": false, + "outputSourceFiles": true, + "outputSourcePath": true, + "dateFormat": "", + "sort": "longname", + "syntaxTheme": "default" + } +} diff --git a/README.md b/README.md index 2afddb1..f99fc93 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,6 @@ -

Kadence: An Extensible, Hardened, and Secure Distributed Systems Framework

+# Kadence --- - -The Kadence Project is a complete implementation of the +Kadence is a complete implementation of the [Kademlia](http://www.scs.stanford.edu/%7Edm/home/papers/kpos.pdf) distributed hash table that aims to effectively mitigate *all vulnerabilities* described in the [S/Kademlia](https://gnunet.org/sites/default/files/SKademlia2007.pdf) diff --git a/docs/AbstractNode.html b/docs/AbstractNode.html new file mode 100644 index 0000000..8bcedb4 --- /dev/null +++ b/docs/AbstractNode.html @@ -0,0 +1,3577 @@ + + + + + + AbstractNode - Documentation + + + + + + + + + + + + + + + + + +
+ +

AbstractNode

+ + + + + + + +
+ +
+ +

+ AbstractNode +

+ +
Represents a network node
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new AbstractNode(options)

+ + + + + +
+ Contructs the primary interface for a kad node +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
options + + +object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
transport + + +AbstractNode~transport + + + + + + + + + + + See Transport Adapters + +
identity + + +buffer + + + + + + + + + + + See Contacts and Identities + +
contact + + +Bucket~contact + + + + + + + + + + + See Contacts and Identities + +
storage + + +AbstractNode~storage + + + + + + + + + + + See storage-adapters + +
logger + + +AbstractNode~logger + + + + + + <optional>
+ + + + + +
+ + +
messenger + + +Messenger + + + + + + <optional>
+ + + + + +
+ See Message Format + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

listen()

+ + + + + +
+ Passes through to the AbstractNode~transport +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

plugin(plugin)

+ + + + + +
+ Accepts an arbitrary function that receives this node as context +for mounting protocol handlers and extending the node with other +methods +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
plugin + + +function + + + + + Using and Authoring Plugins + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

receive(request, response)

+ + + + + +
+ Processes a the given arguments by sending them through the appropriate +middleware stack +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

send(method, params, contact, callbackopt) → {Promise.<(object|array), Error>}

+ + + + + +
+ Sends the [method, params] to the contact and executes the handler on +response or timeout +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
method + + +string + + + + + + + + + + + RPC method name + +
params + + +object +| + +array + + + + + + + + + + + RPC parameters + +
contact + + +Bucket~contact + + + + + + + + + + + Destination address information + +
callback + + +AbstractNode~sendCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<(object|array), Error> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

use(methodopt, middleware)

+ + + + + +
+ Mounts a message handler route for processing incoming RPC messages +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
method + + +string + + + + + + <optional>
+ + + + + +
+ RPC method name to route through + +
middleware + + +AbstractNode~middleware + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(inner) responseError(errorMessage, errorCodeopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
errorMessage + + +string + + + + + + + + + + + Text describing the error encountered + +
errorCode + + +number + + + + + + <optional>
+ + + + + +
+ Error code + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(inner) responseSend(results)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
results + + +array +| + +object + + + + + Result parameters to respond with + +
+ + + + + + + + + + + + + + + + +
+ + + + +

Type Definitions

+ + + +
+

logger

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
debug + + +function + + + + Passed string of debug information
info + + +function + + + + Passed string of general information
warn + + +function + + + + Passed string of warnings
error + + +function + + + + Passed string of error message
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
+ + + +
+ + + +

middleware(erroropt, request, response, next)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
error + + +error + + + + + + <optional>
+ + + + + +
+ Error object resulting from a middleware + +
request + + +AbstractNode~request + + + + + + + + + + + The incoming message object + +
response + + +AbstractNode~response + + + + + + + + + + + The outgoing response object + +
next + + +AbstractNode~next + + + + + + + + + + + Call to proceed to next middleware + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

next(error)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + Indicates to exit the middleware stack + +
+ + + + + + + + + + + + + + + + +
+ + +
+

request

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties
+ +
NameTypeDescription
contact + + +array + + + + Peer who sent this request
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
0 + + +string + + + + Peer's node identity
1 + + +object + + + + Peer's contact information (varies by plugin)
+ + + + + + + + + params + + + + + +array +| + +object + + + + + + + + + + Method parameters (varies by method) + + + + + + + + + method + + + + + +string + + + + + + + + + + Method name being called + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

response

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
send + + +AbstractNode~responseSend + + + +
error + + +AbstractNode~responseError + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+ + + +

sendCallback(error, result)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +null +| + +AbstractNode~sendError + + + + + + +
result + + +object +| + +array +| + +string +| + +number + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+

sendError

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Properties
+ +
NameTypeDescription
message + + +string + + + + Error description
type + + +string + + + + Error type
request + + +object + + + + Request the error is from
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
id + + +string + + + + Message id
params + + +array + + + + Parameters sent
target + + +Bucket~contact + + + + Contact message was for
method + + +string + + + + RPC method in message
+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

storage

+ + + + +
+ Implements a subset of the LevelUP interface +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
get + + +function + + + +
put + + +function + + + +
del + + +function + + + +
createReadStream + + +function + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
+ + + +
+

transport

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
read + + +function + + + + Returns raw message buffer if available
write + + +function + + + + Passed raw message buffer
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
+ + + + + +

Events

+ + + +
+ + + +

error

+ + + + + +
+ Error event fires when a critical failure has occurred; if no handler is +specified, then it will throw +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
+
Type:
+
    +
  • + +Error + + +
  • +
+
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

join

+ + + + + +
+ Join event is triggered when the routing table is no longer empty +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Bucket.html b/docs/Bucket.html new file mode 100644 index 0000000..133b43d --- /dev/null +++ b/docs/Bucket.html @@ -0,0 +1,1281 @@ + + + + + + Bucket - Documentation + + + + + + + + + + + + + + + + + +
+ +

Bucket

+ + + + + + + +
+ +
+ +

+ Bucket +

+ +
Represents a column of the routing table holding up to K contacts
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new Bucket()

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +

Members

+ + + +
+ + + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
head + + +object + + + + The contact at the bucket head
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

length

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
length + + +number + + + + The number of contacts in the bucket
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

tail

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
tail + + +object + + + + The contact at the bucket tail
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

getClosestToKey(key, countopt, exclusiveopt) → {array}

+ + + + + +
+ Returns an array of contacts in the bucket that are closest to the given +key +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
key + + +string +| + +buffer + + + + + + + + + + + + + Reference key for finding other contacts + +
count + + +number + + + + + + <optional>
+ + + + + +
+ + constants.K + + + Max results to return + +
exclusive + + +boolean + + + + + + <optional>
+ + + + + +
+ + false + + + Exclude result matching the key exactly + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +array + + +
+
+ + + +
+ + + +
+ + +
+ + + +

indexOf(key) → {number}

+ + + + + +
+ Returns the index of the given node id +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +string + + + + + Node identity key for getting index + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + + +
+ + + +
+ + +
+ + + +

set(nodeId, contact) → {number}

+ + + + + +
+ Sets the contact to the node ID in the bucket if it is not full; if the +bucket already contains the contact, move it to the tail - otherwise we +place it at the head +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
nodeId + + +string + + + + + The identity key for the contact + +
contact + + +object + + + + + The address information for the contact + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + +
+ index +
+ + +
+ + + +
+ + + + +

Type Definitions

+ + + +
+

contact

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
0 + + +string + + + + Node identity key
1 + + +object + + + + Contact information (varies by plugins)
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +array + + +
  • +
+ + + + + +
+ + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/ContactList.html b/docs/ContactList.html new file mode 100644 index 0000000..d326a52 --- /dev/null +++ b/docs/ContactList.html @@ -0,0 +1,1008 @@ + + + + + + ContactList - Documentation + + + + + + + + + + + + + + + + + +
+ +

ContactList

+ + + + + + + +
+ +
+ +

+ ContactList +

+ +
Manages contact lists returned from FIND_NODE queries
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new ContactList(key, contacts)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +string + + + + + Lookup key for this operation + +
contacts + + +Array.<Bucket~contact> + + + + + List of contacts to initialize with + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +

Members

+ + + +
+

active

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
active + + +Array.<Bucket~contact> + + + + Contacts in the list that are active
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

closest

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
closest + + +Bucket~contact + + + + The contact closest to the reference key
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

uncontacted

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
uncontacted + + +Array.<Bucket~contact> + + + + Contacts in the list that have not been +contacted
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

add(contacts)

+ + + + + +
+ Adds the given contacts to the list +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
contacts + + +Array.<Bucket~contact> + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

contacted(contact)

+ + + + + +
+ Marks the supplied contact as contacted +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
contact + + +Bucket~contact + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

responded(contact)

+ + + + + +
+ Marks the supplied contact as active +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
contact + + +Bucket~contact + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Control.html b/docs/Control.html new file mode 100644 index 0000000..346e073 --- /dev/null +++ b/docs/Control.html @@ -0,0 +1,1112 @@ + + + + + + Control - Documentation + + + + + + + + + + + + + + + + + +
+ +

Control

+ + + + + + + +
+ +
+ +

+ Control +

+ +
The Kadence daemon can be controlled by another process on the same host or +remotely via socket connection. By default, the daemon is configured to +listen on a UNIX domain socket located at $HOME/.config/kadence/kadence.sock. +Once connected to the daemon, you may send it control commands to build +networks in other languages. The controller understands newline terminated +JSON-RPC 2.0 payloads.
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new Control(node)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

getProtocolInfo(callback)

+ + + + + +
+ Returns basic informations about the running node +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
callback + + +Control~getProtocolInfoCallback + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

listMethods(callback)

+ + + + + +
+ Returns a list of the support methods from the controller +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
callback + + +Control~listMethodsCallback + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +

Type Definitions

+ + + +
+ + + +

getProtocolInfoCallback(error, info)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
info + + +object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
versions + + +object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
software + + +string + + + + + + +
protocol + + +string + + + + + + +
+ + +
identity + + +string + + + + + + +
contact + + +object + + + + + + +
peers + + +Array.<array> + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

listMethodsCallback(error, methods)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
methods + + +Array.<object> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
method + + +string + + + + + + +
params + + +Array.<string> + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/ErrorRules.html b/docs/ErrorRules.html new file mode 100644 index 0000000..6915e7d --- /dev/null +++ b/docs/ErrorRules.html @@ -0,0 +1,663 @@ + + + + + + ErrorRules - Documentation + + + + + + + + + + + + + + + + + +
+ +

ErrorRules

+ + + + + + + +
+ +
+ +

+ ErrorRules +

+ + +
+ +
+
+ + +
+ + + +

new ErrorRules(node)

+ + + + + +
+ Constructs a error rules instance in the context of a +AbstractNode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +AbstractNode + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

internalError(error, request, response, next)

+ + + + + +
+ Formats the errors response according to the error object given +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

methodNotFound(error, request, response, next)

+ + + + + +
+ Assumes if no error object exists, then there is simply no method defined +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/HTTPSTransport.html b/docs/HTTPSTransport.html new file mode 100644 index 0000000..0a37edd --- /dev/null +++ b/docs/HTTPSTransport.html @@ -0,0 +1,436 @@ + + + + + + HTTPSTransport - Documentation + + + + + + + + + + + + + + + + + +
+ +

HTTPSTransport

+ + + + + + + +
+ +
+ +

+ HTTPSTransport +

+ +
Extends the HTTP transport with SSL
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new HTTPSTransport(options)

+ + + + + +
+ Contructs a new HTTPS transport adapter +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
options + + +object + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +buffer + + + + + SSL private key buffer + +
cert + + +buffer + + + + + SSL certificate buffer + +
ca + + +Array.<buffer> + + + + + List of certificate authority certificates + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

listen()

+ + + + + +
+ Binds the server to the given address/port +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/HTTPTransport.html b/docs/HTTPTransport.html new file mode 100644 index 0000000..a14880d --- /dev/null +++ b/docs/HTTPTransport.html @@ -0,0 +1,265 @@ + + + + + + HTTPTransport - Documentation + + + + + + + + + + + + + + + + + +
+ +

HTTPTransport

+ + + + + + + +
+ +
+ +

+ HTTPTransport +

+ +
Represents a transport adapter over HTTP
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new HTTPTransport()

+ + + + + +
+ Contructs a HTTP transport adapter +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

listen()

+ + + + + +
+ Binds the server to the given address/port +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/KademliaNode.html b/docs/KademliaNode.html new file mode 100644 index 0000000..0a96237 --- /dev/null +++ b/docs/KademliaNode.html @@ -0,0 +1,4130 @@ + + + + + + KademliaNode - Documentation + + + + + + + + + + + + + + + + + +
+ +

KademliaNode

+ + + + + + + +
+ +
+ +

+ KademliaNode +

+ +
Extends AbstractNode with Kademlia-specific rules
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new KademliaNode()

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

expire(callbackopt) → {Promise}

+ + + + + +
+ Items expire T_EXPIRE seconds after the original publication. All items +are assigned an expiration time which is "exponentially inversely +proportional to the number of nodes between the current node and the node +whose ID is closest to the key", where this number is "inferred from the +bucket structure of the current node". +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
callback + + +KademliaNode~expireCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + + +
+ + + +
+ + +
+ + + +

iterativeFindNode(key, callbackopt) → {Promise.<Array.<Bucket~contact>>}

+ + + + + +
+ Basic kademlia lookup operation that builds a set of K contacts closest +to the given key +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
key + + +buffer +| + +string + + + + + + + + + + + Reference key for node lookup + +
callback + + +KademliaNode~iterativeFindNodeCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<Array.<Bucket~contact>> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

iterativeFindValue(key, callbackopt) → {Promise.<object>}

+ + + + + +
+ Kademlia search operation that is conducted as a node lookup and builds +a list of K closest contacts. If at any time during the lookup the value +is returned, the search is abandoned. If no value is found, the K closest +contacts are returned. Upon success, we must store the value at the +nearest node seen during the search that did not return the value. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
key + + +buffer +| + +string + + + + + + + + + + + Key for value lookup + +
callback + + +KademliaNode~iterativeFindValueCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<object> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

iterativeStore(key, value, callback) → {Promise.<number>}

+ + + + + +
+ Performs a KademliaNode#iterativeFindNode to collect K contacts +nearest to the given key, sending a STORE message to each of them. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +buffer +| + +string + + + + + Key to store data under + +
value + + +buffer +| + +string +| + +object + + + + + Value to store by key + +
callback + + +KademliaNode~iterativeStoreCallback + + + + + + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<number> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

join(peer, joinListeneropt) → {Promise}

+ + + + + +
+ Inserts the given contact into the routing table and uses it to perform +a KademliaNode#iterativeFindNode for this node's identity, +then refreshes all buckets further than it's closest neighbor, which will +be in the occupied bucket with the lowest index +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
peer + + +Bucket~contact + + + + + + + + + + + Peer to bootstrap from + +
joinListener + + +function + + + + + + <optional>
+ + + + + +
+ Function to set as join listener + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + + +
+ + + +
+ + +
+ + + +

listen()

+ + + + + +
+ Adds the kademlia rule handlers before calling super#listen() +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

ping(peer, callbackopt) → {Promise.<number>}

+ + + + + +
+ Sends a PING message to the supplied contact, resolves with latency +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
peer + + +Bucket~contact + + + + + + + + + + + + +
callback + + +KademliaNode~pingCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<number> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

plugin(plugin)

+ + + + + +
+ Accepts an arbitrary function that receives this node as context +for mounting protocol handlers and extending the node with other +methods +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
plugin + + +function + + + + + Using and Authoring Plugins + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

receive(request, response)

+ + + + + +
+ Processes a the given arguments by sending them through the appropriate +middleware stack +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

refresh(startIndex, callbackopt) → {Promise}

+ + + + + +
+ If no node lookups have been performed in any given bucket's range for +T_REFRESH, the node selects a random number in that range and does a +refresh, an iterativeFindNode using that number as key. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
startIndex + + +number + + + + + + + + + + + + 0 + + + bucket index to start refresh from + +
callback + + +KademliaNode~refreshCallback + + + + + + <optional>
+ + + + + +
+ + + + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + + +
+ + + +
+ + +
+ + + +

replicate(callbackopt) → {Promise}

+ + + + + +
+ Performs a scan of the storage adapter and performs +republishing/replication of items stored. Items that we did not publish +ourselves get republished every T_REPLICATE. Items we did publish get +republished every T_REPUBLISH. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
callback + + +KademliaNode~replicateCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise + + +
+
+ + + +
+ + + +
+ + +
+ + + +

send(method, params, contact, callbackopt) → {Promise.<(object|array), Error>}

+ + + + + +
+ Sends the [method, params] to the contact and executes the handler on +response or timeout +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
method + + +string + + + + + + + + + + + RPC method name + +
params + + +object +| + +array + + + + + + + + + + + RPC parameters + +
contact + + +Bucket~contact + + + + + + + + + + + Destination address information + +
callback + + +AbstractNode~sendCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<(object|array), Error> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

use(methodopt, middleware)

+ + + + + +
+ Mounts a message handler route for processing incoming RPC messages +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
method + + +string + + + + + + <optional>
+ + + + + +
+ RPC method name to route through + +
middleware + + +AbstractNode~middleware + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +

Type Definitions

+ + + +
+

entry

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
value + + +string +| + +object +| + +array + + + + The primary entry value
publisher + + +string + + + + Node identity of the original publisher
timestamp + + +number + + + + Last update/replicate time
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
+ + + +
+ + + +

expireCallback(error)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

iterativeFindNodeCallback(error, contacts)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
contacts + + +Array.<Bucket~contact> + + + + + Result of the lookup operation + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

iterativeFindValueCallback(error, value, contact)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
value + + +KademliaNode~entry + + + + + + +
contact + + +null +| + +Bucket~contact + + + + + Contact responded with entry + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

iterativeStoreCallback(error, stored)

+ + + + + +
+ Note that if there is a protocol/validation error, you will not receive +it as an error in the callback. Be sure to also check that stored > 0 as +part of error handling here. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
stored + + +number + + + + + Total nodes who stored the pair + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

pingCallback(error, latency)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
latency + + +number + + + + + Milliseconds before response received + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

refreshCallback(error, bucketsRefreshed)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
bucketsRefreshed + + +array + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

replicateCallback(error)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +

Events

+ + + +
+ + + +

error

+ + + + + +
+ Error event fires when a critical failure has occurred; if no handler is +specified, then it will throw +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + +
+
Type:
+
    +
  • + +Error + + +
  • +
+
+ + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

join

+ + + + + +
+ Join event is triggered when the routing table is no longer empty +
+ + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/KademliaRules.html b/docs/KademliaRules.html new file mode 100644 index 0000000..c86f959 --- /dev/null +++ b/docs/KademliaRules.html @@ -0,0 +1,968 @@ + + + + + + KademliaRules - Documentation + + + + + + + + + + + + + + + + + +
+ +

KademliaRules

+ + + + + + + +
+ +
+ +

+ KademliaRules +

+ +
Represent kademlia protocol handlers
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new KademliaRules(node)

+ + + + + +
+ Constructs a kademlia rules instance in the context of a +KademliaNode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

findNode(request, response, next)

+ + + + + +
+ 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. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

findValue(request, response, next)

+ + + + + +
+ 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. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

ping(request, response)

+ + + + + +
+ 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. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

store(request, response, next)

+ + + + + +
+ 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. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/Messenger.html b/docs/Messenger.html new file mode 100644 index 0000000..f63e57a --- /dev/null +++ b/docs/Messenger.html @@ -0,0 +1,1467 @@ + + + + + + Messenger - Documentation + + + + + + + + + + + + + + + + + +
+ +

Messenger

+ + + + + + + +
+ +
+ +

+ Messenger +

+ +
Represents and duplex stream for dispatching messages to a given transport +adapter and receiving messages to process through middleware stacks
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new Messenger(optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
serializer + + +Messenger~serializer + + + + + + <optional>
+ + + + + +
+ Serializer function + +
deserializer + + +Messenger~deserializer + + + + + + <optional>
+ + + + + +
+ Deserializer function + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(static) JsonRpcDeserializer(rawMessage, callback)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
rawMessage + + +buffer + + + + + Incoming message as buffer + +
callback + + +function + + + + + Transform stream callback(err, data) + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(static) JsonRpcSerializer(data, sender, receiver, callback)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +array + + + + + Object to transform + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
0 + + +object + + + + + JSON payload, parsed into an object + +
+ + +
sender + + +Bucket~contact + + + + + Origin peer for message + +
receiver + + +Bucket~contact + + + + + Destination peer for message + +
callback + + +function + + + + + Transform stream callback(err, data) + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(inner) deserializer(data, encoding, callback)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +object +| + +buffer + + + + + Incoming message buffer or parsed JSON data + +
encoding + + +string +| + +null + + + + + Encoding of incoming data + +
callback + + +Messenger~deserializerCallback + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(inner) serializer(data, encoding, callback)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +object +| + +buffer + + + + + Outgoing message buffer or parsed JSON data + +
encoding + + +string +| + +null + + + + + Encoding of incoming data + +
callback + + +Messenger~serializerCallback + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +

Type Definitions

+ + + +
+ + + +

deserializerCallback(error, data)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
data + + +buffer +| + +object + + + + + Deserialized data to pass through middleware + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

serializerCallback(error, data)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error +| + +null + + + + + + +
data + + +buffer +| + +object + + + + + Serialized data to pass through middleware + +
+ + + + + + + + + + + + + + + + +
+ + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/RoutingTable.html b/docs/RoutingTable.html new file mode 100644 index 0000000..6ffdfcd --- /dev/null +++ b/docs/RoutingTable.html @@ -0,0 +1,1504 @@ + + + + + + RoutingTable - Documentation + + + + + + + + + + + + + + + + + +
+ +

RoutingTable

+ + + + + + + +
+ +
+ +

+ RoutingTable +

+ +
Represents a kademlia routing table
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new RoutingTable(identity)

+ + + + + +
+ Constructs a routing table +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +buffer + + + + + Reference point for calculating distances + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +

Members

+ + + +
+

length

+ + + + +
+ Returns the total buckets in the routing table +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
length + + +number + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

size

+ + + + +
+ Returns the total contacts in the routing table +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
size + + +number + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

addContactByNodeId(nodeId, contact) → {array}

+ + + + + +
+ Adds the contact to the routing table in the proper bucket position, +returning the [bucketIndex, bucket, contactIndex, contact]; if the +returned contactIndex is -1, it indicates the bucket is full and the +contact was not added; kademlia implementations should PING the contact +at bucket.head to determine if it should be dropped before calling this +method again +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
nodeId + + +string +| + +buffer + + + + + Node identity to add + +
contact + + +object + + + + + contact information for peer + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +array + + +
+
+ + + +
+ + + +
+ + +
+ + + +

getClosestBucket() → {Bucket}

+ + + + + +
+ Returns the [index, bucket] of the occupied bucket with the lowest index +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Bucket + + +
+
+ + + +
+ + + +
+ + +
+ + + +

getClosestContactsToKey(key, nopt, exclusiveopt) → {map}

+ + + + + +
+ Returns a array of N contacts closest to the supplied key +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
key + + +string +| + +buffer + + + + + + + + + + + + + Key to get buckets for + +
n + + +number + + + + + + <optional>
+ + + + + +
+ + 20 + + + Number of results to return + +
exclusive + + +boolean + + + + + + <optional>
+ + + + + +
+ + false + + + Exclude exact matches + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +map + + +
+
+ + + +
+ + + +
+ + +
+ + + +

getContactByNodeId(nodeId) → {Bucket~contact}

+ + + + + +
+ Returns the contact object associated with the given node id +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
nodeId + + +string +| + +buffer + + + + + Node identity of the contact + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Bucket~contact + + +
+
+ + + +
+ + + +
+ + +
+ + + +

indexOf(nodeId) → {number}

+ + + + + +
+ Returns the bucket index of the given node id +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
nodeId + + +string +| + +buffer + + + + + Node identity to get index for + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + + +
+ + + +
+ + +
+ + + +

removeContactByNodeId(nodeId) → {boolean}

+ + + + + +
+ Removes the contact from the routing table given a node id +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
nodeId + + +string +| + +buffer + + + + + Node identity to remove + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/UDPTransport.html b/docs/UDPTransport.html new file mode 100644 index 0000000..193f109 --- /dev/null +++ b/docs/UDPTransport.html @@ -0,0 +1,483 @@ + + + + + + UDPTransport - Documentation + + + + + + + + + + + + + + + + + +
+ +

UDPTransport

+ + + + + + + +
+ +
+ +

+ UDPTransport +

+ +
Implements a UDP transport adapter
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new UDPTransport(socketOptsopt)

+ + + + + +
+ Constructs a datagram socket interface +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
socketOpts + + +object + + + + + + <optional>
+ + + + + +
+ Passed to dgram.createSocket(options) + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

listen(portopt, addressopt, callbackopt)

+ + + + + +
+ Binds the socket to the [port] [, address] [, callback] +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
port + + +number + + + + + + <optional>
+ + + + + +
+ + 0 + + + Port to bind to + +
address + + +string + + + + + + <optional>
+ + + + + +
+ + 0.0.0.0 + + + Address to bind to + +
callback + + +function + + + + + + <optional>
+ + + + + +
+ + + called after bind complete + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/bucket.js.html b/docs/bucket.js.html new file mode 100644 index 0000000..0310634 --- /dev/null +++ b/docs/bucket.js.html @@ -0,0 +1,186 @@ + + + + + + bucket.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

bucket.js

+ + + + + + + +
+
+
'use strict';
+
+const constants = require('./constants');
+const utils = require('./utils');
+
+
+/**
+ * @typedef {array} Bucket~contact
+ * @property {string} 0 - Node identity key
+ * @property {object} 1 - Contact information (varies by plugins)
+ */
+
+/**
+ * Represents a column of the routing table holding up to K contacts
+ */
+class Bucket extends Map {
+
+  /**
+   * @constructor
+   */
+  constructor() {
+    super();
+  }
+
+  /**
+   * @property {number} length - The number of contacts in the bucket
+   */
+  get length() {
+    return super.size;
+  }
+
+  /**
+   * @property {object} head - The contact at the bucket head
+   */
+  get head() {
+    return [...super.entries()].shift();
+  }
+
+  /**
+   * @property {object} tail - The contact at the bucket tail
+   */
+  get tail() {
+    return [...super.entries()].pop();
+  }
+
+  /**
+   * Sets the contact to the node ID in the bucket if it is not full; if the
+   * bucket already contains the contact, move it to the tail - otherwise we
+   * place it at the head
+   * @param {string} nodeId - The identity key for the contact
+   * @param {object} contact - The address information for the contact
+   * @returns {number} index
+   */
+  set(nodeId, contact) {
+    if (this.has(nodeId)) {
+      super.delete(nodeId);
+      super.set(nodeId, contact);
+    } else if (this.size < constants.K) {
+      let bucketEntries = [...this.entries()];
+
+      super.clear();
+      super.set(nodeId, contact);
+
+      for (let [nodeId, contact] of bucketEntries) {
+        super.set(nodeId, contact);
+      }
+    }
+
+    return this.indexOf(nodeId);
+  }
+
+  /**
+   * Returns the index of the given node id
+   * @param {string} key - Node identity key for getting index
+   * @returns {number}
+   */
+  indexOf(key) {
+    let isMissing = -1;
+    let index = isMissing;
+
+    for (let nodeId of this.keys()) {
+      index++;
+
+      if (key !== nodeId) {
+        continue;
+      }
+
+      return index;
+    }
+
+    return isMissing;
+  }
+
+  /**
+   * Returns an array of contacts in the bucket that are closest to the given
+   * key
+   * @param {string|buffer} key - Reference key for finding other contacts
+   * @param {number} [count=constants.K] - Max results to return
+   * @param {boolean} [exclusive=false] - Exclude result matching the key exactly
+   * @returns {array}
+   */
+  getClosestToKey(key, count = constants.K, exclusive = false) {
+    let contacts = [];
+
+    for (let [identity, contact] of this.entries()) {
+      contacts.push({
+        contact, identity, distance: utils.getDistance(identity, key)
+      });
+    }
+
+    return new Map(contacts.sort((a, b) => {
+      return utils.compareKeyBuffers(
+        Buffer.from(a.distance, 'hex'),
+        Buffer.from(b.distance, 'hex')
+      );
+    }).filter((result) => {
+      if (exclusive) {
+        return result.identity !== key.toString('hex');
+      } else {
+        return true;
+      }
+    }).map((obj) => [obj.identity, obj.contact]).splice(0, count));
+  }
+}
+
+module.exports = Bucket;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/constants.js.html b/docs/constants.js.html new file mode 100644 index 0000000..ea69e09 --- /dev/null +++ b/docs/constants.js.html @@ -0,0 +1,143 @@ + + + + + + constants.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

constants.js

+ + + + + + + +
+
+
/**
+ * @module kadence/constants
+ */
+
+'use strict';
+
+/**
+ * @constant {number} ALPHA - Degree of parallelism
+ */
+exports.ALPHA = 3;
+
+/**
+ * @constant {number} B - Number of bits for nodeID creation
+ */
+exports.B = 160;
+
+/**
+ * @constant {number} K - Number of contacts held in a bucket
+ */
+exports.K = 20;
+
+/**
+ * @constant {number} T_REFRESH - Interval for performing router refresh
+ */
+exports.T_REFRESH = 3600000;
+
+/**
+ * @constant {number} T_REPLICATE - Interval for replicating local data
+ */
+exports.T_REPLICATE = 3600000;
+
+/**
+ * @constant {number} T_REPUBLISH - Interval for republishing data
+ */
+exports.T_REPUBLISH = 86400000;
+
+/**
+ * @constant {number} T_EXPIRE - Interval for expiring local data entries
+ */
+exports.T_EXPIRE = 86405000;
+
+/**
+ * @constant {number} T_RESPONSETIMEOUT - Time to wait for RPC response
+ */
+exports.T_RESPONSETIMEOUT = 10000;
+
+/**
+ * @constant {number} MAX_UNIMPROVED_REFRESHES - Quit refreshing no improvement
+ */
+exports.MAX_UNIMPROVED_REFRESHES = 3;
+
+/**
+ * @constant {number} IDENTITY_DIFFICULTY - Equihash params for identity proofs
+ */
+exports.IDENTITY_DIFFICULTY = { n: 126, k: 5 };
+
+/**
+ * @constant {number} TESTNET_DIFFICULTY - Testnet difficulty override
+ */
+exports.TESTNET_DIFFICULTY = { n: 90, k: 5 };
+
+/**
+ * @constant {number} LRU_CACHE_SIZE - Number of used hashcash stamps to track
+ */
+exports.LRU_CACHE_SIZE = 50;
+
+/**
+ * @constant {number} FILTER_DEPTH - Number of neighborhood hops to track
+ * subsrciptions for
+ */
+exports.FILTER_DEPTH = 3;
+
+/**
+ * @constant {number} MAX_RELAY_HOPS - Maximum times a message instance will be
+ * relayed when published
+ */
+exports.MAX_RELAY_HOPS = 6;
+
+/**
+ * @constant {number} SOFT_STATE_TIMEOUT - Time to wait before busting the
+ * subscription cache
+ */
+exports.SOFT_STATE_TIMEOUT = 3600000;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/contact-list.js.html b/docs/contact-list.js.html new file mode 100644 index 0000000..5115abe --- /dev/null +++ b/docs/contact-list.js.html @@ -0,0 +1,155 @@ + + + + + + contact-list.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

contact-list.js

+ + + + + + + +
+
+
'use strict';
+
+const utils = require('./utils');
+
+/**
+ * Manages contact lists returned from FIND_NODE queries
+ */
+class ContactList {
+
+  /**
+   * @constructor
+   * @param {string} key - Lookup key for this operation
+   * @param {Bucket~contact[]} contacts - List of contacts to initialize with
+   */
+  constructor(key, contacts = []) {
+    this.key = key;
+    this._contacts = [];
+    this._contacted = new Set();
+    this._active = new Set();
+
+    this.add(contacts);
+  }
+
+  /**
+   * @property {Bucket~contact} closest - The contact closest to the reference key
+   */
+  get closest() {
+    return this._contacts[0];
+  }
+
+  /**
+   * @property {Bucket~contact[]} active - Contacts in the list that are active
+   */
+  get active() {
+    return this._contacts.filter(contact => this._active.has(contact[0]));
+  }
+
+  /**
+   * @property {Bucket~contact[]} uncontacted - Contacts in the list that have not been
+   * contacted
+   */
+  get uncontacted() {
+    return this._contacts.filter(contact => !this._contacted.has(contact[0]));
+  }
+
+  /**
+   * Adds the given contacts to the list
+   * @param {Bucket~contact[]} contacts
+   */
+  add(contacts) {
+    let identities = this._contacts.map(c => c[0]);
+    let added = [];
+
+    contacts.forEach(contact => {
+      if (identities.indexOf(contact[0]) === -1) {
+        this._contacts.push(contact);
+        identities.push(contact[0]);
+        added.push(contact);
+      }
+    });
+
+    this._contacts.sort(this._identitySort.bind(this));
+
+    return added;
+  }
+
+  /**
+   * Marks the supplied contact as contacted
+   * @param {Bucket~contact} contact
+   */
+  contacted(contact) {
+    this._contacted.add(contact[0]);
+  }
+
+  /**
+   * Marks the supplied contact as active
+   * @param {Bucket~contact} contact
+   */
+  responded(contact) {
+    this._active.add(contact[0]);
+  }
+
+  /**
+   * @private
+   */
+  _identitySort([aIdentity], [bIdentity]) {
+    return utils.compareKeyBuffers(
+      Buffer.from(utils.getDistance(aIdentity, this.key), 'hex'),
+      Buffer.from(utils.getDistance(bIdentity, this.key), 'hex')
+    );
+  }
+
+}
+
+module.exports = ContactList;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/control.js.html b/docs/control.js.html new file mode 100644 index 0000000..a9195cf --- /dev/null +++ b/docs/control.js.html @@ -0,0 +1,203 @@ + + + + + + control.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

control.js

+ + + + + + + +
+
+
'use strict';
+
+const constants = require('./constants');
+const version = require('./version');
+const utils = require('./utils');
+
+
+/**
+ * The Kadence daemon can be controlled by another process on the same host or
+ * remotely via socket connection. By default, the daemon is configured to
+ * listen on a UNIX domain socket located at $HOME/.config/kadence/kadence.sock.
+ * Once connected to the daemon, you may send it control commands to build
+ * networks in other languages. The controller understands newline terminated
+ * JSON-RPC 2.0 payloads.
+ */
+class Control {
+
+  /**
+   * @constructor
+   * @param {KademliaNode} node
+   */
+  constructor(node) {
+    this.node = node;
+  }
+
+  /**
+   * @private
+   */
+  _parseMethodSignature(name) {
+    const method = name;
+    const func = this[method].toString();
+    const args = func.split(`${method}(`)[1].split(')')[0];
+    const params = args.split(', ').map(s => s.trim());
+
+    params.pop();
+
+    return { method, params };
+  }
+
+  /**
+   * Returns a list of the support methods from the controller
+   * @param {Control~listMethodsCallback} callback
+   */
+  listMethods(callback) {
+    callback(null, Object.getOwnPropertyNames(Object.getPrototypeOf(this))
+      .filter(method => {
+        return method[0] !== '_' && method !== 'constructor' &&
+          typeof this[method] === 'function';
+      })
+      .map(this._parseMethodSignature.bind(this))
+      .sort((a, b) => b.method < a.method));
+  }
+  /**
+   * @callback Control~listMethodsCallback
+   * @param {error|null} error
+   * @param {object[]} methods
+   * @param {string} methods.method
+   * @param {string[]} methods.params
+   */
+
+  /**
+   * Returns basic informations about the running node
+   * @param {Control~getProtocolInfoCallback} callback
+   */
+  getProtocolInfo(callback) {
+    const peers = [], dump = this.node.router.getClosestContactsToKey(
+      this.node.identity,
+      constants.K * constants.B
+    );
+
+    for (let peer of dump) {
+      peers.push(peer);
+    }
+
+    callback(null, {
+      versions: version,
+      identity: this.node.identity.toString('hex'),
+      contact: this.node.contact,
+      peers
+    });
+  }
+  /**
+   * @callback Control~getProtocolInfoCallback
+   * @param {error|null} error
+   * @param {object} info
+   * @param {object} info.versions
+   * @param {string} info.versions.software
+   * @param {string} info.versions.protocol
+   * @param {string} info.identity
+   * @param {object} info.contact
+   * @param {array[]} info.peers
+   */
+
+  /**
+   * {@link KademliaNode#iterativeFindNode}
+   */
+  /* istanbul ignore next */
+  iterativeFindNode(hexKey, callback) {
+    this.node.iterativeFindNode(hexKey, callback);
+  }
+
+  /**
+   * {@link KademliaNode#iterativeFindValue}
+   */
+  /* istanbul ignore next */
+  iterativeFindValue(hexKey, callback) {
+    this.node.iterativeFindValue(Buffer.from(hexKey, 'hex'), callback);
+  }
+
+  /**
+   * {@link KademliaNode#iterativeStore}
+   */
+  /* istanbul ignore next */
+  iterativeStore(hexValue, callback) {
+    let hexKey = utils.hash160(Buffer.from(hexValue, 'hex')).toString('hex');
+    this.node.iterativeStore(hexKey, hexValue, function(err, count) {
+      if (err) {
+        return callback(err);
+      }
+
+      callback(null, count, hexKey);
+    });
+  }
+
+  /**
+   * {@link module:kadence/quasar~QuasarPlugin#quasarSubscribe}
+   */
+  /* istanbul ignore next */
+  quasarSubscribe(hexKey, callback) {
+    this.node.quasarSubscribe(hexKey, callback);
+  }
+
+  /**
+   * {@link module:kadence/quasar~QuasarPlugin#quasarPublish}
+   */
+  /* istanbul ignore next */
+  quasarPublish(hexKey, contentValue, callback) {
+    this.node.quasarPublish(hexKey, contentValue, callback);
+  }
+
+}
+
+module.exports = Control;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/fonts/OpenSans-Bold-webfont.eot b/docs/fonts/OpenSans-Bold-webfont.eot new file mode 100644 index 0000000..5d20d91 Binary files /dev/null and b/docs/fonts/OpenSans-Bold-webfont.eot differ diff --git a/docs/fonts/OpenSans-Bold-webfont.svg b/docs/fonts/OpenSans-Bold-webfont.svg new file mode 100644 index 0000000..3ed7be4 --- /dev/null +++ b/docs/fonts/OpenSans-Bold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Bold-webfont.woff b/docs/fonts/OpenSans-Bold-webfont.woff new file mode 100644 index 0000000..1205787 Binary files /dev/null and b/docs/fonts/OpenSans-Bold-webfont.woff differ diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.eot b/docs/fonts/OpenSans-BoldItalic-webfont.eot new file mode 100644 index 0000000..1f639a1 Binary files /dev/null and b/docs/fonts/OpenSans-BoldItalic-webfont.eot differ diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.svg b/docs/fonts/OpenSans-BoldItalic-webfont.svg new file mode 100644 index 0000000..6a2607b --- /dev/null +++ b/docs/fonts/OpenSans-BoldItalic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-BoldItalic-webfont.woff b/docs/fonts/OpenSans-BoldItalic-webfont.woff new file mode 100644 index 0000000..ed760c0 Binary files /dev/null and b/docs/fonts/OpenSans-BoldItalic-webfont.woff differ diff --git a/docs/fonts/OpenSans-Italic-webfont.eot b/docs/fonts/OpenSans-Italic-webfont.eot new file mode 100644 index 0000000..0c8a0ae Binary files /dev/null and b/docs/fonts/OpenSans-Italic-webfont.eot differ diff --git a/docs/fonts/OpenSans-Italic-webfont.svg b/docs/fonts/OpenSans-Italic-webfont.svg new file mode 100644 index 0000000..e1075dc --- /dev/null +++ b/docs/fonts/OpenSans-Italic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Italic-webfont.woff b/docs/fonts/OpenSans-Italic-webfont.woff new file mode 100644 index 0000000..ff652e6 Binary files /dev/null and b/docs/fonts/OpenSans-Italic-webfont.woff differ diff --git a/docs/fonts/OpenSans-Light-webfont.eot b/docs/fonts/OpenSans-Light-webfont.eot new file mode 100644 index 0000000..1486840 Binary files /dev/null and b/docs/fonts/OpenSans-Light-webfont.eot differ diff --git a/docs/fonts/OpenSans-Light-webfont.svg b/docs/fonts/OpenSans-Light-webfont.svg new file mode 100644 index 0000000..11a472c --- /dev/null +++ b/docs/fonts/OpenSans-Light-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Light-webfont.woff b/docs/fonts/OpenSans-Light-webfont.woff new file mode 100644 index 0000000..e786074 Binary files /dev/null and b/docs/fonts/OpenSans-Light-webfont.woff differ diff --git a/docs/fonts/OpenSans-LightItalic-webfont.eot b/docs/fonts/OpenSans-LightItalic-webfont.eot new file mode 100644 index 0000000..8f44592 Binary files /dev/null and b/docs/fonts/OpenSans-LightItalic-webfont.eot differ diff --git a/docs/fonts/OpenSans-LightItalic-webfont.svg b/docs/fonts/OpenSans-LightItalic-webfont.svg new file mode 100644 index 0000000..431d7e3 --- /dev/null +++ b/docs/fonts/OpenSans-LightItalic-webfont.svg @@ -0,0 +1,1835 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-LightItalic-webfont.woff b/docs/fonts/OpenSans-LightItalic-webfont.woff new file mode 100644 index 0000000..43e8b9e Binary files /dev/null and b/docs/fonts/OpenSans-LightItalic-webfont.woff differ diff --git a/docs/fonts/OpenSans-Regular-webfont.eot b/docs/fonts/OpenSans-Regular-webfont.eot new file mode 100644 index 0000000..6bbc3cf Binary files /dev/null and b/docs/fonts/OpenSans-Regular-webfont.eot differ diff --git a/docs/fonts/OpenSans-Regular-webfont.svg b/docs/fonts/OpenSans-Regular-webfont.svg new file mode 100644 index 0000000..25a3952 --- /dev/null +++ b/docs/fonts/OpenSans-Regular-webfont.svg @@ -0,0 +1,1831 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Regular-webfont.woff b/docs/fonts/OpenSans-Regular-webfont.woff new file mode 100644 index 0000000..e231183 Binary files /dev/null and b/docs/fonts/OpenSans-Regular-webfont.woff differ diff --git a/docs/fonts/OpenSans-Semibold-webfont.eot b/docs/fonts/OpenSans-Semibold-webfont.eot new file mode 100755 index 0000000..d8375dd Binary files /dev/null and b/docs/fonts/OpenSans-Semibold-webfont.eot differ diff --git a/docs/fonts/OpenSans-Semibold-webfont.svg b/docs/fonts/OpenSans-Semibold-webfont.svg new file mode 100755 index 0000000..eec4db8 --- /dev/null +++ b/docs/fonts/OpenSans-Semibold-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-Semibold-webfont.ttf b/docs/fonts/OpenSans-Semibold-webfont.ttf new file mode 100755 index 0000000..b329084 Binary files /dev/null and b/docs/fonts/OpenSans-Semibold-webfont.ttf differ diff --git a/docs/fonts/OpenSans-Semibold-webfont.woff b/docs/fonts/OpenSans-Semibold-webfont.woff new file mode 100755 index 0000000..28d6ade Binary files /dev/null and b/docs/fonts/OpenSans-Semibold-webfont.woff differ diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.eot b/docs/fonts/OpenSans-SemiboldItalic-webfont.eot new file mode 100755 index 0000000..0ab1db2 Binary files /dev/null and b/docs/fonts/OpenSans-SemiboldItalic-webfont.eot differ diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.svg b/docs/fonts/OpenSans-SemiboldItalic-webfont.svg new file mode 100755 index 0000000..7166ec1 --- /dev/null +++ b/docs/fonts/OpenSans-SemiboldItalic-webfont.svg @@ -0,0 +1,1830 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf b/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf new file mode 100755 index 0000000..d2d6318 Binary files /dev/null and b/docs/fonts/OpenSans-SemiboldItalic-webfont.ttf differ diff --git a/docs/fonts/OpenSans-SemiboldItalic-webfont.woff b/docs/fonts/OpenSans-SemiboldItalic-webfont.woff new file mode 100755 index 0000000..d4dfca4 Binary files /dev/null and b/docs/fonts/OpenSans-SemiboldItalic-webfont.woff differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..a2503e2 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,171 @@ + + + + + + Home - Documentation + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
+

Kadence

+

Kadence is a complete implementation of the +Kademlia distributed +hash table that aims to effectively mitigate all vulnerabilities described in +the S/Kademlia +paper and then some! Kadence provides developers of distributed systems a +complete framework for inventing new protocols on a rock solid base as well as +providing a complete reference implementation of a Kadence network.

+

Ready to get started?

+
$ npm install -g @tacticalchihuahua/kadence
+$ kadence --help
+
+

If you're new to Kadence, check out our tutorial for Getting Started!

+

Features

+

Publish & Subscribe

+

Kadence implements a completely decentralized publish/subscribe protocol based +on Quasar, +allowing you to build anything from peer-to-peer social networks to real time +sensor networks for the internet of things.

+

DDoS & Spam Protection

+

Kadence enforces a proof of work system +called Hashcash for relaying +messages to prevent abuse and make large scale denial of service and spam +attacks cost prohibitive.

+

Churn Impact Reduction

+

Kadence proactively evicts offline or misbehaving peers from its routing table +and uses an exponential cooldown time for allowing them back in to prevent +unreliable contacts from propagating through the network.

+

Bandwidth Metering

+

Kadence monitors bandwidth and enables end users to configure their maximum +bandwidth usage within a timeframe to suit their individual needs or prevent +overages with internet services providers that enforce +bandwidth caps.

+

End-to-End Encryption

+

Kadence can automatically generate SSL certificates and supports full +end-to-end encryption via TLS using it's built in HTTPS transport adapter to +prevent eavesdropping and man in the middle attacks.

+

Cryptographic Identities

+

Kadence extends Kademlia's node identity selection with the same cryptography +bitcoin uses for securing funds. Node identities are derived from the hash of +the public portion of an ECDSA +key pair and each message is signed to ensure it hasn't been tampered with in +transit.

+

Sybil & Eclipse Mitigation

+

Kadence employs a proof of work system +using Equihash for generating valid +node identities and subsequent acceptance into the overlay network. This +forces nodes into sufficiently random sectors of the key space and makes +Sybil and +Eclipse +attacks computationally very difficult and ultimately ineffective.

+

Automatic NAT Traversal

+

Kadence supports multiple strategies for punching through +network address translation. +This enables peers behind even the strictest of firewalls to become addressable +and join the network. Fallback to secure reverse tunnels is supported through +the use of Diglet servers.

+

Multiple Network Transports

+

Kadence supports the use of multiple transport adapters and is agnostic to the +underlying network protocol. Support for UDP and HTTP/HTTPS ship by default. +Plugin your own custom transport layer using using a simple interface.

+

Persistent Routing Tables

+

Kadence remembers peers between restarts so after you've joined the network once +subsequent joins are fast and automatically select the best initial peers for +bootstrapping.

+

Sender & Destination Anonymity

+

Kadence ships with full support for +Tor Hidden Services out of +the box with no additional software installation or configuration required. +This enables fully anonymized structured networks and leverages the latest +version 3 hidden services protocol.

+

Configurable Trust Policies

+

Kadence provides a flexible trust policy plugin allowing for fine-tuned, +per-identity, per-method trust policies. Blacklist misbehaving nodes on an +open network or whitelist identities on an explicit trust-based network.

+

Simple Plugin Interface

+

Kadence exposes a simple interface for extending the protocol with your own +application logic. Users of Express will find it +comfortable and familiar. If you are new to building distributed systems, you +will find it easy to get started.

+

Research

+

Kadence is used in academic research on distributed systems. Here are some +notable papers!

+ +

License

+

Kadence - Extensible, Hardened, and Secure Distributed Systems Framework
+Copyright (C) 2019 Lily Anne Hall.

+

This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version.

+

This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details.

+

You should have received a copy of the GNU Affero General Public License +along with this program. If not, see http://www.gnu.org/licenses/.

+
+ + + + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/messenger.js.html b/docs/messenger.js.html new file mode 100644 index 0000000..c6b348b --- /dev/null +++ b/docs/messenger.js.html @@ -0,0 +1,274 @@ + + + + + + messenger.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

messenger.js

+ + + + + + + +
+
+
'use strict';
+
+const { EventEmitter } = require('events');
+const { Transform: TransformStream } = require('stream');
+const merge = require('merge');
+const jsonrpc = require('jsonrpc-lite');
+const uuid = require('uuid');
+const MetaPipe = require('metapipe');
+
+
+/**
+ * A factory class for creating metapipe instances from a "template"
+ */
+class MetaPipeFactory {
+
+  /**
+   * @constructor
+   * @private
+   */
+  constructor(options) {
+    this._options = options;
+    this._template = [];
+  }
+
+  /**
+   * @private
+   */
+  append(fn) {
+    this._template.push({ type: 'append', construct: fn });
+  }
+
+  /**
+   * @private
+   */
+  prepend(fn) {
+    this._template.push({ type: 'prepend', construct: fn });
+  }
+
+  /**
+   * @private
+   */
+  create() {
+    const metapipe = new MetaPipe(this._options);
+
+    this._template.forEach(instruction => {
+      switch (instruction.type) {
+        case 'append':
+          metapipe.append(instruction.construct());
+          break;
+        case 'prepend':
+          metapipe.prepend(instruction.construct());
+          break;
+        default:
+          throw new Error(`Invalid instruction type "${instruction.type}"`);
+      }
+    });
+
+    return metapipe;
+  }
+
+}
+
+/**
+ * Represents and duplex stream for dispatching messages to a given transport
+ * adapter and receiving messages to process through middleware stacks
+ * @class
+ */
+class Messenger extends EventEmitter {
+
+  static get DEFAULTS() {
+    return {
+      serializer: Messenger.JsonRpcSerializer,
+      deserializer: Messenger.JsonRpcDeserializer
+    };
+  }
+
+  /**
+   * @function
+   * @memberof Messenger
+   * @param {array} data - Object to transform
+   * @param {object} data.0 - JSON payload, parsed into an object
+   * @param {Bucket~contact} sender - Origin peer for message
+   * @param {Bucket~contact} receiver - Destination peer for message
+   * @param {function} callback - Transform stream callback(err, data)
+   */
+  static get JsonRpcSerializer() {
+    return function([object, sender, receiver], callback) {
+      let message = jsonrpc.parseObject(
+        merge({ jsonrpc: '2.0', id: uuid() }, object)
+      );
+      let notification = jsonrpc.notification('IDENTIFY', sender);
+
+      switch (message.type) {
+        case 'request':
+        case 'error':
+        case 'success':
+          return callback(null, [
+            message.payload.id,
+            Buffer.from(JSON.stringify([
+              message.payload,
+              notification
+            ]), 'utf8'),
+            receiver
+          ]);
+        case 'invalid':
+        case 'notification':
+        default:
+          return callback(new Error(`Invalid message type "${message.type}"`));
+      }
+    }
+  }
+
+
+  /**
+   * @function
+   * @memberof Messenger
+   * @param {buffer} rawMessage - Incoming message as buffer
+   * @param {function} callback - Transform stream callback(err, data)
+   */
+  static get JsonRpcDeserializer() {
+    return function(buffer, callback) {
+      let [message, notification] = jsonrpc.parse(buffer.toString('utf8'));
+
+      switch (message.type) {
+        case 'request':
+        case 'error':
+        case 'success':
+          return callback(null, [message, notification]);
+        case 'invalid':
+        case 'notification':
+        default:
+          return callback(new Error(`Invalid message type "${message.type}"`));
+      }
+    }
+  }
+
+  /**
+   * @interface Messenger~serializer
+   * @function
+   * @param {object|buffer} data - Outgoing message buffer or parsed JSON data
+   * @param {string|null} encoding - Encoding of incoming data
+   * @param {Messenger~serializerCallback} callback
+   */
+
+  /**
+   * @callback Messenger~serializerCallback
+   * @param {error|null} error
+   * @param {buffer|object} data - Serialized data to pass through middleware
+   */
+
+  /**
+   * @interface Messenger~deserializer
+   * @function
+   * @param {object|buffer} data - Incoming message buffer or parsed JSON data
+   * @param {string|null} encoding - Encoding of incoming data
+   * @param {Messenger~deserializerCallback} callback
+   */
+
+  /**
+   * @callback Messenger~deserializerCallback
+   * @param {error|null} error
+   * @param {buffer|object} data - Deserialized data to pass through middleware
+   */
+
+  /**
+   * @constructor
+   * @param {object} [options]
+   * @param {Messenger~serializer} [options.serializer] - Serializer function
+   * @param {Messenger~deserializer} [options.deserializer] - Deserializer function
+   */
+  constructor(options=Messenger.DEFAULTS) {
+    super();
+
+    this._opts = merge(Messenger.DEFAULTS, options);
+    this.serializer = new MetaPipeFactory({ objectMode: true });
+    this.deserializer = new MetaPipeFactory({ objectMode: true });
+
+    this.serializer.append(() => new TransformStream({
+      objectMode: true,
+      transform: (object, enc, cb) => this._serialize(object, cb)
+    }));
+    this.deserializer.append(() => new TransformStream({
+      objectMode: true,
+      transform: (object, enc, cb) => this._deserialize(object, cb)
+    }));
+  }
+
+  /**
+   * Serializes a message to a buffer
+   * @private
+   */
+  _serialize(object, callback) {
+    this._opts.serializer(object, (err, data) => {
+      callback(null, err ? undefined : data);
+    });
+  }
+
+  /**
+   * Deserializes a buffer into a message
+   * @private
+   */
+  _deserialize(object, callback) {
+    if (!Buffer.isBuffer(object)) {
+      return callback(new Error('Cannot deserialize non-buffer chunk'));
+    }
+
+    this._opts.deserializer(object, (err, data) => {
+      callback(null, err ? undefined : data);
+    });
+  }
+
+}
+
+module.exports = Messenger;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/module-kadence_churnfilter-ChurnFilterPlugin.html b/docs/module-kadence_churnfilter-ChurnFilterPlugin.html new file mode 100644 index 0000000..fd437c2 --- /dev/null +++ b/docs/module-kadence_churnfilter-ChurnFilterPlugin.html @@ -0,0 +1,1063 @@ + + + + + + ChurnFilterPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

ChurnFilterPlugin

+ + + + + + + +
+ +
+ +

+ kadence/churnfilter~ + + ChurnFilterPlugin +

+ +
Plugin that tracks contacts that are not online and evicts them from the +routing table, prevents re-entry into the routing table using an exponential +cooldown time.
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new ChurnFilterPlugin(node, optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
node + + +AbstractNode + + + + + + + + + + + + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
cooldownMultiplier + + +number + + + + + + <optional>
+ + + + + +
+ + 2 + + + Multiply cooldown time +by this number after every offense + +
cooldownResetTime + + +string + + + + + + <optional>
+ + + + + +
+ + "10M" + + + Human time string +for resetting the cooldown multiplier after no block added for a given +peer fingerprint + +
cooldownBaseTimeout + + +string + + + + + + <optional>
+ + + + + +
+ + "1M" + + + Human time string +for starting timeout, multiplied by two every time the cooldown is reset +and broken again + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

delBlock(fingerprint)

+ + + + + +
+ Deletes the blocked fingerprint +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
fingerprint + + +string +| + +buffer + + + + + Node ID to remove block + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

hasBlock(fingerprint) → {boolean}

+ + + + + +
+ Checks if the fingerprint is blocked +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
fingerprint + + +string +| + +buffer + + + + + Node ID to check + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

reset()

+ + + + + +
+ Clears all blocked and cooldown data +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

resetCooldownForStablePeers()

+ + + + + +
+ Releases blocked to reset cooldown multipliers for fingerprints with +cooldowns that are long expired and not blocked +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

setBlock(fingerprint) → {object}

+ + + + + +
+ Creates a new block or renews the cooldown for an existing block +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
fingerprint + + +string +| + +buffer + + + + + Node ID to block + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_churnfilter.html b/docs/module-kadence_churnfilter.html new file mode 100644 index 0000000..ff663ab --- /dev/null +++ b/docs/module-kadence_churnfilter.html @@ -0,0 +1,409 @@ + + + + + + kadence/churnfilter - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/churnfilter

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/churnfilter"))(optionsopt)

+ + + + + +
+ Registers a module:kadence/contentaddress~ChurnFilterPlugin with +a KademliaNode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
cooldownMultiplier + + +number + + + + + + <optional>
+ + + + + +
+ + 2 + + + Multiply cooldown time +by this number after every offense + +
cooldownResetTime + + +string + + + + + + <optional>
+ + + + + +
+ + "60M" + + + Human time string +for resetting the cooldown multiplier after no block added for a given +peer fingerprint + +
cooldownBaseTimeout + + +string + + + + + + <optional>
+ + + + + +
+ + "5M" + + + Human time string +for starting timeout, multiplied by two every time the cooldown is reset +and broken again + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
ChurnFilterPlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_constants.html b/docs/module-kadence_constants.html new file mode 100644 index 0000000..1f99914 --- /dev/null +++ b/docs/module-kadence_constants.html @@ -0,0 +1,1209 @@ + + + + + + kadence/constants - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/constants

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + +

Members

+ + + +
+

(inner, constant) ALPHA :number

+ + + + +
+ Degree of parallelism +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) B :number

+ + + + +
+ Number of bits for nodeID creation +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) FILTER_DEPTH :number

+ + + + +
+ Number of neighborhood hops to track +subsrciptions for +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) IDENTITY_DIFFICULTY :number

+ + + + +
+ Equihash params for identity proofs +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) K :number

+ + + + +
+ Number of contacts held in a bucket +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) LRU_CACHE_SIZE :number

+ + + + +
+ Number of used hashcash stamps to track +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) MAX_RELAY_HOPS :number

+ + + + +
+ Maximum times a message instance will be +relayed when published +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) MAX_UNIMPROVED_REFRESHES :number

+ + + + +
+ Quit refreshing no improvement +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) SOFT_STATE_TIMEOUT :number

+ + + + +
+ Time to wait before busting the +subscription cache +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) T_EXPIRE :number

+ + + + +
+ Interval for expiring local data entries +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) T_REFRESH :number

+ + + + +
+ Interval for performing router refresh +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) T_REPLICATE :number

+ + + + +
+ Interval for replicating local data +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) T_REPUBLISH :number

+ + + + +
+ Interval for republishing data +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) T_RESPONSETIMEOUT :number

+ + + + +
+ Time to wait for RPC response +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) TESTNET_DIFFICULTY :number

+ + + + +
+ Testnet difficulty override +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +number + + +
  • +
+ + + + + +
+ + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_contentaddress-ContentAddressPlugin.html b/docs/module-kadence_contentaddress-ContentAddressPlugin.html new file mode 100644 index 0000000..037bfc5 --- /dev/null +++ b/docs/module-kadence_contentaddress-ContentAddressPlugin.html @@ -0,0 +1,579 @@ + + + + + + ContentAddressPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

ContentAddressPlugin

+ + + + + + + +
+ +
+ +

+ module:kadence/contentaddress~ + + ContentAddressPlugin +

+ +
Enforces that any KademliaNode~entry stored in the DHT must be +content-addressable (keyed by the hash of it's value).
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new ContentAddressPlugin(node, optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
node + + +AbstractNode + + + + + + + + + + + + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
keyAlgorithm + + +string + + + + + + <optional>
+ + + + + +
+ + "rmd160" + + + Algorithm for hashing + +
valueEncoding + + +string + + + + + + <optional>
+ + + + + +
+ + "base64" + + + Text encoding of value + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

validate(request, response, next)

+ + + + + +
+ Validate the the key matches the hash of the value +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_contentaddress.html b/docs/module-kadence_contentaddress.html new file mode 100644 index 0000000..56a963b --- /dev/null +++ b/docs/module-kadence_contentaddress.html @@ -0,0 +1,362 @@ + + + + + + kadence/contentaddress - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/contentaddress

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/contentaddress"))(optionsopt)

+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
keyAlgorithm + + +string + + + + + + <optional>
+ + + + + +
+ + "rmd160" + + + Algorithm for hashing + +
valueEncoding + + +string + + + + + + <optional>
+ + + + + +
+ + "base64" + + + Text encoding of value + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
ContentAddressPlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_eclipse-EclipseIdentity.html b/docs/module-kadence_eclipse-EclipseIdentity.html new file mode 100644 index 0000000..8b74a75 --- /dev/null +++ b/docs/module-kadence_eclipse-EclipseIdentity.html @@ -0,0 +1,524 @@ + + + + + + EclipseIdentity - Documentation + + + + + + + + + + + + + + + + + +
+ +

EclipseIdentity

+ + + + + + + +
+ +
+ +

+ module:kadence/eclipse~ + + EclipseIdentity +

+ +
Generates an identity for use with the +module:kadence/spartacus~SpartacusPlugin that satisfies a proof of +work
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new EclipseIdentity(publicKey, nonceopt, proofopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
publicKey + + +string + + + + + + + + + + + SECP256K1 public key + +
nonce + + +number + + + + + + <optional>
+ + + + + +
+ Equihash proof nonce + +
proof + + +buffer + + + + + + <optional>
+ + + + + +
+ Equihash proof value + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

solve() → {Promise.<EclipseIdentity>}

+ + + + + +
+ Returns a equihash proof and resulting fingerprint +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<EclipseIdentity> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

validate() → {boolean}

+ + + + + +
+ Validates the +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_eclipse-EclipsePlugin.html b/docs/module-kadence_eclipse-EclipsePlugin.html new file mode 100644 index 0000000..e081da5 --- /dev/null +++ b/docs/module-kadence_eclipse-EclipsePlugin.html @@ -0,0 +1,255 @@ + + + + + + EclipsePlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

EclipsePlugin

+ + + + + + + +
+ +
+ +

+ module:kadence/eclipse~ + + EclipsePlugin +

+ +
Enforces proof of work difficulty for entering the routing table and ensures +a high degree of randomness in resulting node identity
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new EclipsePlugin(node, identity)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
identity + + +EclipseIdentity + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_eclipse-EclipseRules.html b/docs/module-kadence_eclipse-EclipseRules.html new file mode 100644 index 0000000..4e9e0cf --- /dev/null +++ b/docs/module-kadence_eclipse-EclipseRules.html @@ -0,0 +1,393 @@ + + + + + + EclipseRules - Documentation + + + + + + + + + + + + + + + + + +
+ +

EclipseRules

+ + + + + + + +
+ +
+ +

+ module:kadence/eclipse~ + + EclipseRules +

+ +
Enforces identities that satisfy a proof of work
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new EclipseRules(node)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +Node + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

validate(request, response)

+ + + + + +
+ Validates all incoming RPC messages +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_eclipse.html b/docs/module-kadence_eclipse.html new file mode 100644 index 0000000..a963745 --- /dev/null +++ b/docs/module-kadence_eclipse.html @@ -0,0 +1,243 @@ + + + + + + kadence/eclipse - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/eclipse

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/eclipse"))(identity)

+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +EclipseIdentity + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
EclipseIdentity
+
+ +
EclipsePlugin
+
+ +
EclipseRules
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_hashcash-HashCashPlugin.html b/docs/module-kadence_hashcash-HashCashPlugin.html new file mode 100644 index 0000000..dbdea03 --- /dev/null +++ b/docs/module-kadence_hashcash-HashCashPlugin.html @@ -0,0 +1,1063 @@ + + + + + + HashCashPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

HashCashPlugin

+ + + + + + + +
+ +
+ +

+ kadence/hashcash~ + + HashCashPlugin +

+ +
Requires proof of work to process messages and performs said work before +issuing RPC messages to peers
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new HashCashPlugin(node, optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
node + + +object + + + + + + + + + + + + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
methods + + +Array.<string> + + + + + + <optional>
+ + + + + +
+ + [] + + + RPC methods to enforce hashcash + +
difficulty + + +number + + + + + + <optional>
+ + + + + +
+ + 8 + + + Leading zero bits in stamp + +
timeframe + + +number + + + + + + <optional>
+ + + + + +
+ + 172800000 + + + Timestamp valid window + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(static) parse(header) → {module:kadence/hashcash~HashCashPlugin~stamp}

+ + + + + +
+ Parses hashcash stamp header into an object +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
header + + +string + + + + + Hashcash header proof stamp + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +module:kadence/hashcash~HashCashPlugin~stamp + + +
+
+ + + +
+ + + +
+ + +
+ + + +

prove()

+ + + + + +
+ Add proof of work to outgoing message +
+ + + + + +
+ + + + + + + + + + + + +
Implements:
+
+ + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

verify()

+ + + + + +
+ Verifies the proof of work on the request object +
+ + + + + +
+ + + + + + + + + + + + +
Implements:
+
+ + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +

Type Definitions

+ + + +
+

stamp

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
ver + + +number + + + + Hashcash version
bits + + +number + + + + Number of zero bits of difficulty
date + + +number + + + + UNIX timestamp
resource + + +string + + + + Sender and target node identities
ext + + +string + + + + Empty string
rand + + +string + + + + String encoded random number
counter + + +number + + + + Base 16 counter
toString + + +function + + + + Reserializes the parsed header
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_hashcash.html b/docs/module-kadence_hashcash.html new file mode 100644 index 0000000..7ad2d78 --- /dev/null +++ b/docs/module-kadence_hashcash.html @@ -0,0 +1,404 @@ + + + + + + kadence/hashcash - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/hashcash

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/hashcash"))(optionsopt)

+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
methods + + +Array.<string> + + + + + + <optional>
+ + + + + +
+ + [] + + + RPC methods to enforce hashcash + +
difficulty + + +number + + + + + + <optional>
+ + + + + +
+ + 8 + + + Leading zero bits in stamp + +
timeframe + + +number + + + + + + <optional>
+ + + + + +
+ + 172800000 + + + Timestamp valid window + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
HashCashPlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_hibernate-HibernatePlugin.html b/docs/module-kadence_hibernate-HibernatePlugin.html new file mode 100644 index 0000000..9aa038c --- /dev/null +++ b/docs/module-kadence_hibernate-HibernatePlugin.html @@ -0,0 +1,976 @@ + + + + + + HibernatePlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

HibernatePlugin

+ + + + + + + +
+ +
+ +

+ kadence/hibernate~ + + HibernatePlugin +

+ +
Represents a bandwidth meter which will trigger hibernation
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new HibernatePlugin(node, optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
node + + +KademliaNode + + + + + + + + + + + + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
limit + + +string + + + + + + <optional>
+ + + + + +
+ + 5gb + + + The accounting max bandwidth + +
interval + + +string + + + + + + <optional>
+ + + + + +
+ + 1d + + + The accounting reset interval + +
reject + + +Array.<string> + + + + + + <optional>
+ + + + + +
+ + + List of methods to reject during +hibernation + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +

Members

+ + + +
+

hibernating

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
hibernating + + +boolean + + + + Indicates if our limits are reached
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

detect(request, response, next)

+ + + + + +
+ Check if hibernating when messages received +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

meter(type) → {stream.Transform}

+ + + + + +
+ Return a meter stream that increments the given accounting property +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
type + + +string + + + + + ['inbound', 'outbound', 'unknown'] + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +stream.Transform + + +
+
+ + + +
+ + + +
+ + +
+ + + +

start()

+ + + + + +
+ Starts the accounting reset timeout +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_hibernate.html b/docs/module-kadence_hibernate.html new file mode 100644 index 0000000..7fe90fc --- /dev/null +++ b/docs/module-kadence_hibernate.html @@ -0,0 +1,402 @@ + + + + + + kadence/hibernate - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/hibernate

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/hibernate"))(optionsopt)

+ + + + + +
+ Regsiters a HibernatePlugin with an AbstractNode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
limit + + +string + + + + + + <optional>
+ + + + + +
+ + 5gb + + + The accounting max bandwidth + +
interval + + +string + + + + + + <optional>
+ + + + + +
+ + 1d + + + The accounting reset interval + +
reject + + +Array.<string> + + + + + + <optional>
+ + + + + +
+ + + List of methods to reject during +hibernation + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
HibernatePlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_logger-IncomingMessageLogger.html b/docs/module-kadence_logger-IncomingMessageLogger.html new file mode 100644 index 0000000..5f96b22 --- /dev/null +++ b/docs/module-kadence_logger-IncomingMessageLogger.html @@ -0,0 +1,228 @@ + + + + + + IncomingMessageLogger - Documentation + + + + + + + + + + + + + + + + + +
+ +

IncomingMessageLogger

+ + + + + + + +
+ +
+ +

+ module:kadence/logger~ + + IncomingMessageLogger +

+ +
Logs all incoming messages
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new IncomingMessageLogger(logger)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
logger + + +AbstractNode~logger + + + + + Logger to use + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_logger-OutgoingMessageLogger.html b/docs/module-kadence_logger-OutgoingMessageLogger.html new file mode 100644 index 0000000..d94335b --- /dev/null +++ b/docs/module-kadence_logger-OutgoingMessageLogger.html @@ -0,0 +1,228 @@ + + + + + + OutgoingMessageLogger - Documentation + + + + + + + + + + + + + + + + + +
+ +

OutgoingMessageLogger

+ + + + + + + +
+ +
+ +

+ module:kadence/logger~ + + OutgoingMessageLogger +

+ +
Logs all outgoing messages
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new OutgoingMessageLogger(logger)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
logger + + +AbstractNode~logger + + + + + Logger to use + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_logger.html b/docs/module-kadence_logger.html new file mode 100644 index 0000000..0f55fda --- /dev/null +++ b/docs/module-kadence_logger.html @@ -0,0 +1,251 @@ + + + + + + kadence/logger - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/logger

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/logger"))(loggeropt)

+ + + + + +
+ Attaches a verbose logger to a AbstractNode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
logger + + +AbstractNode~logger + + + + + + <optional>
+ + + + + +
+ Custom logger + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
IncomingMessageLogger
+
+ +
OutgoingMessageLogger
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_onion-OnionPlugin.html b/docs/module-kadence_onion-OnionPlugin.html new file mode 100644 index 0000000..2f8fe72 --- /dev/null +++ b/docs/module-kadence_onion-OnionPlugin.html @@ -0,0 +1,697 @@ + + + + + + OnionPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

OnionPlugin

+ + + + + + + +
+ +
+ +

+ module:kadence/onion~ + + OnionPlugin +

+ +
SOCKS5 proxy plugin, wraps HTTP* transports createRequest method
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new OnionPlugin(node, optionsopt)

+ + + + + +
+ Creates the transport wrapper for using a SOCKS5 proxy +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
node + + +object + + + + + + + + + + + + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
dataDirectory + + +string + + + + + + <optional>
+ + + + + +
+ Write hidden service data + +
virtualPort + + +number + + + + + + <optional>
+ + + + + +
+ Virtual hidden service port + +
localMapping + + +string + + + + + + <optional>
+ + + + + +
+ IP/Port string of target service + +
torrcEntries + + +object + + + + + + <optional>
+ + + + + +
+ Additional torrc entries + +
passthroughLoggingEnabled + + +boolean + + + + + + <optional>
+ + + + + +
+ Passthrough tor log + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

createClearAgent() → {Agent}

+ + + + + +
+ Returns a clear text agent instance to use for the provided target +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Agent + + +
+
+ + + +
+ + + +
+ + +
+ + + +

createSecureAgent() → {Agent}

+ + + + + +
+ Returns an agent instance to use for the provided target +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Agent + + +
+
+ + + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_onion.html b/docs/module-kadence_onion.html new file mode 100644 index 0000000..f48e795 --- /dev/null +++ b/docs/module-kadence_onion.html @@ -0,0 +1,489 @@ + + + + + + kadence/onion - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/onion

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/onion"))(node, optionsopt)

+ + + + + +
+ Registers a OnionPlugin with an AbstractNode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
node + + +object + + + + + + + + + + + + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
dataDirectory + + +string + + + + + + <optional>
+ + + + + +
+ Write hidden service data + +
virtualPort + + +number + + + + + + <optional>
+ + + + + +
+ Virtual hidden service port + +
localMapping + + +string + + + + + + <optional>
+ + + + + +
+ IP/Port string of target service + +
torrcEntries + + +object + + + + + + <optional>
+ + + + + +
+ Additional torrc entries + +
passthroughLoggingEnabled + + +boolean + + + + + + <optional>
+ + + + + +
+ Passthrough tor log + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
OnionPlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_quasar-QuasarPlugin.html b/docs/module-kadence_quasar-QuasarPlugin.html new file mode 100644 index 0000000..851de00 --- /dev/null +++ b/docs/module-kadence_quasar-QuasarPlugin.html @@ -0,0 +1,1765 @@ + + + + + + QuasarPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

QuasarPlugin

+ + + + + + + +
+ +
+ +

+ module:kadence/quasar~ + + QuasarPlugin +

+ +
Implements the primary interface for the publish-subscribe system +and decorates the given node object with it's public methods
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new QuasarPlugin(node)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +

Members

+ + + +
+

neighbors

+ + + + +
+ Returns our ALPHA closest neighbors +
+ + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
neighbors + + +Array.<Bucket~contact> + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

hasNeighborSubscribedTo(topic) → {boolean}

+ + + + + +
+ Check if our neighbors are subscribed to the topic +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
topic + + +string + + + + + Topic to check subscription + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

isSubscribedTo(topic) → {boolean}

+ + + + + +
+ Check if we are subscribed to the topic +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
topic + + +string + + + + + Topic to check subscription + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

pullFilterFrom(contact, callback)

+ + + + + +
+ Requests the attenuated bloom filter from the supplied contact +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
contact + + +Bucket~contact + + + + + + +
callback + + +function + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

pullFilters(callbackopt)

+ + + + + +
+ Requests neighbor bloom filters and merges with our records +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
callback + + +function + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

pushFilters(callbackopt)

+ + + + + +
+ Notifies neighbors that our subscriptions have changed +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
callback + + +function + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

pushFilterTo(contact, callback)

+ + + + + +
+ Sends our attenuated bloom filter to the supplied contact +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
contact + + +Bucket~contact + + + + + + +
callback + + +function + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

quasarPublish(topic, contents, optionsopt, callbackopt)

+ + + + + +
+ Publishes the content to the network by selecting ALPHA contacts closest +to the node identity (or the supplied routing key). Errors if message is +unable to be delivered to any contacts. Tries to deliver to ALPHA contacts +until exhausted. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
topic + + +string + + + + + + + + + + + Identifier for subscribers + +
contents + + +object + + + + + + + + + + + Arbitrary publication payload + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
routingKey + + +string + + + + + + <optional>
+ + + + + +
+ Publish to neighbors close to this +key instead of our own identity + +
+ + +
callback + + +QuasarPlugin~quasarPublishCallback + + + + + + <optional>
+ + + + + +
+ + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

quasarSubscribe(topics, handler)

+ + + + + +
+ Publishes the content to the network +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
topics + + +string +| + +Array.<string> + + + + + Identifier for subscribers + +
handler + + +QuasarPlugin~quasarSubscribeHandler + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_quasar-QuasarRules.html b/docs/module-kadence_quasar-QuasarRules.html new file mode 100644 index 0000000..ada69cf --- /dev/null +++ b/docs/module-kadence_quasar-QuasarRules.html @@ -0,0 +1,934 @@ + + + + + + QuasarRules - Documentation + + + + + + + + + + + + + + + + + +
+ +

QuasarRules

+ + + + + + + +
+ +
+ +

+ module:kadence/quasar~ + + QuasarRules +

+ +
Implements the handlers for Quasar message types
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new QuasarRules(quasar)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
quasar + + +module:kadence/quasar~QuasarPlugin + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(static) shouldRelayPublication(request, attenuatedBloomFilter)

+ + + + + +
+ Returns a boolean indicating if we should relay the message to the contact +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
attenuatedBloomFilter + + +array + + + + + List of topic bloom filters + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

publish(request, response, next)

+ + + + + +
+ Upon receipt of a PUBLISH message, we validate it, then check if we or +our neighbors are subscribed. If we are subscribed, we execute our +handler. If our neighbors are subscribed, we relay the publication to +ALPHA random of the closest K. If our neighbors are not subscribed, we +relay the publication to a random contact +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

subscribe(request, response)

+ + + + + +
+ Upon receipt of a SUBSCRIBE message, we simply respond with a serialized +version of our attenuated bloom filter +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

update(request, response, next)

+ + + + + +
+ Upon receipt of an UPDATE message we merge the delivered attenuated bloom +filter with our own +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_quasar.html b/docs/module-kadence_quasar.html new file mode 100644 index 0000000..43ddd80 --- /dev/null +++ b/docs/module-kadence_quasar.html @@ -0,0 +1,187 @@ + + + + + + kadence/quasar - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/quasar

+ + + + + + + +
+ +
+ + + + + +
+ + + +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_rolodex-RolodexPlugin.html b/docs/module-kadence_rolodex-RolodexPlugin.html new file mode 100644 index 0000000..f136118 --- /dev/null +++ b/docs/module-kadence_rolodex-RolodexPlugin.html @@ -0,0 +1,1043 @@ + + + + + + RolodexPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

RolodexPlugin

+ + + + + + + +
+ +
+ +

+ kadence/rolodex~ + + RolodexPlugin +

+ +
Keeps track of seen contacts in a compact file so they can be used as +bootstrap nodes
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new RolodexPlugin(node, peerCacheFilePath)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
peerCacheFilePath + + +string + + + + + Path to file to use for storing peers + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

getBootstrapCandidates() → {Array.<string>}

+ + + + + +
+ Returns a list of bootstrap nodes from local profiles +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Array.<string> + + +
+
+ + +
+ urls +
+ + +
+ + + +
+ + +
+ + + +

getExternalPeerInfo(identity) → {object}

+ + + + + +
+ Returns the external peer data for the given identity +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string + + + + + Identity key for the peer + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + + +
+ + + +
+ + +
+ + + +

getInternalPeerInfo(identity) → {object}

+ + + + + +
+ Returns the internal peer data for the given identity +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string + + + + + Identity key for the peer + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + + +
+ + + +
+ + +
+ + + +

setExternalPeerInfo(identity, data) → {object}

+ + + + + +
+ Returns the external peer data for the given identity +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string + + + + + Identity key for the peer + +
data + + +object + + + + + Peer's external contact information + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + + +
+ + + +
+ + +
+ + + +

setInternalPeerInfo(identity, data) → {object}

+ + + + + +
+ Returns the internal peer data for the given identity +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string + + + + + Identity key for the peer + +
data + + +object + + + + + Our own internal peer information + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_rolodex.html b/docs/module-kadence_rolodex.html new file mode 100644 index 0000000..562f148 --- /dev/null +++ b/docs/module-kadence_rolodex.html @@ -0,0 +1,237 @@ + + + + + + kadence/rolodex - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/rolodex

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/rolodex"))(peerCacheFilePath)

+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
peerCacheFilePath + + +string + + + + + Path to file to use for storing peers + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
RolodexPlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_spartacus-SpartacusPlugin.html b/docs/module-kadence_spartacus-SpartacusPlugin.html new file mode 100644 index 0000000..5ac85a2 --- /dev/null +++ b/docs/module-kadence_spartacus-SpartacusPlugin.html @@ -0,0 +1,938 @@ + + + + + + SpartacusPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

SpartacusPlugin

+ + + + + + + +
+ +
+ +

+ module:kadence/spartacus~ + + SpartacusPlugin +

+ +
Implements the spartacus decorations to the node object
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new SpartacusPlugin(node, privateKeyopt, optionsopt)

+ + + + + +
+ Creates the plugin instance given a node and optional identity +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
node + + +KademliaNode + + + + + + + + + + + + + + +
privateKey + + +buffer + + + + + + <optional>
+ + + + + +
+ + + SECP256K1 private key + +
options + + +object + + + + + + <optional>
+ + + + + +
+ + {} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
checkPublicKeyHash + + +boolean + + + + + + <optional>
+ + + + + +
+ + true + + + + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

deserialize()

+ + + + + +
+ Parses and verifies the signature payload, then passes through to the +JsonRpcDeserializer if successful +
+ + + + + +
+ + + + + + + + + + + + +
Implements:
+
+ + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

serialize()

+ + + + + +
+ Processes with JsonRpcSerializer then signs the result and appends an +additional payload containing signature+identity information +
+ + + + + +
+ + + + + + + + + + + + +
Implements:
+
+ + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + +

setValidationPeriod(period)

+ + + + + +
+ Sets the validation period for nodes +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
period + + +number + + + + + Milliseconds to honor a proven contact response + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

validate(node, request, response, next)

+ + + + + +
+ Checks if the sender is addressable at the claimed contact information +and cross checks signatures between the original sender and the node +addressed. This is intended to prevent reflection attacks and general +DDoS via spam. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
request + + +AbstractNode~request + + + + + + +
response + + +AbstractNode~response + + + + + + +
next + + +AbstractNode~next + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_spartacus.html b/docs/module-kadence_spartacus.html new file mode 100644 index 0000000..66815c3 --- /dev/null +++ b/docs/module-kadence_spartacus.html @@ -0,0 +1,263 @@ + + + + + + kadence/spartacus - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/spartacus

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/spartacus"))(priv, opts)

+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
priv + + +string + + + + + Private key + +
opts + + +object + + + + + Plugin options + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
SpartacusPlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_traverse-NATPMPStrategy.html b/docs/module-kadence_traverse-NATPMPStrategy.html new file mode 100644 index 0000000..947f08e --- /dev/null +++ b/docs/module-kadence_traverse-NATPMPStrategy.html @@ -0,0 +1,530 @@ + + + + + + NATPMPStrategy - Documentation + + + + + + + + + + + + + + + + + +
+ +

NATPMPStrategy

+ + + + + + + +
+ +
+ +

+ module:kadence/traverse~ + + NATPMPStrategy +

+ +
Uses NAT-PMP to attempt port forward on gateway device
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new NATPMPStrategy(optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
publicPort + + +number + + + + + + <optional>
+ + + + + +
+ + contact.port + + + Port number to map + +
mappingTtl + + +number + + + + + + <optional>
+ + + + + +
+ + 0 + + + TTL for port mapping on router + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

exec(node, callback)

+ + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
callback + + +function + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_traverse-ReverseTunnelStrategy.html b/docs/module-kadence_traverse-ReverseTunnelStrategy.html new file mode 100644 index 0000000..c502eb7 --- /dev/null +++ b/docs/module-kadence_traverse-ReverseTunnelStrategy.html @@ -0,0 +1,660 @@ + + + + + + ReverseTunnelStrategy - Documentation + + + + + + + + + + + + + + + + + +
+ +

ReverseTunnelStrategy

+ + + + + + + +
+ +
+ +

+ module:kadence/traverse~ + + ReverseTunnelStrategy +

+ +
Uses a secure reverse HTTPS tunnel via the Diglet package to traverse NAT. +This requires a running Diglet server on the internet. By default, this +plugin will use a test server operated by bookchin, but this may not be +reliable or available. It is highly recommended to deploy your own Diglet +server and configure your nodes to use them instead. +There is detailed documentation +on deploying a Diglet server at the project page.
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new ReverseTunnelStrategy(optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
remoteAddress + + +string + + + + + + <optional>
+ + + + + +
+ + tunnel.bookch.in + + + Diglet server address + +
remotePort + + +number + + + + + + <optional>
+ + + + + +
+ + 8443 + + + Diglet server port + +
privateKey + + +buffer + + + + + + <optional>
+ + + + + +
+ + + SECP256K1 private key if using spartacus + +
secureLocalConnection + + +boolean + + + + + + <optional>
+ + + + + +
+ + false + + + Set to true if using HTTPSTransport + +
verboseLogging + + +boolean + + + + + + <optional>
+ + + + + +
+ + false + + + Useful for debugging + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

exec(node, callback)

+ + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
callback + + +function + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_traverse-TraversePlugin.html b/docs/module-kadence_traverse-TraversePlugin.html new file mode 100644 index 0000000..ae1da2f --- /dev/null +++ b/docs/module-kadence_traverse-TraversePlugin.html @@ -0,0 +1,255 @@ + + + + + + TraversePlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

TraversePlugin

+ + + + + + + +
+ +
+ +

+ module:kadence/traverse~ + + TraversePlugin +

+ +
Establishes a series of NAT traversal strategies to execute before +AbstractNode#listen
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new TraversePlugin(node, strategies)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
strategies + + +Array.<module:kadence/traverse~TraverseStrategy> + + + + + + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_traverse-TraverseStrategy.html b/docs/module-kadence_traverse-TraverseStrategy.html new file mode 100644 index 0000000..d065154 --- /dev/null +++ b/docs/module-kadence_traverse-TraverseStrategy.html @@ -0,0 +1,333 @@ + + + + + + TraverseStrategy - Documentation + + + + + + + + + + + + + + + + + +
+ +

TraverseStrategy

+ + + + + + + +
+ +
+ +

+ module:kadence/traverse~ + + TraverseStrategy +

+ + +
+ +
+
+ + +
+ + + +

new TraverseStrategy()

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

exec(node, callback)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
callback + + +function + + + + + Called on travere complete or failed + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_traverse-UPNPStrategy.html b/docs/module-kadence_traverse-UPNPStrategy.html new file mode 100644 index 0000000..c7cf75c --- /dev/null +++ b/docs/module-kadence_traverse-UPNPStrategy.html @@ -0,0 +1,530 @@ + + + + + + UPNPStrategy - Documentation + + + + + + + + + + + + + + + + + +
+ +

UPNPStrategy

+ + + + + + + +
+ +
+ +

+ module:kadence/traverse~ + + UPNPStrategy +

+ +
Uses UPnP to attempt port forward on gateway device
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new UPNPStrategy(optionsopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
options + + +object + + + + + + <optional>
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
publicPort + + +number + + + + + + <optional>
+ + + + + +
+ + contact.port + + + Port number to map + +
mappingTtl + + +number + + + + + + <optional>
+ + + + + +
+ + 0 + + + TTL for mapping on router + +
+ + +
+ + + + + + + + + + + + + + + + +
+ +
+ + +

Extends

+ + + + + + + + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

exec(node, callback)

+ + + + + + + + + +
+ + + + + + + + +
Overrides:
+
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
node + + +KademliaNode + + + + + + +
callback + + +function + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_traverse.html b/docs/module-kadence_traverse.html new file mode 100644 index 0000000..43e19b0 --- /dev/null +++ b/docs/module-kadence_traverse.html @@ -0,0 +1,280 @@ + + + + + + kadence/traverse - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/traverse

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/traverse"))(strategies)

+ + + + + +
+ Registers a module:kadence/traverse~TraversePlugin with an +AbstractNode. Strategies are attempted in the order they are +defined. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
strategies + + +Array.<module:kadence/traverse~TraverseStrategy> + + + + + + +
+ + + + + + + + + + + + + + + + +
+
Example
+ +

Proper Configuration

+ +
const node = new kadence.KademliaNode(node_options);
+const keys = node.plugin(kadence.spartacus(key_options));
+
+node.plugin(kadence.traverse([
+  new kadence.traverse.UPNPStrategy({
+    publicPort: 8080,
+    mappingTtl: 0
+  }),
+  new kadence.traverse.NATPMPStrategy({
+    publicPort: 8080,
+    mappingTtl: 0
+  }),
+  new kadence.traverse.ReverseTunnelStrategy({
+    remoteAddress: 'my.diglet.server',
+    remotePort: 8443,
+    privateKey: keys.privateKey,
+    secureLocalConnection: false,
+    verboseLogging: false
+  })
+]));
+
+node.listen(node.contact.port);
+ +
+ +
+ + +
+ + + + + + +

Classes

+ +
+
NATPMPStrategy
+
+ +
ReverseTunnelStrategy
+
+ +
TraversePlugin
+
+ +
TraverseStrategy
+
+ +
UPNPStrategy
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_trust-TrustPlugin.html b/docs/module-kadence_trust-TrustPlugin.html new file mode 100644 index 0000000..78d56de --- /dev/null +++ b/docs/module-kadence_trust-TrustPlugin.html @@ -0,0 +1,1056 @@ + + + + + + TrustPlugin - Documentation + + + + + + + + + + + + + + + + + +
+ +

TrustPlugin

+ + + + + + + +
+ +
+ +

+ module:kadence/trust~ + + TrustPlugin +

+ +
Handles user-defined rules for allowing and preventing the processing of +messages from given identities
+ + +
+ +
+
+ + +
+ + +

Constructor

+ + +

new TrustPlugin(policies, modeopt)

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
policies + + +Array.<module:kadence/trust~TrustPlugin~policy> + + + + + + + + + + + + + + +
mode + + +number + + + + + + <optional>
+ + + + + +
+ + TrustPlugin.MODE_BLACKLIST + + + Blacklist or whitelist + +
+ + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + +

Members

+ + + +
+

(static) MODE_BLACKLIST

+ + + + +
+ Mode flag passed to TrustPlugin to place into blacklist mode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + +
+

(static) MODE_WHITELIST

+ + + + +
+ Mode flag passed to TrustPlugin to place into whitelist mode +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

addTrustPolicy(policy) → {TrustPlugin}

+ + + + + +
+ Adds a new trust policy +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
policy + + +module:kadence/trust~TrustPlugin~policy + + + + + + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +TrustPlugin + + +
+
+ + + +
+ + + +
+ + +
+ + + +

getTrustPolicy(identity) → {module:kadence/trust~TrustPlugin~policy|null}

+ + + + + +
+ Returns the trust policy for the given identity +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string +| + +buffer + + + + + Identity key for the policy + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +module:kadence/trust~TrustPlugin~policy +| + +null + + +
+
+ + + +
+ + + +
+ + +
+ + + +

removeTrustPolicy(identity) → {TrustPlugin}

+ + + + + +
+ Removes an existing trust policy +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string +| + +buffer + + + + + Trust policy to remove + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +TrustPlugin + + +
+
+ + + +
+ + + +
+ + + + +

Type Definitions

+ + + +
+

policy

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
identity + + +string +| + +buffer + + + + Node identity key
methods + + +Array.<string> + + + + Methods, wildcard (*) supported for all
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
+ + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_trust.html b/docs/module-kadence_trust.html new file mode 100644 index 0000000..e21c70d --- /dev/null +++ b/docs/module-kadence_trust.html @@ -0,0 +1,295 @@ + + + + + + kadence/trust - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/trust

+ + + + + + + +
+ +
+ + + + + +
+ +
+
+ + + + + +
+ + + +

(require("kadence/trust"))(policies, modeopt)

+ + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
policies + + +Array.<module:kadence/trust~TrustPlugin~policy> + + + + + + + + + + + + + + +
mode + + +number + + + + + + <optional>
+ + + + + +
+ + TrustPlugin.MODE_BLACKLIST + + + Blacklist or whitelist + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + + + + +

Classes

+ +
+
TrustPlugin
+
+
+ + + + + + + + + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_utils.html b/docs/module-kadence_utils.html new file mode 100644 index 0000000..babdfd1 --- /dev/null +++ b/docs/module-kadence_utils.html @@ -0,0 +1,4286 @@ + + + + + + kadence/utils - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/utils

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + + + +

Methods

+ + + +
+ + + +

(static) compareKeyBuffers(b1, b2) → {number}

+ + + + + +
+ Compare two buffers for sorting +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
b1 + + +buffer + + + + + Buffer to compare + +
b2 + + +buffer + + + + + Buffer to compare + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) eqsolve(input) → {Promise.<EquihashProof>}

+ + + + + +
+ Performs an equihash solution using defaults +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
input + + +buffer + + + + + Input hash to solve + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +Promise.<EquihashProof> + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) eqverify(input, proof) → {boolean}

+ + + + + +
+ Perform an equihash proof verification +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
input + + +buffer + + + + + Input hash for proof + +
proof + + +buffer + + + + + Equihash proof to verify + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) generatePrivateKey() → {buffer}

+ + + + + +
+ Generates a private key +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +buffer + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) getBucketIndex(referenceKey, foreignKey) → {number}

+ + + + + +
+ Calculate the index of the bucket that key would belong to +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
referenceKey + + +string + + + + + Key to compare + +
foreignKey + + +string + + + + + Key to compare + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +number + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) getContactURL(contact) → {string}

+ + + + + +
+ Returns a stringified URL from the supplied contact object +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
contact + + +Bucket~contact + + + + + + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) getDistance(key1, key2) → {buffer}

+ + + + + +
+ Calculate the distance between two keys +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key1 + + +string + + + + + Identity key to compare + +
key2 + + +string + + + + + Identity key to compare + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +buffer + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) getPowerOfTwoBufferForIndex(referenceKey, bucketIndex) → {buffer}

+ + + + + +
+ Returns a buffer with a power-of-two value given a bucket index +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
referenceKey + + +string +| + +buffer + + + + + Key to find next power of two + +
bucketIndex + + +number + + + + + Bucket index for key + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +buffer + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) getRandomBufferInBucketRange(referenceKey, index)

+ + + + + +
+ Generate a random number within the bucket's range +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
referenceKey + + +buffer + + + + + Key for bucket distance reference + +
index + + +number + + + + + Bucket index for random buffer selection + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(static) getRandomKeyBuffer() → {buffer}

+ + + + + +
+ Returns a random valid key/identity as a buffer +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +buffer + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) getRandomKeyString() → {string}

+ + + + + +
+ Returns a random valid key/identity as a string +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) hash160(input)

+ + + + + +
+ Returns the RMD-160 hash of the input +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
input + + +buffer + + + + + Data to hash + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(static) hash256(input)

+ + + + + +
+ Returns the SHA-256 hash of the input +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
input + + +buffer + + + + + Data to hash + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(static) isCompatibleVersion(version) → {boolean}

+ + + + + +
+ Returns whether or not the supplied semver tag is compatible +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
version + + +string + + + + + The semver tag from the contact + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) isHexaString(str) → {boolean}

+ + + + + +
+ Tests if a string is valid hex +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
str + + +string + + + + + + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) isValidContact(contact, loopback) → {boolean}

+ + + + + +
+ Determines if the supplied contact is valid +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
contact + + +Bucket~contact + + + + + The contact information for a given peer + +
loopback + + +boolean + + + + + Allows contacts that are localhost + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) keyBufferIsValid(key) → {boolean}

+ + + + + +
+ Determines if the given buffer key is valid +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +buffer + + + + + Node ID or item key + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) keyStringIsValid(key) → {boolean}

+ + + + + +
+ Determines if the given string key is valid +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
key + + +string + + + + + Node ID or item key + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) parseContactURL() → {object}

+ + + + + +
+ Returns a parsed contact object from a URL +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +object + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) preventConvoy(func, maxtimeopt) → {function}

+ + + + + +
+ Wraps the supplied function in a pseudo-random length timeout to help +prevent convoy effects. These occur when a number of processes need to use +a resource in turn. There is a tendency for such bursts of activity to +drift towards synchronization, which can be disasterous. In Kademlia all +nodes are requird to republish their contents every hour (T_REPLICATE). A +convoy effect might lead to this being synchronized across the network, +which would appear to users as the network dying every hour. The default +timeout will be between 0 and 30 minutes unless specified. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDescription
func + + +function + + + + + + + + + + + Function to wrap to execution later + +
maxtime + + +number + + + + + + <optional>
+ + + + + +
+ Maximum timeout + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +function + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) satisfiesDifficulty(buffer, difficulty) → {boolean}

+ + + + + +
+ Returns a boolean indicating if the supplied buffer meets the given +difficulty requirement +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
buffer + + +buffer + + + + + Buffer to check difficulty + +
difficulty + + +number + + + + + Number of leading zeroes + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +boolean + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) toBinaryStringFromBuffer(buffer) → {string}

+ + + + + +
+ Converts a buffer to a string representation of binary +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
buffer + + +buffer + + + + + Byte array to convert to binary string + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) toPublicKeyHash(publicKey) → {buffer}

+ + + + + +
+ Takes a public key are returns the identity +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
publicKey + + +buffer + + + + + Raw public key bytes + +
+ + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +buffer + + +
+
+ + + +
+ + + +
+ + +
+ + + +

(static) validateLogger(logger)

+ + + + + +
+ Validates the given object is a logger +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
logger + + +AbstractNode~logger + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(static) validateStorageAdapter(storageAdapter)

+ + + + + +
+ Validates the given object is a storage adapter +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
storageAdapter + + +AbstractNode~storage + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + +
+ + + +

(static) validateTransport(transport)

+ + + + + +
+ Validates the given object is a transport +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
transport + + +AbstractNode~transport + + + + + + +
+ + + + + + + + + + + + + + + + +
+ + + + +

Type Definitions

+ + + +
+

EquihashProof

+ + + + + + + + +
Properties:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
n + + +number + + + +
k + + +number + + + +
nonce + + +number + + + +
value + + +buffer + + + +
+ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +object + + +
  • +
+ + + + + +
+ + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/module-kadence_version.html b/docs/module-kadence_version.html new file mode 100644 index 0000000..7b67183 --- /dev/null +++ b/docs/module-kadence_version.html @@ -0,0 +1,352 @@ + + + + + + kadence/version - Documentation + + + + + + + + + + + + + + + + + +
+ +

kadence/version

+ + + + + + + +
+ +
+ + + +
+ +
+
+ + + + + +
+ + + + + + + + + + + + +

Members

+ + + +
+

(inner, constant) protocol :string

+ + + + +
+ The supported protocol version +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +string + + +
  • +
+ + + + + +
+ + + +
+

(inner, constant) software :string

+ + + + +
+ The current software version +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + +
Type:
+
    +
  • + +string + + +
  • +
+ + + + + +
+ + + + + +

Methods

+ + + +
+ + + +

(static) toString() → {string}

+ + + + + +
+ Returns human readable string of versions +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+
Returns:
+ + + +
+
+ Type: +
+
+ +string + + +
+
+ + + +
+ + + +
+ + + + + + +
+ +
+ + + + +
+ +
+ + + + + + + \ No newline at end of file diff --git a/docs/node-abstract.js.html b/docs/node-abstract.js.html new file mode 100644 index 0000000..628c848 --- /dev/null +++ b/docs/node-abstract.js.html @@ -0,0 +1,528 @@ + + + + + + node-abstract.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

node-abstract.js

+ + + + + + + +
+
+
'use strict';
+
+const uuid = require('uuid');
+const async = require('async');
+const assert = require('assert');
+const bunyan = require('bunyan');
+const merge = require('merge');
+const constants = require('./constants');
+const utils = require('./utils');
+const { EventEmitter } = require('events');
+const RoutingTable = require('./routing-table');
+const Messenger = require('./messenger');
+const ErrorRules = require('./rules-errors');
+
+
+/**
+ * @typedef {object} AbstractNode~logger
+ * @property {function} debug - Passed string of debug information
+ * @property {function} info - Passed string of general information
+ * @property {function} warn - Passed string of warnings
+ * @property {function} error - Passed string of error message
+ */
+
+/**
+ * @typedef {object} AbstractNode~transport
+ * @property {function} read - Returns raw message buffer if available
+ * @property {function} write - Passed raw message buffer
+ */
+
+/**
+ * @typedef {object} AbstractNode~storage
+ * @description Implements a subset of the LevelUP interface
+ * @property {function} get
+ * @property {function} put
+ * @property {function} del
+ * @property {function} createReadStream
+ */
+
+/**
+ * @typedef AbstractNode~request
+ * @property {array} contact - Peer who sent this request
+ * @property {string} contact.0 - Peer's node identity
+ * @property {object} contact.1 - Peer's contact information (varies by plugin)
+ * @property {array|object} params - Method parameters (varies by method)
+ * @property {string} method - Method name being called
+ */
+
+/**
+ * @typedef AbstractNode~response
+ * @property {AbstractNode~responseSend} send
+ * @property {AbstractNode~responseError} error
+ */
+
+/**
+ * @typedef {function} AbstractNode~next
+ * @param {error|null} error - Indicates to exit the middleware stack
+ */
+
+/**
+ * @typedef AbstractNode~sendError
+ * @property {string} message - Error description
+ * @property {string} type - Error type
+ * @property {object} request - Request the error is from
+ * @property {string} request.id - Message id
+ * @property {array} request.params - Parameters sent
+ * @property {Bucket~contact} request.target - Contact message was for
+ * @property {string} request.method - RPC method in message
+ */
+
+/**
+ * @method AbstractNode~responseSend
+ * @param {array|object} results - Result parameters to respond with
+ */
+
+/**
+ * @method AbstractNode~responseError
+ * @param {string} errorMessage - Text describing the error encountered
+ * @param {number} [errorCode] - Error code
+ */
+
+/**
+ * Represents a network node
+ */
+class AbstractNode extends EventEmitter {
+
+  /**
+   * Join event is triggered when the routing table is no longer empty
+   * @event AbstractNode#join
+   */
+
+  /**
+   * Error event fires when a critical failure has occurred; if no handler is
+   * specified, then it will throw
+   * @event AbstractNode#error
+   * @type {Error}
+   */
+
+  static get DEFAULTS() {
+    return {
+      logger: bunyan.createLogger({ name: 'kadence' }),
+      identity: utils.getRandomKeyBuffer(),
+      transport: null,
+      storage: null,
+      messenger: new Messenger(),
+      contact: {}
+    };
+  }
+
+  static validate(options) {
+    if (typeof options.identity === 'string') {
+      options.identity = Buffer.from(options.identity, 'hex');
+    }
+
+    utils.validateStorageAdapter(options.storage);
+    utils.validateLogger(options.logger);
+    utils.validateTransport(options.transport);
+    assert.ok(utils.keyBufferIsValid(options.identity), 'Invalid identity');
+  }
+
+  /**
+   * Contructs the primary interface for a kad node
+   * @constructor
+   * @param {object} options
+   * @param {AbstractNode~transport} options.transport - See {@tutorial transport-adapters}
+   * @param {buffer} options.identity - See {@tutorial identities}
+   * @param {Bucket~contact} options.contact - See {@tutorial identities}
+   * @param {AbstractNode~storage} options.storage - See {@tutorial storage-adapters}
+   * @param {AbstractNode~logger} [options.logger]
+   * @param {Messenger} [options.messenger] - See {@tutorial messengers}
+   */
+  constructor(options) {
+    AbstractNode.validate(options = merge(AbstractNode.DEFAULTS, options));
+    super();
+
+    this._middlewares = { '*': [] };
+    this._errors = { '*': [] };
+    this._pending = new Map();
+
+    this.rpc = options.messenger;
+    this.transport = options.transport;
+    this.storage = options.storage;
+    this.identity = options.identity;
+    this.contact = options.contact;
+    this.logger = options.logger;
+    this.router = new RoutingTable(this.identity);
+
+    this._init();
+  }
+
+  /**
+   * Establishes listeners and creates the message pipeline
+   * @private
+   */
+  _init() {
+    this.transport.on('error', (err) => {
+      this.logger.warn(err.message.toLowerCase());
+      if (err.dispose && this._pending.get(err.dispose)) {
+        const pending = this._pending.get(err.dispose);
+        err.type = 'TIMEOUT';
+        pending.handler(err);
+        this._pending.delete(err.dispose);
+      }
+    });
+
+    this.transport.on('data', data => {
+      this.rpc.deserializer.create()
+        .once('error', err => this.logger.warn(err.message.toLowerCase()))
+        .once('data', data => this._process(data))
+        .write(data);
+    });
+
+    setInterval(() => this._timeout(), constants.T_RESPONSETIMEOUT);
+  }
+
+  /**
+   * Processes deserialized messages
+   * @private
+   */
+  _process([message, contact]) {
+    /* eslint complexity: [2, 8] */
+    this._updateContact(...contact.payload.params);
+
+    // NB: If we are receiving a request, then pass it through the middleware
+    // NB: stacks to process it
+    if (message.type === 'request') {
+      return this.receive(
+        merge({}, message.payload, { contact: contact.payload.params }),
+        {
+          send: (data) => {
+            this.rpc.serializer.create()
+              .once('data', data => this.transport.write(data))
+              .once('error', err => this.logger.warn(err.message.toLowerCase()))
+              .write([
+                merge({ id: message.payload.id }, { result: data }),
+                [this.identity.toString('hex'), this.contact],
+                contact.payload.params
+              ]);
+          },
+          error: (msg, code = -32000) => {
+            this.rpc.serializer.create()
+              .once('data', data => this.transport.write(data))
+              .once('error', err => this.logger.warn(err.message.toLowerCase()))
+              .write([
+                merge({ id: message.payload.id }, {
+                  error: { message: msg, code }
+                }),
+                [this.identity.toString('hex'), this.contact],
+                contact.payload.params
+              ]);
+          }
+        }
+      );
+    }
+
+    // NB: If we aren't expecting this message, just throw it away
+    if (!this._pending.has(message.payload.id)) {
+      return this.logger.warn(
+        `received late or invalid response from ${contact.payload.params[0]}`
+      );
+    }
+
+    // NB: Check to make sure that the response comes from the identity
+    // NB: that the request was origninally intended for, unless the message
+    // NB: was sent to a null identity (such as during bootstrapping)
+    const { handler, fingerprint } = this._pending.get(message.payload.id);
+    const nullFingerprint = Buffer.alloc(constants.B / 8, 0).toString('hex');
+    const msgSentToNullFingerprint = fingerprint === nullFingerprint;
+    const fingerprintsMatch = fingerprint === contact.payload.params[0];
+
+    if (!msgSentToNullFingerprint && !fingerprintsMatch) {
+      handler(new Error(
+        'Response fingerprint differs from request destination'
+      ), null);
+      this._pending.delete(message.payload.id);
+      return;
+    }
+
+    // NB: Otherwise, check if we are waiting on a response to a pending
+    // NB: message and fire the result handler
+    const handlerArgs = [
+      (message.type === 'error'
+        ? new Error(message.payload.error.message)
+        : null),
+      (message.type === 'success'
+        ? message.payload.result
+        : null)
+    ];
+
+    handler(...handlerArgs);
+    this._pending.delete(message.payload.id);
+  }
+
+  /**
+   * Enumerates all pending handlers and fires them with a timeout error if
+   * they have been pending too long
+   * @private
+   */
+  _timeout() {
+    let now = Date.now();
+    let err = new Error('Timed out waiting for response');
+
+    err.type = 'TIMEOUT';
+
+    for (let [id, entry] of this._pending.entries()) {
+      if (entry.timestamp + constants.T_RESPONSETIMEOUT >= now) {
+        continue;
+      }
+
+      entry.handler(err);
+      this._pending.delete(id);
+    }
+  }
+
+  /**
+   * Adds the given contact to the routing table
+   * @private
+   */
+  _updateContact(identity, contact) {
+    if (identity === this.identity.toString('hex')) {
+      return null;
+    } else {
+      return this.router.addContactByNodeId(identity, contact);
+    }
+  }
+
+  /**
+   * Validates the contact tuple
+   * @private
+   */
+  _validateContact(target) {
+    return (Array.isArray(target) && target[0] && target[1])
+      && (this.transport._validate ? this.transport._validate(target) : true);
+  }
+
+  /**
+   * Sends the [method, params] to the contact and executes the handler on
+   * response or timeout
+   * @param {string} method - RPC method name
+   * @param {object|array} params - RPC parameters
+   * @param {Bucket~contact} contact - Destination address information
+   * @param {AbstractNode~sendCallback} [callback]
+   * @returns {Promise<object|array,Error>}
+   */
+  send(method, params, target, handler) {
+    if (typeof handler === 'function') {
+      return this._send(method, params, target).then(function() {
+        handler(null, ...arguments);
+      }, handler);
+    } else {
+      return this._send(method, params, target);
+    }
+  }
+  /**
+   * @callback AbstractNode~sendCallback
+   * @param {null|AbstractNode~sendError} error
+   * @param {object|array|string|number} result
+   */
+
+  /**
+   * @private
+   */
+  _send(method, params, target) {
+    return new Promise((resolve, reject) => {
+      const id = uuid();
+      const timestamp = Date.now();
+
+      if (!this._validateContact(target)) {
+        return reject(new Error('Refusing to send message to invalid contact'));
+      }
+
+      target[0] = target[0].toString('hex'); // NB: Allow identity to be a buffer
+
+      function wrapped(err, ...params) {
+        if (err) {
+          err.request = { id, method, params, target };
+          return reject(err);
+        }
+
+        resolve(...params);
+      }
+
+      this._pending.set(id, {
+        handler: wrapped,
+        timestamp,
+        fingerprint: target[0]
+      });
+      this.rpc.serializer.create()
+        .once('error', err => reject(err))
+        .once('data', data => this.transport.write(data))
+        .write([
+          { id, method, params },
+          [this.identity.toString('hex'), this.contact],
+          target
+        ]);
+    });
+  }
+
+  /**
+   * Accepts an arbitrary function that receives this node as context
+   * for mounting protocol handlers and extending the node with other
+   * methods
+   * @param {function} plugin - {@tutorial plugins}
+   */
+  plugin(func) {
+    assert(typeof func === 'function', 'Invalid plugin supplied');
+    return func(this);
+  }
+
+  /**
+   * Mounts a message handler route for processing incoming RPC messages
+   * @param {string} [method] - RPC method name to route through
+   * @param {AbstractNode~middleware} middleware
+   */
+  use(method, middleware) {
+    if (typeof method === 'function') {
+      middleware = method;
+      method = '*';
+    }
+
+    // NB: If middleware function takes 4 arguments, it is an error handler
+    const type = middleware.length === 4 ? '_errors' : '_middlewares';
+    const stack = this[type][method] = this[type][method] || [];
+
+    stack.push(middleware);
+  }
+  /**
+   * @callback AbstractNode~middleware
+   * @param {error} [error] - Error object resulting from a middleware
+   * @param {AbstractNode~request} request - The incoming message object
+   * @param {AbstractNode~response} response - The outgoing response object
+   * @param {AbstractNode~next} next - Call to proceed to next middleware
+   */
+
+  /**
+   * Passes through to the {@link AbstractNode~transport}
+   */
+  listen() {
+    let handlers = new ErrorRules(this);
+
+    this.use(handlers.methodNotFound.bind(handlers));
+    this.use(handlers.internalError.bind(handlers));
+
+    this.transport.listen(...arguments);
+  }
+
+  /**
+   * Processes a the given arguments by sending them through the appropriate
+   * middleware stack
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   */
+  receive(request, response) {
+    const self = this;
+    const { method } = request;
+
+    // NB: First pass the the arguments through the * middleware stack
+    // NB: Then pass the arguments through the METHOD middleware stack
+    function processRequest(callback) {
+      async.series([
+        (next) => self._middleware('*', [request, response], next),
+        (next) => self._middleware(method, [request, response], next)
+      ], callback)
+    }
+
+    // NB: Repeat the same steps for the error stack
+    function handleErrors(err) {
+      async.series([
+        (next) => self._error('*', [err, request, response], next),
+        (next) => self._error(method, [err, request, response], next)
+      ]);
+    }
+
+    processRequest(handleErrors);
+  }
+
+  /**
+   * Send the arguments through the stack type
+   * @private
+   */
+  _stack(type, method, args, callback) {
+    async.eachSeries(this[type][method] || [], (middleware, done) => {
+      try {
+        middleware(...args, done);
+      } catch (err) {
+        done(err);
+      }
+    }, callback);
+  }
+
+  /**
+   * Send the arguments through the middleware
+   * @private
+   */
+  _middleware() {
+    this._stack('_middlewares', ...arguments);
+  }
+
+  /**
+   * Send the arguments through the error handlers
+   * @private
+   */
+  _error() {
+    this._stack('_errors', ...arguments);
+  }
+
+}
+
+module.exports = AbstractNode;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/node-kademlia.js.html b/docs/node-kademlia.js.html new file mode 100644 index 0000000..2445552 --- /dev/null +++ b/docs/node-kademlia.js.html @@ -0,0 +1,713 @@ + + + + + + node-kademlia.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

node-kademlia.js

+ + + + + + + +
+
+
'use strict';
+
+const async = require('async');
+const { Writable: WritableStream } = require('stream');
+const constants = require('./constants');
+const { knuthShuffle: shuffle } = require('knuth-shuffle');
+const utils = require('./utils');
+const AbstractNode = require('./node-abstract');
+const KademliaRules = require('./rules-kademlia');
+const ContactList = require('./contact-list');
+const MetaPipe = require('metapipe');
+
+
+/**
+ * Extends {@link AbstractNode} with Kademlia-specific rules
+ * @class
+ * @extends {AbstractNode}
+ */
+class KademliaNode extends AbstractNode {
+
+  /**
+   * @typedef {object} KademliaNode~entry
+   * @property {string|object|array} value - The primary entry value
+   * @property {string} publisher - Node identity of the original publisher
+   * @property {number} timestamp - Last update/replicate time
+   */
+
+  /**
+   * @constructor
+   */
+  constructor(options) {
+    super(options);
+
+    this._lookups = new Map(); // NB: Track the last lookup time for buckets
+    this._pings = new Map();
+    this._updateContactQueue = async.queue(
+      (task, cb) => this._updateContactWorker(task, cb),
+      1
+    );
+
+    this.replicatePipeline = new MetaPipe({ objectMode: true });
+    this.expirePipeline = new MetaPipe({ objectMode: true });
+  }
+
+  /**
+   * Adds the kademlia rule handlers before calling super#listen()
+   */
+  listen() {
+    let handlers = new KademliaRules(this);
+
+    this.use('PING', handlers.ping.bind(handlers));
+    this.use('STORE', handlers.store.bind(handlers));
+    this.use('FIND_NODE', handlers.findNode.bind(handlers));
+    this.use('FIND_VALUE', handlers.findValue.bind(handlers));
+
+    setInterval(
+      utils.preventConvoy(() => this.refresh(0)),
+      constants.T_REFRESH
+    );
+    setInterval(
+      utils.preventConvoy(() => this.replicate(() => this.expire())),
+      constants.T_REPLICATE
+    );
+
+    super.listen(...arguments);
+  }
+
+  /**
+   * Inserts the given contact into the routing table and uses it to perform
+   * a {@link KademliaNode#iterativeFindNode} for this node's identity,
+   * then refreshes all buckets further than it's closest neighbor, which will
+   * be in the occupied bucket with the lowest index
+   * @param {Bucket~contact} peer - Peer to bootstrap from
+   * @param {function} [joinListener] - Function to set as join listener
+   * @returns {Promise}
+   */
+  join(peer, callback) {
+    if (typeof callback === 'function') {
+      return this._join(peer).then(function() {
+        callback(null, ...arguments);
+      }, callback);
+    } else {
+      return this._join(peer);
+    }
+  }
+
+  /**
+   * @private
+   */
+  _join([identity, contact]) {
+    return new Promise((resolve, reject) => {
+      this.router.addContactByNodeId(identity, contact);
+      async.series([
+        (next) => this.iterativeFindNode(this.identity.toString('hex'), next),
+        (next) => this.refresh(this.router.getClosestBucket() + 1, next)
+      ], (err) => {
+        if (err) {
+          reject(err);
+        } else {
+          resolve();
+        }
+      });
+    });
+  }
+
+  /**
+   * Sends a PING message to the supplied contact, resolves with latency
+   * @param {Bucket~contact} peer
+   * @param {KademliaNode~pingCallback} [callback]
+   * @returns {Promise<number>}
+   */
+  ping(contact, callback) {
+    if (typeof callback ==='function') {
+      return this._ping(contact).then(function() {
+        callback(null, ...arguments);
+      }, callback);
+    } else {
+      return this._ping(contact);
+    }
+  }
+  /**
+   * @callback KademliaNode~pingCallback
+   * @param {error|null} error
+   * @param {number} latency - Milliseconds before response received
+   */
+
+  /**
+   * @private
+   */
+  _ping(contact) {
+    return new Promise((resolve, reject) => {
+      const start = Date.now();
+
+      this.send('PING', [], contact, (err) => {
+        if (err) {
+          return reject(err);
+        }
+
+        resolve(Date.now() - start);
+      });
+    });
+  }
+
+  /**
+   * @private
+   */
+  _createStorageItem(value) {
+    const keys = Object.keys(value);
+    const alreadyHasMetadata = keys.includes('value') &&
+                               keys.includes('publisher') &&
+                               keys.includes('timestamp');
+
+    if (alreadyHasMetadata) {
+      value.timestamp = Date.now();
+      value.publisher = value.publisher.toString('hex');
+      return value;
+    }
+
+    return {
+      value: value,
+      timestamp: Date.now(),
+      publisher: this.identity.toString('hex')
+    };
+  }
+
+  /**
+   * Performs a {@link KademliaNode#iterativeFindNode} to collect K contacts
+   * nearest to the given key, sending a STORE message to each of them.
+   * @param {buffer|string} key - Key to store data under
+   * @param {buffer|string|object} value - Value to store by key
+   * @param {KademliaNode~iterativeStoreCallback} callback
+   * @returns {Promise<number>}
+   */
+  iterativeStore(key, value, callback) {
+    if (typeof callback === 'function') {
+      return this._iterativeStore(key, value).then(function() {
+        callback(null, ...arguments);
+      }, callback);
+    } else {
+      return this._iterativeStore(key, value);
+    }
+  }
+  /**
+   * Note that if there is a protocol/validation error, you will not receive
+   * it as an error in the callback. Be sure to also check that stored > 0 as
+   * part of error handling here.
+   * @callback KademliaNode~iterativeStoreCallback
+   * @param {error|null} error
+   * @param {number} stored - Total nodes who stored the pair
+   */
+
+  /**
+   * @private
+   */
+  _iterativeStore(key, value) {
+    return new Promise((resolve, reject) => {
+      key = key.toString('hex');
+      let stored = 0;
+
+      const createStoreRpc = (target) => {
+        return ['STORE', [key, this._createStorageItem(value)], target];
+      };
+
+      const dispatchStoreRpcs = (contacts, callback) => {
+        async.eachLimit(contacts, constants.ALPHA, (target, done) => {
+          this.send(...createStoreRpc(target), (err) => {
+            stored = err ? stored : stored + 1;
+            done();
+          });
+        }, callback);
+      };
+
+      async.waterfall([
+        (next) => this.iterativeFindNode(key, next),
+        (contacts, next) => dispatchStoreRpcs(contacts, next),
+        (next) => {
+          this.storage.put(key, this._createStorageItem(value), {
+            valueEncoding: 'json'
+          }, next);
+        }
+      ], () => {
+        if (stored === 0) {
+          return reject(new Error('Failed to stored entry with peers'));
+        }
+        resolve(stored);
+      });
+    });
+  }
+
+  /**
+   * Basic kademlia lookup operation that builds a set of K contacts closest
+   * to the given key
+   * @param {buffer|string} key - Reference key for node lookup
+   * @param {KademliaNode~iterativeFindNodeCallback} [callback]
+   * @returns {Promise<Bucket~contact[]>}
+   */
+  iterativeFindNode(key, callback) {
+    key = key.toString('hex');
+
+    if (typeof callback === 'function') {
+      return this._iterativeFind('FIND_NODE', key).then(function() {
+        callback(null, ...arguments);
+      }, callback);
+    } else {
+      return this._iterativeFind('FIND_NODE', key);
+    }
+  }
+  /**
+   * @callback KademliaNode~iterativeFindNodeCallback
+   * @param {error|null} error
+   * @param {Bucket~contact[]} contacts - Result of the lookup operation
+   */
+
+  /**
+   * Kademlia search operation that is conducted as a node lookup and builds
+   * a list of K closest contacts. If at any time during the lookup the value
+   * is returned, the search is abandoned. If no value is found, the K closest
+   * contacts are returned. Upon success, we must store the value at the
+   * nearest node seen during the search that did not return the value.
+   * @param {buffer|string} key - Key for value lookup
+   * @param {KademliaNode~iterativeFindValueCallback} [callback]
+   * @returns {Promise<object>}
+   */
+  iterativeFindValue(key, callback) {
+    key = key.toString('hex');
+
+    if (typeof callback === 'function') {
+      return this._iterativeFind('FIND_VALUE', key).then(function() {
+        callback(null, ...arguments);
+      }, callback);
+    } else {
+      return this._iterativeFind('FIND_VALUE', key);
+    }
+  }
+  /**
+   * @callback KademliaNode~iterativeFindValueCallback
+   * @param {error|null} error
+   * @param {KademliaNode~entry} value
+   * @param {null|Bucket~contact} contact - Contact responded with entry
+   */
+
+  /**
+   * Performs a scan of the storage adapter and performs
+   * republishing/replication of items stored. Items that we did not publish
+   * ourselves get republished every T_REPLICATE. Items we did publish get
+   * republished every T_REPUBLISH.
+   * @param {KademliaNode~replicateCallback} [callback]
+   * @returns {Promise}
+   */
+  replicate(callback) {
+    if (typeof callback === 'function') {
+      return this._replicate().then(callback, callback);
+    } else {
+      return this._replicate();
+    }
+  }
+  /**
+   * @callback KademliaNode~replicateCallback
+   * @param {error|null} error
+   */
+
+  /**
+   * @private
+   */
+  _replicate() {
+    const self = this;
+    const now = Date.now();
+
+    return new Promise((resolve, reject) => {
+      const itemStream = this.storage.createReadStream({
+        valueEncoding: 'json'
+      });
+      const replicateStream = new WritableStream({
+        objectMode: true,
+        write: maybeReplicate
+      });
+
+      function maybeReplicate({ key, value }, enc, next) {
+        const isPublisher = value.publisher === self.identity.toString('hex');
+        const republishDue = (value.timestamp + constants.T_REPUBLISH) <= now;
+        const replicateDue = (value.timestamp + constants.T_REPLICATE) <= now;
+        const shouldRepublish = isPublisher && republishDue;
+        const shouldReplicate = !isPublisher && replicateDue;
+
+        if (shouldReplicate || shouldRepublish) {
+          return self.iterativeStore(key, value, next);
+        }
+
+        next();
+      }
+
+      function triggerCallback(err) {
+        itemStream.removeAllListeners();
+        replicateStream.removeAllListeners();
+
+        if (err) {
+          return reject(err);
+        }
+
+        resolve();
+      }
+
+      itemStream.on('error', triggerCallback);
+      replicateStream.on('error', triggerCallback);
+      replicateStream.on('finish', triggerCallback);
+      itemStream.pipe(this.replicatePipeline).pipe(replicateStream);
+    });
+  }
+
+  /**
+   * Items expire T_EXPIRE seconds after the original publication. All items
+   * are assigned an expiration time which is "exponentially inversely
+   * proportional to the number of nodes between the current node and the node
+   * whose ID is closest to the key", where this number is "inferred from the
+   * bucket structure of the current node".
+   * @param {KademliaNode~expireCallback} [callback]
+   * @returns {Promise}
+   */
+  expire(callback) {
+    if (typeof callback === 'function') {
+      return this._expire().then(callback, callback);
+    } else {
+      return this._expire();
+    }
+  }
+  /**
+   * @callback KademliaNode~expireCallback
+   * @param {error|null} error
+   */
+
+  /**
+   * @private
+   */
+  _expire() {
+    const self = this;
+    const now = Date.now();
+
+    return new Promise((resolve, reject) => {
+      const itemStream = this.storage.createReadStream({
+        valueEncoding: 'json'
+      });
+      const expireStream = new WritableStream({
+        objectMode: true,
+        write: maybeExpire
+      });
+
+      function maybeExpire({ key, value }, enc, next) {
+        if ((value.timestamp + constants.T_EXPIRE) <= now) {
+          return self.storage.del(key, next);
+        }
+
+        next();
+      }
+
+      function triggerCallback(err) {
+        itemStream.removeAllListeners();
+        expireStream.removeAllListeners();
+
+        if (err) {
+          return reject(err);
+        }
+
+        resolve();
+      }
+
+      itemStream.on('error', triggerCallback);
+      expireStream.on('error', triggerCallback);
+      expireStream.on('finish', triggerCallback);
+      itemStream.pipe(this.expirePipeline).pipe(expireStream);
+    });
+  }
+
+  /**
+   * If no node lookups have been performed in any given bucket's range for
+   * T_REFRESH, the node selects a random number in that range and does a
+   * refresh, an iterativeFindNode using that number as key.
+   * @param {number} startIndex - bucket index to start refresh from
+   * @param {KademliaNode~refreshCallback} [callback]
+   * @returns {Promise}
+   */
+  refresh(startIndex = 0, callback) {
+    if (typeof callback === 'function') {
+      return this._refresh(startIndex).then(callback, callback);
+    } else {
+      return this._refresh(startIndex);
+    }
+  }
+  /**
+   * @callback KademliaNode~refreshCallback
+   * @param {error|null} error
+   * @param {array} bucketsRefreshed
+   */
+
+  /**
+   * @private
+   */
+  _refresh(startIndex) {
+    const now = Date.now();
+    const indices = [
+      ...this.router.entries()
+    ].slice(startIndex).map((entry) => entry[0]);
+
+    // NB: We want to avoid high churn during refresh and prevent further
+    // NB: refreshes if lookups in the next bucket do not return any new
+    // NB: contacts. To do this we will shuffle the bucket indexes we are
+    // NB: going to check and only continue to refresh if new contacts were
+    // NB: discovered in the last MAX_UNIMPROVED_REFRESHES consecutive lookups.
+    let results = new Set(), consecutiveUnimprovedLookups = 0;
+
+    function isDiscoveringNewContacts() {
+      return consecutiveUnimprovedLookups < constants.MAX_UNIMPROVED_REFRESHES;
+    }
+
+    return new Promise((resolve, reject) => {
+      async.eachSeries(shuffle(indices), (index, next) => {
+        if (!isDiscoveringNewContacts()) {
+          return resolve();
+        }
+
+        const lastBucketLookup = this._lookups.get(index) || 0;
+        const needsRefresh = lastBucketLookup + constants.T_REFRESH <= now;
+
+        if (needsRefresh) {
+          return this.iterativeFindNode(
+            utils.getRandomBufferInBucketRange(this.identity, index)
+              .toString('hex'),
+            (err, contacts) => {
+              if (err) {
+                return next(err);
+              }
+
+              let discoveredNewContacts = false;
+
+              for (let [identity] of contacts) {
+                if (!results.has(identity)) {
+                  discoveredNewContacts = true;
+                  consecutiveUnimprovedLookups = 0;
+                  results.add(identity);
+                }
+              }
+
+              if (!discoveredNewContacts) {
+                consecutiveUnimprovedLookups++;
+              }
+
+              next();
+            }
+          );
+        }
+
+        next();
+      }, (err) => {
+        if (err) {
+          return reject(err);
+        }
+
+        resolve();
+      });
+    });
+  }
+
+  /**
+   * Builds an list of closest contacts for a particular RPC
+   * @private
+   */
+  _iterativeFind(method, key) {
+    return new Promise((resolve) => {
+      function createRpc(target) {
+        return [method, [key], target];
+      }
+
+      let shortlist = new ContactList(key, [
+        ...this.router.getClosestContactsToKey(key, constants.ALPHA)
+      ]);
+      let closest = shortlist.closest;
+
+      this._lookups.set(utils.getBucketIndex(this.identity, key), Date.now());
+
+      function iterativeLookup(selection, continueLookup = true) {
+        if (!selection.length) {
+          return resolve(shortlist.active.slice(0, constants.K));
+        }
+
+        async.each(selection, (contact, next) => {
+          // NB: mark this node as contacted so as to avoid repeats
+          shortlist.contacted(contact);
+
+          this.send(...createRpc(contact), (err, result) => {
+            if (err) {
+              return next();
+            }
+
+            // NB: mark this node as active to include it in any return values
+            shortlist.responded(contact);
+
+            // NB: If the result is a contact/node list, just keep track of it
+            // NB: Otherwise, do not proceed with iteration, just callback
+            if (Array.isArray(result) || method !== 'FIND_VALUE') {
+              shortlist
+                .add(Array.isArray(result) ? result : [])
+                .forEach(contact => {
+                  // NB: If it wasn't in the shortlist, we haven't added to the
+                  // NB: routing table, so do that now.
+                  this._updateContact(...contact);
+                });
+
+              return next();
+            }
+
+            // NB: If we did get an item back, get the closest node we contacted
+            // NB: who is missing the value and store a copy with them
+            const closestMissingValue = shortlist.active[0]
+
+            if (closestMissingValue) {
+              this.send('STORE', [
+                key,
+                this._createStorageItem(result)
+              ], closestMissingValue, () => null);
+            }
+
+            // NB: we found a value, so stop searching
+            resolve(result, contact);
+          });
+        }, () => {
+
+          // NB: If we have reached at least K active nodes, or haven't found a
+          // NB: closer node, even on our finishing trip, return to the caller
+          // NB: the K closest active nodes.
+          if (shortlist.active.length >= constants.K ||
+            (closest[0] === shortlist.closest[0] && !continueLookup)
+          ) {
+            return resolve(shortlist.active.slice(0, constants.K));
+          }
+
+          // NB: we haven't discovered a closer node, call k uncalled nodes and
+          // NB: finish up
+          if (closest[0] === shortlist.closest[0]) {
+            return iterativeLookup.call(
+              this,
+              shortlist.uncontacted.slice(0, constants.K),
+              false
+            );
+          }
+
+          closest = shortlist.closest;
+
+          // NB: continue the lookup with ALPHA close, uncontacted nodes
+          iterativeLookup.call(
+            this,
+            shortlist.uncontacted.slice(0, constants.ALPHA),
+            true
+          );
+        });
+      }
+
+      iterativeLookup.call(
+        this,
+        shortlist.uncontacted.slice(0, constants.ALPHA),
+        true
+      );
+    });
+  }
+  /**
+   * Adds the given contact to the routing table
+   * @private
+   */
+  _updateContact(identity, contact) {
+    this._updateContactQueue.push({ identity, contact }, (err, headId) => {
+      if (err) {
+        this.router.removeContactByNodeId(headId);
+        this.router.addContactByNodeId(identity, contact);
+      }
+    });
+  }
+
+  /**
+   * Worker for updating contact in a routing table bucket
+   * @private
+   */
+  _updateContactWorker(task, callback) {
+    const { identity, contact } = task;
+
+    if (identity === this.identity.toString('hex')) {
+      return callback();
+    }
+
+    const now = Date.now();
+    const reset = 600000;
+    const [, bucket, contactIndex] = this.router.addContactByNodeId(
+      identity,
+      contact
+    );
+
+    const [headId, headContact] = bucket.head;
+    const lastPing = this._pings.get(headId);
+
+    if (contactIndex !== -1) {
+      return callback();
+    }
+
+    if (lastPing && lastPing.responded && lastPing.timestamp > (now - reset)) {
+      return callback();
+    }
+
+    this.ping([headId, headContact], (err) => {
+      this._pings.set(headId, { timestamp: Date.now(), responded: !err });
+      callback(err, headId);
+    });
+  }
+
+}
+
+module.exports = KademliaNode;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-churnfilter.js.html b/docs/plugin-churnfilter.js.html new file mode 100644 index 0000000..ee3f8b3 --- /dev/null +++ b/docs/plugin-churnfilter.js.html @@ -0,0 +1,270 @@ + + + + + + plugin-churnfilter.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-churnfilter.js

+ + + + + + + +
+
+
/**
+ * @module kadence/churnfilter
+ */
+
+'use strict';
+
+const ms = require('ms');
+const merge = require('merge');
+
+
+/**
+ * Plugin that tracks contacts that are not online and evicts them from the
+ * routing table, prevents re-entry into the routing table using an exponential
+ * cooldown time.
+ */
+class ChurnFilterPlugin {
+
+  static get DEFAULTS() {
+    return {
+      cooldownBaseTimeout: '1M', // Start block at N minutes
+      cooldownMultiplier: 2, // Multiply the block time by M every offense
+      cooldownResetTime: '10M' // Until no offense has occured for K minutes
+    };
+  }
+
+  /**
+   * @constructor
+   * @param {AbstractNode} node
+   * @param {object} [options]
+   * @param {number} [options.cooldownMultiplier=2] - Multiply cooldown time
+   * by this number after every offense
+   * @param {string} [options.cooldownResetTime="10M"] - Human time string
+   * for resetting the cooldown multiplier after no block added for a given
+   * peer fingerprint
+   * @param {string} [options.cooldownBaseTimeout="1M"] - Human time string
+   * for starting timeout, multiplied by two every time the cooldown is reset
+   * and broken again
+   */
+  constructor(node, options) {
+    this.node = node;
+    this.opts = merge(ChurnFilterPlugin.DEFAULTS, options);
+    this.cooldown = new Map();
+    this.blocked = new Set();
+
+    // Not sure how well this is going to work in a production environment yet
+    // so let's warn users that it could be problematic
+    this.node.logger.warn(
+      'the churn filter plugin may not be suitable for production networks'
+    );
+
+    this._wrapAbstractNodeSend(); // Detect timeouts and network errors
+    this._wrapAbstractNodeUpdateContact(); // Gatekeep the routing table
+
+    setInterval(
+      this.resetCooldownForStablePeers.bind(this),
+      ms(this.opts.cooldownBaseTimeout)
+    );
+  }
+
+  /**
+   * @private
+   */
+  _wrapAbstractNodeUpdateContact() {
+    const _updateContact = this.node._updateContact.bind(this.node);
+
+    this.node._updateContact = (identity, contact) => {
+      if (this.hasBlock(identity)) {
+        this.node.logger.debug(
+          'preventing entry of blocked fingerprint %s into routing table',
+          identity
+        );
+        return null;
+      }
+
+      _updateContact(identity, contact);
+    };
+  }
+
+  /**
+   * @private
+   */
+  _wrapAbstractNodeSend() {
+    const send = this.node.send.bind(this.node);
+
+    this.node.send = (method, params, target, handler) => {
+      if (this.hasBlock(target[0])) {
+        this.node.logger.warn(
+          'sending message to contact %s with active block',
+          target[0]
+        );
+      }
+
+      send(method, params, target, (err, result) => {
+        if (err && (err.type === 'TIMEOUT' || err.dispose)) {
+          this.node.logger.info('setting temporary block for %s', target[0]);
+          this.setBlock(target[0]);
+        }
+
+        handler(err, result);
+      });
+    };
+  }
+
+  /**
+   * Checks if the fingerprint is blocked
+   * @param {string|buffer} fingerprint - Node ID to check
+   * @returns {boolean}
+   */
+  hasBlock(fingerprint) {
+    fingerprint = fingerprint.toString('hex');
+
+    if (this.blocked.has(fingerprint)) {
+      return !this.cooldown.get(fingerprint).expired;
+    }
+
+    return false;
+  }
+
+  /**
+   * Creates a new block or renews the cooldown for an existing block
+   * @param {string|buffer} fingerprint - Node ID to block
+   * @returns {object}
+   */
+  setBlock(fingerprint) {
+    fingerprint = fingerprint.toString('hex');
+
+    let cooldown = this.cooldown.get(fingerprint);
+
+    if (cooldown) {
+      cooldown.duration = cooldown.expired
+        ? cooldown.duration
+        : cooldown.duration * this.opts.cooldownMultiplier;
+      cooldown.time = Date.now();
+    } else {
+      cooldown = {
+        duration: ms(this.opts.cooldownBaseTimeout),
+        time: Date.now(),
+        get expiration() {
+          return this.time + this.duration;
+        },
+        get expired() {
+          return this.expiration <= Date.now();
+        }
+      };
+    }
+
+    this.cooldown.set(fingerprint, cooldown);
+    this.blocked.add(fingerprint);
+    this.node.router.removeContactByNodeId(fingerprint);
+  }
+
+  /**
+   * Deletes the blocked fingerprint
+   * @param {string|buffer} fingerprint - Node ID to remove block
+   */
+  delBlock(fingerprint) {
+    this.cooldown.delete(fingerprint);
+    this.blocked.delete(fingerprint);
+  }
+
+  /**
+   * Clears all blocked and cooldown data
+   */
+  reset() {
+    this.cooldown.clear();
+    this.blocked.clear();
+  }
+
+  /**
+   * Releases blocked to reset cooldown multipliers for fingerprints with
+   * cooldowns that are long expired and not blocked
+   */
+  resetCooldownForStablePeers() {
+    const now = Date.now();
+
+    for (let [fingerprint, cooldown] of this.cooldown) {
+      if (this.hasBlock(fingerprint)) {
+        continue;
+      }
+
+      let { expired, expiration } = cooldown;
+
+      if (expired && (now - expiration >= ms(this.opts.cooldownResetTime))) {
+        this.delBlock(fingerprint);
+      }
+    }
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/contentaddress~ChurnFilterPlugin} with
+ * a {@link KademliaNode}
+ * @param {object} [options]
+ * @param {number} [options.cooldownMultiplier=2] - Multiply cooldown time
+ * by this number after every offense
+ * @param {string} [options.cooldownResetTime="60M"] - Human time string
+ * for resetting the cooldown multiplier after no block added for a given
+ * peer fingerprint
+ * @param {string} [options.cooldownBaseTimeout="5M"] - Human time string
+ * for starting timeout, multiplied by two every time the cooldown is reset
+ * and broken again
+ */
+module.exports = function(options) {
+  return function(node) {
+    return new ChurnFilterPlugin(node, options);
+  }
+};
+
+module.exports.ChurnFilterPlugin = ChurnFilterPlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-contentaddress.js.html b/docs/plugin-contentaddress.js.html new file mode 100644 index 0000000..1b8e24a --- /dev/null +++ b/docs/plugin-contentaddress.js.html @@ -0,0 +1,157 @@ + + + + + + plugin-contentaddress.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-contentaddress.js

+ + + + + + + +
+
+
/**
+ * @module kadence/contentaddress
+ */
+
+'use strict';
+
+const { createHash } = require('crypto');
+const merge = require('merge');
+const assert = require('assert');
+
+
+/**
+ * Enforces that any {@link KademliaNode~entry} stored in the DHT must be
+ * content-addressable (keyed by the hash of it's value).
+ */
+class ContentAddressPlugin {
+
+  static get DEFAULTS() {
+    return {
+      keyAlgorithm: 'ripemd160',
+      valueEncoding: 'base64'
+    };
+  }
+
+  /**
+   * @constructor
+   * @param {AbstractNode} node
+   * @param {object} [options]
+   * @param {string} [options.keyAlgorithm="rmd160"] - Algorithm for hashing
+   * @param {string} [options.valueEncoding="base64"] - Text encoding of value
+   */
+  constructor(node, options) {
+    this.node = node;
+    this.opts = merge(ContentAddressPlugin.DEFAULTS, options);
+
+    this.node.use('STORE', (req, res, next) => this.validate(req, res, next));
+    this._wrapIterativeStore();
+  }
+
+  /**
+   * @private
+   */
+  _wrapIterativeStore() {
+    let iterativeStore = this.node.iterativeStore.bind(this.node);
+
+    this.node.iterativeStore = (key, value, callback) => {
+      try {
+        const buffer = Buffer.from(value, this.opts.valueEncoding);
+        const hash = createHash(this.opts.keyAlgorithm).update(buffer)
+          .digest('hex');
+
+        assert(key === hash);
+      } catch (err) {
+        return callback(new Error('Item failed validation check'));
+      }
+
+      iterativeStore(key, value, callback);
+    };
+  }
+
+  /**
+   * Validate the the key matches the hash of the value
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   * @param {AbstractNode~next} next
+   */
+  validate(request, response, next) {
+    let buffer, hash, [key, item] = request.params;
+
+    try {
+      buffer = Buffer.from(item.value, this.opts.valueEncoding);
+      hash = createHash(this.opts.keyAlgorithm).update(buffer).digest('hex');
+
+      assert(key === hash);
+    } catch (err) {
+      return next(new Error('Item failed validation check'));
+    }
+
+    next();
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/contentaddress~ContentAddressPlugin} with
+ * a {@link KademliaNode}
+ * @param {object} [options]
+ * @param {string} [options.keyAlgorithm="rmd160"] - Algorithm for hashing
+ * @param {string} [options.valueEncoding="base64"] - Text encoding of value
+ */
+module.exports = function(options) {
+  return function(node) {
+    return new ContentAddressPlugin(node, options);
+  }
+};
+
+module.exports.ContentAddressPlugin = ContentAddressPlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-eclipse.js.html b/docs/plugin-eclipse.js.html new file mode 100644 index 0000000..3badca8 --- /dev/null +++ b/docs/plugin-eclipse.js.html @@ -0,0 +1,208 @@ + + + + + + plugin-eclipse.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-eclipse.js

+ + + + + + + +
+
+
/**
+ * @module kadence/eclipse
+ */
+
+'use strict';
+
+const assert = require('assert');
+const utils = require('./utils');
+const constants = require('./constants');
+const { EventEmitter } = require('events');
+
+
+/**
+ * Generates an identity for use with the
+ * {@link module:kadence/spartacus~SpartacusPlugin} that satisfies a proof of
+ * work
+ */
+class EclipseIdentity extends EventEmitter {
+
+  /**
+   * @constructor
+   * @param {string} publicKey - SECP256K1 public key
+   * @param {number} [nonce] - Equihash proof nonce
+   * @param {buffer} [proof] - Equihash proof value
+   */
+  constructor(publicKey, nonce, proof) {
+    super();
+
+    this.pubkey = publicKey;
+    this.nonce = nonce || 0;
+    this.proof = proof || Buffer.from([]);
+    this.fingerprint = utils.hash160(this.proof);
+  }
+
+  /**
+   * Returns a equihash proof and resulting fingerprint
+   * @returns {Promise<EclipseIdentity>}
+   */
+  solve() {
+    return new Promise((resolve, reject) => {
+      utils.eqsolve(
+        utils.hash256(this.pubkey),
+        constants.IDENTITY_DIFFICULTY
+      ).then(proof => {
+        this.nonce = proof.nonce;
+        this.proof = proof.value;
+        this.fingerprint = utils.hash160(this.proof);
+        resolve(this);
+      }, reject);
+    });
+  }
+
+  /**
+   * Validates the
+   * @returns {boolean}
+   */
+  validate() {
+    return utils.eqverify(utils.hash256(this.pubkey), {
+      n: constants.IDENTITY_DIFFICULTY.n,
+      k: constants.IDENTITY_DIFFICULTY.k,
+      nonce: this.nonce,
+      value: this.proof
+    });
+  }
+
+}
+
+/**
+ * Enforces identities that satisfy a proof of work
+ */
+class EclipseRules {
+
+  /**
+   * @constructor
+   * @param {Node} node
+   */
+  constructor(node) {
+    this.node = node;
+  }
+
+  /**
+   * Validates all incoming RPC messages
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   */
+  validate(request, response, next) {
+    const [fingerprint, contact] = request.contact;
+    const identity = new EclipseIdentity(
+      Buffer.from(contact.pubkey || '', 'hex'),
+      contact.nonce,
+      Buffer.from(contact.proof || '', 'hex')
+    );
+
+    try {
+      assert(identity.fingerprint.toString('hex') === fingerprint,
+        'Fingerprint does not match the proof hash');
+      assert(identity.validate(),
+        'Identity proof is invalid or does not satisfy the difficulty');
+    } catch (err) {
+      return next(err);
+    }
+
+    return next();
+  }
+
+}
+
+/**
+ * Enforces proof of work difficulty for entering the routing table and ensures
+ * a high degree of randomness in resulting node identity
+ */
+class EclipsePlugin {
+
+  /**
+   * @constructor
+   * @param {KademliaNode} node
+   * @param {EclipseIdentity} identity
+   */
+  constructor(node, identity) {
+    assert(identity instanceof EclipseIdentity, 'No eclipse identity supplied');
+
+    this.node = node;
+    this.rules = new EclipseRules(this.node);
+    this.identity = identity;
+    this.node.contact.pubkey = identity.pubkey.toString('hex');
+    this.node.contact.nonce = identity.nonce;
+    this.node.contact.proof = identity.proof.toString('hex');
+    this.node.identity = identity.fingerprint;
+
+    this.node.use(this.rules.validate.bind(this.rules));
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/eclipse~EclipsePlugin} with a
+ * {@link KademliaNode}
+ * @param {EclipseIdentity} identity
+ */
+module.exports = function(identity) {
+  return function(node) {
+    return new EclipsePlugin(node, identity);
+  }
+};
+
+module.exports.EclipsePlugin = EclipsePlugin;
+module.exports.EclipseRules = EclipseRules;
+module.exports.EclipseIdentity = EclipseIdentity;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-hashcash.js.html b/docs/plugin-hashcash.js.html new file mode 100644 index 0000000..3eedca7 --- /dev/null +++ b/docs/plugin-hashcash.js.html @@ -0,0 +1,361 @@ + + + + + + plugin-hashcash.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-hashcash.js

+ + + + + + + +
+
+
/**
+ * @module kadence/hashcash
+ */
+
+'use strict';
+
+const { fork } = require('child_process');
+const path = require('path');
+const { Transform } = require('stream');
+const async = require('async');
+const merge = require('merge');
+const jsonrpc = require('jsonrpc-lite');
+const crypto = require('crypto');
+const assert = require('assert');
+const LRUCache = require('lru-cache');
+const utils = require('./utils');
+
+
+/**
+ * Requires proof of work to process messages and performs said work before
+ * issuing RPC messages to peers
+ */
+class HashCashPlugin {
+
+  static get METHOD() {
+    return 'HASHCASH';
+  }
+
+  static get DEFAULTS() {
+    return {
+      methods: [], // All methods by default
+      difficulty: 8, // 8 leading zeroes
+      timeframe: 172800000 // 2 day window
+    };
+  }
+
+  /**
+   * @constructor
+   * @param {object} node
+   * @param {object} [options]
+   * @param {string[]} [options.methods=[]] - RPC methods to enforce hashcash
+   * @param {number} [options.difficulty=8] - Leading zero bits in stamp
+   * @param {number} [options.timeframe=172800000] - Timestamp valid window
+   */
+  constructor(node, options = {}) {
+    this._opts = merge(HashCashPlugin.DEFAULTS, options);
+    this._node = node;
+    this._cache = new LRUCache(1000);
+
+    this._node.rpc.deserializer.prepend(() => new Transform({
+      transform: this.verify.bind(this),
+      objectMode: true
+    }));
+
+    this._node.rpc.serializer.append(() => new Transform({
+      transform: this.prove.bind(this),
+      objectMode: true
+    }));
+  }
+
+  /**
+   * Verifies the proof of work on the request object
+   * @implements {Messenger~deserializer}
+   */
+  verify(data, encoding, callback) {
+    /* eslint max-statements: [2, 26] */
+    let payload = jsonrpc.parse(data.toString('utf8')).map((obj) => {
+      return obj.payload;
+    });
+    let verifyMessage = (this._opts.methods.includes(payload[0].method) ||
+      this._opts.methods.length === 0) &&
+      typeof payload[0].method !== 'undefined';
+
+    if (!verifyMessage) {
+      return callback(null, data);
+    }
+
+    let proof = payload.filter(m => m.method === HashCashPlugin.METHOD).pop();
+    let contact = payload.filter(m => m.method === 'IDENTIFY').pop();
+
+    if (!proof) {
+      return callback(new Error('HashCash stamp is missing from payload'));
+    }
+
+    let stamp = HashCashPlugin.parse(proof.params[0]);
+    let sender = stamp.resource.substr(0, 40);
+    let target = Buffer.from(stamp.resource.substr(40, 40), 'hex');
+    let method = Buffer.from(
+      stamp.resource.substr(80),
+      'hex'
+    ).toString('utf8');
+
+    try {
+      assert(this._cache.get(stamp.toString()) !== 1, 'Cannot reuse proof');
+      assert(stamp.bits === this._opts.difficulty, 'Invalid proof difficulty');
+      assert(sender === contact.params[0], 'Invalid sender in proof');
+      assert(
+        Buffer.compare(target, this._node.identity) === 0,
+        'Invalid target in proof'
+      );
+      assert(method === payload[0].method, 'Invalid proof for called method');
+
+      let now = Date.now();
+
+      assert(utils.satisfiesDifficulty(utils.hash160(stamp.toString()),
+        this._opts.difficulty), 'Invalid HashCash stamp');
+      assert(
+        now - Math.abs(stamp.date) <= this._opts.timeframe,
+        'HashCash stamp is expired'
+      );
+    } catch (err) {
+      return callback(err);
+    }
+
+    this._cache.set(stamp.toString(), 1);
+    callback(null, data);
+  }
+
+  /**
+   * Add proof of work to outgoing message
+   * @implements {Messenger~serializer}
+   */
+  prove(data, encoding, callback) {
+    let [id, buffer, target] = data;
+    let now = Date.now();
+    let payload = jsonrpc.parse(buffer.toString('utf8')).map((obj) => {
+      return obj.payload;
+    });
+    let stampMessage = (this._opts.methods.includes(payload[0].method) ||
+      this._opts.methods.length === 0) &&
+      typeof payload[0].method !== 'undefined';
+
+    if (!stampMessage) {
+      return callback(null, data);
+    }
+
+    this._node.logger.debug(`mining hashcash stamp for ${payload[0].method}`);
+
+    // NB: "Pause" the timeout timer for the request this is associated with
+    // NB: so that out mining does not eat into the reasonable time for a
+    // NB: response.
+    const pending = this._node._pending.get(id) || {};
+    pending.timestamp = Infinity;
+
+    HashCashPlugin.create(
+      this._node.identity.toString('hex'),
+      target[0],
+      payload[0].method,
+      this._opts.difficulty,
+      (err, result) => {
+        if (err) {
+          return callback(err);
+        }
+
+        pending.timestamp = Date.now(); // NB: Reset the timeout counter
+
+        let delta = Date.now() - now;
+        let proof = jsonrpc.notification(HashCashPlugin.METHOD, [
+          result.header
+        ]);
+
+        this._node.logger.debug(`mined stamp ${result.header} in ${delta}ms`);
+        payload.push(proof);
+        callback(null, [
+          id,
+          Buffer.from(JSON.stringify(payload), 'utf8'),
+          target
+        ]);
+      }
+    );
+  }
+
+  /**
+   * Parses hashcash stamp header into an object
+   * @static
+   * @param {string} header - Hashcash header proof stamp
+   * @returns {module:kadence/hashcash~HashCashPlugin~stamp}
+   */
+  static parse(header) {
+    let parts = header.split(':');
+    let parsed = {
+      ver: parseInt(parts[0]),
+      bits: parseInt(parts[1]),
+      date: parseInt(parts[2]),
+      resource: parts[3],
+      ext: '',
+      rand: parts[5],
+      counter: parseInt(parts[6], 16),
+      toString() {
+        return [
+          this.ver, this.bits, this.date, this.resource,
+          this.ext, this.rand, this.counter.toString(16)
+        ].join(':');
+      }
+    };
+
+    return parsed;
+  }
+  /**
+   * @typedef module:kadence/hashcash~HashCashPlugin~stamp
+   * @property {number} ver - Hashcash version
+   * @property {number} bits - Number of zero bits of difficulty
+   * @property {number} date - UNIX timestamp
+   * @property {string} resource - Sender and target node identities
+   * @property {string} ext - Empty string
+   * @property {string} rand - String encoded random number
+   * @property {number} counter - Base 16 counter
+   * @property {function} toString - Reserializes the parsed header
+   */
+
+  /**
+   * Creates the hashcash stamp header
+   * @static
+   * @param {string} sender
+   * @param {string} target
+   * @param {string} method
+   * @param {number} difficulty
+   * @param {function} callback
+   */
+  /* eslint max-params: [2, 5] */
+  static create(sender = '00', target = '00', method = '00', bits = 8, cb) {
+    const proc = fork(
+      path.join(__dirname, 'plugin-hashcash.worker.js'),
+      [
+        sender,
+        target,
+        method,
+        bits
+      ],
+      {
+        env: process.env
+      }
+    );
+
+    proc.on('message', msg => {
+      if (msg.error) {
+        return cb(new Error(msg.error));
+      }
+
+      cb(null, msg);
+    });
+  }
+
+  /**
+   * @private
+   */
+  static _worker(sender = '00', target = '00', method = '00', bits = 8, cb) {
+    let header = {
+      ver: 1,
+      bits: bits,
+      date: Date.now(),
+      resource: Buffer.concat([
+        Buffer.from(sender, 'hex'),
+        Buffer.from(target, 'hex'),
+        Buffer.from(method)
+      ]).toString('hex'),
+      ext: '',
+      rand: crypto.randomBytes(12).toString('base64'),
+      counter: Math.ceil(Math.random() * 10000000000),
+      toString() {
+        return [
+          this.ver, this.bits, this.date, this.resource,
+          this.ext, this.rand, this.counter.toString(16)
+        ].join(':');
+      }
+    };
+
+    function isSolution() {
+      return utils.satisfiesDifficulty(utils.hash160(header.toString()), bits);
+    }
+
+    async.whilst(() => !isSolution(), (done) => {
+      setImmediate(() => {
+        header.counter++;
+        done();
+      });
+    }, () => {
+      cb(null, {
+        header: header.toString(),
+        time: Date.now() - header.date
+      });
+    });
+  }
+
+}
+
+/**
+ * Registers the {@link module:kadence/hashcash~HashCashPlugin} with an
+ * {@link AbstractNode}
+ * @param {object} [options]
+ * @param {string[]} [options.methods=[]] - RPC methods to enforce hashcash
+ * @param {number} [options.difficulty=8] - Leading zero bits in stamp
+ * @param {number} [options.timeframe=172800000] - Timestamp valid window
+ */
+module.exports = function(options) {
+  return function(node) {
+    return new HashCashPlugin(node, options);
+  }
+};
+
+module.exports.HashCashPlugin = HashCashPlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-hibernate.js.html b/docs/plugin-hibernate.js.html new file mode 100644 index 0000000..0554666 --- /dev/null +++ b/docs/plugin-hibernate.js.html @@ -0,0 +1,215 @@ + + + + + + plugin-hibernate.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-hibernate.js

+ + + + + + + +
+
+
/**
+ * @module kadence/hibernate
+ */
+
+'use strict';
+
+const { EventEmitter } = require('events');
+const { Transform } = require('stream');
+const merge = require('merge');
+const bytes = require('bytes');
+const ms = require('ms');
+
+
+/**
+ * Represents a bandwidth meter which will trigger hibernation
+ */
+class HibernatePlugin extends EventEmitter {
+
+  static get DEFAULTS() {
+    return {
+      limit: '5gb',
+      interval: '1d',
+      reject: ['STORE', 'FIND_VALUE']
+    };
+  }
+
+  /**
+   * @constructor
+   * @param {KademliaNode} node
+   * @param {object} [options]
+   * @param {string} [options.limit=5gb] - The accounting max bandwidth
+   * @param {string} [options.interval=1d] - The accounting reset interval
+   * @param {string[]} [options.reject] - List of methods to reject during
+   * hibernation
+   */
+  constructor(node, options) {
+    super();
+
+    this.node = node;
+    this.opts = merge(HibernatePlugin.DEFAULTS, options);
+    this.limit = bytes(this.opts.limit);
+    this.interval = ms(this.opts.interval);
+    this.reject = this.opts.reject;
+
+    // This plugin could potentially be used for denial of service attacks
+    // so let's warn users that it could be problematic
+    this.node.logger.warn(
+      'the hibernation plugin may not be suitable for production networks'
+    );
+
+    this.node.rpc.deserializer.prepend(() => this.meter('inbound'));
+    this.node.rpc.serializer.append(() => this.meter('outbound'));
+    this.node.use((req, res, next) => this.detect(req, res, next));
+    this.start();
+  }
+
+
+  /**
+   * @property {boolean} hibernating - Indicates if our limits are reached
+   */
+  get hibernating() {
+    return this.accounting.total >= this.limit;
+  }
+
+  /**
+   * Starts the accounting reset timeout
+   */
+  start() {
+    const now = Date.now();
+
+    if (this.accounting) {
+      this.emit('reset', merge({}, this.accounting, {
+        hibernating: this.hibernating
+      }));
+    } else {
+      this.emit('start');
+    }
+
+    this.accounting = {
+      start: now,
+      end: now + this.interval,
+      inbound: 0,
+      outbound: 0,
+      unknown: 0,
+      get total() {
+        return this.inbound + this.outbound + this.unknown;
+      },
+      get reset() {
+        return this.end - Date.now();
+      }
+    };
+
+    setTimeout(() => this.start(), this.interval);
+  }
+
+  /**
+   * Return a meter stream that increments the given accounting property
+   * @param {string} type - ['inbound', 'outbound', 'unknown']
+   * @returns {stream.Transform}
+   */
+  meter(type) {
+    if (!['inbound', 'outbound'].includes(type)) {
+      type = 'unknown';
+    }
+
+    const inc = (data) => {
+      if (Buffer.isBuffer(data)) {
+        this.accounting[type] += data.length;
+      } else if (Array.isArray(data)) {
+        this.accounting[type] += data[1].length;
+      } else {
+        this.accounting[type] = Buffer.from(data).length;
+      }
+    }
+
+    return new Transform({
+      transform: (data, enc, callback) => {
+        inc(data);
+        callback(null, data);
+      },
+      objectMode: true
+    });
+  }
+
+  /**
+   * Check if hibernating when messages received
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   * @param {AbstractNode~next} next
+   */
+  detect(request, response, next) {
+    if (this.hibernating && this.reject.includes(request.method)) {
+      next(new Error(`Hibernating, try ${request.method} again later`));
+    } else {
+      next();
+    }
+  }
+
+}
+
+/**
+ * Regsiters a {@link HibernatePlugin} with an {@link AbstractNode}
+ * @param {object} [options]
+ * @param {string} [options.limit=5gb] - The accounting max bandwidth
+ * @param {string} [options.interval=1d] - The accounting reset interval
+ * @param {string[]} [options.reject] - List of methods to reject during
+ * hibernation
+ */
+module.exports = function(options) {
+  return function(node) {
+    return new module.exports.HibernatePlugin(node, options);
+  };
+};
+
+module.exports.HibernatePlugin = HibernatePlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-logger.js.html b/docs/plugin-logger.js.html new file mode 100644 index 0000000..517f103 --- /dev/null +++ b/docs/plugin-logger.js.html @@ -0,0 +1,170 @@ + + + + + + plugin-logger.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-logger.js

+ + + + + + + +
+
+
/**
+ * @module kadence/logger
+ */
+
+'use strict';
+
+const { Transform } = require('stream');
+const bunyan = require('bunyan');
+
+
+/**
+ * Logs all incoming messages
+ */
+class IncomingMessageLogger extends Transform {
+
+  /**
+   * @constructor
+   * @param {AbstractNode~logger} logger - Logger to use
+   */
+  constructor(logger) {
+    super({ objectMode: true });
+    this.logger = logger;
+  }
+
+  /**
+   * @private
+   */
+  _transform(data, enc, callback) {
+    let [rpc, ident] = data;
+
+    if (!ident.payload.params[0] || !ident.payload.params[1]) {
+      return callback();
+    }
+
+    if (rpc.payload.method) {
+      this.logger.info(
+        `received ${rpc.payload.method} (${rpc.payload.id}) from ` +
+        `${ident.payload.params[0]} ` +
+        `(${ident.payload.params[1].hostname}:` +
+        `${ident.payload.params[1].port})`
+      );
+    } else {
+      this.logger.info(
+        `received response from ${ident.payload.params[0]} to ` +
+        `${rpc.payload.id}`
+      );
+    }
+
+    callback(null, data);
+  }
+
+}
+
+/**
+ * Logs all outgoing messages
+ */
+class OutgoingMessageLogger extends Transform {
+
+  /**
+   * @constructor
+   * @param {AbstractNode~logger} logger - Logger to use
+   */
+  constructor(logger) {
+    super({ objectMode: true });
+    this.logger = logger;
+  }
+
+  /**
+   * @private
+   */
+  _transform(data, enc, callback) {
+    let [rpc,, recv] = data;
+
+    if (!recv[0] || !recv[1]) {
+      return callback();
+    }
+
+    if (rpc.method) {
+      this.logger.info(
+        `sending ${rpc.method} (${rpc.id}) to ${recv[0]} ` +
+        `(${recv[1].hostname}:${recv[1].port})`
+      );
+    } else {
+      this.logger.info(
+        `sending response to ${recv[0]} for ${rpc.id}`
+      );
+    }
+
+    callback(null, data);
+  }
+
+}
+
+/**
+ * Attaches a verbose logger to a {@link AbstractNode}
+ * @param {AbstractNode~logger} [logger] - Custom logger
+ */
+module.exports = function(logger) {
+  logger = logger = bunyan.createLogger({ name: 'kadence' });
+
+  return function(node) {
+    node.rpc.deserializer.append(() => new IncomingMessageLogger(logger));
+    node.rpc.serializer.prepend(() => new OutgoingMessageLogger(logger));
+
+    return logger;
+  };
+};
+
+module.exports.IncomingMessage = IncomingMessageLogger;
+module.exports.OutgoingMessage = OutgoingMessageLogger;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-onion.js.html b/docs/plugin-onion.js.html new file mode 100644 index 0000000..bc9aad0 --- /dev/null +++ b/docs/plugin-onion.js.html @@ -0,0 +1,292 @@ + + + + + + plugin-onion.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-onion.js

+ + + + + + + +
+
+
/**
+ * @module kadence/onion
+ */
+
+'use strict';
+
+const os = require('os');
+const fs = require('fs');
+const path = require('path');
+const split = require('split');
+const merge = require('merge');
+const socks = require('socks');
+const hsv3 = require('@tacticalchihuahua/granax/hsv3');
+
+
+/**
+ * SOCKS5 proxy plugin, wraps HTTP* transports createRequest method
+ */
+class OnionPlugin {
+
+  static get DEFAULTS() {
+    return {
+      dataDirectory: path.join(os.tmpdir(), 'kad-onion-default'),
+      virtualPort: 80,
+      localMapping: '127.0.0.1:8080',
+      passthroughLoggingEnabled: false,
+      torrcEntries: {},
+      socksVersion: 5
+    };
+  }
+
+  /**
+   * Creates the transport wrapper for using a SOCKS5 proxy
+   * @constructor
+   * @param {object} node
+   * @param {object} [options]
+   * @param {string} [options.dataDirectory] - Write hidden service data
+   * @param {number} [options.virtualPort] - Virtual hidden service port
+   * @param {string} [options.localMapping] - IP/Port string of target service
+   * @param {object} [options.torrcEntries] - Additional torrc entries
+   * @param {boolean} [options.passthroughLoggingEnabled] - Passthrough tor log
+   */
+  constructor(node, options) {
+    this._opts = merge(OnionPlugin.DEFAULTS, options);
+    this.logger = node.logger;
+    this.node = node;
+    this.node.onion = this;
+
+    this._wrapNodeListen(node);
+  }
+
+  /**
+   * Returns an agent instance to use for the provided target
+   * @returns {Agent}
+   */
+  createSecureAgent() {
+    return new socks.Agent({
+      proxy: {
+        ipaddress: '127.0.0.1',
+        port: this.socksPort,
+        type: this._opts.socksVersion
+      },
+      timeout: 30000
+    }, true, false);
+  }
+
+  /**
+   * Returns a clear text agent instance to use for the provided target
+   * @returns {Agent}
+   */
+  createClearAgent() {
+    return new socks.Agent({
+      proxy: {
+        ipaddress: '127.0.0.1',
+        port: this.socksPort,
+        type: this._opts.socksVersion
+      },
+      timeout: 30000
+    }, false, false);
+  }
+
+  /**
+   * @private
+   */
+  _wrapTransportRequest(transport) {
+    this._createRequest = this._createRequest ||
+                          transport._createRequest.bind(transport);
+
+    transport._createRequest = (options) => {
+      options.agent = options.protocol === 'https:'
+        ? this.createSecureAgent()
+        : this.createClearAgent();
+
+      return this._createRequest(options);
+    };
+  }
+
+  /**
+   * @private
+   */
+  _waitForBootstrap() {
+    return new Promise(resolve => {
+      this.tor.on('STATUS_CLIENT', (status) => {
+        let notice = status[0].split(' ')[1];
+        let summary = null;
+
+        if (status[0].includes('SUMMARY')) {
+          summary = status[0].split('SUMMARY="');
+          summary = summary[summary.length - 1].split('"')[0];
+        }
+
+        if (notice === 'CIRCUIT_ESTABLISHED') {
+          this.logger.info('connected to the tor network');
+          this.tor.removeEventListeners(() => resolve());
+        } else if (summary) {
+          this.logger.info('bootstrapping tor, ' + summary.toLowerCase());
+        }
+      });
+
+      this.tor.addEventListeners(['STATUS_CLIENT'], () => {
+        this.logger.info('listening for bootstrap status for tor client');
+      });
+    });
+  }
+
+  /**
+   * @private
+   */
+  _getSocksProxyPort() {
+    return new Promise((resolve, reject) => {
+      this.logger.info('connected to tor control port');
+      this.logger.info('querying tor for socks proxy port');
+
+      this.tor.getInfo('net/listeners/socks', (err, result) => {
+        if (err) {
+          return reject(err);
+        }
+
+        let [, socksPort] = result.replace(/"/g, '').split(':');
+        this.socksPort = parseInt(socksPort);
+
+        resolve(this.socksPort);
+      });
+    });
+  }
+
+  /**
+   * @private
+   */
+  async _setupTorController() {
+    return new Promise((resolve, reject) => {
+      this.tor = hsv3([
+        {
+          dataDirectory: path.join(this._opts.dataDirectory, 'hidden_service'),
+          virtualPort: this._opts.virtualPort,
+          localMapping: this._opts.localMapping
+        }
+      ], merge(this._opts.torrcEntries, {
+        DataDirectory: this._opts.dataDirectory
+      }));
+
+      this.tor.on('error', reject).on('ready', async () => {
+        await this._waitForBootstrap();
+        await this._getSocksProxyPort();
+
+        this.node.contact.hostname = fs.readFileSync(
+          path.join(this._opts.dataDirectory, 'hidden_service', 'hostname')
+        ).toString().trim();
+        this.node.contact.port = this._opts.virtualPort;
+
+        this._wrapTransportRequest(this.node.transport);
+        resolve();
+      });
+
+      if (this._opts.passthroughLoggingEnabled) {
+        this.tor.process.stdout.pipe(split()).on('data', (data) => {
+          let message = data.toString().split(/\[(.*?)\]/);
+
+          message.shift(); // NB: Remove timestamp
+          message.shift(); // NB: Remove type
+          message[0] = message[0] ? message[0].trim() : ''; // NB: Trim white
+          message = message.join(''); // NB: Put it back together
+
+          this.logger.info(`tor process: ${message}`);
+        });
+      }
+    });
+  }
+
+  /**
+   * @private
+   */
+  async _wrapNodeListen(node) {
+    const listen = node.listen.bind(node);
+
+    node.listen = async (port, address, callback) => {
+      this.logger.info('spawning tor client and controller');
+
+      if (typeof address === 'function') {
+        callback = address;
+        address = '127.0.0.1';
+      }
+
+      try {
+        await this._setupTorController();
+      } catch (err) {
+        return node.emit('error', err);
+      }
+
+      listen(port, address, callback);
+    };
+  }
+
+}
+
+/**
+ * Registers a {@link OnionPlugin} with an {@link AbstractNode}
+ * @param {object} node
+ * @param {object} [options]
+ * @param {string} [options.dataDirectory] - Write hidden service data
+ * @param {number} [options.virtualPort] - Virtual hidden service port
+ * @param {string} [options.localMapping] - IP/Port string of target service
+ * @param {object} [options.torrcEntries] - Additional torrc entries
+ * @param {boolean} [options.passthroughLoggingEnabled] - Passthrough tor log
+ */
+module.exports = function(options) {
+  return function(node) {
+    return new OnionPlugin(node, options);
+  }
+};
+
+module.exports.OnionPlugin = OnionPlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-quasar.js.html b/docs/plugin-quasar.js.html new file mode 100644 index 0000000..de0f93d --- /dev/null +++ b/docs/plugin-quasar.js.html @@ -0,0 +1,507 @@ + + + + + + plugin-quasar.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-quasar.js

+ + + + + + + +
+
+
/**
+ * @module kadence/quasar
+ */
+
+'use strict';
+
+const assert = require('assert');
+const merge = require('merge');
+const async = require('async');
+const { knuthShuffle } = require('knuth-shuffle');
+const uuid = require('uuid');
+const constants = require('./constants');
+const utils = require('./utils');
+const BloomFilter = require('atbf');
+const LruCache = require('lru-cache');
+
+
+/**
+ * Implements the handlers for Quasar message types
+ */
+class QuasarRules {
+
+  /**
+   * @constructor
+   * @param {module:kadence/quasar~QuasarPlugin} quasar
+   */
+  constructor(quasar) {
+    this.quasar = quasar;
+  }
+
+  /**
+   * Upon receipt of a PUBLISH message, we validate it, then check if we or
+   * our neighbors are subscribed. If we are subscribed, we execute our
+   * handler. If our neighbors are subscribed, we relay the publication to
+   * ALPHA random of the closest K. If our neighbors are not subscribed, we
+   * relay the publication to a random contact
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   * @param {AbstractNode~next} next
+   */
+  publish(request, response, next) {
+    /* eslint max-statements: [2, 18] */
+    let { ttl, topic, uuid, contents } = request.params;
+    let neighbors = [...this.quasar.node.router.getClosestContactsToKey(
+      this.quasar.node.identity,
+      constants.K
+    ).entries()];
+
+    if (this.quasar.cached.get(uuid)) {
+      return next(new Error('Message previously routed'));
+    }
+
+    if (ttl > constants.MAX_RELAY_HOPS || ttl < 0) {
+      return next(new Error('Message includes invalid TTL'));
+    }
+
+    neighbors = knuthShuffle(neighbors.filter(([nodeId]) => {
+      return request.params.publishers.indexOf(nodeId) === -1;
+    })).splice(0, constants.ALPHA);
+
+    request.params.publishers.push(this.quasar.node.identity.toString('hex'));
+    this.quasar.cached.set(uuid, Date.now());
+
+    if (this.quasar.isSubscribedTo(topic)) {
+      this.quasar.groups.get(topic)(contents, topic);
+
+      async.each(neighbors, (contact, done) => {
+        this._relayPublication(request, contact, done);
+      });
+      return response.send([]);
+    }
+
+    if (ttl - 1 === 0) {
+      return response.send([]);
+    }
+
+    async.each(neighbors, (contact, done) => {
+      this.quasar.pullFilterFrom(contact, (err, filter) => {
+        if (err) {
+          return done();
+        }
+
+        if (!QuasarRules.shouldRelayPublication(request, filter)) {
+          contact = this.quasar._getRandomContact();
+        }
+
+        this._relayPublication(request, contact, done);
+      });
+    });
+    response.send([]);
+  }
+
+  /**
+   * Upon receipt of a SUBSCRIBE message, we simply respond with a serialized
+   * version of our attenuated bloom filter
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   */
+  subscribe(request, response) {
+    response.send(this.quasar.filter.toHexArray());
+  }
+
+  /**
+   * Upon receipt of an UPDATE message we merge the delivered attenuated bloom
+   * filter with our own
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   * @param {AbstractNode~next} next
+   */
+  update(request, response, next) {
+    if (!Array.isArray(request.params)) {
+      return next(new Error('Invalid bloom filters supplied'));
+    }
+
+    try {
+      request.params.forEach(str => assert(utils.isHexaString(str),
+        'Invalid hex string'));
+      this.quasar.filter.merge(BloomFilter.from(request.params));
+    } catch (err) {
+      return next(err);
+    }
+
+    response.send([]);
+  }
+
+  /**
+   * Returns a boolean indicating if we should relay the message to the contact
+   * @param {AbstractNode~request} request
+   * @param {array} attenuatedBloomFilter - List of topic bloom filters
+   */
+  static shouldRelayPublication(request, filter) {
+    let negated = true;
+
+    filter.forEach((level) => {
+      if (level.has(request.params.topic)) {
+        negated = false;
+      }
+    });
+
+    request.params.publishers.forEach((pub) => {
+      filter.forEach((level) => {
+        if (level.has(pub)) {
+          negated = true;
+        }
+      });
+    });
+
+    return !negated;
+  }
+
+  /**
+   * Takes a request object for a publication and relays it to the supplied
+   * contact
+   * @private
+   */
+  _relayPublication(request, contact, callback) {
+    this.quasar.node.send(
+      request.method,
+      merge({}, request.params, { ttl: request.params.ttl - 1 }),
+      contact,
+      callback
+    );
+  }
+
+}
+
+
+/**
+ * Implements the primary interface for the publish-subscribe system
+ * and decorates the given node object with it's public methods
+ */
+class QuasarPlugin {
+
+  static get PUBLISH_METHOD() {
+    return 'PUBLISH';
+  }
+
+  static get SUBSCRIBE_METHOD() {
+    return 'SUBSCRIBE';
+  }
+
+  static get UPDATE_METHOD() {
+    return 'UPDATE';
+  }
+
+  /**
+   * @constructor
+   * @param {KademliaNode} node
+   */
+  constructor(node) {
+    const handlers = new QuasarRules(this);
+
+    this.cached = new LruCache(constants.LRU_CACHE_SIZE)
+    this.groups = new Map();
+    this.filter = new BloomFilter({
+      filterDepth: constants.FILTER_DEPTH,
+      bitfieldSize: constants.B
+    });
+    this._lastUpdate = 0;
+
+    this.node = node;
+    this.node.quasarSubscribe = this.quasarSubscribe.bind(this);
+    this.node.quasarPublish = this.quasarPublish.bind(this);
+
+    this.node.use(QuasarPlugin.UPDATE_METHOD, handlers.update.bind(handlers));
+    this.node.use(QuasarPlugin.PUBLISH_METHOD,
+      handlers.publish.bind(handlers));
+    this.node.use(QuasarPlugin.SUBSCRIBE_METHOD,
+      handlers.subscribe.bind(handlers));
+
+    this.filter[0].add(this.node.identity.toString('hex'));
+  }
+
+  /**
+   * Returns our ALPHA closest neighbors
+   * @property {Bucket~contact[]} neighbors
+   */
+  get neighbors() {
+    return [...this.node.router.getClosestContactsToKey(
+      this.node.identity.toString('hex'),
+      constants.ALPHA
+    ).entries()];
+  }
+
+  /**
+   * Publishes the content to the network by selecting ALPHA contacts closest
+   * to the node identity (or the supplied routing key). Errors if message is
+   * unable to be delivered to any contacts. Tries to deliver to ALPHA contacts
+   * until exhausted.
+   * @param {string} topic - Identifier for subscribers
+   * @param {object} contents - Arbitrary publication payload
+   * @param {object} [options]
+   * @param {string} [options.routingKey] - Publish to neighbors close to this
+   * key instead of our own identity
+   * @param {QuasarPlugin~quasarPublishCallback} [callback]
+   */
+  quasarPublish(topic, contents, options = {}, callback = () => null) {
+    if (typeof options === 'function') {
+      callback = options;
+      options = {};
+    }
+
+    const publicationId = uuid.v4();
+    const neighbors = [...this.node.router.getClosestContactsToKey(
+      options.routingKey || this.node.identity.toString('hex'),
+      this.node.router.size
+    ).entries()];
+
+    let deliveries = [];
+
+    async.until(() => {
+      return deliveries.length === constants.ALPHA || !neighbors.length;
+    }, done => {
+      const candidates = [];
+
+      for (let i = 0; i < constants.ALPHA - deliveries.length; i++) {
+        candidates.push(neighbors.shift());
+      }
+
+      async.each(candidates, (contact, next) => {
+        this.node.send(QuasarPlugin.PUBLISH_METHOD, {
+          uuid: publicationId,
+          topic,
+          contents,
+          publishers: [this.node.identity.toString('hex')],
+          ttl: constants.MAX_RELAY_HOPS
+        }, contact, err => {
+          if (err) {
+            this.node.logger.warn(err.message);
+          } else {
+            deliveries.push(contact);
+          }
+
+          next();
+        });
+      }, done);
+    }, err => {
+      if (!err && deliveries.length === 0) {
+        err = new Error('Failed to deliver any publication messages');
+      }
+
+      callback(err, deliveries);
+    });
+  }
+  /**
+   * @callback QuasarPlugin~quasarPublishCallback
+   * @param {error|null} err
+   * @param {Bucket~contact[]} deliveries
+   */
+
+  /**
+   * Publishes the content to the network
+   * @param {string|string[]} topics - Identifier for subscribers
+   * @param {QuasarPlugin~quasarSubscribeHandler} handler
+   */
+  quasarSubscribe(topics, handler) {
+    const self = this;
+
+    if (Array.isArray(topics)) {
+      topics.forEach((topic) => addTopicToFilter(topic));
+    } else {
+      addTopicToFilter(topics);
+    }
+
+    function addTopicToFilter(topic) {
+      self.filter[0].add(topic);
+      self.groups.set(topic, handler);
+    }
+
+    this.pullFilters(() => this.pushFilters());
+  }
+  /**
+   * @callback QuasarPlugin~quasarSubscribeHandler
+   * @param {object} publicationContent
+   */
+
+  /**
+   * Requests neighbor bloom filters and merges with our records
+   * @param {function} [callback]
+   */
+  pullFilters(callback = () => null) {
+    const now = Date.now();
+
+    if (this._lastUpdate > now - constants.SOFT_STATE_TIMEOUT) {
+      return callback();
+    } else {
+      this._lastUpdate = now;
+    }
+
+    async.each(this.neighbors, (contact, done) => {
+      this.pullFilterFrom(contact, (err, filter) => {
+        if (err) {
+          this.node.logger.warn('failed to pull filter from %s, reason: %s',
+            contact[0], err.message);
+        } else {
+          this.filter.merge(filter);
+        }
+
+        done(err);
+      });
+    }, callback);
+  }
+
+  /**
+   * Requests the attenuated bloom filter from the supplied contact
+   * @param {Bucket~contact} contact
+   * @param {function} callback
+   */
+  pullFilterFrom(contact, callback) {
+    const method = QuasarPlugin.SUBSCRIBE_METHOD;
+
+    this.node.send(method, [], contact, (err, result) => {
+      if (err) {
+        return callback(err);
+      }
+
+      try {
+        result.forEach(str => assert(utils.isHexaString(str),
+          'Invalid hex string'));
+        return callback(null, BloomFilter.from(result));
+      } catch (err) {
+        return callback(err);
+      }
+    });
+  }
+
+  /**
+   * Notifies neighbors that our subscriptions have changed
+   * @param {function} [callback]
+   */
+  pushFilters(callback = () => null) {
+    const now = Date.now();
+
+    if (this._lastUpdate > now - constants.SOFT_STATE_TIMEOUT) {
+      return callback();
+    } else {
+      this._lastUpdate = now;
+    }
+
+    async.each(this.neighbors, (contact, done) => {
+      this.pushFilterTo(contact, done);
+    }, callback);
+  }
+
+  /**
+   * Sends our attenuated bloom filter to the supplied contact
+   * @param {Bucket~contact} contact
+   * @param {function} callback
+   */
+  pushFilterTo(contact, callback) {
+    this.node.send(QuasarPlugin.UPDATE_METHOD, this.filter.toHexArray(),
+      contact, callback);
+  }
+
+  /**
+   * Check if we are subscribed to the topic
+   * @param {string} topic - Topic to check subscription
+   * @returns {boolean}
+   */
+  isSubscribedTo(topic) {
+    return this.filter[0].has(topic) && this.groups.has(topic);
+  }
+
+  /**
+   * Check if our neighbors are subscribed to the topic
+   * @param {string} topic - Topic to check subscription
+   * @returns {boolean}
+   */
+  hasNeighborSubscribedTo(topic) {
+    let index = 1;
+
+    while (this.filter[index]) {
+      if (this.filter[index].has(topic)) {
+        return true;
+      } else {
+        index++;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * Returns a random contact from the routing table
+   * @private
+   */
+  _getRandomContact() {
+    return knuthShuffle([...this.node.router.getClosestContactsToKey(
+      this.node.identity.toString('hex'),
+      this.node.router.size,
+      true
+    ).entries()]).shift();
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/quasar~QuasarPlugin} with a {@link KademliaNode}
+ */
+module.exports = function() {
+  return function(node) {
+    return new QuasarPlugin(node);
+  };
+};
+
+module.exports.QuasarPlugin = QuasarPlugin;
+module.exports.QuasarRules = QuasarRules;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-rolodex.js.html b/docs/plugin-rolodex.js.html new file mode 100644 index 0000000..d15f799 --- /dev/null +++ b/docs/plugin-rolodex.js.html @@ -0,0 +1,282 @@ + + + + + + plugin-rolodex.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-rolodex.js

+ + + + + + + +
+
+
/**
+ * @module kadence/rolodex
+ */
+
+'use strict';
+
+const fs = require('fs');
+const utils = require('./utils');
+const { EventEmitter } = require('events');
+
+
+/**
+ * Keeps track of seen contacts in a compact file so they can be used as
+ * bootstrap nodes
+ */
+class RolodexPlugin extends EventEmitter {
+
+  static get EXTERNAL_PREFIX() {
+    return 'external';
+  }
+
+  static get INTERNAL_PREFIX() {
+    return 'internal';
+  }
+
+  /**
+   * @constructor
+   * @param {KademliaNode} node
+   * @param {string} peerCacheFilePath - Path to file to use for storing peers
+   */
+  constructor(node, peerCacheFilePath) {
+    super();
+
+    this._peerCacheFilePath = peerCacheFilePath;
+    this._cache = {};
+    this.node = node;
+
+    // When a contact is added to the routing table, cache it
+    this.node.router.events.on('add', identity => {
+      this.node.logger.debug(`updating cached peer profile ${identity}`);
+      const contact = this.node.router.getContactByNodeId(identity);
+      if (contact) {
+        contact.timestamp = Date.now();
+        this.setExternalPeerInfo(identity, contact);
+      }
+    });
+
+    // When a contact is dropped from the routing table, remove it from cache
+    this.node.router.events.on('remove', identity => {
+      this.node.logger.debug(`dropping cached peer profile ${identity}`);
+      delete this._cache[`${RolodexPlugin.EXTERNAL_PREFIX}:${identity}`];
+      delete this._cache[`${RolodexPlugin.INTERNAL_PREFIX}:${identity}`];
+    });
+
+    this._sync();
+  }
+
+  /**
+   * @private
+   */
+  _sync() {
+    const _syncRecursive = () => {
+      setTimeout(() => {
+        this._syncToFile().then(() => {
+          _syncRecursive();
+        }, (err) => {
+          this.node.logger.error(`failed to write peer cache, ${err.message}`);
+        });
+      }, 60 * 1000);
+    };
+
+    this._syncFromFile().then(() => {
+      _syncRecursive();
+    }, (err) => {
+      this.node.logger.error(`failed to read peer cache, ${err.message}`);
+      _syncRecursive();
+    });
+  }
+
+  /**
+   * @private
+   */
+  _syncToFile() {
+    return new Promise((resolve, reject) => {
+      if (!this._peerCacheFilePath) {
+        return resolve();
+      }
+
+      fs.writeFile(
+        this._peerCacheFilePath,
+        JSON.stringify(this._cache),
+        (err) => {
+          if (err) {
+            reject(err);
+          } else {
+            resolve();
+          }
+        }
+      );
+    });
+  }
+
+  /**
+   * @private
+   */
+  _syncFromFile() {
+    return new Promise((resolve, reject) => {
+      if (!this._peerCacheFilePath) {
+        return resolve();
+      }
+
+      fs.readFile(this._peerCacheFilePath, (err, data) => {
+        if (err) {
+          return reject(err);
+        }
+
+        try {
+          this._cache = JSON.parse(data.toString());
+        } catch (err) {
+          return reject(err);
+        }
+
+        resolve();
+      });
+    });
+  }
+
+  /**
+   * Returns a list of bootstrap nodes from local profiles
+   * @returns {string[]} urls
+   */
+  getBootstrapCandidates() {
+    const candidates = [];
+    return new Promise(resolve => {
+      for (let key in this._cache) {
+        const [prefix, identity] = key.split(':');
+
+        /* istanbul ignore else */
+        if (prefix === RolodexPlugin.EXTERNAL_PREFIX) {
+          candidates.push([identity, this._cache[key]]);
+        }
+      }
+
+      resolve(candidates.sort((a, b) => b[1].timestamp - a[1].timestamp)
+        .map(utils.getContactURL));
+    });
+  }
+
+  /**
+   * Returns the external peer data for the given identity
+   * @param {string} identity - Identity key for the peer
+   * @returns {object}
+   */
+  getExternalPeerInfo(identity) {
+    return new Promise((resolve, reject) => {
+      const data = this._cache[`${RolodexPlugin.EXTERNAL_PREFIX}:${identity}`];
+      /* istanbul ignore if */
+      if (!data) {
+        reject(new Error('Peer not found'));
+      } else {
+        resolve(data);
+      }
+    });
+  }
+
+  /**
+   * Returns the internal peer data for the given identity
+   * @param {string} identity - Identity key for the peer
+   * @returns {object}
+   */
+  getInternalPeerInfo(identity) {
+    return new Promise((resolve, reject) => {
+      const data = this._cache[`${RolodexPlugin.INTERNAL_PREFIX}:${identity}`];
+      /* istanbul ignore if */
+      if (!data) {
+        reject(new Error('Peer not found'));
+      } else {
+        resolve(data);
+      }
+    });
+  }
+
+  /**
+   * Returns the external peer data for the given identity
+   * @param {string} identity - Identity key for the peer
+   * @param {object} data - Peer's external contact information
+   * @returns {object}
+   */
+  setExternalPeerInfo(identity, data) {
+    return new Promise((resolve) => {
+      this._cache[`${RolodexPlugin.EXTERNAL_PREFIX}:${identity}`] = data;
+      resolve(data);
+    });
+  }
+
+  /**
+   * Returns the internal peer data for the given identity
+   * @param {string} identity - Identity key for the peer
+   * @param {object} data - Our own internal peer information
+   * @returns {object}
+   */
+  setInternalPeerInfo(identity, data) {
+    return new Promise((resolve) => {
+      this._cache[`${RolodexPlugin.INTERNAL_PREFIX}:${identity}`] = data;
+      resolve(data);
+    });
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/rolodex~RolodexPlugin} with a
+ * {@link KademliaNode}
+ * @param {string} peerCacheFilePath - Path to file to use for storing peers
+ */
+module.exports = function(peerCacheFilePath) {
+  return function(node) {
+    return new RolodexPlugin(node, peerCacheFilePath);
+  }
+};
+
+module.exports.RolodexPlugin = RolodexPlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-spartacus.js.html b/docs/plugin-spartacus.js.html new file mode 100644 index 0000000..21cd645 --- /dev/null +++ b/docs/plugin-spartacus.js.html @@ -0,0 +1,280 @@ + + + + + + plugin-spartacus.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-spartacus.js

+ + + + + + + +
+
+
/**
+ * @module kadence/spartacus
+ */
+
+'use strict';
+
+const merge = require('merge');
+const assert = require('assert');
+const secp256k1 = require('secp256k1');
+const utils = require('./utils');
+const jsonrpc = require('jsonrpc-lite');
+const { Transform } = require('stream');
+
+
+/**
+ * Implements the spartacus decorations to the node object
+ */
+class SpartacusPlugin {
+
+  static get DEFAULTS() {
+    return {
+      checkPublicKeyHash: true
+    };
+  }
+
+  /**
+   * Creates the plugin instance given a node and optional identity
+   * @constructor
+   * @param {KademliaNode} node
+   * @param {buffer} [privateKey] - SECP256K1 private key
+   * @param {object} [options={}]
+   * @param {boolean} [options.checkPublicKeyHash=true]
+   */
+  constructor(node, priv, opts) {
+    priv = priv || utils.generatePrivateKey();
+
+    this.opts = merge(SpartacusPlugin.DEFAULTS, opts);
+    this.privateKey = priv;
+    this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
+    this.identity = utils.toPublicKeyHash(this.publicKey);
+    this._validatedContacts = new Map();
+    this._pendingValidators = new Map();
+
+    node.contact.pubkey = this.publicKey.toString('hex');
+    node.identity = node.router.identity = this.identity;
+
+    node.rpc.serializer.append(() => new Transform({
+      transform: this.serialize.bind(this),
+      objectMode: true
+    }));
+    node.rpc.deserializer.prepend(() => new Transform({
+      transform: this.deserialize.bind(this),
+      objectMode: true
+    }));
+    node.use((req, res, next) => this.validate(node, req, res, next));
+    this.setValidationPeriod();
+  }
+
+  /**
+   * Sets the validation period for nodes
+   * @param {number} period - Milliseconds to honor a proven contact response
+   */
+  setValidationPeriod(n = 10800000) {
+    this._validationPeriod = n;
+  }
+
+  /**
+   * Checks if the sender is addressable at the claimed contact information
+   * and cross checks signatures between the original sender and the node
+   * addressed. This is intended to prevent reflection attacks and general
+   * DDoS via spam.
+   * @param {KademliaNode} node
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   * @param {AbstractNode~next} next
+   */
+  validate(node, req, res, next) {
+    const period = this._validationPeriod;
+    const record = this._validatedContacts.get(req.contact[0]);
+    const validated = record && record.validated;
+    const fresh = validated && ((Date.now() - record.timestamp) < period);
+
+    if (this._pendingValidators.get(req.contact[0])) {
+      return next(); // NB: Let's not get into an infinte validation loop
+    }
+
+    if (validated && fresh) {
+      return next();
+    }
+
+    this._pendingValidators.set(req.contact[0], req.contact[1]);
+    node.ping(req.contact, (err) => {
+      this._pendingValidators.delete(req.contact[0]);
+
+      if (err) {
+        return this._validatedContacts.set(req.contact[0], {
+          validated: false,
+          timestamp: Date.now()
+        });
+      }
+
+      this._validatedContacts.set(req.contact[0], {
+        validated: true,
+        timestamp: Date.now()
+      });
+      next();
+    });
+  }
+
+  /**
+   * Processes with JsonRpcSerializer then signs the result and appends an
+   * additional payload containing signature+identity information
+   * @implements {Messenger~serializer}
+   */
+  serialize(data, encoding, callback) {
+    let [id, buffer, target] = data;
+    let payload = jsonrpc.parse(buffer.toString('utf8')).map((obj) => {
+      return obj.payload;
+    });
+    let { signature, recovery } = secp256k1.sign(
+      utils._sha256(buffer),
+      this.privateKey
+    );
+    let authenticate = jsonrpc.notification('AUTHENTICATE', [
+      Buffer.concat([Buffer.from([recovery]), signature]).toString('base64'),
+      this.publicKey.toString('hex')
+    ]);
+
+    payload.push(authenticate);
+    callback(null, [
+      id,
+      Buffer.from(JSON.stringify(payload), 'utf8'),
+      target
+    ]);
+  }
+
+  /**
+   * Parses and verifies the signature payload, then passes through to the
+   * JsonRpcDeserializer if successful
+   * @implements {Messenger~deserializer}
+   */
+  deserialize(buffer, encoding, callback) {
+    /* eslint max-statements: [2, 30] */
+    /* eslint complexity: [2, 12] */
+    let payload = jsonrpc.parse(buffer.toString('utf8'))
+
+    try {
+      payload = payload.map(obj => {
+        assert(obj.type !== 'invalid');
+        return obj.payload;
+      });
+    } catch (err) {
+      return callback(new Error('Failed to parse received payload'));
+    }
+
+    let [, identify] = payload;
+    let authenticate = payload.filter(m => m.method === 'AUTHENTICATE').pop();
+
+    if (typeof authenticate === 'undefined') {
+      return callback(new Error('Missing authentication payload in message'));
+    }
+
+    let identity = Buffer.from(identify.params[0], 'hex');
+    let [signature, publicKey] = authenticate.params;
+
+    let signedPayload = [];
+
+    for (let i = 0; i < payload.length; i++) {
+      if (payload[i].method === 'AUTHENTICATE') {
+        break;
+      } else {
+        signedPayload.push(payload[i]);
+      }
+    }
+
+    signedPayload = utils._sha256(
+      Buffer.from(JSON.stringify(signedPayload), 'utf8')
+    );
+
+    let publicKeyHash = utils.toPublicKeyHash(Buffer.from(publicKey, 'hex'));
+    let pendingValid = this._pendingValidators.get(
+      identity.toString('hex')
+    );
+
+    if (pendingValid && pendingValid.pubkey !== publicKey) {
+      return callback(new Error('Failed pending contact validation'));
+    }
+
+    if (this.opts.checkPublicKeyHash && publicKeyHash.compare(identity) !== 0) {
+      return callback(new Error('Identity does not match public key'));
+    }
+
+    try {
+      assert.ok(secp256k1.verify(
+        signedPayload,
+        Buffer.from(signature, 'base64').slice(1),
+        Buffer.from(publicKey, 'hex')
+      ));
+    } catch (err) {
+      return callback(new Error('Message includes invalid signature'));
+    }
+
+    callback(null, buffer);
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/spartacus~SpartacusPlugin} with a
+ * {@link KademliaNode}
+ * @param {string} priv - Private key
+ * @param {object} opts - Plugin options
+ */
+module.exports = function(priv, opts) {
+  return function(node) {
+    return new SpartacusPlugin(node, priv, opts);
+  };
+};
+
+module.exports.SpartacusPlugin = SpartacusPlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-traverse.js.html b/docs/plugin-traverse.js.html new file mode 100644 index 0000000..d9559e1 --- /dev/null +++ b/docs/plugin-traverse.js.html @@ -0,0 +1,424 @@ + + + + + + plugin-traverse.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-traverse.js

+ + + + + + + +
+
+
/**
+ * @module kadence/traverse
+ */
+
+'use strict';
+
+const { createLogger } = require('bunyan');
+const ip = require('ip');
+const merge = require('merge');
+const async = require('async');
+const { get_gateway_ip: getGatewayIp } = require('network');
+const natpmp = require('nat-pmp');
+const natupnp = require('nat-upnp');
+const url = require('url');
+const diglet = require('@tacticalchihuahua/diglet');
+
+
+/**
+ * Establishes a series of NAT traversal strategies to execute before
+ * {@link AbstractNode#listen}
+ */
+class TraversePlugin {
+
+  static get TEST_INTERVAL() {
+    return 600000;
+  }
+
+  /**
+   * @constructor
+   * @param {KademliaNode} node
+   * @param {module:kadence/traverse~TraverseStrategy[]} strategies
+   */
+  constructor(node, strategies) {
+    this.node = node;
+    this.strategies = strategies;
+    this._originalContact = merge({}, node.contact);
+
+    this._wrapNodeListen();
+  }
+
+  /**
+   * @private
+   * @param {function} callback
+   */
+  _execTraversalStrategies(callback) {
+    async.detectSeries(this.strategies, (strategy, test) => {
+      this.node.logger.info(
+        `attempting nat traversal strategy ${strategy.constructor.name}`
+      );
+      this.node.contact = this._originalContact;
+      strategy.exec(this.node, (err) => {
+        if (err) {
+          this.node.logger.warn(err.message);
+          test(null, false);
+        } else {
+          this._testIfReachable(test);
+        }
+      });
+    }, callback);
+  }
+
+  /**
+   * @private
+   */
+  _startTestInterval() {
+    clearInterval(this._testInterval);
+
+    this._testInterval = setInterval(() => {
+      this._testIfReachable((err, isReachable) => {
+        /* istanbul ignore else */
+        if (!isReachable) {
+          this.node.logger.warn('no longer reachable, retrying traversal');
+          this._execTraversalStrategies(() => null);
+        }
+      });
+    }, TraversePlugin.TEST_INTERVAL);
+  }
+
+  /**
+   * @private
+   */
+  _testIfReachable(callback) {
+    if (!ip.isPublic(this.node.contact.hostname)) {
+      this.node.logger.warn('traversal strategy failed, not reachable');
+      return callback(null, false);
+    }
+
+    callback(null, true);
+  }
+
+  /**
+   * @private
+   */
+  _wrapNodeListen() {
+    const self = this;
+    const listen = this.node.listen.bind(this.node);
+
+    this.node.listen = function() {
+      let args = [...arguments];
+      let listenCallback = () => null;
+
+      if (typeof args[args.length - 1] === 'function') {
+        listenCallback = args.pop();
+      }
+
+      listen(...args, () => {
+        self._execTraversalStrategies((err, strategy) => {
+          if (err) {
+            self.node.logger.error('traversal errored %s', err.message);
+          } else if (!strategy) {
+            self.node.logger.warn('traversal failed - may not be reachable');
+          } else {
+            self.node.logger.info('traversal succeeded - you are reachable');
+          }
+
+          self._startTestInterval();
+          listenCallback();
+        });
+      });
+    };
+  }
+
+}
+
+/**
+ * Uses NAT-PMP to attempt port forward on gateway device
+ * @extends {module:kadence/traverse~TraverseStrategy}
+ */
+class NATPMPStrategy {
+
+  static get DEFAULTS() {
+    return {
+      publicPort: 0,
+      mappingTtl: 0,
+      timeout: 10000
+    };
+  }
+
+  /**
+   * @constructor
+   * @param {object} [options]
+   * @param {number} [options.publicPort=contact.port] - Port number to map
+   * @param {number} [options.mappingTtl=0] - TTL for port mapping on router
+   */
+  constructor(options) {
+    this.options = merge(NATPMPStrategy.DEFAULTS, options);
+  }
+
+  /**
+   * @param {KademliaNode} node
+   * @param {function} callback
+   */
+  exec(node, callback) {
+    async.waterfall([
+      (next) => getGatewayIp(next),
+      (gateway, next) => {
+        const timeout = setTimeout(() => {
+          next(new Error('NAT-PMP traversal timed out'));
+        }, this.options.timeout);
+        this.client = natpmp.connect(gateway);
+        this.client.portMapping({
+          public: this.options.publicPort || node.contact.port,
+          private: node.contact.port,
+          ttl: this.options.mappingTtl
+        }, err => {
+          clearTimeout(timeout);
+          next(err);
+        });
+      },
+      (next) => this.client.externalIp(next)
+    ], (err, info) => {
+      if (err) {
+        return callback(err);
+      }
+
+      node.contact.port = this.options.publicPort;
+      node.contact.hostname = info.ip.join('.');
+
+      callback(null);
+    });
+  }
+
+}
+
+/**
+ * Uses UPnP to attempt port forward on gateway device
+ * @extends {module:kadence/traverse~TraverseStrategy}
+ */
+class UPNPStrategy {
+
+  static get DEFAULTS() {
+    return {
+      publicPort: 0,
+      mappingTtl: 0
+    };
+  }
+
+  /**
+   * @constructor
+   * @param {object} [options]
+   * @param {number} [options.publicPort=contact.port] - Port number to map
+   * @param {number} [options.mappingTtl=0] - TTL for mapping on router
+   */
+  constructor(options) {
+    this.client = natupnp.createClient();
+    this.options = merge(UPNPStrategy.DEFAULTS, options);
+  }
+
+  /**
+   * @param {KademliaNode} node
+   * @param {function} callback
+   */
+  exec(node, callback) {
+    async.waterfall([
+      (next) => {
+        this.client.portMapping({
+          public: this.options.publicPort || node.contact.port,
+          private: node.contact.port,
+          ttl: this.options.mappingTtl
+        }, err => next(err));
+      },
+      (next) => this.client.externalIp(next)
+    ], (err, ip) => {
+      if (err) {
+        return callback(err);
+      }
+
+      node.contact.port = this.options.publicPort;
+      node.contact.hostname = ip;
+
+      callback(null);
+    });
+  }
+
+}
+
+/**
+ * Uses a secure reverse HTTPS tunnel via the Diglet package to traverse NAT.
+ * This requires a running Diglet server on the internet. By default, this
+ * plugin will use a test server operated by bookchin, but this may not be
+ * reliable or available. It is highly recommended to deploy your own Diglet
+ * server and configure your nodes to use them instead.
+ * There is {@link https://gitlab.com/bookchin/diglet detailed documentation}
+ * on deploying a Diglet server at the project page.
+ * @extends {module:kadence/traverse~TraverseStrategy}
+ */
+class ReverseTunnelStrategy {
+
+  static get DEFAULTS() {
+    return {
+      remoteAddress: 'tun.tacticalchihuahua.lol',
+      remotePort: 8443,
+      secureLocalConnection: false,
+      verboseLogging: false
+    };
+  }
+
+  /**
+   * @constructor
+   * @param {object} [options]
+   * @param {string} [options.remoteAddress=tunnel.bookch.in] - Diglet server address
+   * @param {number} [options.remotePort=8443] - Diglet server port
+   * @param {buffer} [options.privateKey] - SECP256K1 private key if using spartacus
+   * @param {boolean} [options.secureLocalConnection=false] - Set to true if using {@link HTTPSTransport}
+   * @param {boolean} [options.verboseLogging=false] - Useful for debugging
+   */
+  constructor(options) {
+    this.options = merge(ReverseTunnelStrategy.DEFAULTS, options);
+  }
+
+  /**
+   * @param {KademliaNode} node
+   * @param {function} callback
+   */
+  exec(node, callback) {
+    const opts = {
+      localAddress: '127.0.0.1',
+      localPort: node.contact.port,
+      remoteAddress: this.options.remoteAddress,
+      remotePort: this.options.remotePort,
+      logger: this.options.verboseLogging
+        ? node.logger
+        : createLogger({ name: 'kadence', level: 'warn' }),
+      secureLocalConnection: this.options.secureLocalConnection
+    };
+
+    if (this.options.privateKey) {
+      opts.privateKey = this.options.privateKey;
+    }
+
+    this.tunnel = new diglet.Tunnel(opts);
+
+    this.tunnel.once('connected', () => {
+      node.contact.hostname = url.parse(this.tunnel.url).hostname;
+      node.contact.port = 443;
+      node.contact.protocol = 'https:';
+
+      this.tunnel.removeListener('disconnected', callback);
+      callback()
+    });
+
+    this.tunnel.once('disconnected', callback);
+    this.tunnel.open();
+  }
+
+}
+
+/**
+ * @class
+ */
+class TraverseStrategy {
+
+  constructor() {}
+
+  /**
+   * @param {KademliaNode} node
+   * @param {function} callback - Called on travere complete or failed
+   */
+  exec(node, callback) {
+    callback(new Error('Not implemented'));
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/traverse~TraversePlugin} with an
+ * {@link AbstractNode}. Strategies are attempted in the order they are
+ * defined.
+ * @param {module:kadence/traverse~TraverseStrategy[]} strategies
+ * @example <caption>Proper Configuration</caption>
+ * const node = new kadence.KademliaNode(node_options);
+ * const keys = node.plugin(kadence.spartacus(key_options));
+ *
+ * node.plugin(kadence.traverse([
+ *   new kadence.traverse.UPNPStrategy({
+ *     publicPort: 8080,
+ *     mappingTtl: 0
+ *   }),
+ *   new kadence.traverse.NATPMPStrategy({
+ *     publicPort: 8080,
+ *     mappingTtl: 0
+ *   }),
+ *   new kadence.traverse.ReverseTunnelStrategy({
+ *     remoteAddress: 'my.diglet.server',
+ *     remotePort: 8443,
+ *     privateKey: keys.privateKey,
+ *     secureLocalConnection: false,
+ *     verboseLogging: false
+ *   })
+ * ]));
+ *
+ * node.listen(node.contact.port);
+ */
+module.exports = function(strategies) {
+  return function(node) {
+    return new module.exports.TraversePlugin(node, strategies);
+  };
+};
+
+module.exports.ReverseTunnelStrategy = ReverseTunnelStrategy;
+module.exports.UPNPStrategy = UPNPStrategy;
+module.exports.NATPMPStrategy = NATPMPStrategy;
+module.exports.TraverseStrategy = TraverseStrategy;
+module.exports.TraversePlugin = TraversePlugin;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/plugin-trust.js.html b/docs/plugin-trust.js.html new file mode 100644 index 0000000..e5305f0 --- /dev/null +++ b/docs/plugin-trust.js.html @@ -0,0 +1,256 @@ + + + + + + plugin-trust.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

plugin-trust.js

+ + + + + + + +
+
+
/**
+ * @module kadence/trust
+ */
+
+'use strict';
+
+const assert = require('assert');
+const utils = require('./utils');
+
+
+/**
+ * Handles user-defined rules for allowing and preventing the processing of
+ * messages from given identities
+ */
+class TrustPlugin {
+
+  /**
+   * @typedef {object} module:kadence/trust~TrustPlugin~policy
+   * @property {string|buffer} identity - Node identity key
+   * @property {string[]} methods - Methods, wildcard (*) supported for all
+   */
+
+  /**
+   * Validates the trust policy format
+   * @private
+   */
+  static validatePolicy(policy) {
+    assert(typeof policy === 'object', 'Invalid policy object');
+    assert(
+      utils.keyBufferIsValid(policy.identity) ||
+        utils.keyStringIsValid(policy.identity) || policy.identity === '*',
+      'Invalid policy identity'
+    );
+    assert(Array.isArray(policy.methods) && policy.methods.length,
+      'No policy methods defined');
+  }
+
+  /**
+   * Mode flag passed to {@link TrustPlugin} to place into blacklist mode
+   * @static
+   */
+  static get MODE_BLACKLIST() {
+    return 0x000;
+  }
+
+  /**
+   * Mode flag passed to {@link TrustPlugin} to place into whitelist mode
+   * @static
+   */
+  static get MODE_WHITELIST() {
+    return 0xfff;
+  }
+
+  /**
+   * @constructor
+   * @param {module:kadence/trust~TrustPlugin~policy[]} policies
+   * @param {number} [mode=TrustPlugin.MODE_BLACKLIST] - Blacklist or whitelist
+   */
+  constructor(node, policies = [], mode = TrustPlugin.MODE_BLACKLIST) {
+    assert([
+      TrustPlugin.MODE_BLACKLIST,
+      TrustPlugin.MODE_WHITELIST
+    ].includes(mode), `Invalid trust policy mode "${mode}"`);
+
+    this.mode = mode;
+    this.policies = new Map();
+    this.node = node;
+
+    policies.forEach(policy => this.addTrustPolicy(policy));
+
+    // NB: Automatically trust ourselves if this is a whitelist
+    if (this.mode === TrustPlugin.MODE_WHITELIST) {
+      this.addTrustPolicy({
+        identity: node.identity.toString('hex'),
+        methods: ['*']
+      });
+    }
+
+    const send = this.node.send.bind(this.node);
+
+    this.node.use(this._checkIncoming.bind(this));
+    this.node.send = (method, params, contact, callback) => {
+      this._checkOutgoing(method, contact, err => {
+        if (err) {
+          return callback(err);
+        }
+        send(method, params, contact, callback);
+      });
+    };
+  }
+
+  /**
+   * Checks the incoming message
+   * @private
+   */
+  _checkIncoming(request, response, callback) {
+    const [identity] = request.contact;
+    const method = request.method;
+    const policy = this.getTrustPolicy(identity);
+
+    this._checkPolicy(identity, method, policy, callback);
+  }
+
+  /**
+   * Checks the outgoing message
+   * @private
+   */
+  _checkOutgoing(method, contact, callback) {
+    const [identity] = contact;
+    const policy = this.getTrustPolicy(identity);
+
+    this._checkPolicy(identity, method, policy, callback);
+  }
+
+  /**
+   * Checks policy against identity and method
+   * @private
+   */
+  _checkPolicy(identity, method, policy, next) {
+    /* eslint complexity: [2, 10] */
+    switch (this.mode) {
+      case TrustPlugin.MODE_BLACKLIST:
+        if (!policy) {
+          next();
+        } else if (policy.includes('*') || policy.includes(method)) {
+          next(new Error(`Refusing to handle ${method} message to/from ` +
+            `${identity} due to trust policy`));
+        } else {
+          next();
+        }
+        break;
+      case TrustPlugin.MODE_WHITELIST:
+        if (!policy) {
+          next(new Error(`Refusing to handle ${method} message to/from ` +
+            `${identity} due to trust policy`));
+        } else if (policy.includes('*') || policy.includes(method)) {
+          next();
+        } else {
+          next(new Error(`Refusing to handle ${method} message to/from ` +
+            `${identity} due to trust policy`));
+        }
+        break;
+      default:
+        /* istanbul ignore next */
+        throw new Error('Failed to determine trust mode');
+    }
+  }
+
+  /**
+   * Adds a new trust policy
+   * @param {module:kadence/trust~TrustPlugin~policy} policy
+   * @returns {TrustPlugin}
+   */
+  addTrustPolicy(policy) {
+    TrustPlugin.validatePolicy(policy);
+    this.policies.set(policy.identity.toString('hex'), policy.methods);
+    return this;
+  }
+
+  /**
+   * Returns the trust policy for the given identity
+   * @param {string|buffer} identity - Identity key for the policy
+   * @returns {module:kadence/trust~TrustPlugin~policy|null}
+   */
+  getTrustPolicy(identity) {
+    return this.policies.get(identity.toString('hex')) ||
+      this.policies.get('*');
+  }
+
+  /**
+   * Removes an existing trust policy
+   * @param {string|buffer} identity - Trust policy to remove
+   * @returns {TrustPlugin}
+   */
+  removeTrustPolicy(identity) {
+    this.policies.delete(identity.toString('hex'));
+    return this;
+  }
+
+}
+
+/**
+ * Registers a {@link module:kadence/trust~TrustPlugin} with a
+ * {@link KademliaNode}
+ * @param {module:kadence/trust~TrustPlugin~policy[]} policies
+ * @param {number} [mode=TrustPlugin.MODE_BLACKLIST] - Blacklist or whitelist
+ */
+module.exports = function(policies, mode) {
+  return function(node) {
+    return new TrustPlugin(node, policies, mode);
+  }
+};
+
+module.exports.TrustPlugin = TrustPlugin;
+module.exports.MODE_BLACKLIST = TrustPlugin.MODE_BLACKLIST;
+module.exports.MODE_WHITELIST = TrustPlugin.MODE_WHITELIST;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/routing-table.js.html b/docs/routing-table.js.html new file mode 100644 index 0000000..26599b9 --- /dev/null +++ b/docs/routing-table.js.html @@ -0,0 +1,218 @@ + + + + + + routing-table.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

routing-table.js

+ + + + + + + +
+
+
'use strict';
+
+const { EventEmitter } = require('events');
+const Bucket = require('./bucket');
+const utils = require('./utils');
+const constants = require('./constants');
+
+
+/**
+ * Represents a kademlia routing table
+ */
+class RoutingTable extends Map {
+
+  /**
+   * Constructs a routing table
+   * @constructor
+   * @param {buffer} identity - Reference point for calculating distances
+   */
+  constructor(identity) {
+    super();
+
+    this.identity = identity || utils.getRandomKeyBuffer();
+    this.events = new EventEmitter();
+
+    for (let b = 0; b < constants.B; b++) {
+      this.set(b, new Bucket());
+    }
+  }
+
+  /**
+   * Returns the total contacts in the routing table
+   * @property {number} size
+   */
+  get size() {
+    let contacts = 0;
+    this.forEach((bucket) => contacts += bucket.length);
+    return contacts;
+  }
+
+  /**
+   * Returns the total buckets in the routing table
+   * @property {number} length
+   */
+  get length() {
+    let buckets = 0;
+    this.forEach(() => buckets++);
+    return buckets;
+  }
+
+  /**
+   * Returns the bucket index of the given node id
+   * @param {string|buffer} nodeId - Node identity to get index for
+   * @returns {number}
+   */
+  indexOf(nodeId) {
+    return utils.getBucketIndex(this.identity, nodeId);
+  }
+
+  /**
+   * Returns the contact object associated with the given node id
+   * @param {string|buffer} nodeId - Node identity of the contact
+   * @returns {Bucket~contact}
+   */
+  getContactByNodeId(nodeId) {
+    nodeId = nodeId.toString('hex');
+
+    return this.get(this.indexOf(nodeId)).get(nodeId);
+  }
+
+  /**
+   * Removes the contact from the routing table given a node id
+   * @param {string|buffer} nodeId - Node identity to remove
+   * @return {boolean}
+   */
+  removeContactByNodeId(nodeId) {
+    nodeId = nodeId.toString('hex');
+
+    this.events.emit('remove', nodeId);
+    return this.get(this.indexOf(nodeId)).delete(nodeId);
+  }
+
+  /**
+   * Adds the contact to the routing table in the proper bucket position,
+   * returning the [bucketIndex, bucket, contactIndex, contact]; if the
+   * returned contactIndex is -1, it indicates the bucket is full and the
+   * contact was not added; kademlia implementations should PING the contact
+   * at bucket.head to determine if it should be dropped before calling this
+   * method again
+   * @param {string|buffer} nodeId - Node identity to add
+   * @param {object} contact - contact information for peer
+   * @returns {array}
+   */
+  addContactByNodeId(nodeId, contact) {
+    nodeId = nodeId.toString('hex');
+
+    const bucketIndex = this.indexOf(nodeId);
+    const bucket = this.get(bucketIndex);
+    const contactIndex = bucket.set(nodeId, contact);
+
+    this.events.emit('add', nodeId);
+    return [bucketIndex, bucket, contactIndex, contact];
+  }
+
+  /**
+   * Returns the [index, bucket] of the occupied bucket with the lowest index
+   * @returns {Bucket}
+   */
+  getClosestBucket() {
+    for (let [index, bucket] of this) {
+      if (index < constants.B - 1 && bucket.length === 0) {
+        continue;
+      }
+      return [index, bucket];
+    }
+  }
+
+  /**
+   * Returns a array of N contacts closest to the supplied key
+   * @param {string|buffer} key - Key to get buckets for
+   * @param {number} [n=20] - Number of results to return
+   * @param {boolean} [exclusive=false] - Exclude exact matches
+   * @returns {map}
+   */
+  getClosestContactsToKey(key, n = constants.K, exclusive = false) {
+    const bucketIndex = this.indexOf(key);
+    const contactResults = new Map();
+
+    function _addNearestFromBucket(bucket) {
+      let entries = [...bucket.getClosestToKey(key, n, exclusive).entries()];
+
+      entries.splice(0, n - contactResults.size)
+        .forEach(([id, contact]) => {
+          /* istanbul ignore else */
+          if (contactResults.size < n) {
+            contactResults.set(id, contact);
+          }
+        });
+    }
+
+    let ascIndex = bucketIndex;
+    let descIndex = bucketIndex;
+
+    _addNearestFromBucket(this.get(bucketIndex));
+
+    while (contactResults.size < n && descIndex >= 0) {
+      _addNearestFromBucket(this.get(descIndex--));
+    }
+
+    while (contactResults.size < n && ascIndex < constants.B) {
+      _addNearestFromBucket(this.get(ascIndex++));
+    }
+
+    return contactResults;
+  }
+
+}
+
+module.exports = RoutingTable;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/rules-errors.js.html b/docs/rules-errors.js.html new file mode 100644 index 0000000..06bac1d --- /dev/null +++ b/docs/rules-errors.js.html @@ -0,0 +1,107 @@ + + + + + + rules-errors.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

rules-errors.js

+ + + + + + + +
+
+
'use strict';
+
+/**
+ * @class
+ */
+class ErrorRules {
+
+  /**
+   * Constructs a error rules instance in the context of a
+   * {@link AbstractNode}
+   * @constructor
+   * @param {AbstractNode} node
+   */
+  constructor(node) {
+    this.node = node;
+  }
+
+  /**
+   * Assumes if no error object exists, then there is simply no method defined
+   * @param {error|null} error
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   * @param {AbstractNode~next} next
+   */
+  methodNotFound(err, request, response, next) {
+    if (err) {
+      return next();
+    }
+
+    response.error('Method not found', -32601);
+  }
+
+  /**
+   * Formats the errors response according to the error object given
+   * @param {error|null} error
+   * @param {AbstractNode~request} request
+   * @param {AbstractNode~response} response
+   * @param {AbstractNode~next} next
+   */
+  internalError(err, request, response, next) {
+    response.error(err.message, err.code || -32603);
+    next()
+  }
+
+}
+
+module.exports = ErrorRules;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/rules-kademlia.js.html b/docs/rules-kademlia.js.html new file mode 100644 index 0000000..57e701d --- /dev/null +++ b/docs/rules-kademlia.js.html @@ -0,0 +1,175 @@ + + + + + + rules-kademlia.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

rules-kademlia.js

+ + + + + + + +
+
+
'use strict';
+
+const assert = require('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;
+
+
+
+ + + + +
+ +
+ + + + + + + diff --git a/docs/scripts/linenumber.js b/docs/scripts/linenumber.js new file mode 100644 index 0000000..8d52f7e --- /dev/null +++ b/docs/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/docs/scripts/prettify/Apache-License-2.0.txt b/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js new file mode 100644 index 0000000..041e1f5 --- /dev/null +++ b/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p code { + font-size: 0.85em; +} + +.readme table { + margin-bottom: 1em; + border-collapse: collapse; + border-spacing: 0; +} + +.readme table tr { + background-color: #fff; + border-top: 1px solid #ccc; +} + +.readme table th, +.readme table td { + padding: 6px 13px; + border: 1px solid #ddd; +} + +.readme table tr:nth-child(2n) { + background-color: #f8f8f8; +} + +/** Nav **/ +nav { + float: left; + display: block; + width: 250px; + background: #fff; + overflow: auto; + position: fixed; + height: 100%; + padding: 10px; + border-right: 1px solid #eee; + /* box-shadow: 0 0 3px rgba(0,0,0,0.1); */ +} + +nav li { + list-style: none; + padding: 0; + margin: 0; +} + +.nav-heading { + margin-top: 10px; + font-weight: bold; +} + +.nav-heading a { + color: #888; + font-size: 14px; + display: inline-block; +} + +.nav-item-type { + /* margin-left: 5px; */ + width: 18px; + height: 18px; + display: inline-block; + text-align: center; + border-radius: 0.2em; + margin-right: 5px; + font-weight: bold; + line-height: 20px; + font-size: 13px; +} + +.type-function { + background: #B3E5FC; + color: #0288D1; +} + +.type-class { + background: #D1C4E9; + color: #4527A0; +} + +.type-member { + background: #C8E6C9; + color: #388E3C; +} + +.type-module { + background: #E1BEE7; + color: #7B1FA2; +} + + +/** Footer **/ +footer { + color: hsl(0, 0%, 28%); + margin-left: 250px; + display: block; + padding: 30px; + font-style: italic; + font-size: 90%; + border-top: 1px solid #eee; +} + +.ancestors { + color: #999 +} + +.ancestors a { + color: #999 !important; + text-decoration: none; +} + +.clear { + clear: both +} + +.important { + font-weight: bold; + color: #950B02; +} + +.yes-def { + text-indent: -1000px +} + +.type-signature { + color: #aaa +} + +.name, .signature { + font-family: Consolas, Monaco, 'Andale Mono', monospace +} + +.details { + margin-top: 14px; + border-left: 2px solid #DDD; + line-height: 30px; +} + +.details dt { + width: 120px; + float: left; + padding-left: 10px; +} + +.details dd { + margin-left: 70px +} + +.details ul { + margin: 0 +} + +.details ul { + list-style-type: none +} + +.details li { + margin-left: 30px +} + +.details pre.prettyprint { + margin: 0 +} + +.details .object-value { + padding-top: 0 +} + +.description { + margin-bottom: 1em; + margin-top: 1em; +} + +.code-caption { + font-style: italic; + font-size: 107%; + margin: 0; +} + +.prettyprint { + font-size: 13px; + border: 1px solid #ddd; + border-radius: 3px; + box-shadow: 0 1px 3px hsla(0, 0%, 0%, 0.05); + overflow: auto; +} + +.prettyprint.source { + width: inherit +} + +.prettyprint code { + font-size: 12px; + line-height: 18px; + display: block; + background-color: #fff; + color: #4D4E53; +} + +.prettyprint code:empty:before { + content: ''; +} + +.prettyprint > code { + padding: 15px +} + +.prettyprint .linenums code { + padding: 0 15px +} + +.prettyprint .linenums li:first-of-type code { + padding-top: 15px +} + +.prettyprint code span.line { + display: inline-block +} + +.prettyprint.linenums { + padding-left: 70px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.prettyprint.linenums ol { + padding-left: 0 +} + +.prettyprint.linenums li { + border-left: 3px #ddd solid +} + +.prettyprint.linenums li.selected, .prettyprint.linenums li.selected * { + background-color: lightyellow +} + +.prettyprint.linenums li * { + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.params, .props { + border-spacing: 0; + border: 1px solid #ddd; + border-collapse: collapse; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); + width: 100%; + font-size: 14px; + /* margin-left: 15px; */ +} + +.params .name, .props .name, .name code { + color: #4D4E53; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 100%; +} + +.params td, .params th, .props td, .props th { + margin: 0px; + text-align: left; + vertical-align: top; + padding: 10px; + display: table-cell; +} + +.params td { + border-top: 1px solid #eee +} + +.params thead tr, .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params .params thead tr, .props .props thead tr { + background-color: #fff; + font-weight: bold; +} + +.params td.description > p:first-child, .props td.description > p:first-child { + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child, .props td.description > p:last-child { + margin-bottom: 0; + padding-bottom: 0; +} + +dl.param-type { + /* border-bottom: 1px solid hsl(0, 0%, 87%); */ + margin: 0; + padding: 0; + font-size: 16px; +} + +.param-type dt, .param-type dd { + display: inline-block +} + +.param-type dd { + font-family: Consolas, Monaco, 'Andale Mono', monospace; + display: inline-block; + padding: 0; + margin: 0; + font-size: 14px; +} + +.disabled { + color: #454545 +} + +/* navicon button */ +.navicon-button { + display: none; + position: relative; + padding: 2.0625rem 1.5rem; + transition: 0.25s; + cursor: pointer; + user-select: none; + opacity: .8; +} +.navicon-button .navicon:before, .navicon-button .navicon:after { + transition: 0.25s; +} +.navicon-button:hover { + transition: 0.5s; + opacity: 1; +} +.navicon-button:hover .navicon:before, .navicon-button:hover .navicon:after { + transition: 0.25s; +} +.navicon-button:hover .navicon:before { + top: .825rem; +} +.navicon-button:hover .navicon:after { + top: -.825rem; +} + +/* navicon */ +.navicon { + position: relative; + width: 2.5em; + height: .3125rem; + background: #000; + transition: 0.3s; + border-radius: 2.5rem; +} +.navicon:before, .navicon:after { + display: block; + content: ""; + height: .3125rem; + width: 2.5rem; + background: #000; + position: absolute; + z-index: -1; + transition: 0.3s 0.25s; + border-radius: 1rem; +} +.navicon:before { + top: .625rem; +} +.navicon:after { + top: -.625rem; +} + +/* open */ +.nav-trigger:checked + label:not(.steps) .navicon:before, +.nav-trigger:checked + label:not(.steps) .navicon:after { + top: 0 !important; +} + +.nav-trigger:checked + label .navicon:before, +.nav-trigger:checked + label .navicon:after { + transition: 0.5s; +} + +/* Minus */ +.nav-trigger:checked + label { + transform: scale(0.75); +} + +/* × and + */ +.nav-trigger:checked + label.plus .navicon, +.nav-trigger:checked + label.x .navicon { + background: transparent; +} + +.nav-trigger:checked + label.plus .navicon:before, +.nav-trigger:checked + label.x .navicon:before { + transform: rotate(-45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus .navicon:after, +.nav-trigger:checked + label.x .navicon:after { + transform: rotate(45deg); + background: #FFF; +} + +.nav-trigger:checked + label.plus { + transform: scale(0.75) rotate(45deg); +} + +.nav-trigger:checked ~ nav { + left: 0 !important; +} + +.nav-trigger:checked ~ .overlay { + display: block; +} + +.nav-trigger { + position: fixed; + top: 0; + clip: rect(0, 0, 0, 0); +} + +.overlay { + display: none; + position: fixed; + top: 0; + bottom: 0; + left: 0; + right: 0; + width: 100%; + height: 100%; + background: hsla(0, 0%, 0%, 0.5); + z-index: 1; +} + +.section-method { + margin-bottom: 30px; + padding-bottom: 30px; + border-bottom: 1px solid #eee; +} + +@media only screen and (min-width: 320px) and (max-width: 680px) { + body { + overflow-x: hidden; + } + + nav { + background: #FFF; + width: 250px; + height: 100%; + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: -250px; + z-index: 3; + padding: 0 10px; + transition: left 0.2s; + } + + .navicon-button { + display: inline-block; + position: fixed; + top: 1.5em; + right: 0; + z-index: 2; + } + + #main { + width: 100%; + min-width: 360px; + } + + #main h1.page-title { + margin: 1em 0; + } + + #main section { + padding: 0; + } + + footer { + margin-left: 0; + } +} + +@media only print { + nav { + display: none; + } + + #main { + float: none; + width: 100%; + } +} diff --git a/docs/styles/prettify-jsdoc.css b/docs/styles/prettify-jsdoc.css new file mode 100644 index 0000000..834a866 --- /dev/null +++ b/docs/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: hsl(104, 100%, 24%); + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/docs/styles/prettify-tomorrow.css b/docs/styles/prettify-tomorrow.css new file mode 100644 index 0000000..81e74d1 --- /dev/null +++ b/docs/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: hsl(104, 100%, 24%); } + + /* a keyword */ + .kwd { + color: hsl(240, 100%, 50%); } + + /* a comment */ + .com { + color: hsl(0, 0%, 60%); } + + /* a type name */ + .typ { + color: hsl(240, 100%, 32%); } + + /* a literal value */ + .lit { + color: hsl(240, 100%, 40%); } + + /* punctuation */ + .pun { + color: #000000; } + + /* lisp open bracket */ + .opn { + color: #000000; } + + /* lisp close bracket */ + .clo { + color: #000000; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Consolas, Monaco, 'Andale Mono', monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/docs/transport-http.js.html b/docs/transport-http.js.html new file mode 100644 index 0000000..e518aba --- /dev/null +++ b/docs/transport-http.js.html @@ -0,0 +1,253 @@ + + + + + + transport-http.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

transport-http.js

+ + + + + + + +
+
+
'use strict';
+
+const http = require('http');
+const https = require('https');
+const { Duplex: DuplexStream } = require('stream');
+const merge = require('merge');
+const concat = require('concat-stream');
+const constants = require('./constants');
+const utils = require('./utils');
+
+
+/**
+ * Represents a transport adapter over HTTP
+ */
+class HTTPTransport extends DuplexStream {
+
+  static get DEFAULTS() {
+    return {
+      allowLoopbackAddresses: true
+    };
+  }
+
+  /**
+   * Contructs a HTTP transport adapter
+   * @constructor
+   */
+  constructor(options) {
+    super({ objectMode: true });
+
+    this._options = merge({}, HTTPTransport.DEFAULTS, options);
+    this._pending = new Map();
+    this.server = this._createServer(this._options);
+
+    this.server.on('error', (err) => this.emit('error', err));
+    setInterval(() => this._timeoutPending(), constants.T_RESPONSETIMEOUT);
+  }
+
+  /**
+   * Creates the HTTP server object
+   * @private
+   */
+  _createServer() {
+    return http.createServer();
+  }
+
+  /**
+   * Returns a HTTP request object
+   * @private
+   */
+  _createRequest(options) {
+    if (options.protocol === 'https:') {
+      return https.request(...arguments);
+    }
+
+    return http.request(...arguments);
+  }
+
+  /**
+   * Implements the readable interface
+   * @private
+   */
+  _read() {
+    if (this.server.listeners('request').length) {
+      return;
+    }
+
+    this.server.on('request', (req, res) => this._handle(req, res));
+  }
+
+  /**
+   * Every T_RESPONSETIMEOUT, we destroy any open sockets that are still
+   * waiting
+   * @private
+   */
+  _timeoutPending() {
+    const now = Date.now();
+
+    this._pending.forEach(({ timestamp, response }, id) => {
+      let timeout = timestamp + constants.T_RESPONSETIMEOUT;
+
+      if (now >= timeout) {
+        response.statusCode = 504;
+        response.end('Gateway Timeout');
+        this._pending.delete(id);
+      }
+    });
+  }
+
+  /**
+   * Implements the writable interface
+   * @private
+   */
+  _write([id, buffer, target], encoding, callback) {
+    let [, contact] = target;
+
+    // NB: If responding to a received request...
+    if (this._pending.has(id)) {
+      this._pending.get(id).response.end(buffer);
+      this._pending.delete(id);
+      return callback(null);
+    }
+
+    // NB: If originating an outbound request...
+    const reqopts = {
+      hostname: contact.hostname,
+      port: contact.port,
+      protocol: contact.protocol,
+      method: 'POST',
+      headers: {
+        'x-kad-message-id': id
+      }
+    };
+
+    if (typeof contact.path === 'string') {
+      reqopts.path = contact.path;
+    }
+
+    const request = this._createRequest(reqopts);
+
+    request.on('response', (response) => {
+      response.on('error', (err) => this.emit('error', err));
+      response.pipe(concat((buffer) => {
+        if (response.statusCode >= 400) {
+          let err = new Error(buffer.toString());
+          err.dispose = id;
+          this.emit('error', err);
+        } else {
+          this.push(buffer);
+        }
+      }));
+    });
+
+    request.on('error', (err) => {
+      err.dispose = id;
+      this.emit('error', err);
+    });
+    request.end(buffer);
+    callback();
+  }
+
+  /**
+   * Default request handler
+   * @private
+   */
+  _handle(req, res) {
+    req.on('error', (err) => this.emit('error', err));
+    res.on('error', (err) => this.emit('error', err));
+
+    if (!req.headers['x-kad-message-id']) {
+      res.statusCode = 400;
+      return res.end();
+    }
+
+    res.setHeader('X-Kad-Message-ID', req.headers['x-kad-message-id']);
+    res.setHeader('Access-Control-Allow-Origin', req.headers.origin || '*');
+    res.setHeader('Access-Control-Allow-Methods', '*');
+    res.setHeader('Access-Control-Allow-Headers', '*');
+    res.setHeader('Access-Control-Allow-Credentials', 'true');
+
+    if (!['POST', 'OPTIONS'].includes(req.method)) {
+      res.statusCode = 405;
+    }
+
+    if (req.method !== 'POST') {
+      return res.end();
+    }
+
+    req.pipe(concat((buffer) => {
+      this._pending.set(req.headers['x-kad-message-id'], {
+        timestamp: Date.now(),
+        response: res
+      });
+      this.push(buffer);
+    }));
+  }
+
+  /**
+   * @private
+   */
+  _validate(contact) {
+    return utils.isValidContact(contact, this._options.allowLoopbackAddresses);
+  }
+
+  /**
+   * Binds the server to the given address/port
+   */
+  listen() {
+    this.server.listen(...arguments);
+  }
+
+}
+
+module.exports = HTTPTransport;
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:06 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + diff --git a/docs/transport-https.js.html b/docs/transport-https.js.html new file mode 100644 index 0000000..0d36d04 --- /dev/null +++ b/docs/transport-https.js.html @@ -0,0 +1,107 @@ + + + + + + transport-https.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

transport-https.js

+ + + + + + + +
+
+
'use strict';
+
+const HTTPTransport = require('./transport-http');
+const https = require('https');
+const merge = require('merge');
+
+/**
+ * Extends the HTTP transport with SSL
+ */
+class HTTPSTransport extends HTTPTransport {
+
+  static get DEFAULTS() {
+    return {};
+  }
+
+  /**
+   * Contructs a new HTTPS transport adapter
+   * @constructor
+   * @extends {HTTPTransport}
+   * @param {object} options
+   * @param {buffer} options.key - SSL private key buffer
+   * @param {buffer} options.cert - SSL certificate buffer
+   * @param {buffer[]} options.ca - List of certificate authority certificates
+   */
+  constructor(options) {
+    super(merge({}, HTTPSTransport.DEFAULTS, options));
+  }
+
+  /**
+   * Constructs the HTTPS server
+   * @private
+   */
+  _createServer() {
+    return https.createServer(...arguments);
+  }
+
+  /**
+   * Constructs the HTTPS request
+   * @private
+   */
+  _createRequest() {
+    return https.request(...arguments);
+  }
+
+}
+
+module.exports = HTTPSTransport;
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:06 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + diff --git a/docs/transport-udp.js.html b/docs/transport-udp.js.html new file mode 100644 index 0000000..baa1530 --- /dev/null +++ b/docs/transport-udp.js.html @@ -0,0 +1,139 @@ + + + + + + transport-udp.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

transport-udp.js

+ + + + + + + +
+
+
'use strict';
+
+const merge = require('merge');
+const { Duplex: DuplexStream } = require('stream');
+const dgram = require('dgram');
+const utils = require('./utils');
+
+
+/**
+ * Implements a UDP transport adapter
+ */
+class UDPTransport extends DuplexStream {
+
+  static get DEFAULTS() {
+    return {
+      type: 'udp4',
+      reuseAddr: false,
+      allowLoopbackAddresses: true
+    };
+  }
+
+  /**
+   * Constructs a datagram socket interface
+   * @constructor
+   * @param {object} [socketOpts] - Passed to dgram.createSocket(options)
+   */
+  constructor(options) {
+    super({ objectMode: true });
+    this._options = merge(UDPTransport.DEFAULTS, options);
+
+    this.socket = dgram.createSocket({
+      type: this._options.type,
+      reuseAddr: this._options.reuseAddr
+    });
+
+    this.socket.on('error', (err) => this.emit('error', err));
+  }
+
+  /**
+   * Implements the writable interface
+   * @private
+   */
+  _write([, buffer, target], encoding, callback) {
+    let [, contact] = target;
+
+    this.socket.send(buffer, 0, buffer.length, contact.port, contact.hostname,
+      callback);
+  }
+
+  /**
+   * Implements the readable interface
+   * @private
+   */
+  _read() {
+    this.socket.once('message', (buffer) => {
+      this.push(buffer);
+    });
+  }
+
+  /**
+   * @private
+   */
+  _validate(contact) {
+    return utils.isValidContact(contact, this._options.allowLoopbackAddresses);
+  }
+
+  /**
+   * Binds the socket to the [port] [, address] [, callback]
+   * @param {number} [port=0] - Port to bind to
+   * @param {string} [address=0.0.0.0] - Address to bind to
+   * @param {function} [callback] - called after bind complete
+   */
+  listen() {
+    this.socket.bind(...arguments);
+  }
+
+}
+
+module.exports = UDPTransport;
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:06 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + diff --git a/docs/tutorial-config.html b/docs/tutorial-config.html new file mode 100644 index 0000000..49e97b3 --- /dev/null +++ b/docs/tutorial-config.html @@ -0,0 +1,185 @@ + + + + + + Configuration Guide - Documentation + + + + + + + + + + + + + + + + + +
+ +

Configuration Guide

+ + +
+ +
+ +
+ +
+

This guide will show you how to get started with running kadence! A Kadence +node requires a configuration file to get up and running. The path to this +file is given to kadence when starting a node (or the defaults will be used).

+
kadence --config myconfig.ini
+
+

If a configuration file is not supplied, a minimal default configuration is +automatically created and used, which will generate a private extended key, +self-signed certificate, database, and other necessary files. All of this data +will be created and stored in $HOME/.config/kadence, unless a --datadir +option is supplied. Valid configuration files may be in either INI or JSON +format.

+

DaemonPidFilePath

+
Default: $HOME/.config/kadence/kadence.pid
+

The location to write the PID file for the daemon.

+

PrivateExtendedKeyPath

+
Default: $HOME/.config/kadence/kadence.prv
+

Path to private extended key file to use for master identity.

+

ChildDerivationIndex

+
Default: 0
+

The index for deriving this node's identity in accordance with the identity +difficulty.

+

EmbeddedDatabaseDirectory

+
Default: $HOME/.config/kadence/kadence.dht
+

Sets the directory to store DHT entries.

+

EmbeddedPeerCachePath

+
Default: $HOME/.config/kadence/peercache
+

File to store discovered peers for bootstrapping on subsequent restarts.

+

EmbeddedWalletDirectory

+
Default: $HOME/.config/kadence/wallet.dat
+

Sets the directory to store solution files for storing entries in the DHT.

+

NodePublicPort

+
Default: 5274
+

Sets the port number to advertise to the network for reaching this node.

+

NodeListenPort

+
Default: 5274
+

Sets the local port to bind the node's RPC service.

+

NodePublicAddress

+
Default: 127.0.0.1
+

Sets the public address to advertise to the network for reaching this node. +If traversal strategies are enabled and succeed, this will be changed +automatically. If onion mode is enabled, then this should be left at it's +default.

+

NodeListenAddress

+
Default: 0.0.0.0
+

Sets the address to bind the RPC service.

+

BandwidthAccountingEnabled

+
Default: 0
+

Enables bandwidth metering and hibernation mode. When the property +BandwidthAccountingEnabled is 1, we will enter low-bandwidth mode if the we +exceed BandwidthAccountingMax within the period defined by +BandwidthAccountingReset until the interval is finished.

+

BandwidthAccountingMax

+
Default: 5GB
+

Sets the maximum number of bandwidth to use per accounting interval for data +transfer. Low-bandwidth RPC messages will still be allowed.

+

BandwidthAccountingReset

+
Default: 24HR
+

Resets the bandwidth accounting on an interval defined by this property.

+

VerboseLoggingEnabled

+
Default: 1
+

More detailed logging of messages sent and received. Useful for debugging.

+

LogFilePath

+
Default: $HEAD/.config/kadence.log
+

Path to write the daemon's log file. Log file will rotate either every 24 hours +or when it exceeds 10MB, whichever happens first.

+

LogFileMaxBackCopies

+
Default: 3
+

Maximum number of rotated log files to keep.

+

NetworkBootstrapNodes[]

+
Default: (empty)
+

Add a map of network bootstrap nodes to this section to use for discovering +other peers. Default configuration should come with a list of known and +trusted contacts.

+

OnionEnabled

+
Default: 0
+

Places Kadence into anonymous mode, which establishes the node exclusively as +a Tor hidden services and forces all requests through the Tor network.

+

OnionVirtualPort

+
Default: 443
+

The virtual port to use for the hidden service.

+

OnionHiddenServiceDirectory

+
Default: $HOME/.config/kadence/hidden_service
+

The directory to store hidden service keys and other information required by +the Tor process.

+

OnionLoggingEnabled

+
Default: 0
+

Redirects the Tor process log output through Kadence's logger for the purpose of +debugging.

+

OnionLoggingVerbosity

+
Default: notice
+

Defines the verbosity level of the Tor process logging. Valid options are: +debug, info, notice.

+

TraverseNatEnabled

+
Default: 1
+

Enables UPnP and NAT-PMP traversal strategies for becoming addressable on the +public internet.

+

TraversePortForwardTTL

+
Default: 0
+

How long to keep the port mapping active on the router. The value 0 means +indefinitely (until revoked).

+

SSLEnabled

+
Default: 0
+

Flag to instruct the daemon to use SSL/TLS to secure communication.

+

SSLCertificatePath

+
Default: $HOME/.config/kadence/kadence.crt
+

Path to the SSL certificate for our node.

+

SSLKeyPath

+
Default: $HOME/.config/kadence/kadence.key
+

Path to the SSL private key for our node.

+

SSLAuthorityPaths[]

+
Default: (emtpy)
+

Paths to intermediate certificate authority chains.

+

ControlPortEnabled

+
Default: 0
+

Enables the Control interface over a TCP socket.

+

ControlPort

+
Default: 5275
+

The TCP port to for the control interface to listen on.

+

ControlSockEnabled

+
Default: 1
+

Enables the Control interface over a UNIX domain socket.

+

ControlSock

+
Default: $HOME/.config/kadence/kadence.sock
+

The path to the file to use for the control interface.

+

TestNetworkEnabled

+
Default: 0
+

Places Kadence into test mode, significantly lowering the identity solution +difficulty and the permission solution difficulty.

+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-identities.html b/docs/tutorial-identities.html new file mode 100644 index 0000000..a91cdec --- /dev/null +++ b/docs/tutorial-identities.html @@ -0,0 +1,106 @@ + + + + + + Contacts and Identities - Documentation + + + + + + + + + + + + + + + + + +
+ +

Contacts and Identities

+ + +
+ +
+ +
+ +
+

Kadence represents other peers by using a Bucket~contact pair. Any +time an entry in a Bucket is retrieved or placed, it is in the format +of a tuple. The item at index 0 is always the string representation of the +module:kadence/constants~B size identity key in hexadecimal. The item +at index 1 can be any arbitrary JSON serializable object that the +transport adapter in use understands.

+

For example, the HTTPTransport and the UDPTransport both accept +an object cotaining hostname and port properties. Other transports may +accept whatever they need. When constructing your KademliaNode +instance, these properties are set by you as identity and contact. If the +identity value is omitted, it will be randomly generated.

+
+

Take note that for a stable network, you will need to persist identities +generated as nodes store data based on this key.

+
+
const node = new kadence.KademliaNode({
+  // ...
+  identity: Buffer.from('059e5ce8d0d3ee0225ffe982e38f3f5f6f748328', 'hex'),
+  contact: {
+    hostname: 'my.reachable.hostname',
+    port: 1337
+  }
+});
+
+

Since nodes may be using module:kadence/traverse to become addressable +on the internet, this Bucket~contact pair is included in every message +payload instead of relying on inferred return address information at the +transport layer. This makes every JSON-RPC message payload an array, containing +a request message at index 0 and a idenity notification at index 1.

+
[
+  {
+    "jsonrpc": "2.0",
+    "id": "<uuid>",
+    "method": "FIND_NODE",
+    "params": ["059e5ce8d0d3ee0225ffe982e38f3f5f6f748328"]
+  },
+  {
+    "jsonrpc": "2.0",
+    "method": "IDENTITY",
+    "params": [
+      "059e5ce8d0d3ee0225ffe982e38f3f5f6f748328",
+      {
+        "hostname": "<reachable hostname>",
+        "port": 1337
+      }
+    ]
+  }
+]
+
+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-install.html b/docs/tutorial-install.html new file mode 100644 index 0000000..164787a --- /dev/null +++ b/docs/tutorial-install.html @@ -0,0 +1,102 @@ + + + + + + Installing Kadence - Documentation + + + + + + + + + + + + + + + + + +
+ +

Installing Kadence

+ + +
+ +
+ +
+ +
+

Make sure you have the following prerequisites installed:

+ +

Node.js + NPM

+

GNU+Linux & Mac OSX

+
wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.0/install.sh | bash
+
+

Close your shell and open an new one. Now that you can call the nvm program, +install Node.js (which comes with NPM):

+
nvm install --lts
+
+

Build Dependencies

+

GNU+Linux

+

Debian / Ubuntu / Mint / Trisquel / and Friends

+
apt install git python build-essential
+
+

Red Hat / Fedora / CentOS

+
yum groupinstall 'Development Tools'
+
+

You might also find yourself lacking a C++11 compiler - +see this.

+

Mac OSX

+
xcode-select --install
+
+

Windows

+

Run as administrator in PowerShell or cmd:

+
npm install -g windows-build-tools
+
+

Daemon

+

This package exposes the program kadence. To install, use the --global flag.

+
npm install -g @deadcanaries/kadence
+
+

Core Library

+

This package exposes a module providing a complete reference implementation +of the protocol. To use it in your project, from your project's root +directory, install as a dependency.

+
npm install @tacticalchihuahua/kadence --save
+
+

Then you can require the library with:

+
const kadence = require('@tacticalchihuahua/kadence');
+
+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-messengers.html b/docs/tutorial-messengers.html new file mode 100644 index 0000000..5255457 --- /dev/null +++ b/docs/tutorial-messengers.html @@ -0,0 +1,100 @@ + + + + + + Message Format - Documentation + + + + + + + + + + + + + + + + + +
+ +

Message Format

+ + +
+ +
+ +
+ +
+

Kadence implements a generic Messenger class that is used as the interface +between the application layer and the AbstractNode~transport. This +interface exposes 2 primary members: Messenger~serializer and +Messenger~deserializer.

+

As you might expect, both of these objects are streams. Both are transform +streams. The transport adapter's readable end is piped through the +deserializer which is then processed by the middleware stack implemented by +the AbstractNode. The serializer is piped through the transport +adapter's writable end, which dispatches the message.

+

The serializer and deserializer are +metapipe objects (transform streams +which are composed of a modifiable stack of transform streams). This means you +can extend the behavior of messages processing by simply prepending or +appending your own transforms.

+
+

By default, these metapipes use a built-in JSON-RPC serializer and +deserializer. It is possible to completely change the message format sent +over the network if desired by passing KademliaNode your own instance +of Messenger using your own serializer and deserializer.

+
+

Below is an example of extended the message processing pipeline.

+
const { Transform } = require('stream');
+const node = new kadence.KademliaNode(options);
+
+node.rpc.serializer.prepend(() => new Transform({
+  transform function(data, encoding, callback) {
+
+  },
+  objectMode: true
+}));
+
+node.rpc.deserializer.append(() => new Transform({
+  transform: function(data, encoding, callback) {
+
+  },
+  objectMode: true
+}));
+
+
+

Note that the KademliaRules still expect the deserialized message to +include method and params properties (conforming to +AbstractNode~request).

+
+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-middleware.html b/docs/tutorial-middleware.html new file mode 100644 index 0000000..2c6d4ab --- /dev/null +++ b/docs/tutorial-middleware.html @@ -0,0 +1,144 @@ + + + + + + Middleware Stack - Documentation + + + + + + + + + + + + + + + + + +
+ +

Middleware Stack

+ + +
+ +
+ +
+ +
+

Kadence exposes an interface similar to express's +use() method to allow for extending the protocol via message processing +middleware. There are 4 distinct middleware stacks that process incoming +messages in a particular order:

+

Global Message Stack

+

Global middleware is applied to any and all deserialized messages that are +provided to AbstractNode from the Messenger~deserializer. +Handlers added here are useful for any global logic that needs to be applied +such as validation of message format.

+

Global middleware handlers follow the signature defined by +AbstractNode~middleware and are supplied as the only argument to +AbstractNode#use. The middleware handler receives, in order, +AbstractNode~request, AbstractNode~response, and +AbstractNode~next (which may be called with an error to exit the stack +and trigger the error handler stack).

+

A simple example of a global middleware handler is a node blacklist. In the +example below, we define a set of node identities from which we wish to reject +all messages.

+
const blacklist = new Set([/* misbehaving node ids */]);
+
+node.use(function(request, response, next) {
+  let [identity] = request.contact;
+
+  if (blacklist.includes(identity)) {
+    return next(new Error('Go away!')); // Exit this stack and enter error stack
+  }
+
+  next(); // Continue to next handler in stack
+});
+
+

Filtered Message Stack

+

The primary function of the middleware stack is to enable developers to invent +new protocols by defining handlers for new methods. Similar to the Express +framework, if we wish to only apply a handler to certain types of messages, we +can define the method name as the first argument supplied to +AbstractNode#use and our handler as the second.

+

This enables us to extend the base Kademlia protocol with new methods and +behaviors. If a message is received that invokes a method for which there is +not a handler defined, after being processed by the global stack, it will enter +the error handler stack with a "Method not found" error object.

+

To demonstrate how this works, we provide an example of an ECHO handler - a +new protocol method that simply echoes the argument provided back to the +sender.

+
node.use('ECHO', function(request, response, next) {
+  let [message] = request.params;
+
+  if (!message) {
+    return next(new Error('Nothing to echo')); // Exit to the error stack
+  }
+
+  response.send([message]); // Respond back with the argument provided
+});
+
+

Like the global message stack, the filtered message stack can also have many +handlers defined. This is useful in the event that you want to provide +per-message-type validation without placing all of that logic into a single +handler. The same rules apply, call AbstractNode~next to move to the +next handler in the stack and call AbstractNode~response#send to halt +the stack and respond to the message.

+

Error Handler Stack

+

Error handling middleware is applied to any message which previously resulting +in a call to AbstractNode~next with an error parameter. They are +defined by including an error argument in the first position to a +AbstractNode~middleware function. These can be scoped globally or by +protocol and will behave just like the global message stack and filtered +message stack. When a message enters the error handler stack, first it will +pass through the global error handlers then the filtered error handlers. If +there are no error handler middleware functions defined, the default handler, +which simply responds with the error message, is used.

+
node.use(function(err, request, response, next) {
+  if (!err) {
+    response.error('Method not found', -32602);
+  } else {
+    response.error(err.message, err.code);
+  }
+});
+
+node.use('ECHO', function(err, request, response, next) {
+  response.error(`ECHO error: ${err.message}`);
+});
+
+

Remember, the number of arguments supplied to AbstractNode~middleware +matters. Error handlers are registered if and only if there are 4 arguments +provided to AbstractNode~middleware: error, +AbstractNode~request, AbstractNode~response, and +AbstractNode~next in order. If there are less than four arguments +provided to the handler, it will not be inserted into the error handler stack.

+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-nat.html b/docs/tutorial-nat.html new file mode 100644 index 0000000..d7f9f13 --- /dev/null +++ b/docs/tutorial-nat.html @@ -0,0 +1,112 @@ + + + + + + Traversing NATs and Firewalls - Documentation + + + + + + + + + + + + + + + + + +
+ +

Traversing NATs and Firewalls

+ + +
+ +
+ +
+ +
+

One of the most frustrating and daunting problems when deploying a distributed +network to users is dealing with NAT (or "Network Address Translation". +Kadence provides a plugin for traversing these systems with mulitple strategies +and is capable of breaking out of just about any network (albeit sometimes at +the expensive of performance).

+

This functionality is encapsulated in the module:kadence/traverse +plugin, which initializes a module:kadence/traverse~TraversePlugin. +This plugin makes use of the module:kadence/traverse~TraverseStrategy +instances that are passed to it. At the time of writing, Kadence supports +UPnP, NAT-PMP, and a fallback reverse HTTPS tunneling mechanism (for use with +the HTTPSTransport and HTTPTransport).

+

Using the Plugin

+

Generally, you'll want to use all of the available strategies, but since some +strategies may only work with certain transports, you must explicity define +them when calling the plugin.

+
const node = new kadence.KademliaNode(options); // See "Getting Started"
+
+node.spartacus = node.plugin(kadence.spartacus()); // Optional, but recommended
+
+node.traverse = node.plugin(kadence.traverse([ // List in order of attempt
+  new kadence.traverse.UPNPStrategy({
+    mappingTtl: 0, // Means keep this mapping until unmapped
+    publicPort: node.contact.port // The public port to map
+  }),
+  new kadence.traverse.NATPMPStrategy({
+    mappingTtl: 0, // Means keep this mapping until unmapped
+    publicPort: node.contact.port // The public port to map
+  }),
+  new kadence.traverse.ReverseTunnelStrategy({
+    remoteAddress: 'tun.tacticalchihuahua.lol', // Hostname of a Diglet server
+    remotePort: 8443, // Tunnel port of a Diglet server
+    verboseLogging: false, // If debuggin, set to `true`
+    secureLocalConnection: false, // Set to `true` if using HTTPSTransport
+    privateKey: node.spartacus.privateKey // Uses identity for tunnel routing
+  })
+]));
+
+

In the example above, we are assuming use of the HTTPTransport. When +the method KademliaNode#listen is called, this plugin will execute +each strategy in the order they are defined until one of them successfully +traverses the NAT and becomes public on the internet. If all of them fail, a +message will indicate that you are not addressable.

+

The UPnP and NAT-PMP strategies attempt to instruct the router / NAT device +to forward a port to internet. Sometimes this is a feature that must be +enabled on the router itself and sometimes it is not supported at all - either +by the device or the ISP.

+

The reverse tunnel strategy works by establishing an outbound connection to a +Diglet server which acts as a reverse +proxy back to your node on the outbound connection. By default, the plugin will +use a test server, but this may or may not be online or functioning at any +given time. The recommendation is to run your own Diglet server, which is very +simple to set up. Ideally, your users should each run their own Diglet server +to prevent dense centralization around a single tunnel, however this is +generally unreasonable to expect so it's good to bake in a rotating list of +public Diglet servers into your code.

+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-plugins.html b/docs/tutorial-plugins.html new file mode 100644 index 0000000..b9f6729 --- /dev/null +++ b/docs/tutorial-plugins.html @@ -0,0 +1,102 @@ + + + + + + Using and Authoring Plugins - Documentation + + + + + + + + + + + + + + + + + +
+ +

Using and Authoring Plugins

+ + +
+ +
+ +
+ +
+

Kadence plugins are a simple way to package additional features. A plugin is just +a function that receives an instance of KademliaNode. This function can +then apply any decorations desired.

+

Included Plugins

+ +

Example: "Howdy, Neighbor" Plugin

+
/**
+ * Example "howdy, neighbor" plugin
+ * @function
+ * @param {KademliaNode} node
+ */
+module.exports = function(node) {
+
+  const { identity } = node;
+
+  /**
+   * Respond to HOWDY messages
+   */
+  node.use('HOWDY', (req, res) => {
+    res.send(['howdy, neighbor']);
+  });
+
+  /**
+   * Say howdy to our nearest neighbor
+   */
+  node.sayHowdy = function(callback) {
+    let neighbor = [
+      ...node.router.getClosestContactsToKey(identity).entries()
+    ].shift();
+    
+    node.send('HOWDY', ['howdy, neighbor'], neighbor, callback);
+  };
+
+};
+
+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-protocol.html b/docs/tutorial-protocol.html new file mode 100644 index 0000000..1b0c02c --- /dev/null +++ b/docs/tutorial-protocol.html @@ -0,0 +1,287 @@ + + + + + + Protocol Specification - Documentation + + + + + + + + + + + + + + + + + +
+ +

Protocol Specification

+ + +
+ +
+ +
+ +
+

Version 3.0 (March 3, 2018)

+

Lily Anne Hall (lily@tacticalchihuahua.lol)

+
+

0 License

+

Copyright (C) 2018 Lily Anne Hall

+

Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 +or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +A copy of the license is included in the "LICENSE" file.

+

1 Introduction

+

This specification documents the Kadence protocol in its entirety for +the purpose of enabling its implementation in other languages. Described here, +is the protocol base - the minimum specification for compatibility with +Kadence. Additional optional extensions to this work may be defined in a +future specification.

+

2 Identities

+

Every node (host computer speaking the Kadence protocol) on the network possesses +a unique cryptographic identity. This identity is used to derive a special +160 bit identifier for the purpose of organizaing the overlay structure and +routing messages (3.1: Kademlia). In order for a node to join the network it +must generate an identity.

+

Identities are the RMD160 hash of an Equihash proof where the node's public +key is the proof input. Messages are signed with the corresponding private key. +This is designed to provide some resilience against sybil and, in particular, +eclipse attacks. An eclipse attack is a type of censorship by which an attacker +is able to manipulate the network's routing tables such that the attacker is +able to "surround" a target without their knowledge.

+

In every message exchanged on the network, each party will include a tuple +structure which includes enough information to locate and authenticate each +party.

+
["<node_id>", { /* <contact> */ }]
+
+

2.1 Contact Hash Map

+

The second entry in the identity tuple contains additional information specific +to addressing the node on the network. This includes:

+
{
+  "hostname": "xxxxxxxx.onion",
+  "port": 80,
+  "protocol": "http:",
+  "pubkey": "...",
+  "proof": "..."
+}
+
+

Additional properties may be included based on individual use cases within the +network, however the properties above are required.

+

3 Network Structure

+

Kadence employs a structured network, meaning that nodes are organized and +route messages based on a deterministic metric. The network uses a +Kademlia distributed +hash table as the basis for the network overlay. In addition to Kademlia, +Kadence also employs other extensions to mitigate issues and attacks defined +by the work on S/Kademlia.

+

3.1 Kademlia

+

Once an Kadence node has completed generating its identity, it bootstraps its +routing table by following the Kademlia "join" procedure. This involves +querying a single known "seed" node for contact information about other nodes +that possess a Node ID that is close (XOR distance) to its own +(4.4 FIND_NODE). This is done iteratively, sending the same query to the +ALPHA (3) results that are closest, until the further queries no longer +yield results that are closer or the routing table is sufficiently +bootstrapped.

+

3.2 Transport

+

The Kadence network operates over HTTP and exclusively over +Tor.

+

Each Kadence node exposes a V3 hidden service to other nodes for receiving RPC +messages (4. Remote Procedure Calls). Requests sent to the RPC endpoint +require a special HTTP header x-kad-message-id to be included that matches +the id parameter in the associated RPC message (4.1 Structure and Authentication).

+

4 Remote Procedure Calls

+
    +
  • Method: POST
  • +
  • Path: /
  • +
  • Content Type: application/json
  • +
  • Headers: x-kad-message-id
  • +
+

4.1 Structure and Authentication

+

Each remote procedure call sent and received between nodes is composed in the +same structure. Messages are formatted as a +JSON-RPC 2.0 batch payload containing +3 objects. These objects are positional, so ordering matters. The anatomy of a +message takes the form of:

+
[{ /* rpc */ },{ /* notification */ },{ /* notification */ }]
+
+

At position 0 is the RPC request/response object, which must follow the +JSON-RPC specification for such an object. It must contain the properties: +jsonrpc, id, method, and params if it is a request. It must contain the +properties: jsonrpc, id, and one of result or error if it is a +response.

+

At positions 1 and 2 are a JSON-RPC notification object, meaning that it is not +required to contain an id property since no response is required. These two +notifications always assert methods IDENTIFY and AUTHENTICATE respectively. +Together, these objects provide the recipient with information regarding the +identity and addressing information of the sender as well as a cryptographic +signature to authenticate the payload.

+

For STORE message, an additional HASHCASH message is included in the +payload to prevent spam.

+
Example: Request
+
[
+  {
+    "jsonrpc": "2.0",
+    "id": "<uuid_version_4>",
+    "method": "<method_name>",
+    "params": ["<parameter_one>", "<parameter_two>"]
+  },
+  {
+    "jsonrpc": "2.0",
+    "method": "IDENTIFY",
+    "params": [
+      "<proof_hash>", 
+      {
+        "hostname": "sender.onion",
+        "port": 80,
+        "protocol": "http:",
+        "pubkey": "...",
+        "proof": "..."
+      }
+    ]
+  },
+  {
+    "jsonrpc": "2.0",
+    "method": "AUTHENTICATE",
+    "params": [
+      "<payload_signature>",
+      "<public_key>"
+    ]
+  }
+]
+
+
Example: Response
+
[
+  {
+    "jsonrpc": "2.0",
+    "id": "<uuid_version_4_from_request>",
+    "result": ["<result_one>", "<result_two>"]
+  },
+  {
+    "jsonrpc": "2.0",
+    "method": "IDENTIFY",
+    "params": [
+      "<proof_hash>", 
+      {
+        "hostname": "receiver.onion",
+        "port": 80,
+        "protocol": "http:",
+        "pubkey": "...",
+        "proof": "..."
+      }
+    ]
+  },
+  {
+    "jsonrpc": "2.0",
+    "method": "AUTHENTICATE",
+    "params": [
+      "<payload_signature>",
+      "<public_key>"
+    ]
+  }
+]
+
+

In the examples above, proof_hash and public_key must be encoded +as hexidecimal string and payload_signature must be encoded as a +base64 string which is the concatenation of the public key recovery number with +the actual signature of the payload - excluding the object at index 2 +(AUTHENTICATE). This means that the message to be signed is +[rpc, identify].

+
+

Note the exclusion of a timestamp or incrementing nonce in the payload means +that a man-in-the-middle could carry out a replay attack. To combat this, it +is urged that the id parameter of the RPC message (which is a universally +unique identifier) be stored for a reasonable period of time and nodes should +reject messages that attempt to use a duplicate UUID.

+
+

The rest of this section describes each individual method in the base protocol +and defines the parameter and result signatures that are expected. If any RPC +message yields an error, then an error property including code and +message should be send in place of the result property.

+

4.2 PING

+

This RPC involves one node sending a PING message to another, which +presumably replies. 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.

+

Parameters: []
+Results: []

+

4.3 FIND_NODE

+

Basic kademlia lookup operation that builds a set of K contacts closest to the +the given key. 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.

+

Parameters: [key_160_hex]
+Results: [contact_0, contact_1, ...contactN]

+

4.4 FIND_VALUE

+

Kademlia search operation that is conducted as a node lookup and builds a list +of K closest contacts. If at any time during the lookup the value is returned, +the search is abandoned. If no value is found, the K closest contacts are +returned. Upon success, we must store the value at the nearest node seen during +the search that did not return the value.

+

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.

+

If a value is returned, it must be in the form of an object with properties: +timestamp as a UNIX timestamp in milliseconds, publisher as a 160 bit +public key hash in hexidecimal of the original publisher, and value which may +be of mixed type that is valid JSON.

+

Parameters: [key_160_hex]
+Results: { timestamp, publisher, value } or [...contactN]

+

4.5 STORE

+

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. Kadence requires that the key is the RMD160 hash of the supplied blob +and that the blob is exactly equal to 2MiB in size and encoded as base64.

+

Parameters: [key_160_hex, 2mib_value_base64]
+Results: [key_160_hex, 2mib_value_base64]

+

An additional HASHCASH payload is appended to this message.

+
{
+  "jsonrpc": "2.0",
+  "method": "HASHCASH",
+  "params": ["<hashcash_stamp>"]
+}
+
+

The stamp follows the hashcash specification. The resource segment of the stamp +is the sender identity, target identity, and method name concatenated. The +difficulty may be adjusted by community consensus to account for potential +attacks.

+

9 References

+
    +
  • Kademlia (http://www.scs.stanford.edu/~dm/home/papers/kpos.pdf)
  • +
  • S/Kademlia (http://www.tm.uka.de/doc/SKademlia_2007.pdf)
  • +
+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-quickstart.html b/docs/tutorial-quickstart.html new file mode 100644 index 0000000..d4bf5f9 --- /dev/null +++ b/docs/tutorial-quickstart.html @@ -0,0 +1,292 @@ + + + + + + Getting Started - Documentation + + + + + + + + + + + + + + + + + +
+ +

Getting Started

+ + +
+ +
+ +
+ +
+

Getting started with building distributed systems with Kadence is simple, but +requires a basic understanding of it's architecture in order to make the most +effective use of what it has to offer. There are two ways to build on top of +Kadence: using the complete reference implementation as a daemon and +communicating with it from any language using the Control interface +or using the core library directly using JavaScript. This tutorial will cover +both approaches.

+
+

Note that this guide assumes you already have Kadence installed. If you have +not installed Kadence, follow our guide for Installing Kadence and come +back here when you're ready!

+
+

Contents

+
    +
  1. Using the Library
  2. +
  3. Using the Daemon
  4. +
+

+

Using the Library

+

Not all use-cases require the exact properties of a "proper" Kadence network. +Because of this, Kadence exposes it's core library as a complete framework for +building distributed systems. This guide will demonstrate how to use the +Kadence framework to invent new distributed protocols.

+

Start by following the guide for Installing Kadence and install Kadence +locally to a new project. First create your project.

+
$ mkdir myproject
+$ cd myproject
+$ npm init
+
+

Then install Kadence and save the dependency to your package.json file.

+
$ npm install @deadcanaries/kadence --save
+
+

Creating a Node

+

Most of the framework revolves around the instantiation of a +KademliaNode, which exposes the primary interface for extending the +protocol. There are several required options to provide, notably:

+ +

For this example we'll be using the UDPTransport and a LevelDB database +provided by the levelup and leveldown packages.

+
const kadence = require('@tacticalchihuahua/kadence');
+const levelup = require('levelup');
+const leveldown = require('leveldown');
+const encode = require('encoding-down');
+
+const node = new kadence.KademliaNode({
+  identity: kadence.utils.getRandomKeyBuffer(),
+  transport: new kadence.UDPTransport(),
+  storage: levelup(encode(leveldown('path/to/database'))),
+  contact: {
+    hostname: 'my.hostname',
+    port: 8080
+  }
+});
+
+

The code above is the minimum setup for a complete Kademlia DHT. If this is all +you require, then all you need to do is listen on the port specified in the +contact.port option and join a known seed with KademliaNode#join. The +provided seed must be defined as a tuple (array) where the first item is the +hex encoded identity key of the seed and the second item is the +Bucket~contact object. You can read more about this structure in our +guide on Contacts and Identities.

+

If this node is the "first node" in the network, you don't need to call +KademliaNode#join, instead our node will just listen for connections +from others.

+
const seed = ['0000000000000000000000000000000000000000', { // (sample)
+  hostname: 'seed.hostname',
+  port: 8080
+}];
+
+node.once('join', function() {
+  console.info(`connected to ${node.router.size} peers`);
+});
+
+node.once('error', function(err) {
+  console.error('failed to join the network', err);
+});
+
+node.listen(node.contact.port);
+node.join(seed);
+
+

That's it, for a basic minimal Kademlia DHT, you're finished! Now you can use +the methods on KademliaNode to store and retrieve entries from the +network. To learn more about using plugins, extending the protocol with +middleware, custom transports, and the message pipelines, see:

+ +
+

Note! If you are using Kadence to build a distributed network from scratch +the best place to start is the reference implementation. +This provides a complete working Kadence network that leverages all the +features provided by the library as well as autogenerating keys, managing +configuration, and more!

+
+

+

Using the Daemon

+

Kadence "proper" describes a Kademlia DHT with several notable protocol +extensions - specifically the extensions that are authored in the core library +as plugins:

+ +

The daemon also leverages the following plugins that do not affect the protocol +itself, but rather provide features that improve the user experience or enable +other optional features.

+ +

Together these plugins combined with the base implementation of the Kademlia +DHT form the Kadence protocol and a complete standalone program for running a +configurable Kadence node. If you installed Kadence using the -g or +--global flag, you now have access to the kadence command line program. +This program handles everything you need to interact with a Kadence network +from any programming language.

+

Identity Generation

+

Kadence mitigates eclipse attacks (a form of a sybil attack) by requiring node +identities to act as a proof-of-work solution. This means that Kadence expects +your node identity to be derived from a public key of which the Scrypt hash +contains a number of leading zero bits as defined by the value of +module:kadence/constants~IDENTITY_DIFFICULTY. This prevents adversaries +from quickly generating a large number of identities that are close enough to +each other to "surround" sections of the keyspace which could allow them to +poison the routing table, deny service, or otherwise manipulate portions of the +network.

+

The first time you run kadence, it will automatically begin "mining" a valid +identity, which can take some time depending on your hardware. If you are just +getting started with testing Kadence, you'll probably want to set +TestNetworkEnabled=1 in your $HOME/.config/kadence/config or set the +environment variable kadence_TestNetworkEnabled=1 (see Configuration Guide). +This will reduce the difficulty significantly and allow you to get started +quickly. In a live "production" network, you can pass --solvers N where N +is the number of CPU cores you'd like to dedicate to identity mining (and +solution mining as discussed later).

+

In the example below, we are also setting kadence_TraverseNatEnabled=0 +because for now we aren't interested in punching out and becoming addessable +on the internet.

+
$ export kadence_TestNetworkEnabled=1 kadence_TraverseNatEnabled=0
+
+$ kadence
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"kadence is running in test mode, difficulties are reduced","time":"2018-03-16T15:28:05.188Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":40,"msg":"identity derivation not yet solved - 0 is invalid","time":"2018-03-16T15:28:05.357Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"solving identity derivation index with 1 solver processes, this can take a while","time":"2018-03-16T15:28:05.357Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"forking derivation process 0","time":"2018-03-16T15:28:05.377Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"solved identity derivation index 11 in 882ms","time":"2018-03-16T15:28:06.239Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"initializing kadence","time":"2018-03-16T15:28:06.244Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"validating solutions in wallet, this can take some time","time":"2018-03-16T15:28:06.257Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"node listening on local port 5274 and exposed at https://127.0.0.1:5274","time":"2018-03-16T15:28:06.262Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"binding controller to path /home/bookchin/.config/kadence/kadence.sock","time":"2018-03-16T15:28:06.262Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"forking solver process 0","time":"2018-03-16T15:28:06.263Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"no bootstrap seeds provided and no known profiles","time":"2018-03-16T15:28:06.269Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"running in seed mode (waiting for connections)","time":"2018-03-16T15:28:06.269Z","v":0}
+{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"derivation solver 0 exited normally","time":"2018-03-16T15:28:06.272Z","v":0}
+
+

Notice the log message solved identity derivation index 11 in 882ms. This +means that a new hierarchically deterministic private extended key was +generated and the child private key at index 11 yielded a public key that when +hashed with Scrypt satisfies the identity difficulty. Now you can join your +test Kadence network.

+

Solution Mining

+

Once your Kadence node has generated a valid identity, you'll begin seeing log +messages similar to the following:

+
{"name":"kadence","hostname":"librem","pid":23409,"level":30,"msg":"solver 0 found solution in 4 attempts (226ms)","time":"2018-03-16T15:28:06.804Z","v":0}
+
+

This is part of Kadence's permission protocol for storing entries in the DHT. +In a basic Kademlia network, entries can be stored and overwritten by any +party. Kadence employs a proof-of-work system that requires nodes attempting +to store an entry provide a "solution". Solutions are "mined" by a process +similar to how Kadence identities are generated, but instead are derived from +the identity solution. When a solution is found, it is stored in a "wallet" - +a directory of solution files.

+

Solutions are then hashed and the resulting 160 bit key can be used to store +arbitrary data in the DHT and is keyed by the solution hash. In practice, this +means that your application must track any mapping from a key your application +understands to the solution hash that was used to store the entry in the +network.

+
+

While TestNetworkEnabled=1, these solutions will be found very quickly, so +it's probably desirable to start the daemon with --solvers 0 after you have +mined enough solutions to use during development.

+
+

Controlling the Daemon

+

The Kadence daemon exposes a control interface to other applications by default +over a UNIX domain socket located at $HOME/.config/kadence/kadence.sock, but +may also be configured to listen on a TCP port instead. You may not enable both +types at once.

+

The control interface speaks JSON-RPC 2.0 and it's API is documented here. You can interact with the controller from any language that +can open a socket connection. For this example we'll use telnet and use the +TCP socket interface.

+
$ export kadence_ControlPortEnabled=1 kadence_ControlSockEnabled=0
+
+$ kadence --solvers 0
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"kadence is running in test mode, difficulties are reduced","time":"2018-03-16T16:43:04.440Z","v":0}
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"initializing kadence","time":"2018-03-16T16:43:04.503Z","v":0}
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"validating solutions in wallet, this can take some time","time":"2018-03-16T16:43:04.519Z","v":0}
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"node listening on local port 5274 and exposed at https://127.0.0.1:5274","time":"2018-03-16T16:43:04.576Z","v":0}
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"binding controller to port 5275","time":"2018-03-16T16:43:04.577Z","v":0}
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"there are no solver processes running","time":"2018-03-16T16:43:04.577Z","v":0}
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"no bootstrap seeds provided and no known profiles","time":"2018-03-16T16:43:04.578Z","v":0}
+{"name":"kadence","hostname":"librem","pid":24893,"level":30,"msg":"running in seed mode (waiting for connections)","time":"2018-03-16T16:43:04.578Z","v":0}
+
+

When starting Kadence with ControlPortEnabled=1, you'll notice a log message +binding controller to port 5275. Open a connection to this port and you can +start sending commands by typing a JSON-RPC payload and pressing return (which +terminates the command with a \r\n). The result of the command will be +written back to the socket.

+
$ telnet localhost 5275
+Trying ::1...
+Trying 127.0.0.1...
+Connected to localhost.
+Escape character is '^]'.
+{"jsonrpc":"2.0","id":"1234567890","method":"getProtocolInfo","params":[]}
+{"jsonrpc":"2.0","id":"1234567890","result":[{"versions":{"protocol":"1.0.0","software":"3.1.2"},"identity":"27f06eba2be0a5f1399bfc0ebd477522118d1f69","contact":{"hostname":"127.0.0.1","protocol":"https:","port":5274,"xpub":"xpub69sEXvvUfWbQg8FSCPWPojcrUpkbtNkKDLSNSTx9GAsB1MVpeZ5eoCQTo4EViDnVn7pPpLbGq83aoD24vTGPnDKnXxqGJxxNbEJhizfFFQH","index":11,"agent":"1.0.0"},"peers":[]}]}
+^]
+telnet> quit
+Connection closed.
+
+

Complete documentation on configuration properties and what they do can be +reviewed in the Configuration Guide.

+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/tutorial-transport-adapters.html b/docs/tutorial-transport-adapters.html new file mode 100644 index 0000000..4053a78 --- /dev/null +++ b/docs/tutorial-transport-adapters.html @@ -0,0 +1,127 @@ + + + + + + Transport Adapters - Documentation + + + + + + + + + + + + + + + + + +
+ +

Transport Adapters

+ + +
+ +
+ +
+ +
+

Kadence does not impose any particular transport layer, which makes it very +flexible for applying to many use cases. As far as Kadence is concerned, a valid +transport adapter is any objectMode +DuplexStream +that exposes a listen() method.

+

Kadence ships with UDP and HTTP(S) transports so you don't need to implement a +transport adapter yourself to get started. If your network layer needs are not +met by these, check out the interface for AbstractNode~transport.

+

API for Transport Implementers

+

The transport adapter interface has been designed to make implementing any +given networking or communication layer easy using JavaScript's inheritance +model.

+

First, a developer would declare a new JavaScript class that extends the +DuplexStream +class, and implements the _read, _write, and listen methods. This +architecture makes it simple to implement any type of transport layer.

+

When a consumer reads from the stream, they shall expect to receive a raw +buffer representing a received message which is processed by a +Messenger instance. When a consumer writes to the stream, they shall +expect the adapter to dispatch the message to the target. Calling listen on +the stream should perform any initialization needed, like binding to a port.

+

Transport streams must be placed in objectMode. The _read method must push +the received messages as raw buffers to be parsed by the deserializer used by +the Messenger class (which by default is JSON-RPC). The _write method +receives an array object as it's first argument which contains the following:

+
[
+  // String: unique identifier for the message, can be a request or a response
+  messageId,
+  // Buffer: raw payload to be delivered to the target
+  messagePayload,
+  [
+    // String: target contact's identity key
+    identityKey,
+    // Object: target contact's address information (transport-specific)
+    contactInfo
+  ]
+]
+
+

Example: UDP Transport

+

Implementing a UDP based transport adapter is very simple given that no state +must be maintained between requests and responses, so we will use it as a +simple example of how you might implement a transport.

+
const { Duplex: DuplexStream } = require('stream');
+const dgram = require('dgram');
+
+class UDPTransport extends DuplexStream {
+
+  constructor(options) {
+    super({ objectMode: true });
+    this.socket = dgram.createSocket();
+  }
+
+  _write([id, buffer, target], encoding, callback) {
+    let [, contact] = target;
+    this.socket.send(buffer, 0, buffer.length, contact.port, contact.hostname,
+                     callback);
+  }
+
+  _read() {
+    this.socket.once('message', (buffer) => {
+      this.push(buffer);
+    });
+  }
+
+  listen() {
+    this.socket.bind(...arguments);
+  }
+
+}
+
+
+ +
+ +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:07 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + \ No newline at end of file diff --git a/docs/utils.js.html b/docs/utils.js.html new file mode 100644 index 0000000..c0499e1 --- /dev/null +++ b/docs/utils.js.html @@ -0,0 +1,507 @@ + + + + + + utils.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

utils.js

+ + + + + + + +
+
+
/**
+* @module kadence/utils
+*/
+
+'use strict';
+
+const secp256k1 = require('secp256k1');
+const url = require('url');
+const constants = require('./constants');
+const semver = require('semver');
+const ip = require('ip');
+const crypto = require('crypto');
+const assert = require('assert');
+const { randomBytes, createHash } = crypto;
+const ms = require('ms');
+//const equihash = require('equihash/lib/khovratovich'); // does not build on gcc-11 +
+
+
+/**
+ * Tests if a string is valid hex
+ * @param {string} str
+ * @returns {boolean}
+ */
+module.exports.isHexaString = function(str) {
+  return Buffer.from(str, 'hex').length === str.length / 2;
+};
+
+/**
+ * Returns a random valid key/identity as a string
+ * @returns {string}
+ */
+exports.getRandomKeyString = function() {
+  return exports.getRandomKeyBuffer().toString('hex');
+};
+
+/**
+ * Returns a random valid key/identity as a buffer
+ * @returns {buffer}
+ */
+exports.getRandomKeyBuffer = function() {
+  return crypto.randomBytes(constants.B / 8);
+};
+
+/**
+ * Determines if the given string key is valid
+ * @param {string} key - Node ID or item key
+ * @returns {boolean}
+ */
+exports.keyStringIsValid = function(key) {
+  let buf;
+
+  try {
+    buf = Buffer.from(key, 'hex');
+  } catch (err) {
+    return false;
+  }
+
+  return exports.keyBufferIsValid(buf);
+};
+
+/**
+ * Determines if the given buffer key is valid
+ * @param {buffer} key - Node ID or item key
+ * @returns {boolean}
+ */
+exports.keyBufferIsValid = function(key) {
+  return Buffer.isBuffer(key) && key.length === constants.B / 8;
+};
+
+/**
+ * Calculate the distance between two keys
+ * @param {string} key1 - Identity key to compare
+ * @param {string} key2 - Identity key to compare
+ * @returns {buffer}
+ */
+exports.getDistance = function(id1, id2) {
+  id1 = !Buffer.isBuffer(id1)
+    ? Buffer.from(id1, 'hex')
+    : id1;
+  id2 = !Buffer.isBuffer(id2)
+    ? Buffer.from(id2, 'hex')
+    : id2;
+
+  assert(exports.keyBufferIsValid(id1), 'Invalid key supplied');
+  assert(exports.keyBufferIsValid(id2), 'Invalid key supplied');
+
+  return Buffer.alloc(constants.B / 8)
+    .map((b, index) => id1[index] ^ id2[index]);
+};
+
+/**
+ * Compare two buffers for sorting
+ * @param {buffer} b1 - Buffer to compare
+ * @param {buffer} b2 - Buffer to compare
+ * @returns {number}
+ */
+exports.compareKeyBuffers = function(b1, b2) {
+  assert(exports.keyBufferIsValid(b1), 'Invalid key supplied');
+  assert(exports.keyBufferIsValid(b2), 'Invalid key supplied');
+
+  for (let index = 0; index < b1.length; index++) {
+    let bits = b1[index];
+
+    if (bits !== b2[index]) {
+      return bits < b2[index] ? -1 : 1;
+    }
+  }
+
+  return 0;
+};
+
+/**
+ * Calculate the index of the bucket that key would belong to
+ * @param {string} referenceKey - Key to compare
+ * @param {string} foreignKey - Key to compare
+ * @returns {number}
+ */
+exports.getBucketIndex = function(referenceKey, foreignKey) {
+  let distance = exports.getDistance(referenceKey, foreignKey);
+  let bucketIndex = constants.B;
+
+  for (let byteValue of distance) {
+    if (byteValue === 0) {
+      bucketIndex -= 8;
+      continue;
+    }
+
+    for (let i = 0; i < 8; i++) {
+      if (byteValue & (0x80 >> i)) {
+        return --bucketIndex;
+      } else {
+        bucketIndex--;
+      }
+    }
+  }
+
+  return bucketIndex;
+};
+
+/**
+ * Returns a buffer with a power-of-two value given a bucket index
+ * @param {string|buffer} referenceKey - Key to find next power of two
+ * @param {number} bucketIndex - Bucket index for key
+ * @returns {buffer}
+ */
+exports.getPowerOfTwoBufferForIndex = function(referenceKey, exp) {
+  assert(exp >= 0 && exp < constants.B, 'Index out of range');
+
+  const buffer = Buffer.isBuffer(referenceKey)
+    ? Buffer.from(referenceKey)
+    : Buffer.from(referenceKey, 'hex');
+  const byteValue = parseInt(exp / 8);
+
+  // NB: We set the byte containing the bit to the right left shifted amount
+  buffer[constants.K - byteValue - 1] = 1 << (exp % 8);
+
+  return buffer;
+};
+
+/**
+ * Generate a random number within the bucket's range
+ * @param {buffer} referenceKey - Key for bucket distance reference
+ * @param {number} index - Bucket index for random buffer selection
+ */
+exports.getRandomBufferInBucketRange = function(referenceKey, index) {
+  let base = exports.getPowerOfTwoBufferForIndex(referenceKey, index);
+  let byte = parseInt(index / 8); // NB: Randomize bytes below the power of two
+
+  for (let i = constants.K - 1; i > (constants.K - byte - 1); i--) {
+    base[i] = parseInt(Math.random() * 256);
+  }
+
+  // NB: Also randomize the bits below the number in that byte and remember
+  // NB: arrays are off by 1
+  for (let j = index - 1; j >= byte * 8; j--) {
+    let one = Math.random() >= 0.5;
+    let shiftAmount = j - byte * 8;
+
+    base[constants.K - byte - 1] |= one ? (1 << shiftAmount) : 0;
+  }
+
+  return base;
+};
+
+/**
+ * Validates the given object is a storage adapter
+ * @param {AbstractNode~storage} storageAdapter
+ */
+exports.validateStorageAdapter = function(storage) {
+  assert(typeof storage === 'object',
+    'No storage adapter supplied');
+  assert(typeof storage.get === 'function',
+    'Store has no get method');
+  assert(typeof storage.put === 'function',
+    'Store has no put method');
+  assert(typeof storage.del === 'function',
+    'Store has no del method');
+  assert(typeof storage.createReadStream === 'function',
+    'Store has no createReadStream method');
+};
+
+/**
+ * Validates the given object is a logger
+ * @param {AbstractNode~logger} logger
+ */
+exports.validateLogger = function(logger) {
+  assert(typeof logger === 'object',
+    'No logger object supplied');
+  assert(typeof logger.debug === 'function',
+    'Logger has no debug method');
+  assert(typeof logger.info === 'function',
+    'Logger has no info method');
+  assert(typeof logger.warn === 'function',
+    'Logger has no warn method');
+  assert(typeof logger.error === 'function',
+    'Logger has no error method');
+};
+
+/**
+ * Validates the given object is a transport
+ * @param {AbstractNode~transport} transport
+ */
+exports.validateTransport = function(transport) {
+  assert(typeof transport === 'object',
+    'No transport adapter supplied');
+  assert(typeof transport.read === 'function',
+    'Transport has no read method');
+  assert(typeof transport.write === 'function',
+    'Transport has no write method');
+};
+
+/**
+ * Returns the SHA-256 hash of the input
+ * @param {buffer} input - Data to hash
+ */
+module.exports.hash256 = function(input) {
+  return crypto.createHash('sha256').update(input).digest();
+};
+
+/**
+ * @typedef EquihashProof
+ * @type {object}
+ * @property {number} n
+ * @property {number} k
+ * @property {number} nonce
+ * @property {buffer} value
+ */
+
+/**
+ * Performs an equihash solution using defaults
+ * @param {buffer} input - Input hash to solve
+ * @returns {Promise<EquihashProof>}
+ */
+module.exports.eqsolve = function() {
+  return Promise.reject(new Error('Equihash implementation is not functional'));
+};
+
+/**
+ * Perform an equihash proof verification
+ * @param {buffer} input - Input hash for proof
+ * @param {buffer} proof - Equihash proof to verify
+ * @returns {boolean}
+ */
+module.exports.eqverify = function(/*input, proof*/) {
+  throw new Error('Equihash implementation is not functional');
+  // return equihash.verify(input, proof);
+};
+
+/**
+ * Returns the RMD-160 hash of the input
+ * @param {buffer} input - Data to hash
+ */
+module.exports.hash160 = function(input) {
+  return crypto.createHash('ripemd160').update(input).digest();
+};
+
+/**
+ * Returns a stringified URL from the supplied contact object
+ * @param {Bucket~contact} contact
+ * @returns {string}
+ */
+module.exports.getContactURL = function(contact) {
+  const [id, info] = contact;
+
+  return `${info.protocol}//${info.hostname}:${info.port}/#${id}`;
+};
+
+/**
+ * Returns a parsed contact object from a URL
+ * @returns {object}
+ */
+module.exports.parseContactURL = function(addr) {
+  const { protocol, hostname, port, hash } = url.parse(addr);
+  const contact = [
+    (hash ? hash.substr(1) : null) ||
+      Buffer.alloc(constants.B / 8).fill(0).toString('hex'),
+    {
+      protocol,
+      hostname,
+      port
+    }
+  ];
+
+  return contact;
+};
+
+/**
+ * Returns whether or not the supplied semver tag is compatible
+ * @param {string} version - The semver tag from the contact
+ * @returns {boolean}
+ */
+module.exports.isCompatibleVersion = function(version) {
+  const local = require('./version').protocol;
+  const remote = version;
+  const sameMajor = semver.major(local) === semver.major(remote);
+  const diffs = ['prerelease', 'prepatch', 'preminor', 'premajor'];
+
+  if (diffs.indexOf(semver.diff(remote, local)) !== -1) {
+    return false;
+  } else {
+    return sameMajor;
+  }
+};
+
+/**
+ * Determines if the supplied contact is valid
+ * @param {Bucket~contact} contact - The contact information for a given peer
+ * @param {boolean} loopback - Allows contacts that are localhost
+ * @returns {boolean}
+ */
+module.exports.isValidContact = function(contact, loopback) {
+  const [, info] = contact;
+  const isValidAddr = ip.isV4Format(info.hostname) ||
+                      ip.isV6Format(info.hostname) ||
+                      ip.isPublic(info.hostname);
+  const isValidPort = info.port > 0;
+  const isAllowedAddr = ip.isLoopback(info.hostname) ? !!loopback : true;
+
+  return isValidPort && isValidAddr && isAllowedAddr;
+};
+
+/**
+ * Converts a buffer to a string representation of binary
+ * @param {buffer} buffer - Byte array to convert to binary string
+ * @returns {string}
+ */
+module.exports.toBinaryStringFromBuffer = function(buffer) {
+  const mapping = {
+    '0': '0000',
+    '1': '0001',
+    '2': '0010',
+    '3': '0011',
+    '4': '0100',
+    '5': '0101',
+    '6': '0110',
+    '7': '0111',
+    '8': '1000',
+    '9': '1001',
+    'a': '1010',
+    'b': '1011',
+    'c': '1100',
+    'd': '1101',
+    'e': '1110',
+    'f': '1111'
+  };
+  const hexaString = buffer.toString('hex').toLowerCase();
+  const bitmaps = [];
+
+  for (let i = 0; i < hexaString.length; i++) {
+    bitmaps.push(mapping[hexaString[i]]);
+  }
+
+  return bitmaps.join('');
+};
+
+/**
+ * Returns a boolean indicating if the supplied buffer meets the given
+ * difficulty requirement
+ * @param {buffer} buffer - Buffer to check difficulty
+ * @param {number} difficulty - Number of leading zeroes
+ * @returns {boolean}
+ */
+module.exports.satisfiesDifficulty = function(buffer, difficulty) {
+  const binString = module.exports.toBinaryStringFromBuffer(buffer);
+  const prefix = Array(difficulty).fill('0').join('');
+
+  return binString.substr(0, difficulty) === prefix;
+};
+
+/**
+ * @private
+ */
+module.exports._sha256 = function(input) {
+  return createHash('sha256').update(input).digest();
+};
+
+/**
+ * @private
+ */
+module.exports._rmd160 = function(input) {
+  return createHash('ripemd160').update(input).digest();
+};
+
+/**
+ * Generates a private key
+ * @returns {buffer}
+ */
+module.exports.generatePrivateKey = function() {
+  let privKey
+
+  do {
+    privKey = randomBytes(32);
+  } while (!secp256k1.privateKeyVerify(privKey))
+
+  return privKey;
+};
+
+/**
+ * Takes a public key are returns the identity
+ * @param {buffer} publicKey - Raw public key bytes
+ * @returns {buffer}
+ */
+module.exports.toPublicKeyHash = function(publicKey) {
+  return exports._rmd160(exports._sha256(publicKey));
+};
+
+/**
+ * Wraps the supplied function in a pseudo-random length timeout to help
+ * prevent convoy effects. These occur when a number of processes need to use
+ * a resource in turn. There is a tendency for such bursts of activity to
+ * drift towards synchronization, which can be disasterous. In Kademlia all
+ * nodes are requird to republish their contents every hour (T_REPLICATE). A
+ * convoy effect might lead to this being synchronized across the network,
+ * which would appear to users as the network dying every hour. The default
+ * timeout will be between 0 and 30 minutes unless specified.
+ * @param {function} func - Function to wrap to execution later
+ * @param {number} [maxtime] - Maximum timeout
+ * @returns {function}
+ */
+module.exports.preventConvoy = function(func, timeout) {
+  return function() {
+    let t = Math.ceil(
+      Math.random() * (typeof timeout !== 'number' ? ms('30m') : timeout)
+    );
+    return setTimeout(func, t);
+  };
+};
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:06 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + diff --git a/docs/version.js.html b/docs/version.js.html new file mode 100644 index 0000000..850dd8f --- /dev/null +++ b/docs/version.js.html @@ -0,0 +1,93 @@ + + + + + + version.js - Documentation + + + + + + + + + + + + + + + + + +
+ +

version.js

+ + + + + + + +
+
+
/**
+ * @module kadence/version
+ */
+
+'use strict';
+
+var semver = require('semver');
+var assert = require('assert');
+
+module.exports = {
+  /**
+   * @constant {string} protocol - The supported protocol version
+   */
+  protocol: '2.0.0',
+  /**
+   * @constant {string} software - The current software version
+   */
+  software: require('../package').version,
+  /**
+   * Returns human readable string of versions
+   * @function
+   * @returns {string}
+   */
+  toString: function() {
+    let { software, protocol } = module.exports;
+    return `kadence v${software} protocol v${protocol}`;
+  }
+};
+
+assert(
+  semver.valid(module.exports.protocol),
+  'Invalid protocol version specified'
+);
+
+
+
+ + + + +
+ +
+ +
+ Generated by JSDoc 3.6.11 on Fri Nov 08 2024 12:34:06 GMT-0800 (Pacific Standard Time) using the Minami theme. +
+ + + + + diff --git a/package-lock.json b/package-lock.json index bdecc6f..6a57de8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@tacticalchihuahua/kadence", - "version": "6.1.9", + "version": "7.0.0-unstable", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -3539,6 +3539,12 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" }, + "minami": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/minami/-/minami-1.2.3.tgz", + "integrity": "sha512-3f2QqqbUC1usVux0FkQMFYB73yd9JIxmHSn1dWQacizL6hOUaNu6mA3KxZ9SfiCc4qgcgq+5XP59+hP7URa1Dw==", + "dev": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", diff --git a/package.json b/package.json index 24d0766..ef0e9c9 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "istanbul": "^1.1.0-alpha.1", "jsdoc": "^3.6.3", "memdown": "^2.0.0", + "minami": "^1.2.3", "mocha": "^5.2.0", "proxyquire": "^1.8.0", "rimraf": "^2.6.1", @@ -102,7 +103,8 @@ "linter": "eslint ./index.js ./lib", "start": "docker-compose up --build --force-recreate --always-recreate-deps", "test": "npm run unit-tests && npm run integration-tests && npm run e2e-tests && npm run linter", - "unit-tests": "mocha --exit test/*.unit.js" + "unit-tests": "mocha --exit test/*.unit.js", + "generate-docs": "mkdir -p ./docs && rm -r ./docs && jsdoc lib -r -R README.md -u ./tutorials -c .jsdoc.json --verbose -d ./docs" }, "version": "7.0.0-unstable" } diff --git a/doc/config.md b/tutorials/config.md similarity index 100% rename from doc/config.md rename to tutorials/config.md diff --git a/doc/identities.md b/tutorials/identities.md similarity index 100% rename from doc/identities.md rename to tutorials/identities.md diff --git a/doc/index.json b/tutorials/index.json similarity index 100% rename from doc/index.json rename to tutorials/index.json diff --git a/doc/install.md b/tutorials/install.md similarity index 100% rename from doc/install.md rename to tutorials/install.md diff --git a/doc/messengers.md b/tutorials/messengers.md similarity index 100% rename from doc/messengers.md rename to tutorials/messengers.md diff --git a/doc/middleware.md b/tutorials/middleware.md similarity index 100% rename from doc/middleware.md rename to tutorials/middleware.md diff --git a/doc/nat.md b/tutorials/nat.md similarity index 100% rename from doc/nat.md rename to tutorials/nat.md diff --git a/doc/plugins.md b/tutorials/plugins.md similarity index 100% rename from doc/plugins.md rename to tutorials/plugins.md diff --git a/doc/protocol.md b/tutorials/protocol.md similarity index 100% rename from doc/protocol.md rename to tutorials/protocol.md diff --git a/doc/quickstart.md b/tutorials/quickstart.md similarity index 100% rename from doc/quickstart.md rename to tutorials/quickstart.md diff --git a/doc/transport-adapters.md b/tutorials/transport-adapters.md similarity index 100% rename from doc/transport-adapters.md rename to tutorials/transport-adapters.md