diff --git a/js/models/core/PrivateKey.js b/js/models/core/PrivateKey.js index c1ea17561..9d9a7b009 100644 --- a/js/models/core/PrivateKey.js +++ b/js/models/core/PrivateKey.js @@ -10,45 +10,57 @@ var util = bitcore.util; var PublicKeyRing = require('./PublicKeyRing'); function PrivateKey(opts) { + opts = opts || {}; this.network = opts.networkName === 'testnet' ? networks.testnet : networks.livenet; var init = opts.extendedPrivateKeyString || this.network.name; - this.BIP32 = opts.BIP32 || new BIP32(init); + this.bip = opts.BIP32 || new BIP32(init); + this.privateKeyCache = opts.privateKeyCache || {}; this._calcId(); }; PrivateKey.prototype._calcId = function() { - this.id = util.ripe160(this.BIP32.extendedPublicKey).toString('hex'); + this.id = util.ripe160(this.bip.extendedPublicKey).toString('hex'); }; -PrivateKey.prototype.getBIP32 = function(index,isChange) { - if (typeof index === 'undefined') { - return this.BIP32; - } - return this.BIP32.derive( isChange ? - PublicKeyRing.ChangeBranch(index):PublicKeyRing.PublicBranch(index) ); -}; - - -PrivateKey.fromObj = function(o) { - return new PrivateKey({ - extendedPrivateKeyString: o.extendedPrivateKeyString, - networkName: o.networkName, - }); +PrivateKey.fromObj = function(obj) { + return new PrivateKey(obj); }; PrivateKey.prototype.toObj = function() { return { - extendedPrivateKeyString: this.BIP32.extendedPrivateKeyString(), + extendedPrivateKeyString: this.getExtendedPrivateKeyString(), networkName: this.network.name, + privateKeyCache: this.privateKeyCache }; }; +PrivateKey.prototype.getExtendedPublicKeyString = function() { + return this.bip.extendedPublicKeyString(); +}; + +PrivateKey.prototype.getExtendedPrivateKeyString = function() { + return this.bip.extendedPrivateKeyString(); +}; + +PrivateKey.prototype._getBIP32 = function(path) { + if (typeof path === 'undefined') { + return this.bip; + } + return this.bip.derive(path); +}; + PrivateKey.prototype.get = function(index,isChange) { - var derivedBIP32 = this.getBIP32(index,isChange); + var path = PublicKeyRing.Branch(index, isChange); + var pk = this.privateKeyCache[path]; + if (!pk) { + var derivedBIP32 = this._getBIP32(path); + pk = this.privateKeyCache[path] = derivedBIP32.eckey.private.toString('hex'); + } else { + //console.log('cache hit!'); + } var wk = new WalletKey({network: this.network}); - var p = derivedBIP32.eckey.private.toString('hex'); - wk.fromObj({priv: p}); + wk.fromObj({priv: pk}); return wk; }; diff --git a/js/models/core/PublicKeyRing.js b/js/models/core/PublicKeyRing.js index 25feab792..25a31e6b9 100644 --- a/js/models/core/PublicKeyRing.js +++ b/js/models/core/PublicKeyRing.js @@ -40,12 +40,8 @@ function PublicKeyRing(opts) { * */ -PublicKeyRing.PublicBranch = function (index) { - return 'm/0/'+index; -}; - -PublicKeyRing.ChangeBranch = function (index) { - return 'm/1/'+index; +PublicKeyRing.Branch = function (index, isChange) { + return 'm/'+(isChange?1:0)+'/'+index; }; PublicKeyRing.fromObj = function (data) { @@ -137,7 +133,7 @@ PublicKeyRing.prototype.getPubKeys = function (index, isChange) { var pubKeys = []; var l = this.copayersBIP32.length; for(var i=0; i + diff --git a/test/test.PrivateKey.js b/test/test.PrivateKey.js index 8875e7f3f..463f18054 100644 --- a/test/test.PrivateKey.js +++ b/test/test.PrivateKey.js @@ -24,8 +24,8 @@ describe('PrivateKey model', function() { it('should create an instance', function () { var w = new PrivateKey(config); should.exist(w); - should.exist(w.BIP32); - should.exist(w.BIP32.derive); + should.exist(w.bip); + should.exist(w.bip.derive); }); it('should derive priv keys', function () { @@ -76,15 +76,14 @@ describe('PrivateKey model', function() { var w1 = new PrivateKey(config); var w2 = PrivateKey.fromObj(w1.toObj()); - w2.getBIP32().extendedPrivateKeyString().should.equal(w1.getBIP32().extendedPrivateKeyString()); - w2.getBIP32().extendedPublicKeyString().should.equal(w1.getBIP32().extendedPublicKeyString()); + w2.toObj().extendedPrivateKeyString.should.equal(w1.toObj().extendedPrivateKeyString); w2.id.should.equal(w1.id); - w2.getBIP32(1,1).extendedPrivateKeyString().should - .equal(w1.getBIP32(1,1).extendedPrivateKeyString()); - w2.getBIP32(1,0).extendedPrivateKeyString().should - .equal(w1.getBIP32(1,0).extendedPrivateKeyString()); + JSON.stringify(w2.get(1,1).storeObj()).should + .equal(JSON.stringify(w1.get(1,1).storeObj())); + JSON.stringify(w2.get(1,0).storeObj()).should + .equal(JSON.stringify(w1.get(1,0).storeObj())); }); diff --git a/test/test.TxProposals.js b/test/test.TxProposals.js index aa120a17b..4c1f6c0bd 100644 --- a/test/test.TxProposals.js +++ b/test/test.TxProposals.js @@ -43,7 +43,7 @@ var createPKR = function (bip32s) { for(var i=0; i<5; i++) { if (bip32s) { var b=bip32s[i]; - w.addCopayer(b?b.extendedPublicKeyString():null); + w.addCopayer(b?b.getExtendedPublicKeyString():null); } else w.addCopayer(); @@ -110,7 +110,7 @@ describe('TxProposals model', function() { networkName: config.networkName, }); var start = new Date().getTime(); - var pkr=createPKR([priv.getBIP32()]); + var pkr=createPKR([priv]); var ts = Date.now(); var isChange=0; var index=0; @@ -150,7 +150,7 @@ describe('TxProposals model', function() { var ts = Date.now(); var isChange=0; var index=0; - var pkr = createPKR([priv.getBIP32()]); + var pkr = createPKR([priv]); var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }}; @@ -225,7 +225,7 @@ var _dumpChunks = function (scriptSig, label) { var ts = Date.now(); var isChange=0; var index=0; - var pkr = createPKR([priv.getBIP32(), priv2.getBIP32()]); + var pkr = createPKR([priv, priv2]); var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }}; var w = new TxProposals({ @@ -325,7 +325,7 @@ var _dumpChunks = function (scriptSig, label) { var ts = Date.now(); var isChange=0; var index=0; - var pkr = createPKR([priv.getBIP32(), priv2.getBIP32(), priv3.getBIP32() ]); + var pkr = createPKR([priv, priv2, priv3]); var opts = {remainderOut: { address: pkr.generateAddress(true).toString() }}; var w = new TxProposals({ @@ -416,7 +416,7 @@ var _dumpChunks = function (scriptSig, label) { it('#toObj #fromObj roundtrip', function () { var priv = new PrivateKey(config); - var pkr = createPKR([priv.getBIP32()]); + var pkr = createPKR([priv]); var w = new TxProposals({ walletId: 'qwerty', networkName: config.networkName, diff --git a/test/test.Wallet.js b/test/test.Wallet.js index 87423c7ee..f7eaad05a 100644 --- a/test/test.Wallet.js +++ b/test/test.Wallet.js @@ -41,7 +41,7 @@ describe('Wallet model', function() { requiredCopayers: c.requiredCopayers, totalCopayers: c.totalCopayers, }); - c.publicKeyRing.addCopayer(c.privateKey.getBIP32().extendedPublicKeyString()); + c.publicKeyRing.addCopayer(c.privateKey.getExtendedPublicKeyString()); c.txProposals = new copay.TxProposals({ networkName: c.networkName, @@ -87,16 +87,16 @@ describe('Wallet model', function() { } ]; - var createW2 = function (bip32s) { + var createW2 = function (privateKeys) { var w = createW(); should.exist(w); var pkr = w.publicKeyRing; for(var i=0; i<4; i++) { - if (bip32s) { - var b=bip32s[i]; - pkr.addCopayer(b?b.extendedPublicKeyString():null); + if (privateKeys) { + var k=privateKeys[i]; + pkr.addCopayer(k?k.getExtendedPublicKeyString():null); } else pkr.addCopayer(); diff --git a/test/test.performance.js b/test/test.performance.js new file mode 100644 index 000000000..ee552ad7f --- /dev/null +++ b/test/test.performance.js @@ -0,0 +1,51 @@ +'use strict'; + +var chai = chai || require('chai'); +var should = chai.should(); + +var WebRTC = require('../js/models/network/WebRTC'); +var Insight = require('../js/models/blockchain/Insight'); +var FakeStorage = require('./mocks/FakeStorage'); +var PrivateKey = require('../js/models/core/PrivateKey'); + +var WalletFactory = typeof copay === 'undefined' ? require('soop').load('../js/models/core/WalletFactory',{ + Network: WebRTC, + Blockchain: Insight, + Storage: FakeStorage, +}) : copay.WalletFactory; + + +describe('Performance tests', function() { + var config = { + wallet: { + requiredCopayers: 1, + totalCopayers: 1, + spendUnconfirmed: 1, + }, + networkName: 'testnet', + }; + + describe('PrivateKey', function() { + it('should optimize BIP32 private key gen time with cache', function() { + var k1 = new PrivateKey(); + var generateN = 25; + var generated = []; + var start1 = new Date().getTime(); + for (var i=0; i