mirror of https://github.com/BTCPrivate/copay.git
optimize public key generation for BIP32 with cache
This commit is contained in:
parent
6d822a9153
commit
3e8ca41a0a
|
@ -25,10 +25,12 @@ function PublicKeyRing(opts) {
|
||||||
this.requiredCopayers = opts.requiredCopayers || 3;
|
this.requiredCopayers = opts.requiredCopayers || 3;
|
||||||
this.totalCopayers = opts.totalCopayers || 5;
|
this.totalCopayers = opts.totalCopayers || 5;
|
||||||
|
|
||||||
this.copayersBIP32 = [];
|
this.copayersBIP32 = opts.copayersBIP32 || [];
|
||||||
|
|
||||||
this.changeAddressIndex=0;
|
this.changeAddressIndex= opts.changeAddressIndex || 0;
|
||||||
this.addressIndex=0;
|
this.addressIndex= opts.addressIndex || 0;
|
||||||
|
|
||||||
|
this.publicKeysCache = opts.publicKeysCache || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -45,24 +47,13 @@ PublicKeyRing.Branch = function (index, isChange) {
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.fromObj = function (data) {
|
PublicKeyRing.fromObj = function (data) {
|
||||||
if (!data.ts) {
|
if (data instanceof PublicKeyRing) {
|
||||||
throw new Error('bad data format: Did you use .toObj()?');
|
throw new Error('bad data format: Did you use .toObj()?');
|
||||||
}
|
}
|
||||||
var config = { networkName: data.networkName || 'livenet' };
|
data.copayersBIP32 = data.copayersExtPubKeys.map(function(pk) {
|
||||||
|
|
||||||
var w = new PublicKeyRing(config);
|
|
||||||
|
|
||||||
w.walletId = data.walletId;
|
|
||||||
w.requiredCopayers = data.requiredCopayers;
|
|
||||||
w.totalCopayers = data.totalCopayers;
|
|
||||||
w.addressIndex = data.addressIndex;
|
|
||||||
w.changeAddressIndex = data.changeAddressIndex;
|
|
||||||
w.copayersBIP32 = data.copayersExtPubKeys.map( function (pk) {
|
|
||||||
return new BIP32(pk);
|
return new BIP32(pk);
|
||||||
});
|
});
|
||||||
|
return new PublicKeyRing(data);
|
||||||
w.ts = data.ts;
|
|
||||||
return w;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
PublicKeyRing.prototype.toObj = function() {
|
PublicKeyRing.prototype.toObj = function() {
|
||||||
|
@ -77,7 +68,7 @@ PublicKeyRing.prototype.toObj = function() {
|
||||||
copayersExtPubKeys: this.copayersBIP32.map( function (b) {
|
copayersExtPubKeys: this.copayersBIP32.map( function (b) {
|
||||||
return b.extendedPublicKeyString();
|
return b.extendedPublicKeyString();
|
||||||
}),
|
}),
|
||||||
ts: parseInt(Date.now() / 1000),
|
publicKeysCache: this.publicKeysCache
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,7 +82,6 @@ PublicKeyRing.prototype.registeredCopayers = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
PublicKeyRing.prototype.isComplete = function () {
|
PublicKeyRing.prototype.isComplete = function () {
|
||||||
return this.registeredCopayers() >= this.totalCopayers;
|
return this.registeredCopayers() >= this.totalCopayers;
|
||||||
};
|
};
|
||||||
|
@ -130,13 +120,20 @@ PublicKeyRing.prototype.addCopayer = function (newEpk) {
|
||||||
PublicKeyRing.prototype.getPubKeys = function (index, isChange) {
|
PublicKeyRing.prototype.getPubKeys = function (index, isChange) {
|
||||||
this._checkKeys();
|
this._checkKeys();
|
||||||
|
|
||||||
var pubKeys = [];
|
var path = PublicKeyRing.Branch(index, isChange);
|
||||||
|
var pubKeys = this.publicKeysCache[path];
|
||||||
|
if (!pubKeys) {
|
||||||
|
pubKeys = [];
|
||||||
var l = this.copayersBIP32.length;
|
var l = this.copayersBIP32.length;
|
||||||
for(var i=0; i<l; i++) {
|
for(var i=0; i<l; i++) {
|
||||||
var path = PublicKeyRing.Branch(index, isChange);
|
|
||||||
var bip32 = this.copayersBIP32[i].derive(path);
|
var bip32 = this.copayersBIP32[i].derive(path);
|
||||||
pubKeys[i] = bip32.eckey.public;
|
pubKeys[i] = bip32.eckey.public;
|
||||||
}
|
}
|
||||||
|
this.publicKeysCache[path] = pubKeys;
|
||||||
|
//console.log('cache fill['+path+']='+pubKeys.length);
|
||||||
|
} else {
|
||||||
|
//console.log('cache hit!');
|
||||||
|
}
|
||||||
|
|
||||||
return pubKeys;
|
return pubKeys;
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,32 +2,25 @@
|
||||||
|
|
||||||
var chai = chai || require('chai');
|
var chai = chai || require('chai');
|
||||||
var should = chai.should();
|
var should = chai.should();
|
||||||
|
var PrivateKey = require('../js/models/core/PrivateKey');
|
||||||
|
var PublicKeyRing = require('../js/models/core/PublicKeyRing');
|
||||||
|
|
||||||
describe('Performance tests', function() {
|
describe('Performance tests', function() {
|
||||||
var config = {
|
|
||||||
wallet: {
|
|
||||||
requiredCopayers: 1,
|
|
||||||
totalCopayers: 1,
|
|
||||||
spendUnconfirmed: 1,
|
|
||||||
},
|
|
||||||
networkName: 'testnet',
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('PrivateKey', function() {
|
describe('PrivateKey', function() {
|
||||||
it('should optimize BIP32 private key gen time with cache', function() {
|
it('should optimize BIP32 private key gen time with cache', function() {
|
||||||
var k1 = new PrivateKey();
|
var k1 = new PrivateKey();
|
||||||
var generateN = 25;
|
var generateN = 25;
|
||||||
var generated = [];
|
var generated = [];
|
||||||
var start1 = new Date().getTime();
|
var start1 = new Date().getTime();
|
||||||
for (var i=0; i<generateN; i++) {
|
for (var i = 0; i < generateN; i++) {
|
||||||
var k = JSON.stringify(k1.get(i, false).storeObj());
|
var k = JSON.stringify(k1.get(i, false).storeObj());
|
||||||
generated.push(k);
|
generated.push(k);
|
||||||
}
|
}
|
||||||
var delta1 = new Date().getTime() - start1;
|
var delta1 = new Date().getTime() - start1;
|
||||||
var backup = k1.toObj();
|
var backup = k1.toObj();
|
||||||
var k2 = new PrivateKey(backup);
|
var k2 = PrivateKey.fromObj(backup);
|
||||||
var start2 = new Date().getTime();
|
var start2 = new Date().getTime();
|
||||||
for (var i=0; i<generateN; i++) {
|
for (var i = 0; i < generateN; i++) {
|
||||||
var k = JSON.stringify(k2.get(i, false).storeObj());
|
var k = JSON.stringify(k2.get(i, false).storeObj());
|
||||||
generated[i].should.equal(k);
|
generated[i].should.equal(k);
|
||||||
}
|
}
|
||||||
|
@ -35,5 +28,42 @@ describe('Performance tests', function() {
|
||||||
delta2.should.be.below(delta1);
|
delta2.should.be.below(delta1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('PublicKeyRing', function() {
|
||||||
|
var maxN = 7;
|
||||||
|
for (var n = 1; n < maxN; n++) {
|
||||||
|
for (var m = 1; m <= n; m++) {
|
||||||
|
var M = m;
|
||||||
|
var N = n;
|
||||||
|
(function(M, N) {
|
||||||
|
it('should optimize BIP32 publickey gen time with cache for ' + M + '-of-' + N, function() {
|
||||||
|
var pkr1 = new PublicKeyRing({
|
||||||
|
totalCopayers: N,
|
||||||
|
requiredCopayers: M
|
||||||
|
});
|
||||||
|
for (var i = 0; i < N; i++) {
|
||||||
|
pkr1.addCopayer(); // add new random ext public key
|
||||||
|
}
|
||||||
|
var generateN = 5;
|
||||||
|
var generated = [];
|
||||||
|
var start1 = new Date().getTime();
|
||||||
|
for (var i = 0; i < generateN; i++) {
|
||||||
|
var pubKeys = JSON.stringify(pkr1.getPubKeys(i, false));
|
||||||
|
generated.push(pubKeys);
|
||||||
|
}
|
||||||
|
var delta1 = new Date().getTime() - start1;
|
||||||
|
var backup = pkr1.toObj();
|
||||||
|
var pkr2 = PublicKeyRing.fromObj(backup);
|
||||||
|
var start2 = new Date().getTime();
|
||||||
|
for (var i = 0; i < generateN; i++) {
|
||||||
|
var pubKeys = JSON.stringify(pkr2.getPubKeys(i, false));
|
||||||
|
generated[i].should.equal(pubKeys);
|
||||||
|
}
|
||||||
|
var delta2 = new Date().getTime() - start2;
|
||||||
|
delta2.should.be.below(delta1);
|
||||||
|
});
|
||||||
|
})(M, N);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue