Implement getpubkey
This commit is contained in:
parent
9208e95e20
commit
09f7be7062
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
|
@ -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
31
test.js
|
@ -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") {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user