diff --git a/lib/hdprivatekey.js b/lib/hdprivatekey.js index 3b478b815..0d951d599 100644 --- a/lib/hdprivatekey.js +++ b/lib/hdprivatekey.js @@ -306,7 +306,10 @@ HDPrivateKey.prototype._buildFromBuffers = function(arg) { /* jshint maxstatements: 20 */ HDPrivateKey._validateBufferArguments(arg); - this._buffers = arg; + Object.defineProperty(this, '_buffers', { + configurable: false, + value: arg + }); var sequence = [ arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode, @@ -321,21 +324,35 @@ HDPrivateKey.prototype._buildFromBuffers = function(arg) { } } - if (!arg.xprivkey) { - this.xprivkey = Base58Check.encode(buffer.Buffer.concat(sequence)); - } else { - this.xprivkey = arg.xprivkey; - } - this.network = Network.get(bufferUtil.integerFromBuffer(arg.version)); - this.depth = bufferUtil.integerFromSingleByteBuffer(arg.depth); - this.privateKey = new PrivateKey(BN().fromBuffer(arg.privateKey)); - this.publicKey = this.privateKey.toPublicKey(); + var xprivkey; - this.fingerPrint = Hash.sha256ripemd160(this.publicKey.toBuffer()).slice(0, HDPrivateKey.ParentFingerPrintSize); + if (!arg.xprivkey) { + xprivkey = Base58Check.encode(buffer.Buffer.concat(sequence)); + } else { + xprivkey = arg.xprivkey; + } + + var privateKey = new PrivateKey(BN().fromBuffer(arg.privateKey)); + var publicKey = privateKey.toPublicKey(); + var size = HDPrivateKey.ParentFingerPrintSize; + var fingerPrint = Hash.sha256ripemd160(publicKey.toBuffer()).slice(0, size); + + jsUtil.defineImmutable(this, { + xprivkey: xprivkey, + network: Network.get(bufferUtil.integerFromBuffer(arg.version)), + depth: bufferUtil.integerFromSingleByteBuffer(arg.depth), + privateKey: privateKey, + publicKey: publicKey, + fingerPrint: fingerPrint + }); var HDPublicKey = require('./hdpublickey'); - this.hdPublicKey = new HDPublicKey(this); - this.xpubkey = this.hdPublicKey.xpubkey; + var hdPublicKey = new HDPublicKey(this); + + jsUtil.defineImmutable(this, { + hdPublicKey: hdPublicKey, + xpubkey: hdPublicKey.xpubkey + }); return this; }; diff --git a/lib/hdpublickey.js b/lib/hdpublickey.js index a11e0c0eb..99ef1ad9a 100644 --- a/lib/hdpublickey.js +++ b/lib/hdpublickey.js @@ -279,7 +279,11 @@ HDPublicKey.prototype._buildFromBuffers = function (arg) { /* jshint maxstatements: 20 */ HDPublicKey._validateBufferArguments(arg); - this._buffers = arg; + + Object.defineProperty(this, '_buffers', { + configurable: false, + value: arg + }); var sequence = [ arg.version, arg.depth, arg.parentFingerPrint, arg.childIndex, arg.chainCode, @@ -295,16 +299,25 @@ HDPublicKey.prototype._buildFromBuffers = function (arg) { } } + var xpubkey; + if (!arg.xpubkey) { - this.xpubkey = Base58Check.encode(bufferUtil.concat(sequence)); + xpubkey = Base58Check.encode(bufferUtil.concat(sequence)); } else { - this.xpubkey = arg.xpubkey; + xpubkey = arg.xpubkey; } - this.network = Network.get(bufferUtil.integerFromBuffer(arg.version)); - this.depth = bufferUtil.integerFromSingleByteBuffer(arg.depth); - this.publicKey = PublicKey.fromString(arg.publicKey); - this.fingerPrint = Hash.sha256ripemd160(this.publicKey.toBuffer()).slice(0, HDPublicKey.ParentFingerPrintSize); + var publicKey = PublicKey.fromString(arg.publicKey); + var size = HDPublicKey.ParentFingerPrintSize; + var fingerPrint = Hash.sha256ripemd160(publicKey.toBuffer()).slice(0, size); + + jsUtil.defineImmutable(this, { + xpubkey: xpubkey, + network: Network.get(bufferUtil.integerFromBuffer(arg.version)), + depth: bufferUtil.integerFromSingleByteBuffer(arg.depth), + publicKey: publicKey, + fingerPrint: fingerPrint + }); return this; }; diff --git a/lib/util/js.js b/lib/util/js.js index a4b2e3ae7..9b53732bb 100644 --- a/lib/util/js.js +++ b/lib/util/js.js @@ -31,5 +31,22 @@ module.exports = { } }, isHexa: isHexa, - isHexaString: isHexa + isHexaString: isHexa, + + /** + * Define immutable properties on a target object + * + * @param {Object} target - An object to be extended + * @param {Object} values - An object of properties + * @return {Object} The target object + */ + defineImmutable: function defineImmutable(target, values){ + Object.keys(values).forEach(function(key){ + Object.defineProperty(target, key, { + configurable: false, + value: values[key] + }); + }); + return target; + } }; diff --git a/test/hdprivatekey.js b/test/hdprivatekey.js index c1329f9ac..2643ddca9 100644 --- a/test/hdprivatekey.js +++ b/test/hdprivatekey.js @@ -50,6 +50,13 @@ describe('HDPrivate key interface', function() { should.exist(new HDPrivateKey().xprivkey); }); + it('should not be able to change read-only properties', function() { + var hdkey = new HDPrivateKey(); + expect(function() { + hdkey.fingerPrint = 'notafingerprint'; + }).to.throw(TypeError); + }); + it('should error with an invalid checksum', function() { expectFailBuilding(xprivkey + '1', errors.InvalidB58Checksum); }); diff --git a/test/hdpublickey.js b/test/hdpublickey.js index 9ab06ddad..8f28b670a 100644 --- a/test/hdpublickey.js +++ b/test/hdpublickey.js @@ -69,6 +69,13 @@ describe('HDPublicKey interface', function() { expectFailBuilding(null, hdErrors.MustSupplyArgument); }); + it('should not be able to change read-only properties', function() { + var publicKey = new HDPublicKey(xprivkey); + expect(function() { + publicKey.fingerPrint = 'notafingerprint'; + }).to.throw(TypeError); + }); + it('doesn\'t recognize an invalid argument', function() { expectFailBuilding(1, hdErrors.UnrecognizedArgument); expectFailBuilding(true, hdErrors.UnrecognizedArgument);