mempool: refactor fee estimation.
This commit is contained in:
parent
161fe6e2a1
commit
a11344db0e
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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() {
|
||||||
|
|||||||
@ -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.
|
||||||
|
|||||||
@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user