add testing for key cache

This commit is contained in:
Esteban Ordano 2014-11-30 19:08:21 -03:00
parent 13fd2592fc
commit 865a69f6f2
3 changed files with 90 additions and 6 deletions

View File

@ -18,11 +18,13 @@ bitcore.encoding.BufferReader = require('./lib/encoding/bufferreader');
bitcore.encoding.BufferWriter = require('./lib/encoding/bufferwriter');
bitcore.encoding.Varint = require('./lib/encoding/varint');
// utilities
bitcore.util = {};
bitcore.util.bitcoin = require('./lib/util/bitcoin');
bitcore.util.buffer = require('./lib/util/buffer');
bitcore.util.js = require('./lib/util/js');
// errors thrown by the library
bitcore.errors = require('./lib/errors');
// main bitcoin library
@ -41,7 +43,7 @@ bitcore.Txin = require('./lib/txin');
bitcore.Txout = require('./lib/txout');
//dependencies, subject to change
// dependencies, subject to change
bitcore.deps = {};
bitcore.deps.bnjs = require('bn.js');
bitcore.deps.bs58 = require('bs58');
@ -53,3 +55,6 @@ bitcore.deps.elliptic = require('elliptic');
//bitcore.txpartial = require('lib/txpartial');
//bitcore.bip70 = require('lib/bip70');
// Internal usage, exposed for testing/advanced tweaking
bitcore._HDKeyCache = require('./lib/hdkeycache');

View File

@ -1,16 +1,45 @@
'use strict';
var cache = {};
module.exports = {
_cache: {},
_count: 0,
_eraseIndex: 0,
_usedList: {},
_usedIndex: {},
_CACHE_SIZE: 5000,
get: function(xkey, number, hardened) {
hardened = !!hardened;
var key = xkey + '/' + number + '/' + hardened;
if (cache[key]) {
return cache[key];
if (this._cache[key]) {
this._cacheHit(key);
return this._cache[key];
}
},
set: function(xkey, number, hardened, derived) {
hardened = !!hardened;
var key = xkey + '/' + number + '/' + hardened;
cache[key] = derived;
this._cache[key] = derived;
this._cacheHit(key);
},
_cacheHit: function(key) {
if (this._usedIndex[key]) {
delete this._usedList[this._usedIndex[key]];
}
this._usedList[this._count] = key;
this._usedIndex[key] = this._count;
this._count++;
this._cacheRemove();
},
_cacheRemove: function() {
while (this._eraseIndex < this._count - this._CACHE_SIZE) {
if (this._usedList[this._eraseIndex]) {
var removeKey = this._usedList[this._eraseIndex];
delete this._usedIndex[removeKey];
delete this._cache[removeKey];
}
delete this._usedList[this._eraseIndex];
this._eraseIndex++;
}
}
};

50
test/hdkeycache.js Normal file
View File

@ -0,0 +1,50 @@
'use strict';
var _ = require('lodash');
var expect = require('chai').expect;
var bitcore = require('..');
var HDPrivateKey = bitcore.HDPrivateKey;
var xprivkey = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
describe('HDKey cache', function() {
/* jshint unused: false */
var cache = bitcore._HDKeyCache;
var master = new HDPrivateKey(xprivkey);
beforeEach(function() {
cache._cache = {};
cache._count = 0;
cache._eraseIndex = 0;
cache._usedIndex = {};
cache._usedList = {};
cache._CACHE_SIZE = 3; // Reduce for quick testing
});
it('saves a derived key', function() {
var child = master.derive(0);
expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child.xprivkey);
});
it('starts erasing unused keys', function() {
var child1 = master.derive(0);
var child2 = child1.derive(0);
var child3 = child2.derive(0);
expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey);
var child4 = child3.derive(0);
expect(cache._cache[master.xprivkey + '/0/false']).to.equal(undefined);
});
it('avoids erasing keys that get cache hits ("hot keys")', function() {
var child1 = master.derive(0);
var child2 = master.derive(0).derive(0);
expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey);
var child1_copy = master.derive(0);
expect(cache._cache[master.xprivkey + '/0/false'].xprivkey).to.equal(child1.xprivkey);
});
it('keeps the size of the cache small', function() {
var child1 = master.derive(0);
var child2 = child1.derive(0);
var child3 = child2.derive(0);
var child4 = child3.derive(0);
expect(_.size(cache._cache)).to.equal(3);
});
});