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

View File

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

View File

@ -63,7 +63,7 @@ function FullNode(options) {
}); });
// Fee estimation. // Fee estimation.
this.fees = new Fees(this.network, this.logger); this.fees = new Fees(this.logger);
this.fees.init(); this.fees.init();
// Mempool needs access to the chain. // 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) { WalletDB.prototype.estimateFee = co(function* estimateFee(blocks) {
var rate;
if (!this.client) if (!this.client)
return this.network.feeRate; 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;
}); });
/** /**