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

View File

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