db. mempool.

This commit is contained in:
Christopher Jeffrey 2016-05-13 02:53:55 -07:00
parent 679d4194b1
commit ced84ca25b
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
2 changed files with 367 additions and 100 deletions

View File

@ -173,4 +173,156 @@ LowlevelUp.prototype.approximateSize = function approximateSize(start, end, call
return this.binding.approximateSize(start, end, callback);
};
/**
* Test whether a key exists.
* @param {String} key
* @param {Function} callback - Returns [Error, Boolean].
*/
LowlevelUp.prototype.has = function has(key, callback) {
return this.get(key, function(err, value) {
if (err && err.type !== 'NotFoundError')
return callback(err);
return callback(null, value != null);
});
};
/**
* Get and deserialize a record with a callback.
* @param {String} key
* @param {Function} parse
* @param {Function} callback - Returns [Error, Object].
*/
LowlevelUp.prototype.fetch = function fetch(key, parse, callback) {
return this.get(key, function(err, value) {
if (err && err.type !== 'NotFoundError')
return callback(err);
if (!value)
return callback();
try {
value = parse(value, key);
} catch (e) {
return callback(e);
}
return callback(null, value);
});
};
/**
* Collect all keys from iterator options.
* @param {Object} options - Iterator options.
* @param {Function} callback - Returns [Error, Array].
*/
LowlevelUp.prototype.iterate = function iterate(options, callback) {
var items = [];
var iter;
iter = this.iterator({
gte: options.gte,
lte: options.lte,
keys: true,
values: options.values,
fillCache: false,
keyAsBuffer: false,
limit: options.limit,
reverse: options.reverse
});
(function next() {
iter.next(function(err, key, value) {
if (err) {
return iter.end(function() {
callback(err);
});
}
if (key === undefined) {
return iter.end(function(err) {
if (err)
return callback(err);
return callback(null, items);
});
}
if (options.values) {
if (options.parse) {
try {
value = options.parse(value);
} catch (e) {
return iter.end(function() {
return callback(e);
});
}
}
if (value)
items.push(value);
return next();
}
if (options.transform)
key = options.transform(key);
if (key)
items.push(key);
next();
});
})();
};
/**
* Collect all keys from iterator options.
* Proxy the keys to further lookups.
* @param {Object} options - Iterator options.
* @param {Function} callback - Returns [Error, Array].
*/
LowlevelUp.prototype.lookup = function lookup(options, callback) {
var self = this;
var items = [];
options.values = false;
return this.iterate(options, function(err, keys) {
if (err)
return callback(err);
utils.forEachSerial(keys, function(key, next) {
self.get(key, function(err, value) {
if (err && err.type !== 'NotFoundError')
return callback(err);
if (!value)
return next();
if (!options.parse) {
items.push(value);
return next();
}
try {
value = options.parse(value, key);
} catch (e) {
return callback(e);
}
if (value)
items.push(value);
next();
});
}, function(err) {
if (err)
return callback(err);
return callback(null, items);
});
});
};
module.exports = LowlevelUp;

View File

