optimize PrivateKey with cache

This commit is contained in:
Manuel Araoz 2014-04-17 17:01:31 -03:00
parent f90decca0e
commit 5847d6aff2
8 changed files with 109 additions and 50 deletions

View File

@ -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;
};

View File

@ -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<l; i++) {
var path = isChange ? PublicKeyRing.ChangeBranch(index) : PublicKeyRing.PublicBranch(index);
var path = PublicKeyRing.Branch(index, isChange);
var bip32 = this.copayersBIP32[i].derive(path);
pubKeys[i] = bip32.eckey.public;
}

View File

@ -69,7 +69,7 @@ WalletFactory.prototype.read = function(walletId) {
// JIC: Add our key
try {
w.publicKeyRing.addCopayer(
w.privateKey.getBIP32().extendedPublicKeyString()
w.privateKey.getExtendedPublicKeyString()
);
} catch (e) {
this.log('NOT NECCESARY AN ERROR:', e); //TODO
@ -94,7 +94,7 @@ WalletFactory.prototype.create = function(opts) {
requiredCopayers: requiredCopayers,
totalCopayers: totalCopayers,
});
opts.publicKeyRing.addCopayer(opts.privateKey.getBIP32().extendedPublicKeyString());
opts.publicKeyRing.addCopayer(opts.privateKey.getExtendedPublicKeyString());
this.log('\t### PublicKeyRing Initialized');
opts.txProposals = opts.txProposals || new TxProposals({
@ -110,7 +110,7 @@ WalletFactory.prototype.create = function(opts) {
opts.spendUnconfirmed = opts.spendUnconfirmed || this.walletDefaults.spendUnconfirmed;
opts.requiredCopayers = requiredCopayers;
opts.totalCopayers = totalCopayers;
var w = new Wallet(opts);
var w = new Wallet(opts);
w.store();
return w;
};
@ -139,7 +139,7 @@ WalletFactory.prototype.openRemote = function(peedId) {
requiredCopayers: requiredCopayers,
totalCopayers: totalCopayers,
});
opts.publicKeyRing.addCopayer(opts.privateKey.getBIP32().extendedPublicKeyString());
opts.publicKeyRing.addCopayer(opts.privateKey.getExtendedPublicKeyString());
this.log('\t### PublicKeyRing Initialized');
opts.txProposals = opts.txProposals || new TxProposals({

View File

@ -19,6 +19,7 @@
</script>
<script src="test.API.js"></script>
<script src="test.blockchain.Insight.js"></script>
<script src="test.performance.js"></script>
<script src="test.PrivateKey.js"></script>
<script src="test.PublicKeyRing.js"></script>
<script src="test.storage.LocalPlain.js"></script>

View File

@ -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()));
});

View File

@ -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,

View File

@ -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();

51
test/test.performance.js Normal file
View File

@ -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<generateN; i++) {
var k = JSON.stringify(k1.get(i, false).storeObj());
generated.push(k);
}
var delta1 = new Date().getTime() - start1;
var backup = k1.toObj();
var k2 = new PrivateKey(backup);
var start2 = new Date().getTime();
for (var i=0; i<generateN; i++) {
var k = JSON.stringify(k2.get(i, false).storeObj());
generated[i].should.equal(k);
}
var delta2 = new Date().getTime() - start2;
delta2.should.be.below(delta1);
});
});
});