Do not use promises in crypto

Because sha.js is faster than WebCryptoAPI for POW.
This commit is contained in:
Kagami Hiiragi 2015-01-03 17:52:27 +03:00
parent f6165d891b
commit bf8b663c5d
10 changed files with 163 additions and 258 deletions

View File

@ -12,14 +12,6 @@ API documentation is available [here](https://bitchan.github.io/bitmessage/docs/
* [Protocol specification](https://bitmessage.org/wiki/Protocol_specification) * [Protocol specification](https://bitmessage.org/wiki/Protocol_specification)
* [Whitepaper](https://bitmessage.org/bitmessage.pdf) * [Whitepaper](https://bitmessage.org/bitmessage.pdf)
## Implementation details
With the help of browserify `bitmessage` provides different implementations for Browser and Node.js with the same API. Because WebCryptoAPI defines asynchronous promise-driven API, implementation for Node needs to use promises too.
* Use Node.js crypto module/library bindings where possible
* Use WebCryptoAPI where possible
* Promise-driven API
## Feature matrix (both Browser and Node) ## Feature matrix (both Browser and Node)
- [ ] crypto - [ ] crypto
@ -83,13 +75,10 @@ With the help of browserify `bitmessage` provides different implementations for
## Usage ## Usage
```js ```js
// Generating a new Bitmessage identity. // Generate a new random Bitmessage identity.
var Address = require("bitmessage").Address; var Address = require("bitmessage").Address;
Address.fromRandom().then(function(addr) { var addr = Address.fromRandom();
addr.encode().then(function(str) { console.log("New random Bitmessage address:", addr.encode());
console.log("New random Bitmessage address:", str);
});
});
``` ```
## License ## License

View File

@ -6,7 +6,6 @@
"use strict"; "use strict";
require("es6-promise").polyfill();
require("object.assign").shim(); require("object.assign").shim();
var assert = require("assert"); var assert = require("assert");
var bufferEqual = require("buffer-equal"); var bufferEqual = require("buffer-equal");
@ -30,14 +29,20 @@ function Address(opts) {
assert(this.version >= 1, "Version too low"); assert(this.version >= 1, "Version too low");
this.stream = this.stream || 1; this.stream = this.stream || 1;
if (this.ripe) { if (this.ripe) {
assertripelen(getripelen(this.ripe), this.version); assertripelen(getripelen(this.ripe), this.version, this.ripe);
if (this.ripe.length < 20) {
var fullripe = new Buffer(20);
fullripe.fill(0);
this.ripe.copy(fullripe, 20 - this.ripe.length);
this.ripe = fullripe;
}
} }
} }
/** /**
* 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>} Decoded address object. * @return {Address} Decoded address object.
*/ */
Address.decode = function(str) { Address.decode = function(str) {
str = str.trim(); str = str.trim();
@ -45,18 +50,10 @@ Address.decode = function(str) {
str = str.slice(3); str = str.slice(3);
} }
var bytes; var bytes = bs58.decode(str);
try {
bytes = bs58.decode(str);
} catch(e) {
return Promise.reject(e);
}
// Checksum validating.
var data = new Buffer(bytes.slice(0, -4)); var data = new Buffer(bytes.slice(0, -4));
var checksum = new Buffer(bytes.slice(-4)); var checksum = new Buffer(bytes.slice(-4));
return getchecksum(data).then(function(realchecksum) { assert(bufferEqual(checksum, getchecksum(data)), "Bad checkum");
assert(bufferEqual(checksum, realchecksum), "Bad checkum");
var decoded = var_int.decode(data); var decoded = var_int.decode(data);
var version = decoded.value; var version = decoded.value;
@ -66,26 +63,16 @@ Address.decode = function(str) {
var stream = decoded.value; var stream = decoded.value;
var ripe = decoded.rest; var ripe = decoded.rest;
var ripelen = ripe.length;
if (version === 4) { if (version === 4) {
assert(ripe[0] !== 0, "Ripe encode error"); assert(ripe[0] !== 0, "Ripe encode error");
} }
// Prevent extra allocation. God, kill me please for premature
// optimizations.
if (ripelen < 20) {
var zeroes = new Buffer(Array(20 - ripelen));
ripe = Buffer.concat([zeroes, ripe]);
}
return new Address({version: version, stream: stream, ripe: ripe}); return new Address({version: version, stream: stream, ripe: ripe});
});
}; };
// Compute the Bitmessage checksum for the given data. // Compute the Bitmessage checksum for the given data.
function getchecksum(data) { function getchecksum(data) {
return bmcrypto.sha512(data).then(bmcrypto.sha512).then(function(dhash) { return bmcrypto.sha512(bmcrypto.sha512(data)).slice(0, 4);
return dhash.slice(0, 4);
});
} }
// Get RIPEMD160(SHA512(SIGN_PUBLIC_KEY || ENC_PUBLIC_KEY)). // Get RIPEMD160(SHA512(SIGN_PUBLIC_KEY || ENC_PUBLIC_KEY)).
@ -104,39 +91,36 @@ function keys2ripe(signKey, encKey) {
encPublicKey = encKey; encPublicKey = encKey;
} }
var concat = Buffer.concat([signPublicKey, encPublicKey]); var concat = Buffer.concat([signPublicKey, encPublicKey]);
return bmcrypto.sha512(concat).then(bmcrypto.ripemd160); return bmcrypto.ripemd160(bmcrypto.sha512(concat));
} }
/** /**
* Calculate the Ripe hash of the address. * Calculate the ripe hash of the address.
* @param {?Object} opts - Options * @param {?Object} opts - Options
* @return {Promise.<Buffer>} Resulting Ripe hash. * @return {Buffer} Resulting ripe hash.
*/ */
Address.prototype.getRipe = function(opts) { Address.prototype.getRipe = function(opts) {
var self = this; var ripe;
var ripepromise;
if (self.ripe) {
ripepromise = Promise.resolve(self.ripe);
} else {
opts = opts || {}; opts = opts || {};
var signKey = self.signPrivateKey || self.signPublicKey; if (this.ripe) {
ripe = this.ripe;
} else {
var signKey = this.signPrivateKey || this.signPublicKey;
assert(signKey, "No signing key"); assert(signKey, "No signing key");
var encKey = self.encPrivateKey || self.encPublicKey; var encKey = this.encPrivateKey || this.encPublicKey;
assert(encKey, "No encryption key"); assert(encKey, "No encryption key");
ripepromise = keys2ripe(signKey, encKey); ripe = keys2ripe(signKey, encKey);
} }
return ripepromise.then(function(ripe) {
var ripelen = getripelen(ripe); var ripelen = getripelen(ripe);
assertripelen(ripelen, self.version); assertripelen(ripelen, this.version, ripe);
if (opts.short) { if (opts.short) {
return ripe.slice(20 - ripelen); return ripe.slice(20 - ripelen);
} else { } else {
return ripe; return ripe;
} }
});
}; };
// Get truncated Ripe hash length. // Get truncated ripe hash length.
function getripelen(ripe) { function getripelen(ripe) {
var zeroes = 0; var zeroes = 0;
for (var i = 0; i < 20, ripe[i] === 0; i++) { for (var i = 0; i < 20, ripe[i] === 0; i++) {
@ -145,21 +129,24 @@ function getripelen(ripe) {
return 20 - zeroes; return 20 - zeroes;
} }
// Do neccessary checkings of the truncated Ripe hash length depending // Do neccessary checkings of the truncated ripe hash length depending
// on the address version. // on the address version.
function assertripelen(ripelen, version) { function assertripelen(ripelen, version, ripe) {
if (ripe) {
assert(ripe.length <= 20, "Bad ripe");
}
switch (version) { switch (version) {
case 1: case 1:
assert(ripelen === 20, "Bad ripe length"); assert(ripelen === 20, "Bad ripe length");
break; break;
case 2: case 2:
case 3: case 3:
assert(ripelen >= 18, "Ripe too short"); assert(ripelen >= 18, "Ripe is too short");
assert(ripelen <= 20, "Ripe too long"); assert(ripelen <= 20, "Ripe is too long");
break; break;
case 4: case 4:
assert(ripelen >= 4, "Ripe too short"); assert(ripelen >= 4, "Ripe is too short");
assert(ripelen <= 20, "Ripe too long"); assert(ripelen <= 20, "Ripe is too long");
break; break;
default: default:
throw new Error("Bad version"); throw new Error("Bad version");
@ -179,61 +166,52 @@ function checkripelen(ripelen, version) {
/** /**
* Encode Bitmessage address object into address string. * Encode Bitmessage address object into address string.
* @return {Promise.<string>} Address string. * @return {string} Address string.
*/ */
Address.prototype.encode = function() { Address.prototype.encode = function() {
var self = this; var ripe = this.getRipe({short: true});
return self.getRipe({short: true}).then(function(ripe) {
var data = Buffer.concat([ var data = Buffer.concat([
var_int.encode(self.version), var_int.encode(this.version),
var_int.encode(self.stream), var_int.encode(this.stream),
ripe, ripe,
]); ]);
return getchecksum(data).then(function(checksum) { var addr = Buffer.concat([data, getchecksum(data)]);
var addr = Buffer.concat([data, checksum]);
return "BM-" + bs58.encode(addr); return "BM-" + bs58.encode(addr);
});
});
}; };
function popkey(obj, key) {
var value = obj[key];
delete obj[key];
return value;
}
/** /**
* Create new Bitmessage address from 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>} Generated address object. * @return {Address} Generated address object.
*/ */
Address.fromRandom = function(opts) { Address.fromRandom = function(opts) {
opts = opts || {}; opts = Object.assign({}, opts);
var version = opts.version || 4; var version = opts.version = opts.version || 4;
var ripelen = opts.ripelen || 19; var ripelen = popkey(opts, "ripelen") || 19;
try {
assertripelen(ripelen, version); assertripelen(ripelen, version);
} catch(e) { // Should the generated ripe length be strictly equal to the specified
return Promise.reject(e); // (less or equal by default).
} var strictripelen = !!popkey(opts, "strictripelen");
// Should the generated Ripe length be strictly equal to the specified
// (less-or-equal by default);
var strictripelen = !!opts.strictripelen;
var nextTick = typeof setImmediate === "undefined" ?
process.nextTick :
setImmediate;
// TODO(Kagami): Speed it up using web workers in Browser.
// TODO(Kagami): Bind to C++ version of this code in Node.
var encPrivateKey, encPublicKey, ripe;
var signPrivateKey = bmcrypto.getPrivate(); var signPrivateKey = bmcrypto.getPrivate();
var signPublicKey = bmcrypto.getPublic(signPrivateKey); var signPublicKey = bmcrypto.getPublic(signPrivateKey);
var keysbuf = Buffer(130);
// FIXME(Kagami): This function is rather slow in browsers so signPublicKey.copy(keysbuf);
// generation of ripelen=18 currently is disabled (see `test.js`). It while (true) {
// should be heavily profiled to determine the bottleneck. encPrivateKey = bmcrypto.getPrivate();
// TODO(Kagami): We may want to run this in the web worker to speedup encPublicKey = bmcrypto.getPublic(encPrivateKey);
// the search. Currently WebCryptoAPI is not available in Firefox in encPublicKey.copy(keysbuf, 65);
// web workers (see ripe = bmcrypto.ripemd160(bmcrypto.sha512(keysbuf));
// <https://bugzilla.mozilla.org/show_bug.cgi?id=842818>) but is
// available in Chrome (at least in 39.0+).
return new Promise(function(resolve, reject) {
function tryKey() {
var encPrivateKey = bmcrypto.getPrivate();
var encPublicKey = bmcrypto.getPublic(encPrivateKey);
return keys2ripe(signPublicKey, encPublicKey).then(function(ripe) {
var len = getripelen(ripe); var len = getripelen(ripe);
if ( if (
(strictripelen && len === ripelen) || (strictripelen && len === ripelen) ||
@ -241,20 +219,14 @@ Address.fromRandom = function(opts) {
) { ) {
// TODO(Kagami): Do we need to put all these properties or compute // TODO(Kagami): Do we need to put all these properties or compute
// them manually via ECMA5 getters/setters instead? // them manually via ECMA5 getters/setters instead?
resolve(new Address(Object.assign({ opts.signPrivateKey = signPrivateKey;
signPrivateKey: signPrivateKey, opts.signPublicKey = signPublicKey;
signPublicKey: signPublicKey, opts.encPrivateKey = encPrivateKey;
encPrivateKey: encPrivateKey, opts.encPublicKey = encPublicKey;
encPublicKey: encPublicKey, opts.ripe = ripe;
ripe: ripe, return new Address(opts);
}, opts)));
} else {
nextTick(tryKey);
} }
}).catch(reject);
} }
tryKey();
});
}; };
module.exports = Address; module.exports = Address;

View File

@ -12,7 +12,7 @@ var platform = require("./platform");
/** /**
* Calculate SHA-512 hash. * Calculate SHA-512 hash.
* @param {Buffer} buf - Input data * @param {Buffer} buf - Input data
* @return {Promise.<Buffer>} Resulting hash. * @return {Buffer} Resulting hash.
* @function * @function
*/ */
exports.sha512 = platform.sha512; exports.sha512 = platform.sha512;
@ -20,7 +20,7 @@ exports.sha512 = platform.sha512;
/** /**
* Calculate SHA-256 hash. * Calculate SHA-256 hash.
* @param {Buffer} buf - Input data * @param {Buffer} buf - Input data
* @return {Promise.<Buffer>} Resulting hash. * @return {Buffer} Resulting hash.
* @function * @function
*/ */
exports.sha256 = platform.sha256; exports.sha256 = platform.sha256;
@ -28,7 +28,7 @@ exports.sha256 = platform.sha256;
/** /**
* Calculate RIPEMD-160 hash. * Calculate RIPEMD-160 hash.
* @param {Buffer} buf - Input data * @param {Buffer} buf - Input data
* @return {Promise.<Buffer>} Resulting hash. * @return {Buffer} Resulting hash.
* @function * @function
*/ */
exports.ripemd160 = platform.ripemd160; exports.ripemd160 = platform.ripemd160;

View File

@ -1,5 +1,5 @@
/** /**
* Working with messages. * Working with messages.
* @see {@link https://bitmessage.org/wiki/Protocol_specification#Message_types} * @see {@link https://bitmessage.org/wiki/Protocol_specification#Message_types}
* @module bitmessage/message * @module bitmessage/messages
*/ */

View File

@ -1,5 +1,5 @@
/** /**
* Working with objects. * Working with objects.
* @see {@link https://bitmessage.org/wiki/Protocol_specification#Object_types} * @see {@link https://bitmessage.org/wiki/Protocol_specification#Object_types}
* @module bitmessage/object * @module bitmessage/objects
*/ */

View File

@ -1,38 +1,21 @@
/** /**
* Browser implementation of platform-specific routines. * Browser implementation of platform-specific routines.
* @see {@link http://www.w3.org/TR/WebCryptoAPI/}
* @see {@link http://caniuse.com/#feat=cryptography}
* @see {@link https://sites.google.com/a/chromium.org/dev/blink/webcrypto}
*/ */
"use strict"; "use strict";
require("es6-promise").polyfill(); var createHash = require("sha.js");
var assert = require("assert");
var ripemd160 = require("ripemd160"); var ripemd160 = require("ripemd160");
// Support `webkit` prefix for Safari (not tested yet).
// TODO(Kagami): Try to support IE11.
var subtle = window.crypto.subtle || window.crypto.webkitSubtle;
assert(subtle, "WebCryptoAPI is not supported");
exports.sha512 = function(buf) { exports.sha512 = function(buf) {
return subtle.digest({name: "SHA-512"}, buf).then(function(arr) { return createHash("sha512").update(buf).digest();
return new Buffer(new Uint8Array(arr));
});
}; };
exports.sha256 = function(buf) { exports.sha256 = function(buf) {
return subtle.digest({name: "SHA-256"}, buf).then(function(arr) { return createHash("sha256").update(buf).digest();
return new Buffer(new Uint8Array(arr));
});
}; };
exports.ripemd160 = function(buf) { exports.ripemd160 = ripemd160;
// XXX(Kagami): RIPEMD is not defined in WebCryptoAPI so we provide it
// using pure JS third-party implementation.
return Promise.resolve(ripemd160(buf));
};
exports.randomBytes = function(size) { exports.randomBytes = function(size) {
var arr = new Uint8Array(size); var arr = new Uint8Array(size);

View File

@ -1,29 +1,22 @@
/** /**
* Node.js implementation of platform-specific routines. * Node.js implementation of platform-specific routines.
* @see {@link http://nodejs.org/api/crypto.html}
*/ */
"use strict"; "use strict";
require("es6-promise").polyfill();
var crypto = require("crypto"); var crypto = require("crypto");
var createHash = crypto.createHash;
exports.sha512 = function(buf) { exports.sha512 = function(buf) {
var hash = crypto.createHash("sha512"); return createHash("sha512").update(buf).digest();
hash.update(buf);
return Promise.resolve(hash.digest());
}; };
exports.sha256 = function(buf) { exports.sha256 = function(buf) {
var hash = crypto.createHash("sha256"); return createHash("sha256").update(buf).digest();
hash.update(buf);
return Promise.resolve(hash.digest());
}; };
exports.ripemd160 = function(buf) { exports.ripemd160 = function(buf) {
var hash = crypto.createHash("ripemd160"); return createHash("ripemd160").update(buf).digest();
hash.update(buf);
return Promise.resolve(hash.digest());
}; };
exports.randomBytes = crypto.randomBytes; exports.randomBytes = crypto.randomBytes;

View File

@ -6,7 +6,6 @@
"use strict"; "use strict";
require("es6-promise").polyfill();
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");
@ -14,41 +13,31 @@ var bmcrypto = require("./crypto");
// Compute the WIF checksum for the given data. // Compute the WIF checksum for the given data.
function getchecksum(data) { function getchecksum(data) {
return bmcrypto.sha256(data).then(bmcrypto.sha256).then(function(dhash) { return bmcrypto.sha256(bmcrypto.sha256(data)).slice(0, 4);
return dhash.slice(0, 4);
});
} }
/** /**
* Decode WIF encoded private key. * Decode WIF encoded private key.
* @param {string} wif - Encoded key * @param {string} wif - Encoded key
* @return {Promise.<Buffer>} Private key. * @return {Buffer} Private key.
*/ */
exports.decode = function(wif) { exports.decode = function(wif) {
var bytes; var bytes = bs58.decode(wif);
try {
bytes = bs58.decode(wif);
assert(bytes[0] === 0x80, "Bad WIF"); assert(bytes[0] === 0x80, "Bad WIF");
} catch(e) {
return Promise.reject(e);
}
var data = new Buffer(bytes.slice(0, -4)); var data = new Buffer(bytes.slice(0, -4));
var checksum = new Buffer(bytes.slice(-4)); var checksum = new Buffer(bytes.slice(-4));
return getchecksum(data).then(function(realchecksum) { assert(bufferEqual(checksum, getchecksum(data)), "Bad checkum");
assert(bufferEqual(checksum, realchecksum), "Bad checkum");
return data.slice(1); return data.slice(1);
});
}; };
/** /**
* Convert private key to a WIF. * Convert private key to a WIF.
* @param {Buffer} privateKey - A private key to encode * @param {Buffer} privateKey - A private key to encode
* @return {Promise.<string>} Encoded private key. * @return {string} Encoded private key.
*/ */
exports.encode = function(privateKey) { exports.encode = function(privateKey) {
var data = Buffer.concat([new Buffer([0x80]), privateKey]); var data = Buffer.concat([new Buffer([0x80]), privateKey]);
return getchecksum(data).then(function(checksum) { var checksum = getchecksum(data);
var bytes = Buffer.concat([data, checksum]); var bytes = Buffer.concat([data, checksum]);
return bs58.encode(bytes); return bs58.encode(bytes);
});
}; };

View File

@ -48,8 +48,8 @@
"bs58": "^2.0.0", "bs58": "^2.0.0",
"buffer-equal": "~0.0.1", "buffer-equal": "~0.0.1",
"eccrypto": "^0.1.1", "eccrypto": "^0.1.1",
"es6-promise": "^2.0.1",
"object.assign": "^1.1.1", "object.assign": "^1.1.1",
"ripemd160": "^0.2.0" "ripemd160": "^0.2.0",
"sha.js": "^2.3.0"
} }
} }

53
test.js
View File

@ -13,21 +13,15 @@ var Address = bitmessage.Address;
describe("Crypto", function() { describe("Crypto", function() {
it("should implement SHA-512 hash", function() { it("should implement SHA-512 hash", function() {
return bmcrypto.sha512(Buffer("test")).then(function(res) { expect(bmcrypto.sha512(Buffer("test")).toString("hex")).to.equal("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff");
expect(res.toString("hex")).to.equal("ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff");
});
}); });
it("should implement SHA-256 hash", function() { it("should implement SHA-256 hash", function() {
return bmcrypto.sha256(Buffer("test")).then(function(res) { expect(bmcrypto.sha256(Buffer("test")).toString("hex")).to.equal("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08");
expect(res.toString("hex")).to.equal("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08");
});
}); });
it("should implement RIPEMD-160 hash", function() { it("should implement RIPEMD-160 hash", function() {
return bmcrypto.ripemd160(Buffer("test")).then(function(res) { expect(bmcrypto.ripemd160(Buffer("test")).toString("hex")).to.equal("5e52fee47e6b070565f74372468cdc699de89107");
expect(res.toString("hex")).to.equal("5e52fee47e6b070565f74372468cdc699de89107");
});
}); });
it("should implement cryptographically secure PRNG", function() { it("should implement cryptographically secure PRNG", function() {
@ -165,78 +159,63 @@ describe("WIF", function() {
var encPrivateKey = Buffer("9f9969c93c2d186787a7653f70e49be34c03c4a853e6ad0c867db0946bc433c6", "hex"); var encPrivateKey = Buffer("9f9969c93c2d186787a7653f70e49be34c03c4a853e6ad0c867db0946bc433c6", "hex");
it("should decode", function() { it("should decode", function() {
return WIF.decode(wifSign) var key1 = WIF.decode(wifSign);
.then(function(key1) {
expect(Buffer.isBuffer(key1)).to.be.true; expect(Buffer.isBuffer(key1)).to.be.true;
expect(key1.length).to.equal(32); expect(key1.length).to.equal(32);
expect(key1.toString("hex")).to.equal(signPrivateKey.toString("hex")); expect(key1.toString("hex")).to.equal(signPrivateKey.toString("hex"));
return WIF.decode(wifEnc).then(function(key2) { var key2 = WIF.decode(wifEnc);
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 Address({signPrivateKey: key1, encPrivateKey: key2}).encode(); var addrStr = Address({signPrivateKey: key1, encPrivateKey: key2}).encode();
}); expect(addrStr).to.equal("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z");
}).then(function(str) {
expect(str).to.equal("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z");
});
}); });
it("should encode", function() { it("should encode", function() {
return WIF.encode(signPrivateKey).then(function(wif1) { var wif1 = WIF.encode(signPrivateKey);
expect(wif1).to.equal(wifSign); expect(wif1).to.equal(wifSign);
return WIF.encode(encPrivateKey); var wif2 = WIF.encode(encPrivateKey);
}).then(function(wif2) {
expect(wif2).to.equal(wifEnc); expect(wif2).to.equal(wifEnc);
}); });
}); });
});
describe("Address", function() { describe("Address", function() {
it("should decode Bitmessage address", function() { it("should decode Bitmessage address", function() {
return Address.decode("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z") var addr = Address.decode("BM-2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z")
.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.ripe.toString("hex")).to.equal("003ab6655de4bd8c603eba9b00dd5970725fdd56"); expect(addr.ripe.toString("hex")).to.equal("003ab6655de4bd8c603eba9b00dd5970725fdd56");
}); });
});
it("should decode Bitmessage address badly formatted", function() { it("should decode Bitmessage address badly formatted", function() {
return Address.decode(" 2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z ") var addr = Address.decode(" 2cTux3PGRqHTEH6wyUP2sWeT4LrsGgy63z ")
.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.ripe.toString("hex")).to.equal("003ab6655de4bd8c603eba9b00dd5970725fdd56"); expect(addr.ripe.toString("hex")).to.equal("003ab6655de4bd8c603eba9b00dd5970725fdd56");
}); });
});
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.fromRandom().then(function(addr) { var addr = Address.fromRandom();
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 addr.encode().then(function(str) { var str = addr.encode();
expect(str.slice(0, 3)).to.equal("BM-"); expect(str.slice(0, 3)).to.equal("BM-");
return Address.decode(str).then(function(addr2) { var addr2 = Address.decode(str);
expect(addr2.version).to.equal(4); expect(addr2.version).to.equal(4);
expect(addr2.stream).to.equal(1); expect(addr2.stream).to.equal(1);
expect(addr2.ripe.length).to.equal(20); expect(addr2.ripe.length).to.equal(20);
expect(addr2.ripe[0]).to.equal(0); expect(addr2.ripe[0]).to.equal(0);
}); });
});
});
});
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.fromRandom({ripelen: 18}).then(function(addr) { var addr = Address.fromRandom({ripelen: 18});
return addr.getRipe({short: true}).then(function(ripe) { var ripe = addr.getRipe({short: true});
expect(ripe.length).to.be.at.most(18); expect(ripe.length).to.be.at.most(18);
}); });
});
});
} }
}); });