@ -72,7 +72,6 @@ function Mempool(options) {
this.writeLock = new bcoin.locker(this);
this.db = null;
this.tx = null;
this.size = 0;
this.waiting = {};
this.orphans = {};
@ -140,9 +139,6 @@ Mempool.prototype._init = function _init() {
self.db = bcoin.ldb(options);
// Use the txdb object for its get methods.
self.tx = new TXDB(self.db);
self.db.open(function(err) {
if (err) {
unlock();
@ -167,59 +163,6 @@ Mempool.prototype._init = function _init() {
});
};
function TXDB(db, options) {
bcoin.txdb.call(this, db, options);
}
utils.inherits(TXDB, bcoin.txdb);
TXDB.prototype.getTX = function getTX(hash, callback) {
return this.db.get('t/' + hash, function(err, entry) {
if (err && err.type !== 'NotFoundError')
return callback(err);
if (!entry)
return callback();
try {
entry = MempoolEntry.fromRaw(entry);
} catch (e) {
return callback(e);
}
return callback(null, entry.tx, entry);
});
};
TXDB.prototype.getRangeEntries = function getRangeEntries(options, callback) {
var self = this;
var entries = [];
return this.getRangeHashes(options, function(err, hashes) {
if (err)
return callback(err);
utils.forEachSerial(hashes, function(hash, next) {
self.getTX(hash, function(err, tx, entry) {
if (err)
return callback(err);
if (!entry)
return next();
entries.push(entry);
next();
});
}, function(err) {
if (err)
return callback(err);
return callback(null, entries);
});
});
};
/**
* Tally up total memory usage from database.
* @param {Function} callback - Returns [Error, Number].
@ -409,7 +352,7 @@ Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) {
if (this.getSize() <= this.maxSize)
return callback(null, true);
this.tx.getRangeEntries({
this.getRange({
start: 0,
end: utils.now() - constants.mempool.MEMPOOL_EXPIRY
}, function(err, entries) {
@ -418,27 +361,40 @@ Mempool.prototype.limitMempoolSize = function limitMempoolSize(callback) {
utils.forEachSerial(function(entry, next) {
if (self.getSize() <= self.maxSize)
return next();
return callback(null, true);
self.removeUnchecked(entry, true, function(err) {
if (err)
return next(err);
rate = entry.fees.muln(1000).divn(entry.size).toNumber();
rate += self.minReasonableFee;
if (rate > self.minFeeRate) {
self.minFeeRate = rate;
self.blockSinceBump = false;
}
next();
}, true);
self.removeUnchecked(entry, true, next, true);
}, function(err) {
if (err)
return callback(err);
return callback(null, self.getSize() <= self.maxSize);
if (self.getSize() <= self.maxSize)
return callback(null, true);
self.getSnapshot(function(err, hashes) {
if (err)
return callback(err);
utils.forEachSerial(hashes, function(hash, next) {
if (self.getSize() <= self.maxSize)
return callback(null, true);
self.getEntry(hash, function(err, entry) {
if (err)
return next(err);
if (!entry)
return next();
self.removeUnchecked(entry, true, next, true);
});
}, function(err) {
if (err)
return callback(err);
return callback(null, self.getSize() <= self.maxSize);
});
});
});
});
};
@ -475,7 +431,15 @@ Mempool.prototype.limitOrphans = function limitOrphans(callback) {
*/
Mempool.prototype.getTX = function getTX(hash, callback) {
return this.tx.getTX(hash, callback);
return this.getEntry(hash, function(err, entry) {
if (err)
return callback(err);
if (!entry)
return callback();
return callback(null, entry.tx);
});
};
/**
@ -486,15 +450,9 @@ Mempool.prototype.getTX = function getTX(hash, callback) {
*/
Mempool.prototype.getEntry = function getEntry(hash, callback) {
return this.tx.getTX(hash, function(err, tx, entry) {
if (err)
return callback(err);
if (!entry)
return callback();
return callback(null, entry);
});
return this.db.fetch('t/' + hash, function(entry) {
return MempoolEntry.fromRaw(entry);
}, callback);
};
/**
@ -505,7 +463,12 @@ Mempool.prototype.getEntry = function getEntry(hash, callback) {
*/
Mempool.prototype.getCoin = function getCoin(hash, index, callback) {
return this.tx.getCoin(hash, index, callback);
return this.db.fetch('c/' + hash + '/' + index, function(coin) {
coin = bcoin.coin.fromRaw(coin);
coin.hash = hash;
coin.index = index;
return coin;
}, callback);
};
/**
@ -519,27 +482,52 @@ Mempool.prototype.getCoin = function getCoin(hash, index, callback) {
*/
Mempool.prototype.isSpent = function isSpent(hash, index, callback) {
return this.tx.isSpent(hash, index, callback);
return this.db.fetch('s/' + hash, function(spender) {
return spender.toString('hex');
}, callback);
};
/**
* Find all coins pertaining to a certain address.
* @param {Base58Address[]|Base58Address} addresses
* @param {Base58Address} address
* @param {Function} callback - Returns [Error, {@link Coin}[]].
*/
Mempool.prototype.getCoinsByAddress = function getCoinsByAddress(addresses, callback) {
return this.tx.getCoinsByAddress(addresses, callback);
Mempool.prototype.getCoinsByAddress = function getCoinsByAddress(address, callback) {
return this.db.lookup({
gte: 'C/' + address,
lte: 'C/' + address + '~',
transform: function(key) {
key = key.split('/');
return 'c/' + key[2] + '/' + key[3];
},
parse: function(data, key) {
var coin = bcoin.coin.fromRaw(data);
var hash = key.split('/');
coin.hash = hash[1];
coin.index = +hash[2];
return coin;
}
}, callback);
};
/**
* Find all transactions pertaining to a certain address.
* @param {Base58Address[]|Base58Address} addresses
* @param {Base58Address} address
* @param {Function} callback - Returns [Error, {@link TX}[]].
*/
Mempool.prototype.getTXByAddress = function getTXByAddress(addresses, callback) {
return this.tx.getTXByAddress(addresses, callback);
Mempool.prototype.getTXByAddress = function getTXByAddress(address, callback) {
return this.db.lookup({
gte: 'T/' + address,
lte: 'T/' + address + '~',
transform: function(key) {
return 't/' + key.split('/')[2];
},
parse: function(data, key) {
return bcoin.tx.fromRaw(data);
}
}, callback);
};
/**
@ -552,7 +540,37 @@ Mempool.prototype.getTXByAddress = function getTXByAddress(addresses, callback)
*/
Mempool.prototype.fillHistory = function fillHistory(tx, callback) {
return this.tx.fillHistory(tx, callback);
var self = this;
if (Array.isArray(tx)) {
return utils.forEachSerial(tx, function(tx, next) {
self.fillHistory(tx, next);
}, callback);
}
callback = utils.asyncify(callback);
if (tx.isCoinbase())
return callback(null, tx);
utils.forEach(tx.inputs, function(input, next) {
if (input.coin)
return next();
self.getTX(input.prevout.hash, function(err, tx) {
if (err)
return next(err);
if (tx)
input.coin = bcoin.coin(tx, input.prevout.index);
next();
});
}, function(err) {
if (err)
return callback(err);
return callback(null, tx);
});
};
/**
@ -563,7 +581,37 @@ Mempool.prototype.fillHistory = function fillHistory(tx, callback) {
*/
Mempool.prototype.fillCoins = function fillCoins(tx, callback) {
return this.tx.fillCoins(tx, callback);
var self = this;
if (Array.isArray(tx)) {
return utils.forEachSerial(tx, function(tx, next) {
self.fillCoins(tx, next);
}, callback);
}
callback = utils.asyncify(callback);
if (tx.isCoinbase())
return callback(null, tx);
utils.forEach(tx.inputs, function(input, next) {
if (input.coin)
return next();
self.getCoin(input.prevout.hash, input.prevout.index, function(err, coin) {
if (err)
return callback(err);
if (coin)
input.coin = coin;
next();
});
}, function(err) {
if (err)
return callback(err);
return callback(null, tx);
});
};
/**
@ -573,7 +621,7 @@ Mempool.prototype.fillCoins = function fillCoins(tx, callback) {
*/
Mempool.prototype.hasTX = function hasTX(hash, callback) {
return this.tx.hasTX(hash, callback);
return this.db.has('t/' + hash, callback);
};
/**
@ -583,7 +631,18 @@ Mempool.prototype.hasTX = function hasTX(hash, callback) {
*/
Mempool.prototype.getRange = function getRange(options, callback) {
return this.tx.getRange(options, callback);
return this.db.lookup({
gte: 'm/' + pad32(options.start) + '/',
lte: 'm/' + pad32(options.end) + '/~',
transform: function(key) {
return 't/' + key.split('/')[2];
},
parse: function(data, key) {
return MempoolEntry.fromRaw(data);
},
limit: options.limit,
reverse: options.reverse
}, callback);
};
/**
@ -834,6 +893,15 @@ Mempool.prototype.removeUnchecked = function removeUnchecked(entry, limit, callb
self.size -= self.memUsage(entry.tx);
self.total--;
if (limit) {
rate = entry.fees.muln(1000).divn(entry.size).toNumber();
rate += self.minReasonableFee;
if (rate > self.minFeeRate) {
self.minFeeRate = rate;
self.blockSinceBump = false;
}
}
self.emit('remove tx', entry.tx);
return callback();
@ -1113,7 +1181,29 @@ Mempool.prototype.storeOrphan = function storeOrphan(tx, callback, force) {
*/
Mempool.prototype.getBalance = function getBalance(callback) {
return this.tx.getBalance(callback);
var total = new bn(0);
var i;
return this.db.iterate({
gte: 'c',
lte: 'c~',
values: true,
parse: function(data, key) {
return bcoin.coin.fromRaw(data);
}
}, function(err, coins) {
if (err)
return callback(err);
for (i = 0; i < coins.length; i++)
total.iadd(coins[i].value);
return callback(null, {
confirmed: new bn(0),
unconfirmed: total,
total: total
});
});
};
/**
@ -1122,7 +1212,14 @@ Mempool.prototype.getBalance = function getBalance(callback) {
*/
Mempool.prototype.getHistory = function getHistory(callback) {
return this.tx.getHistory(callback);
return this.db.iterate({
gte: 't',
lte: 't~',
values: true,
parse: function(data, key) {
return bcoin.tx.fromRaw(data);
}
}, callback);
};
/**
@ -1359,7 +1456,13 @@ Mempool.prototype.fillAllCoins = function fillAllCoins(tx, callback) {
*/
Mempool.prototype.getSnapshot = function getSnapshot(callback) {
return this.tx.getHistoryHashes(callback);
return this.db.iterate({
gte: 't',
lte: 't~',
transform: function(key) {
return key.split('/')[1];
}
}, callback);
};
/**
@ -1384,7 +1487,19 @@ Mempool.prototype.checkLocks = function checkLocks(tx, flags, callback) {
*/
Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx, callback) {
return this.tx.isDoubleSpend(tx, callback);
var self = this;
utils.everySerial(tx.inputs, function(input, next) {
self.isSpent(input.prevout.hash, input.prevout.index, function(err, spent) {
if (err)
return next(err);
return next(null, !spent);
});
}, function(err, result) {
if (err)
return callback(err);
return callback(null, !result);
});
};
/**