txdb coin cache.

This commit is contained in:
Christopher Jeffrey 2016-07-01 01:01:51 -07:00
parent 6612b7ced3
commit 5e103f0706
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 94 additions and 18 deletions

View File

@ -63,6 +63,8 @@ function TXDB(db, options) {
this.jobs = [];
this.locker = new bcoin.locker(this);
this.coinCache = new bcoin.lru(10000, 1);
// Try to optimize for up to 1m addresses.
// We use a regular bloom filter here
// because we never want members to
@ -394,6 +396,8 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
batch.del('c/' + key);
self.coinCache.remove(key);
return next();
}
@ -528,7 +532,12 @@ TXDB.prototype._add = function add(tx, map, callback, force) {
batch.put('C/' + id + '/' + key, DUMMY);
}
batch.put('c/' + key, coin.toRaw());
coin = coin.toRaw();
batch.put('c/' + key, coin);
self.coinCache.set(key, coin);
updated = true;
}
@ -759,6 +768,7 @@ TXDB.prototype._confirm = function _confirm(tx, map, callback, force) {
utils.forEachSerial(tx.outputs, function(output, next, i) {
var address = output.getHash('hex');
var key = hash + '/' + i;
// Only update coins if this output is ours.
if (!map.hasPaths(address))
@ -772,8 +782,11 @@ TXDB.prototype._confirm = function _confirm(tx, map, callback, force) {
return next();
coin.height = tx.height;
coin = coin.toRaw();
batch.put('c/' + hash + '/' + i, coin.toRaw());
batch.put('c/' + key, coin);
self.coinCache.set(key, coin);
next();
});
@ -864,7 +877,8 @@ TXDB.prototype.lazyRemove = function lazyRemove(tx, callback, force) {
TXDB.prototype._remove = function remove(tx, map, callback, force) {
var self = this;
var unlock, hash, batch, i, j, path, id, key, paths, address, input, output;
var unlock, hash, batch, i, j, path, id;
var key, paths, address, input, output, coin;
unlock = this._lock(remove, [tx, map, callback], force);
@ -926,9 +940,13 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
batch.put('C/' + id + '/' + key, DUMMY);
}
batch.put('c/' + key, input.coin.toRaw());
coin = input.coin.toRaw();
batch.put('c/' + key, coin);
batch.del('s/' + key);
batch.del('o/' + key);
self.coinCache.set(key, coin);
}
for (i = 0; i < tx.outputs.length; i++) {
@ -951,6 +969,8 @@ TXDB.prototype._remove = function remove(tx, map, callback, force) {
}
batch.del('c/' + key);
self.coinCache.remove(key);
}
batch.write(function(err) {
@ -1049,6 +1069,7 @@ TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
}
utils.forEachSerial(tx.outputs, function(output, next, i) {
var key = hash + '/' + i;
self.getCoin(hash, i, function(err, coin) {
if (err)
return next(err);
@ -1057,8 +1078,11 @@ TXDB.prototype._unconfirm = function unconfirm(tx, map, callback, force) {
return next();
coin.height = tx.height;
coin = coin.toRaw();
batch.put('c/' + hash + '/' + i, coin.toRaw());
batch.put('c/' + key, coin);
self.coinCache.set(key, coin);
next();
});
@ -1550,10 +1574,25 @@ TXDB.prototype.hasTX = function hasTX(hash, callback) {
*/
TXDB.prototype.getCoin = function getCoin(hash, index, callback) {
this.db.fetch('c/' + hash + '/' + index, function(data) {
var key = hash + '/' + index;
var coin = this.coinCache.get(key);
if (coin) {
try {
coin = bcoin.coin.fromRaw(coin);
} catch (e) {
return callback(e);
}
coin.hash = hash;
coin.index = index;
return callback(null, coin);
}
this.db.fetch('c/' + key, function(data) {
var coin = bcoin.coin.fromRaw(data);
coin.hash = hash;
coin.index = index;
self.coinCache.set(key, data);
return coin;
}, callback);
};
@ -1565,7 +1604,12 @@ TXDB.prototype.getCoin = function getCoin(hash, index, callback) {
*/
TXDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
return this.db.has('c/' + hash + '/' + index, callback);
var key = hash + '/' + index;
if (this.coinCache.has(key))
return callback(null, true);
return this.db.has('c/' + key, callback);
};
/**
@ -1578,28 +1622,59 @@ TXDB.prototype.getBalance = function getBalance(id, callback) {
var self = this;
var confirmed = 0;
var unconfirmed = 0;
var key, coin;
if (typeof id === 'function') {
callback = id;
id = null;
}
function parse(data) {
var height = data.readUInt32LE(4, true);
var value = utils.read64N(data, 8);
assert(data.length >= 16);
if (height === 0x7fffffff)
unconfirmed += value;
else
confirmed += value;
}
return this.getCoinHashes(id, function(err, hashes) {
if (err)
return callback(err);
utils.forEachSerial(hashes, function(hash, next) {
self.db.fetch('c/' + hash[0] + '/' + hash[1], function(data, key) {
var height = data.readUInt32LE(4, true);
var value = utils.read64N(data, 8);
key = hash[0] + '/' + hash[1];
coin = self.coinCache.get(key);
assert(data.length >= 16);
if (coin) {
try {
parse(coin);
} catch (e) {
return next(e);
}
return next();
}
if (height === 0x7fffffff)
unconfirmed += value;
else
confirmed += value;
}, next);
self.db.get('c/' + key, function(err, data) {
if (err)
return next(err);
if (!data)
return next();
try {
parse(data);
} catch (e) {
return callback(e);
}
self.coinCache.set(key, data);
next();
});
}, function(err) {
if (err)
return callback(err);

View File

@ -157,7 +157,6 @@ Wallet.prototype.open = function open(callback) {
/**
* Close the wallet, unregister with the database.
* @method
* @param {Function} callback
*/
@ -2421,7 +2420,7 @@ MasterKey.fromOptions = function fromOptions(options) {
* Decrypt the key and set a timeout to destroy decrypted data.
* @param {Buffer|String} passphrase - Zero this yourself.
* @param {Number} [timeout=60000] timeout in ms.
* @returns {HDPrivateKey}
* @param {Function} callback - Returns [Error, {@link HDPrivateKey}].
*/
MasterKey.prototype.unlock = function _unlock(passphrase, timeout, callback) {
@ -2515,6 +2514,7 @@ MasterKey.prototype.destroy = function destroy() {
/**
* Decrypt the key permanently.
* @param {Buffer|String} passphrase - Zero this yourself.
* @param {Function} callback
*/
MasterKey.prototype.decrypt = function decrypt(passphrase, callback) {
@ -2559,6 +2559,7 @@ MasterKey.prototype.decrypt = function decrypt(passphrase, callback) {
/**
* Encrypt the key permanently.
* @param {Buffer|String} passphrase - Zero this yourself.
* @param {Function} callback
*/
MasterKey.prototype.encrypt = function encrypt(passphrase, callback) {