txdb: refactor. clean up.

This commit is contained in:
Christopher Jeffrey 2016-08-12 02:04:59 -07:00
parent c582a0fad8
commit 63c9c81831
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 211 additions and 155 deletions

View File

@ -284,10 +284,10 @@ config.parseArg = function parseArg(argv) {
if (arg[0] === '-') {
// e.g. -abc
arg = arg.substring(1).split('');
arg = arg.substring(1);
for (i = 0; i < arg.length; i++) {
key = arg[i].trim();
key = arg[i];
alias = config.alias.arg[key];
if (alias)
key = alias;

View File

@ -64,6 +64,7 @@ function TXDB(db, options) {
this.busy = false;
this.jobs = [];
this.locker = new bcoin.locker(this);
this.current = null;
this.coinCache = new bcoin.lru(10000, 1);
@ -90,6 +91,77 @@ TXDB.prototype._lock = function _lock(func, args, force) {
return this.locker.lock(func, args, force);
};
/**
* Start a batch.
* @returns {Batch}
*/
TXDB.prototype.start = function start() {
assert(!this.current);
this.current = this.db.batch();
return this.current;
};
/**
* Put key and value to current batch.
* @param {String} key
* @param {Buffer} value
*/
TXDB.prototype.put = function put(key, value) {
assert(this.current);
this.current.put(key, value);
};
/**
* Delete key from current batch.
* @param {String} key
*/
TXDB.prototype.del = function del(key) {
assert(this.current);
this.current.del(key);
};
/**
* Get current batch.
* @returns {Batch}
*/
TXDB.prototype.batch = function batch() {
assert(this.current);
return this.current;
};
/**
* Drop current batch.
* @returns {Batch}
*/
TXDB.prototype.drop = function drop() {
assert(this.current);
this.current.clear();
this.current = null;
};
/**
* Commit current batch.
* @param {Function} callback
*/
TXDB.prototype.commit = function commit(callback) {
var self = this;
assert(this.current);
this.current.write(function(err) {
if (err) {
self.current = null;
return callback(err);
}
self.current = null;
return callback();
});
};
/**
* Load the bloom filter into memory.
* @private
@ -146,7 +218,7 @@ TXDB.prototype.getInfo = function getInfo(tx, callback) {
if (!this.testFilter(addresses))
return callback();
this.mapAddresses(addresses, function(err, table) {
this.getTable(addresses, function(err, table) {
if (err)
return callback(err);
@ -160,12 +232,12 @@ TXDB.prototype.getInfo = function getInfo(tx, callback) {
};
/**
* Map address hashes to a wallet ID.
* Map address hashes to paths.
* @param {Hash[]} address - Address hashes.
* @param {Function} callback - Returns [Error, {@link AddressTable}].
*/
TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
TXDB.prototype.getTable = function getTable(address, callback) {
var self = this;
var table = {};
var count = 0;
@ -217,20 +289,21 @@ TXDB.prototype.mapAddresses = function mapAddresses(address, callback) {
TXDB.prototype._addOrphan = function _addOrphan(key, outpoint, callback) {
var self = this;
var batch = this.batch();
var p;
this.db.get('o/' + key, function(err, buf) {
this.db.get('o/' + key, function(err, data) {
if (err)
return callback(err);
p = new BufferWriter();
if (buf)
p.writeBytes(buf);
if (data)
p.writeBytes(data);
p.writeBytes(outpoint);
self.batch().put('o/' + key, p.render());
batch.put('o/' + key, p.render());
return callback();
});
@ -245,10 +318,10 @@ TXDB.prototype._addOrphan = function _addOrphan(key, outpoint, callback) {
TXDB.prototype._getOrphans = function _getOrphans(key, callback) {
var self = this;
var out = [];
var items = [];
this.db.fetch('o/' + key, function(buf) {
var p = new BufferReader(buf);
this.db.fetch('o/' + key, function(data) {
var p = new BufferReader(data);
var orphans = [];
while (p.left())
@ -267,7 +340,7 @@ TXDB.prototype._getOrphans = function _getOrphans(key, callback) {
if (err)
return next(err);
out.push([orphan, tx]);
items.push([orphan, tx]);
next();
});
@ -275,7 +348,7 @@ TXDB.prototype._getOrphans = function _getOrphans(key, callback) {
if (err)
return callback(err);
return callback(null, out);
return callback(null, items);
});
});
};
@ -296,7 +369,7 @@ TXDB.prototype.writeGenesis = function writeGenesis(callback) {
callback = utils.wrap(callback, unlock);
self.db.has('R', function(err, result) {
this.db.has('R', function(err, result) {
if (err)
return callback(err);
@ -304,6 +377,7 @@ TXDB.prototype.writeGenesis = function writeGenesis(callback) {
return callback();
hash = new Buffer(self.network.genesis.hash, 'hex');
self.db.put('R', hash, callback);
});
};
@ -399,46 +473,6 @@ TXDB.prototype.removeBlock = function removeBlock(block, callback, force) {
});
};
TXDB.prototype.start = function start() {
assert(!this.current);
this.current = this.db.batch();
return this.current;
};
TXDB.prototype.put = function put(key, value) {
assert(this.current);
this.current.put(key, value);
};
TXDB.prototype.del = function del(key, value) {
assert(this.current);
this.current.del(key);
};
TXDB.prototype.batch = function batch() {
assert(this.current);
return this.current;
};
TXDB.prototype.drop = function drop() {
assert(this.current);
this.current.clear();
this.current = null;
};
TXDB.prototype.commit = function commit(callback) {
var self = this;
assert(this.current);
this.current.write(function(err) {
if (err) {
self.current = null;
return callback(err);
}
self.current = null;
return callback();
});
};
/**
* Add a transaction to the database, map addresses
* to wallet IDs, potentially store orphans, resolve
@ -456,7 +490,7 @@ TXDB.prototype.add = function add(tx, callback, force) {
callback = utils.wrap(callback, unlock);
return this.getInfo(tx, function(err, info) {
this.getInfo(tx, function(err, info) {
if (err)
return callback(err);
@ -469,12 +503,22 @@ TXDB.prototype.add = function add(tx, callback, force) {
self.logger.debug(info.paths);
return self._add(tx, info, callback);
self._add(tx, info, callback);
});
};
/**
* Retrieve coins for own inputs, remove
* double spenders, and verify inputs.
* @private
* @param {TX} tx
* @param {PathInfo} info
* @param {Function} callback - Returns [Error].
*/
TXDB.prototype._verify = function _verify(tx, info, callback) {
var self = this;
utils.forEachSerial(tx.inputs, function(input, next, i) {
var prevout = input.prevout;
var address, paths;
@ -555,12 +599,20 @@ TXDB.prototype._verify = function _verify(tx, info, callback) {
});
};
TXDB.prototype._resolveOrphans = function _resolveOrphans(tx, i, callback) {
/**
* Attempt to resolve orphans for an output.
* @private
* @param {TX} tx
* @param {Number} index
* @param {Function} callback
*/
TXDB.prototype._resolveOrphans = function _resolveOrphans(tx, index, callback) {
var self = this;
var batch = this.batch();
var hash = tx.hash('hex');
var output = tx.outputs[i];
var key = hash + '/' + i;
var output = tx.outputs[index];
var key = hash + '/' + index;
var coin;
this._getOrphans(key, function(err, orphans) {
@ -572,12 +624,12 @@ TXDB.prototype._resolveOrphans = function _resolveOrphans(tx, i, callback) {
batch.del('o/' + key);
coin = bcoin.coin.fromTX(tx, i);
coin = bcoin.coin.fromTX(tx, index);
// Add input to orphan
utils.forEachSerial(orphans, function(pair, next) {
var input = pair[0];
var orphan = pair[1];
utils.forEachSerial(orphans, function(item, next) {
var input = item[0];
var orphan = item[1];
// Probably removed by some other means.
if (!orphan)
@ -586,7 +638,7 @@ TXDB.prototype._resolveOrphans = function _resolveOrphans(tx, i, callback) {
orphan.inputs[input.index].coin = coin;
assert(orphan.inputs[input.index].prevout.hash === hash);
assert(orphan.inputs[input.index].prevout.index === i);
assert(orphan.inputs[input.index].prevout.index === index);
// Verify that input script is correct, if not - add
// output to unspent and remove orphan from storage
@ -605,10 +657,19 @@ TXDB.prototype._resolveOrphans = function _resolveOrphans(tx, i, callback) {
});
};
/**
* Add transaction, runs _confirm (separate batch) and
* verify (separate batch for double spenders).
* @private
* @param {TX} tx
* @param {PathInfo} info
* @param {Function} callback
*/
TXDB.prototype._add = function add(tx, info, callback) {
var self = this;
var updated = false;
var batch, hash, i, j, unlock, path, paths, id;
var batch, hash, i, j, path, paths, id;
if (tx.mutable)
tx = tx.toTX();
@ -692,8 +753,10 @@ TXDB.prototype._add = function add(tx, info, callback) {
next();
}, function(err) {
if (err)
if (err) {
self.drop();
return callback(err);
}
// Add unspent outputs or resolve orphans
utils.forEachSerial(tx.outputs, function(output, next, i) {
@ -712,7 +775,7 @@ TXDB.prototype._add = function add(tx, info, callback) {
self._resolveOrphans(tx, i, function(err, orphans) {
if (err)
return callback(err);
return next(err);
if (orphans)
return next();
@ -736,8 +799,10 @@ TXDB.prototype._add = function add(tx, info, callback) {
next();
});
}, function(err) {
if (err)
if (err) {
self.drop();
return callback(err);
}
self.commit(function(err) {
if (err)
@ -834,20 +899,19 @@ TXDB.prototype._removeRecursive = function _removeRecursive(tx, callback) {
if (err)
return next(err);
if (!spent)
return next();
// Remove all of the spender's spenders first.
if (spent) {
return self.getTX(spent.hash, function(err, tx) {
if (err)
return callback(err);
self.getTX(spent.hash, function(err, tx) {
if (err)
return next(err);
if (!tx)
return callback(new Error('Could not find spender.'));
if (!tx)
return next(new Error('Could not find spender.'));
return self._removeRecursive(tx, next);
});
}
next();
self._removeRecursive(tx, next);
});
});
}, function(err) {
if (err)
@ -856,14 +920,16 @@ TXDB.prototype._removeRecursive = function _removeRecursive(tx, callback) {
self.start();
// Remove the spender.
self._lazyRemove(tx, function(err, res1, res2) {
if (err)
self._lazyRemove(tx, function(err, result, info) {
if (err) {
self.drop();
return callback(err);
}
self.commit(function(err) {
if (err)
return callback(err);
callback(null, res1, res2);
return callback(null, result, info);
});
});
});
@ -880,7 +946,8 @@ TXDB.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) {
var self = this;
utils.everySerial(tx.inputs, function(input, next) {
self.isSpent(input.prevout.hash, input.prevout.index, function(err, spent) {
var prevout = input.prevout;
self.isSpent(prevout.hash, prevout.index, function(err, spent) {
if (err)
return next(err);
return next(null, !spent);
@ -901,7 +968,7 @@ TXDB.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) {
TXDB.prototype.isSpent = function isSpent(hash, index, callback) {
var key = 's/' + hash + '/' + index;
return this.db.fetch(key, function(data) {
this.db.fetch(key, function(data) {
return bcoin.outpoint.fromRaw(data);
}, callback);
};
@ -918,9 +985,8 @@ TXDB.prototype.isSpent = function isSpent(hash, index, callback) {
TXDB.prototype._confirm = function _confirm(tx, info, callback) {
var self = this;
var hash, batch, unlock, i, id;
hash = tx.hash('hex');
var hash = tx.hash('hex');
var batch, i, id;
this.getTX(hash, function(err, existing) {
if (err)
@ -982,8 +1048,10 @@ TXDB.prototype._confirm = function _confirm(tx, info, callback) {
next();
});
}, function(err) {
if (err)
if (err) {
self.drop();
return callback(err);
}
self.emit('confirmed', tx, info);
self.emit('tx', tx, info);
@ -1005,40 +1073,18 @@ TXDB.prototype._confirm = function _confirm(tx, info, callback) {
TXDB.prototype.remove = function remove(hash, callback, force) {
var self = this;
var unlock = this._lock(remove, [hash, callback], force);
if (hash.hash)
hash = hash.hash('hex');
if (!unlock)
return;
this.getTX(hash, function(err, tx) {
callback = utils.wrap(callback, unlock);
this._removeRecursive(hash, function(err, result, info) {
if (err)
return callback(err);
if (!tx)
return callback(null, true);
assert(tx.hash('hex') === hash);
return self.getInfo(tx, function(err, info) {
if (err)
return callback(err);
if (!info)
return callback(null, false);
self.start();
return self._remove(tx, info, function(err, res1, res2) {
if (err) {
self.drop();
return callback(err);
}
self.commit(function(err) {
if (err)
return callback(err);
callback(null, res1, res2);
});
});
});
return callback(null, !!result, info);
});
};
@ -1059,7 +1105,7 @@ TXDB.prototype._lazyRemove = function lazyRemove(tx, callback) {
if (!info)
return callback(null, false);
return self._remove(tx, info, callback);
self._remove(tx, info, callback);
});
};
@ -1073,12 +1119,9 @@ TXDB.prototype._lazyRemove = function lazyRemove(tx, callback) {
TXDB.prototype._remove = function remove(tx, info, callback) {
var self = this;
var unlock, hash, batch, i, j, path, id;
var key, paths, address, input, output, coin;
hash = tx.hash('hex');
batch = this.batch();
var hash = tx.hash('hex');
var batch = this.batch();
var i, j, path, id, key, paths, address, input, output, coin;
batch.del('t/' + hash);
@ -1190,9 +1233,7 @@ TXDB.prototype.unconfirm = function unconfirm(hash, callback, force) {
if (!tx)
return callback(null, true);
assert(tx.hash('hex') === hash);
return self.getInfo(tx, function(err, info) {
self.getInfo(tx, function(err, info) {
if (err)
return callback(err);
@ -1201,7 +1242,7 @@ TXDB.prototype.unconfirm = function unconfirm(hash, callback, force) {
self.start();
return self._unconfirm(tx, info, function(err, res1, res2) {
self._unconfirm(tx, info, function(err, res1, res2) {
if (err) {
self.drop();
return callback(err);
@ -1225,11 +1266,10 @@ TXDB.prototype.unconfirm = function unconfirm(hash, callback, force) {
TXDB.prototype._unconfirm = function unconfirm(tx, info, callback, force) {
var self = this;
var batch, unlock, hash, height, i, id;
batch = this.batch();
hash = tx.hash('hex');
height = tx.height;
var batch = this.batch();
var hash = tx.hash('hex');
var height = tx.height;
var i, id;
if (height !== -1)
return callback(null, false, info);
@ -1453,7 +1493,7 @@ TXDB.prototype.getRange = function getLast(id, options, callback) {
id = null;
}
return this.getRangeHashes(id, options, function(err, hashes) {
this.getRangeHashes(id, options, function(err, hashes) {
if (err)
return callback(err);
@ -1492,7 +1532,7 @@ TXDB.prototype.getLast = function getLast(id, limit, callback) {
id = null;
}
return this.getRange(id, {
this.getRange(id, {
start: 0,
end: 0xffffffff,
reverse: true,
@ -1515,7 +1555,7 @@ TXDB.prototype.getHistory = function getHistory(id, callback) {
id = null;
}
return this.getHistoryHashes(id, function(err, hashes) {
this.getHistoryHashes(id, function(err, hashes) {
if (err)
return callback(err);
@ -1554,7 +1594,7 @@ TXDB.prototype.getLastTime = function getLastTime(id, callback) {
id = null;
}
return this.getHistory(id, function(err, txs) {
this.getHistory(id, function(err, txs) {
if (err)
return callback(err);
@ -1590,7 +1630,7 @@ TXDB.prototype.getUnconfirmed = function getUnconfirmed(id, callback) {
id = null;
}
return this.getUnconfirmedHashes(id, function(err, hashes) {
this.getUnconfirmedHashes(id, function(err, hashes) {
if (err)
return callback(err);
@ -1630,7 +1670,7 @@ TXDB.prototype.getCoins = function getCoins(id, callback) {
id = null;
}
return this.getCoinHashes(id, function(err, hashes) {
this.getCoinHashes(id, function(err, hashes) {
if (err)
return callback(err);
@ -1707,10 +1747,12 @@ TXDB.prototype.fillCoins = function fillCoins(tx, callback) {
}
utils.forEachSerial(tx.inputs, function(input, next) {
var prevout = input.prevout;
if (input.coin)
return next();
self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
self.getCoin(prevout.hash, prevout.index, function(err, coin) {
if (err)
return callback(err);
@ -1771,7 +1813,7 @@ TXDB.prototype.toDetails = function toDetails(id, tx, callback) {
if (Array.isArray(tx)) {
out = [];
utils.forEachSerial(tx, function(tx, next) {
return utils.forEachSerial(tx, function(tx, next) {
self.toDetails(tx, function(err, details) {
if (err)
return next(err);
@ -1812,7 +1854,7 @@ TXDB.prototype.toDetails = function toDetails(id, tx, callback) {
*/
TXDB.prototype.hasTX = function hasTX(hash, callback) {
return this.db.has('t/' + hash, callback);
this.db.has('t/' + hash, callback);
};
/**
@ -1859,7 +1901,7 @@ TXDB.prototype.hasCoin = function hasCoin(hash, index, callback) {
if (this.coinCache.has(key))
return callback(null, true);
return this.db.has('c/' + key, callback);
this.db.has('c/' + key, callback);
};
/**
@ -1891,7 +1933,7 @@ TXDB.prototype.getBalance = function getBalance(id, callback) {
confirmed += value;
}
return this.getCoinHashes(id, function(err, hashes) {
this.getCoinHashes(id, function(err, hashes) {
if (err)
return callback(err);
@ -1965,23 +2007,37 @@ TXDB.prototype.zap = function zap(id, age, callback, force) {
if (!utils.isNumber(age))
return callback(new Error('Age must be a number.'));
return this.getRange(id, {
this.getRange(id, {
start: 0,
end: bcoin.now() - age
}, function(err, txs) {
if (err)
return callback(err);
self.fillHistory(txs, function(err) {
if (err)
return callback(err);
utils.forEachSerial(txs, function(tx, next) {
if (tx.ts !== 0)
return next();
self.remove(tx.hash('hex'), next, true);
}, callback);
});
};
utils.forEachSerial(txs, function(tx, next) {
if (tx.ts !== 0)
return next();
self.remove(tx.hash('hex'), next, true);
}, callback);
});
/**
* Abandon transaction.
* @param {Hash} hash
* @param {Function} callback
*/
TXDB.prototype.abandon = function abandon(hash, callback, force) {
var self = this;
this.db.has('p/' + hash, function(err, result) {
if (err)
return callback(err);
if (!result)
return callback(new Error('TX not found.'));
self.remove(hash, callback, force);
});
};