db. mempool.
This commit is contained in:
parent
679d4194b1
commit
ced84ca25b
@ -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;
|
||||
|
||||
@ -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);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user