Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules/
npm-debug.log
npm-debug.log
*.pem
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
0.1.2 / 2015-01-20
------------------
* Switched to tls-encrypted connections.

------------------
Pre Fork:

0.1.1 / 2014-01-22
------------------
* Add `stateChange` events when Peer state changes.
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# P2P Node
Low-level library to handle peer-to-peer traffic on Cryptcurrency networks. A raw `socket` object in Node emits `data` events whenever the stream is updated. This library sits on top of a raw socket connection, and instead of emitting `data` events every time the stream updates, it waits and emits `message` events whenever a complete message has arrived.
Low-level library to handle peer-to-peer traffic on Cryptcurrency networks. A raw `socket` object in Node emits `data` events whenever the stream is updated. This library sits on top of a raw tls connection, and instead of emitting `data` events every time the stream updates, it waits and emits `message` events whenever a complete message has arrived. The tls connection is then wrapped as a simple socket connection.

It uses the [Bitcoin protocol structure](https://en.bitcoin.it/wiki/Protocol_specification#Message_structure) to parse incoming messages; any stream that's encoded as follows can be parsed:

Expand Down Expand Up @@ -30,7 +30,7 @@ p.on('message', function(d) {
## Events

### `connect`
When the socket connects
When the socket connects/the connection is established

Data object passed to listeners:

Expand All @@ -41,14 +41,14 @@ Data object passed to listeners:
```

### `error`
If the socket errors out
If the socket/connection errors out

Data object passed to listeners:

```
{
peer: Peer,
error: Error object from Socket
error: Error object from Stream
}
```

Expand Down
46 changes: 35 additions & 11 deletions lib/Peer.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
var net = require('net');
var settings = require('./settings');
var onlinePackage = settings.useTLS? require('tls') : net;
var events = require('events');
var util = require('util');
var sha256 = require('crypto-hashing').sha256;

var fallback = Number(process.version.match(/^v\d+\.(\d+\.\d+)/)[1]) < 11.3

var Host = function Host(host, port) {
var _host = false, // Private variables
_port = false,
_version = false;

Object.defineProperties(this, {
'host': {
get: function() { return _host; },
Expand All @@ -22,12 +26,12 @@ var Host = function Host(host, port) {
enumerable: true
}
});

if (Array.isArray(host)) {
host = new Buffer(host);
}
_port = +port || this.defaultPort;

if (typeof host === 'undefined') {
_host = 'localhost';
_version = 4;
Expand Down Expand Up @@ -94,7 +98,7 @@ var Peer = exports.Peer = function Peer(host, port, magic) {
value: host
});
if (typeof magic !== 'undefined') this.magicBytes = magic;

var myState = 'new';
Object.defineProperty(this, 'state', {
enumerable: true,
Expand All @@ -115,23 +119,35 @@ util.inherits(Peer, events.EventEmitter);
Peer.prototype.MAX_RECEIVE_BUFFER = 1024*1024*10;
Peer.prototype.magicBytes = 0xD9B4BEF9;

Peer.prototype.connect = function connect(socket) {
Peer.prototype.connect = function connect(connection) {
this.state = 'connecting';
this.inbound = new Buffer(this.MAX_RECEIVE_BUFFER);
this.inboundCursor = 0;

if (typeof socket === 'undefined' || !(socket instanceof net.Socket)) {
socket = net.createConnection(this.host.port, this.host.host, this.handleConnect.bind(this));
function isActive(connection){
if(settings.useTLS){
var target_proto = new onlinePackage.createSecurePair().cleartext.__proto__;
return (fallback? connection.__proto__ === target_proto : connection instanceof onlinePackage.TLSSocket);
} else {
return connection instanceof net.Socket;
}
}

if (typeof connection === 'undefined' || !isActive(connection)) {
var options = settings.useTLS? settings.TLS_connection_options : {};
options.host = this.host.host;
options.port = this.host.port;
connection = onlinePackage.connect(options, this.handleConnect.bind(this));
} else {
this.state = 'connected'; // Binding to an already-connected socket; will not fire a 'connect' event, but will still fire a 'stateChange' event
}
Object.defineProperty(this, 'socket', {
enumerable: false,
configurable: false,
writable:false,
value: socket
value: connection
});
this.socket.on('error', this.handleError.bind(this));
//this.socket.on('error', this.handleError.bind(this));
this.socket.on('data', this.handleData.bind(this));
this.socket.on('end', this.handleEnd.bind(this));
this.socket.on('close', this.handleClose.bind(this));
Expand All @@ -140,17 +156,25 @@ Peer.prototype.connect = function connect(socket) {
};

Peer.prototype.disconnect = function disconnect() {
if(this.state != 'connected'){
this.on('connect', function(){this.disconnect();});
return;
}
this.state = 'disconnecting';
this.socket.end(); // Inform the other end we're going away
};

Peer.prototype.destroy = function destroy() {
if(this.state != 'connected'){
this.on('connect', function(){this.disconnect();});
return;
}
this.socket.destroy();
};

Peer.prototype.getUUID = function getUUID() {
return this.host.host+'~'+this.host.port;
}
};

Peer.prototype.handleConnect = function handleConnect() {
this.state = 'connected';
Expand Down Expand Up @@ -201,7 +225,7 @@ Peer.prototype.send = function send(command, data, callback) {
var checksum = this.messageChecksum(data);
checksum.copy(out, 20); // checksum
data.copy(out, 24);

this.socket.write(out, null, callback);
};

Expand Down
10 changes: 10 additions & 0 deletions lib/settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
var fs = require('fs')

module.exports={
useTLS:false,
TLS_connection_options: {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
rejectUnauthorized: false
}
}
17 changes: 10 additions & 7 deletions test/peer.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
var Peer = require('../lib/Peer').Peer;
var assert = require("assert");
var net = require('net');
var settings = require('../test/test-settings');
var onlinePackage = require(settings.useTLS? 'tls' : 'net');

var suboptions = settings.TLS_server_options;

describe('P2P Peer', function() {
it('should properly connect to indicated host', function(done) {
it('should properly connect to and disconnect from indicated host', function(done) {
var localPeer = false;
var server = net.createServer(function(socket) {
var server = onlinePackage.createServer(suboptions, function(clTxtStream) {
server.close();
localPeer.destroy();
localPeer.disconnect();
done();
});
server.listen(function() {
Expand All @@ -23,9 +26,9 @@ describe('P2P Peer', function() {

beforeEach(function(done) {
serverPeer = false;
server = net.createServer(function(socket) {
serverPeer = new Peer(socket.remoteAddress, socket.remotePort, magic);
serverPeer.connect(socket);
server = onlinePackage.createServer(suboptions, function(clTxtStream) {
serverPeer = new Peer(clTxtStream.remoteAddress, clTxtStream.remotePort, magic);
serverPeer.connect(clTxtStream);
});
localPeer = false;
server.listen(function() {
Expand Down
11 changes: 11 additions & 0 deletions test/test-settings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
var fs = require('fs')

module.exports={
useTLS:false,
TLS_server_options: {
key: fs.readFileSync('key.pem'),
cert: fs.readFileSync('cert.pem'),
rejectUnauthorized:false,
requestCert:true
}
}