Moved block and version messages and shared utils into seperate files.

This commit is contained in:
Braydon Fuller 2015-03-12 04:34:30 -04:00
parent 608e41de07
commit 6ca2f6982c
4 changed files with 267 additions and 229 deletions

View File

@ -2,7 +2,7 @@
var Message = require('./message');
var BloomFilter = require('../bloomfilter');
var packageInfo = require('../../package.json');
var inherits = require('util').inherits;
var bitcore = require('bitcore');
var BN = bitcore.crypto.BN;
@ -11,6 +11,7 @@ var BufferWriter = bitcore.encoding.BufferWriter;
var BufferUtil = bitcore.util.buffer;
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var utils = require('./utils');
function builder(options) {
/* jshint maxstatements: 150 */
@ -46,192 +47,7 @@ function builder(options) {
commands: commands
};
/* shared */
function checkFinished(parser) {
if(!parser.finished()) {
throw new Error('Data still available after parsing');
}
}
function getNonce() {
return bitcore.crypto.Random.getRandomBuffer(8);
}
function writeIP(ip, bw) {
var words = ip.v6.split(':').map(function(s) {
return new Buffer(s, 'hex');
});
for (var i = 0; i < words.length; i++) {
var word = words[i];
bw.write(word);
}
}
function writeAddr(addr, bw) {
if (_.isUndefined(addr)) {
var pad = new Buffer(Array(26));
bw.write(pad);
return;
}
bw.writeUInt64LEBN(addr.services);
writeIP(addr.ip, bw);
bw.writeUInt16BE(addr.port);
}
function writeInventory(inventory, bw) {
bw.writeVarintNum(inventory.length);
inventory.forEach(function(value) {
bw.writeUInt32LE(value.type);
bw.write(value.hash);
});
}
function parseIP(parser) {
var ipv6 = [];
var ipv4 = [];
for (var a = 0; a < 8; a++) {
var word = parser.read(2);
ipv6.push(word.toString('hex'));
if (a >= 6) {
ipv4.push(word[0]);
ipv4.push(word[1]);
}
}
ipv6 = ipv6.join(':');
ipv4 = ipv4.join('.');
return {
v6: ipv6,
v4: ipv4
};
}
function parseAddr(parser) {
var services = parser.readUInt64LEBN();
var ip = parseIP(parser);
var port = parser.readUInt16BE();
return {
services: services,
ip: ip,
port: port
};
}
function sanitizeStartStop(obj) {
/* jshint maxcomplexity: 10 */
$.checkArgument(_.isUndefined(options.starts) || _.isArray(options.starts));
var starts = obj.starts;
var stop = obj.stop;
if (starts) {
starts = starts.map(function(hash) {
if (_.isString(hash)) {
return BufferUtil.reverse(new Buffer(hash, 'hex'));
} else {
return hash;
}
});
} else {
starts = [];
}
for (var i = 0; i < starts.length; i++) {
if (starts[i].length !== 32) {
throw new Error('Invalid hash ' + i + ' length: ' + starts[i].length);
}
}
stop = obj.stop;
if (_.isString(stop)) {
stop = BufferUtil.reverse(new Buffer(stop, 'hex'));
}
if (!stop) {
stop = BufferUtil.NULL_HASH;
}
obj.starts = starts;
obj.stop = stop;
return obj;
}
/**
* The version message is used on connection creation to advertise
* the type of node. The remote node will respond with its version, and no
* communication is possible until both peers have exchanged their versions.
* By default, bitcore advertises itself as named `bitcore:0.8`.
*
* @param{Object} obj - properties for the version
* @param{String} obj.subversion - version of the client
* @param{Buffer} obj.nonce - a random 8 byte buffer
*/
commands.version = function(obj) {
Message.call(this, obj);
this.command = 'version';
_.assign(this, obj);
this.magicNumber = magicNumber;
this.nonce = this.nonce || getNonce();
this.services = this.services || new BN(1, 10);
this.timestamp = this.timestamp || new Date();
this.version = this.version || protocolVersion;
this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/';
this.startHeight = this.startHeight || 0;
};
inherits(commands.version, Message);
commands.version.fromObject = function(obj) {
return new commands.version(obj);
};
commands.version.fromBuffer = function(payload) {
var parser = new BufferReader(payload);
var obj = {};
obj.version = parser.readUInt32LE();
obj.services = parser.readUInt64LEBN();
obj.timestamp = new Date(parser.readUInt64LEBN().toNumber() * 1000);
obj.addrMe = {
services: parser.readUInt64LEBN(),
ip: parseIP(parser),
port: parser.readUInt16BE()
};
obj.addrYou = {
services: parser.readUInt64LEBN(),
ip: parseIP(parser),
port: parser.readUInt16BE()
};
obj.nonce = parser.read(8);
obj.subversion = parser.readVarLengthBuffer().toString();
obj.startHeight = parser.readUInt32LE();
if(parser.finished()) {
obj.relay = true;
} else {
obj.relay = !!parser.readUInt8();
}
checkFinished(parser);
return commands.version.fromObject(obj);
};
commands.version.prototype.getPayload = function() {
var bw = new BufferWriter();
bw.writeUInt32LE(this.version);
bw.writeUInt64LEBN(this.services);
var timestampBuffer = new Buffer(Array(8));
timestampBuffer.writeUInt32LE(Math.round(this.timestamp.getTime() / 1000), 0);
bw.write(timestampBuffer);
writeAddr(this.addrMe, bw);
writeAddr(this.addrYou, bw);
bw.write(this.nonce);
bw.writeVarintNum(this.subversion.length);
bw.write(new Buffer(this.subversion, 'ascii'));
bw.writeUInt32LE(this.startHeight);
bw.writeUInt8(this.relay);
return bw.concat();
};
commands.version = require('./commands/version')({magicNumber: magicNumber});
/* verack */
@ -263,7 +79,7 @@ function builder(options) {
Message.call(this, options);
this.command = 'ping';
this.magicNumber = magicNumber;
this.nonce = options.nonce || getNonce();
this.nonce = options.nonce || utils.getNonce();
};
inherits(commands.ping, Message);
@ -280,7 +96,7 @@ function builder(options) {
var parser = new BufferReader(payload);
obj.nonce = parser.read(8);
checkFinished(parser);
utils.checkFinished(parser);
return commands.ping.fromObject(obj);
};
@ -303,7 +119,7 @@ function builder(options) {
var parser = new BufferReader(payload);
obj.nonce = parser.read(8);
checkFinished(parser);
utils.checkFinished(parser);
return commands.pong.fromObject(obj);
};
@ -311,28 +127,7 @@ function builder(options) {
return this.nonce;
};
/* block */
commands.block = function(options) {
Message.call(this, options);
this.command = 'block';
this.magicNumber = magicNumber;
this.block = options.block;
};
inherits(commands.block, Message);
commands.block.fromObject = function(options) {
return new commands.block(options);
};
commands.block.fromBuffer = function(payload) {
var block = Block.fromBuffer(payload);
return commands.block.fromObject({block: block});
};
commands.block.prototype.getPayload = function() {
return this.block.toBuffer();
};
commands.block = require('./commands/block')({Block: Block, magicNumber: magicNumber});
/* tx */
@ -390,13 +185,13 @@ function builder(options) {
obj.inventory.push({type: type, hash: hash});
}
checkFinished(parser);
utils.checkFinished(parser);
return commands.getdata.fromObject(obj);
};
commands.getdata.prototype.getPayload = function() {
var bw = new BufferWriter();
writeInventory(this.inventory, bw);
utils.writeInventory(this.inventory, bw);
return bw.concat();
};
@ -433,7 +228,7 @@ function builder(options) {
$.checkState(txn_count === 0, 'txn_count should always be 0');
}
checkFinished(parser);
utils.checkFinished(parser);
return commands.headers.fromObject(obj);
};
@ -476,14 +271,14 @@ function builder(options) {
obj.inventory.push({type: type, hash: hash});
}
checkFinished(parser);
utils.checkFinished(parser);
return commands.notfound.fromObject(obj);
};
commands.notfound.prototype.getPayload = function() {
var bw = new BufferWriter();
writeInventory(this.inventory, bw);
utils.writeInventory(this.inventory, bw);
return bw.concat();
};
@ -504,7 +299,7 @@ function builder(options) {
commands.inv.prototype.getPayload = function() {
var bw = new BufferWriter();
writeInventory(this.inventory, bw);
utils.writeInventory(this.inventory, bw);
return bw.concat();
};
@ -521,7 +316,7 @@ function builder(options) {
obj.inventory.push({type: type, hash: hash});
}
checkFinished(parser);
utils.checkFinished(parser);
return commands.inv.fromObject(obj);
};
@ -550,12 +345,12 @@ function builder(options) {
// todo: time only available on versions >=31402
var time = new Date(parser.readUInt32LE() * 1000);
var addr = parseAddr(parser);
var addr = utils.parseAddr(parser);
addr.time = time;
obj.addresses.push(addr);
}
checkFinished(parser);
utils.checkFinished(parser);
return commands.addr.fromObject(obj);
};
@ -566,7 +361,7 @@ function builder(options) {
for (var i = 0; i < this.addresses.length; i++) {
var addr = this.addresses[i];
bw.writeUInt32LE(addr.time.getTime() / 1000);
writeAddr(addr, bw);
utils.writeAddr(addr, bw);
}
return bw.concat();
@ -594,7 +389,7 @@ function builder(options) {
var parser = new BufferReader(payload);
obj.payload = parser.readVarLengthBuffer();
obj.signature = parser.readVarLengthBuffer();
checkFinished(parser);
utils.checkFinished(parser);
return commands.alert.fromObject(obj);
};
@ -717,7 +512,7 @@ function builder(options) {
$.checkArgument(payload);
var parser = new BufferReader(payload);
obj.data = parser.readVarLengthBuffer();
checkFinished(parser);
utils.checkFinished(parser);
return commands.filteradd.fromObject(obj);
};
@ -763,7 +558,7 @@ function builder(options) {
this.version = protocolVersion;
this.magicNumber = magicNumber;
options = sanitizeStartStop(options);
options = utils.sanitizeStartStop(options);
this.starts = options.starts;
this.stop = options.stop;
@ -787,7 +582,7 @@ function builder(options) {
obj.starts.push(parser.read(32));
}
obj.stop = parser.read(32);
checkFinished(parser);
utils.checkFinished(parser);
return commands.getblocks.fromObject(obj);
};
@ -818,7 +613,7 @@ function builder(options) {
this.version = protocolVersion;
this.magicNumber = magicNumber;
options = sanitizeStartStop(options);
options = utils.sanitizeStartStop(options);
this.starts = options.starts;
this.stop = options.stop;
@ -842,7 +637,7 @@ function builder(options) {
obj.starts.push(parser.read(32));
}
obj.stop = parser.read(32);
checkFinished(parser);
utils.checkFinished(parser);
return commands.getheaders.fromObject(obj);
};
@ -881,7 +676,6 @@ function builder(options) {
};
/* getaddr */
//todo: need test data
commands.getaddr = function(options) {
Message.call(this, options);
this.magicNumber = magicNumber;

View File

@ -0,0 +1,35 @@
'use strict';
var Message = require('../message');
var inherits = require('util').inherits;
var bitcore = require('bitcore');
var Block = bitcore.Block;
var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
function BlockMessage(options) {
Message.call(this, options);
this.command = 'block';
this.magicNumber = magicNumber;
this.block = options.block;
}
inherits(BlockMessage, Message);
BlockMessage.fromObject = function(options) {
return new BlockMessage(options);
};
BlockMessage.fromBuffer = function(payload) {
var block = Block.fromBuffer(payload);
return BlockMessage.fromObject({block: block});
};
BlockMessage.prototype.getPayload = function() {
return this.block.toBuffer();
};
module.exports = function(options) {
Block = options.Block;
magicNumber = options.magicNumber;
return BlockMessage;
};

View File

@ -0,0 +1,100 @@
'use strict';
var Message = require('../message');
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');
var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0);
var protocolVersion = 70000;
var packageInfo = require('../../../package.json');
/**
* The version message is used on connection creation to advertise
* the type of node. The remote node will respond with its version, and no
* communication is possible until both peers have exchanged their versions.
* By default, bitcore advertises itself as named `bitcore:0.8`.
*
* @param{Object} obj - properties for the version
* @param{String} obj.subversion - version of the client
* @param{Buffer} obj.nonce - a random 8 byte buffer
*/
function VersionMessage(obj) {
/* jshint maxcomplexity: 10 */
Message.call(this, obj);
this.command = 'version';
_.assign(this, obj);
this.magicNumber = magicNumber;
this.nonce = this.nonce || utils.getNonce();
this.services = this.services || new BN(1, 10);
this.timestamp = this.timestamp || new Date();
this.version = this.version || protocolVersion;
this.subversion = this.subversion || '/bitcore:' + packageInfo.version + '/';
this.startHeight = this.startHeight || 0;
};
inherits(VersionMessage, Message);
VersionMessage.fromObject = function(obj) {
return new VersionMessage(obj);
};
VersionMessage.fromBuffer = function(payload) {
var parser = new BufferReader(payload);
var obj = {};
obj.version = parser.readUInt32LE();
obj.services = parser.readUInt64LEBN();
obj.timestamp = new Date(parser.readUInt64LEBN().toNumber() * 1000);
obj.addrMe = {
services: parser.readUInt64LEBN(),
ip: utils.parseIP(parser),
port: parser.readUInt16BE()
};
obj.addrYou = {
services: parser.readUInt64LEBN(),
ip: utils.parseIP(parser),
port: parser.readUInt16BE()
};
obj.nonce = parser.read(8);
obj.subversion = parser.readVarLengthBuffer().toString();
obj.startHeight = parser.readUInt32LE();
if(parser.finished()) {
obj.relay = true;
} else {
obj.relay = !!parser.readUInt8();
}
utils.checkFinished(parser);
return VersionMessage.fromObject(obj);
};
VersionMessage.prototype.getPayload = function() {
var bw = new BufferWriter();
bw.writeUInt32LE(this.version);
bw.writeUInt64LEBN(this.services);
var timestampBuffer = new Buffer(Array(8));
timestampBuffer.writeUInt32LE(Math.round(this.timestamp.getTime() / 1000), 0);
bw.write(timestampBuffer);
utils.writeAddr(this.addrMe, bw);
utils.writeAddr(this.addrYou, bw);
bw.write(this.nonce);
bw.writeVarintNum(this.subversion.length);
bw.write(new Buffer(this.subversion, 'ascii'));
bw.writeUInt32LE(this.startHeight);
bw.writeUInt8(this.relay);
return bw.concat();
};
module.exports = function(options) {
magicNumber = options.magicNumber || magicNumber;
protocolVersion = options.protocolVersion || protocolVersion;
return VersionMessage;
};

109
lib/messages/utils.js Normal file
View File

@ -0,0 +1,109 @@
'use strict';
var bitcore = require('bitcore');
var BufferUtil = bitcore.util.buffer;
var $ = bitcore.util.preconditions;
var _ = bitcore.deps._;
var utils;
module.exports = utils = {
checkFinished: function checkFinished(parser) {
if(!parser.finished()) {
throw new Error('Data still available after parsing');
}
},
getNonce: function getNonce() {
return bitcore.crypto.Random.getRandomBuffer(8);
},
writeIP: function writeIP(ip, bw) {
var words = ip.v6.split(':').map(function(s) {
return new Buffer(s, 'hex');
});
for (var i = 0; i < words.length; i++) {
var word = words[i];
bw.write(word);
}
},
writeAddr: function writeAddr(addr, bw) {
if (_.isUndefined(addr)) {
var pad = new Buffer(Array(26));
bw.write(pad);
return;
}
bw.writeUInt64LEBN(addr.services);
utils.writeIP(addr.ip, bw);
bw.writeUInt16BE(addr.port);
},
writeInventory: function writeInventory(inventory, bw) {
bw.writeVarintNum(inventory.length);
inventory.forEach(function(value) {
bw.writeUInt32LE(value.type);
bw.write(value.hash);
});
},
parseIP: function parseIP(parser) {
var ipv6 = [];
var ipv4 = [];
for (var a = 0; a < 8; a++) {
var word = parser.read(2);
ipv6.push(word.toString('hex'));
if (a >= 6) {
ipv4.push(word[0]);
ipv4.push(word[1]);
}
}
ipv6 = ipv6.join(':');
ipv4 = ipv4.join('.');
return {
v6: ipv6,
v4: ipv4
};
},
parseAddr: function parseAddr(parser) {
var services = parser.readUInt64LEBN();
var ip = utils.parseIP(parser);
var port = parser.readUInt16BE();
return {
services: services,
ip: ip,
port: port
};
},
sanitizeStartStop: function sanitizeStartStop(obj) {
/* jshint maxcomplexity: 10 */
/* jshint maxstatements: 20 */
$.checkArgument(_.isUndefined(obj.starts) || _.isArray(obj.starts));
var starts = obj.starts;
var stop = obj.stop;
if (starts) {
starts = starts.map(function(hash) {
if (_.isString(hash)) {
return BufferUtil.reverse(new Buffer(hash, 'hex'));
} else {
return hash;
}
});
} else {
starts = [];
}
for (var i = 0; i < starts.length; i++) {
if (starts[i].length !== 32) {
throw new Error('Invalid hash ' + i + ' length: ' + starts[i].length);
}
}
stop = obj.stop;
if (_.isString(stop)) {
stop = BufferUtil.reverse(new Buffer(stop, 'hex'));
}
if (!stop) {
stop = BufferUtil.NULL_HASH;
}
obj.starts = starts;
obj.stop = stop;
return obj;
}
};