diff --git a/integration/bitcoind.js b/integration/bitcoind.js index c3aadee..6325622 100644 --- a/integration/bitcoind.js +++ b/integration/bitcoind.js @@ -18,6 +18,7 @@ var Pool = p2p.Pool; var Networks = bitcore.Networks; var Messages = p2p.Messages; var Block = bitcore.Block; +var Transaction = bitcore.Transaction; // config var network = Networks.livenet; @@ -25,6 +26,10 @@ var blockHash = { 'livenet': '000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9', 'testnet': '0000000058cc069d964711cd25083c0a709f4df2b34c8ff9302ce71fe5b45786' }; +var txHash = { + 'livenet': '22231e8219a0617a0ded618b5dc713fdf9b0db8ebd5bb3322d3011a703119d3b', + 'testnet': '22231e8219a0617a0ded618b5dc713fdf9b0db8ebd5bb3322d3011a703119d3b' +}; // These tests require a running bitcoind instance describe('Integration with ' + network.name + ' bitcoind', function() { @@ -126,15 +131,25 @@ describe('Integration with ' + network.name + ' bitcoind', function() { }); it('can request block data', function(cb) { connect(function(peer) { - peer.on('block', function(message) { + peer.once('block', function(message) { (message.block instanceof Block).should.equal(true); cb(); }); - // TODO: replace this for a new Messages.GetData.forTransaction(hash) - var message = new Messages.GetData([{ - type: Messages.Inventory.TYPE.BLOCK, - hash: BufferUtil.reverse(new Buffer(blockHash[network.name], 'hex')) - }]); + // TODO: replace this for a new Messages.GetData.forBlock(hash) + var message = Messages.GetData.forBlock(blockHash[network.name]); + peer.sendMessage(message); + }); + }); + it('can handle request tx data not found', function(cb) { + connect(function(peer) { + var hash = 'e2dfb8afe1575bfacae1a0b4afc49af7ddda69285857267bae0e22be15f74a3a'; + var expected = Messages.NotFound.forTransaction(hash); + peer.once('notfound', function(message) { + (message instanceof Messages.NotFound).should.equal(true); + message.should.deep.equal(expected); + cb(); + }); + var message = Messages.GetData.forTransaction(hash); peer.sendMessage(message); }); }); diff --git a/lib/messages.js b/lib/messages.js index 456b3aa..89cc4b0 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -19,7 +19,6 @@ var $ = bitcore.util.preconditions; var Hash = bitcore.crypto.Hash; var Random = bitcore.crypto.Random; var TransactionModel = bitcore.Transaction; -var errors = bitcore.Errors; var CONNECTION_NONCE = Random.getPseudoRandomBuffer(8); var PROTOCOL_VERSION = 70000; @@ -130,7 +129,7 @@ Message.buildMessage = function(command, payload) { */ Message.prototype.fromBuffer = function(payload) { /* jshint unused: false */ - throw new errors.NotImplemented(); + return this; }; /** @@ -264,6 +263,13 @@ module.exports.Version = Message.COMMANDS.version = Version; * @param{Array} inventory - reported elements */ function Inventory(inventory) { + $.checkArgument(_.isUndefined(inventory) || + _.isArray(inventory), 'Inventory for ' + + this.constructor.name + ' must be an array of objects'); + $.checkArgument(_.isUndefined(inventory) || + inventory.length === 0 || + (inventory[0] && !_.isUndefined(inventory[0].type) && !_.isUndefined(inventory[0].hash)), + 'Inventory for ' + this.constructor.name + ' must be an array of objects'); this.command = 'inv'; /** * @name P2P.Message.Inventory.inventory @@ -287,16 +293,26 @@ Inventory.TYPE_NAME = [ 'FILTERED_BLOCK' ]; +Inventory.forItem = function(type, hash) { + $.checkArgument(hash); + if (_.isString(hash)) { + hash = new Buffer(hash, 'hex'); + hash = BufferUtil.reverse(hash); + } + return { + type: type, + typeName: Inventory.TYPE_NAME[type], + hash: hash + }; +}; + Inventory.prototype.fromBuffer = function(payload) { var parser = new BufferReader(payload); var count = parser.readVarintNum(); for (var i = 0; i < count; i++) { var type = parser.readUInt32LE(); - this.inventory.push({ - type: type, - typeName: Inventory.TYPE_NAME[type], - hash: parser.read(32) - }); + var hash = parser.read(32); + this.inventory.push(Inventory.forItem(type, hash)); } return this; @@ -314,8 +330,32 @@ Inventory.prototype.getPayload = function() { return put.buffer(); }; +var creatorForItem = function(clazz, type) { + return function(hash) { + return new clazz([Inventory.forItem(type, hash)]); + }; +}; + module.exports.Inventory = Message.COMMANDS.inv = Inventory; +/** + * notfound is a response to a getdata, sent if any requested data + * items could not be relayed, for example, because the requested + * transaction was not in the memory pool or relay set. + * + * (from bitcoin's protocol spec) + * + * @name P2P.Message.NotFound + * @param{Array} inventory - not found elements + */ +function NotFound(inventory) { + Inventory.call(this, inventory); + this.command = 'notfound'; +} + +util.inherits(NotFound, Inventory); +module.exports.NotFound = Message.COMMANDS.notfound = NotFound; + /** * getdata is used in response to inv, to retrieve the content of a specific * object, and is usually sent after receiving an inv packet, after filtering @@ -324,22 +364,18 @@ module.exports.Inventory = Message.COMMANDS.inv = Inventory; * chain is not allowed to avoid having clients start to depend on nodes having * full transaction indexes (which modern nodes do not). * - * (taken from bitcoin's protocol spec) + * (from bitcoin's protocol spec) * * @name P2P.Message.GetData * @param{Array} inventory - requested elements */ function GetData(inventory) { - $.checkArgument(_.isUndefined(inventory) || - _.isArray(inventory), 'Inventory for GetData must be an array of objects'); - $.checkArgument(_.isUndefined(inventory) || - inventory.length === 0 || - (inventory[0] && !_.isUndefined(inventory[0].type) && !_.isUndefined(inventory[0].hash)), - 'Inventory for GetData must be an array of objects'); + Inventory.call(this, inventory); this.command = 'getdata'; this.inventory = inventory || []; } + util.inherits(GetData, Inventory); module.exports.GetData = Message.COMMANDS.getdata = GetData; @@ -480,9 +516,6 @@ function GetAddresses() { } util.inherits(GetAddresses, Message); -GetAddresses.prototype.fromBuffer = function() { - return new GetAddresses(); -}; module.exports.GetAddresses = Message.COMMANDS.getaddr = GetAddresses; /** @@ -495,9 +528,6 @@ function VerAck() { } util.inherits(VerAck, Message); -VerAck.prototype.fromBuffer = function() { - return new VerAck(); -}; module.exports.VerAck = Message.COMMANDS.verack = VerAck; /** @@ -737,9 +767,6 @@ function GetHeaders(starts, stop) { } util.inherits(GetHeaders, GetBlocks); -GetHeaders.prototype.fromBuffer = function() { - return new GetHeaders(); -}; module.exports.GetHeaders = Message.COMMANDS.getheaders = GetHeaders; /** @@ -769,3 +796,10 @@ Buffers.prototype.skip = function(i) { this.buffers[0] = new Buffer(this.buffers[0].slice(pos.offset)); this.length -= i; }; + + + +[Inventory, GetData, NotFound].forEach(function(clazz) { + clazz.forBlock = creatorForItem(clazz, Inventory.TYPE.BLOCK); + clazz.forTransaction = creatorForItem(clazz, Inventory.TYPE.TX); +});