Merge pull request #696 from yemel/feature/transport
P2P Peer Connections
This commit is contained in:
commit
005cf074fd
92
docs/Peer.md
Normal file
92
docs/Peer.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# Peer
|
||||||
|
|
||||||
|
Represents a node from the p2p bitcoin network. The Peer class supports connecting directly to other nodes or through a socks5 proxy like Tor.
|
||||||
|
|
||||||
|
The code to create a new peer looks like this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var Peer = bitcore.transport.Peer;
|
||||||
|
|
||||||
|
// default port
|
||||||
|
var livenetPeer = new Peer('5.9.85.34');
|
||||||
|
var testnetPeer = new Peer('5.9.85.34', bitcore.testnet);
|
||||||
|
|
||||||
|
// custom port
|
||||||
|
var livenetPeer = new Peer('5.9.85.34', 8334);
|
||||||
|
var testnetPeer = new Peer('5.9.85.34', 18334, bitcore.testnet);
|
||||||
|
|
||||||
|
// use sock5 proxy (Tor)
|
||||||
|
var peer = new Peer('5.9.85.34').setProxy('localhost', 9050);
|
||||||
|
```
|
||||||
|
|
||||||
|
A peer instance is always in one of the following states:
|
||||||
|
|
||||||
|
* `disconnected`: No connection with the remote node.
|
||||||
|
* `connecting`: While establishing the connection.
|
||||||
|
* `connected`: Exchanging version packages.
|
||||||
|
* `ready`: Connection ready for sending and receiving messages.
|
||||||
|
|
||||||
|
You can subscribe to the change of those states as follows:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var Peer = bitcore.transport.Peer;
|
||||||
|
|
||||||
|
var peer = new Peer('5.9.85.34');
|
||||||
|
|
||||||
|
peer.on('ready', function() {
|
||||||
|
// peer info
|
||||||
|
console.log(peer.version, peer.subversion, peer.bestHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on('disconnect', function() {
|
||||||
|
console.log('connection closed');
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.connect();
|
||||||
|
```
|
||||||
|
|
||||||
|
Once connected, a peer instance can send and receive messages. Every time a message arrives it's emitted as a new event. Let's see an example of this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var peer = new bitcore.transport.Peer('5.9.85.34');
|
||||||
|
|
||||||
|
// handle events
|
||||||
|
peer.on('inv', function(message) {
|
||||||
|
// message.inventory[]
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on('tx', function(message) {
|
||||||
|
// message.transaction
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on('addr', function(message) {
|
||||||
|
// message.addresses[]
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.connect();
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to send messages the Peer class offers the `sendMessage(message)` method, which receives an instance of a message. All supported messages can be found on the `bitcore.transport.Messages` module. For more information about messages refer to the [protocol specification](https://en.bitcoin.it/wiki/Protocol_specification).
|
||||||
|
|
||||||
|
An example for requesting other connected nodes to a peers looks like this:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
var bitcore = require('bitcore');
|
||||||
|
var peer = new bitcore.transport.Peer('5.9.85.34');
|
||||||
|
|
||||||
|
peer.on('ready', function() {
|
||||||
|
var message = new bitcore.transport.Messages.GetAddresses();
|
||||||
|
peer.sendMessage(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.on('addr', function(message) {
|
||||||
|
message.addresses.forEach(function(address) {
|
||||||
|
// do something
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
peer.connect();
|
||||||
|
```
|
||||||
5
index.js
5
index.js
@ -24,6 +24,11 @@ bitcore.util.bitcoin = require('./lib/util/bitcoin');
|
|||||||
bitcore.util.buffer = require('./lib/util/buffer');
|
bitcore.util.buffer = require('./lib/util/buffer');
|
||||||
bitcore.util.js = require('./lib/util/js');
|
bitcore.util.js = require('./lib/util/js');
|
||||||
|
|
||||||
|
// transport
|
||||||
|
bitcore.transport = {};
|
||||||
|
bitcore.transport.Peer = require('./lib/transport/peer');
|
||||||
|
bitcore.transport.Messages = require('./lib/transport/messages');
|
||||||
|
|
||||||
// errors thrown by the library
|
// errors thrown by the library
|
||||||
bitcore.errors = require('./lib/errors');
|
bitcore.errors = require('./lib/errors');
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
|
|
||||||
|
var BufferUtil = require('./util/buffer');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A network is merely a map containing values that correspond to version
|
* A network is merely a map containing values that correspond to version
|
||||||
* numbers for each bitcoin network. Currently only supporting "livenet"
|
* numbers for each bitcoin network. Currently only supporting "livenet"
|
||||||
@ -25,7 +27,17 @@ _.extend(livenet, {
|
|||||||
privatekey: 0x80,
|
privatekey: 0x80,
|
||||||
scripthash: 0x05,
|
scripthash: 0x05,
|
||||||
xpubkey: 0x0488b21e,
|
xpubkey: 0x0488b21e,
|
||||||
xprivkey: 0x0488ade4
|
xprivkey: 0x0488ade4,
|
||||||
|
networkMagic: BufferUtil.integerAsBuffer(0xf9beb4d9),
|
||||||
|
port: 8333,
|
||||||
|
dnsSeeds: [
|
||||||
|
'seed.bitcoin.sipa.be',
|
||||||
|
'dnsseed.bluematt.me',
|
||||||
|
'dnsseed.bitcoin.dashjr.org',
|
||||||
|
'seed.bitcoinstats.com',
|
||||||
|
'seed.bitnodes.io',
|
||||||
|
'bitseed.xf2.org'
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,16 +52,26 @@ _.extend(testnet, {
|
|||||||
privatekey: 0xef,
|
privatekey: 0xef,
|
||||||
scripthash: 0xc4,
|
scripthash: 0xc4,
|
||||||
xpubkey: 0x043587cf,
|
xpubkey: 0x043587cf,
|
||||||
xprivkey: 0x04358394
|
xprivkey: 0x04358394,
|
||||||
|
networkMagic: BufferUtil.integerAsBuffer(0x0b110907),
|
||||||
|
port: 18333,
|
||||||
|
dnsSeeds: [
|
||||||
|
'testnet-seed.bitcoin.petertodd.org',
|
||||||
|
'testnet-seed.bluematt.me'
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
var networkMaps = {};
|
var networkMaps = {};
|
||||||
|
|
||||||
_.each(_.values(livenet), function(value) {
|
_.each(_.values(livenet), function(value) {
|
||||||
networkMaps[value] = livenet;
|
if (!_.isObject(value)) {
|
||||||
|
networkMaps[value] = livenet;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
_.each(_.values(testnet), function(value) {
|
_.each(_.values(testnet), function(value) {
|
||||||
networkMaps[value] = testnet;
|
if (!_.isObject(value)) {
|
||||||
|
networkMaps[value] = testnet;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
554
lib/transport/messages.js
Normal file
554
lib/transport/messages.js
Normal file
@ -0,0 +1,554 @@
|
|||||||
|
'use strict';
|
||||||
|
/* jshint curly: false */
|
||||||
|
|
||||||
|
var Buffers = require('buffers');
|
||||||
|
var Put = require('bufferput');
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
var BlockHeaderModel = require('../blockheader');
|
||||||
|
var BlockModel = require('../block');
|
||||||
|
var BufferReader = require('../encoding/bufferreader');
|
||||||
|
var BufferUtil = require('../util/buffer');
|
||||||
|
var Hash = require('../crypto/hash');
|
||||||
|
var Random = require('../crypto/random');
|
||||||
|
var TransactionModel = require('../transaction');
|
||||||
|
|
||||||
|
var CONNECTION_NONCE = Random.getPseudoRandomBuffer(8);
|
||||||
|
var PROTOCOL_VERSION = 70000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static helper for consuming a data buffer until the next message.
|
||||||
|
*
|
||||||
|
* @param{Network} network - the network object
|
||||||
|
* @param{Buffer} dataBuffer - the buffer to read from
|
||||||
|
* @returns{Message|undefined} A message or undefined if there is nothing to read.
|
||||||
|
*/
|
||||||
|
var parseMessage = function(network, dataBuffer) {
|
||||||
|
if (dataBuffer.length < 20) return;
|
||||||
|
|
||||||
|
// Search the next magic number
|
||||||
|
if (!discardUntilNextMessage(network, dataBuffer)) return;
|
||||||
|
|
||||||
|
var PAYLOAD_START = 16;
|
||||||
|
var payloadLen = (dataBuffer.get(PAYLOAD_START)) +
|
||||||
|
(dataBuffer.get(PAYLOAD_START + 1) << 8) +
|
||||||
|
(dataBuffer.get(PAYLOAD_START + 2) << 16) +
|
||||||
|
(dataBuffer.get(PAYLOAD_START + 3) << 24);
|
||||||
|
|
||||||
|
var messageLength = 24 + payloadLen;
|
||||||
|
if (dataBuffer.length < messageLength) return;
|
||||||
|
|
||||||
|
var command = dataBuffer.slice(4, 16).toString('ascii').replace(/\0+$/, '');
|
||||||
|
var payload = dataBuffer.slice(24, messageLength);
|
||||||
|
var checksum = dataBuffer.slice(20, 24);
|
||||||
|
|
||||||
|
var checksumConfirm = Hash.sha256sha256(payload).slice(0, 4);
|
||||||
|
if (!BufferUtil.equals(checksumConfirm, checksum)) {
|
||||||
|
dataBuffer.skip(messageLength);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(command, 'FULL MESSAGE', dataBuffer.slice(0, messageLength).toString('hex'));
|
||||||
|
console.log(command, 'PAYLOAD MESSAGE', payload.toString('hex'));
|
||||||
|
|
||||||
|
dataBuffer.skip(messageLength);
|
||||||
|
return Message.buildMessage(command, payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.parseMessage = parseMessage;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function that discards data until founds the next message.
|
||||||
|
*/
|
||||||
|
function discardUntilNextMessage(network, dataBuffer) {
|
||||||
|
var magicNumber = network.networkMagic;
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
for (;;) {
|
||||||
|
// check if it's the beginning of a new message
|
||||||
|
var packageNumber = dataBuffer.slice(0, 4);
|
||||||
|
if (BufferUtil.equals(packageNumber, magicNumber)) {
|
||||||
|
dataBuffer.skip(i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// did we reach the end of the buffer?
|
||||||
|
if (i > (dataBuffer.length - 4)) {
|
||||||
|
dataBuffer.skip(i);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
i++; // continue scanning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract Message that knows how to parse and serialize itself.
|
||||||
|
* Concret subclases should implement {fromBuffer} and {getPayload} methods.
|
||||||
|
*/
|
||||||
|
function Message() {}
|
||||||
|
|
||||||
|
Message.COMMANDS = {};
|
||||||
|
|
||||||
|
Message.buildMessage = function(command, payload) {
|
||||||
|
try {
|
||||||
|
var CommandClass = Message.COMMANDS[command];
|
||||||
|
return new CommandClass().fromBuffer(payload);
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Error while parsing message', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse instance state from buffer.
|
||||||
|
*
|
||||||
|
* @param{Buffer} payload - the buffer to read from
|
||||||
|
* @returns{Message} The same message instance
|
||||||
|
*/
|
||||||
|
Message.prototype.fromBuffer = function(payload) {
|
||||||
|
/* jshint unused: false */
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the payload into a buffer.
|
||||||
|
*
|
||||||
|
* @returns{Buffer} the serialized payload
|
||||||
|
*/
|
||||||
|
Message.prototype.getPayload = function() {
|
||||||
|
return BufferUtil.EMPTY_BUFFER;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize the message into a buffer.
|
||||||
|
*
|
||||||
|
* @returns{Buffer} the serialized message
|
||||||
|
*/
|
||||||
|
Message.prototype.serialize = function(network) {
|
||||||
|
var magic = network.networkMagic;
|
||||||
|
var commandBuf = new Buffer(this.command, 'ascii');
|
||||||
|
if (commandBuf.length > 12) throw 'Command name too long';
|
||||||
|
|
||||||
|
var payload = this.getPayload();
|
||||||
|
var checksum = Hash.sha256sha256(payload).slice(0, 4);
|
||||||
|
|
||||||
|
// -- HEADER --
|
||||||
|
var message = new Put();
|
||||||
|
message.put(magic);
|
||||||
|
message.put(commandBuf);
|
||||||
|
message.pad(12 - commandBuf.length); // zero-padded
|
||||||
|
message.word32le(payload.length);
|
||||||
|
message.put(checksum);
|
||||||
|
|
||||||
|
// -- BODY --
|
||||||
|
message.put(payload);
|
||||||
|
|
||||||
|
return message.buffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version Message
|
||||||
|
*
|
||||||
|
* @param{string} subversion - version of the client
|
||||||
|
* @param{Buffer} nonce - a random 8 bytes buffer
|
||||||
|
*/
|
||||||
|
function Version(subversion, nonce) {
|
||||||
|
this.command = 'version';
|
||||||
|
this.version = PROTOCOL_VERSION;
|
||||||
|
this.subversion = subversion || '/BitcoinX:0.1/';
|
||||||
|
this.nonce = nonce || CONNECTION_NONCE;
|
||||||
|
}
|
||||||
|
util.inherits(Version, Message);
|
||||||
|
|
||||||
|
Version.prototype.fromBuffer = function(payload) {
|
||||||
|
var parser = new BufferReader(payload);
|
||||||
|
this.version = parser.readUInt32LE();
|
||||||
|
this.services = parser.readUInt64LEBN();
|
||||||
|
this.timestamp = parser.readUInt64LEBN();
|
||||||
|
this.addr_me = parser.read(26);
|
||||||
|
this.addr_you = parser.read(26);
|
||||||
|
this.nonce = parser.read(8);
|
||||||
|
this.subversion = parser.readVarintBuf().toString();
|
||||||
|
this.start_height = parser.readUInt32LE();
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Version.prototype.getPayload = function() {
|
||||||
|
var put = new Put();
|
||||||
|
put.word32le(this.version); // version
|
||||||
|
put.word64le(1); // services
|
||||||
|
put.word64le(Math.round(new Date().getTime() / 1000)); // timestamp
|
||||||
|
put.pad(26); // addr_me
|
||||||
|
put.pad(26); // addr_you
|
||||||
|
put.put(this.nonce);
|
||||||
|
put.varint(this.subversion.length);
|
||||||
|
put.put(new Buffer(this.subversion, 'ascii'));
|
||||||
|
put.word32le(0);
|
||||||
|
|
||||||
|
return put.buffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Version = Message.COMMANDS.version = Version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inv Message
|
||||||
|
*
|
||||||
|
* @param{Array} inventory - reported elements
|
||||||
|
*/
|
||||||
|
function Inventory(inventory) {
|
||||||
|
this.command = 'inv';
|
||||||
|
this.inventory = inventory || [];
|
||||||
|
}
|
||||||
|
util.inherits(Inventory, Message);
|
||||||
|
|
||||||
|
Inventory.prototype.fromBuffer = function(payload) {
|
||||||
|
var parser = new BufferReader(payload);
|
||||||
|
var count = parser.readVarintNum();
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
this.inventory.push({
|
||||||
|
type: parser.readUInt32LE(),
|
||||||
|
hash: parser.read(32)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Inventory.prototype.getPayload = function() {
|
||||||
|
var put = new Put();
|
||||||
|
|
||||||
|
put.varint(this.inventory.length);
|
||||||
|
this.inventory.forEach(function(value) {
|
||||||
|
put.word32le(value.type);
|
||||||
|
put.put(value.hash);
|
||||||
|
});
|
||||||
|
|
||||||
|
return put.buffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Inventory = Message.COMMANDS.inv = Inventory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getdata Message
|
||||||
|
*
|
||||||
|
* @param{Array} inventory - requested elements
|
||||||
|
*/
|
||||||
|
function GetData(inventory) {
|
||||||
|
this.command = 'getdata';
|
||||||
|
this.inventory = inventory || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(GetData, Inventory);
|
||||||
|
module.exports.GetData = GetData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping Message
|
||||||
|
*
|
||||||
|
* @param{Buffer} nonce - a random 8 bytes buffer
|
||||||
|
*/
|
||||||
|
function Ping(nonce) {
|
||||||
|
this.command = 'ping';
|
||||||
|
this.nonce = nonce || CONNECTION_NONCE;
|
||||||
|
}
|
||||||
|
util.inherits(Ping, Message);
|
||||||
|
|
||||||
|
Ping.prototype.fromBuffer = function(payload) {
|
||||||
|
this.nonce = new BufferReader(payload).read(8);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Ping.prototype.getPayload = function() {
|
||||||
|
return this.nonce;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Ping = Message.COMMANDS.ping = Ping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pong Message
|
||||||
|
*
|
||||||
|
* @param{Buffer} nonce - a random 8 bytes buffer
|
||||||
|
*/
|
||||||
|
function Pong(nonce) {
|
||||||
|
this.command = 'pong';
|
||||||
|
this.nonce = nonce || CONNECTION_NONCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(Pong, Ping);
|
||||||
|
module.exports.Pong = Message.COMMANDS.pong = Pong;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Addr Message
|
||||||
|
*
|
||||||
|
* @param{Array} addresses - array of know addresses
|
||||||
|
*/
|
||||||
|
function Addresses(addresses) {
|
||||||
|
this.command = 'addr';
|
||||||
|
this.addresses = addresses || [];
|
||||||
|
}
|
||||||
|
util.inherits(Addresses, Message);
|
||||||
|
|
||||||
|
Addresses.prototype.fromBuffer = function(payload) {
|
||||||
|
var parser = new BufferReader(payload);
|
||||||
|
var addrCount = Math.min(parser.readVarintNum(), 1000);
|
||||||
|
|
||||||
|
this.addresses = [];
|
||||||
|
for (var i = 0; i < addrCount; i++) {
|
||||||
|
// TODO: Time actually depends on the version of the other peer (>=31402)
|
||||||
|
this.addresses.push({
|
||||||
|
time: parser.readUInt32LE(),
|
||||||
|
services: parser.readUInt64LEBN(),
|
||||||
|
ip: parser.read(16),
|
||||||
|
port: parser.readUInt16BE()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Addresses.prototype.getPayload = function() {
|
||||||
|
var put = new Put();
|
||||||
|
put.varint(this.addresses.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < this.addresses.length; i++) {
|
||||||
|
put.word32le(this.addresses[i].time);
|
||||||
|
put.word64le(this.addresses[i].services);
|
||||||
|
put.put(this.addresses[i].ip);
|
||||||
|
put.word16be(this.addresses[i].port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return put.buffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Addresses = Message.COMMANDS.addr = Addresses;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GetAddr Message
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function GetAddresses() {
|
||||||
|
this.command = 'getaddr';
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(GetAddresses, Message);
|
||||||
|
module.exports.GetAddresses = Message.COMMANDS.getaddr = GetAddresses;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verack Message
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function VerAck() {
|
||||||
|
this.command = 'verack';
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(VerAck, Message);
|
||||||
|
module.exports.VerAck = Message.COMMANDS.verack = VerAck;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reject Message
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function Reject() {
|
||||||
|
this.command = 'reject';
|
||||||
|
}
|
||||||
|
util.inherits(Reject, Message);
|
||||||
|
|
||||||
|
// TODO: Parse REJECT message
|
||||||
|
|
||||||
|
module.exports.Reject = Message.COMMANDS.reject = Reject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alert Message
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function Alert(payload, signature) {
|
||||||
|
this.command = 'alert';
|
||||||
|
this.payload = payload || new Buffer(32);
|
||||||
|
this.signature = signature || new Buffer(32);
|
||||||
|
}
|
||||||
|
util.inherits(Alert, Message);
|
||||||
|
|
||||||
|
Alert.prototype.fromBuffer = function(payload) {
|
||||||
|
var parser = new BufferReader(payload);
|
||||||
|
this.payload = parser.readVarintBuf(); // TODO: Use current format
|
||||||
|
this.signature = parser.readVarintBuf();
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Alert.prototype.getPayload = function() {
|
||||||
|
var put = new Put();
|
||||||
|
put.varint(this.payload.length);
|
||||||
|
put.put(this.payload);
|
||||||
|
|
||||||
|
put.varint(this.signature.length);
|
||||||
|
put.put(this.signature);
|
||||||
|
|
||||||
|
return put.buffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Alert = Message.COMMANDS.alert = Alert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Headers Message
|
||||||
|
*
|
||||||
|
* @param{Array} blockheaders - array of block headers
|
||||||
|
*/
|
||||||
|
function Headers(blockheaders) {
|
||||||
|
this.command = 'headers';
|
||||||
|
this.headers = blockheaders || [];
|
||||||
|
}
|
||||||
|
util.inherits(Headers, Message);
|
||||||
|
|
||||||
|
Headers.prototype.fromBuffer = function(payload) {
|
||||||
|
var parser = new BufferReader(payload);
|
||||||
|
var count = parser.readVarintNum();
|
||||||
|
|
||||||
|
this.headers = [];
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var header = BlockHeaderModel._fromBufferReader(parser);
|
||||||
|
this.headers.push(header);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Headers.prototype.getPayload = function() {
|
||||||
|
var put = new Put();
|
||||||
|
put.varint(this.headers.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < this.headers.length; i++) {
|
||||||
|
var buffer = this.headers[i].toBuffer();
|
||||||
|
put.put(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return put.buffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Headers = Message.COMMANDS.headers = Headers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Block Message
|
||||||
|
*
|
||||||
|
* @param{Block} block
|
||||||
|
*/
|
||||||
|
function Block(block) {
|
||||||
|
this.command = 'block';
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
util.inherits(Block, Message);
|
||||||
|
|
||||||
|
Block.prototype.fromBuffer = function(payload) {
|
||||||
|
this.block = BlockModel(payload);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Block.prototype.getPayload = function() {
|
||||||
|
return this.block.toBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Block = Message.COMMANDS.block = Block;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tx Message
|
||||||
|
*
|
||||||
|
* @param{Transaction} transaction
|
||||||
|
*/
|
||||||
|
function Transaction(transaction) {
|
||||||
|
this.command = 'tx';
|
||||||
|
this.transaction = transaction;
|
||||||
|
}
|
||||||
|
util.inherits(Transaction, Message);
|
||||||
|
|
||||||
|
Transaction.prototype.fromBuffer = function(payload) {
|
||||||
|
this.transaction = TransactionModel(payload);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
Transaction.prototype.getPayload = function() {
|
||||||
|
return this.transaction.toBuffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.Transaction = Message.COMMANDS.tx = Transaction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getblocks Message
|
||||||
|
*
|
||||||
|
* @param{Array} starts - array of buffers with the starting block hashes
|
||||||
|
* @param{Buffer} [stop] - hash of the last block
|
||||||
|
*/
|
||||||
|
function GetBlocks(starts, stop) {
|
||||||
|
this.command = 'getblocks';
|
||||||
|
this.version = PROTOCOL_VERSION;
|
||||||
|
this.starts = starts || [];
|
||||||
|
this.stop = stop || BufferUtil.NULL_HASH;
|
||||||
|
}
|
||||||
|
util.inherits(GetBlocks, Message);
|
||||||
|
|
||||||
|
GetBlocks.prototype.fromBuffer = function(payload) {
|
||||||
|
var parser = new BufferReader(payload);
|
||||||
|
this.version = parser.readUInt32LE();
|
||||||
|
|
||||||
|
var startCount = Math.min(parser.readVarintNum(), 500);
|
||||||
|
this.starts = [];
|
||||||
|
for (var i = 0; i < startCount; i++) {
|
||||||
|
this.starts.push(parser.read(32));
|
||||||
|
}
|
||||||
|
this.stop = parser.read(32);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
GetBlocks.prototype.getPayload = function() {
|
||||||
|
var put = new Put();
|
||||||
|
put.word32le(this.version);
|
||||||
|
put.varint(this.starts.length);
|
||||||
|
|
||||||
|
for (var i = 0; i < this.starts.length; i++) {
|
||||||
|
if (this.starts[i].length !== 32) {
|
||||||
|
throw new Error('Invalid hash length');
|
||||||
|
}
|
||||||
|
put.put(this.starts[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.stop.length !== 32) {
|
||||||
|
throw new Error('Invalid hash length');
|
||||||
|
}
|
||||||
|
put.put(this.stop);
|
||||||
|
|
||||||
|
return put.buffer();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.GetBlocks = Message.COMMANDS.getblocks = GetBlocks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Getheaders Message
|
||||||
|
*
|
||||||
|
* @param{Array} starts - array of buffers with the starting block hashes
|
||||||
|
* @param{Buffer} [stop] - hash of the last block
|
||||||
|
*/
|
||||||
|
function GetHeaders(starts, stop) {
|
||||||
|
this.command = 'getheaders';
|
||||||
|
this.version = PROTOCOL_VERSION;
|
||||||
|
this.starts = starts || [];
|
||||||
|
this.stop = stop || BufferUtil.NULL_HASH;
|
||||||
|
}
|
||||||
|
|
||||||
|
util.inherits(GetHeaders, GetBlocks);
|
||||||
|
module.exports.GetHeaders = Message.COMMANDS.getheaders = GetHeaders;
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Remove this PATCH (yemel)
|
||||||
|
Buffers.prototype.skip = function (i) {
|
||||||
|
if (i === 0) return;
|
||||||
|
|
||||||
|
if (i === this.length) {
|
||||||
|
this.buffers = [];
|
||||||
|
this.length = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = this.pos(i);
|
||||||
|
this.buffers = this.buffers.slice(pos.buf);
|
||||||
|
this.buffers[0] = new Buffer(this.buffers[0].slice(pos.offset));
|
||||||
|
this.length -= i;
|
||||||
|
};
|
||||||
192
lib/transport/peer.js
Normal file
192
lib/transport/peer.js
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Buffers = require('buffers');
|
||||||
|
var EventEmitter = require('events').EventEmitter;
|
||||||
|
var Net = require('net');
|
||||||
|
var Socks5Client = require('socks5-client');
|
||||||
|
var util = require('util');
|
||||||
|
|
||||||
|
var Networks = require('../networks');
|
||||||
|
var Messages = require('./messages');
|
||||||
|
|
||||||
|
var MAX_RECEIVE_BUFFER = 10000000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Peer instance represents a remote bitcoin node and allows to communicate
|
||||||
|
* with it using the standar messages of the bitcoin p2p protocol.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
* var peer = new Peer('127.0.0.1').setProxy('127.0.0.1', 9050);
|
||||||
|
* peer.on('tx', function(tx) {
|
||||||
|
* console.log('New transaction: ', tx.id);
|
||||||
|
* });
|
||||||
|
* peer.connect();
|
||||||
|
*
|
||||||
|
* @param {String} host - IP address of the remote host
|
||||||
|
* @param {Number} [port] - Port number of the remote host
|
||||||
|
* @param {Network} [network] - The context for this communication
|
||||||
|
* @returns {Peer} A new instance of Peer.
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function Peer(host, port, network) {
|
||||||
|
if (!(this instanceof Peer)) {
|
||||||
|
return new Peer(host, port, network);
|
||||||
|
}
|
||||||
|
|
||||||
|
// overloading stuff
|
||||||
|
if (port instanceof Object && !network) {
|
||||||
|
network = port;
|
||||||
|
port = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.host = host;
|
||||||
|
this.status = Peer.STATUS.DISCONNECTED;
|
||||||
|
this.network = network || Networks.livenet;
|
||||||
|
this.port = port || this.network.port;
|
||||||
|
|
||||||
|
this.dataBuffer = new Buffers();
|
||||||
|
|
||||||
|
this.version = 0;
|
||||||
|
this.bestHeight = 0;
|
||||||
|
this.subversion = null;
|
||||||
|
|
||||||
|
// set message handlers
|
||||||
|
var self = this;
|
||||||
|
this.on('verack', function() {
|
||||||
|
self.status = Peer.STATUS.READY;
|
||||||
|
self.emit('ready');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('version', function(message) {
|
||||||
|
self.version = message.version;
|
||||||
|
self.subversion = message.subversion;
|
||||||
|
self.bestHeight = message.start_height
|
||||||
|
});
|
||||||
|
|
||||||
|
this.on('ping', function(message) {
|
||||||
|
self.sendPong(message.nonce);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
util.inherits(Peer, EventEmitter);
|
||||||
|
|
||||||
|
Peer.STATUS = {
|
||||||
|
DISCONNECTED: 'disconnected',
|
||||||
|
CONNECTING: 'connecting',
|
||||||
|
CONNECTED: 'connected',
|
||||||
|
READY: 'ready'
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a socks5 proxy for the connection. Enables the use of the TOR network.
|
||||||
|
*
|
||||||
|
* @param {String} host - IP address of the proxy
|
||||||
|
* @param {Number} port - Port number of the proxy
|
||||||
|
* @returns {Peer} The same Peer instance.
|
||||||
|
*/
|
||||||
|
Peer.prototype.setProxy = function(host, port) {
|
||||||
|
if (this.status != Peer.STATUS.DISCONNECTED) {
|
||||||
|
throw Error('Invalid State');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.proxy = {
|
||||||
|
host: host,
|
||||||
|
port: port
|
||||||
|
};
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init the connection with the remote peer.
|
||||||
|
*
|
||||||
|
* @returns {Socket} The same peer instance.
|
||||||
|
*/
|
||||||
|
Peer.prototype.connect = function() {
|
||||||
|
this.socket = this._getSocket();
|
||||||
|
this.status = Peer.STATUS.CONNECTING;
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
this.socket.on('connect', function(ev) {
|
||||||
|
self.status = Peer.STATUS.CONNECTED;
|
||||||
|
self.emit('connect');
|
||||||
|
self._sendVersion();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.on('error', self.disconnect.bind(this));
|
||||||
|
this.socket.on('end', self.disconnect.bind(this));
|
||||||
|
|
||||||
|
this.socket.on('data', function(data) {
|
||||||
|
self.dataBuffer.push(data);
|
||||||
|
|
||||||
|
if (self.dataBuffer.length > MAX_RECEIVE_BUFFER) return self.disconnect();
|
||||||
|
self._readMessage();
|
||||||
|
});
|
||||||
|
|
||||||
|
this.socket.connect(this.port, this.host);
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnects the remote connection.
|
||||||
|
*
|
||||||
|
* @returns {Socket} The same peer instance.
|
||||||
|
*/
|
||||||
|
Peer.prototype.disconnect = function() {
|
||||||
|
this.status = Peer.STATUS.DISCONNECTED;
|
||||||
|
this.socket.destroy();
|
||||||
|
this.emit('disconnect');
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a Message to the remote peer.
|
||||||
|
*
|
||||||
|
* @param {Message} message - A message instance
|
||||||
|
*/
|
||||||
|
Peer.prototype.sendMessage = function(message) {
|
||||||
|
this.socket.write(message.serialize(this.network));
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function that sends VERSION message to the remote peer.
|
||||||
|
*/
|
||||||
|
Peer.prototype._sendVersion = function() {
|
||||||
|
var message = new Messages.Version();
|
||||||
|
this.sendMessage(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a PONG message to the remote peer.
|
||||||
|
*/
|
||||||
|
Peer.prototype._sendPong = function(nonce) {
|
||||||
|
var message = new Messages.Pong(nonce);
|
||||||
|
this.sendMessage(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function that tries to read a message from the data buffer
|
||||||
|
*/
|
||||||
|
Peer.prototype._readMessage = function() {
|
||||||
|
var message = Messages.parseMessage(this.network, this.dataBuffer);
|
||||||
|
|
||||||
|
if (message) {
|
||||||
|
this.emit(message.command, message);
|
||||||
|
this._readMessage();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal function that creates a socket using a proxy if neccesary.
|
||||||
|
*
|
||||||
|
* @returns {Socket} A Socket instance not yet connected.
|
||||||
|
*/
|
||||||
|
Peer.prototype._getSocket = function() {
|
||||||
|
if (this.proxy) {
|
||||||
|
return new Socks5Client(this.proxy.host, this.proxy.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Net.Socket();
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Peer;
|
||||||
@ -19,6 +19,21 @@ function equals(a, b) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Fill a buffer with a value.
|
||||||
|
*
|
||||||
|
* @param {Buffer} buffer
|
||||||
|
* @param {number} value
|
||||||
|
* @return {Buffer}
|
||||||
|
*/
|
||||||
|
fill: function fill(buffer, value) {
|
||||||
|
var length = buffer.length;
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
buffer[i] = value;
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given argument is an instance of a buffer. Tests for
|
* Returns true if the given argument is an instance of a buffer. Tests for
|
||||||
* both node's Buffer and Uint8Array
|
* both node's Buffer and Uint8Array
|
||||||
@ -136,3 +151,6 @@ module.exports = {
|
|||||||
return new buffer.Buffer(string, 'hex');
|
return new buffer.Buffer(string, 'hex');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.NULL_HASH = module.exports.fill(new Buffer(32), 0);
|
||||||
|
module.exports.EMPTY_BUFFER = new Buffer(0);
|
||||||
|
|||||||
15
npm-shrinkwrap.json
generated
15
npm-shrinkwrap.json
generated
@ -22,6 +22,16 @@
|
|||||||
"from": "bs58@2.0.0",
|
"from": "bs58@2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/bs58/-/bs58-2.0.0.tgz"
|
"resolved": "https://registry.npmjs.org/bs58/-/bs58-2.0.0.tgz"
|
||||||
},
|
},
|
||||||
|
"bufferput": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"from": "bufferput@0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bufferput/-/bufferput-0.1.2.tgz"
|
||||||
|
},
|
||||||
|
"buffers": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"from": "buffers@0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz"
|
||||||
|
},
|
||||||
"elliptic": {
|
"elliptic": {
|
||||||
"version": "0.15.12",
|
"version": "0.15.12",
|
||||||
"from": "elliptic@0.15.12",
|
"from": "elliptic@0.15.12",
|
||||||
@ -78,6 +88,11 @@
|
|||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"from": "sha512@=0.0.1",
|
"from": "sha512@=0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/sha512/-/sha512-0.0.1.tgz"
|
"resolved": "https://registry.npmjs.org/sha512/-/sha512-0.0.1.tgz"
|
||||||
|
},
|
||||||
|
"socks5-client": {
|
||||||
|
"version": "0.3.6",
|
||||||
|
"from": "socks5-client@0.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/socks5-client/-/socks5-client-0.3.6.tgz"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,13 +74,16 @@
|
|||||||
"asn1.js": "=0.4.1",
|
"asn1.js": "=0.4.1",
|
||||||
"bn.js": "=0.15.2",
|
"bn.js": "=0.15.2",
|
||||||
"bs58": "=2.0.0",
|
"bs58": "=2.0.0",
|
||||||
|
"bufferput": "^0.1.2",
|
||||||
|
"buffers": "^0.1.1",
|
||||||
"elliptic": "=0.15.14",
|
"elliptic": "=0.15.14",
|
||||||
"hash.js": "=0.3.2",
|
"hash.js": "=0.3.2",
|
||||||
"inherits": "=2.0.1",
|
"inherits": "=2.0.1",
|
||||||
"jsrsasign": "=0.0.3",
|
"jsrsasign": "=0.0.3",
|
||||||
"lodash": "=2.4.1",
|
"lodash": "=2.4.1",
|
||||||
"protobufjs": "=3.0.0",
|
"protobufjs": "=3.0.0",
|
||||||
"sha512": "=0.0.1"
|
"sha512": "=0.0.1",
|
||||||
|
"socks5-client": "^0.3.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"brfs": "1.2.0",
|
"brfs": "1.2.0",
|
||||||
@ -97,9 +100,7 @@
|
|||||||
"run-sequence": "^1.0.2",
|
"run-sequence": "^1.0.2",
|
||||||
"karma": "^0.12.28",
|
"karma": "^0.12.28",
|
||||||
"karma-firefox-launcher": "^0.1.3",
|
"karma-firefox-launcher": "^0.1.3",
|
||||||
"karma-mocha": "^0.1.9",
|
"karma-mocha": "^0.1.9"
|
||||||
"mocha": "~2.0.1",
|
|
||||||
"run-sequence": "^1.0.2"
|
|
||||||
},
|
},
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
|
|||||||
24
test/data/messages.js
Normal file
24
test/data/messages.js
Normal file
File diff suppressed because one or more lines are too long
353
test/transport/messages.js
Normal file
353
test/transport/messages.js
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var chai = require('chai');
|
||||||
|
|
||||||
|
var should = chai.should();
|
||||||
|
|
||||||
|
var bitcore = require('../..');
|
||||||
|
var Data = require('../data/messages');
|
||||||
|
var Messages = bitcore.transport.Messages;
|
||||||
|
var Networks = bitcore.Networks;
|
||||||
|
|
||||||
|
describe('Messages', function() {
|
||||||
|
|
||||||
|
describe('Version', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Version();
|
||||||
|
message.command.should.equal('version');
|
||||||
|
message.version.should.equal(70000);
|
||||||
|
message.subversion.should.equal('/BitcoinX:0.1/');
|
||||||
|
should.exist(message.nonce);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Version();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Version();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to parse payload', function() {
|
||||||
|
var payload = new Buffer(Data.VERSION.payload, 'hex');
|
||||||
|
new Messages.Version().fromBuffer(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('VerAck', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.VerAck();
|
||||||
|
message.command.should.equal('verack');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.VerAck();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.VerAck();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to parse payload', function() {
|
||||||
|
var payload = new Buffer(Data.VERACK.payload, 'hex');
|
||||||
|
new Messages.VerAck().fromBuffer(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Inventory', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Inventory();
|
||||||
|
message.command.should.equal('inv');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Inventory();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Inventory();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to parse payload', function() {
|
||||||
|
var payload = new Buffer(Data.INV.payload, 'hex');
|
||||||
|
new Messages.Inventory().fromBuffer(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Addresses', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Addresses();
|
||||||
|
message.command.should.equal('addr');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Addresses();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Addresses();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to parse payload', function() {
|
||||||
|
var payload = new Buffer(Data.ADDR.payload, 'hex');
|
||||||
|
new Messages.Addresses().fromBuffer(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Ping', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Ping();
|
||||||
|
message.command.should.equal('ping');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Ping();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Ping();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to parse payload', function() {
|
||||||
|
var payload = new Buffer(Data.PING.payload, 'hex');
|
||||||
|
new Messages.Ping().fromBuffer(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Pong', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Pong();
|
||||||
|
message.command.should.equal('pong');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Pong();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Pong();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to parse payload', function() {
|
||||||
|
var payload = new Buffer(Data.PING.payload, 'hex');
|
||||||
|
new Messages.Pong().fromBuffer(payload);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Alert', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Alert();
|
||||||
|
message.command.should.equal('alert');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Alert();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Alert();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Reject', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Reject();
|
||||||
|
message.command.should.equal('reject');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Reject();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Reject();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Block', function() {
|
||||||
|
var blockHex = 'f9beb4d91d0100000100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000';
|
||||||
|
var block = new bitcore.Block(new Buffer(blockHex, 'hex'));
|
||||||
|
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Block(block);
|
||||||
|
message.command.should.equal('block');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Block(block);
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Block(block);
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GetBlocks', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.GetBlocks();
|
||||||
|
message.command.should.equal('getblocks');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.GetBlocks();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.GetBlocks();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GetHeaders', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.GetHeaders();
|
||||||
|
message.command.should.equal('getheaders');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.GetHeaders();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.GetHeaders();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GetData', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.GetData();
|
||||||
|
message.command.should.equal('getdata');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.GetData();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.GetData();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GetData', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.GetData();
|
||||||
|
message.command.should.equal('getdata');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.GetData();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.GetData();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GetAddresses', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.GetAddresses();
|
||||||
|
message.command.should.equal('getaddr');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.GetAddresses();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.GetAddresses();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Headers', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Headers();
|
||||||
|
message.command.should.equal('headers');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Headers();
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Headers();
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Transaction', function() {
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var message = new Messages.Transaction(new bitcore.Transaction());
|
||||||
|
message.command.should.equal('tx');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the payload', function() {
|
||||||
|
var message = new Messages.Transaction(new bitcore.Transaction());
|
||||||
|
var payload = message.getPayload();
|
||||||
|
should.exist(payload);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to serialize the message', function() {
|
||||||
|
var message = new Messages.Transaction(new bitcore.Transaction());
|
||||||
|
var buffer = message.serialize(Networks.livenet);
|
||||||
|
should.exist(buffer);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
69
test/transport/peer.js
Normal file
69
test/transport/peer.js
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var chai = require('chai');
|
||||||
|
var Net = require('net');
|
||||||
|
var Socks5Client = require('socks5-client');
|
||||||
|
|
||||||
|
/* jshint unused: false */
|
||||||
|
var should = chai.should();
|
||||||
|
var expect = chai.expect;
|
||||||
|
|
||||||
|
var bitcore = require('../..');
|
||||||
|
var Peer = bitcore.transport.Peer;
|
||||||
|
var Networks = bitcore.Networks;
|
||||||
|
|
||||||
|
describe('Peer', function() {
|
||||||
|
|
||||||
|
it('should be able to create instance', function() {
|
||||||
|
var peer = new Peer('localhost');
|
||||||
|
peer.host.should.equal('localhost');
|
||||||
|
peer.network.should.equal(Networks.livenet);
|
||||||
|
peer.port.should.equal(Networks.livenet.port);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to create instance setting a port', function() {
|
||||||
|
var peer = new Peer('localhost', 8111);
|
||||||
|
peer.host.should.equal('localhost');
|
||||||
|
peer.network.should.equal(Networks.livenet);
|
||||||
|
peer.port.should.equal(8111);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to create instance setting a network', function() {
|
||||||
|
var peer = new Peer('localhost', Networks.testnet);
|
||||||
|
peer.host.should.equal('localhost');
|
||||||
|
peer.network.should.equal(Networks.testnet);
|
||||||
|
peer.port.should.equal(Networks.testnet.port);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be able to create instance setting port and network', function() {
|
||||||
|
var peer = new Peer('localhost', 8111, Networks.testnet);
|
||||||
|
peer.host.should.equal('localhost');
|
||||||
|
peer.network.should.equal(Networks.testnet);
|
||||||
|
peer.port.should.equal(8111);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support creating instance without new', function() {
|
||||||
|
var peer = Peer('localhost', 8111, Networks.testnet);
|
||||||
|
peer.host.should.equal('localhost');
|
||||||
|
peer.network.should.equal(Networks.testnet);
|
||||||
|
peer.port.should.equal(8111);
|
||||||
|
});
|
||||||
|
|
||||||
|
// only for node TODO: (yemel)
|
||||||
|
it.skip('should be able to set a proxy', function() {
|
||||||
|
var peer, peer2, socket;
|
||||||
|
|
||||||
|
peer = new Peer('localhost');
|
||||||
|
expect(peer.proxy).to.be.undefined();
|
||||||
|
socket = peer._getSocket();
|
||||||
|
socket.should.be.instanceof(Net.Socket);
|
||||||
|
|
||||||
|
peer2 = peer.setProxy('127.0.0.1', 9050);
|
||||||
|
peer2.proxy.host.should.equal('127.0.0.1');
|
||||||
|
peer2.proxy.port.should.equal(9050);
|
||||||
|
socket = peer2._getSocket();
|
||||||
|
socket.should.be.instanceof(Socks5Client);
|
||||||
|
|
||||||
|
peer.should.equal(peer2);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user