Implement messages.version
This commit is contained in:
parent
3d5968bcc1
commit
808fc5eaf1
|
@ -36,7 +36,7 @@ API documentation is available [here](https://bitchan.github.io/bitmessage/docs/
|
|||
- [x] service features
|
||||
- [x] pubkey features
|
||||
- [ ] Message types
|
||||
- [ ] version
|
||||
- [x] version
|
||||
- [ ] verack
|
||||
- [ ] addr
|
||||
- [ ] inv
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/** Current protocol version. */
|
||||
exports.PROTOCOL_VERSION = 3;
|
||||
|
||||
/** [Common structures.]{@link module:bitmessage/structs} */
|
||||
exports.structs = require("./structs");
|
||||
/** [Messages.]{@link module:bitmessage/messages} */
|
||||
|
|
|
@ -3,3 +3,97 @@
|
|||
* @see {@link https://bitmessage.org/wiki/Protocol_specification#Message_types}
|
||||
* @module bitmessage/messages
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
var assert = require("./util").assert;
|
||||
var structs = require("./structs");
|
||||
var UserAgent = require("./user-agent");
|
||||
var util = require("./util");
|
||||
|
||||
/**
|
||||
* Version message.
|
||||
* @see {@link https://bitmessage.org/wiki/Protocol_specification#version}
|
||||
* @namespace
|
||||
*/
|
||||
exports.version = {
|
||||
/** Random nonce used to detect connections to self. */
|
||||
NONCE: new Buffer("20bde0a3355dad78", "hex"),
|
||||
|
||||
/**
|
||||
* Decode version payload.
|
||||
* NOTE: `nonce` is copied.
|
||||
* @param {Buffer} buf - Buffer that starts with encoded version
|
||||
* payload
|
||||
* @return {Object} Decoded version structure.
|
||||
*/
|
||||
decode: function(payload) {
|
||||
// 4 + 8 + 8 + 26 + 26 + 8 + (1+) + (1+)
|
||||
assert(payload.length >= 82, "Message payload is too small");
|
||||
var protoVersion = payload.readUInt32BE(0, true);
|
||||
var services = structs.serviceFeatures.decode(payload.slice(4, 12));
|
||||
var time = util.readTime64BE(payload, 12);
|
||||
var short = {short: true};
|
||||
var addrRecv = structs.net_addr.decode(payload.slice(20, 46), short);
|
||||
var addrFrom = structs.net_addr.decode(payload.slice(46, 72), short);
|
||||
var nonce = new Buffer(8);
|
||||
payload.copy(nonce, 0, 72, 80);
|
||||
var decodedUa = UserAgent.decode(payload.slice(80));
|
||||
var decodedStreamNumbers = structs.var_int_list.decode(decodedUa.rest);
|
||||
return {
|
||||
version: protoVersion,
|
||||
services: services,
|
||||
time: time,
|
||||
remoteHost: addrRecv.host,
|
||||
remotePort: addrRecv.port,
|
||||
port: addrFrom.port,
|
||||
nonce: nonce,
|
||||
software: decodedUa.software,
|
||||
streamNumbers: decodedStreamNumbers.list,
|
||||
// NOTE(Kagami): Real data length. It may be some gap between end
|
||||
// of stream numbers list and end of payload:
|
||||
// [payload..............[stream numbers]xxxx]
|
||||
// We are currently ignoring that.
|
||||
length: 80 + decodedUa.length + decodedStreamNumbers.length,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Encode version payload.
|
||||
* @param {Object} opts - Version options
|
||||
* @return {Buffer} Encoded version payload.
|
||||
*/
|
||||
encode: function(opts) {
|
||||
// Deal with default options.
|
||||
var services = opts.services || [structs.serviceFeatures.NODE_NETWORK];
|
||||
var time = opts.time || new Date();
|
||||
var nonce = opts.nonce || exports.version.NONCE;
|
||||
var software = opts.software || UserAgent.SELF;
|
||||
var streamNumbers = opts.streamNumbers || [1];
|
||||
// Start encoding.
|
||||
var protoVersion = new Buffer(4);
|
||||
protoVersion.writeUInt32BE(require("./").PROTOCOL_VERSION, 0);
|
||||
var addrRecv = structs.net_addr.encode({
|
||||
services: services,
|
||||
host: opts.remoteHost,
|
||||
port: opts.remotePort,
|
||||
short: true,
|
||||
});
|
||||
var addrFrom = structs.net_addr.encode({
|
||||
services: services,
|
||||
host: "127.0.0.1",
|
||||
port: opts.port,
|
||||
short: true,
|
||||
});
|
||||
return Buffer.concat([
|
||||
protoVersion,
|
||||
structs.serviceFeatures.encode(services),
|
||||
util.writeTime64BE(null, time),
|
||||
addrRecv,
|
||||
addrFrom,
|
||||
nonce,
|
||||
UserAgent.encode(software),
|
||||
structs.var_int_list.encode(streamNumbers),
|
||||
]);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -59,13 +59,14 @@ var message = exports.message = {
|
|||
command = command.slice(0, firstNonNull).toString("ascii");
|
||||
var payloadLength = buf.readUInt32BE(16, true);
|
||||
assert(payloadLength <= 262144, "Payload is too big");
|
||||
var checksum = buf.slice(20, 24);
|
||||
var length = 24 + payloadLength;
|
||||
assert(buf.length >= length, "Truncated payload");
|
||||
var checksum = buf.slice(20, 24);
|
||||
// NOTE(Kagami): We do copy instead of slice to protect against
|
||||
// possible source buffer modification by user.
|
||||
var payload = new Buffer(payloadLength);
|
||||
buf.copy(payload, 0, 24, length);
|
||||
assert(bufferEqual(checksum, getmsgchecksum(payload)), "Bad checkum");
|
||||
assert(bufferEqual(checksum, getmsgchecksum(payload)), "Bad checksum");
|
||||
var rest = buf.slice(length);
|
||||
return {command: command, payload: payload, length: length, rest: rest};
|
||||
},
|
||||
|
@ -393,7 +394,7 @@ exports.net_addr = {
|
|||
encode: function(opts) {
|
||||
// Be aware of `Buffer.slice` quirk in browserify:
|
||||
// <http://git.io/lNZF1A> (does not modify parent buffer's memory in
|
||||
// old browsers).
|
||||
// old browsers). So we use offset instead of `buf = buf.slice`.
|
||||
var buf, shift;
|
||||
if (opts.short) {
|
||||
buf = new Buffer(26);
|
||||
|
@ -404,7 +405,8 @@ exports.net_addr = {
|
|||
time = Math.floor(time.getTime() / 1000);
|
||||
buf.writeUInt32BE(Math.floor(time / 4294967296), 0, true); // high32
|
||||
buf.writeUInt32BE(time % 4294967296, 4, true); // low32
|
||||
buf.writeUInt32BE(opts.stream, 8);
|
||||
var stream = opts.stream || 1;
|
||||
buf.writeUInt32BE(stream, 8);
|
||||
shift = 12;
|
||||
}
|
||||
var services = opts.services || [serviceFeatures.NODE_NETWORK];
|
||||
|
|
53
lib/util.js
53
lib/util.js
|
@ -1,7 +1,58 @@
|
|||
"use strict";
|
||||
|
||||
exports.assert = function(condition, message) {
|
||||
var assert = exports.assert = function(condition, message) {
|
||||
if (!condition) {
|
||||
throw new Error(message || "Assertion failed");
|
||||
}
|
||||
};
|
||||
|
||||
// Missing methods to read/write 64 bits integers from/to buffers.
|
||||
// TODO(Kagami): Use this helpers in structs, pow, platform.
|
||||
|
||||
var MAX_SAFE_INTEGER = exports.MAX_SAFE_INTEGER = 9007199254740991;
|
||||
|
||||
exports.readUInt64BE = function(buf, offset, noAssert) {
|
||||
offset = offset || 0;
|
||||
var hi = buf.readUInt32BE(offset, noAssert);
|
||||
var lo = buf.readUInt32BE(offset + 4, noAssert);
|
||||
// Max safe number = 2^53 - 1 =
|
||||
// 0b0000000000011111111111111111111111111111111111111111111111111111
|
||||
// = 2097151*(2^32) + (2^32 - 1).
|
||||
// So it's safe until hi <= 2097151. See
|
||||
// <http://mdn.io/issafeinteger>, <https://stackoverflow.com/q/307179>
|
||||
// for details.
|
||||
assert(noAssert || hi <= 2097151, "Unsafe integer");
|
||||
return hi * 4294967296 + lo;
|
||||
};
|
||||
|
||||
var readTimestamp64BE = exports.readTimestamp64BE = function(buf, offset) {
|
||||
offset = offset || 0;
|
||||
var timeHi = buf.readUInt32BE(offset);
|
||||
var timeLo = buf.readUInt32BE(offset + 4);
|
||||
// JavaScript's Date object can't work with timestamps higher than
|
||||
// 8640000000000 (~2^43, ~275760 year). Hope JavaScript will support
|
||||
// 64-bit numbers up to this date.
|
||||
assert(timeHi <= 2011, "Time is too high");
|
||||
assert(timeHi !== 2011 || timeLo <= 2820767744, "Time is too high");
|
||||
return timeHi * 4294967296 + timeLo;
|
||||
};
|
||||
|
||||
exports.readTime64BE = function(buf, offset) {
|
||||
var timestamp = readTimestamp64BE(buf, offset);
|
||||
return new Date(timestamp * 1000);
|
||||
};
|
||||
|
||||
exports.writeUInt64BE = function(buf, value, offset, noAssert) {
|
||||
buf = buf || new Buffer(8);
|
||||
offset = offset || 0;
|
||||
assert(noAssert || value <= MAX_SAFE_INTEGER, "Unsafe integer");
|
||||
buf.writeUInt32BE(Math.floor(value / 4294967296), offset, noAssert);
|
||||
buf.writeUInt32BE(value % 4294967296, offset + 4, noAssert);
|
||||
return buf;
|
||||
};
|
||||
var writeUInt64BE = exports.writeUInt64BE;
|
||||
|
||||
exports.writeTime64BE = function(buf, time, offset, noAssert) {
|
||||
var timestamp = Math.floor(time.getTime() / 1000);
|
||||
return writeUInt64BE(buf, timestamp, offset, noAssert);
|
||||
};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#ifndef BITCHAN_BITMESSAGE_POW_H_
|
||||
#define BITCHAN_BITMESSAGE_POW_H_
|
||||
|
||||
static const int MAX_POOL_SIZE = 1024;
|
||||
static const int HASH_SIZE = 64;
|
||||
static const size_t MAX_POOL_SIZE = 1024;
|
||||
static const size_t HASH_SIZE = 64;
|
||||
|
||||
int pow(size_t pool_size,
|
||||
uint64_t target,
|
||||
|
|
28
test.js
28
test.js
|
@ -16,6 +16,8 @@ var encrypted = structs.encrypted;
|
|||
var messageEncodings = structs.messageEncodings;
|
||||
var serviceFeatures = structs.serviceFeatures;
|
||||
var pubkeyFeatures = structs.pubkeyFeatures;
|
||||
var messages = bitmessage.messages;
|
||||
var version = messages.version;
|
||||
var WIF = bitmessage.WIF;
|
||||
var POW = bitmessage.POW;
|
||||
var Address = bitmessage.Address;
|
||||
|
@ -72,6 +74,10 @@ describe("Common structures", function() {
|
|||
expect(res.command).to.equal("");
|
||||
});
|
||||
|
||||
it("should throw when decoding message with truncated payload", function() {
|
||||
expect(message.decode.bind(null, Buffer("e9beb4d97465737400000000000000000000000770b33ce97061796c6f61", "hex"))).to.throw(Error);
|
||||
});
|
||||
|
||||
it("should encode", function() {
|
||||
expect(message.encode({command: "test", payload: Buffer("payload")}).toString("hex")).to.equal("e9beb4d97465737400000000000000000000000770b33ce97061796c6f6164");
|
||||
});
|
||||
|
@ -275,6 +281,28 @@ describe("Common structures", function() {
|
|||
});
|
||||
});
|
||||
|
||||
describe("Message types", function() {
|
||||
describe("version", function() {
|
||||
it("should encode and decode", function() {
|
||||
var res = version.decode(version.encode({
|
||||
remoteHost: "1.2.3.4",
|
||||
remotePort: 48444,
|
||||
port: 8444,
|
||||
}));
|
||||
expect(res.version).to.equal(3);
|
||||
expect(res.services).to.deep.equal([serviceFeatures.NODE_NETWORK]);
|
||||
expect(res.time).to.be.instanceof(Date);
|
||||
expect(res.remoteHost).to.equal("1.2.3.4");
|
||||
expect(res.remotePort).to.equal(48444);
|
||||
expect(res.port).to.equal(8444);
|
||||
expect(bufferEqual(res.nonce, version.NONCE)).to.be.true;
|
||||
expect(res.software).to.deep.equal(UserAgent.SELF);
|
||||
expect(res.streamNumbers).to.deep.equal([1]);
|
||||
expect(res.length).to.equal(101);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("WIF", function() {
|
||||
var wifSign = "5JgQ79vTBusc61xYPtUEHYQ38AXKdDZgQ5rFp7Cbb4ZjXUKFZEV";
|
||||
var wifEnc = "5K2aL8cnsEWHwHfHnUrPo8QdYyRfoYUBmhAnWY5GTpDLbeyusnE";
|
||||
|
|
Loading…
Reference in New Issue
Block a user