From e460bf915dd2996d327c44707ba95af0155a3600 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 12 Mar 2015 10:41:35 -0400 Subject: [PATCH] moved the rest of the commands into seperate files --- lib/messages/builder.js | 626 +-------------------------- lib/messages/commands/addr.js | 60 +++ lib/messages/commands/alert.js | 49 +++ lib/messages/commands/filteradd.js | 51 +++ lib/messages/commands/filterclear.js | 32 ++ lib/messages/commands/filterload.js | 44 ++ lib/messages/commands/getaddr.js | 33 ++ lib/messages/commands/getblocks.js | 74 ++++ lib/messages/commands/getdata.js | 50 +++ lib/messages/commands/getheaders.js | 72 +++ lib/messages/commands/headers.js | 65 +++ lib/messages/commands/inv.js | 50 +++ lib/messages/commands/mempool.js | 32 ++ lib/messages/commands/merkleblock.js | 50 +++ lib/messages/commands/notfound.js | 50 +++ lib/messages/commands/pong.js | 39 ++ lib/messages/commands/reject.js | 34 ++ lib/messages/commands/tx.js | 40 ++ 18 files changed, 844 insertions(+), 607 deletions(-) create mode 100644 lib/messages/commands/addr.js create mode 100644 lib/messages/commands/alert.js create mode 100644 lib/messages/commands/filteradd.js create mode 100644 lib/messages/commands/filterclear.js create mode 100644 lib/messages/commands/filterload.js create mode 100644 lib/messages/commands/getaddr.js create mode 100644 lib/messages/commands/getblocks.js create mode 100644 lib/messages/commands/getdata.js create mode 100644 lib/messages/commands/getheaders.js create mode 100644 lib/messages/commands/headers.js create mode 100644 lib/messages/commands/inv.js create mode 100644 lib/messages/commands/mempool.js create mode 100644 lib/messages/commands/merkleblock.js create mode 100644 lib/messages/commands/notfound.js create mode 100644 lib/messages/commands/pong.js create mode 100644 lib/messages/commands/reject.js create mode 100644 lib/messages/commands/tx.js diff --git a/lib/messages/builder.js b/lib/messages/builder.js index 37c795c..38082f7 100644 --- a/lib/messages/builder.js +++ b/lib/messages/builder.js @@ -1,20 +1,10 @@ 'use strict'; -var Message = require('./message'); -var BloomFilter = require('../bloomfilter'); - -var inherits = require('util').inherits; var bitcore = require('bitcore'); -var BufferReader = bitcore.encoding.BufferReader; -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 */ - /* jshint maxcomplexity: 10 */ + /* jshint maxstatements: 50 */ + /* jshint maxcomplexity: 8 */ if (!options) { options = {}; @@ -49,602 +39,24 @@ function builder(options) { commands.version = require('./commands/version')(options); commands.verack = require('./commands/verack')(options); commands.ping = require('./commands/ping')(options); - - /* pong */ - - commands.pong = function(options) { - Message.call(this, options); - this.command = 'pong'; - this.magicNumber = magicNumber; - this.nonce = options.nonce; - }; - inherits(commands.pong, Message); - - commands.pong.fromObject = function(obj) { - return new commands.pong(obj); - }; - - commands.pong.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - obj.nonce = parser.read(8); - - utils.checkFinished(parser); - return commands.pong.fromObject(obj); - }; - - commands.pong.prototype.getPayload = function() { - return this.nonce; - }; - + commands.pong = require('./commands/pong')(options); commands.block = require('./commands/block')(options); - - /* tx */ - - commands.tx = function(options) { - Message.call(this, options); - this.command = 'tx'; - this.magicNumber = magicNumber; - this.transaction = options.transaction; - }; - inherits(commands.tx, Message); - - commands.tx.fromObject = function(options) { - return new commands.tx(options); - }; - - commands.tx.fromBuffer = function(payload) { - var transaction; - if (Transaction.prototype.fromBuffer) { - transaction = Transaction().fromBuffer(payload); - } else { - transaction = Transaction.fromBuffer(payload); - } - return commands.tx.fromObject({transaction: transaction}); - }; - - commands.tx.prototype.getPayload = function() { - return this.transaction.toBuffer(); - }; - - /* getdata */ - - commands.getdata = function(options) { - Message.call(this, options); - this.command = 'getdata'; - this.magicNumber = magicNumber; - this.inventory = options.inventory; - }; - - inherits(commands.getdata, Message); - - commands.getdata.fromObject = function(options) { - return new commands.getdata(options); - }; - - commands.getdata.fromBuffer = function(payload) { - var obj = { - inventory: [] - }; - - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - for (var i = 0; i < count; i++) { - var type = parser.readUInt32LE(); - var hash = parser.read(32); - obj.inventory.push({type: type, hash: hash}); - } - - utils.checkFinished(parser); - return commands.getdata.fromObject(obj); - }; - - commands.getdata.prototype.getPayload = function() { - var bw = new BufferWriter(); - utils.writeInventory(this.inventory, bw); - return bw.concat(); - }; - - /** - * Sent in response to a `getheaders` message. It contains information about - * block headers. - * - * @param{Array} blockheaders - array of block headers - */ - commands.headers = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'headers'; - this.headers = options.headers; - }; - inherits(commands.headers, Message); - - commands.headers.fromObject = function(options) { - return new commands.headers(options); - }; - - commands.headers.fromBuffer = function(payload) { - var obj = {}; - - $.checkArgument(payload && payload.length > 0, 'No data found to create Headers message'); - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - - obj.headers = []; - for (var i = 0; i < count; i++) { - var header = BlockHeader.fromBufferReader(parser); - obj.headers.push(header); - var txn_count = parser.readUInt8(); - $.checkState(txn_count === 0, 'txn_count should always be 0'); - - } - utils.checkFinished(parser); - - return commands.headers.fromObject(obj); - }; - - commands.headers.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.headers.length); - for (var i = 0; i < this.headers.length; i++) { - var buffer = this.headers[i].toBuffer(); - bw.write(buffer); - bw.writeUInt8(0); - } - return bw.concat(); - }; - - /* notfound */ - - commands.notfound = function(options) { - Message.call(this, options); - this.command = 'notfound'; - this.magicNumber = magicNumber; - this.inventory = options.inventory; - }; - inherits(commands.notfound, Message); - - commands.notfound.fromObject = function(options) { - return new commands.notfound(options); - }; - - commands.notfound.fromBuffer = function(payload) { - var obj = { - inventory: [] - }; - - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - for (var i = 0; i < count; i++) { - var type = parser.readUInt32LE(); - var hash = parser.read(32); - obj.inventory.push({type: type, hash: hash}); - } - - utils.checkFinished(parser); - return commands.notfound.fromObject(obj); - - }; - - commands.notfound.prototype.getPayload = function() { - var bw = new BufferWriter(); - utils.writeInventory(this.inventory, bw); - return bw.concat(); - }; - - /* inv */ - - commands.inv = function(options) { - Message.call(this, options); - this.command = 'inv'; - this.magicNumber = magicNumber; - this.inventory = options.inventory; - }; - - inherits(commands.inv, Message); - - commands.inv.fromObject = function(options) { - return new commands.inv(options); - }; - - commands.inv.prototype.getPayload = function() { - var bw = new BufferWriter(); - utils.writeInventory(this.inventory, bw); - return bw.concat(); - }; - - commands.inv.fromBuffer = function(payload) { - var obj = { - inventory: [] - }; - - var parser = new BufferReader(payload); - var count = parser.readVarintNum(); - for (var i = 0; i < count; i++) { - var type = parser.readUInt32LE(); - var hash = parser.read(32); - obj.inventory.push({type: type, hash: hash}); - } - - utils.checkFinished(parser); - return commands.inv.fromObject(obj); - }; - - /* addr */ - - commands.addr = function(options) { - Message.call(this, options); - this.command = 'addr'; - this.magicNumber = magicNumber; - this.addresses = options.addresses; - }; - inherits(commands.addr, Message); - - commands.addr.fromObject = function(options) { - return new commands.addr(options); - }; - - commands.addr.fromBuffer = function(payload) { - var parser = new BufferReader(payload); - - var addrCount = parser.readVarintNum(); - - var obj = {}; - obj.addresses = []; - for (var i = 0; i < addrCount; i++) { - // todo: time only available on versions >=31402 - var time = new Date(parser.readUInt32LE() * 1000); - - var addr = utils.parseAddr(parser); - addr.time = time; - obj.addresses.push(addr); - } - - utils.checkFinished(parser); - return commands.addr.fromObject(obj); - }; - - commands.addr.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.addresses.length); - - for (var i = 0; i < this.addresses.length; i++) { - var addr = this.addresses[i]; - bw.writeUInt32LE(addr.time.getTime() / 1000); - utils.writeAddr(addr, bw); - } - - return bw.concat(); - }; - - /* alert */ - - commands.alert = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'alert'; - - this.payload = options.payload || new Buffer(32); - this.signature = options.signature || new Buffer(32); - - }; - inherits(commands.alert, Message); - - commands.alert.fromObject = function(options) { - return new commands.alert(options); - }; - - commands.alert.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - obj.payload = parser.readVarLengthBuffer(); - obj.signature = parser.readVarLengthBuffer(); - utils.checkFinished(parser); - return commands.alert.fromObject(obj); - }; - - commands.alert.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.payload.length); - bw.write(this.payload); - - bw.writeVarintNum(this.signature.length); - bw.write(this.signature); - - return bw.concat(); - }; - - /* reject */ - // todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject - commands.reject = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'reject'; - }; - inherits(commands.reject, Message); - - commands.reject.fromObject = function(options) { - return new commands.reject(options); - }; - - commands.reject.fromBuffer = function(payload) { - var obj = {}; - return commands.reject.fromObject(obj); - }; - - commands.reject.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; - - /** - * Contains information about a MerkleBlock - * - * @name P2P.Message.MerkleBlock - * @param {MerkleBlock} block - */ - commands.merkleblock = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'merkleblock'; - $.checkArgument( - _.isUndefined(options.merkleBlock) || - options.merkleBlock instanceof MerkleBlock - ); - this.merkleBlock = options.merkleBlock; - }; - inherits(commands.merkleblock, Message); - - commands.merkleblock.fromObject = function(options) { - return new commands.merkleblock(options); - }; - - commands.merkleblock.fromBuffer = function(payload) { - var obj = {}; - $.checkArgument(BufferUtil.isBuffer(payload)); - obj.merkleBlock = MerkleBlock.fromBuffer(payload); - return commands.merkleblock.fromObject(obj); - }; - - commands.merkleblock.prototype.getPayload = function() { - return this.merkleBlock ? this.merkleBlock.toBuffer() : BufferUtil.EMPTY_BUFFER; - }; - - /* filterload */ - - commands.filterload = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'filterload'; - $.checkArgument(_.isUndefined(options.filter) || options.filter instanceof BloomFilter, - 'BloomFilter object or undefined required for FilterLoad'); - this.filter = options.filter; - }; - inherits(commands.filterload, Message); - - commands.filterload.fromObject = function(options) { - return new commands.filterload(options); - }; - - commands.filterload.fromBuffer = function(payload) { - var obj = {}; - obj.filter = BloomFilter.fromBuffer(payload); - return commands.filterload.fromObject(obj); - }; - - commands.filterload.prototype.getPayload = function() { - if(this.filter) { - return this.filter.toBuffer(); - } else { - return BufferUtil.EMPTY_BUFFER; - } - }; - - /** - * Request peer to add data to a bloom filter already set by 'filterload' - * - * @name P2P.Message.filteradd - * @param{Buffer} data - Array of bytes representing bloom filter data - */ - commands.filteradd = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'filteradd'; - this.data = options.data || BufferUtil.EMPTY_BUFFER; - }; - inherits(commands.filteradd, Message); - - commands.filteradd.fromObject = function(options) { - return new commands.filteradd(options); - }; - - commands.filteradd.fromBuffer = function(payload) { - var obj = {}; - $.checkArgument(payload); - var parser = new BufferReader(payload); - obj.data = parser.readVarLengthBuffer(); - utils.checkFinished(parser); - return commands.filteradd.fromObject(obj); - }; - - commands.filteradd.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeVarintNum(this.data.length); - bw.write(this.data); - return bw.concat(); - }; - - /* filterclear */ - - commands.filterclear = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'filterclear'; - }; - inherits(commands.filterclear, Message); - - commands.filterclear.fromObject = function(options) { - return new commands.filterclear(options); - }; - - commands.filterclear.fromBuffer = function(payload) { - return commands.filterclear.fromObject({}); - }; - - commands.filterclear.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; - - /** - * 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{Array} starts - array of buffers or strings with the starting block hashes - * @param{Buffer} [stop] - hash of the last block - */ - commands.getblocks = function(options) { - Message.call(this, options); - this.command = 'getblocks'; - this.version = protocolVersion; - this.magicNumber = magicNumber; - - options = utils.sanitizeStartStop(options); - this.starts = options.starts; - this.stop = options.stop; - - }; - inherits(commands.getblocks, Message); - - commands.getblocks.fromObject = function(obj) { - return new commands.getblocks(obj); - }; - - commands.getblocks.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - $.checkArgument(!parser.finished(), 'No data received in payload'); - - obj.version = parser.readUInt32LE(); - var startCount = parser.readVarintNum(); - - obj.starts = []; - for (var i = 0; i < startCount; i++) { - obj.starts.push(parser.read(32)); - } - obj.stop = parser.read(32); - utils.checkFinished(parser); - return commands.getblocks.fromObject(obj); - }; - - commands.getblocks.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeUInt32LE(this.version); - bw.writeVarintNum(this.starts.length); - for (var i = 0; i < this.starts.length; i++) { - bw.write(this.starts[i]); - } - if (this.stop.length !== 32) { - throw new Error('Invalid hash length: ' + this.stop.length); - } - bw.write(this.stop); - return bw.concat(); - }; - - /** - * Request block headers starting from a hash - * - * @param{Array} starts - array of buffers with the starting block hashes - * @param{Buffer} [stop] - hash of the last block - */ - //todo: need test data - commands.getheaders = function(options) { - Message.call(this, options); - this.command = 'getheaders'; - this.version = protocolVersion; - this.magicNumber = magicNumber; - - options = utils.sanitizeStartStop(options); - this.starts = options.starts; - this.stop = options.stop; - - }; - inherits(commands.getheaders, Message); - - commands.getheaders.fromObject = function(obj) { - return new commands.getheaders(obj); - }; - - commands.getheaders.fromBuffer = function(payload) { - var obj = {}; - var parser = new BufferReader(payload); - $.checkArgument(!parser.finished(), 'No data received in payload'); - - obj.version = parser.readUInt32LE(); - var startCount = Math.min(parser.readVarintNum(), 500); - - obj.starts = []; - for (var i = 0; i < startCount; i++) { - obj.starts.push(parser.read(32)); - } - obj.stop = parser.read(32); - utils.checkFinished(parser); - return commands.getheaders.fromObject(obj); - }; - - commands.getheaders.prototype.getPayload = function() { - var bw = new BufferWriter(); - bw.writeUInt32LE(this.version); - bw.writeVarintNum(this.starts.length); - for (var i = 0; i < this.starts.length; i++) { - bw.write(this.starts[i]); - } - if (this.stop.length !== 32) { - throw new Error('Invalid hash length: ' + this.stop.length); - } - bw.write(this.stop); - return bw.concat(); - }; - - /* mempool */ - commands.mempool = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'mempool'; - }; - inherits(commands.mempool, Message); - - commands.mempool.fromObject = function(options) { - return new commands.mempool(options); - }; - - commands.mempool.fromBuffer = function(payload) { - return commands.mempool.fromObject({}); - }; - - commands.mempool.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; - - /* getaddr */ - commands.getaddr = function(options) { - Message.call(this, options); - this.magicNumber = magicNumber; - this.command = 'getaddr'; - }; - inherits(commands.getaddr, Message); - - commands.getaddr.fromObject = function(options) { - return new commands.getaddr(options); - }; - - commands.getaddr.fromBuffer = function(payload) { - var obj = {}; - return commands.getaddr.fromObject(obj); - }; - - commands.getaddr.prototype.getPayload = function() { - return BufferUtil.EMPTY_BUFFER; - }; + commands.tx = require('./commands/tx')(options); + commands.getdata = require('./commands/getdata')(options); + commands.headers = require('./commands/headers')(options); + commands.notfound = require('./commands/notfound')(options); + commands.inv = require('./commands/inv')(options); + commands.addr = require('./commands/addr')(options); + commands.alert = require('./commands/alert')(options); + commands.reject = require('./commands/reject')(options); + commands.merkleblock = require('./commands/merkleblock')(options); + commands.filterload = require('./commands/filterload')(options); + commands.filteradd = require('./commands/filteradd')(options); + commands.filterclear = require('./commands/filterclear')(options); + commands.getblocks = require('./commands/getblocks')(options); + commands.getheaders = require('./commands/getheaders')(options); + commands.mempool = require('./commands/mempool')(options); + commands.getaddr = require('./commands/getaddr')(options); return exported; diff --git a/lib/messages/commands/addr.js b/lib/messages/commands/addr.js new file mode 100644 index 0000000..1aa7d79 --- /dev/null +++ b/lib/messages/commands/addr.js @@ -0,0 +1,60 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function AddrMessage(options) { + Message.call(this, options); + this.command = 'addr'; + this.magicNumber = magicNumber; + this.addresses = options.addresses; +}; +inherits(AddrMessage, Message); + +AddrMessage.fromObject = function(options) { + return new AddrMessage(options); +}; + +AddrMessage.fromBuffer = function(payload) { + var parser = new BufferReader(payload); + + var addrCount = parser.readVarintNum(); + + var obj = {}; + obj.addresses = []; + for (var i = 0; i < addrCount; i++) { + // todo: time only available on versions >=31402 + var time = new Date(parser.readUInt32LE() * 1000); + + var addr = utils.parseAddr(parser); + addr.time = time; + obj.addresses.push(addr); + } + + utils.checkFinished(parser); + return AddrMessage.fromObject(obj); +}; + +AddrMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.addresses.length); + + for (var i = 0; i < this.addresses.length; i++) { + var addr = this.addresses[i]; + bw.writeUInt32LE(addr.time.getTime() / 1000); + utils.writeAddr(addr, bw); + } + + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return AddrMessage; +}; diff --git a/lib/messages/commands/alert.js b/lib/messages/commands/alert.js new file mode 100644 index 0000000..6a3c8f3 --- /dev/null +++ b/lib/messages/commands/alert.js @@ -0,0 +1,49 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function AlertMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'alert'; + + this.payload = options.payload || new Buffer(32); + this.signature = options.signature || new Buffer(32); +} +inherits(AlertMessage, Message); + +AlertMessage.fromObject = function(options) { + return new AlertMessage(options); +}; + +AlertMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.payload = parser.readVarLengthBuffer(); + obj.signature = parser.readVarLengthBuffer(); + utils.checkFinished(parser); + return AlertMessage.fromObject(obj); +}; + +AlertMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.payload.length); + bw.write(this.payload); + + bw.writeVarintNum(this.signature.length); + bw.write(this.signature); + + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return AlertMessage; +}; diff --git a/lib/messages/commands/filteradd.js b/lib/messages/commands/filteradd.js new file mode 100644 index 0000000..a7966f7 --- /dev/null +++ b/lib/messages/commands/filteradd.js @@ -0,0 +1,51 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferUtil = bitcore.util.buffer; +var BufferWriter = bitcore.encoding.BufferWriter; +var BufferReader = bitcore.encoding.BufferReader; +var $ = bitcore.util.preconditions; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Request peer to add data to a bloom filter already set by 'filterload' + * + * @name P2P.Message.filteradd + * @param{Buffer} data - Array of bytes representing bloom filter data + */ +function FilteraddMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filteradd'; + this.data = options.data || BufferUtil.EMPTY_BUFFER; +}; +inherits(FilteraddMessage, Message); + +FilteraddMessage.fromObject = function(options) { + return new FilteraddMessage(options); +}; + +FilteraddMessage.fromBuffer = function(payload) { + var obj = {}; + $.checkArgument(payload); + var parser = new BufferReader(payload); + obj.data = parser.readVarLengthBuffer(); + utils.checkFinished(parser); + return FilteraddMessage.fromObject(obj); +}; + +FilteraddMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.data.length); + bw.write(this.data); + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return FilteraddMessage; +}; diff --git a/lib/messages/commands/filterclear.js b/lib/messages/commands/filterclear.js new file mode 100644 index 0000000..0da1f5a --- /dev/null +++ b/lib/messages/commands/filterclear.js @@ -0,0 +1,32 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function FilterclearMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filterclear'; +}; +inherits(FilterclearMessage, Message); + +FilterclearMessage.fromObject = function(options) { + return new FilterclearMessage(options); +}; + +FilterclearMessage.fromBuffer = function(payload) { + return FilterclearMessage.fromObject({}); +}; + +FilterclearMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return FilterclearMessage; +}; diff --git a/lib/messages/commands/filterload.js b/lib/messages/commands/filterload.js new file mode 100644 index 0000000..7e0ece9 --- /dev/null +++ b/lib/messages/commands/filterload.js @@ -0,0 +1,44 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; +var BloomFilter = require('../../bloomfilter'); +var $ = bitcore.util.preconditions; +var _ = bitcore.deps._; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function FilterloadMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'filterload'; + $.checkArgument(_.isUndefined(options.filter) || options.filter instanceof BloomFilter, + 'BloomFilter object or undefined required for FilterLoad'); + this.filter = options.filter; +} +inherits(FilterloadMessage, Message); + +FilterloadMessage.fromObject = function(options) { + return new FilterloadMessage(options); +}; + +FilterloadMessage.fromBuffer = function(payload) { + var obj = {}; + obj.filter = BloomFilter.fromBuffer(payload); + return FilterloadMessage.fromObject(obj); +}; + +FilterloadMessage.prototype.getPayload = function() { + if(this.filter) { + return this.filter.toBuffer(); + } else { + return BufferUtil.EMPTY_BUFFER; + } +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return FilterloadMessage; +}; diff --git a/lib/messages/commands/getaddr.js b/lib/messages/commands/getaddr.js new file mode 100644 index 0000000..104590f --- /dev/null +++ b/lib/messages/commands/getaddr.js @@ -0,0 +1,33 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function GetaddrMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'getaddr'; +} +inherits(GetaddrMessage, Message); + +GetaddrMessage.fromObject = function(options) { + return new GetaddrMessage(options); +}; + +GetaddrMessage.fromBuffer = function(payload) { + var obj = {}; + return GetaddrMessage.fromObject(obj); +}; + +GetaddrMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return GetaddrMessage; +}; diff --git a/lib/messages/commands/getblocks.js b/lib/messages/commands/getblocks.js new file mode 100644 index 0000000..a45b818 --- /dev/null +++ b/lib/messages/commands/getblocks.js @@ -0,0 +1,74 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var $ = bitcore.util.preconditions; + +var protocolVersion = 70000; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * 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{Array} starts - array of buffers or strings with the starting block hashes + * @param{Buffer} [stop] - hash of the last block + */ +function GetblocksMessage(options) { + Message.call(this, options); + this.command = 'getblocks'; + this.version = protocolVersion; + this.magicNumber = magicNumber; + + options = utils.sanitizeStartStop(options); + this.starts = options.starts; + this.stop = options.stop; + +}; +inherits(GetblocksMessage, Message); + +GetblocksMessage.fromObject = function(obj) { + return new GetblocksMessage(obj); +}; + +GetblocksMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + $.checkArgument(!parser.finished(), 'No data received in payload'); + + obj.version = parser.readUInt32LE(); + var startCount = parser.readVarintNum(); + + obj.starts = []; + for (var i = 0; i < startCount; i++) { + obj.starts.push(parser.read(32)); + } + obj.stop = parser.read(32); + utils.checkFinished(parser); + return GetblocksMessage.fromObject(obj); +}; + +GetblocksMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeVarintNum(this.starts.length); + for (var i = 0; i < this.starts.length; i++) { + bw.write(this.starts[i]); + } + if (this.stop.length !== 32) { + throw new Error('Invalid hash length: ' + this.stop.length); + } + bw.write(this.stop); + return bw.concat(); +}; + +module.exports = function(options) { + protocolVersion = options.protocolVersion || protocolVersion; + magicNumber = options.magicNumber || magicNumber; + return GetblocksMessage; +}; diff --git a/lib/messages/commands/getdata.js b/lib/messages/commands/getdata.js new file mode 100644 index 0000000..d8d71b1 --- /dev/null +++ b/lib/messages/commands/getdata.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function GetdataMessage(options) { + Message.call(this, options); + this.command = 'getdata'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; +} +inherits(GetdataMessage, Message); + +GetdataMessage.fromObject = function(options) { + return new GetdataMessage(options); +}; + +GetdataMessage.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + utils.checkFinished(parser); + return GetdataMessage.fromObject(obj); +}; + +GetdataMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + utils.writeInventory(this.inventory, bw); + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return GetdataMessage; +}; diff --git a/lib/messages/commands/getheaders.js b/lib/messages/commands/getheaders.js new file mode 100644 index 0000000..e272d7c --- /dev/null +++ b/lib/messages/commands/getheaders.js @@ -0,0 +1,72 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var $ = bitcore.util.preconditions; + +var protocolVersion = 70000; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Request block headers starting from a hash + * + * @param{Array} starts - array of buffers with the starting block hashes + * @param{Buffer} [stop] - hash of the last block + */ +function GetheadersMessage(options) { + Message.call(this, options); + this.command = 'getheaders'; + this.version = protocolVersion; + this.magicNumber = magicNumber; + + options = utils.sanitizeStartStop(options); + this.starts = options.starts; + this.stop = options.stop; + +} +inherits(GetheadersMessage, Message); + +GetheadersMessage.fromObject = function(obj) { + return new GetheadersMessage(obj); +}; + +GetheadersMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + $.checkArgument(!parser.finished(), 'No data received in payload'); + + obj.version = parser.readUInt32LE(); + var startCount = Math.min(parser.readVarintNum(), 500); + + obj.starts = []; + for (var i = 0; i < startCount; i++) { + obj.starts.push(parser.read(32)); + } + obj.stop = parser.read(32); + utils.checkFinished(parser); + return GetheadersMessage.fromObject(obj); +}; + +GetheadersMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeUInt32LE(this.version); + bw.writeVarintNum(this.starts.length); + for (var i = 0; i < this.starts.length; i++) { + bw.write(this.starts[i]); + } + if (this.stop.length !== 32) { + throw new Error('Invalid hash length: ' + this.stop.length); + } + bw.write(this.stop); + return bw.concat(); +}; + +module.exports = function(options) { + protocolVersion = options.protocolVersion || protocolVersion; + magicNumber = options.magicNumber || magicNumber; + return GetheadersMessage; +}; diff --git a/lib/messages/commands/headers.js b/lib/messages/commands/headers.js new file mode 100644 index 0000000..05b1549 --- /dev/null +++ b/lib/messages/commands/headers.js @@ -0,0 +1,65 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; +var $ = bitcore.util.preconditions; + +var BlockHeader = bitcore.BlockHeader; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Sent in response to a `getheaders` message. It contains information about + * block headers. + * + * @param{Array} blockheaders - array of block headers + */ +function HeadersMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'headers'; + this.headers = options.headers; +}; +inherits(HeadersMessage, Message); + +HeadersMessage.fromObject = function(options) { + return new HeadersMessage(options); +}; + +HeadersMessage.fromBuffer = function(payload) { + var obj = {}; + + $.checkArgument(payload && payload.length > 0, 'No data found to create Headers message'); + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + + obj.headers = []; + for (var i = 0; i < count; i++) { + var header = BlockHeader.fromBufferReader(parser); + obj.headers.push(header); + var txn_count = parser.readUInt8(); + $.checkState(txn_count === 0, 'txn_count should always be 0'); + } + utils.checkFinished(parser); + + return HeadersMessage.fromObject(obj); +}; + +HeadersMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + bw.writeVarintNum(this.headers.length); + for (var i = 0; i < this.headers.length; i++) { + var buffer = this.headers[i].toBuffer(); + bw.write(buffer); + bw.writeUInt8(0); + } + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return HeadersMessage; +}; diff --git a/lib/messages/commands/inv.js b/lib/messages/commands/inv.js new file mode 100644 index 0000000..3ddc0be --- /dev/null +++ b/lib/messages/commands/inv.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function InvMessage(options) { + Message.call(this, options); + this.command = 'inv'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; +} +inherits(InvMessage, Message); + +InvMessage.fromObject = function(options) { + return new InvMessage(options); +}; + +InvMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + utils.writeInventory(this.inventory, bw); + return bw.concat(); +}; + +InvMessage.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + utils.checkFinished(parser); + return InvMessage.fromObject(obj); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return InvMessage; +}; diff --git a/lib/messages/commands/mempool.js b/lib/messages/commands/mempool.js new file mode 100644 index 0000000..321f087 --- /dev/null +++ b/lib/messages/commands/mempool.js @@ -0,0 +1,32 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function MempoolMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'mempool'; +} +inherits(MempoolMessage, Message); + +MempoolMessage.fromObject = function(options) { + return new MempoolMessage(options); +}; + +MempoolMessage.fromBuffer = function(payload) { + return MempoolMessage.fromObject({}); +}; + +MempoolMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return MempoolMessage; +}; diff --git a/lib/messages/commands/merkleblock.js b/lib/messages/commands/merkleblock.js new file mode 100644 index 0000000..af2ade1 --- /dev/null +++ b/lib/messages/commands/merkleblock.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; +var $ = bitcore.util.preconditions; +var _ = bitcore.deps._; + +var MerkleBlock = bitcore.MerkleBlock; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +/** + * Contains information about a MerkleBlock + * + * @name P2P.Message.MerkleBlock + * @param {MerkleBlock} block + */ +function MerkleblockMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'merkleblock'; + $.checkArgument( + _.isUndefined(options.merkleBlock) || + options.merkleBlock instanceof MerkleBlock + ); + this.merkleBlock = options.merkleBlock; +} +inherits(MerkleblockMessage, Message); + +MerkleblockMessage.fromObject = function(options) { + return new MerkleblockMessage(options); +}; + +MerkleblockMessage.fromBuffer = function(payload) { + var obj = {}; + $.checkArgument(BufferUtil.isBuffer(payload)); + obj.merkleBlock = MerkleBlock.fromBuffer(payload); + return MerkleblockMessage.fromObject(obj); +}; + +MerkleblockMessage.prototype.getPayload = function() { + return this.merkleBlock ? this.merkleBlock.toBuffer() : BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + MerkleBlock = options.MerkleBlock || MerkleBlock; + return MerkleblockMessage; +}; diff --git a/lib/messages/commands/notfound.js b/lib/messages/commands/notfound.js new file mode 100644 index 0000000..d8f27f4 --- /dev/null +++ b/lib/messages/commands/notfound.js @@ -0,0 +1,50 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; +var BufferWriter = bitcore.encoding.BufferWriter; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function NotfoundMessage(options) { + Message.call(this, options); + this.command = 'notfound'; + this.magicNumber = magicNumber; + this.inventory = options.inventory; +} +inherits(NotfoundMessage, Message); + +NotfoundMessage.fromObject = function(options) { + return new NotfoundMessage(options); +}; + +NotfoundMessage.fromBuffer = function(payload) { + var obj = { + inventory: [] + }; + + var parser = new BufferReader(payload); + var count = parser.readVarintNum(); + for (var i = 0; i < count; i++) { + var type = parser.readUInt32LE(); + var hash = parser.read(32); + obj.inventory.push({type: type, hash: hash}); + } + + utils.checkFinished(parser); + return NotfoundMessage.fromObject(obj); +}; + +NotfoundMessage.prototype.getPayload = function() { + var bw = new BufferWriter(); + utils.writeInventory(this.inventory, bw); + return bw.concat(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return NotfoundMessage; +}; diff --git a/lib/messages/commands/pong.js b/lib/messages/commands/pong.js new file mode 100644 index 0000000..b91cf51 --- /dev/null +++ b/lib/messages/commands/pong.js @@ -0,0 +1,39 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var utils = require('../utils'); +var BufferReader = bitcore.encoding.BufferReader; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function PongMessage(options) { + Message.call(this, options); + this.command = 'pong'; + this.magicNumber = magicNumber; + this.nonce = options.nonce; +} +inherits(PongMessage, Message); + +PongMessage.fromObject = function(obj) { + return new PongMessage(obj); +}; + +PongMessage.fromBuffer = function(payload) { + var obj = {}; + var parser = new BufferReader(payload); + obj.nonce = parser.read(8); + + utils.checkFinished(parser); + return PongMessage.fromObject(obj); +}; + +PongMessage.prototype.getPayload = function() { + return this.nonce; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return PongMessage; +}; diff --git a/lib/messages/commands/reject.js b/lib/messages/commands/reject.js new file mode 100644 index 0000000..4b38d67 --- /dev/null +++ b/lib/messages/commands/reject.js @@ -0,0 +1,34 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); +var BufferUtil = bitcore.util.buffer; + +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +// todo: add payload: https://en.bitcoin.it/wiki/Protocol_documentation#reject +function RejectMessage(options) { + Message.call(this, options); + this.magicNumber = magicNumber; + this.command = 'reject'; +}; +inherits(RejectMessage, Message); + +RejectMessage.fromObject = function(options) { + return new RejectMessage(options); +}; + +RejectMessage.fromBuffer = function(payload) { + var obj = {}; + return RejectMessage.fromObject(obj); +}; + +RejectMessage.prototype.getPayload = function() { + return BufferUtil.EMPTY_BUFFER; +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + return RejectMessage; +}; diff --git a/lib/messages/commands/tx.js b/lib/messages/commands/tx.js new file mode 100644 index 0000000..676809a --- /dev/null +++ b/lib/messages/commands/tx.js @@ -0,0 +1,40 @@ +'use strict'; + +var Message = require('../message'); +var inherits = require('util').inherits; +var bitcore = require('bitcore'); + +var Transaction = bitcore.Transaction; +var magicNumber = bitcore.Networks.defaultNetwork.networkMagic.readUInt32LE(0); + +function TransactionMessage(options) { + Message.call(this, options); + this.command = 'tx'; + this.magicNumber = magicNumber; + this.transaction = options.transaction; +}; +inherits(TransactionMessage, Message); + +TransactionMessage.fromObject = function(options) { + return new TransactionMessage(options); +}; + +TransactionMessage.fromBuffer = function(payload) { + var transaction; + if (Transaction.prototype.fromBuffer) { + transaction = Transaction().fromBuffer(payload); + } else { + transaction = Transaction.fromBuffer(payload); + } + return TransactionMessage.fromObject({transaction: transaction}); +}; + +TransactionMessage.prototype.getPayload = function() { + return this.transaction.toBuffer(); +}; + +module.exports = function(options) { + magicNumber = options.magicNumber || magicNumber; + Transaction = options.Transaction || Transaction; + return TransactionMessage; +};