diff --git a/lib/address.js b/lib/address.js index 5680c63..e5b341e 100644 --- a/lib/address.js +++ b/lib/address.js @@ -23,21 +23,14 @@ function Address(opts) { if (!(this instanceof Address)) { return new Address(opts); } - opts = opts || {}; - objectAssign(this, opts); - this.version = this.version || 4; + opts = objectAssign({}, opts); + // Pull out version right away because it may be needed in setters. + this.version = popkey(opts, "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, 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; - } - } + this.stream = popkey(opts, "stream") || 1; + // Merge remained values. + objectAssign(this, opts); } /** @@ -80,46 +73,16 @@ function getaddrchecksum(data) { return bmcrypto.sha512(bmcrypto.sha512(data)).slice(0, 4); } -// Get RIPEMD160(SHA512(SIGN_PUBLIC_KEY || ENC_PUBLIC_KEY)). -// Arguments could be either private or public keys. Private keys are -// **always** 32 bytes in length. -function keys2ripe(signKey, encKey) { - var signPublicKey, encPublicKey; - if (signKey.length === 32) { - signPublicKey = bmcrypto.getPublic(signKey); - } else { - signPublicKey = signKey; - } - if (encKey.length === 32) { - encPublicKey = bmcrypto.getPublic(encKey); - } else { - encPublicKey = encKey; - } - var dataToHash = Buffer.concat([signPublicKey, encPublicKey]); - return bmcrypto.ripemd160(bmcrypto.sha512(dataToHash)); -} - /** * Calculate the ripe hash of the address. * @param {?Object} opts - Options * @return {Buffer} Resulting ripe hash. */ Address.prototype.getRipe = function(opts) { - var ripe; opts = opts || {}; - if (this.ripe) { - ripe = this.ripe; - } else { - var signKey = this.signPrivateKey || this.signPublicKey; - assert(signKey, "No signing key"); - var encKey = this.encPrivateKey || this.encPublicKey; - assert(encKey, "No encryption key"); - ripe = keys2ripe(signKey, encKey); - } - var ripelen = getripelen(ripe); - assertripelen(ripelen, this.version, ripe); + var ripe = this.ripe; if (opts.short) { - return ripe.slice(20 - ripelen); + return ripe.slice(20 - getripelen(ripe)); } else { return ripe; } @@ -231,13 +194,8 @@ Address.fromRandom = function(opts) { ripe = bmcrypto.ripemd160(bmcrypto.sha512(keysbuf)); len = getripelen(ripe); if (len <= ripelen && checkripelen(len, version)) { - // TODO(Kagami): Do we need to put all these properties or compute - // them manually via ECMA5 getters/setters instead? opts.signPrivateKey = signPrivateKey; - opts.signPublicKey = signPublicKey; opts.encPrivateKey = encPrivateKey; - opts.encPublicKey = encPublicKey; - opts.ripe = ripe; return new Address(opts); } } @@ -280,13 +238,8 @@ Address.fromPassphrase = function(opts) { ripe = bmcrypto.ripemd160(bmcrypto.sha512(keysbuf)); len = getripelen(ripe); if (len <= ripelen && checkripelen(len, version)) { - // TODO(Kagami): Do we need to put all these properties or compute - // them manually via ECMA5 getters/setters instead? opts.signPrivateKey = signPrivateKey; - opts.signPublicKey = signPublicKey; opts.encPrivateKey = encPrivateKey; - opts.encPublicKey = encPublicKey; - opts.ripe = ripe; return new Address(opts); } signnonce += 2; @@ -294,4 +247,81 @@ Address.fromPassphrase = function(opts) { } }; +Object.defineProperty(Address.prototype, "signPrivateKey", { + get: function() { + return this._signPrivateKey; + }, + set: function(signPrivateKey) { + this._signPrivateKey = signPrivateKey; + // Invalidate cached values; + delete this._signPublicKey; + delete this._ripe; + }, +}); + +Object.defineProperty(Address.prototype, "signPublicKey", { + get: function() { + if (this._signPublicKey) { + return this._signPublicKey; + } else if (this.signPrivateKey) { + this._signPublicKey = bmcrypto.getPublic(this.signPrivateKey); + return this._signPublicKey; + } else { + throw new Error("No signing key"); + } + }, + set: function(signPublicKey) { + this._signPublicKey = signPublicKey; + }, +}); + +Object.defineProperty(Address.prototype, "encPrivateKey", { + get: function() { + return this._encPrivateKey; + }, + set: function(encPrivateKey) { + this._encPrivateKey = encPrivateKey; + // Invalidate cached values; + delete this._encPublicKey; + delete this._ripe; + }, +}); + +Object.defineProperty(Address.prototype, "encPublicKey", { + get: function() { + if (this._encPublicKey) { + return this._encPublicKey; + } else if (this.encPrivateKey) { + this._encPublicKey = bmcrypto.getPublic(this.encPrivateKey); + return this._encPublicKey; + } else { + throw new Error("No encryption key"); + } + }, + set: function(encPublicKey) { + this._encPublicKey = encPublicKey; + }, +}); + +Object.defineProperty(Address.prototype, "ripe", { + get: function() { + if (this._ripe) { + return this._ripe; + } + var dataToHash = Buffer.concat([this.signPublicKey, this.encPublicKey]); + this._ripe = bmcrypto.ripemd160(bmcrypto.sha512(dataToHash)); + return this._ripe; + }, + set: function(ripe) { + assertripelen(getripelen(ripe), this.version, ripe); + if (ripe.length < 20) { + var fullripe = new Buffer(20); + fullripe.fill(0); + ripe.copy(fullripe, 20 - ripe.length); + ripe = fullripe; + } + this._ripe = ripe; + }, +}); + module.exports = Address; diff --git a/test.js b/test.js index 12ffe60..b773d1d 100644 --- a/test.js +++ b/test.js @@ -581,6 +581,20 @@ describe("High-level classes", function() { expect(Address.decode(addr)).to.equal(addr); }); + it("should provide setters for keys and ripe", function() { + var addr = Address(); + expect(function(){addr.ripe}).to.throw(Error); + addr.signPrivateKey = Buffer("71c95d26c716a5e85e9af9efe26fb5f744dc98005a13d05d23ee92c77e038d9f", "hex"); + expect(addr.signPublicKey.toString("hex")).to.equal("042d391543f574608cbcdfd12a37cc4c74dd36e54510b13a6a1d8b7b1498fb96c92873d33ca17586dace7f5ad0f4532a954061ac06bc5230aed9c8374072546571"); + expect(function(){addr.ripe}).to.throw(Error); + addr.encPrivateKey = Buffer("9f9969c93c2d186787a7653f70e49be34c03c4a853e6ad0c867db0946bc433c6", "hex"); + expect(addr.encPublicKey.toString("hex")).to.equal("04c6ed1b56f2da97fec1b762d43364566faf082c1e4918ae1dbb41757cad41b03b2cc5087f341414e63f6eee72a1fbf0b5f346a1bb3ba944cad204ca597db2bfc8"); + expect(addr.ripe.toString("hex")).to.equal("003ab6655de4bd8c603eba9b00dd5970725fdd56"); + expect(addr.getRipe({short: true}).toString("hex")).to.equal("3ab6655de4bd8c603eba9b00dd5970725fdd56"); + addr.encPrivateKey = Buffer("009969c93c2d186787a7653f70e49be34c03c4a853e6ad0c867db0946bc433c6", "hex"); + expect(addr.getRipe({short: true}).toString("hex")).to.equal("69617ddb1946dc327cadffcf33889fed587fc1e7"); + }); + // FIXME(Kagami): Don't run it in browser currently because it's // very slow. This need to be fixed. if (allTests && typeof window === "undefined") {