Skip to content
This repository has been archived by the owner on Sep 19, 2019. It is now read-only.

Commit

Permalink
refs #4 Add Chord implementation
Browse files Browse the repository at this point in the history
* First working implementation
* Currently uses only successor list (So complexity is O(n))
  • Loading branch information
Max Jonas Werner committed Sep 12, 2014
1 parent 04380e2 commit 533edde
Show file tree
Hide file tree
Showing 7 changed files with 530 additions and 112 deletions.
197 changes: 116 additions & 81 deletions js/chord/chord.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
var ChordNode = require('./node.js');
var ChordNode = require('./node');
var Peer = require('../peer');
var Sha1 = require('../third_party/sha1');
var BigInteger = require('../third_party/BigInteger');
var async = require("async");
var Range = require("./range");

/** @fileOverview Chord DHT implementation */

Expand All @@ -9,23 +14,28 @@ var ChordNode = require('./node.js');
* @param connectionManager {ConnectionManager} The connection manager instance
* to be used for requesting data channel connections.
*/
var Chord = function(connectionManager, hash, keyLength) {
var Chord = function(id, connectionManager) {
if (!(this instanceof Chord)) {
return new Chord(connectionManager, hash, keyLength);
return new Chord(id, connectionManager);
}

Helper.defineProperties(this);

// TODO(max): externalize this so Node can reuse it upon (de-)serializing
hash.update(Math.random().toString());
var digest = hash.digest();
if (typeof id === undefined) {
var hash = new Sha1();
hash.update(Math.random().toString());
id = Sha1.bigInteger(hash.digest());
}

this._localNode = new ChordNode(new Peer(id, null, null), this, true);
this._localNode._successor = id;
this._localNode._predecessor = id;
this._remotes = {};
this._connectionManager = connectionManager;
this._id = hash.bigInteger(digest);
this._fingerTable = {};
this._m = keyLength;
this._m = 160;
this._joined = false; // are we joined to a Chord ring, yet?
this._localNode = new ChordNode(new Peer(this._id, null, {
send: this._onmessage
}), this);

var memoizer = Helper.memoize(Helper.fingerTableIntervalStart.bind(this));
for (var i = 1; i <= this._m; i++) {
Expand Down Expand Up @@ -56,21 +66,21 @@ var Helper = {
},

fingerTableIntervalStart: function(i) {
return this._id.add(BigInt(2).pow(i - 1)).mod(BigInt(2).pow(this._m));
},

/**
* [start, end)
*/
inInterval: function(val, start, end) {
return val.greaterOrEquals(start) && val.lesser(end);
return this._id.add(BigInteger(2).pow(i - 1)).mod(BigInteger(2).pow(this._m));
},

/**
* (start, end)
*/
inOpenInterval: function(val, start, end) {
return val.greater(start) && val.lesser(end);
defineProperties: function(object) {
Object.defineProperty(object, "connectionManager", {
set: function(cm) {
object._connectionManager = cm;
}
});
Object.defineProperty(object, "id", {
get: function() {
return object._localNode.id;
},
set: function(id) {}
});
}
};

Expand All @@ -79,98 +89,108 @@ var Helper = {
**/

Chord.prototype._init_finger_table = function(remote, successCallback) {
remote.find_successor(this._fingerTable[1].start, function(successor) {
this._fingerTable[1].node = successor;
this._predecessor = successor.predecessor;
this._successor.predecessor = this._localNode;
var self = this;
remote.find_successor(this._fingerTable[1].start(), function(successor) {
self._fingerTable[1].node = successor;
self._localNode._predecessor = successor.predecessor;
self._localNode._successor.predecessor = this._localNode;
successCallback();
});
};

Chord.prototype._onmessage = function(msg) {
try {
this._forward(JSON.parse(msg.data));
} catch (e) {
console.log('Unable to parse incoming message ' + JSON.stringify(msg.data) + ': ' + e);
}
};

Chord.prototype._forward = function(msg) {
if (!msg.to) {
throw Error('Unable to route message because no recipient can be determined');
}
if (this._id === msg.to) {
this.deliver(msg);
return;
}

// TODO: fill in magic here (eval via field, route forward via DHT)
};

Chord.prototype._update_others = function() {
// TODO(max) implement
};

Chord.prototype._closest_preceding_finger = function(id) {
var i;
for (i = this._m; i >= 1; i--) {
if (Helper.inOpenInterval(this._fingerTable[i].node._id, this._id, id)) {
if (Range.inOpenInterval(this._fingerTable[i].node._id, this._id, id)) {
return this._fingerTable[i].node;
}
}
return this._localNode;
};

Chord.prototype.log = function(msg) {
console.log("[" + this._localNode._peer.id + "] ", msg, arguments.length > 1 ? Array.prototype.slice.call(arguments, 1) : "");
};

/**
* Public API
**/

/**
*
*
* @param dc DataChannel connection to remote peer
*/
Chord.prototype.add_peer = function(dc) {};
Chord.prototype.create = function(callback) {
callback(this._id);
};

/**
* join the DHT by using the 'bootstrap' node
*
* @param bootstrap {ChordNode} instance of the bootstrap host
* @param bootstrap_id {BigInteger} ID of the bootstrap host
* @param successCallback {Chord~joinCallback} called after the join operation has been
* carried out successfully.
*/
Chord.prototype._join = function(bootstrap, successCallback) {
var i;
// TODO: implement
bootstrap.get_node_id(function() {
this._init_finger_table(bootstrapNode, function() {
this._update_others();
// move keys in (predecessor,n] from successor
successCallback();
Chord.prototype.join = function(bootstrap_id, callback) {
var i, self = this;
self._connectionManager.connect(bootstrap_id, function(err, peer) {
var bootstrapNode = new ChordNode(peer, self);
bootstrapNode.find_successor(self._localNode._peer.id, function(err, successor) {
self._connectionManager.connect(successor, function(err, successorPeer) {
self._localNode._successor = new ChordNode(successorPeer, self);
self._localNode._successor.find_predecessor(self._localNode._peer.id, function(err, predecessor) {
self._localNode._successor.update_predecessor(self._localNode.id(), function(err, res) {
self._connectionManager.connect(predecessor, function(err, predecessorPeer) {
self._localNode._predecessor = new ChordNode(predecessorPeer, self);
self._localNode._predecessor.update_successor(self._localNode.id(), function() {
callback();
});
});
});
});
});
});
});
};

/**
* Public API
**/
Chord.prototype.find_successor = function(id, callback) {
var self = this;
if (self._localNode.responsible(id)) {
callback(null, self._localNode.id());
} else if (Range.inLeftClosedInterval(id, self._localNode.id(), self._localNode.successor_id())) {
self._localNode.successor(function(err, successorNode) {
callback(null, successorNode.id());
});
} else {
self._localNode.successor(function(err, successorNode) {
successorNode.find_successor(id, callback);
});
}
};

Chord.prototype.find_predecessor = function(id, callback) {
var self = this;
if (self._localNode.id().equals(self._localNode.successor_id()) || Range.inLeftClosedInterval(id, self._localNode.id(), self._localNode.successor_id())) {
callback(null, self._localNode.id());
} else if (self._localNode.responsible(id)) {
callback(null, self._localNode.predecessor_id());
} else {
self._localNode.successor(function(err, successorNode) {
successorNode.find_predecessor(id, callback);
});
}
};

/**
*
*
* @param dc DataChannel connection to remote peer
*/
Chord.prototype.addPeer = function(peer) {
var node = new ChordNode(peer, this);
peer.dataChannel.onmessage = this.onmessage.bind(this);
Chord.prototype.add_peer = function(peer, callback) {
// TODO: what if we already have a node with this ID?
this._remotes[peer.id] = new ChordNode(peer, this);
// TODO: implement removing peer/updating finger table
peer.peerConnection.onclosedconnection = this.removePeer.bind(this, peer);
if (!this._joined) {
this._join(node, function() {
this._joined = true;
});
return;
}
//peer.peerConnection.onclosedconnection = this.removePeer.bind(this, peer);
// TODO: update finger table
};

Expand All @@ -180,15 +200,30 @@ Chord.prototype.addPeer = function(peer) {
* @param key
* @param value
*/
Chord.prototype.put = function(key, value) {
// TODO: implement
Chord.prototype.put = function(key, value, callback) {
if (this._localNode.responsible(key)) {
this._localNode.store(key, value);
callback(null);
} else {
this._localNode.successor(function(err, successorNode) {
successorNode.put(key, value, callback);
});
}
};

Chord.prototype.remove = function(key) {
// TODO: implement
Chord.prototype.get = function(key, callback) {
var val;
if (this._localNode.responsible(key)) {
val = this._localNode.get_from_store(key);
callback(null, val);
} else {
this._localNode.successor(function(err, successorNode) {
successorNode.get(key, callback);
});
}
};

Chord.prototype.get = function(key) {
Chord.prototype.remove = function(key) {
// TODO: implement
};

Expand Down
Loading

0 comments on commit 533edde

Please sign in to comment.