refactor network handling

This commit is contained in:
Manuel Araoz 2015-04-20 15:44:09 -03:00
parent 8313c4df5b
commit f01d6c98fb
31 changed files with 75 additions and 83 deletions

View File

@ -36,10 +36,10 @@ For advanced usage, you can also customize which constructor is used for Block a
var messages = new Messages({Block: MyBlock, Transaction: MyTransaction});
```
And additionally custom network magic:
And additionally custom network:
```javascript
var messages = new Messages({magicNumber: 0x0b120907});
var messages = new Messages({network: Networks.testnet});
```
## List of Messages

View File

@ -22,7 +22,7 @@ var Block = bitcore.Block;
var Transaction = bitcore.Transaction;
// config
var network = Networks.livenet;
var network = process.env.NETWORK === 'testnet'? Networks.testnet: Networks.livenet;
var blockHash = {
'livenet': '000000000000000013413cf2536b491bf0988f52e90c476ffeb701c8bfdb1db9',
'testnet': '0000000058cc069d964711cd25083c0a709f4df2b34c8ff9302ce71fe5b45786'
@ -40,8 +40,12 @@ var txHash = {
describe('Integration with ' + network.name + ' bitcoind', function() {
this.timeout(15000);
var opts = {
host: 'localhost',
network: network.name
};
it('handshakes', function(cb) {
var peer = new Peer('localhost', network);
var peer = new Peer(opts);
peer.once('version', function(m) {
m.version.should.be.above(70000);
m.services.toString().should.equal('1');
@ -57,7 +61,7 @@ describe('Integration with ' + network.name + ' bitcoind', function() {
peer.connect();
});
var connect = function(cb) {
var peer = new Peer('localhost', network);
var peer = new Peer(opts);
peer.once('ready', function() {
cb(peer);
});
@ -164,20 +168,28 @@ describe('Integration with ' + network.name + ' bitcoind', function() {
message.headers.length.should.equal(3);
cb();
});
var message = messages.GetHeaders({starts: from, stop: stop});
var message = messages.GetHeaders({
starts: from,
stop: stop
});
peer.sendMessage(message);
});
});
it('gets blocks', function(cb) {
it.only('gets blocks', function(cb) {
connect(function(peer) {
peer.once('inv', function(message) {
message.command.should.equal('inv');
console.log('LLEGO UN INV', message.inventory.length);
if (message.inventory.length === 2) {
message.inventory[0].type.should.equal(Inventory.TYPE.BLOCK);
message.inventory[1].type.should.equal(Inventory.TYPE.BLOCK);
cb();
}
});
var message = messages.GetBlocks({starts: from, stop: stop});
var message = messages.GetBlocks({
starts: from,
stop: stop
});
peer.sendMessage(message);
});
});

View File

@ -11,8 +11,8 @@ function builder(options) {
options = {};
}
if (!options.magicNumber) {
options.magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
if (!options.network) {
options.network = bitcore.Networks.defaultNetwork;
}
options.Block = options.Block || bitcore.Block;
@ -30,7 +30,7 @@ function builder(options) {
},
defaults: {
protocolVersion: options.protocolVersion,
magicNumber: options.magicNumber
network: options.network
},
inventoryCommands: [
'getdata',

View File

@ -12,14 +12,12 @@ var BufferWriter = bitcore.encoding.BufferWriter;
/**
* @param {Array=} arg - An array of addrs
* @param {Object=} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function AddrMessage(arg, options) {
Message.call(this, options);
this.command = 'addr';
this.magicNumber = options.magicNumber;
$.checkArgument(
_.isUndefined(arg) ||
(Array.isArray(arg) &&

View File

@ -12,13 +12,11 @@ var BufferWriter = bitcore.encoding.BufferWriter;
* @param {Buffer=} arg.payload
* @param {Buffer=} arg.signature
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function AlertMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'alert';
if (!arg) {
arg = {};

View File

@ -9,7 +9,6 @@ var _ = bitcore.deps._;
/**
* @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
@ -18,7 +17,6 @@ function BlockMessage(arg, options) {
Message.call(this, options);
this.Block = options.Block;
this.command = 'block';
this.magicNumber = options.magicNumber;
$.checkArgument(
_.isUndefined(arg) || arg instanceof this.Block,
'An instance of Block or undefined is expected'

View File

@ -14,13 +14,11 @@ 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 {Number} options.magicNumber
* @extends Message
* @constructor
*/
function FilteraddMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'filteradd';
$.checkArgument(
_.isUndefined(arg) || BufferUtil.isBuffer(arg),

View File

@ -8,12 +8,10 @@ var BufferUtil = bitcore.util.buffer;
/**
* Request peer to clear data for a bloom filter
* @extends Message
* @param {Number} options.magicNumber
* @constructor
*/
function FilterclearMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'filterclear';
}
inherits(FilterclearMessage, Message);

View File

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

View File

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

View File

@ -16,7 +16,6 @@ var $ = bitcore.util.preconditions;
* @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
*/
@ -24,7 +23,6 @@ function GetblocksMessage(arg, options) {
Message.call(this, options);
this.command = 'getblocks';
this.version = options.protocolVersion;
this.magicNumber = options.magicNumber;
if (!arg) {
arg = {};
}

View File

@ -11,14 +11,12 @@ var _ = bitcore.deps._;
/**
* @param {Object|Array=} - options - If options is an array will use as "inventory"
* @param {Array=} options.inventory - An array of inventory items
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function GetdataMessage(arg, options) {
Message.call(this, options);
this.command = 'getdata';
this.magicNumber = options.magicNumber;
utils.checkInventory(arg);
this.inventory = arg;
}

View File

@ -15,7 +15,6 @@ var $ = bitcore.util.preconditions;
* @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 {Number} options.magicNumber
* @extends Message
* @constructor
*/
@ -23,7 +22,6 @@ function GetheadersMessage(arg, options) {
Message.call(this, options);
this.command = 'getheaders';
this.version = options.protocolVersion;
this.magicNumber = options.magicNumber;
if (!arg) {
arg = {};
}

View File

@ -15,7 +15,6 @@ var $ = bitcore.util.preconditions;
* @param {Array} arg - An array of BlockHeader instances
* @param {Object=} options
* @param {Array=} options.headers - array of block headers
* @param {Number} options.magicNumber
* @param {Function} options.BlockHeader - a BlockHeader constructor
* @extends Message
* @constructor
@ -23,7 +22,6 @@ var $ = bitcore.util.preconditions;
function HeadersMessage(arg, options) {
Message.call(this, options);
this.BlockHeader = options.BlockHeader;
this.magicNumber = options.magicNumber;
this.command = 'headers';
$.checkArgument(
_.isUndefined(arg) || (Array.isArray(arg) && arg[0] instanceof this.BlockHeader),

View File

@ -12,14 +12,12 @@ var _ = bitcore.deps._;
* @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(arg, options) {
Message.call(this, options);
this.command = 'inv';
this.magicNumber = options.magicNumber;
utils.checkInventory(arg);
this.inventory = arg;
}

View File

@ -10,13 +10,11 @@ var BufferUtil = bitcore.util.buffer;
* 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(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'mempool';
}
inherits(MempoolMessage, Message);

View File

@ -12,7 +12,6 @@ var _ = bitcore.deps._;
* @see https://en.bitcoin.it/wiki/Protocol_documentation
* @param {MerkleBlock} arg - An instance of MerkleBlock
* @param {Object=} options
* @param {Number} options.magicNumber
* @param {Function} options.MerkleBlock - a MerkleBlock constructor
* @extends Message
* @constructor
@ -20,7 +19,6 @@ var _ = bitcore.deps._;
function MerkleblockMessage(arg, options) {
Message.call(this, options);
this.MerkleBlock = options.MerkleBlock; // constructor
this.magicNumber = options.magicNumber;
this.command = 'merkleblock';
$.checkArgument(
_.isUndefined(arg) || arg instanceof this.MerkleBlock,

View File

@ -12,14 +12,12 @@ var _ = bitcore.deps._;
* @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(arg, options) {
Message.call(this, options);
this.command = 'notfound';
this.magicNumber = options.magicNumber;
utils.checkInventory(arg);
this.inventory = arg;
}

View File

@ -13,14 +13,12 @@ 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 {Number} options.magicNumber
* @extends Message
* @constructor
*/
function PingMessage(arg, options) {
Message.call(this, options);
this.command = 'ping';
this.magicNumber = options.magicNumber;
$.checkArgument(
_.isUndefined(arg) || (BufferUtil.isBuffer(arg) && arg.length === 8),
'First argument is expected to be an 8 byte buffer'

View File

@ -13,14 +13,12 @@ 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 {Number} options.magicNumber
* @extends Message
* @constructor
*/
function PongMessage(arg, options) {
Message.call(this, options);
this.command = 'pong';
this.magicNumber = options.magicNumber;
$.checkArgument(
_.isUndefined(arg) || (BufferUtil.isBuffer(arg) && arg.length === 8),
'First argument is expected to be an 8 byte buffer'

View File

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

View File

@ -9,14 +9,12 @@ var _ = bitcore.deps._;
/**
* @param {Transaction=} arg - An instance of Transaction
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function TransactionMessage(arg, options) {
Message.call(this, options);
this.command = 'tx';
this.magicNumber = options.magicNumber;
this.Transaction = options.Transaction;
$.checkArgument(
_.isUndefined(arg) || arg instanceof this.Transaction,

View File

@ -7,13 +7,11 @@ var BufferUtil = bitcore.util.buffer;
/**
* A message in response to a version message.
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
function VerackMessage(arg, options) {
Message.call(this, options);
this.magicNumber = options.magicNumber;
this.command = 'verack';
}
inherits(VerackMessage, Message);

View File

@ -23,7 +23,6 @@ var packageInfo = require('../../../package.json');
* @param {Date=} arg.timestamp
* @param {Number=} arg.startHeight
* @param {Object} options
* @param {Number} options.magicNumber
* @extends Message
* @constructor
*/
@ -34,7 +33,6 @@ function VersionMessage(arg, options) {
}
Message.call(this, options);
this.command = 'version';
this.magicNumber = options.magicNumber;
this.version = arg.version || options.protocolVersion;
this.nonce = arg.nonce || utils.getNonce();
this.services = arg.services || new BN(1, 10);

View File

@ -3,11 +3,12 @@
var bitcore = require('bitcore');
var BufferUtil = bitcore.util.buffer;
var Hash = bitcore.crypto.Hash;
var $ = bitcore.util.preconditions;
/**
* A factory to build Bitcoin protocol messages.
* @param {Object=} options
* @param {Number=} options.magicNumber
* @param {Network=} options.network
* @param {Function=} options.Block - A block constructor
* @param {Function=} options.BlockHeader - A block header constructor
* @param {Function=} options.MerkleBlock - A merkle block constructor
@ -26,8 +27,7 @@ function Messages(options) {
if (!options) {
options = {};
}
var defaultMagicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
this.magicNumber = options.magicNumber || defaultMagicNumber;
this.network = options.network || bitcore.Networks.defaultNetwork;
}
Messages.MINIMUM_LENGTH = 20;
@ -75,11 +75,13 @@ Messages.prototype.parseBuffer = function(dataBuffer) {
};
Messages.prototype._discardUntilNextMessage = function(dataBuffer) {
$.checkArgument(dataBuffer);
$.checkState(this.network, 'network must be set');
var i = 0;
for (;;) {
// check if it's the beginning of a new message
var packageNumber = dataBuffer.slice(0, 4).readUInt32LE(0);
if (packageNumber === this.magicNumber) {
var packageNumber = dataBuffer.slice(0, 4).toString('hex');
if (packageNumber === this.network.networkMagic.toString('hex')) {
dataBuffer.skip(i);
return true;
}

View File

@ -1,6 +1,8 @@
'use strict';
var bitcore = require('bitcore');
var $ = bitcore.util.preconditions;
var Networks = bitcore.Networks;
var BufferWriter = bitcore.encoding.BufferWriter;
var Hash = bitcore.crypto.Hash;
@ -9,12 +11,12 @@ var Hash = bitcore.crypto.Hash;
* `getPayload` method to modify the message payload.
* @param {Object=} options
* @param {String=} options.command
* @param {Number=} options.magicNumber
* @param {Network=} options.network
* @constructor
*/
function Message(options) {
this.command = options.command;
this.magicNumber = options.magicNumber;
this.network = options.network || Networks.defaultNetwork;
}
/**
@ -22,7 +24,7 @@ function Message(options) {
* @constructor
*/
Message.prototype.toBuffer = Message.prototype.serialize = function() {
$.checkState(this.network, 'Need to have a defined network to serialize message');
var commandBuf = new Buffer(Array(12));
commandBuf.write(this.command, 'ascii');
@ -30,7 +32,7 @@ Message.prototype.toBuffer = Message.prototype.serialize = function() {
var checksum = Hash.sha256sha256(payload).slice(0, 4);
var bw = new BufferWriter();
bw.writeUInt32LE(this.magicNumber);
bw.write(this.network.networkMagic);
bw.write(commandBuf);
bw.writeUInt32LE(payload.length);
bw.write(checksum);

View File

@ -64,7 +64,7 @@ function Peer(options) {
}
this.messages = options.messages || new Messages({
magicNumber: this.network.networkMagic.readUInt32LE(0),
network: this.network,
Block: bitcore.Block,
Transaction: bitcore.Transaction
});
@ -191,6 +191,7 @@ Peer.prototype.disconnect = function() {
* @param {Message} message - A message instance
*/
Peer.prototype.sendMessage = function(message) {
console.log('>sending', message.command);
this.socket.write(message.toBuffer());
};
@ -218,6 +219,7 @@ Peer.prototype._sendPong = function(nonce) {
Peer.prototype._readMessage = function() {
var message = this.messages.parseBuffer(this.dataBuffer);
if (message) {
console.log('<read', message.command);
this.emit(message.command, message);
this._readMessage();
}

View File

@ -18,7 +18,7 @@ describe('Messages Builder', function() {
it('should return commands with customizations', function() {
// instantiate
var b = builder({
magicNumber: 0xd9b4bef9,
network: bitcore.Networks.testnet,
Block: bitcore.Block,
Transaction: bitcore.Transaction
});

View File

@ -8,7 +8,7 @@ var P2P = require('../../');
var Messages = P2P.Messages;
var messages = new Messages();
var bitcore = require('bitcore');
var Data = require('../data/messages');//todo merge with commandData
var Data = require('../data/messages'); //todo merge with commandData
var commandData = require('../data/messages.json');
function getPayloadBuffer(messageBuffer) {
@ -25,9 +25,9 @@ describe('Messages', function() {
describe('@constructor', function() {
it('sets properties correctly', function() {
var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
var network = bitcore.Networks.defaultNetwork;
var messages = new Messages({
magicNumber: magicNumber,
network: network,
Block: bitcore.Block,
Transaction: bitcore.Transaction
});
@ -35,17 +35,21 @@ describe('Messages', function() {
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);
messages.network.should.deep.equal(network);
});
it('magicNumber should be unique for each set of messages', function() {
var messages = new Messages({magicNumber: 123456});
var messages2 = new Messages({magicNumber: 987654});
messages.magicNumber.should.equal(123456);
messages2.magicNumber.should.equal(987654);
it('network should be unique for each set of messages', function() {
var messages = new Messages({
network: bitcore.Networks.livenet
});
var messages2 = new Messages({
network: bitcore.Networks.testnet
});
messages.network.should.deep.equal(bitcore.Networks.livenet);
messages2.network.should.deep.equal(bitcore.Networks.testnet);
var message1 = messages.Version();
message1.magicNumber.should.equal(123456);
message1.network.should.deep.equal(bitcore.Networks.livenet);
var message2 = messages2.Version();
message2.magicNumber.should.equal(987654);
message2.network.should.deep.equal(bitcore.Networks.testnet);
});
});
@ -81,14 +85,13 @@ describe('Messages', function() {
});
});
describe('Default Magic Number', function() {
describe('Default Network', function() {
var messages = new Messages();
Object.keys(messages.builder.commandsMap).forEach(function(command) {
var name = messages.builder.commandsMap[command];
it(name, function() {
var message = messages[name]();
var defaultMagic = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
message.magicNumber.should.equal(defaultMagic);
message.network.should.deep.equal(bitcore.Networks.defaultNetwork);
});
});
@ -103,8 +106,10 @@ describe('Messages', function() {
});
it('#relay setting works', function() {
[true,false].forEach(function(relay) {
var message = messages.Version({relay: relay});
[true, false].forEach(function(relay) {
var message = messages.Version({
relay: relay
});
message.relay.should.equal(relay);
var messageBuf = message.getPayload();
var newMessage = messages.Version.fromBuffer(messageBuf);
@ -162,7 +167,8 @@ describe('Messages', function() {
'0102000000ec3995c1bf7269ff728818a65e53af00cbbee6b6eca8ac9ce7bc79d87' +
'7041ed8';
var fails = function() {
messages.parseBuffer(buildMessage(invalidCommand));
var bufs = buildMessage(invalidCommand);
messages.parseBuffer(bufs);
};
fails.should.throw('Unsupported message command: malicious');
});

View File

@ -8,15 +8,23 @@ describe('Message', function() {
describe('@constructor', function() {
it('construct with magic number and command', function() {
var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'});
var message = new Message({
network: {
networkMagic: 0xd9b4bef9
},
command: 'command'
});
should.exist(message);
message.command.should.equal('command');
message.magicNumber.should.equal(0xd9b4bef9);
message.network.networkMagic.should.equal(0xd9b4bef9);
});
});
describe('#toBuffer', function() {
it('serialize to a buffer', function() {
var message = new Message({magicNumber: 0xd9b4bef9, command: 'command'});
var message = new Message({
command: 'command'
});
message.getPayload = function() {
return new Buffer(0);
};

1
test/mocha.opts Normal file
View File

@ -0,0 +1 @@
--recursive