Extensibility

- changed options in constructors
- define block and transaction constructors for block and tx messages
This commit is contained in:
Braydon Fuller 2015-03-10 17:47:02 -04:00
parent ae93c147b7
commit 9a813bad96
17 changed files with 1468 additions and 1198 deletions

View File

@ -6,31 +6,19 @@ var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var $ = bitcore.util.preconditions;
BloomFilter.fromBuffer = function fromBuffer(payload) {
var obj = {};
var parser = new BufferReader(payload);
var data = parser.readVarLengthBuffer();
$.checkState(data.length <= BloomFilter.MAX_BLOOM_FILTER_SIZE,
'Filter data must be <= MAX_BLOOM_FILTER_SIZE bytes');
var nHashFuncs = parser.readUInt32LE();
$.checkState(nHashFuncs <= BloomFilter.MAX_HASH_FUNCS,
'Filter nHashFuncs must be <= MAX_HASH_FUNCS');
var nTweak = parser.readUInt32LE();
var nFlags = parser.readUInt8();
var vData = [];
var dataParser = new BufferReader(data);
for(var i = 0; i < data.length; i++) {
vData.push(dataParser.readUInt8());
var length = parser.readUInt8();
obj.vData = [];
for(var i = 0; i < length; i++) {
obj.vData.push(parser.readUInt8());
}
return new BloomFilter({
vData: vData,
nHashFuncs: nHashFuncs,
nTweak: nTweak,
nFlags: nFlags
});
}
obj.nHashFuncs = parser.readUInt32LE();
obj.nTweak = parser.readUInt32LE();
obj.nFlags = parser.readUInt8();
return new BloomFilter(obj);
};
BloomFilter.prototype.toBuffer = function toBuffer() {
var bw = new BufferWriter();

897
lib/commands.js Normal file
View File

@ -0,0 +1,897 @@
'use strict';
var Message = require('./message');
var inherits = require('util').inherits;
var packageInfo = require('../package.json');
var bitcore = require('bitcore');
var BN = bitcore.crypto.BN;
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var BufferUtil = bitcore.util.buffer;
var BloomFilter = require('./bloomfilter');
var Put = require('bufferput'); //todo remove
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
function Commands(options) {
/* jshint maxstatements: 150 */
/* jshint maxcomplexity: 10 */
if (!options) {
options = {};
}
var magicNumber = options.magicNumber;
if (!magicNumber) {
magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
}
var Block = options.Block || bitcore.Block;
var BlockHeader = options.BlockHeader || bitcore.BlockHeader;
var Transaction = options.Transaction || bitcore.Transaction;
var MerkleBlock = options.MerkleBlock || bitcore.MerkleBlock;
var protocolVersion = options.protocolVersion || 70000;
var commands = {};
/* shared */
function checkFinished(parser) {
if(!parser.finished()) {
throw new Error('Data still available after parsing');
}
}
function getNonce() {
return bitcore.crypto.Random.getRandomBuffer(8);
}
function writeIP(ip, bw) {
var words = ip.v6.split(':').map(function(s) {
return new Buffer(s, 'hex');
});
for (var i = 0; i < words.length; i++) {
var word = words[i];
bw.write(word);
}
}
function writeAddr(addr, bw) {
if (_.isUndefined(addr)) {
var pad = new Buffer(Array(26));
bw.write(pad);
return;
}
bw.writeUInt64LEBN(addr.services);
writeIP(addr.ip, bw);
bw.writeUInt16BE(addr.port);
}
function writeInventory(inventory, bw) {
bw.writeVarintNum(inventory.length);
inventory.forEach(function(value) {
bw.writeUInt32LE(value.type);
bw.write(value.hash);
});
}
function parseIP(parser) {
var ipv6 = [];
var ipv4 = [];
for (var a = 0; a < 8; a++) {
var word = parser.read(2);
ipv6.push(word.toString('hex'));
if (a >= 6) {
ipv4.push(word[0]);
ipv4.push(word[1]);
}
}
ipv6 = ipv6.join(':');
ipv4 = ipv4.join('.');
return {
v6: ipv6,
v4: ipv4
};
}
function parseAddr(parser) {
var services = parser.readUInt64LEBN();
var ip = parseIP(parser);
var port = parser.readUInt16BE();
return {
services: services,
ip: ip,
port: port
};
}
function sanitizeStartStop(obj) {
/* jshint maxcomplexity: 10 */
$.checkArgument(_.isUndefined(options.starts) || _.isArray(options.starts));
var starts = obj.starts;
var stop = obj.stop;
if (starts) {
starts = starts.map(function(hash) {
if (_.isString(hash)) {
return BufferUtil.reverse(new Buffer(hash, 'hex'));
} else {
return hash;
}
});
} else {
starts = [];
}
for (var i = 0; i < starts.length; i++) {
if (starts[i].length !== 32) {
throw new Error('Invalid hash ' + i + ' length: ' + starts[i].length);
}
}
stop = obj.stop;
if (_.isString(stop)) {
stop = BufferUtil.reverse(new Buffer(stop, 'hex'));
}
if (!stop) {
stop = BufferUtil.NULL_HASH;
}
obj.starts = starts;
obj.stop = stop;
return obj;
}
/**
* The version message is used on connection creation to advertise
* the type of node. The remote node will respond with its version, and no
* communication is possible until both peers have exchanged their versions.
* By default, bitcore advertises itself as named `bitcore:0.8`.
*
* @param{Object} obj - properties for the version
* @param{String} obj.subversion - version of the client
* @param{Buffer} obj.nonce - a random 8 byte buffer
*/
commands.version = function(obj) {
Message.call(this, obj);
this.command = 'version';
_.assign(this, obj);
this.magicNumber = magicNumber;
this.nonce = this.nonce || getNonce();
this.services = this.services || new BN(1, 10);
this.timestamp = this.timestamp || new Date();
this.version = this.version || protocolVersion;
this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/';
this.startHeight = this.startHeight || 0;
};
inherits(commands.version, Message);
commands.version.fromObject = function(obj) {
return new commands.version(obj);
};
commands.version.fromBuffer = function(payload) {
var parser = new BufferReader(payload);
var obj = {};
obj.version = parser.readUInt32LE();
obj.services = parser.readUInt64LEBN();
obj.timestamp = new Date(parser.readUInt64LEBN().toNumber() * 1000);
obj.addrMe = {
services: parser.readUInt64LEBN(),
ip: parseIP(parser),
port: parser.readUInt16BE()
};
obj.addrYou = {
services: parser.readUInt64LEBN(),
ip: parseIP(parser),
port: parser.readUInt16BE()
};
obj.nonce = parser.read(8);
obj.subversion = parser.readVarLengthBuffer().toString();
obj.startHeight = parser.readUInt32LE();
if(parser.finished()) {
obj.relay = true;
} else {
obj.relay = !!parser.readUInt8();
}
checkFinished(parser);
return commands.version.fromObject(obj);
};
commands.version.prototype.getPayload = function() {
var bw = new BufferWriter();
bw.writeUInt32LE(this.version);
bw.writeUInt64LEBN(this.services);
var timestampBuffer = new Buffer(Array(8));
timestampBuffer.writeUInt32LE(Math.round(this.timestamp.getTime() / 1000), 0);
bw.write(timestampBuffer);
writeAddr(this.addrMe, bw);
writeAddr(this.addrYou, bw);
bw.write(this.nonce);
bw.writeVarintNum(this.subversion.length);
bw.write(new Buffer(this.subversion, 'ascii'));
bw.writeUInt32LE(this.startHeight);
bw.writeUInt8(this.relay);
return bw.concat();
};
/* verack */
commands.verack = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'verack';
};
inherits(commands.verack, Message);
commands.verack.fromObject = function(obj) {
return new commands.verack(obj);
};
commands.verack.fromBuffer = function(payload) {
return commands.verack.fromObject({});
};
commands.verack.prototype.getPayload = function() {
return BufferUtil.EMPTY_BUFFER;
};
/* ping */
commands.ping = function(options) {
Message.call(this, options);
this.command = 'ping';
this.magicNumber = magicNumber;
this.nonce = options.nonce || getNonce();
};
inherits(commands.ping, Message);
commands.ping.prototype.getPayload = function() {
return this.nonce;
};
commands.ping.fromObject = function(obj) {
return new commands.ping(obj);
};
commands.ping.fromBuffer = function(payload) {
var obj = {};
var parser = new BufferReader(payload);
obj.nonce = parser.read(8);
checkFinished(parser);
return commands.ping.fromObject(obj);
};
/* pong */
commands.pong = function(options) {
Message.call(this, options);
this.command = 'pong';
this.magicNumber = magicNumber;
this.nonce = options.nonce;
};
inherits(commands.pong, Message);
commands.pong.fromObject = function(obj) {
return new commands.pong(obj);
};
commands.pong.fromBuffer = function(payload) {
var obj = {};
var parser = new BufferReader(payload);
obj.nonce = parser.read(8);
checkFinished(parser);
return commands.pong.fromObject(obj);
};
commands.pong.prototype.getPayload = function() {
return this.nonce;
};
/* block */
commands.block = function(options) {
Message.call(this, options);
this.command = 'block';
this.magicNumber = magicNumber;
this.block = options.block;
};
inherits(commands.block, Message);
commands.block.fromObject = function(options) {
return new commands.block(options);
};
commands.block.fromBuffer = function(payload) {
var block = Block.fromBuffer(payload);
return commands.block.fromObject({block: block});
};
commands.block.prototype.getPayload = function() {
return this.block.toBuffer();
};
/* tx */
commands.tx = function(options) {
Message.call(this, options);
this.command = 'tx';
this.magicNumber = magicNumber;
this.transaction = options.transaction;
};
inherits(commands.tx, Message);
commands.tx.fromObject = function(options) {
return new commands.tx(options);
};
commands.tx.fromBuffer = function(payload) {
var transaction;
if (Transaction.prototype.fromBuffer) {
transaction = Transaction().fromBuffer(payload);
} else {
transaction = Transaction.fromBuffer(payload);
}
return commands.tx.fromObject({transaction: transaction});
};
commands.tx.prototype.getPayload = function() {
return this.transaction.toBuffer();
};
/* getdata */
commands.getdata = function(options) {
Message.call(this, options);
this.command = 'getdata';
this.magicNumber = magicNumber;
this.inventory = options.inventory;
};
inherits(commands.getdata, Message);
commands.getdata.fromObject = function(options) {
return new commands.getdata(options);
};
commands.getdata.fromBuffer = function(payload) {
var obj = {
inventory: []
};
var parser = new BufferReader(payload);
var count = parser.readVarintNum();
for (var i = 0; i < count; i++) {
var type = parser.readUInt32LE();
var hash = parser.read(32);
obj.inventory.push({type: type, hash: hash});
}
checkFinished(parser);
return commands.getdata.fromObject(obj);
};
commands.getdata.prototype.getPayload = function() {
var bw = new BufferWriter();
writeInventory(this.inventory, bw);
return bw.concat();
};
/**
* Sent in response to a `getheaders` message. It contains information about
* block headers.
*
* @param{Array} blockheaders - array of block headers
*/
commands.headers = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'headers';
this.headers = options.headers;
};
inherits(commands.headers, Message);
commands.headers.fromObject = function(options) {
return new commands.headers(options);
};
commands.headers.fromBuffer = function(payload) {
var obj = {};
$.checkArgument(payload && payload.length > 0, 'No data found to create Headers message');
var parser = new BufferReader(payload);
var count = parser.readVarintNum();
obj.headers = [];
for (var i = 0; i < count; i++) {
var header = BlockHeader.fromBufferReader(parser);
obj.headers.push(header);
var txn_count = parser.readUInt8();
$.checkState(txn_count === 0, 'txn_count should always be 0');
}
checkFinished(parser);
return commands.headers.fromObject(obj);
};
commands.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);
put.varint(0);
}
return put.buffer();
};
/* notfound */
commands.notfound = function(options) {
Message.call(this, options);
this.command = 'notfound';
this.magicNumber = magicNumber;
this.inventory = options.inventory;
};
inherits(commands.notfound, Message);
commands.notfound.fromObject = function(options) {
return new commands.notfound(options);
};
commands.notfound.fromBuffer = function(payload) {
var obj = {
inventory: []
};
var parser = new BufferReader(payload);
var count = parser.readVarintNum();
for (var i = 0; i < count; i++) {
var type = parser.readUInt32LE();
var hash = parser.read(32);
obj.inventory.push({type: type, hash: hash});
}
checkFinished(parser);
return commands.notfound.fromObject(obj);
};
commands.notfound.prototype.getPayload = function() {
var bw = new BufferWriter();
writeInventory(this.inventory, bw);
return bw.concat();
};
/* inv */
commands.inv = function(options) {
Message.call(this, options);
this.command = 'inv';
this.magicNumber = magicNumber;
this.inventory = options.inventory;
};
inherits(commands.inv, Message);
commands.inv.fromObject = function(options) {
return new commands.inv(options);
};
commands.inv.prototype.getPayload = function() {
var bw = new BufferWriter();
writeInventory(this.inventory, bw);
return bw.concat();
};
commands.inv.fromBuffer = function(payload) {
var obj = {
inventory: []
};
var parser = new BufferReader(payload);
var count = parser.readVarintNum();
for (var i = 0; i < count; i++) {
var type = parser.readUInt32LE();
var hash = parser.read(32);
obj.inventory.push({type: type, hash: hash});
}
checkFinished(parser);
return commands.inv.fromObject(obj);
};
/* addr */
commands.addr = function(options) {
Message.call(this, options);
this.command = 'addr';
this.magicNumber = magicNumber;
this.addresses = options.addresses;
};
inherits(commands.addr, Message);
commands.addr.fromObject = function(options) {
return new commands.addr(options);
};
commands.addr.fromBuffer = function(payload) {
var parser = new BufferReader(payload);
var addrCount = parser.readVarintNum();
var obj = {};
obj.addresses = [];
for (var i = 0; i < addrCount; i++) {
// todo: time only available on versions >=31402
var time = new Date(parser.readUInt32LE() * 1000);
var addr = parseAddr(parser);
addr.time = time;
obj.addresses.push(addr);
}
checkFinished(parser);
return commands.addr.fromObject(obj);
};
commands.addr.prototype.getPayload = function() {
var bw = new BufferWriter();
bw.writeVarintNum(this.addresses.length);
for (var i = 0; i < this.addresses.length; i++) {
var addr = this.addresses[i];
bw.writeUInt32LE(addr.time.getTime() / 1000);
writeAddr(addr, bw);
}
return bw.concat();
};
/* alert */
commands.alert = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'alert';
this.payload = options.payload || new Buffer(32);
this.signature = options.signature || new Buffer(32);
};
inherits(commands.alert, Message);
commands.alert.fromObject = function(options) {
return new commands.alert(options);
};
commands.alert.fromBuffer = function(payload) {
var obj = {};
var parser = new BufferReader(payload);
obj.payload = parser.readVarLengthBuffer();
obj.signature = parser.readVarLengthBuffer();
checkFinished(parser);
return commands.alert.fromObject(obj);
};
commands.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();
};
/* reject */
// todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject
commands.reject = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'reject';
};
inherits(commands.reject, Message);
commands.reject.fromObject = function(options) {
return new commands.reject(options);
};
commands.reject.fromBuffer = function(payload) {
var obj = {};
return commands.reject.fromObject(obj);
};
commands.reject.prototype.getPayload = function() {
return BufferUtil.EMPTY_BUFFER;
};
/**
* Contains information about a MerkleBlock
*
* @name P2P.Message.MerkleBlock
* @param {MerkleBlock} block
*/
commands.merkleblock = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'merkleblock';
$.checkArgument(
_.isUndefined(options.merkleBlock) ||
options.merkleBlock instanceof MerkleBlock
);
this.merkleBlock = options.merkleBlock;
};
inherits(commands.merkleblock, Message);
commands.merkleblock.fromObject = function(options) {
return new commands.merkleblock(options);
};
commands.merkleblock.fromBuffer = function(payload) {
var obj = {};
$.checkArgument(BufferUtil.isBuffer(payload));
obj.merkleBlock = MerkleBlock.fromBuffer(payload);
return commands.merkleblock.fromObject(obj);
};
commands.merkleblock.prototype.getPayload = function() {
return this.merkleBlock ? this.merkleBlock.toBuffer() : BufferUtil.EMPTY_BUFFER;
};
/* filterload */
commands.filterload = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'filterload';
$.checkArgument(_.isUndefined(options.filter) || options.filter instanceof BloomFilter,
'BloomFilter object or undefined required for FilterLoad');
this.filter = options.filter;
};
inherits(commands.filterload, Message);
commands.filterload.fromObject = function(options) {
return new commands.filterload(options);
};
commands.filterload.fromBuffer = function(payload) {
var obj = {};
obj.filter = BloomFilter.fromBuffer(payload);
return commands.filterload.fromObject(obj);
};
commands.filterload.prototype.getPayload = function() {
if(this.filter) {
return this.filter.toBuffer();
} else {
return BufferUtil.EMPTY_BUFFER;
}
};
/**
* Request peer to add data to a bloom filter already set by 'filterload'
*
* @name P2P.Message.filteradd
* @param{Buffer} data - Array of bytes representing bloom filter data
*/
commands.filteradd = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'filteradd';
this.data = options.data || BufferUtil.EMPTY_BUFFER;
};
inherits(commands.filteradd, Message);
commands.filteradd.fromObject = function(options) {
return new commands.filteradd(options);
};
commands.filteradd.fromBuffer = function(payload) {
var obj = {};
$.checkArgument(payload);
var parser = new BufferReader(payload);
obj.data = parser.readVarLengthBuffer();
checkFinished(parser);
return commands.filteradd.fromObject(obj);
};
commands.filteradd.prototype.getPayload = function() {
var bw = new BufferWriter();
bw.writeVarintNum(this.data.length);
bw.write(this.data);
return bw.concat();
};
/* filterclear */
commands.filterclear = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'filterclear';
};
inherits(commands.filterclear, Message);
commands.filterclear.fromObject = function(options) {
return new commands.filterclear(options);
};
commands.filterclear.fromBuffer = function(payload) {
return commands.filterclear.fromObject({});
};
commands.filterclear.prototype.getPayload = function() {
return BufferUtil.EMPTY_BUFFER;
};
/**
* Query another peer about blocks. It can query for multiple block hashes,
* and the response will contain all the chains of blocks starting from those
* hashes.
*
* @param{Array} starts - array of buffers or strings with the starting block hashes
* @param{Buffer} [stop] - hash of the last block
*/
commands.getblocks = function(options) {
Message.call(this, options);
this.command = 'getblocks';
this.version = protocolVersion;
this.magicNumber = magicNumber;
options = sanitizeStartStop(options);
this.starts = options.starts;
this.stop = options.stop;
};
inherits(commands.getblocks, Message);
commands.getblocks.fromObject = function(obj) {
return new commands.getblocks(obj);
};
commands.getblocks.fromBuffer = function(payload) {
var obj = {};
var parser = new BufferReader(payload);
$.checkArgument(!parser.finished(), 'No data received in payload');
obj.version = parser.readUInt32LE();
var startCount = parser.readVarintNum();
obj.starts = [];
for (var i = 0; i < startCount; i++) {
obj.starts.push(parser.read(32));
}
obj.stop = parser.read(32);
checkFinished(parser);
return commands.getblocks.fromObject(obj);
};
commands.getblocks.prototype.getPayload = function() {
var bw = new BufferWriter();
bw.writeUInt32LE(this.version);
bw.writeVarintNum(this.starts.length);
for (var i = 0; i < this.starts.length; i++) {
bw.write(this.starts[i]);
}
if (this.stop.length !== 32) {
throw new Error('Invalid hash length: ' + this.stop.length);
}
bw.write(this.stop);
return bw.concat();
};
/**
* Request block headers starting from a hash
*
* @param{Array} starts - array of buffers with the starting block hashes
* @param{Buffer} [stop] - hash of the last block
*/
//todo: need test data
commands.getheaders = function(options) {
Message.call(this, options);
this.command = 'getheaders';
this.version = protocolVersion;
this.magicNumber = magicNumber;
options = sanitizeStartStop(options);
this.starts = options.starts;
this.stop = options.stop;
};
inherits(commands.getheaders, Message);
commands.getheaders.fromObject = function(obj) {
return new commands.getheaders(obj);
};
commands.getheaders.fromBuffer = function(payload) {
var obj = {};
var parser = new BufferReader(payload);
$.checkArgument(!parser.finished(), 'No data received in payload');
obj.version = parser.readUInt32LE();
var startCount = Math.min(parser.readVarintNum(), 500);
obj.starts = [];
for (var i = 0; i < startCount; i++) {
obj.starts.push(parser.read(32));
}
obj.stop = parser.read(32);
checkFinished(parser);
return commands.getheaders.fromObject(obj);
};
commands.getheaders.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++) {
put.put(this.starts[i]);
}
if (this.stop.length !== 32) {
throw new Error('Invalid hash length: ' + this.stop.length);
}
put.put(this.stop);
return put.buffer();
};
/* mempool */
commands.mempool = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'mempool';
};
inherits(commands.mempool, Message);
commands.mempool.fromObject = function(options) {
return new commands.mempool(options);
};
commands.mempool.fromBuffer = function(payload) {
return commands.mempool.fromObject({});
};
commands.mempool.prototype.getPayload = function() {
return BufferUtil.EMPTY_BUFFER;
};
/* getaddr */
//todo: need test data
commands.getaddr = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;
this.command = 'getaddr';
};
inherits(commands.getaddr, Message);
commands.getaddr.fromObject = function(options) {
return new commands.getaddr(options);
};
commands.getaddr.fromBuffer = function(payload) {
var obj = {};
return commands.getaddr.fromObject(obj);
};
commands.getaddr.prototype.getPayload = function() {
return BufferUtil.EMPTY_BUFFER;
};
return commands;
}
module.exports = Commands;

