Merge pull request #62 from braydonf/bug/transactions-message

Transaction Message Version Bug
This commit is contained in:
Manuel Aráoz 2015-04-01 16:54:24 -03:00
commit 89924bb99c
25 changed files with 293 additions and 142 deletions

View File

@ -48,7 +48,7 @@ function builder(options) {
headers: 'Headers',
notfound: 'NotFound',
inv: 'Inventory',
addr: 'Address',
addr: 'Addresses',
alert: 'Alert',
reject: 'Reject',
merkleblock: 'MerkleBlock',
@ -68,17 +68,7 @@ function builder(options) {
var Command = require('./commands/' + key);
exported.commands[key] = function(obj) {
if (!obj) {
obj = {};
}
// pass factory options
obj.Block = options.Block;
obj.BlockHeader = options.BlockHeader;
obj.Transaction = options.Transaction;
obj.MerkleBlock = options.MerkleBlock;
obj.magicNumber = options.magicNumber;
obj.version = options.protocolVersion;
return new Command(obj);
return new Command(obj, options);
};
exported.commands[key]._constructor = Command;

View File

@ -4,21 +4,31 @@ var Message = require('../message');
var inherits = require('util').inherits;
var bitcore = require('bitcore');
var utils = require('../utils');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
/**
* @param {Array=} arg - An array of addrs
* @param {Object=} options
* @param {Array=} options.addresses - An array of addrs
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function AddrMessage(options) {
function AddrMessage(arg, options) {
Message.call(this, options);
this.command = 'addr';
this.magicNumber = options.magicNumber;
this.addresses = options.addresses;
$.checkArgument(
_.isUndefined(arg) ||
(Array.isArray(arg) &&
!_.isUndefined(arg[0].services) &&
!_.isUndefined(arg[0].ip) &&
!_.isUndefined(arg[0].port)),
'First argument is expected to be an array of addrs'
);
this.addresses = arg;
}
inherits(AddrMessage, Message);

View File

@ -8,20 +8,23 @@ var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
/**
* @param {Object=} options
* @param {Buffer=} options.payload
* @param {Buffer=} options.signature
* @param {Object=} arg
* @param {Buffer=} arg.payload
* @param {Buffer=} arg.signature
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function AlertMessage(options) {
function AlertMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'alert';
this.payload = options.payload || new Buffer(32);
this.signature = options.signature || new Buffer(32);
if (!arg) {
arg = {};
}
this.payload = arg.payload || new Buffer(32);
this.signature = arg.signature || new Buffer(32);
}
inherits(AlertMessage, Message);

View File

@ -2,29 +2,28 @@
var Message = require('../message');
var inherits = require('util').inherits;
var bitcore = require('bitcore');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
/**
* @param {Object|Block=} options - If is an instance of Block will use as options.block
* @param {Block=} options.block - An instance of a Block
* @param {Block=} arg - An instance of a Block
* @param {Object} options
* @param {Number} options.magicNumber
* @param {Function} options.Block - A block constructor
* @extends Message
* @constructor
*/
function BlockMessage(options) {
function BlockMessage(arg, options) {
Message.call(this, options);
this.Block = options.Block;
this.command = 'block';
this.magicNumber = options.magicNumber;
var block;
if (options instanceof this.Block) {
block = options;
} else {
block = options.block;
}
this.block = block;
$.checkArgument(
_.isUndefined(arg) || arg instanceof this.Block,
'An instance of Block or undefined is expected'
);
this.block = arg;
}
inherits(BlockMessage, Message);

View File

@ -8,20 +8,25 @@ var BufferUtil = bitcore.util.buffer;
var BufferWriter = bitcore.encoding.BufferWriter;
var BufferReader = bitcore.encoding.BufferReader;
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
/**
* Request peer to add data to a bloom filter already set by 'filterload'
* @param {Buffer=} data - Array of bytes representing bloom filter data
* @param {Object=} options
* @param {Buffer=} options.data - Array of bytes representing bloom filter data
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function FilteraddMessage(options) {
function FilteraddMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'filteradd';
this.data = options.data || BufferUtil.EMPTY_BUFFER;
$.checkArgument(
_.isUndefined(arg) || BufferUtil.isBuffer(arg),
'First argument is expected to be a Buffer or undefined'
);
this.data = arg || BufferUtil.EMPTY_BUFFER;
}
inherits(FilteraddMessage, Message);

View File

@ -11,7 +11,7 @@ var BufferUtil = bitcore.util.buffer;
* @param {Number} options.magicNumber
* @constructor
*/
function FilterclearMessage(options) {
function FilterclearMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'filterclear';

View File

@ -10,20 +10,21 @@ var _ = bitcore.deps._;
/**
* Request peer to send inv messages based on a bloom filter
* @param {BloomFilter=} options.filter - An instance of BloomFilter
* @param {BloomFilter=} arg - An instance of BloomFilter
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function FilterloadMessage(options) {
function FilterloadMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'filterload';
$.checkArgument(
_.isUndefined(options.filter) || options.filter instanceof BloomFilter,
_.isUndefined(arg) || arg instanceof BloomFilter,
'An instance of BloomFilter or undefined is expected'
);
this.filter = options.filter;
this.filter = arg;
}
inherits(FilterloadMessage, Message);

View File

@ -8,10 +8,11 @@ var BufferUtil = bitcore.util.buffer;
/**
* Request information about active peers
* @extends Message
* @param {Object} options
* @param {Number} options.magicNumber
* @constructor
*/
function GetaddrMessage(options) {
function GetaddrMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'getaddr';

View File

@ -12,22 +12,25 @@ var $ = bitcore.util.preconditions;
* 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 {Object=} options
* @param {Array=} options.starts - Array of buffers or strings with the starting block hashes
* @param {Buffer=} options.stop - Hash of the last block
* @param {Object=} arg
* @param {Array=} arg.starts - Array of buffers or strings with the starting block hashes
* @param {Buffer=} arg.stop - Hash of the last block
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function GetblocksMessage(options) {
function GetblocksMessage(arg, options) {
Message.call(this, options);
this.command = 'getblocks';
this.version = options.version;
this.version = options.protocolVersion;
this.magicNumber = options.magicNumber;
options = utils.sanitizeStartStop(options);
this.starts = options.starts;
this.stop = options.stop;
if (!arg) {
arg = {};
}
arg = utils.sanitizeStartStop(arg);
this.starts = arg.starts;
this.stop = arg.stop;
}
inherits(GetblocksMessage, Message);

View File

@ -15,20 +15,12 @@ var _ = bitcore.deps._;
* @extends Message
* @constructor
*/
function GetdataMessage(options) {
function GetdataMessage(arg, options) {
Message.call(this, options);
this.command = 'getdata';
this.magicNumber = options.magicNumber;
var inventory;
if (_.isArray(options)) {
inventory = options;
} else {
inventory = options.inventory;
}
utils.checkInventory(inventory);
this.inventory = inventory;
utils.checkInventory(arg);
this.inventory = arg;
}
inherits(GetdataMessage, Message);

View File

@ -19,15 +19,17 @@ var $ = bitcore.util.preconditions;
* @extends Message
* @constructor
*/
function GetheadersMessage(options) {
function GetheadersMessage(arg, options) {
Message.call(this, options);
this.command = 'getheaders';
this.version = options.version;
this.version = options.protocolVersion;
this.magicNumber = options.magicNumber;
options = utils.sanitizeStartStop(options);
this.starts = options.starts;
this.stop = options.stop;
if (!arg) {
arg = {};
}
arg = utils.sanitizeStartStop(arg);
this.starts = arg.starts;
this.stop = arg.stop;
}
inherits(GetheadersMessage, Message);

View File

@ -6,11 +6,13 @@ var bitcore = require('bitcore');
var utils = require('../utils');
var BufferReader = bitcore.encoding.BufferReader;
var BufferWriter = bitcore.encoding.BufferWriter;
var _ = bitcore.deps._;
var $ = bitcore.util.preconditions;
/**
* Sent in response to a `getheaders` message. It contains information about
* block headers.
* @param {Array} arg - An array of BlockHeader instances
* @param {Object=} options
* @param {Array=} options.headers - array of block headers
* @param {Number} options.magicNumber
@ -18,12 +20,16 @@ var $ = bitcore.util.preconditions;
* @extends Message
* @constructor
*/
function HeadersMessage(options) {
function HeadersMessage(arg, options) {
Message.call(this, options);
this.BlockHeader = options.BlockHeader;
this.magicNumber = options.magicNumber;
this.command = 'headers';
this.headers = options.headers;
$.checkArgument(
_.isUndefined(arg) || (Array.isArray(arg) && arg[0] instanceof this.BlockHeader),
'First argument is expected to be an array of BlockHeader instances'
);
this.headers = arg;
}
inherits(HeadersMessage, Message);

View File

@ -9,25 +9,19 @@ var BufferWriter = bitcore.encoding.BufferWriter;
var _ = bitcore.deps._;
/**
* @param {Object|Array=} - options - If options is an array will use as "inventory"
* @param {Array=} arg - An array of inventory
* @param {Object} options
* @param {Array=} options.inventory - An array of inventory items
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function InvMessage(options) {
function InvMessage(arg, options) {
Message.call(this, options);
this.command = 'inv';
this.magicNumber = options.magicNumber;
var inventory;
if (_.isArray(options)) {
inventory = options;
} else {
inventory = options.inventory;
}
utils.checkInventory(inventory);
this.inventory = inventory;
utils.checkInventory(arg);
this.inventory = arg;
}
inherits(InvMessage, Message);

View File

@ -9,11 +9,12 @@ var BufferUtil = bitcore.util.buffer;
* The mempool message sends a request to a node asking for information about
* transactions it has verified but which have not yet confirmed.
* @see https://en.bitcoin.it/wiki/Protocol_documentation#mempool
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function MempoolMessage(options) {
function MempoolMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'mempool';

View File

@ -10,23 +10,23 @@ var _ = bitcore.deps._;
/**
* Contains information about a MerkleBlock
* @see https://en.bitcoin.it/wiki/Protocol_documentation
* @param {MerkleBlock} arg - An instance of MerkleBlock
* @param {Object=} options
* @param {MerkleBlock=} options.merkleBlock
* @param {Number} options.magicNumber
* @param {Function} options.MerkleBlock - a MerkleBlock constructor
* @extends Message
* @constructor
*/
function MerkleblockMessage(options) {
function MerkleblockMessage(arg, options) {
Message.call(this, options);
this.MerkleBlock = options.MerkleBlock; // constructor
this.magicNumber = options.magicNumber;
this.command = 'merkleblock';
$.checkArgument(
_.isUndefined(options.merkleBlock) || options.merkleBlock instanceof this.MerkleBlock,
_.isUndefined(arg) || arg instanceof this.MerkleBlock,
'An instance of MerkleBlock or undefined is expected'
);
this.merkleBlock = options.merkleBlock;
this.merkleBlock = arg;
}
inherits(MerkleblockMessage, Message);

View File

@ -9,25 +9,19 @@ var BufferWriter = bitcore.encoding.BufferWriter;
var _ = bitcore.deps._;
/**
* @param {Object|Array=} options - If options is an array will use as "inventory"
* @param {Array} arg - An array of inventory
* @param {Object} options
* @param {Array=} options.inventory - An array of inventory items
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function NotfoundMessage(options) {
function NotfoundMessage(arg, options) {
Message.call(this, options);
this.command = 'notfound';
this.magicNumber = options.magicNumber;
var inventory;
if (_.isArray(options)) {
inventory = options;
} else {
inventory = options.inventory;
}
utils.checkInventory(inventory);
this.inventory = inventory;
utils.checkInventory(arg);
this.inventory = arg;
}
inherits(NotfoundMessage, Message);

View File

@ -4,21 +4,28 @@ var Message = require('../message');
var inherits = require('util').inherits;
var bitcore = require('bitcore');
var utils = require('../utils');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var BufferUtil = bitcore.util.buffer;
var BufferReader = bitcore.encoding.BufferReader;
/**
* A message to confirm that a connection is still valid.
* @param {Number} arg - A nonce for the Ping message
* @param {Object=} options
* @param {Buffer=} options.nonce
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function PingMessage(options) {
function PingMessage(arg, options) {
Message.call(this, options);
this.command = 'ping';
this.magicNumber = options.magicNumber;
this.nonce = options.nonce || utils.getNonce();
$.checkArgument(
_.isUndefined(arg) || (BufferUtil.isBuffer(arg) && arg.length === 8),
'First argument is expected to be an 8 byte buffer'
);
this.nonce = arg || utils.getNonce();
}
inherits(PingMessage, Message);

View File

@ -4,21 +4,28 @@ var Message = require('../message');
var inherits = require('util').inherits;
var bitcore = require('bitcore');
var utils = require('../utils');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var BufferUtil = bitcore.util.buffer;
var BufferReader = bitcore.encoding.BufferReader;
/**
* A message in response to a ping message.
* @param {Number} arg - A nonce for the Pong message
* @param {Object=} options
* @param {Buffer=} options.nonce
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function PongMessage(options) {
function PongMessage(arg, options) {
Message.call(this, options);
this.command = 'pong';
this.magicNumber = options.magicNumber;
this.nonce = options.nonce;
$.checkArgument(
_.isUndefined(arg) || (BufferUtil.isBuffer(arg) && arg.length === 8),
'First argument is expected to be an 8 byte buffer'
);
this.nonce = arg || utils.getNonce();
}
inherits(PongMessage, Message);

View File

@ -6,7 +6,7 @@ var bitcore = require('bitcore');
var BufferUtil = bitcore.util.buffer;
// todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject
function RejectMessage(options) {
function RejectMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'reject';

View File

@ -2,28 +2,30 @@
var Message = require('../message');
var inherits = require('util').inherits;
var bitcore = require('bitcore');
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
/**
* @param {Object|Transaction=} options - If is an instance of Transaction will use as options.transaction
* @param {Transaction=} options.transaction - An instance of a Transaction
* @param {Transaction=} arg - An instance of Transaction
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function TransactionMessage(options) {
function TransactionMessage(arg, options) {
Message.call(this, options);
this.command = 'tx';
this.magicNumber = options.magicNumber;
this.Transaction = options.Transaction;
var transaction;
if(options instanceof this.Transaction) {
transaction = options;
} else {
transaction = options.transaction;
$.checkArgument(
_.isUndefined(arg) || arg instanceof this.Transaction,
'An instance of Transaction or undefined is expected'
);
this.transaction = arg;
if (!this.transaction) {
this.transaction = new this.Transaction();
}
this.transaction = transaction;
}
inherits(TransactionMessage, Message);

View File

@ -11,7 +11,7 @@ var BufferUtil = bitcore.util.buffer;
* @extends Message
* @constructor
*/
function VerackMessage(options) {
function VerackMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'verack';

View File

@ -5,7 +5,6 @@ var inherits = require('util').inherits;
var bitcore = require('bitcore');
var BufferWriter = bitcore.encoding.BufferWriter;
var BufferReader = bitcore.encoding.BufferReader;
var _ = bitcore.deps._;
var BN = bitcore.crypto.BN;
var utils = require('../utils');
@ -17,29 +16,32 @@ var packageInfo = require('../../../package.json');
* communication is possible until both peers have exchanged their versions.
*
* @see https://en.bitcoin.it/wiki/Protocol_documentation#version
* @param {Object=} obj - properties for the version
* @param {Buffer=} obj.nonce - a random 8 byte buffer
* @param {String=} obj.subversion - version of the client
* @param {BN=} obj.services
* @param {Date=} obj.timestamp
* @param {Number=} obj.startHeight
* @param {Number} obj.magicNumber
* @param {Object=} arg - properties for the version message
* @param {Buffer=} arg.nonce - a random 8 byte buffer
* @param {String=} arg.subversion - version of the client
* @param {BN=} arg.services
* @param {Date=} arg.timestamp
* @param {Number=} arg.startHeight
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function VersionMessage(obj) {
function VersionMessage(arg, options) {
/* jshint maxcomplexity: 10 */
Message.call(this, obj);
if (!arg) {
arg = {};
}
Message.call(this, options);
this.command = 'version';
_.assign(this, obj);
this.magicNumber = obj.magicNumber;
this.version = obj.version;
this.nonce = this.nonce || utils.getNonce();
this.services = this.services || new BN(1, 10);
this.timestamp = this.timestamp || new Date();
this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/';
this.startHeight = this.startHeight || 0;
this.relay = this.relay === false ? false : true;
this.magicNumber = options.magicNumber;
this.version = arg.version || options.protocolVersion;
this.nonce = arg.nonce || utils.getNonce();
this.services = arg.services || new BN(1, 10);
this.timestamp = arg.timestamp || new Date();
this.subversion = arg.subversion || '/bitcore:' + packageInfo.version + '/';
this.startHeight = arg.startHeight || 0;
this.relay = arg.relay === false ? false : true;
}
inherits(VersionMessage, Message);

View File

@ -7,12 +7,12 @@ var _ = bitcore.deps._;
var utils;
module.exports = utils = {
checkInventory: function(inventory) {
checkInventory: function(arg) {
$.checkArgument(
_.isUndefined(inventory) ||
inventory.length === 0 ||
(!_.isUndefined(inventory[0].type) && !_.isUndefined(inventory[0].hash)),
'Inventory must be an array of inventory objects'
_.isUndefined(arg) ||
(Array.isArray(arg) && arg.length === 0) ||
(Array.isArray(arg) && !_.isUndefined(arg[0].type) && !_.isUndefined(arg[0].hash)),
'Argument is expected to be an array of inventory objects'
);
},
checkFinished: function checkFinished(parser) {

View File

@ -208,7 +208,7 @@ Peer.prototype._sendVersion = function() {
* Send a PONG message to the remote peer.
*/
Peer.prototype._sendPong = function(nonce) {
var message = this.messages.Pong({nonce: nonce});
var message = this.messages.Pong(nonce);
this.sendMessage(message);
};

View File

@ -10,6 +10,38 @@ describe('Command Messages', function() {
var messages = new Messages();
describe('Addr', function() {
it('should error if arg is not an array of addrs', function() {
(function() {
var message = messages.Addresses(['not an addr']);
}).should.throw('First argument is expected to be an array of addrs');
});
it('should instantiate with an array of addrs', function() {
var message = messages.Addresses([{
ip: {
v4: 'localhost'
},
services: 1,
port: 1234
}]);
});
});
describe('Alert', function() {
it('should accept a transaction instance as an argument', function() {
var message = messages.Alert({
payload: new Buffer('abcdef', 'hex'),
signature: new Buffer('123456', 'hex')
});
message.payload.should.deep.equal(new Buffer('abcdef', 'hex'));
message.signature.should.deep.equal(new Buffer('123456', 'hex'));
});
});
describe('Transaction', function() {
it('should accept a transaction instance as an argument', function() {
@ -18,6 +50,18 @@ describe('Command Messages', function() {
message.transaction.should.be.instanceof(bitcore.Transaction);
});
it('should create a transaction instance', function() {
var message = messages.Transaction();
message.transaction.should.be.instanceof(bitcore.Transaction);
});
it('version should remain the same', function() {
var tx = new bitcore.Transaction();
var version = Number(tx.version);
var message = messages.Transaction(tx);
message.transaction.version.should.equal(version);
});
});
describe('Block', function() {
@ -33,6 +77,60 @@ describe('Command Messages', function() {
});
describe('Pong', function() {
it('should error if nonce is not a buffer', function() {
(function() {
var message = messages.Pong('not a buffer');
}).should.throw('First argument is expected to be an 8 byte buffer');
});
it('should error if nonce buffer has invalid length', function() {
(function() {
var message = messages.Pong(new Buffer(Array(9)));
}).should.throw('First argument is expected to be an 8 byte buffer');
});
it('should set a nonce if not included', function() {
var message = messages.Pong();
should.exist(message.nonce);
message.nonce.length.should.equal(8);
});
});
describe('Ping', function() {
it('should error if nonce is not a buffer', function() {
(function() {
var message = messages.Ping('not a buffer');
}).should.throw('First argument is expected to be an 8 byte buffer');
});
it('should error if nonce buffer has invalid length', function() {
(function() {
var message = messages.Ping(new Buffer(Array(9)));
}).should.throw('First argument is expected to be an 8 byte buffer');
});
it('should set a nonce if not included', function() {
var message = messages.Ping();
should.exist(message.nonce);
message.nonce.length.should.equal(8);
});
});
describe('FilterAdd', function() {
it('should error if arg is not a buffer', function() {
(function() {
var message = messages.FilterAdd('not a buffer');
}).should.throw('First argument is expected to be a Buffer or undefined');
});
});
describe('FilterLoad', function() {
it('should return a null payload', function() {
@ -50,6 +148,22 @@ describe('Command Messages', function() {
});
describe('Inventory', function() {
it('should error if arg is not an array', function() {
(function() {
var message = messages.Inventory({});
}).should.throw('Argument is expected to be an array of inventory objects');
});
it('should not error if arg is an empty array', function() {
var message = messages.Inventory([]);
});
it('should error if arg is not an array of inventory objects', function() {
(function() {
var message = messages.Inventory([Number(0)]);
}).should.throw('Argument is expected to be an array of inventory objects');
});
});
describe('Transaction', function() {
it('should be able to pass a custom Transaction', function(done) {
@ -116,6 +230,24 @@ describe('Command Messages', function() {
});
describe('Headers', function() {
it('should error if arg is not an array', function() {
(function() {
var message = messages.Headers({});
}).should.throw('First argument is expected to be an array');
});
it('should error if arg is an empty array', function() {
(function() {
var message = messages.Headers([]);
}).should.throw('First argument is expected to be an array');
});
it('should error if arg is not an array of BlockHeaders', function() {
(function() {
var message = messages.Headers([Number(0)]);
}).should.throw('First argument is expected to be an array');
});
});
describe('MerkleBlock', function() {
it('should return null buffer for payload', function() {