mempool: refactor fee estimation.

This commit is contained in:
Christopher Jeffrey 2017-03-01 09:47:56 -08:00
parent 161fe6e2a1
commit a11344db0e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 51 additions and 54 deletions

View File

@ -15,8 +15,8 @@ var policy = require('../protocol/policy');
var BufferReader = require('../utils/reader');
var StaticWriter = require('../utils/staticwriter');
var encoding = require('../utils/encoding');
var Map = require('../utils/map');
var Logger = require('../node/logger');
var Network = require('../protocol/network');
var global = util.global;
var Float64Array = global.Float64Array || Array;
var Int32Array = global.Int32Array || Array;
@ -52,7 +52,7 @@ function ConfirmStats(type, logger) {
if (!(this instanceof ConfirmStats))
return new ConfirmStats(type, logger);
this.logger = logger || Logger.global;
this.logger = Logger.global;
this.type = type;
this.decay = 0;
@ -70,6 +70,11 @@ function ConfirmStats(type, logger) {
this.txAvg = new Float64Array(0);
this.curBlockVal = new Float64Array(0);
this.avg = new Float64Array(0);
if (logger) {
assert(typeof logger === 'object');
this.logger = logger;
}
}
/**
@ -409,36 +414,41 @@ ConfirmStats.fromRaw = function fromRaw(data, type, logger) {
* Estimator for fees and priority.
* @alias module:mempool.PolicyEstimator
* @constructor
* @param {Network|NetworkType} network
* @param {Logger?} logger
*/
function PolicyEstimator(network, logger) {
function PolicyEstimator(logger) {
if (!(this instanceof PolicyEstimator))
return new PolicyEstimator(network, logger);
return new PolicyEstimator(logger);
this.network = Network.get(network);
this.logger = logger || Logger.global;
this.logger = Logger.global;
this.minTrackedFee = policy.MIN_RELAY >= MIN_FEERATE
? policy.MIN_RELAY
: MIN_FEERATE;
this.minTrackedFee = MIN_FEERATE;
this.minTrackedPri = MIN_PRIORITY;
this.minTrackedPri = policy.FREE_THRESHOLD >= MIN_PRIORITY
? policy.FREE_THRESHOLD
: MIN_PRIORITY;
this.feeStats = new ConfirmStats('FeeRate', this.logger);
this.priStats = new ConfirmStats('Priority', this.logger);
this.feeStats = new ConfirmStats('FeeRate');
this.priStats = new ConfirmStats('Priority');
this.feeUnlikely = 0;
this.feeLikely = INF_FEERATE;
this.priUnlikely = 0;
this.priLikely = INF_PRIORITY;
this.map = {};
this.mapSize = 0;
this.map = new Map();
this.bestHeight = 0;
if (policy.MIN_RELAY >= MIN_FEERATE)
this.minTrackedFee = policy.MIN_RELAY;
if (policy.FREE_THRESHOLD >= MIN_PRIORITY)
this.minTrackedPri = policy.FREE_THRESHOLD;
if (logger) {
assert(typeof logger === 'object');
this.logger = logger;
this.feeStats.logger = logger;
this.priStats.logger = logger;
}
}
/**
@ -481,8 +491,7 @@ PolicyEstimator.prototype.reset = function reset() {
this.priUnlikely = 0;
this.priLikely = INF_PRIORITY;
this.map = {};
this.mapSize = 0;
this.map.reset();
this.bestHeight = 0;
this.init();
@ -494,7 +503,7 @@ PolicyEstimator.prototype.reset = function reset() {
*/
PolicyEstimator.prototype.removeTX = function removeTX(hash) {
var item = this.map[hash];
var item = this.map.get(hash);
if (!item) {
this.logger.spam(
@ -505,8 +514,7 @@ PolicyEstimator.prototype.removeTX = function removeTX(hash) {
this.feeStats.removeTX(item.blockHeight, this.bestHeight, item.bucketIndex);
delete this.map[hash];
this.mapSize--;
this.map.remove(hash);
};
/**
@ -550,7 +558,7 @@ PolicyEstimator.prototype.processTX = function processTX(entry, current) {
var hash = entry.tx.hash('hex');
var fee, rate, priority, item;
if (this.map[hash]) {
if (this.map.has(hash)) {
this.logger.debug(
'estimatefee: Mempool tx %s already tracked.',
entry.tx.txid());
@ -590,8 +598,7 @@ PolicyEstimator.prototype.processTX = function processTX(entry, current) {
return;
}
this.map[hash] = item;
this.mapSize++;
this.map.set(hash, item);
};
/**
@ -691,7 +698,7 @@ PolicyEstimator.prototype.processBlock = function processBlock(height, entries,
this.logger.debug('estimatefee: Done updating estimates'
+ ' for %d confirmed entries. New mempool map size %d.',
entries.length, this.mapSize);
entries.length, this.map.size);
this.logger.debug('estimatefee: Rate: %d.', this.estimateFee());
};
@ -736,12 +743,6 @@ PolicyEstimator.prototype.estimateFee = function estimateFee(target, smart) {
target -= 1;
if (rate < this.network.feeRate)
return this.network.feeRate;
if (rate > this.network.maxFeeRate)
return this.network.maxFeeRate;
if (rate < 0)
return 0;
@ -775,9 +776,6 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s
return Math.floor(priority);
}
// TODO: Add check for mempool limiting txs.
// Should return INF_PRIORITY.
priority = -1;
while (priority < 0 && target <= this.priStats.maxConfirms) {
priority = this.priStats.estimateMedian(
@ -800,7 +798,7 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s
PolicyEstimator.prototype.getSize = function getSize() {
var size = 0;
size += 8;
size += 4;
size += encoding.sizeVarlen(this.feeStats.getSize());
size += encoding.sizeVarlen(this.priStats.getSize());
return size;
@ -815,7 +813,6 @@ PolicyEstimator.prototype.toRaw = function toRaw() {
var size = this.getSize();
var bw = new StaticWriter(size);
bw.writeU32(this.network.magic);
bw.writeU32(this.bestHeight);
bw.writeVarBytes(this.feeStats.toRaw());
bw.writeVarBytes(this.priStats.toRaw());
@ -832,13 +829,8 @@ PolicyEstimator.prototype.toRaw = function toRaw() {
PolicyEstimator.prototype.fromRaw = function fromRaw(data) {
var br = new BufferReader(data);
var network = Network.fromMagic(br.readU32());
var bestHeight = br.readU32();
assert(this.network === network,
'Network mistmatch for policy estimator.');
this.bestHeight = bestHeight;
this.bestHeight = br.readU32();
this.feeStats.fromRaw(br.readVarBytes());
this.priStats.fromRaw(br.readVarBytes());
@ -848,13 +840,12 @@ PolicyEstimator.prototype.fromRaw = function fromRaw(data) {
/**
* Instantiate a policy estimator from serialized data.
* @param {Buffer} data
* @param {Network?} network
* @param {Logger?} logger
* @returns {PolicyEstimator}
*/
PolicyEstimator.fromRaw = function fromRaw(data, network, logger) {
return new PolicyEstimator(network, logger).fromRaw(data);
PolicyEstimator.fromRaw = function fromRaw(data, logger) {
return new PolicyEstimator(logger).fromRaw(data);
};
/**
@ -864,13 +855,9 @@ PolicyEstimator.fromRaw = function fromRaw(data, network, logger) {
*/
PolicyEstimator.prototype.inject = function inject(estimator) {
assert(this.network === estimator.network,
'Network mismatch for policy estimator.');
this.bestHeight = estimator.bestHeight;
this.feeStats = estimator.feeStats;
this.priStats = estimator.priStats;
return this;
};

View File

@ -2330,7 +2330,7 @@ MempoolCache.prototype.getFees = co(function* getFees() {
if (!data)
return;
return Fees.fromRaw(data, this.network);
return Fees.fromRaw(data);
});
MempoolCache.prototype.getEntries = function getEntries() {

View File

@ -63,7 +63,7 @@ function FullNode(options) {
});
// Fee estimation.
this.fees = new Fees(this.network, this.logger);
this.fees = new Fees(this.logger);
this.fees.init();
// Mempool needs access to the chain.

View File

@ -477,10 +477,20 @@ WalletDB.prototype.send = co(function* send(tx) {
*/
WalletDB.prototype.estimateFee = co(function* estimateFee(blocks) {
var rate;
if (!this.client)
return this.network.feeRate;
return yield this.client.estimateFee(blocks);
rate = yield this.client.estimateFee(blocks);
if (rate < this.network.feeRate)
return this.network.feeRate;
if (rate > this.network.maxFeeRate)
return this.network.maxFeeRate;
return rate;
});
/**