diff --git a/lib/transport/message.js b/lib/transport/message.js index a94d0e6..8745ad7 100644 --- a/lib/transport/message.js +++ b/lib/transport/message.js @@ -2,9 +2,10 @@ var Put = require('bufferput'); -var Random = require('../crypto/random'); -var BufferReader = require('../encoding/bufferreader'); var Block = require('../block'); +var BufferReader = require('../encoding/bufferreader'); +var BufferUtil = require('../util/buffer'); +var Random = require('../crypto/random'); var CONNECTION_NONCE = Random.getPseudoRandomBuffer(8); var PROTOCOL_VERSION = 70000; @@ -14,7 +15,10 @@ var MESSAGES = { 'verack': VerAck, 'inv': Inventory, 'ping': Ping, - 'pong': Pong + 'pong': Pong, + 'addr': Addresses, + 'getaddr': GetAddresses, + 'reject': Reject } module.exports.buildMessage = function(command, payload) { @@ -31,9 +35,10 @@ module.exports.buildMessage = function(command, payload) { // ====== VERSION MESSAGE ====== function Version(subversion, nonce) { this.command = 'version'; + this.version = PROTOCOL_VERSION; this.subversion = subversion || '/BitcoinX:0.1/'; this.nonce = nonce || CONNECTION_NONCE; -} +}; Version.fromBuffer = function(payload) { var message = new Version(); @@ -49,11 +54,11 @@ Version.fromBuffer = function(payload) { message.start_height = parser.readUInt32LE(); return message; -} +}; Version.prototype.serialize = function() { var put = new Put(); - put.word32le(PROTOCOL_VERSION); // version + put.word32le(this.version); // version put.word64le(1); // services put.word64le(Math.round(new Date().getTime() / 1000)); // timestamp put.pad(26); // addr_me @@ -64,11 +69,11 @@ Version.prototype.serialize = function() { put.word32le(0); return put.buffer(); -} +}; module.exports.Version = Version; -// ====== INV MESSAGE ====== +// ====== INV/GETDATA MESSAGE ====== function Inventory(inventory) { this.command = 'inv'; this.inventory = inventory || []; @@ -87,7 +92,7 @@ Inventory.fromBuffer = function(payload) { } return message; -} +}; Inventory.prototype.serialize = function() { var put = new Put(); @@ -99,8 +104,15 @@ Inventory.prototype.serialize = function() { }); return put.buffer(); +}; + +function GetData(inventory) { + this.command = 'getdata'; + this.inventory = inventory || []; } +GetData.fromBuffer = Inventory.fromBuffer; +GetData.prototype.serialize = Inventory.prototype.serialize; // ====== PING/PONG MESSAGE ====== function Ping(nonce) { @@ -111,11 +123,11 @@ function Ping(nonce) { Ping.fromBuffer = function(payload) { var nonce = new BufferReader(payload).read(8); return new Ping(nonce); -} +}; Ping.prototype.serialize = function() { return this.nonce; -} +}; function Pong(nonce) { this.command = 'pong'; @@ -126,57 +138,202 @@ Pong.fromBuffer = Ping.fromBuffer; Pong.prototype.serialize = Ping.prototype.serialize; -// ====== VARIOUS MESSAGE ====== - - - -function GetAddr() {}; - -function VerAck() {}; -VerAck.fromBuffer = function() { - return new VerAck(); +// ====== ADDR MESSAGE ====== +function Addresses(nonce) { + this.command = 'addr'; + this.addresses = []; } -function Reject() {}; +Address.fromBuffer = function(payload) { + var message = new Address(); -function Ping(payload) { var parser = new BufferReader(payload); + var addrCount = Math.min(parser.readVarintNum(), 1000); - this.nonce = parser.read(8); -}; - -// ====== PING MESSAGE ====== -function Address(payload) { - var parser = new BufferReader(payload); - - var addrCount = parser.readVarintNum(); - addrCount = Math.min(addrCount, 1000); - - this.addresses = []; - for (i = 0; i < addrCount; i++) { + message.addresses = []; + for (var i = 0; i < addrCount; i++) { // TODO: Time actually depends on the version of the other peer (>=31402) - this.addresses.push({ + message.addresses.push({ time: parser.readUInt32LE(), services: parser.readUInt64LEBN(), - ip: parser.read(16), // TODO: Parse IP Address + ip: parser.read(16), port: parser.readUInt16BE() }); } + + return message; }; -function GetHeaders(payload) { - var parser = new BufferReader(payload); +Address.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; // TODO +}; - this.version = parser.readUInt32LE(); - - var startCount = parser.readVarintNum(); - startCount = Math.min(startCount, 500); - - this.starts = []; - for (i = 0; i < startCount; i++) { - this.starts.push(parser.read(32)); - } - - this.stop = parser.read(32); +// ====== GETADDR MESSAGE ====== +function GetAddresses() { + this.command = 'getaddr'; } +GetAddresses.fromBuffer = function() { + return new GetAddresses(); +}; + +GetAddresses.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; +}; + + +// ====== VERACK MESSAGE ====== +function VerAck() { + this.command = 'verack'; +} + +VerAck.fromBuffer = function() { + return new VerAck(); +}; + +VerAck.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +// ====== REJECT MESSAGE ====== +// TODO: Parse REJECT message +function Reject() { + this.command = 'reject'; +} + +Reject.fromBuffer = function() { + return new Reject(); +}; + +Reject.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +// ====== ALERT MESSAGE ====== +function Alert(payload) { + this.command = 'reject'; +} + +Alert.fromBuffer = function() { + var message = new Alert(); + + var parser = new BufferReader(payload); + message.payload = parser.readVarintBuf(); // TODO: Use current format + message.signature = parser.readVarintBuf(); + return message; +}; + +Alert.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; // TODO: Serialize +}; + +// ====== HEADERS MESSAGE ====== +function Headers(blockheaders) { + this.command = 'headers'; + this.headers = blockheaders || []; +} + +Headers.fromBuffer = function() { + var message = new Headers(); + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + + message.headers = []; + for (i = 0; i < count; i++) { + var header = Block().fromBufferReader(parser); + message.headers.push(header); + } + + return message; +}; + +Headers.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; // TODO: Serialize +}; + +// ====== BLOCK MESSAGE ====== +function Block(block) { + this.command = 'block'; + this.block = block; +} + +Block.fromBuffer = function() { + var parser = new BufferReader(payload); + var block = Block().fromBufferReader(parser); + return new Block(block); +}; + +Block.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; // TODO: Serialize +}; + +// ====== TX MESSAGE ====== +function Transaction(transaction) { + this.command = 'tx'; + this.transaction = transaction; +} + +Transaction.fromBuffer = function() { + var parser = new BufferReader(payload); + var transaction = Transaction().fromBufferReader(parser); + return new Transaction(transaction); +}; + +Transaction.prototype.serialize = function() { + return BufferUtil.EMPTY_BUFFER; // TODO: Serialize +}; + +// ====== GETBLOCKS/GETHEADERS MESSAGE ====== +function GetBlocks(starts, stop) { + this.command = 'getblocks'; + this.version = PROTOCOL_VERSION; + this.starts = starts || []; + this.stop = stop || BufferUtil.NULL_HASH; +} + +GetBlocks.fromBuffer = function() { + var message = new GetBlocks(); + + var parser = new BufferReader(payload); + message.version = parser.readUInt32LE(); + + var startCount = Math.min(parser.readVarintNum(), 500); + message.starts = []; + for (var i = 0; i < startCount; i++) { + message.starts.push(parser.read(32)); + } + message.stop = parser.read(32); +}; + +GetBlocks.prototype.serialize = function() { + var put = new Put(); + put.word32le(this.version); + put.varint(this.starts.length); + + for (var i = 0; i < starts.length; i++) { + if (this.starts[i].length != 32) { + throw new Error('Invalid hash length'); + } + put.put(this.starts[i]); + } + + if (this.stop.length != 32) { + throw new Error('Invalid hash length'); + } + put.put(this.stop); + + return put.buffer(); +}; + +function GetHeaders(starts, stop) { + this.command = 'getheaders'; + this.version = PROTOCOL_VERSION; + this.starts = starts || []; + this.stop = stop || BufferUtil.NULL_HASH; +} + +GetHeaders.fromBuffer = GetBlocks.fromBuffer; +GetHeaders.prototype.serialize = GetBlocks.prototype.serialize; + + diff --git a/lib/util/buffer.js b/lib/util/buffer.js index a84023e..deb9f3a 100644 --- a/lib/util/buffer.js +++ b/lib/util/buffer.js @@ -22,8 +22,6 @@ function equals(a, b) { module.exports = { NULL_HASH: buffertools.fill(new Buffer(32), 0), EMPTY_BUFFER: new Buffer(0), - ZERO_VALUE: buffertools.fill(new Buffer(8), 0), - INT64_MAX: new Buffer('ffffffffffffffff', 'hex'), /** * Returns true if the given argument is an instance of a buffer. Tests for