diff --git a/supernode/index.html b/supernode/index.html
index a897a7b..fe24e42 100644
--- a/supernode/index.html
+++ b/supernode/index.html
@@ -9163,8 +9163,6 @@
})();
}
-
-
reactor.registerEvent('added');
reactor.addEventListener('added', function (someObject) {
console.log('Added fired with data ' + someObject);
@@ -9180,6 +9178,10 @@
console.log('Updated fired with data ' + someObject);
});
+ reactor.registerEvent('bucket_full');
+ reactor.addEventListener('bucket_full', function (someObject) {
+ console.log('Bucket full ' + someObject);
+ });
/*
//Sample Usage
@@ -9200,441 +9202,420 @@
//Checking if existing NodeID can be used
//This first block of if will initialize the configuration of KBucket
//Add Events, Messaging between different K-Buckets, and attach relevant distributed data
- if (typeof KBucketOptions == "undefined" || !KBucketOptions) {
- var KBucketOptions = {};
- var sanitizedFLOAddress = new Uint8Array(20);
- KBucketOptions.localNodeId = window.crypto.getRandomValues(sanitizedFLOAddress);
-
- }
-
- if (typeof KBucket == "undefined" || !KBucket) {
- (function (options) {
-
-
-
- var buf = new Uint8Array(20);
- var randomBytes = window.crypto.getRandomValues(buf);
-
-
- /**
- * @param {Uint8Array} array1
- * @param {Uint8Array} array2
- * @return {Boolean}
- */
- function arrayEquals(array1, array2) {
- if (array1 === array2) {
- return true
- }
- if (array1.length !== array2.length) {
- return false
- }
- for (let i = 0, length = array1.length; i < length; ++i) {
- if (array1[i] !== array2[i]) {
- return false
- }
- }
+ /**
+ * @param {Uint8Array} array1
+ * @param {Uint8Array} array2
+ * @return {Boolean}
+ */
+ function arrayEquals(array1, array2) {
+ if (array1 === array2) {
return true
}
-
- function createNode() {
- return {
- contacts: [],
- dontSplit: false,
- left: null,
- right: null
+ if (array1.length !== array2.length) {
+ return false
+ }
+ for (let i = 0, length = array1.length; i < length; ++i) {
+ if (array1[i] !== array2[i]) {
+ return false
}
}
+ return true
+ }
- function ensureInt8(name, val) {
- if (!(val instanceof Uint8Array)) {
- throw new TypeError(name + ' is not a Uint8Array')
- }
+ function createNode() {
+ return {
+ contacts: [],
+ dontSplit: false,
+ left: null,
+ right: null
+ }
+ }
+
+ function ensureInt8(name, val) {
+ if (!(val instanceof Uint8Array)) {
+ throw new TypeError(name + ' is not a Uint8Array')
+ }
+ }
+
+ /**
+ * Implementation of a Kademlia DHT k-bucket used for storing
+ * contact (peer node) information.
+ *
+ * @extends EventEmitter
+ */
+ function BuildKBucket(options = {}) {
+ /**
+ * `options`:
+ * `distance`: Function
+ * `function (firstId, secondId) { return distance }` An optional
+ * `distance` function that gets two `id` Uint8Arrays
+ * and return distance (as number) between them.
+ * `arbiter`: Function (Default: vectorClock arbiter)
+ * `function (incumbent, candidate) { return contact; }` An optional
+ * `arbiter` function that givent two `contact` objects with the same `id`
+ * returns the desired object to be used for updating the k-bucket. For
+ * more details, see [arbiter function](#arbiter-function).
+ * `localNodeId`: Uint8Array An optional Uint8Array representing the local node id.
+ * If not provided, a local node id will be created via `randomBytes(20)`.
+ * `metadata`: Object (Default: {}) Optional satellite data to include
+ * with the k-bucket. `metadata` property is guaranteed not be altered by,
+ * it is provided as an explicit container for users of k-bucket to store
+ * implementation-specific data.
+ * `numberOfNodesPerKBucket`: Integer (Default: 20) The number of nodes
+ * that a k-bucket can contain before being full or split.
+ * `numberOfNodesToPing`: Integer (Default: 3) The number of nodes to
+ * ping when a bucket that should not be split becomes full. KBucket will
+ * emit a `ping` event that contains `numberOfNodesToPing` nodes that have
+ * not been contacted the longest.
+ *
+ * @param {Object=} options optional
+ */
+
+ this.localNodeId = options.localNodeId || window.crypto.getRandomValues(new Uint8Array(20))
+ this.numberOfNodesPerKBucket = options.numberOfNodesPerKBucket || 20
+ this.numberOfNodesToPing = options.numberOfNodesToPing || 3
+ this.distance = options.distance || this.distance
+ // use an arbiter from options or vectorClock arbiter by default
+ this.arbiter = options.arbiter || this.arbiter
+ this.metadata = Object.assign({}, options.metadata)
+
+ ensureInt8('option.localNodeId as parameter 1', this.localNodeId)
+
+ this.root = createNode()
+
+
+ /**
+ * Default arbiter function for contacts with the same id. Uses
+ * contact.vectorClock to select which contact to update the k-bucket with.
+ * Contact with larger vectorClock field will be selected. If vectorClock is
+ * the same, candidat will be selected.
+ *
+ * @param {Object} incumbent Contact currently stored in the k-bucket.
+ * @param {Object} candidate Contact being added to the k-bucket.
+ * @return {Object} Contact to updated the k-bucket with.
+ */
+ this.arbiter = function (incumbent, candidate) {
+ return incumbent.vectorClock > candidate.vectorClock ? incumbent : candidate
}
/**
- * Implementation of a Kademlia DHT k-bucket used for storing
- * contact (peer node) information.
+ * Default distance function. Finds the XOR
+ * distance between firstId and secondId.
*
- * @extends EventEmitter
+ * @param {Uint8Array} firstId Uint8Array containing first id.
+ * @param {Uint8Array} secondId Uint8Array containing second id.
+ * @return {Number} Integer The XOR distance between firstId
+ * and secondId.
*/
- function KBucket(options = {}) {
- /**
- * `options`:
- * `distance`: Function
- * `function (firstId, secondId) { return distance }` An optional
- * `distance` function that gets two `id` Uint8Arrays
- * and return distance (as number) between them.
- * `arbiter`: Function (Default: vectorClock arbiter)
- * `function (incumbent, candidate) { return contact; }` An optional
- * `arbiter` function that givent two `contact` objects with the same `id`
- * returns the desired object to be used for updating the k-bucket. For
- * more details, see [arbiter function](#arbiter-function).
- * `localNodeId`: Uint8Array An optional Uint8Array representing the local node id.
- * If not provided, a local node id will be created via `randomBytes(20)`.
- * `metadata`: Object (Default: {}) Optional satellite data to include
- * with the k-bucket. `metadata` property is guaranteed not be altered by,
- * it is provided as an explicit container for users of k-bucket to store
- * implementation-specific data.
- * `numberOfNodesPerKBucket`: Integer (Default: 20) The number of nodes
- * that a k-bucket can contain before being full or split.
- * `numberOfNodesToPing`: Integer (Default: 3) The number of nodes to
- * ping when a bucket that should not be split becomes full. KBucket will
- * emit a `ping` event that contains `numberOfNodesToPing` nodes that have
- * not been contacted the longest.
- *
- * @param {Object=} options optional
- */
+ this.distance = function (firstId, secondId) {
+ let distance = 0
+ let i = 0
+ const min = Math.min(firstId.length, secondId.length)
+ const max = Math.max(firstId.length, secondId.length)
+ for (; i < min; ++i) {
+ distance = distance * 256 + (firstId[i] ^ secondId[i])
+ }
+ for (; i < max; ++i) distance = distance * 256 + 255
+ return distance
+ }
- this.localNodeId = options.localNodeId || randomBytes
- this.numberOfNodesPerKBucket = options.numberOfNodesPerKBucket || 20
- this.numberOfNodesToPing = options.numberOfNodesToPing || 3
- this.distance = options.distance || KBucket.distance
- // use an arbiter from options or vectorClock arbiter by default
- this.arbiter = options.arbiter || KBucket.arbiter
- this.metadata = Object.assign({}, options.metadata)
+ /**
+ * Adds a contact to the k-bucket.
+ *
+ * @param {Object} contact the contact object to add
+ */
+ this.add = function (contact) {
+ ensureInt8('contact.id', (contact || {}).id)
- ensureInt8('option.localNodeId as parameter 1', this.localNodeId)
+ let bitIndex = 0
+ let node = this.root
- this.root = createNode()
-
-
- /**
- * Default arbiter function for contacts with the same id. Uses
- * contact.vectorClock to select which contact to update the k-bucket with.
- * Contact with larger vectorClock field will be selected. If vectorClock is
- * the same, candidat will be selected.
- *
- * @param {Object} incumbent Contact currently stored in the k-bucket.
- * @param {Object} candidate Contact being added to the k-bucket.
- * @return {Object} Contact to updated the k-bucket with.
- */
- this.arbiter = function (incumbent, candidate) {
- return incumbent.vectorClock > candidate.vectorClock ? incumbent : candidate
+ while (node.contacts === null) {
+ // this is not a leaf node but an inner node with 'low' and 'high'
+ // branches; we will check the appropriate bit of the identifier and
+ // delegate to the appropriate node for further processing
+ node = this._determineNode(node, contact.id, bitIndex++)
}
- /**
- * Default distance function. Finds the XOR
- * distance between firstId and secondId.
- *
- * @param {Uint8Array} firstId Uint8Array containing first id.
- * @param {Uint8Array} secondId Uint8Array containing second id.
- * @return {Number} Integer The XOR distance between firstId
- * and secondId.
- */
- this.distance = function (firstId, secondId) {
- let distance = 0
- let i = 0
- const min = Math.min(firstId.length, secondId.length)
- const max = Math.max(firstId.length, secondId.length)
- for (; i < min; ++i) {
- distance = distance * 256 + (firstId[i] ^ secondId[i])
- }
- for (; i < max; ++i) distance = distance * 256 + 255
- return distance
- }
-
- /**
- * Adds a contact to the k-bucket.
- *
- * @param {Object} contact the contact object to add
- */
- this.add = function (contact) {
- ensureInt8('contact.id', (contact || {}).id)
-
- let bitIndex = 0
- let node = this.root
-
- while (node.contacts === null) {
- // this is not a leaf node but an inner node with 'low' and 'high'
- // branches; we will check the appropriate bit of the identifier and
- // delegate to the appropriate node for further processing
- node = this._determineNode(node, contact.id, bitIndex++)
- }
-
- // check if the contact already exists
- const index = this._indexOf(node, contact.id)
- if (index >= 0) {
- this._update(node, index, contact)
- return this
- }
-
- if (node.contacts.length < this.numberOfNodesPerKBucket) {
- node.contacts.push(contact)
- reactor.dispatchEvent('added', contact)
- return this
- }
-
- // the bucket is full
- if (node.dontSplit) {
- // we are not allowed to split the bucket
- // we need to ping the first this.numberOfNodesToPing
- // in order to determine if they are alive
- // only if one of the pinged nodes does not respond, can the new contact
- // be added (this prevents DoS flodding with new invalid contacts)
- this.emit('ping', node.contacts.slice(0, this.numberOfNodesToPing), contact)
- return this
- }
-
- this._split(node, bitIndex)
- return this.add(contact)
- }
-
- /**
- * Get the n closest contacts to the provided node id. "Closest" here means:
- * closest according to the XOR metric of the contact node id.
- *
- * @param {Uint8Array} id Contact node id
- * @param {Number=} n Integer (Default: Infinity) The maximum number of
- * closest contacts to return
- * @return {Array} Array Maximum of n closest contacts to the node id
- */
- this.closest = function (id, n = Infinity) {
- ensureInt8('id', id)
-
- if ((!Number.isInteger(n) && n !== Infinity) || n <= 0) {
- throw new TypeError('n is not positive number')
- }
-
- let contacts = []
-
- for (let nodes = [this.root], bitIndex = 0; nodes.length > 0 && contacts.length < n;) {
- const node = nodes.pop()
- if (node.contacts === null) {
- const detNode = this._determineNode(node, id, bitIndex++)
- nodes.push(node.left === detNode ? node.right : node.left)
- nodes.push(detNode)
- } else {
- contacts = contacts.concat(node.contacts)
- }
- }
-
- return contacts
- .map(a => [this.distance(a.id, id), a])
- .sort((a, b) => a[0] - b[0])
- .slice(0, n)
- .map(a => a[1])
- }
-
- /**
- * Counts the total number of contacts in the tree.
- *
- * @return {Number} The number of contacts held in the tree
- */
- this.count = function () {
- // return this.toArray().length
- let count = 0
- for (const nodes = [this.root]; nodes.length > 0;) {
- const node = nodes.pop()
- if (node.contacts === null) nodes.push(node.right, node.left)
- else count += node.contacts.length
- }
- return count
- }
-
- /**
- * Determines whether the id at the bitIndex is 0 or 1.
- * Return left leaf if `id` at `bitIndex` is 0, right leaf otherwise
- *
- * @param {Object} node internal object that has 2 leafs: left and right
- * @param {Uint8Array} id Id to compare localNodeId with.
- * @param {Number} bitIndex Integer (Default: 0) The bit index to which bit
- * to check in the id Uint8Array.
- * @return {Object} left leaf if id at bitIndex is 0, right leaf otherwise.
- */
- this._determineNode = function (node, id, bitIndex) {
- // *NOTE* remember that id is a Uint8Array and has granularity of
- // bytes (8 bits), whereas the bitIndex is the bit index (not byte)
-
- // id's that are too short are put in low bucket (1 byte = 8 bits)
- // (bitIndex >> 3) finds how many bytes the bitIndex describes
- // bitIndex % 8 checks if we have extra bits beyond byte multiples
- // if number of bytes is <= no. of bytes described by bitIndex and there
- // are extra bits to consider, this means id has less bits than what
- // bitIndex describes, id therefore is too short, and will be put in low
- // bucket
- const bytesDescribedByBitIndex = bitIndex >> 3
- const bitIndexWithinByte = bitIndex % 8
- if ((id.length <= bytesDescribedByBitIndex) && (bitIndexWithinByte !== 0)) {
- return node.left
- }
-
- const byteUnderConsideration = id[bytesDescribedByBitIndex]
-
- // byteUnderConsideration is an integer from 0 to 255 represented by 8 bits
- // where 255 is 11111111 and 0 is 00000000
- // in order to find out whether the bit at bitIndexWithinByte is set
- // we construct (1 << (7 - bitIndexWithinByte)) which will consist
- // of all bits being 0, with only one bit set to 1
- // for example, if bitIndexWithinByte is 3, we will construct 00010000 by
- // (1 << (7 - 3)) -> (1 << 4) -> 16
- if (byteUnderConsideration & (1 << (7 - bitIndexWithinByte))) {
- return node.right
- }
-
- return node.left
- }
-
- /**
- * Get a contact by its exact ID.
- * If this is a leaf, loop through the bucket contents and return the correct
- * contact if we have it or null if not. If this is an inner node, determine
- * which branch of the tree to traverse and repeat.
- *
- * @param {Uint8Array} id The ID of the contact to fetch.
- * @return {Object|Null} The contact if available, otherwise null
- */
- this.get = function (id) {
- ensureInt8('id', id)
-
- let bitIndex = 0
-
- let node = this.root
- while (node.contacts === null) {
- node = this._determineNode(node, id, bitIndex++)
- }
-
- // index of uses contact id for matching
- const index = this._indexOf(node, id)
- return index >= 0 ? node.contacts[index] : null
- }
-
- /**
- * Returns the index of the contact with provided
- * id if it exists, returns -1 otherwise.
- *
- * @param {Object} node internal object that has 2 leafs: left and right
- * @param {Uint8Array} id Contact node id.
- * @return {Number} Integer Index of contact with provided id if it
- * exists, -1 otherwise.
- */
- this._indexOf = function (node, id) {
- for (let i = 0; i < node.contacts.length; ++i) {
- if (arrayEquals(node.contacts[i].id, id)) return i
- }
-
- return -1
- }
-
- /**
- * Removes contact with the provided id.
- *
- * @param {Uint8Array} id The ID of the contact to remove.
- * @return {Object} The k-bucket itself.
- */
- this.remove = function (id) {
- ensureInt8('the id as parameter 1', id)
-
- let bitIndex = 0
- let node = this.root
-
- while (node.contacts === null) {
- node = this._determineNode(node, id, bitIndex++)
- }
-
- const index = this._indexOf(node, id)
- if (index >= 0) {
- const contact = node.contacts.splice(index, 1)[0]
- reactor.dispatchEvent('removed', contact)
- }
-
+ // check if the contact already exists
+ const index = this._indexOf(node, contact.id)
+ if (index >= 0) {
+ this._update(node, index, contact)
return this
}
- /**
- * Splits the node, redistributes contacts to the new nodes, and marks the
- * node that was split as an inner node of the binary tree of nodes by
- * setting this.root.contacts = null
- *
- * @param {Object} node node for splitting
- * @param {Number} bitIndex the bitIndex to which byte to check in the
- * Uint8Array for navigating the binary tree
- */
- this._split = function (node, bitIndex) {
- node.left = createNode()
- node.right = createNode()
-
- // redistribute existing contacts amongst the two newly created nodes
- for (const contact of node.contacts) {
- this._determineNode(node, contact.id, bitIndex).contacts.push(contact)
- }
-
- node.contacts = null // mark as inner tree node
-
- // don't split the "far away" node
- // we check where the local node would end up and mark the other one as
- // "dontSplit" (i.e. "far away")
- const detNode = this._determineNode(node, this.localNodeId, bitIndex)
- const otherNode = node.left === detNode ? node.right : node.left
- otherNode.dontSplit = true
+ if (node.contacts.length < this.numberOfNodesPerKBucket) {
+ node.contacts.push(contact)
+ reactor.dispatchEvent('added', contact)
+ return this
}
- /**
- * Returns all the contacts contained in the tree as an array.
- * If this is a leaf, return a copy of the bucket. `slice` is used so that we
- * don't accidentally leak an internal reference out that might be
- * accidentally misused. If this is not a leaf, return the union of the low
- * and high branches (themselves also as arrays).
- *
- * @return {Array} All of the contacts in the tree, as an array
- */
- this.toArray = function () {
- let result = []
- for (const nodes = [this.root]; nodes.length > 0;) {
- const node = nodes.pop()
- if (node.contacts === null) nodes.push(node.right, node.left)
- else result = result.concat(node.contacts)
- }
- return result
+ // the bucket is full
+ if (node.dontSplit) {
+ // we are not allowed to split the bucket
+ // we need to ping the first this.numberOfNodesToPing
+ // in order to determine if they are alive
+ // only if one of the pinged nodes does not respond, can the new contact
+ // be added (this prevents DoS flodding with new invalid contacts)
+ reactor.dispatchEvent('bucket_full', {1: node.contacts.slice(0, this.numberOfNodesToPing),2: contact})
+ return this
}
- /**
- * Updates the contact selected by the arbiter.
- * If the selection is our old contact and the candidate is some new contact
- * then the new contact is abandoned (not added).
- * If the selection is our old contact and the candidate is our old contact
- * then we are refreshing the contact and it is marked as most recently
- * contacted (by being moved to the right/end of the bucket array).
- * If the selection is our new contact, the old contact is removed and the new
- * contact is marked as most recently contacted.
- *
- * @param {Object} node internal object that has 2 leafs: left and right
- * @param {Number} index the index in the bucket where contact exists
- * (index has already been computed in a previous
- * calculation)
- * @param {Object} contact The contact object to update.
- */
- this._update = function (node, index, contact) {
- // sanity check
- if (!arrayEquals(node.contacts[index].id, contact.id)) {
- throw new Error('wrong index for _update')
- }
-
- const incumbent = node.contacts[index]
-
- /***************Change made by Abhishek*************/
- const selection = this.arbiter(incumbent, contact)
- //const selection = localbitcoinplusplus.kademlia.arbiter(incumbent, contact);
- // if the selection is our old contact and the candidate is some new
- // contact, then there is nothing to do
- if (selection === incumbent && incumbent !== contact) return
-
- node.contacts.splice(index, 1) // remove old contact
- node.contacts.push(selection) // add more recent contact version
- /***************Change made by Abhishek*************/
- reactor.dispatchEvent('updated', {
- ...incumbent,
- ...selection
- })
- //reactor.dispatchEvent('updated', incumbent.concat(selection))
- }
+ this._split(node, bitIndex)
+ return this.add(contact)
}
- window.KBucket = new KBucket(options)
+ /**
+ * Get the n closest contacts to the provided node id. "Closest" here means:
+ * closest according to the XOR metric of the contact node id.
+ *
+ * @param {Uint8Array} id Contact node id
+ * @param {Number=} n Integer (Default: Infinity) The maximum number of
+ * closest contacts to return
+ * @return {Array} Array Maximum of n closest contacts to the node id
+ */
+ this.closest = function (id, n = Infinity) {
+ ensureInt8('id', id)
- })(KBucketOptions);
+ if ((!Number.isInteger(n) && n !== Infinity) || n <= 0) {
+ throw new TypeError('n is not positive number')
+ }
- }
+ let contacts = []
+
+ for (let nodes = [this.root], bitIndex = 0; nodes.length > 0 && contacts.length < n;) {
+ const node = nodes.pop()
+ if (node.contacts === null) {
+ const detNode = this._determineNode(node, id, bitIndex++)
+ nodes.push(node.left === detNode ? node.right : node.left)
+ nodes.push(detNode)
+ } else {
+ contacts = contacts.concat(node.contacts)
+ }
+ }
+
+ return contacts
+ .map(a => [this.distance(a.id, id), a])
+ .sort((a, b) => a[0] - b[0])
+ .slice(0, n)
+ .map(a => a[1])
+ }
+
+ /**
+ * Counts the total number of contacts in the tree.
+ *
+ * @return {Number} The number of contacts held in the tree
+ */
+ this.count = function () {
+ // return this.toArray().length
+ let count = 0
+ for (const nodes = [this.root]; nodes.length > 0;) {
+ const node = nodes.pop()
+ if (node.contacts === null) nodes.push(node.right, node.left)
+ else count += node.contacts.length
+ }
+ return count
+ }
+
+ /**
+ * Determines whether the id at the bitIndex is 0 or 1.
+ * Return left leaf if `id` at `bitIndex` is 0, right leaf otherwise
+ *
+ * @param {Object} node internal object that has 2 leafs: left and right
+ * @param {Uint8Array} id Id to compare localNodeId with.
+ * @param {Number} bitIndex Integer (Default: 0) The bit index to which bit
+ * to check in the id Uint8Array.
+ * @return {Object} left leaf if id at bitIndex is 0, right leaf otherwise.
+ */
+ this._determineNode = function (node, id, bitIndex) {
+ // *NOTE* remember that id is a Uint8Array and has granularity of
+ // bytes (8 bits), whereas the bitIndex is the bit index (not byte)
+
+ // id's that are too short are put in low bucket (1 byte = 8 bits)
+ // (bitIndex >> 3) finds how many bytes the bitIndex describes
+ // bitIndex % 8 checks if we have extra bits beyond byte multiples
+ // if number of bytes is <= no. of bytes described by bitIndex and there
+ // are extra bits to consider, this means id has less bits than what
+ // bitIndex describes, id therefore is too short, and will be put in low
+ // bucket
+ const bytesDescribedByBitIndex = bitIndex >> 3
+ const bitIndexWithinByte = bitIndex % 8
+ if ((id.length <= bytesDescribedByBitIndex) && (bitIndexWithinByte !== 0)) {
+ return node.left
+ }
+
+ const byteUnderConsideration = id[bytesDescribedByBitIndex]
+
+ // byteUnderConsideration is an integer from 0 to 255 represented by 8 bits
+ // where 255 is 11111111 and 0 is 00000000
+ // in order to find out whether the bit at bitIndexWithinByte is set
+ // we construct (1 << (7 - bitIndexWithinByte)) which will consist
+ // of all bits being 0, with only one bit set to 1
+ // for example, if bitIndexWithinByte is 3, we will construct 00010000 by
+ // (1 << (7 - 3)) -> (1 << 4) -> 16
+ if (byteUnderConsideration & (1 << (7 - bitIndexWithinByte))) {
+ return node.right
+ }
+
+ return node.left
+ }
+
+ /**
+ * Get a contact by its exact ID.
+ * If this is a leaf, loop through the bucket contents and return the correct
+ * contact if we have it or null if not. If this is an inner node, determine
+ * which branch of the tree to traverse and repeat.
+ *
+ * @param {Uint8Array} id The ID of the contact to fetch.
+ * @return {Object|Null} The contact if available, otherwise null
+ */
+ this.get = function (id) {
+ ensureInt8('id', id)
+
+ let bitIndex = 0
+
+ let node = this.root
+ while (node.contacts === null) {
+ node = this._determineNode(node, id, bitIndex++)
+ }
+
+ // index of uses contact id for matching
+ const index = this._indexOf(node, id)
+ return index >= 0 ? node.contacts[index] : null
+ }
+
+ /**
+ * Returns the index of the contact with provided
+ * id if it exists, returns -1 otherwise.
+ *
+ * @param {Object} node internal object that has 2 leafs: left and right
+ * @param {Uint8Array} id Contact node id.
+ * @return {Number} Integer Index of contact with provided id if it
+ * exists, -1 otherwise.
+ */
+ this._indexOf = function (node, id) {
+ for (let i = 0; i < node.contacts.length; ++i) {
+ if (arrayEquals(node.contacts[i].id, id)) return i
+ }
+
+ return -1
+ }
+
+ /**
+ * Removes contact with the provided id.
+ *
+ * @param {Uint8Array} id The ID of the contact to remove.
+ * @return {Object} The k-bucket itself.
+ */
+ this.remove = function (id) {
+ ensureInt8('the id as parameter 1', id)
+
+ let bitIndex = 0
+ let node = this.root
+
+ while (node.contacts === null) {
+ node = this._determineNode(node, id, bitIndex++)
+ }
+
+ const index = this._indexOf(node, id)
+ if (index >= 0) {
+ const contact = node.contacts.splice(index, 1)[0]
+ reactor.dispatchEvent('removed', contact)
+ }
+
+ return this
+ }
+
+ /**
+ * Splits the node, redistributes contacts to the new nodes, and marks the
+ * node that was split as an inner node of the binary tree of nodes by
+ * setting this.root.contacts = null
+ *
+ * @param {Object} node node for splitting
+ * @param {Number} bitIndex the bitIndex to which byte to check in the
+ * Uint8Array for navigating the binary tree
+ */
+ this._split = function (node, bitIndex) {
+ node.left = createNode()
+ node.right = createNode()
+
+ // redistribute existing contacts amongst the two newly created nodes
+ for (const contact of node.contacts) {
+ this._determineNode(node, contact.id, bitIndex).contacts.push(contact)
+ }
+
+ node.contacts = null // mark as inner tree node
+
+ // don't split the "far away" node
+ // we check where the local node would end up and mark the other one as
+ // "dontSplit" (i.e. "far away")
+ const detNode = this._determineNode(node, this.localNodeId, bitIndex)
+ const otherNode = node.left === detNode ? node.right : node.left
+ otherNode.dontSplit = true
+ }
+
+ /**
+ * Returns all the contacts contained in the tree as an array.
+ * If this is a leaf, return a copy of the bucket. `slice` is used so that we
+ * don't accidentally leak an internal reference out that might be
+ * accidentally misused. If this is not a leaf, return the union of the low
+ * and high branches (themselves also as arrays).
+ *
+ * @return {Array} All of the contacts in the tree, as an array
+ */
+ this.toArray = function () {
+ let result = []
+ for (const nodes = [this.root]; nodes.length > 0;) {
+ const node = nodes.pop()
+ if (node.contacts === null) nodes.push(node.right, node.left)
+ else result = result.concat(node.contacts)
+ }
+ return result
+ }
+
+ /**
+ * Updates the contact selected by the arbiter.
+ * If the selection is our old contact and the candidate is some new contact
+ * then the new contact is abandoned (not added).
+ * If the selection is our old contact and the candidate is our old contact
+ * then we are refreshing the contact and it is marked as most recently
+ * contacted (by being moved to the right/end of the bucket array).
+ * If the selection is our new contact, the old contact is removed and the new
+ * contact is marked as most recently contacted.
+ *
+ * @param {Object} node internal object that has 2 leafs: left and right
+ * @param {Number} index the index in the bucket where contact exists
+ * (index has already been computed in a previous
+ * calculation)
+ * @param {Object} contact The contact object to update.
+ */
+ this._update = function (node, index, contact) {
+ // sanity check
+ if (!arrayEquals(node.contacts[index].id, contact.id)) {
+ throw new Error('wrong index for _update')
+ }
+
+ const incumbent = node.contacts[index]
+
+ /***************Change made by Abhishek*************/
+ const selection = this.arbiter(incumbent, contact)
+ //const selection = localbitcoinplusplus.kademlia.arbiter(incumbent, contact);
+ // if the selection is our old contact and the candidate is some new
+ // contact, then there is nothing to do
+ if (selection === incumbent && incumbent !== contact) return
+
+ node.contacts.splice(index, 1) // remove old contact
+ node.contacts.push(selection) // add more recent contact version
+ /***************Change made by Abhishek*************/
+ reactor.dispatchEvent('updated', {
+ ...incumbent,
+ ...selection
+ })
+ //reactor.dispatchEvent('updated', incumbent.concat(selection))
+ }
+ }
+