Skip to content

Commit

Permalink
Improved API:
Browse files Browse the repository at this point in the history
- Renamed "Commands" to "builder"
- "Messages.parseMessage" to "Messages.parseBuffer"
- Changed to use private methods for "discardUntilNextMessage" and "buildFromBuffer"
- Cleaned up tests
  • Loading branch information
Braydon Fuller committed Mar 12, 2015
1 parent e8f0725 commit 11bee8b
Show file tree
Hide file tree
Showing 10 changed files with 88 additions and 84 deletions.
20 changes: 17 additions & 3 deletions lib/messages/commands.js → lib/messages/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ var Put = require('bufferput'); //todo remove
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;

function Commands(options) {
function builder(options) {
/* jshint maxstatements: 150 */
/* jshint maxcomplexity: 10 */

Expand All @@ -33,6 +33,20 @@ function Commands(options) {

var commands = {};

var exported = {
constructors: {
Block: Block,
BlockHeader: BlockHeader,
Transaction: Transaction,
MerkleBlock: MerkleBlock
},
defaults: {
protocolVersion: protocolVersion,
magicNumber: magicNumber
},
commands: commands
};

/* shared */

function checkFinished(parser) {
Expand Down Expand Up @@ -890,8 +904,8 @@ function Commands(options) {
return BufferUtil.EMPTY_BUFFER;
};

return commands;
return exported;

}

module.exports = Commands;
module.exports = builder;
33 changes: 13 additions & 20 deletions lib/messages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,34 +5,31 @@ var BufferUtil = bitcore.util.buffer;
var Hash = bitcore.crypto.Hash;

function Messages(options) {
this.commands = new Messages.Commands(options);

this.builder = Messages.builder(options);
if (!options) {
options = {};
}

// map command messages
for (var key in this.commands) {
this[key] = this.commands[key];
}

var defaultMagicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
this.magicNumber = options.magicNumber || defaultMagicNumber;
}

Messages.MINIMUM_LENGTH = 20;
Messages.PAYLOAD_START = 16;
Messages.Message = require('./message');
Messages.Commands = require('./commands');
Messages.builder = require('./builder');

Messages.prototype.build = Messages.prototype.buildFromObject = function(command, object) {
return this.builder.commands[command].fromObject(object);
};

Messages.prototype.parseMessage = function(dataBuffer) {
Messages.prototype.parseBuffer = function(dataBuffer) {
/* jshint maxstatements: 18 */
if (dataBuffer.length < Messages.MINIMUM_LENGTH) {
return;
}

// Search the next magic number
if (!this.discardUntilNextMessage(dataBuffer)) return;
if (!this._discardUntilNextMessage(dataBuffer)) return;

var payloadLen = (dataBuffer.get(Messages.PAYLOAD_START)) +
(dataBuffer.get(Messages.PAYLOAD_START + 1) << 8) +
Expand All @@ -56,10 +53,10 @@ Messages.prototype.parseMessage = function(dataBuffer) {

dataBuffer.skip(messageLength);

return this.buildFromBuffer(command, payload);
return this._buildFromBuffer(command, payload);
};

Messages.prototype.discardUntilNextMessage = function(dataBuffer) {
Messages.prototype._discardUntilNextMessage = function(dataBuffer) {
var i = 0;
for (;;) {
// check if it's the beginning of a new message
Expand All @@ -79,15 +76,11 @@ Messages.prototype.discardUntilNextMessage = function(dataBuffer) {
}
};

Messages.prototype.buildFromBuffer = function(command, payload) {
if (!this.commands[command]) {
Messages.prototype._buildFromBuffer = function(command, payload) {
if (!this.builder.commands[command]) {
throw new Error('Unsupported message command: ' + command);
}
return this.commands[command].fromBuffer(payload);
};

Messages.prototype.build = Messages.prototype.buildFromObject = function(command, object) {
return this.commands[command].fromObject(object);
return this.builder.commands[command].fromBuffer(payload);
};

module.exports = Messages;
2 changes: 1 addition & 1 deletion lib/peer.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +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 = this.messages.parseMessage(this.dataBuffer);
var message = this.messages.parseBuffer(this.dataBuffer);
if (message) {
this.emit(message.command, message);
this._readMessage();
Expand Down
8 changes: 4 additions & 4 deletions test/bloomfilter.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,15 @@ function ParseHex(str) {

describe('BloomFilter', function() {

it('BloomFilter#fromBuffer and toBuffer methods work', function() {
it('#fromBuffer and #toBuffer round trip', function() {
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

it('correctly serialize filter with public keys added', function() {
it('serialize filter with public keys added', function() {

var privateKey = bitcore.PrivateKey.fromWIF('5Kg1gnAjaLfKiwhhPpGS3QfRg2m6awQvaj98JCZBZQ5SuS2F15C');
var publicKey = privateKey.toPublicKey();
Expand All @@ -49,7 +49,7 @@ describe('BloomFilter', function() {

});

it('correctly serialize to a buffer', function() {
it('serialize to a buffer', function() {

var filter = BloomFilter.create(3, 0.01, 0, BloomFilter.BLOOM_UPDATE_ALL);

Expand All @@ -68,7 +68,7 @@ describe('BloomFilter', function() {
actual.should.deep.equal(expected);
});

it('correctly deserialize a buffer', function() {
it('deserialize a buffer', function() {

var buffer = new Buffer('03614e9b050000000000000001', 'hex');
var filter = BloomFilter.fromBuffer(buffer);
Expand Down
18 changes: 9 additions & 9 deletions test/inventory.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ describe('Inventory', function() {
);

describe('@constructor', function() {
it('should create inventory', function() {
it('create inventory', function() {
var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hash});
should.exist(inventory);
});

it('should error with string hash', function() {
it('error with string hash', function() {
(function() {
var inventory = new Inventory({type: Inventory.TYPE.TX, hash: hashedStr});
should.not.exist(inventory);
Expand All @@ -35,7 +35,7 @@ describe('Inventory', function() {
});

describe('#forItem', function() {
it('should handle a string hash (reversed)', function() {
it('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'));
Expand All @@ -44,39 +44,39 @@ describe('Inventory', function() {
});

describe('#forBlock', function() {
it('should use correct block type', function() {
it('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() {
it('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() {
it('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() {
it('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() {
it('write to a buffer writer', function() {
var bw = new BufferWriter();
var inventory = Inventory.forTransaction(hash);
inventory.toBufferWriter(bw);
Expand All @@ -85,7 +85,7 @@ describe('Inventory', function() {
});

describe('#fromBuffer', function() {
it('should deserialize a buffer', function() {
it('deserialize a buffer', function() {
var inventory = Inventory.fromBuffer(inventoryBuffer);
should.exist(inventory);
inventory.type.should.equal(Inventory.TYPE.TX);
Expand Down
28 changes: 14 additions & 14 deletions test/messages/commands.js → test/messages/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

var should = require('chai').should();
var P2P = require('../../');
var Commands = P2P.Messages.Commands;
var builder = P2P.Messages.builder;
var commandData = require('../data/messages.json');
var Data = require('../data/messages');//todo merge with commandData
var bitcore = require('bitcore');
Expand All @@ -11,39 +11,39 @@ function getPayloadBuffer(messageBuffer) {
return new Buffer(messageBuffer.slice(48), 'hex');
}

describe('P2P Command Builder', function() {
describe('Messages Builder', function() {

describe('@constructor', function() {

it('should return commands based on default', function() {
// instantiate
var commands = new Commands();
should.exist(commands);
var b = builder();
should.exist(b);
});

it('should return commands with customizations', function() {
// instantiate
var commands = new Commands({
var b = builder({
magicNumber: 0xd9b4bef9,
Block: bitcore.Block,
Transaction: bitcore.Transaction
});
should.exist(commands);
should.exist(b);
});

});

describe('Commands', function() {

var commands = new Commands();
var b = builder();

describe('#fromBuffer/#toBuffer round trip for all commands', function() {
Object.keys(commands).forEach(function(command) {
Object.keys(b.commands).forEach(function(command) {

it('should round trip buffers for command: ' + command, function(done) {
it(command, function(done) {
var payloadBuffer = getPayloadBuffer(commandData[command].message);
should.exist(commands[command]);
var message = commands[command].fromBuffer(payloadBuffer);
should.exist(b.commands[command]);
var message = b.commands[command].fromBuffer(payloadBuffer);
var outputBuffer = message.getPayload();
outputBuffer.toString('hex').should.equal(payloadBuffer.toString('hex'));
outputBuffer.should.deep.equal(payloadBuffer);
Expand All @@ -58,16 +58,16 @@ describe('P2P Command Builder', function() {
describe('version', function() {
it('#fromBuffer works w/o fRelay arg', function() {
var payloadBuffer = getPayloadBuffer(Data.version.messagenofrelay);
var message = commands.version.fromBuffer(payloadBuffer);
var message = b.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});
var message = b.commands.version.fromObject({relay: relay});
message.relay.should.equal(relay);
var messageBuf = message.getPayload();
var newMessage = commands.version.fromBuffer(messageBuf);
var newMessage = b.commands.version.fromBuffer(messageBuf);
newMessage.relay.should.equal(relay);
});
});
Expand Down
17 changes: 7 additions & 10 deletions test/messages/messages.js → test/messages/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,21 @@ describe('Messages', function() {
Block: bitcore.Block,
Transaction: bitcore.Transaction
});
should.exist(messages.commands);
should.exist(messages.builder.commands);
should.exist(messages.builder.constructors);
messages.builder.constructors.Block.should.equal(bitcore.Block);
messages.builder.constructors.Transaction.should.equal(bitcore.Transaction);
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]);
}

});
});

describe('#parseMessage', function() {
describe('#parseBuffer', function() {
it('fails with invalid command', function() {
var invalidCommand = 'f9beb4d96d616c6963696f757300000025000000bd5e830c' +
'0102000000ec3995c1bf7269ff728818a65e53af00cbbee6b6eca8ac9ce7bc79d87' +
'7041ed8';
var fails = function() {
messages.parseMessage(buildMessage(invalidCommand));
messages.parseBuffer(buildMessage(invalidCommand));
};
fails.should.throw('Unsupported message command: malicious');
});
Expand All @@ -59,7 +56,7 @@ describe('Messages', function() {
'00000069ebcbc34a4f9890da9aea0f773beba883a9afb1ab9ad7647dd4a1cd346c3' +
'728';
[malformed1, malformed2, malformed3].forEach(function(malformed) {
var ret = messages.parseMessage(buildMessage(malformed));
var ret = messages.parseBuffer(buildMessage(malformed));
should.not.exist(ret);
});
});
Expand Down
6 changes: 3 additions & 3 deletions test/messages/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ var should = require('chai').should();
var P2P = require('../../');
var Message = P2P.Messages.Message;

describe('P2P Message', function() {
describe('Message', function() {

describe('@constructor', function() {
it('should construct with magic number and command', function() {
it('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() {
it('serialize to a buffer', function() {
var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'});
message.getPayload = function() {
return new Buffer(0);
Expand Down
Loading

0 comments on commit 11bee8b

Please sign in to comment.