View File

@ -1,9 +1,16 @@
/**
* @namespace P2P
*/
var Messages = require('./messages');
module.exports = {
Messages: require('./messages'),
Message: require('./message'),
Commands: require('./commands'),
Inventory: require('./inventory'),
BloomFilter: require('./bloomfilter'),
Messages: Messages,
messages: new Messages(),
Peer: require('./peer'),
Pool: require('./pool'),
BloomFilter: require('./bloomfilter')
Pool: require('./pool')
};

82
lib/inventory.js Normal file
View File

@ -0,0 +1,82 @@
'use strict';
var bitcore = require('bitcore');
var $ = bitcore.util.preconditions;
var BufferUtil = bitcore.util.buffer;
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var _ = bitcore.deps._;
function Inventory(obj) {
this.type = obj.type;
if (!BufferUtil.isBuffer(obj.hash)) {
throw new TypeError('Unexpected hash, expected to be a buffer');
}
this.hash = obj.hash;
}
Inventory.forItem = function(type, hash) {
$.checkArgument(hash);
//todo: is reversing expected behavior?
if (_.isString(hash)) {
hash = new Buffer(hash, 'hex');
hash = BufferUtil.reverse(hash);
}
return new Inventory({type: type, hash: hash});
};
Inventory.forBlock = function(hash) {
return Inventory.forItem(Inventory.TYPE.BLOCK, hash);
};
Inventory.forFilteredBlock = function(hash) {
return Inventory.forItem(Inventory.TYPE.FILTERED_BLOCK, hash);
};
Inventory.forTransaction = function(hash) {
return Inventory.forItem(Inventory.TYPE.TX, hash);
};
Inventory.prototype.toBuffer = function() {
var bw = new BufferWriter();
bw.writeUInt32LE(this.type);
bw.write(this.hash);
return bw.concat();
};
Inventory.prototype.toBufferWriter = function(bw) {
bw.writeUInt32LE(this.type);
bw.write(this.hash);
return bw;
};
Inventory.fromBuffer = function(payload) {
var parser = new BufferReader(payload);
var obj = {};
obj.type = parser.readUInt32LE();
obj.hash = parser.read(32);
return new Inventory(obj);
};
Inventory.fromBufferWriter = function(bw) {
var obj = {};
obj.type = bw.readUInt32LE();
obj.hash = bw.read(32);
return new Inventory(obj);
};
// https://en.bitcoin.it/wiki/Protocol_specification#Inventory_Vectors
Inventory.TYPE = {};
Inventory.TYPE.ERROR = 0;
Inventory.TYPE.TX = 1;
Inventory.TYPE.BLOCK = 2;
Inventory.TYPE.FILTERED_BLOCK = 3;
Inventory.TYPE_NAME = [
'ERROR',
'TX',
'BLOCK',
'FILTERED_BLOCK'
];
module.exports = Inventory;

