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