diff --git a/lib/messages.js b/lib/messages.js index 4cf3e3b..beb53b1 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -7,6 +7,7 @@ var Buffers = require('buffers'); var Put = require('bufferput'); var util = require('util'); +var _ = require('lodash'); var bitcore = require('bitcore'); @@ -14,6 +15,7 @@ var BlockHeaderModel = bitcore.BlockHeader; var BlockModel = bitcore.Block; var BufferReader = bitcore.encoding.BufferReader; var BufferUtil = bitcore.util.buffer; +var $ = bitcore.util.preconditions; var Hash = bitcore.crypto.Hash; var Random = bitcore.crypto.Random; var TransactionModel = bitcore.Transaction; @@ -281,13 +283,13 @@ Inventory.fromBuffer = function(payload) { 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(); }; @@ -323,7 +325,7 @@ module.exports.GetData = GetData; function Ping(nonce) { this.command = 'ping'; /** - * @desc A random number that should be returned by the peer in a pong message + * @desc A random number that should be returned by the peer in a pong message * @type {number} */ this.nonce = nonce || CONNECTION_NONCE; @@ -351,7 +353,7 @@ module.exports.Ping = Message.COMMANDS.ping = Ping; function Pong(nonce) { this.command = 'pong'; /** - * @desc A random number that must match the one sent in the corresponding `ping` message + * @desc A random number that must match the one sent in the corresponding `ping` message * @type {number} */ this.nonce = nonce || CONNECTION_NONCE; @@ -410,7 +412,10 @@ Addresses.fromBuffer = function(payload) { that.addresses.push({ time: time, services: services, - ip: { v6: ipv6, v4: ipv4 }, + ip: { + v6: ipv6, + v4: ipv4 + }, port: port }); } @@ -530,6 +535,7 @@ function Headers(blockheaders) { util.inherits(Headers, Message); Headers.fromBuffer = function(payload) { + $.checkArgument(payload && payload.length > 0, 'No data found to create Headers message'); var that = new Headers(); var parser = new BufferReader(payload); var count = parser.readVarintNum(); @@ -564,6 +570,7 @@ module.exports.Headers = Message.COMMANDS.headers = Headers; * @param {Block} block */ function Block(block) { + $.checkArgument(_.isUndefined(block) || block instanceof BlockModel); this.command = 'block'; /** @@ -575,13 +582,13 @@ function Block(block) { util.inherits(Block, Message); Block.fromBuffer = function(payload) { - var that = new Block(); - that.block = BlockModel(payload); - return that; + $.checkArgument(BufferUtil.isBuffer(payload)); + var block = BlockModel(payload); + return new Block(block); }; Block.prototype.getPayload = function() { - return this.block.toBuffer(); + return this.block ? this.block.toBuffer() : new Buffer(0); }; module.exports.Block = Message.COMMANDS.block = Block; @@ -593,6 +600,7 @@ module.exports.Block = Message.COMMANDS.block = Block; * @param{Transaction} transaction */ function Transaction(transaction) { + $.checkArgument(_.isUndefined(transaction) || transaction instanceof TransactionModel); this.command = 'tx'; /** * @type {Transaction} @@ -608,7 +616,7 @@ Transaction.fromBuffer = function(payload) { }; Transaction.prototype.getPayload = function() { - return this.transaction.toBuffer(); + return this.transaction ? this.transaction.toBuffer() : new Buffer(0); }; module.exports.Transaction = Message.COMMANDS.tx = Transaction; @@ -643,6 +651,7 @@ util.inherits(GetBlocks, Message); GetBlocks.fromBuffer = function(payload) { var that = new GetBlocks(); var parser = new BufferReader(payload); + $.checkArgument(!parser.finished(), 'No data received in payload'); that.version = parser.readUInt32LE(); var startCount = Math.min(parser.readVarintNum(), 500); @@ -701,6 +710,9 @@ function GetHeaders(starts, stop) { } util.inherits(GetHeaders, GetBlocks); +GetHeaders.fromBuffer = function() { + return new GetHeaders(); +}; module.exports.GetHeaders = Message.COMMANDS.getheaders = GetHeaders; /** @@ -716,7 +728,7 @@ util.inherits(GetMempool, Message); module.exports.GetMempool = Message.COMMANDS.mempool = GetMempool; // TODO: Remove this PATCH (yemel) -Buffers.prototype.skip = function (i) { +Buffers.prototype.skip = function(i) { if (i === 0) return; if (i === this.length) { diff --git a/test/data/messages.json b/test/data/messages.json index cc1fa33..8e89c5f 100644 --- a/test/data/messages.json +++ b/test/data/messages.json @@ -3,6 +3,42 @@ "message": "f9beb4d976657273696f6e000000000065000000fc970f17721101000100000000000000ba62885400000000010000000000000000000000000000000000ffffba8886dceab0010000000000000000000000000000000000ffff05095522208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001", "payload": "721101000100000000000000ba62885400000000010000000000000000000000000000000000ffffba8886dceab0010000000000000000000000000000000000ffff05095522208de7e1c1ef80a1cea70f2f5361746f7368693a302e392e312fa317050001" }, + "ALERT": { + "message": "", + "payload": "" + }, + "REJECT": { + "message": "", + "payload": "" + }, + "GETBLOCKS": { + "message": "", + "payload": "" + }, + "GETDATA": { + "message": "", + "payload": "" + }, + "GETADDR": { + "message": "", + "payload": "" + }, + "GETHEADERS": { + "message": "", + "payload": "" + }, + "HEADERS": { + "message": "", + "payload": "" + }, + "TX": { + "message": "", + "payload": "01000000015884e5db9de218238671572340b207ee85b628074e7e467096c267266baf77a4000000006a473044022013fa3089327b50263029265572ae1b022a91d10ac80eb4f32f291c914533670b02200d8a5ed5f62634a7e1a0dc9188a3cc460a986267ae4d58faf50c79105431327501210223078d2942df62c45621d209fab84ea9a7a23346201b7727b9b45a29c4e76f5effffffff0150690f00000000001976a9147821c0a3768aa9d1a37e16cf76002aef5373f1a888ac00000000" + }, + "BLOCK": { + "message": "", + "payload": "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000" + }, "VERACK": { "message": "f9beb4d976657261636b000000000000000000005df6e0e2", "payload": "" @@ -18,5 +54,9 @@ "PING": { "message": "f9beb4d9706f6e67000000000000000008000000c6466f1e6b86480ae969867c", "payload": "6b86480ae969867c" + }, + "PONG": { + "message": "f9beb4d9706f6e67000000000000000008000000c6466f1e6b86480ae969867c", + "payload": "6b86480ae969867c" } } diff --git a/test/messages.js b/test/messages.js index 3b5de1e..df57655 100644 --- a/test/messages.js +++ b/test/messages.js @@ -41,326 +41,57 @@ describe('Messages', function() { }); }); - var name = 'VerAck'; - describe(name, function() { - var message = new Messages[name](); - it('should be able to create instance', function() { - message.command.should.equal(name.toLowerCase()); - }); + var commands = { + VerAck: 'verack', + Inventory: 'inv', + Addresses: 'addr', + Ping: 'ping', + Pong: 'pong', + Alert: 'alert', + Reject: 'reject', + Block: 'block', + GetBlocks: 'getblocks', + GetHeaders: 'getheaders', + GetData: 'getdata', + GetAddresses: 'getaddr', + Headers: 'headers', + Transaction: 'tx' + }; + // TODO: add data for these + var noPayload = ['Alert', 'Reject', 'GetBlocks', 'GetHeaders', 'GetData', 'Headers']; + var names = Object.keys(commands); + describe.only('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); + }); + 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 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); - }); + it('should be able to serialize the message', function() { + var buffer = message.serialize(Networks.livenet); + should.exist(buffer); + }); - it('should be able to parse payload', function() { - var payload = new Buffer(Data[name.toUpperCase()].payload, 'hex'); - var m = Messages[name].fromBuffer(payload); - should.exist(m); + if (noPayload.indexOf(name) === -1) { + it('should be able to parse payload', function() { + var payload = new Buffer(data.payload, 'hex'); + var m = Messages[name].fromBuffer(payload); + should.exist(m); + }); + } + }); }); }); - 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'); - var m = Messages.Inventory.fromBuffer(payload); - should.exist(m); - }); - }); - - 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'); - var m = Messages.Addresses.fromBuffer(payload); - should.exist(m); - }); - }); - - /* - Data.forEach(function(x) { - console.log(x); - }); - */ - - 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'); - var m = Messages.Ping.fromBuffer(payload); - should.exist(m); - }); - }); - - 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'); - var m = Messages.Pong.fromBuffer(payload); - should.exist(m); - }); - }); - - 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 = '0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000'; - 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); - }); - }); });