diff --git a/lib/address.js b/lib/address.js index b79d5cd..d280988 100644 --- a/lib/address.js +++ b/lib/address.js @@ -11,7 +11,7 @@ var bufferEqual = require("buffer-equal"); var bs58 = require("bs58"); var assert = require("./util").assert; var var_int = require("./structs").var_int; -var pubkeyBitfield = require("./structs").pubkeyBitfield; +var PubkeyBitfield = require("./structs").PubkeyBitfield; var bmcrypto = require("./crypto"); /** @@ -32,10 +32,8 @@ function Address(opts) { // Merge remained values. objectAssign(this, opts); this.stream = this.stream || 1; - if (!this.pubkeyFeatures) { - // Fill up default features. - this.pubkeyFeatures = pubkeyBitfield.encode([pubkeyBitfield.DOES_ACK]); - } + this.pubkeyFeatures = this.pubkeyFeatures || + PubkeyBitfield().set(PubkeyBitfield.DOES_ACK); } /** diff --git a/lib/messages.js b/lib/messages.js index 9616100..de99091 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -11,6 +11,7 @@ var assert = require("./util").assert; var structs = require("./structs"); +var ServicesBitfield = structs.ServicesBitfield; var UserAgent = require("./user-agent"); var util = require("./util"); @@ -33,7 +34,7 @@ exports.version = { // 4 + 8 + 8 + 26 + 26 + 8 + (1+) + (1+) assert(buf.length >= 82, "Buffer is too small"); var protoVersion = buf.readUInt32BE(0, true); - var services = structs.servicesBitfield.decode(buf.slice(4, 12)); + var services = ServicesBitfield(buf.slice(4, 12), {copy: true}); var time = util.readTime64BE(buf, 12); var short = {short: true}; var addrRecv = structs.net_addr.decode(buf.slice(20, 46), short); @@ -67,7 +68,8 @@ exports.version = { */ encode: function(opts) { // Deal with default options. - var services = opts.services || [structs.servicesBitfield.NODE_NETWORK]; + var services = opts.services || + ServicesBitfield().set(ServicesBitfield.NODE_NETWORK); var time = opts.time || new Date(); var nonce = opts.nonce || exports.version.NONCE; assert(nonce.length === 8, "Bad nonce"); @@ -90,7 +92,7 @@ exports.version = { }); return Buffer.concat([ protoVersion, - structs.servicesBitfield.encode(services), + services.buffer, util.writeTime64BE(null, time), addrRecv, addrFrom, diff --git a/lib/structs.js b/lib/structs.js index 3d561a8..7f281fc 100644 --- a/lib/structs.js +++ b/lib/structs.js @@ -379,7 +379,7 @@ exports.net_addr = { res.stream = buf.readUInt32BE(8, true); buf = buf.slice(12); } - res.services = servicesBitfield.decode(buf.slice(0, 8)); + res.services = ServicesBitfield(buf.slice(0, 8), {copy: true}); res.host = inet_ntop(buf.slice(8, 24)); res.port = buf.readUInt16BE(24, true); return res; @@ -409,8 +409,9 @@ exports.net_addr = { buf.writeUInt32BE(stream, 8); shift = 12; } - var services = opts.services || [servicesBitfield.NODE_NETWORK]; - servicesBitfield.encode(services).copy(buf, shift); + var services = opts.services || + ServicesBitfield().set(ServicesBitfield.NODE_NETWORK); + services.buffer.copy(buf, shift); inet_pton(opts.host).copy(buf, shift + 8); buf.writeUInt16BE(opts.port, shift + 24); return buf; @@ -497,37 +498,58 @@ exports.encrypted = { }; // Creates bitfield (LSB 0) class of the specified size. -var bitfield = function(size) { +var Bitfield = function(size) { var bytesize = size / 8; - return { - decode: function(buf) { - assert(buf.length === bytesize, "Bad buffer size"); - var features = []; - var index; - for (var i = 0; i < size; i++) { - index = bytesize - Math.floor(i / 8) - 1; - if ((buf[index] & (1 << (i % 8))) !== 0) { // jshint ignore:line - features.push(i); - } - } - return features; - }, - encode: function(features) { - var buf = new Buffer(bytesize); - buf.fill(0); - if (!Array.isArray(features)) { - features = [features]; + // is the source of inspiration. + function BitfieldInner(buf, opts) { + if (!(this instanceof BitfieldInner)) { + return new BitfieldInner(buf); + } + opts = opts || {}; + if (buf) { + assert(buf.length === bytesize, "Bad buffer size"); + if (opts.copy) { + var dup = new Buffer(bytesize); + dup.fill(0); + buf.copy(dup); + buf = dup; } - features.forEach(function(feature) { - assert(feature >= 0, "Bad feature"); - assert(feature <= (size - 1), "Bad feature"); - var index = bytesize - Math.floor(feature / 8) - 1; - buf[index] |= 1 << (feature % 8); // jshint ignore:line - }); - return buf; - }, + } else { + buf = new Buffer(bytesize); + buf.fill(0); + } + this.buffer = buf; + } + + BitfieldInner.prototype.get = function(bits) { + if (!Array.isArray(bits)) { + bits = [bits]; + } + var buf = this.buffer; + return bits.every(function(bit) { + assert(bit >= 0, "Bit number is too low"); + assert(bit < size, "Bit number is too high"); + var index = bytesize - Math.floor(bit / 8) - 1; + return (buf[index] & (1 << (bit % 8))) !== 0; // jshint ignore:line + }); }; + + BitfieldInner.prototype.set = function(bits) { + if (!Array.isArray(bits)) { + bits = [bits]; + } + var buf = this.buffer; + bits.forEach(function(bit) { + assert(bit >= 0, "Bit number is too low"); + assert(bit < size, "Bit number is too high"); + var index = bytesize - Math.floor(bit / 8) - 1; + buf[index] |= 1 << (bit % 8); // jshint ignore:line + }); + return this; + }; + + return BitfieldInner; }; /** @@ -537,7 +559,7 @@ var bitfield = function(size) { * @static */ // TODO(Kagami): Document methods. -var servicesBitfield = exports.servicesBitfield = objectAssign(bitfield(64), { +var ServicesBitfield = exports.ServicesBitfield = objectAssign(Bitfield(64), { /** This is a normal network node. */ NODE_NETWORK: 0, }); @@ -551,7 +573,7 @@ var servicesBitfield = exports.servicesBitfield = objectAssign(bitfield(64), { // XXX(Kagami): PyBitmessage uses MSB 0 scheme for this bitfield so we // invert the numberes. See // for details. -exports.pubkeyBitfield = objectAssign(bitfield(32), { +exports.PubkeyBitfield = objectAssign(Bitfield(32), { /** * Receiving node expects that the RIPE hash encoded in their address * preceedes the encrypted message data of msg messages bound for diff --git a/test.js b/test.js index 4395510..edbbaff 100644 --- a/test.js +++ b/test.js @@ -14,8 +14,8 @@ var var_int_list = structs.var_int_list; var net_addr = structs.net_addr; var inv_vect = structs.inv_vect; var encrypted = structs.encrypted; -var servicesBitfield = structs.servicesBitfield; -var pubkeyBitfield = structs.pubkeyBitfield; +var ServicesBitfield = structs.ServicesBitfield; +var PubkeyBitfield = structs.PubkeyBitfield; var messages = bitmessage.messages; var version = messages.version; var addr = messages.addr; @@ -209,27 +209,27 @@ describe("Common structures", function() { res = net_addr.decode(Buffer("0000000054aaf6c000000001000000000000000100000000000000000000ffff7f00000120fc", "hex")); expect(res.time.getTime()).to.equal(1420490432000); expect(res.stream).to.equal(1); - expect(res.services).to.have.members([servicesBitfield.NODE_NETWORK]); + expect(res.services.get(ServicesBitfield.NODE_NETWORK)).to.be.true; expect(res.host).to.equal("127.0.0.1"); expect(res.port).to.equal(8444); expect(net_addr.decode.bind(null, Buffer("000000000000000100000000000000000000ffff7f00000120fc", "hex"))).to.throw(Error);; res = net_addr.decode(Buffer("000000000000000100000000000000000000ffff7f00000120fc", "hex"), {short: true}); - expect(res.services).to.have.members([servicesBitfield.NODE_NETWORK]); + expect(res.services.get(ServicesBitfield.NODE_NETWORK)).to.be.true; expect(res.host).to.equal("127.0.0.1"); expect(res.port).to.equal(8444); res = net_addr.decode(Buffer("000000000000000100000000000000000000000000000001fde8", "hex"), {short: true}); - expect(res.services).to.have.members([servicesBitfield.NODE_NETWORK]); + expect(res.services.get(ServicesBitfield.NODE_NETWORK)).to.be.true; expect(res.host).to.equal("0:0:0:0:0:0:0:1"); expect(res.port).to.equal(65000); }); it("should encode", function() { var time = new Date(1420490432000); - expect(net_addr.encode({time: time, stream: 1, services: [servicesBitfield.NODE_NETWORK], host: "127.0.0.1", port: 8444}).toString("hex")).to.equal("0000000054aaf6c000000001000000000000000100000000000000000000ffff7f00000120fc"); - expect(net_addr.encode({short: true, services: [servicesBitfield.NODE_NETWORK], host: "127.0.0.1", port: 8444}).toString("hex")).to.equal("000000000000000100000000000000000000ffff7f00000120fc"); + expect(net_addr.encode({time: time, stream: 1, services: ServicesBitfield().set(ServicesBitfield.NODE_NETWORK), host: "127.0.0.1", port: 8444}).toString("hex")).to.equal("0000000054aaf6c000000001000000000000000100000000000000000000ffff7f00000120fc"); + expect(net_addr.encode({short: true, services: ServicesBitfield().set(ServicesBitfield.NODE_NETWORK), host: "127.0.0.1", port: 8444}).toString("hex")).to.equal("000000000000000100000000000000000000ffff7f00000120fc"); expect(net_addr.encode({short: true, host: "::1", port: 65000}).toString("hex")).to.equal("000000000000000100000000000000000000000000000001fde8"); }); }); @@ -265,24 +265,24 @@ describe("Common structures", function() { }); describe("service features", function() { - it("should decode", function() { - expect(servicesBitfield.decode(Buffer("0000000000000001", "hex"))).to.have.members([servicesBitfield.NODE_NETWORK]); + it("should allow to check bits", function() { + expect(ServicesBitfield(Buffer("0000000000000001", "hex")).get(ServicesBitfield.NODE_NETWORK)).to.be.true; }); - it("should encode", function() { - expect(servicesBitfield.encode([servicesBitfield.NODE_NETWORK]).toString("hex")).to.equal("0000000000000001"); - expect(servicesBitfield.encode(servicesBitfield.NODE_NETWORK).toString("hex")).to.equal("0000000000000001"); + it("should allow to set bits", function() { + expect(ServicesBitfield().set([ServicesBitfield.NODE_NETWORK]).buffer.toString("hex")).to.equal("0000000000000001"); + expect(ServicesBitfield().set(ServicesBitfield.NODE_NETWORK).buffer.toString("hex")).to.equal("0000000000000001"); }); }); describe("pubkey features", function() { - it("should decode", function() { - expect(pubkeyBitfield.decode(Buffer("00000003", "hex"))).to.have.members([pubkeyBitfield.DOES_ACK, pubkeyBitfield.INCLUDE_DESTINATION]); + it("should allow to check bits", function() { + expect(PubkeyBitfield(Buffer("00000003", "hex")).get([PubkeyBitfield.DOES_ACK, PubkeyBitfield.INCLUDE_DESTINATION])).to.be.true; }); - it("should encode", function() { - expect(pubkeyBitfield.encode([pubkeyBitfield.INCLUDE_DESTINATION, pubkeyBitfield.DOES_ACK]).toString("hex")).to.equal("00000003"); - expect(pubkeyBitfield.encode(pubkeyBitfield.DOES_ACK).toString("hex")).to.equal("00000001"); + it("should allow to set bits", function() { + expect(PubkeyBitfield().set([PubkeyBitfield.INCLUDE_DESTINATION, PubkeyBitfield.DOES_ACK]).buffer.toString("hex")).to.equal("00000003"); + expect(PubkeyBitfield().set(PubkeyBitfield.DOES_ACK).buffer.toString("hex")).to.equal("00000001"); }); }); }); @@ -296,7 +296,7 @@ describe("Message types", function() { port: 8444, })); expect(res.version).to.equal(3); - expect(res.services).to.deep.equal([servicesBitfield.NODE_NETWORK]); + expect(res.services.get(ServicesBitfield.NODE_NETWORK)).to.be.true; expect(res.time).to.be.instanceof(Date); expect(res.remoteHost).to.equal("1.2.3.4"); expect(res.remotePort).to.equal(48444);