Add encodePayload/decodePayload to messages module

This commit is contained in:
Kagami Hiiragi 2015-01-26 20:03:43 +03:00
parent 874acfac45
commit 514265b7cd
2 changed files with 149 additions and 20 deletions

View File

@ -9,29 +9,44 @@
"use strict"; "use strict";
var objectAssign = Object.assign || require("object-assign");
var assert = require("./_util").assert; var assert = require("./_util").assert;
var structs = require("./structs"); var structs = require("./structs");
var UserAgent = require("./user-agent"); var UserAgent = require("./user-agent");
var util = require("./_util"); var util = require("./_util");
var message = structs.message;
var ServicesBitfield = structs.ServicesBitfield; var ServicesBitfield = structs.ServicesBitfield;
/** /**
* `version` message. * `version` message.
* @see {@link https://bitmessage.org/wiki/Protocol_specification#version} * @see {@link https://bitmessage.org/wiki/Protocol_specification#version}
* @namespace * @namespace
* @static
*/ */
exports.version = { var version = exports.version = {
/** Random nonce used to detect connections to self. */ /** Random nonce used to detect connections to self. */
NONCE: new Buffer("20bde0a3355dad78", "hex"), 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. * Decode `version` message payload.
* NOTE: `nonce` is copied. * NOTE: `nonce` is copied.
* @param {Buffer} buf - Message payload * @param {Buffer} buf - Message payload
* @return {Object} Decoded `version` structure. * @return {Object} Decoded `version` structure.
*/ */
decode: function(buf) { decodePayload: function(buf) {
// 4 + 8 + 8 + 26 + 26 + 8 + (1+) + (1+) // 4 + 8 + 8 + 26 + 26 + 8 + (1+) + (1+)
assert(buf.length >= 82, "Buffer is too small"); assert(buf.length >= 82, "Buffer is too small");
var protoVersion = buf.readUInt32BE(0, true); 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. * Encode `version` message payload.
* @param {Object} opts - Version options * @param {Object} opts - Version options
* @return {Buffer} Encoded payload. * @return {Buffer} Encoded payload.
*/ */
encode: function(opts) { encodePayload: function(opts) {
// Deal with default options. // Deal with default options.
var services = opts.services || var services = opts.services ||
ServicesBitfield().set(ServicesBitfield.NODE_NETWORK); ServicesBitfield().set(ServicesBitfield.NODE_NETWORK);
var time = opts.time || new Date(); 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"); assert(nonce.length === 8, "Bad nonce");
var userAgent = opts.userAgent || UserAgent.SELF; var userAgent = opts.userAgent || UserAgent.SELF;
var streamNumbers = opts.streamNumbers || [1]; var streamNumbers = opts.streamNumbers || [1];
@ -108,14 +133,26 @@ exports.version = {
* `addr` message. Provide information on known nodes of the network. * `addr` message. Provide information on known nodes of the network.
* @see {@link https://bitmessage.org/wiki/Protocol_specification#addr} * @see {@link https://bitmessage.org/wiki/Protocol_specification#addr}
* @namespace * @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. * Decode `addr` message payload.
* @param {Buffer} buf - Message payload * @param {Buffer} buf - Message payload
* @return {Object} Decoded `addr` structure. * @return {Object} Decoded `addr` structure.
*/ */
decode: function(buf) { decodePayload: function(buf) {
var decoded = structs.var_int.decode(buf); var decoded = structs.var_int.decode(buf);
var listLength = decoded.value; var listLength = decoded.value;
assert(listLength <= 1000, "Too many address entires"); 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. * Encode `addr` message payload.
* @param {Object[]} addrs - Network addresses * @param {Object[]} addrs - Network addresses
* @return {Buffer} Encoded payload. * @return {Buffer} Encoded payload.
*/ */
encode: function(addrs) { encodePayload: function(addrs) {
assert(addrs.length <= 1000, "Too many address entires"); assert(addrs.length <= 1000, "Too many address entires");
var addrsBuf = Buffer.concat(addrs.map(structs.net_addr.encode)); var addrsBuf = Buffer.concat(addrs.map(structs.net_addr.encode));
return Buffer.concat([structs.var_int.encode(addrs.length), addrsBuf]); return Buffer.concat([structs.var_int.encode(addrs.length), addrsBuf]);
@ -153,12 +200,23 @@ exports.addr = {
* @static * @static
*/ */
var inv = exports.inv = { 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. * Decode `inv` message payload.
* @param {Buffer} buf - Message payload * @param {Buffer} buf - Message payload
* @return {Object} Decoded `inv` structure. * @return {Object} Decoded `inv` structure.
*/ */
decode: function(buf) { decodePayload: function(buf) {
var decoded = structs.var_int.decode(buf); var decoded = structs.var_int.decode(buf);
var listLength = decoded.value; var listLength = decoded.value;
assert(listLength <= 50000, "Too many inventory entires"); 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. * Encode `inv` message payload.
* @param {Buffer[]} inventory - Inventory vector list (encoded) * @param {Buffer[]} inventory - Inventory vector list (encoded)
* @return {Buffer} Encoded payload. * @return {Buffer} Encoded payload.
*/ */
encode: function(inventory) { encodePayload: function(inventory) {
assert(inventory.length <= 50000, "Too many inventory entires"); assert(inventory.length <= 50000, "Too many inventory entires");
var invBuf = Buffer.concat(inventory); var invBuf = Buffer.concat(inventory);
return Buffer.concat([structs.var_int.encode(inventory.length), invBuf]); 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} * @see {@link https://bitmessage.org/wiki/Protocol_specification#getdata}
* @namespace * @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. * `error` message.
* @see {@link https://bitmessage.org/wiki/Protocol_specification_v3#error} * @see {@link https://bitmessage.org/wiki/Protocol_specification_v3#error}
* @namespace * @namespace
* @static
*/ */
var error = exports.error = { var error = exports.error = {
/** /**
@ -219,12 +298,23 @@ var error = exports.error = {
*/ */
FATAL: 2, 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. * Decode `error` message payload.
* @param {Buffer} buf - Message payload * @param {Buffer} buf - Message payload
* @return {Object} Decoded `error` structure. * @return {Object} Decoded `error` structure.
*/ */
decode: function(buf) { decodePayload: function(buf) {
assert(buf.length >= 4, "Buffer is too small"); assert(buf.length >= 4, "Buffer is too small");
var decodedFatal = structs.var_int.decode(buf); var decodedFatal = structs.var_int.decode(buf);
var decodedBanTime = structs.var_int.decode(decodedFatal.rest); 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. * Encode `error` message payload.
* @param {Object} opts - Error options * @param {Object} opts - Error options
* @return {Buffer} Encoded payload. * @return {Buffer} Encoded payload.
*/ */
encode: function(opts) { encodePayload: function(opts) {
var fatal = opts.fatal || error.WARNING; var fatal = opts.fatal || error.WARNING;
var banTime = opts.banTime || 0; var banTime = opts.banTime || 0;
var vector = opts.vector || ""; var vector = opts.vector || "";

45
test.js
View File

@ -21,6 +21,7 @@ var messages = bitmessage.messages;
var version = messages.version; var version = messages.version;
var addr = messages.addr; var addr = messages.addr;
var inv = messages.inv; var inv = messages.inv;
var getdata = messages.getdata;
var error = messages.error; var error = messages.error;
var objects = bitmessage.objects; var objects = bitmessage.objects;
var getpubkey = objects.getpubkey; var getpubkey = objects.getpubkey;
@ -362,11 +363,13 @@ describe("Common structures", function() {
describe("Message types", function() { describe("Message types", function() {
describe("version", function() { describe("version", function() {
it("should encode and decode", function() { it("should encode and decode", function() {
var res = version.decode(version.encode({ var encoded = version.encode({
remoteHost: "1.2.3.4", remoteHost: "1.2.3.4",
remotePort: 48444, remotePort: 48444,
port: 8444, port: 8444,
})); });
expect(message.decode(encoded).command).to.equal("version");
var res = version.decode(encoded);
expect(res.version).to.equal(3); expect(res.version).to.equal(3);
expect(res.services.get(ServicesBitfield.NODE_NETWORK)).to.be.true; expect(res.services.get(ServicesBitfield.NODE_NETWORK)).to.be.true;
expect(res.time).to.be.instanceof(Date); expect(res.time).to.be.instanceof(Date);
@ -396,10 +399,12 @@ describe("Message types", function() {
expect(res.length).to.equal(1); expect(res.length).to.equal(1);
expect(res.addrs).to.deep.equal([]); expect(res.addrs).to.deep.equal([]);
res = addr.decode(addr.encode([ var encoded = addr.encode([
{host: "1.2.3.4", port: 8444}, {host: "1.2.3.4", port: 8444},
{host: "ff::1", port: 18444}, {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.length).to.equal(77);
expect(res.addrs.length).to.equal(2); expect(res.addrs.length).to.equal(2);
expect(res.addrs[0].host).to.equal("1.2.3.4"); 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() { it("shouldn't encode/decode more than 1000 entires", function() {
expect(addr.encode.bind(null, Array(2000))).to.throw(/too many/i); 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 vect1 = inv_vect.encode(Buffer("test"));
var vect2 = inv_vect.encode(Buffer("test2")); var vect2 = inv_vect.encode(Buffer("test2"));
var inventory = [vect1, vect2]; 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(res.inventory.length).to.equal(2);
expect(bufferEqual(res.inventory[0], vect1)).to.be.true; expect(bufferEqual(res.inventory[0], vect1)).to.be.true;
expect(bufferEqual(res.inventory[1], vect2)).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() { it("shouldn't encode/decode more than 50000 entires", function() {
expect(inv.encode.bind(null, Array(60000))).to.throw(/too many/i); 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() { describe("error", function() {
it("should encode and decode", 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.fatal).to.equal(0);
expect(res.banTime).to.equal(0); expect(res.banTime).to.equal(0);
expect(res.vector).to.equal(""); expect(res.vector).to.equal("");