Implement getpubkey

This commit is contained in:
Kagami Hiiragi 2015-01-18 14:37:09 +03:00
parent 9208e95e20
commit 09f7be7062
6 changed files with 130 additions and 13 deletions

View File

@ -45,7 +45,7 @@ API documentation is available [here](https://bitchan.github.io/bitmessage/docs/
- [x] error - [x] error
- [x] object - [x] object
- [ ] Object types - [ ] Object types
- [ ] getpubkey - [x] getpubkey
- [ ] pubkey - [ ] pubkey
- [ ] msg - [ ] msg
- [ ] broadcast - [ ] broadcast
@ -55,7 +55,6 @@ API documentation is available [here](https://bitchan.github.io/bitmessage/docs/
- [ ] Address - [ ] Address
- [x] encode - [x] encode
- [x] decode - [x] decode
- [x] getRipe
- [x] fromRandom - [x] fromRandom
- [ ] fromPassphrase - [ ] fromPassphrase
- [x] UserAgent - [x] UserAgent

View File

@ -46,6 +46,10 @@ function Address(opts) {
* @return {Address} Decoded address object. * @return {Address} Decoded address object.
*/ */
Address.decode = function(str) { Address.decode = function(str) {
if (str instanceof Address) {
return str;
}
str = str.trim(); str = str.trim();
if (str.slice(0, 3) === "BM-") { if (str.slice(0, 3) === "BM-") {
str = str.slice(3); str = str.slice(3);
@ -121,6 +125,21 @@ Address.prototype.getRipe = function(opts) {
} }
}; };
/**
* Calculate the address tag.
* @return {Buffer} A 32-byte address tag.
*/
Address.prototype.getTag = function() {
var ripe = this.getRipe();
var dataToHash = Buffer.concat([
var_int.encode(this.version),
var_int.encode(this.stream),
ripe,
]);
var hash = bmcrypto.sha512(bmcrypto.sha512(dataToHash));
return hash.slice(32);
};
// Get truncated ripe hash length. // Get truncated ripe hash length.
function getripelen(ripe) { function getripelen(ripe) {
var zeroes = 0; var zeroes = 0;

View File

@ -5,6 +5,7 @@
* @see {@link https://bitmessage.org/Bitmessage%20Technical%20Paper.pdf} * @see {@link https://bitmessage.org/Bitmessage%20Technical%20Paper.pdf}
* @module bitmessage/messages * @module bitmessage/messages
*/ */
// TODO(Kagami): Document object-like params.
"use strict"; "use strict";
@ -266,8 +267,8 @@ var error = exports.error = {
* a stream. It is the only message which propagates; all others are * a stream. It is the only message which propagates; all others are
* only between two nodes. * only between two nodes.
* NOTE: You shouldn't use `encode` and `decode` methods directly. * NOTE: You shouldn't use `encode` and `decode` methods directly.
* Instead, get type of the object and encode/decode it with appropriate * Instead, get type of the object and process it using appropriate
* function from `objects` module. * namespace from `objects` module.
* @see {@link https://bitmessage.org/wiki/Protocol_specification#object} * @see {@link https://bitmessage.org/wiki/Protocol_specification#object}
* @namespace * @namespace
*/ */
@ -285,20 +286,21 @@ exports.object = {
* @return {?number} Object type. * @return {?number} Object type.
*/ */
getType: function(buf) { getType: function(buf) {
// Per v3 spec object starts with nonce (8 bytes), expiresTime (8 assert(buf.length >= 22, "object message payload is too small");
// bytes) and objectType (4 bytes). So we need only first 20 bytes
// to read the type.
if (buf.length >= 20) {
return buf.readUInt32BE(16, true); return buf.readUInt32BE(16, true);
}
}, },
/** /**
* Decode `object` message payload. * Decode `object` message payload.
* NOTE: `nonce` is copied, `payload` references input buffer. * NOTE: `nonce` and `payload` are copied.
* @param {Buffer} buf - Message payload * @param {Buffer} buf - Message payload
* @return {Object} Decoded `object` structure. * @return {Object} Decoded `object` structure.
*/ */
// FIXME(Kagami): Check for POW.
// TODO(Kagami): Option to not fail on bad POW (may be useful for
// bitchan).
// TODO(Kagami): Option to not fail on expired object (would be useful
// for bitchan).
decode: function(buf) { decode: function(buf) {
// 8 + 8 + 4 + (1+) + (1+) // 8 + 8 + 4 + (1+) + (1+)
assert(buf.length >= 22, "object message payload is too small"); assert(buf.length >= 22, "object message payload is too small");
@ -311,13 +313,15 @@ exports.object = {
var type = buf.readUInt32BE(16, true); var type = buf.readUInt32BE(16, true);
var decodedVersion = structs.var_int.decode(buf.slice(20)); var decodedVersion = structs.var_int.decode(buf.slice(20));
var decodedStream = structs.var_int.decode(decodedVersion.rest); var decodedStream = structs.var_int.decode(decodedVersion.rest);
var payload = new Buffer(decodedStream.rest.length);
decodedStream.rest.copy(payload);
return { return {
nonce: nonce, nonce: nonce,
ttl: ttl, ttl: ttl,
type: type, type: type,
version: decodedVersion.value, version: decodedVersion.value,
stream: decodedStream.value, stream: decodedStream.value,
payload: decodedStream.rest, payload: payload,
}; };
}, },
@ -326,6 +330,7 @@ exports.object = {
* @param {Object} opts - Object options * @param {Object} opts - Object options
* @return {Buffer} Encoded payload. * @return {Buffer} Encoded payload.
*/ */
// TODO(Kagami): Do a POW if nonce is not provided.
encode: function(opts) { encode: function(opts) {
assert(opts.nonce.length === 8, "Bad nonce"); assert(opts.nonce.length === 8, "Bad nonce");
assert(opts.ttl > 0, "Bad TTL"); assert(opts.ttl > 0, "Bad TTL");

View File

@ -1,5 +1,68 @@
/** /**
* Working with objects. * Working with objects.
* NOTE: All operations with objects in this module are asynchronous and
* return promises.
* @see {@link https://bitmessage.org/wiki/Protocol_specification#Object_types} * @see {@link https://bitmessage.org/wiki/Protocol_specification#Object_types}
* @module bitmessage/objects * @module bitmessage/objects
*/ */
// TODO(Kagami): Document object-like params.
"use strict";
var objectAssign = Object.assign || require("object-assign");
var assert = require("./util").assert;
var promise = require("./platform").promise;
var object = require("./messages").object;
var Address = require("./address");
/**
* `getpubkey` object. When a node has the hash of a public key (from an
* address) but not the public key itself, it must send out a request
* for the public key.
* @see {@link https://bitmessage.org/wiki/Protocol_specification#getpubkey}
* @namespace
*/
exports.getpubkey = {
/**
* Decode `getpubkey` object message payload.
* @param {Buffer} buf - Message payload
* @return {Promise.<Object>} A promise that contained decoded
* `object` structure when fulfilled.
*/
decodeAsync: function(buf) {
return new promise(function(resolve) {
var decoded = object.decode(buf);
assert(decoded.type === object.GETPUBKEY, "Wrong object type");
var payload = decoded.payload;
delete decoded.payload;
if (decoded.version < 4) {
assert(payload.length === 20, "getpubkey ripe is too small");
// Payload is copied so it's safe to return it right away.
decoded.ripe = payload;
} else {
assert(payload.length === 32, "getpubkey tag is too small");
// Payload is copied so it's safe to return it right away.
decoded.tag = payload;
}
resolve(decoded);
});
},
/**
* Encode `getpubkey` object message payload.
* @param {Object} opts - `getpubkey` object options
* @return {Promise.<Buffer>} A promise that contained encoded message
* payload when fulfilled.
*/
encodeAsync: function(opts) {
return new promise(function(resolve) {
opts = objectAssign({}, opts);
opts.type = object.GETPUBKEY;
var addr = Address.decode(opts.to);
opts.version = addr.version;
opts.stream = addr.stream;
opts.payload = opts.version < 4 ? addr.getRipe() : addr.getTag();
resolve(object.encode(opts));
});
},
};

View File

@ -3,7 +3,7 @@
* @see {@link https://bitmessage.org/wiki/Protocol_specification#Common_structures} * @see {@link https://bitmessage.org/wiki/Protocol_specification#Common_structures}
* @module bitmessage/structs * @module bitmessage/structs
*/ */
// TODO(Kagami): Find a way how to document object params properly. // TODO(Kagami): Document object-like params.
"use strict"; "use strict";

31
test.js
View File

@ -22,6 +22,8 @@ var addr = messages.addr;
var inv = messages.inv; var inv = messages.inv;
var error = messages.error; var error = messages.error;
var object = messages.object; var object = messages.object;
var objects = bitmessage.objects;
var getpubkey = objects.getpubkey;
var WIF = bitmessage.WIF; var WIF = bitmessage.WIF;
var POW = bitmessage.POW; var POW = bitmessage.POW;
var Address = bitmessage.Address; var Address = bitmessage.Address;
@ -414,6 +416,24 @@ describe("Message types", function() {
}); });
}); });
describe("Object types", function() {
describe("getpubkey", function() {
it("should encode and decode", function() {
return getpubkey.encodeAsync({
nonce: Buffer(8),
ttl: 100,
to: "2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z",
}).then(getpubkey.decodeAsync)
.then(function(res) {
expect(res.ttl).to.equal(100);
expect(res.version).to.equal(4);
expect(res.stream).to.equal(1);
expect(res.tag.toString("hex")).to.equal("facf1e3e6c74916203b7f714ca100d4d60604f0917696d0f09330f82f52bed1a");
});
});
});
});
describe("WIF", function() { describe("WIF", function() {
var wifSign = "5JgQ79vTBusc61xYPtUEHYQ38AXKdDZgQ5rFp7Cbb4ZjXUKFZEV"; var wifSign = "5JgQ79vTBusc61xYPtUEHYQ38AXKdDZgQ5rFp7Cbb4ZjXUKFZEV";
var wifEnc = "5K2aL8cnsEWHwHfHnUrPo8QdYyRfoYUBmhAnWY5GTpDLbeyusnE"; var wifEnc = "5K2aL8cnsEWHwHfHnUrPo8QdYyRfoYUBmhAnWY5GTpDLbeyusnE";
@ -521,6 +541,17 @@ describe("High-level classes", function() {
expect(addr2.ripe[0]).to.equal(0); expect(addr2.ripe[0]).to.equal(0);
}); });
it("should calculate tag", function() {
var addr = Address.decode("2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z");
expect(addr.getTag().toString("hex")).to.equal("facf1e3e6c74916203b7f714ca100d4d60604f0917696d0f09330f82f52bed1a");
});
it("should allow to decode Address instance", function() {
var addr = Address.decode("2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z");
expect(addr.ripe.toString("hex")).to.equal("003ab6655de4bd8c603eba9b00dd5970725fdd56");
expect(Address.decode(addr)).to.equal(addr);
});
// FIXME(Kagami): Don't run it in browser currently because it's // FIXME(Kagami): Don't run it in browser currently because it's
// very slow. This need to be fixed. // very slow. This need to be fixed.
if (allTests && typeof window === "undefined") { if (allTests && typeof window === "undefined") {