34
lib/message.js Normal file
View File

@ -0,0 +1,34 @@
'use strict';
var bitcore = require('bitcore');
var BufferWriter = bitcore.encoding.BufferWriter;
var Hash = bitcore.crypto.Hash;
/**
* Base message that can be inherited to add an additional
* `getPayload` method to modify the message payload.
*/
function Message(options) {
this.command = options.command;
this.magicNumber = options.magicNumber;
}
Message.prototype.toBuffer = Message.prototype.serialize = function() {
var commandBuf = new Buffer(Array(12));
commandBuf.write(this.command, 'ascii');
var payload = this.getPayload();
var checksum = Hash.sha256sha256(payload).slice(0, 4);
var bw = new BufferWriter();
bw.writeUInt32LE(this.magicNumber);
bw.write(commandBuf);
bw.writeUInt32LE(payload.length);
bw.write(checksum);
bw.write(payload);
return bw.concat();
};
module.exports = Message;

File diff suppressed because it is too large Load Diff

View File

@ -4,14 +4,10 @@ var Buffers = require('buffers');
var EventEmitter = require('events').EventEmitter;
var Net = require('net');
var Socks5Client = require('socks5-client');
var util = require('util');
var bitcore = require('bitcore');
var $ = bitcore.util.preconditions;
var Networks = bitcore.Networks;
var Messages = require('./messages');
var MAX_RECEIVE_BUFFER = 10000000;
var util = require('util');
/**
* A Peer instance represents a remote bitcoin node and allows to communicate
@ -33,28 +29,33 @@ var MAX_RECEIVE_BUFFER = 10000000;
* @returns {Peer} A new instance of Peer.
* @constructor
*/
function Peer(host, port, network, relay) {
function Peer(options) {
if (!(this instanceof Peer)) {
return new Peer(host, port, network);
return new Peer(options);
}
// overloading stuff
if (port instanceof Object && !network) {
network = port;
port = undefined;
}
this.host = host || 'localhost';
this.host = options.host || 'localhost';
this.status = Peer.STATUS.DISCONNECTED;
this.network = network || Networks.defaultNetwork;
this.port = port || this.network.port;
this.relay = relay === false ? false : true;
this.port = options.port;
this.network = Networks.get(options.network) || Networks.defaultNetwork;
if (!this.port) {
this.port = this.network.port;
}
this.messages = options.messages || new Messages({
magicNumber: this.network.networkMagic.readUInt32LE(0),
Block: bitcore.Block,
Transaction: bitcore.Transaction
});
this.dataBuffer = new Buffers();
this.version = 0;
this.bestHeight = 0;
this.subversion = null;
this.relay = options.relay === false ? false : true;
// set message handlers
var self = this;
@ -76,6 +77,7 @@ function Peer(host, port, network, relay) {
}
util.inherits(Peer, EventEmitter);
Peer.MAX_RECEIVE_BUFFER = 10000000;
Peer.STATUS = {
DISCONNECTED: 'disconnected',
CONNECTING: 'connecting',
@ -91,8 +93,6 @@ Peer.STATUS = {
* @returns {Peer} The same Peer instance.
*/
Peer.prototype.setProxy = function(host, port) {
$.checkState(this.status === Peer.STATUS.DISCONNECTED);
this.proxy = {
host: host,
port: port
@ -122,7 +122,7 @@ Peer.prototype.connect = function() {
this.socket.on('data', function(data) {
self.dataBuffer.push(data);
if (self.dataBuffer.length > MAX_RECEIVE_BUFFER) {
if (self.dataBuffer.length > Peer.MAX_RECEIVE_BUFFER) {
// TODO: handle this case better
return self.disconnect();
}
@ -155,14 +155,17 @@ Peer.prototype.disconnect = function() {
* @param {Message} message - A message instance
*/
Peer.prototype.sendMessage = function(message) {
this.socket.write(message.serialize(this.network));
this.socket.write(message.toBuffer());
};
/**
* Internal function that sends VERSION message to the remote peer.
*/
Peer.prototype._sendVersion = function() {
var message = new Messages.Version(null, null, this.relay);
// todo: include sending ip address
var message = this.messages.buildFromObject('version', {
relay: this.relay
});
this.sendMessage(message);
};
@ -170,7 +173,9 @@ Peer.prototype._sendVersion = function() {
* Send a PONG message to the remote peer.
*/
Peer.prototype._sendPong = function(nonce) {
var message = new Messages.Pong(nonce);
var message = this.messages.buildFromObject('pong', {
nonce: nonce
});
this.sendMessage(message);
};
@ -178,8 +183,7 @@ Peer.prototype._sendPong = function(nonce) {
* Internal function that tries to read a message from the data buffer
*/
Peer.prototype._readMessage = function() {
var message = Messages.parseMessage(this.network, this.dataBuffer);
var message = this.messages.parseMessage(this.dataBuffer);
if (message) {
this.emit(message.command, message);
this._readMessage();
@ -199,4 +203,20 @@ Peer.prototype._getSocket = function() {
return new Net.Socket();
};
// 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;
};
module.exports = Peer;

View File

@ -2,11 +2,10 @@
var dns = require('dns');
var EventEmitter = require('events').EventEmitter;
var bitcore = require('bitcore');
var Networks = bitcore.Networks;
var sha256 = bitcore.crypto.Hash.sha256;
var Peer = require('./peer');
var Networks = bitcore.Networks;
var util = require('util');
function now() {
@ -43,7 +42,6 @@ function Pool(network, options) {
var self = this;
options = options || {};
this.network = Networks.get(network) || Networks.defaultNetwork;
this.keepalive = false;
this._connectedPeers = {};
@ -51,8 +49,10 @@ function Pool(network, options) {
this.listenAddr = options.listenAddr !== false;
this.dnsSeed = options.dnsSeed !== false;
this.relay = options.relay !== false;
this.maxSize = options.maxSize || Pool.MaxConnectedPeers;
this.messages = options.messages;
this.network = Networks.get(network) || Networks.defaultNetwork;
this.relay = options.relay === false ? false : true;
if (options.addrs) {
for(var i = 0; i < options.addrs.length; i++) {
@ -186,7 +186,12 @@ Pool.prototype._connectPeer = function _connectPeer(addr) {
function addConnectedPeer(addr) {
var port = addr.port || self.network.port;
var ip = addr.ip.v4 || addr.ip.v6;
var peer = new Peer(ip, port, self.network, self.relay);
var peer = new Peer({
ip: ip,
port: port,
messages: self.messages,
relay: self.relay
});
peer.on('disconnect', function peerDisconnect() {
self.emit('peerdisconnect', peer, addr);
});
@ -298,4 +303,12 @@ Pool.prototype.inspect = function inspect() {
this._addrs.length + '>';
};
Pool.prototype.sendMessage = function(message) {
// broadcast to peers
for(var key in this._connectedPeers) {
var peer = this._connectedPeers[key];
peer.sendMessage(message);
}
};
module.exports = Pool;

View File

@ -1,6 +1,7 @@
'use strict';
var chai = require('chai');
var should = chai.should();
var assert = require('assert');
var bitcore = require('bitcore');
@ -8,6 +9,10 @@ var Data = require('./data/messages');
var P2P = require('../');
var BloomFilter = P2P.BloomFilter;
function getPayloadBuffer(messageBuffer) {
return new Buffer(messageBuffer.slice(48), 'hex');
}
// convert a hex string to a bytes buffer
function ParseHex(str) {
var result = [];
@ -22,9 +27,9 @@ function ParseHex(str) {
describe('BloomFilter', function() {
it('BloomFilter#fromBuffer and toBuffer methods work', function() {
var testPayload = Data.FILTERLOAD.payload;
var filter = new BloomFilter.fromBuffer(new Buffer(testPayload, 'hex'));
filter.toBuffer().toString('hex').should.equal(testPayload);
var testPayloadBuffer = getPayloadBuffer(Data.filterload.message);
var filter = new BloomFilter.fromBuffer(testPayloadBuffer);
filter.toBuffer().should.deep.equal(testPayloadBuffer);
});
// test data from: https://github.com/bitcoin/bitcoin/blob/master/src/test/bloom_tests.cpp

79
test/commands.js Normal file
View File

@ -0,0 +1,79 @@
'use strict';
var should = require('chai').should();
var P2P = require('../');
var Commands = P2P.Commands;
var commandData = require('./data/messages.json');
var Data = require('./data/messages');//todo merge with commandData
var bitcore = require('bitcore');
function getPayloadBuffer(messageBuffer) {
return new Buffer(messageBuffer.slice(48), 'hex');
}
describe('P2P Command Builder', function() {
describe('@constructor', function() {
it('should return commands based on default', function() {
// instantiate
var commands = new Commands();
should.exist(commands);
});
it('should return commands with customizations', function() {
// instantiate
var commands = new Commands({
magicNumber: 0xd9b4bef9,
Block: bitcore.Block,
Transaction: bitcore.Transaction
});
should.exist(commands);
});
});
describe('Commands', function() {
var commands = new Commands();
describe('#fromBuffer/#toBuffer round trip for all commands', function() {
Object.keys(commands).forEach(function(command) {
it('should round trip buffers for command: ' + command, function(done) {
var payloadBuffer = getPayloadBuffer(commandData[command].message);
should.exist(commands[command]);
var message = commands[command].fromBuffer(payloadBuffer);
var outputBuffer = message.getPayload();
outputBuffer.toString('hex').should.equal(payloadBuffer.toString('hex'));
outputBuffer.should.deep.equal(payloadBuffer);
var expectedBuffer = new Buffer(commandData[command].message, 'hex');
message.toBuffer().should.deep.equal(expectedBuffer);
done();
});
});
});
describe('version', function() {
it('#fromBuffer works w/o fRelay arg', function() {
var payloadBuffer = getPayloadBuffer(Data.version.messagenofrelay);
var message = commands.version.fromBuffer(payloadBuffer);
message.relay.should.equal(true);
});
it('#relay setting works', function() {
[true,false].forEach(function(relay) {
var message = commands.version.fromObject({relay: relay});
message.relay.should.equal(relay);
var messageBuf = message.getPayload();
var newMessage = commands.version.fromBuffer(messageBuf);
newMessage.relay.should.equal(relay);
});
});
});
});
});

File diff suppressed because one or more lines are too long

96
test/inventory.js Normal file
View File

@ -0,0 +1,96 @@
'use strict';
/*jshint immed: false */
var should = require('chai').should();
var bitcore = require('bitcore');
var P2P = require('../');
var Inventory = P2P.Inventory;
var BufferUtils = bitcore.util.buffer;
var BufferWriter = bitcore.encoding.BufferWriter;
describe('Inventory', function() {
var hash = new Buffer('eb951630aba498b9a0d10f72b5ea9e39d5ff04b03dc2231e662f52057f948aa1', 'hex');
var hashedStr = BufferUtils.reverse(new Buffer(hash, 'hex')).toString('hex');
var inventoryBuffer = new Buffer(
'01000000eb951630aba498b9a0d10f72b5ea9e39d5ff04b03dc2231e662f52057f948aa1',
'hex'
);
describe('@constructor', function() {
it('should create inventory', function() {
var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hash});
should.exist(inventory);
});
it('should error with string hash', function() {
(function() {
var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hashedStr});
should.not.exist(inventory);
}).should.throw('Unexpected hash');
});
});
describe('#forItem', function() {
it('should handle a string hash (reversed)', function() {
var inventory = Inventory.forItem(Inventory.TYPE.TX, hashedStr);
should.exist(inventory);
inventory.hash.should.deep.equal(new Buffer(hash, 'hex'));
});
});
describe('#forBlock', function() {
it('should use correct block type', function() {
var inventory = Inventory.forBlock(hash);
should.exist(inventory);
inventory.type.should.equal(Inventory.TYPE.BLOCK);
});
});
describe('#forFilteredBlock', function() {
it('should use correct filtered block type', function() {
var inventory = Inventory.forFilteredBlock(hash);
should.exist(inventory);
inventory.type.should.equal(Inventory.TYPE.FILTERED_BLOCK);
});
});
describe('#forTransaction', function() {
it('should use correct filtered tx type', function() {
var inventory = Inventory.forTransaction(hash);
should.exist(inventory);
inventory.type.should.equal(Inventory.TYPE.TX);
});
});
describe('#toBuffer', function() {
it('should serialize correctly', function() {
var inventory = Inventory.forTransaction(hash);
var buffer = inventory.toBuffer();
buffer.should.deep.equal(inventoryBuffer);
});
});
describe('#toBufferWriter', function() {
it('should write to a buffer writer', function() {
var bw = new BufferWriter();
var inventory = Inventory.forTransaction(hash);
inventory.toBufferWriter(bw);
bw.concat().should.deep.equal(inventoryBuffer);
});
});
describe('#fromBuffer', function() {
it('should deserialize a buffer', function() {
var inventory = Inventory.fromBuffer(inventoryBuffer);
should.exist(inventory);
inventory.type.should.equal(Inventory.TYPE.TX);
inventory.hash.should.deep.equal(hash);
});
});
});

29
test/message.js Normal file
View File

@ -0,0 +1,29 @@
'use strict';
var should = require('chai').should();
var P2P = require('../');
var Message = P2P.Message;
describe('P2P Message', function() {
describe('@constructor', function() {
it('should construct with magic number and command', function() {
var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'});
message.command.should.equal('command');
message.magicNumber.should.equal(0xd9b4bef9);
});
});
describe('#toBuffer', function() {
it('should serialize to a buffer', function() {
var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'});
message.getPayload = function() {
return new Buffer(0);
};
var buffer = message.toBuffer();
var expectedBuffer = new Buffer('f9beb4d9636f6d6d616e640000000000000000005df6e0e2', 'hex');
buffer.should.deep.equal(expectedBuffer);
});
});
});

View File

@ -1,160 +1,79 @@
'use strict';
var chai = require('chai');
var should = chai.should();
var Buffers = require('buffers');
var bitcore = require('bitcore');
var Data = require('./data/messages');
var P2P = require('../');
var BloomFilter = P2P.BloomFilter;
var messages = P2P.messages;
var Messages = P2P.Messages;
var Networks = bitcore.Networks;
var BufferUtils = bitcore.util.buffer;
var network = Networks.livenet;
var bitcore = require('bitcore');
describe('Messages', function() {
var commands = {
Version: 'version',
VerAck: 'verack',
Inventory: 'inv',
Addresses: 'addr',
Ping: 'ping',
Pong: 'pong',
Alert: 'alert',
Reject: 'reject',
Block: 'block',
MerkleBlock: 'merkleblock',
FilterLoad: 'filterload',
FilterAdd: 'filteradd',
FilterClear: 'filterclear',
GetBlocks: 'getblocks',
GetHeaders: 'getheaders',
GetData: 'getdata',
GetAddresses: 'getaddr',
Headers: 'headers',
Transaction: 'tx',
NotFound: 'notfound'
};
// TODO: add data for these
var noPayload = ['Reject', 'GetBlocks', 'GetHeaders'];
var names = Object.keys(commands);
describe('named', function() {
names.forEach(function(name) {
var command = commands[name];
var data = Data[command.toUpperCase()];
it('should have data for ' + name, function() {
should.exist(data);
});
it('command for name ' + name, function() {
Messages.Message.COMMANDS[command].should.equal(Messages[name]);
});
describe(name, function() {
var message = new Messages[name]();
it('should be able to create instance', function() {
message.command.should.equal(command);
});
it('should be able to serialize the payload', function() {
var payload = message.getPayload();
should.exist(payload);
});
it('should be able to serialize the message', function() {
var buffer = message.serialize(Networks.livenet);
should.exist(buffer);
});
if (noPayload.indexOf(name) === -1) {
it('should be able to parse payload', function() {
var payload = new Buffer(data.payload, 'hex');
var m = new Messages[name]().fromBuffer(payload);
should.exist(m);
});
}
});
});
});
var buildMessage = function(hex) {
var m = Buffers();
m.push(new Buffer(hex, 'hex'));
return m;
};
it('fails with invalid command', function() {
var invalidCommand = 'f9beb4d96d616c6963696f757300000025000000bd5e830c' +
'0102000000ec3995c1bf7269ff728818a65e53af00cbbee6b6eca8ac9ce7bc79d87' +
'7041ed8';
var fails = function() {
Messages.parseMessage(network, buildMessage(invalidCommand));
};
fails.should.throw('Unsupported message command: malicious');
});
it('ignores malformed messages', function() {
var malformed1 = 'd8c4c3d976657273696f6e000000000065000000fc970f1772110' +
'1000100000000000000ba6288540000000001000000000000000000000000000000' +
'0000ffffba8886dceab0010000000000000000000000000000000000ffff0509552' +
'2208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001';
var malformed2 = 'f9beb4d967657464617461000000000089000000d88134740102' +
'0000006308e4a380c949dbad182747b0f7b6a89e874328ca41f37287f74a81b8f84' +
'86d';
var malformed3 = 'f9beb4d967657464617461000000000025000000616263640102' +
'00000069ebcbc34a4f9890da9aea0f773beba883a9afb1ab9ad7647dd4a1cd346c3' +
'728';
[malformed1, malformed2, malformed3].forEach(function(malformed) {
var ret = Messages.parseMessage(network, buildMessage(malformed));
should.not.exist(ret);
describe('@constructor', function() {
it('sets properties correctly', function() {
var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
var messages = new Messages({
magicNumber: magicNumber,
Block: bitcore.Block,
Transaction: bitcore.Transaction
});
should.exist(messages.commands);
messages.magicNumber.should.equal(magicNumber);
// check that commands are mapped as messages
for(var key in messages.commands) {
messages[key].should.equal(messages.commands[key]);
}
});
});
it('Inventory#from family methods work', function() {
var hash = 'eb951630aba498b9a0d10f72b5ea9e39d5ff04b03dc2231e662f52057f948aa1';
[Messages.Inventory, Messages.GetData, Messages.NotFound].forEach(function(clazz) {
var b = clazz.forBlock(hash);
var mb = clazz.forMerkleBlock(hash);
(b instanceof clazz).should.equal(true);
var t = clazz.forTransaction(hash);
(t instanceof clazz).should.equal(true);
clazz.forBlock(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(b);
clazz.forMerkleBlock(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(mb);
clazz.forFilteredBlock(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(mb);
clazz.forTransaction(BufferUtils.reverse(new Buffer(hash, 'hex'))).should.deep.equal(t);
describe('#parseMessage', function() {
it('fails with invalid command', function() {
var invalidCommand = 'f9beb4d96d616c6963696f757300000025000000bd5e830c' +
'0102000000ec3995c1bf7269ff728818a65e53af00cbbee6b6eca8ac9ce7bc79d87' +
'7041ed8';
var fails = function() {
messages.parseMessage(buildMessage(invalidCommand));
};
fails.should.throw('Unsupported message command: malicious');
});
});
it('Version#fromBuffer works w/o fRelay arg', function() {
var messageHex = Data.VERSION_NO_FRELAY.payload;
var message = new Messages.Version()
.fromBuffer(new Buffer(messageHex, 'hex'));
});
it('Version#relay setting works', function() {
[true,false].forEach(function(relay) {
var message = new Messages.Version(null,null,relay);
message.relay.should.equal(relay);
var messageBuf = message.getPayload();
var newMessage = new Messages.Version().fromBuffer(messageBuf)
newMessage.relay.should.equal(relay);
it('ignores malformed messages', function() {
var malformed1 = 'd8c4c3d976657273696f6e000000000065000000fc970f1772110' +
'1000100000000000000ba6288540000000001000000000000000000000000000000' +
'0000ffffba8886dceab0010000000000000000000000000000000000ffff0509552' +
'2208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001';
var malformed2 = 'f9beb4d967657464617461000000000089000000d88134740102' +
'0000006308e4a380c949dbad182747b0f7b6a89e874328ca41f37287f74a81b8f84' +
'86d';
var malformed3 = 'f9beb4d967657464617461000000000025000000616263640102' +
'00000069ebcbc34a4f9890da9aea0f773beba883a9afb1ab9ad7647dd4a1cd346c3' +
'728';
[malformed1, malformed2, malformed3].forEach(function(malformed) {
var ret = messages.parseMessage(buildMessage(malformed));
should.not.exist(ret);
});
});
});
it('FilterLoad#fromBuffer method works', function() {
var testPayload = Data.FILTERLOAD.payload;
var msg = new Messages.FilterLoad().fromBuffer(new Buffer(testPayload, 'hex'));
msg.getPayload().toString('hex').should.equal(testPayload);
describe('#discardUntilNextMessage', function() {
});
it('MerkleBlock#fromBuffer method works', function() {
var testPayload = Data.MERKLEBLOCK.payload;
var msg = new Messages.MerkleBlock().fromBuffer(new Buffer(testPayload, 'hex'));
msg.getPayload().toString('hex').should.equal(testPayload);
describe('#buildFromBuffer', function() {
});
describe('#build/#buildFromObject', function() {
});
});

View File

@ -12,8 +12,8 @@ var fs = require('fs');
var bitcore = require('bitcore');
var _ = bitcore.deps._;
var p2p = require('../');
var Peer = p2p.Peer;
var P2P = require('../');
var Peer = P2P.Peer;
var Networks = bitcore.Networks;
describe('Peer', function() {
@ -52,7 +52,7 @@ describe('Peer', function() {
return stub;
};
peer.on('connect', function() {
dataCallback(fs.readFileSync('./test/connection.log'));
dataCallback(fs.readFileSync('./test/data/connection.log'));
});
var check = function(message) {
received[message.command]++;
@ -77,28 +77,28 @@ describe('Peer', function() {
});
it('should be able to create instance setting a port', function() {
var peer = new Peer('localhost', 8111);
var peer = new Peer({host: 'localhost', port: 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);
var peer = new Peer({host: 'localhost', network: 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);
var peer = new Peer({host: 'localhost', port: 8111, network: 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);
var peer = Peer({host: 'localhost', port: 8111, network: Networks.testnet});
peer.host.should.equal('localhost');
peer.network.should.equal(Networks.testnet);
peer.port.should.equal(8111);
@ -122,17 +122,17 @@ describe('Peer', function() {
});
it('Peer.relay setting set properly', function() {
var peer = new Peer('localhost');
var peer = new Peer({host: 'localhost'});
peer.relay.should.equal(true);
var peer2 = new Peer('localhost', null, null, false);
var peer2 = new Peer({host: 'localhost', relay: false});
peer2.relay.should.equal(false);
var peer3 = new Peer('localhost', null, null, true);
var peer3 = new Peer({host: 'localhost', relay: true});
peer3.relay.should.equal(true);
});
it('Peer.relay setting respected', function() {
[true,false].forEach(function(relay) {
var peer = new Peer('localhost', null, null, relay);
var peer = new Peer({host: 'localhost', relay: relay});
var peerSendMessageStub = sinon.stub(Peer.prototype, 'sendMessage', function(message) {
message.relay.should.equal(relay);
});

View File

@ -10,13 +10,17 @@ var bitcore = require('bitcore');
var P2P = require('../');
var Peer = P2P.Peer;
var MessagesData = require('./data/messages');
var Messages = P2P.Messages;
var messages = P2P.messages;
var Pool = P2P.Pool;
var Networks = bitcore.Networks;
var dns = require('dns');
var sinon = require('sinon');
function getPayloadBuffer(messageBuffer) {
return new Buffer(messageBuffer.slice(48), 'hex');
}
describe('Pool', function() {
it('should be able to create instance', function() {
@ -95,8 +99,8 @@ describe('Pool', function() {
// mock a addr peer event
var peerMessageStub = sinon.stub(Peer.prototype, '_readMessage', function() {
var payload = new Buffer(MessagesData.ADDR.payload, 'hex');
var message = new Messages.Addresses().fromBuffer(payload);
var payloadBuffer = getPayloadBuffer(MessagesData.addr.message);
var message = messages.buildFromBuffer('addr', payloadBuffer);
this.emit(message.command, message);
});
@ -145,8 +149,8 @@ describe('Pool', function() {
// mock a addr peer event
var peerMessageStub = sinon.stub(Peer.prototype, '_readMessage', function() {
var payload = new Buffer(MessagesData.ADDR.payload, 'hex');
var message = new Messages.Addresses().fromBuffer(payload);
var payloadBuffer = getPayloadBuffer(MessagesData.addr.message);
var message = messages.buildFromBuffer('addr', payloadBuffer);
this.emit(message.command, message);
});