From 514265b7cda3d97a08a309e3149724e4b3787e73 Mon Sep 17 00:00:00 2001 From: Kagami Hiiragi Date: Mon, 26 Jan 2015 20:03:43 +0300 Subject: [PATCH] Add encodePayload/decodePayload to messages module --- lib/messages.js | 124 +++++++++++++++++++++++++++++++++++++++++++----- test.js | 45 ++++++++++++++---- 2 files changed, 149 insertions(+), 20 deletions(-) diff --git a/lib/messages.js b/lib/messages.js index 1069d1b..0b0c129 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -9,29 +9,44 @@ "use strict"; +var objectAssign = Object.assign || require("object-assign"); var assert = require("./_util").assert; var structs = require("./structs"); var UserAgent = require("./user-agent"); var util = require("./_util"); +var message = structs.message; var ServicesBitfield = structs.ServicesBitfield; /** * `version` message. * @see {@link https://bitmessage.org/wiki/Protocol_specification#version} * @namespace + * @static */ -exports.version = { +var version = exports.version = { /** Random nonce used to detect connections to self. */ NONCE: new Buffer("20bde0a3355dad78", "hex"), + /** + * Decode `version` message. + * NOTE: `nonce` is copied. + * @param {Buffer} buf - Message + * @return {Object} Decoded `version` structure. + */ + decode: function(buf) { + var decoded = message.decode(buf); + assert(decoded.command === "version", "Bad command"); + return version.decodePayload(decoded.payload); + }, + /** * Decode `version` message payload. * NOTE: `nonce` is copied. * @param {Buffer} buf - Message payload * @return {Object} Decoded `version` structure. */ - decode: function(buf) { + decodePayload: function(buf) { // 4 + 8 + 8 + 26 + 26 + 8 + (1+) + (1+) assert(buf.length >= 82, "Buffer is too small"); var protoVersion = buf.readUInt32BE(0, true); @@ -62,17 +77,27 @@ exports.version = { }; }, + /** + * Encode `version` message. + * @param {Object} opts - Version options + * @return {Buffer} Encoded message. + */ + encode: function(opts) { + var payload = version.encodePayload(opts); + return message.encode("version", payload); + }, + /** * Encode `version` message payload. * @param {Object} opts - Version options * @return {Buffer} Encoded payload. */ - encode: function(opts) { + encodePayload: function(opts) { // Deal with default options. var services = opts.services || ServicesBitfield().set(ServicesBitfield.NODE_NETWORK); var time = opts.time || new Date(); - var nonce = opts.nonce || exports.version.NONCE; + var nonce = opts.nonce || version.NONCE; assert(nonce.length === 8, "Bad nonce"); var userAgent = opts.userAgent || UserAgent.SELF; var streamNumbers = opts.streamNumbers || [1]; @@ -108,14 +133,26 @@ exports.version = { * `addr` message. Provide information on known nodes of the network. * @see {@link https://bitmessage.org/wiki/Protocol_specification#addr} * @namespace + * @static */ -exports.addr = { +var addr = exports.addr = { + /** + * Decode `addr` message. + * @param {Buffer} buf - Message + * @return {Object} Decoded `addr` structure. + */ + decode: function(buf) { + var decoded = message.decode(buf); + assert(decoded.command === "addr", "Bad command"); + return addr.decodePayload(decoded.payload); + }, + /** * Decode `addr` message payload. * @param {Buffer} buf - Message payload * @return {Object} Decoded `addr` structure. */ - decode: function(buf) { + decodePayload: function(buf) { var decoded = structs.var_int.decode(buf); var listLength = decoded.value; assert(listLength <= 1000, "Too many address entires"); @@ -133,12 +170,22 @@ exports.addr = { }; }, + /** + * Encode `addr` message. + * @param {Object[]} addrs - Network addresses + * @return {Buffer} Encoded message. + */ + encode: function(addrs) { + var payload = addr.encodePayload(addrs); + return message.encode("addr", payload); + }, + /** * Encode `addr` message payload. * @param {Object[]} addrs - Network addresses * @return {Buffer} Encoded payload. */ - encode: function(addrs) { + encodePayload: function(addrs) { assert(addrs.length <= 1000, "Too many address entires"); var addrsBuf = Buffer.concat(addrs.map(structs.net_addr.encode)); return Buffer.concat([structs.var_int.encode(addrs.length), addrsBuf]); @@ -153,12 +200,23 @@ exports.addr = { * @static */ var inv = exports.inv = { + /** + * Decode `inv` message. + * @param {Buffer} buf - Message + * @return {Object} Decoded `inv` structure. + */ + decode: function(buf) { + var decoded = message.decode(buf); + assert(decoded.command === "inv", "Bad command"); + return inv.decodePayload(decoded.payload); + }, + /** * Decode `inv` message payload. * @param {Buffer} buf - Message payload * @return {Object} Decoded `inv` structure. */ - decode: function(buf) { + decodePayload: function(buf) { var decoded = structs.var_int.decode(buf); var listLength = decoded.value; assert(listLength <= 50000, "Too many inventory entires"); @@ -176,12 +234,22 @@ var inv = exports.inv = { }; }, + /** + * Encode `inv` message. + * @param {Buffer[]} inventory - Inventory vector list (encoded) + * @return {Buffer} Encoded message. + */ + encode: function(inventory) { + var payload = inv.encodePayload(inventory); + return message.encode("inv", payload); + }, + /** * Encode `inv` message payload. * @param {Buffer[]} inventory - Inventory vector list (encoded) * @return {Buffer} Encoded payload. */ - encode: function(inventory) { + encodePayload: function(inventory) { assert(inventory.length <= 50000, "Too many inventory entires"); var invBuf = Buffer.concat(inventory); return Buffer.concat([structs.var_int.encode(inventory.length), invBuf]); @@ -195,12 +263,23 @@ var inv = exports.inv = { * @see {@link https://bitmessage.org/wiki/Protocol_specification#getdata} * @namespace */ -exports.getdata = inv; +exports.getdata = objectAssign({}, inv, { + decode: function(buf) { + var decoded = message.decode(buf); + assert(decoded.command === "getdata", "Bad command"); + return inv.decodePayload(decoded.payload); + }, + encode: function(inventory) { + var payload = inv.encodePayload(inventory); + return message.encode("getdata", payload); + }, +}); /** * `error` message. * @see {@link https://bitmessage.org/wiki/Protocol_specification_v3#error} * @namespace + * @static */ var error = exports.error = { /** @@ -219,12 +298,23 @@ var error = exports.error = { */ FATAL: 2, + /** + * Decode `error` message. + * @param {Buffer} buf - Message + * @return {Object} Decoded `error` structure. + */ + decode: function(buf) { + var decoded = message.decode(buf); + assert(decoded.command === "error", "Bad command"); + return error.decodePayload(decoded.payload); + }, + /** * Decode `error` message payload. * @param {Buffer} buf - Message payload * @return {Object} Decoded `error` structure. */ - decode: function(buf) { + decodePayload: function(buf) { assert(buf.length >= 4, "Buffer is too small"); var decodedFatal = structs.var_int.decode(buf); var decodedBanTime = structs.var_int.decode(decodedFatal.rest); @@ -246,12 +336,22 @@ var error = exports.error = { }; }, + /** + * Encode `error` message. + * @param {Object} opts - Error options + * @return {Buffer} Encoded message. + */ + encode: function(opts) { + var payload = error.encodePayload(opts); + return message.encode("error", payload); + }, + /** * Encode `error` message payload. * @param {Object} opts - Error options * @return {Buffer} Encoded payload. */ - encode: function(opts) { + encodePayload: function(opts) { var fatal = opts.fatal || error.WARNING; var banTime = opts.banTime || 0; var vector = opts.vector || ""; diff --git a/test.js b/test.js index 2a119d7..9d28b6c 100644 --- a/test.js +++ b/test.js @@ -21,6 +21,7 @@ var messages = bitmessage.messages; var version = messages.version; var addr = messages.addr; var inv = messages.inv; +var getdata = messages.getdata; var error = messages.error; var objects = bitmessage.objects; var getpubkey = objects.getpubkey; @@ -362,11 +363,13 @@ describe("Common structures", function() { describe("Message types", function() { describe("version", function() { it("should encode and decode", function() { - var res = version.decode(version.encode({ + var encoded = version.encode({ remoteHost: "1.2.3.4", remotePort: 48444, port: 8444, - })); + }); + expect(message.decode(encoded).command).to.equal("version"); + var res = version.decode(encoded); expect(res.version).to.equal(3); expect(res.services.get(ServicesBitfield.NODE_NETWORK)).to.be.true; expect(res.time).to.be.instanceof(Date); @@ -396,10 +399,12 @@ describe("Message types", function() { expect(res.length).to.equal(1); expect(res.addrs).to.deep.equal([]); - res = addr.decode(addr.encode([ + var encoded = addr.encode([ {host: "1.2.3.4", port: 8444}, {host: "ff::1", port: 18444}, - ])); + ]); + expect(message.decode(encoded).command).to.equal("addr"); + res = addr.decode(encoded); expect(res.length).to.equal(77); expect(res.addrs.length).to.equal(2); expect(res.addrs[0].host).to.equal("1.2.3.4"); @@ -410,7 +415,7 @@ describe("Message types", function() { it("shouldn't encode/decode more than 1000 entires", function() { expect(addr.encode.bind(null, Array(2000))).to.throw(/too many/i); - expect(addr.decode.bind(null, var_int.encode(2000))).to.throw(/too many/i); + expect(addr.decodePayload.bind(null, var_int.encode(2000))).to.throw(/too many/i); }); }); @@ -419,7 +424,9 @@ describe("Message types", function() { var vect1 = inv_vect.encode(Buffer("test")); var vect2 = inv_vect.encode(Buffer("test2")); var inventory = [vect1, vect2]; - var res = inv.decode(inv.encode(inventory)); + var encoded = inv.encode(inventory); + expect(message.decode(encoded).command).to.equal("inv"); + var res = inv.decode(encoded); expect(res.inventory.length).to.equal(2); expect(bufferEqual(res.inventory[0], vect1)).to.be.true; expect(bufferEqual(res.inventory[1], vect2)).to.be.true; @@ -428,13 +435,35 @@ describe("Message types", function() { it("shouldn't encode/decode more than 50000 entires", function() { expect(inv.encode.bind(null, Array(60000))).to.throw(/too many/i); - expect(inv.decode.bind(null, var_int.encode(60000))).to.throw(/too many/i); + expect(inv.decodePayload.bind(null, var_int.encode(60000))).to.throw(/too many/i); + }); + }); + + describe("getdata", function() { + it("should encode and decode", function() { + var vect1 = inv_vect.encode(Buffer("test")); + var vect2 = inv_vect.encode(Buffer("test2")); + var inventory = [vect1, vect2]; + var encoded = getdata.encode(inventory); + expect(message.decode(encoded).command).to.equal("getdata"); + var res = getdata.decode(encoded); + expect(res.inventory.length).to.equal(2); + expect(bufferEqual(res.inventory[0], vect1)).to.be.true; + expect(bufferEqual(res.inventory[1], vect2)).to.be.true; + expect(res.length).to.equal(65); + }); + + it("shouldn't encode/decode more than 50000 entires", function() { + expect(getdata.encode.bind(null, Array(60000))).to.throw(/too many/i); + expect(getdata.decodePayload.bind(null, var_int.encode(60000))).to.throw(/too many/i); }); }); describe("error", function() { it("should encode and decode", function() { - var res = error.decode(error.encode({errorText: "test"})); + var encoded = error.encode({errorText: "test"}); + expect(message.decode(encoded).command).to.equal("error"); + var res = error.decode(encoded); expect(res.fatal).to.equal(0); expect(res.banTime).to.equal(0); expect(res.vector).to.equal("");