OOPify
This commit is contained in:
parent
b2b660827c
commit
a3475d9d6a
10
README.md
10
README.md
|
@ -55,8 +55,8 @@ With the help of browserify `bitmessage` provides different implementations for
|
||||||
- [x] encode
|
- [x] encode
|
||||||
- [x] decode
|
- [x] decode
|
||||||
- [x] getRipe
|
- [x] getRipe
|
||||||
- [x] getRandom
|
- [x] fromRandom
|
||||||
- [ ] getDeterministic
|
- [ ] fromPassphrase
|
||||||
- [ ] Message
|
- [ ] Message
|
||||||
- [ ] encode
|
- [ ] encode
|
||||||
- [ ] decode
|
- [ ] decode
|
||||||
|
@ -78,11 +78,11 @@ With the help of browserify `bitmessage` provides different implementations for
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
// Generating a new Bitmessage identity.
|
||||||
var Address = require("bitmessage").Address;
|
var Address = require("bitmessage").Address;
|
||||||
Address.getRandom().then(function(addr) {
|
Address.fromRandom().then(function(addr) {
|
||||||
Address.encode(addr).then(function(str) {
|
addr.encode().then(function(str) {
|
||||||
console.log("New random Bitmessage address:", str);
|
console.log("New random Bitmessage address:", str);
|
||||||
console.log("Private keys and parameters:", addr);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
147
lib/address.js
147
lib/address.js
|
@ -6,18 +6,40 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
require("es6-promise").polyfill();
|
require("es6-promise").polyfill();
|
||||||
|
require("object.assign").shim();
|
||||||
var assert = require("assert");
|
var assert = require("assert");
|
||||||
var bufferEqual = require("buffer-equal");
|
var bufferEqual = require("buffer-equal");
|
||||||
var bs58 = require("bs58");
|
var bs58 = require("bs58");
|
||||||
var varint = require("./varint");
|
var varint = require("./varint");
|
||||||
var bmcrypto = require("./crypto");
|
var bmcrypto = require("./crypto");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new Bitmessage address object.
|
||||||
|
* @param {?Object} opts - Address options
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
function Address(opts) {
|
||||||
|
if (!(this instanceof Address)) {
|
||||||
|
return new Address(opts);
|
||||||
|
}
|
||||||
|
opts = opts || {};
|
||||||
|
Object.assign(this, opts);
|
||||||
|
this.version = this.version || 4;
|
||||||
|
assert(this.version <= 4, "Version too high");
|
||||||
|
assert(this.version >= 1, "Version too low");
|
||||||
|
this.stream = this.stream || 1;
|
||||||
|
if (this.ripe) {
|
||||||
|
assertripelen(getripelen(this.ripe), this.version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse Bitmessage address into address object.
|
* Parse Bitmessage address into address object.
|
||||||
* @param {String} str - Address string (with or without `BM-` prefix)
|
* @param {String} str - Address string (with or without `BM-` prefix)
|
||||||
* @return {Promise.<Address,Error>} Decoded address object
|
* @return {Promise.<Address,Error>} Decoded address object
|
||||||
|
* @static
|
||||||
*/
|
*/
|
||||||
exports.decode = function(str) {
|
Address.decode = function(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);
|
||||||
|
@ -38,18 +60,15 @@ exports.decode = function(str) {
|
||||||
|
|
||||||
var decoded = varint.decode(data);
|
var decoded = varint.decode(data);
|
||||||
var version = decoded.value;
|
var version = decoded.value;
|
||||||
assertversion(version);
|
|
||||||
|
|
||||||
data = decoded.rest;
|
data = decoded.rest;
|
||||||
decoded = varint.decode(data);
|
decoded = varint.decode(data);
|
||||||
var stream = decoded.value;
|
var stream = decoded.value;
|
||||||
assertstream(stream);
|
|
||||||
|
|
||||||
var ripe = decoded.rest;
|
var ripe = decoded.rest;
|
||||||
var ripelen = ripe.length;
|
var ripelen = ripe.length;
|
||||||
assertripelen(ripelen, version);
|
|
||||||
if (version === 4) {
|
if (version === 4) {
|
||||||
assert(ripe[0] !== 0, "Ripe decode error");
|
assert(ripe[0] !== 0, "Ripe encode error");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prevent extra allocation. God, kill me please for premature
|
// Prevent extra allocation. God, kill me please for premature
|
||||||
|
@ -58,7 +77,7 @@ exports.decode = function(str) {
|
||||||
var zeroes = new Buffer(Array(20 - ripelen));
|
var zeroes = new Buffer(Array(20 - ripelen));
|
||||||
ripe = Buffer.concat([zeroes, ripe]);
|
ripe = Buffer.concat([zeroes, ripe]);
|
||||||
}
|
}
|
||||||
return {version: version, stream: stream, ripe: ripe};
|
return new Address({version: version, stream: stream, ripe: ripe});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,38 +108,33 @@ function keys2ripe(signKey, encKey) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get Ripe hash for the given address object.
|
* Calculate the Ripe hash of the address.
|
||||||
* @param {Address} addr - Address object
|
|
||||||
* @param {?Object} opts - Options
|
* @param {?Object} opts - Options
|
||||||
* @return {Buffer} Resulting Ripe hash.
|
* @return {Promise.<Buffer,Error>} Resulting Ripe hash
|
||||||
*/
|
*/
|
||||||
function getRipe(addr, opts) {
|
Address.prototype.getRipe = function(opts) {
|
||||||
var signKey = addr.signPrivateKey || addr.signPublicKey;
|
var self = this;
|
||||||
assert(signKey, "No signing key");
|
var ripepromise;
|
||||||
var encKey = addr.encPrivateKey || addr.encPublicKey;
|
if (self.ripe) {
|
||||||
assert(encKey, "No encryption key");
|
ripepromise = Promise.resolve(self.ripe);
|
||||||
opts = opts || {};
|
} else {
|
||||||
return keys2ripe(signKey, encKey).then(function(ripe) {
|
opts = opts || {};
|
||||||
|
var signKey = self.signPrivateKey || self.signPublicKey;
|
||||||
|
assert(signKey, "No signing key");
|
||||||
|
var encKey = self.encPrivateKey || self.encPublicKey;
|
||||||
|
assert(encKey, "No encryption key");
|
||||||
|
ripepromise = keys2ripe(signKey, encKey);
|
||||||
|
}
|
||||||
|
return ripepromise.then(function(ripe) {
|
||||||
|
var ripelen = getripelen(ripe);
|
||||||
|
assertripelen(ripelen, self.version);
|
||||||
if (opts.short) {
|
if (opts.short) {
|
||||||
var ripelen = getripelen(ripe);
|
|
||||||
return ripe.slice(20 - ripelen);
|
return ripe.slice(20 - ripelen);
|
||||||
} else {
|
} else {
|
||||||
return ripe;
|
return ripe;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
exports.getRipe = getRipe;
|
|
||||||
|
|
||||||
// Do neccessary checkings of the address version.
|
|
||||||
function assertversion(version) {
|
|
||||||
assert(version <= 4, "Version too high");
|
|
||||||
assert(version >= 1, "Version too low");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do neccessary checkings of the stream number.
|
|
||||||
function assertstream(stream) {
|
|
||||||
assert(stream, "No stream");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get truncated Ripe hash length.
|
// Get truncated Ripe hash length.
|
||||||
function getripelen(ripe) {
|
function getripelen(ripe) {
|
||||||
|
@ -148,7 +162,7 @@ function assertripelen(ripelen, version) {
|
||||||
assert(ripelen <= 20, "Ripe too long");
|
assert(ripelen <= 20, "Ripe too long");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("Wrong version");
|
throw new Error("Bad version");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,34 +179,14 @@ function checkripelen(ripelen, version) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode Bitmessage address object into address string.
|
* Encode Bitmessage address object into address string.
|
||||||
* @param {Address} addr - Address object
|
|
||||||
* @return {Promise.<string,Error>} Address string
|
* @return {Promise.<string,Error>} Address string
|
||||||
*/
|
*/
|
||||||
exports.encode = function(addr) {
|
Address.prototype.encode = function() {
|
||||||
var version, stream, ripepromise;
|
var self = this;
|
||||||
try {
|
return self.getRipe({short: true}).then(function(ripe) {
|
||||||
version = addr.version;
|
|
||||||
assertversion(version);
|
|
||||||
stream = addr.stream;
|
|
||||||
assertstream(stream);
|
|
||||||
|
|
||||||
if (addr.ripe) {
|
|
||||||
ripepromise = Promise.resolve(addr.ripe);
|
|
||||||
} else {
|
|
||||||
ripepromise = getRipe(addr);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
return Promise.reject(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ripepromise.then(function(ripe) {
|
|
||||||
var ripelen = getripelen(ripe);
|
|
||||||
assertripelen(ripelen, version);
|
|
||||||
// Skip leading zeroes.
|
|
||||||
ripe = ripe.slice(20 - ripelen);
|
|
||||||
var data = Buffer.concat([
|
var data = Buffer.concat([
|
||||||
varint.encode(version),
|
varint.encode(self.version),
|
||||||
varint.encode(stream),
|
varint.encode(self.stream),
|
||||||
ripe,
|
ripe,
|
||||||
]);
|
]);
|
||||||
return getchecksum(data).then(function(checksum) {
|
return getchecksum(data).then(function(checksum) {
|
||||||
|
@ -203,24 +197,18 @@ exports.encode = function(addr) {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create new Bitmessage address using random encryption and signing
|
* Create new Bitmessage address from random encryption and signing
|
||||||
* private keys.
|
* private keys.
|
||||||
* @param {?Object} opts - Address options
|
* @param {?Object} opts - Address options
|
||||||
* @return {Promise.<Address,Error>} Generated address object
|
* @return {Promise.<Address,Error>} Generated address object
|
||||||
|
* @static
|
||||||
*/
|
*/
|
||||||
exports.getRandom = function(opts) {
|
Address.fromRandom = function(opts) {
|
||||||
var version, stream, ripelen, signPrivateKey;
|
|
||||||
opts = opts || {};
|
opts = opts || {};
|
||||||
|
var version = opts.version || 4;
|
||||||
|
var ripelen = opts.ripelen || 19;
|
||||||
try {
|
try {
|
||||||
version = opts.version || 4;
|
|
||||||
assertversion(version);
|
|
||||||
stream = opts.stream || 1;
|
|
||||||
assertstream(version);
|
|
||||||
ripelen = opts.ripelen || 19;
|
|
||||||
assertripelen(ripelen, version);
|
assertripelen(ripelen, version);
|
||||||
// Place it to try-catch since there might be not enough entropy to
|
|
||||||
// generate the key and the function will fail.
|
|
||||||
signPrivateKey = bmcrypto.getPrivate();
|
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
return Promise.reject(e);
|
return Promise.reject(e);
|
||||||
}
|
}
|
||||||
|
@ -230,6 +218,8 @@ exports.getRandom = function(opts) {
|
||||||
var nextTick = typeof setImmediate === "undefined" ?
|
var nextTick = typeof setImmediate === "undefined" ?
|
||||||
process.nextTick :
|
process.nextTick :
|
||||||
setImmediate;
|
setImmediate;
|
||||||
|
|
||||||
|
var signPrivateKey = bmcrypto.getPrivate();
|
||||||
var signPublicKey = bmcrypto.getPublic(signPrivateKey);
|
var signPublicKey = bmcrypto.getPublic(signPrivateKey);
|
||||||
|
|
||||||
// FIXME(Kagami): This function is rather slow in browsers so
|
// FIXME(Kagami): This function is rather slow in browsers so
|
||||||
|
@ -242,24 +232,23 @@ exports.getRandom = function(opts) {
|
||||||
// available in Chrome (at least in 39.0+).
|
// available in Chrome (at least in 39.0+).
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
function tryKey() {
|
function tryKey() {
|
||||||
var encPrivateKey;
|
var encPrivateKey = bmcrypto.getPrivate();
|
||||||
try {
|
var encPublicKey = bmcrypto.getPublic(encPrivateKey);
|
||||||
encPrivateKey = bmcrypto.getPrivate();
|
return keys2ripe(signPublicKey, encPublicKey).then(function(ripe) {
|
||||||
} catch(e) {
|
|
||||||
reject(e);
|
|
||||||
}
|
|
||||||
return keys2ripe(signPublicKey, encPrivateKey).then(function(ripe) {
|
|
||||||
var len = getripelen(ripe);
|
var len = getripelen(ripe);
|
||||||
if (
|
if (
|
||||||
(strictripelen && len === ripelen) ||
|
(strictripelen && len === ripelen) ||
|
||||||
(!strictripelen && len <= ripelen && checkripelen(ripelen, version))
|
(!strictripelen && len <= ripelen && checkripelen(ripelen, version))
|
||||||
) {
|
) {
|
||||||
resolve({
|
// XXX(Kagami): Do we need to put all these properties or
|
||||||
version: version,
|
// compute them manually via ECMA5 getters/setters instead?
|
||||||
stream: stream,
|
resolve(new Address(Object.assign({
|
||||||
signPrivateKey: signPrivateKey,
|
signPrivateKey: signPrivateKey,
|
||||||
|
signPublicKey: signPublicKey,
|
||||||
encPrivateKey: encPrivateKey,
|
encPrivateKey: encPrivateKey,
|
||||||
});
|
encPublicKey: encPublicKey,
|
||||||
|
ripe: ripe,
|
||||||
|
}, opts)));
|
||||||
} else {
|
} else {
|
||||||
nextTick(tryKey);
|
nextTick(tryKey);
|
||||||
}
|
}
|
||||||
|
@ -268,3 +257,5 @@ exports.getRandom = function(opts) {
|
||||||
tryKey();
|
tryKey();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports = Address;
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
"es6-promise": "^2.0.1",
|
"es6-promise": "^2.0.1",
|
||||||
"int64-native": "^0.3.2",
|
"int64-native": "^0.3.2",
|
||||||
"node-int64": "^0.3.2",
|
"node-int64": "^0.3.2",
|
||||||
|
"object.assign": "^1.1.1",
|
||||||
"ripemd160": "^0.2.0"
|
"ripemd160": "^0.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
17
test.js
17
test.js
|
@ -72,14 +72,9 @@ describe("WIF", function() {
|
||||||
expect(Buffer.isBuffer(key2)).to.be.true;
|
expect(Buffer.isBuffer(key2)).to.be.true;
|
||||||
expect(key2.length).to.equal(32);
|
expect(key2.length).to.equal(32);
|
||||||
expect(key2.toString("hex")).to.equal(encPrivateKey.toString("hex"));
|
expect(key2.toString("hex")).to.equal(encPrivateKey.toString("hex"));
|
||||||
return {
|
return Address({signPrivateKey: key1, encPrivateKey: key2}).encode();
|
||||||
version: 4,
|
|
||||||
stream: 1,
|
|
||||||
signPrivateKey: key1,
|
|
||||||
encPrivateKey: key2,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}).then(Address.encode).then(function(str) {
|
}).then(function(str) {
|
||||||
expect(str).to.equal("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z");
|
expect(str).to.equal("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -158,12 +153,12 @@ describe("Address", function() {
|
||||||
|
|
||||||
it("should allow to generate new Bitmessage address", function() {
|
it("should allow to generate new Bitmessage address", function() {
|
||||||
this.timeout(10000);
|
this.timeout(10000);
|
||||||
return Address.getRandom().then(function(addr) {
|
return Address.fromRandom().then(function(addr) {
|
||||||
expect(addr.version).to.equal(4);
|
expect(addr.version).to.equal(4);
|
||||||
expect(addr.stream).to.equal(1);
|
expect(addr.stream).to.equal(1);
|
||||||
expect(addr.signPrivateKey.length).to.equal(32);
|
expect(addr.signPrivateKey.length).to.equal(32);
|
||||||
expect(addr.encPrivateKey.length).to.equal(32);
|
expect(addr.encPrivateKey.length).to.equal(32);
|
||||||
return Address.encode(addr).then(function(str) {
|
return addr.encode().then(function(str) {
|
||||||
expect(str.slice(0, 3)).to.equal("BM-");
|
expect(str.slice(0, 3)).to.equal("BM-");
|
||||||
return Address.decode(str).then(function(addr2) {
|
return Address.decode(str).then(function(addr2) {
|
||||||
expect(addr2.version).to.equal(4);
|
expect(addr2.version).to.equal(4);
|
||||||
|
@ -178,8 +173,8 @@ describe("Address", function() {
|
||||||
if (allTests) {
|
if (allTests) {
|
||||||
it("should allow to generate shorter address", function() {
|
it("should allow to generate shorter address", function() {
|
||||||
this.timeout(60000);
|
this.timeout(60000);
|
||||||
return Address.getRandom({ripelen: 18}).then(function(addr) {
|
return Address.fromRandom({ripelen: 18}).then(function(addr) {
|
||||||
return Address.getRipe(addr, {short: true}).then(function(ripe) {
|
return addr.getRipe({short: true}).then(function(ripe) {
|
||||||
expect(ripe.length).to.be.at.most(18);
|
expect(ripe.length).to.be.at.most(18);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue
Block a user