diff --git a/lib/bcoin/fees.js b/lib/bcoin/fees.js index 209f6af0..13cc30cc 100644 --- a/lib/bcoin/fees.js +++ b/lib/bcoin/fees.js @@ -12,6 +12,8 @@ var bcoin = require('./env'); var utils = bcoin.utils; var assert = require('assert'); var constants = bcoin.protocol.constants; +var BufferReader = require('./reader'); +var BufferWriter = require('./writer'); var global = bcoin.utils.global; var Float64Array = global.Float64Array || Array; var Int32Array = global.Int32Array || Array; @@ -38,7 +40,7 @@ var FREE_THRESHOLD = constants.tx.FREE_THRESHOLD; /** * Confirmation stats. - * @exposes ConfirmStats + * @exports ConfirmStats * @constructor * @param {Number} buckets * @param {Number} maxConfirms @@ -286,9 +288,99 @@ ConfirmStats.prototype.removeTX = function removeTX(entryHeight, bestHeight, buc } }; +/** + * Serialize confirm stats. + * @returns {Buffer} + */ + +ConfirmStats.prototype.toRaw = function toRaw() { + var p = new BufferWriter(); + var i; + + function writeArray(buckets) { + var i; + + p.writeVarint(buckets.length); + + for (i = 0; i < buckets.length; i++) + p.writeDouble(buckets[i]); + } + + p.writeDouble(this.decay); + writeArray(this.buckets); + writeArray(this.avg); + writeArray(this.txAvg); + p.writeVarint(this.maxConfirms); + + for (i = 0; i < this.maxConfirms; i++) + writeArray(this.confAvg[i]); + + return p.render(); +}; + +/** + * Instantiate confirm stats from serialized data. + * @param {Buffer} data + * @param {String} type + * @returns {ConfirmStats} + */ + +ConfirmStats.fromRaw = function fromRaw(data, type) { + var p = new BufferReader(data); + var i, decay, buckets, avg, txAvg, maxConfirms, confAvg, stats; + + function readArray() { + var buckets = new Float64Array(p.readVarint()); + var i; + + for (i = 0; i < buckets.length; i++) + buckets[i] = p.readDouble(); + + return buckets; + } + + decay = p.readDouble(); + buckets = readArray(); + avg = readArray(); + txAvg = readArray(); + maxConfirms = p.readVarint(); + confAvg = new Array(maxConfirms); + + for (i = 0; i < maxConfirms; i++) + confAvg[i] = readArray(); + + if (decay <= 0 || decay >= 1) + throw new Error('Decay must be between 0 and 1 (non-inclusive).'); + + if (buckets.length <= 1 || buckets.length > 1000) + throw new Error('Must have between 2 and 1000 fee/pri buckets.'); + + if (avg.length !== buckets.length) + throw new Error('Mismatch in fee/pri average bucket count.'); + + if (txAvg.length !== buckets.length) + throw new Error('Mismatch in tx count bucket count.'); + + if (maxConfirms <= 0 || maxConfirms > 6 * 24 * 7) + throw new Error('Must maintain estimates for between 1 and 1008 confirms.'); + + for (i = 0; i < maxConfirms; i++) { + if (confAvg[i].length !== buckets.length) + throw new Error('Mismatch in fee/pri conf average bucket count.'); + } + + stats = new ConfirmStats(buckets, maxConfirms, decay, type); + + stats.avg = avg; + stats.txAvg = txAvg; + stats.confAvg = confAvg; + + return stats; +}; + /** * Estimator for fees and priority. - * @exposes PolicyEstimator + * @exports PolicyEstimator * @constructor * @param {Rate} minRelay * @param {Network|NetworkType} network @@ -641,6 +733,41 @@ PolicyEstimator.prototype.estimatePriority = function estimatePriority(target, s return Math.floor(priority); }; +/** + * Serialize the estimator. + * @returns {Buffer} + */ + +PolicyEstimator.prototype.toRaw = function toRaw() { + var p = new BufferWriter(); + p.writeU32(this.bestHeight); + p.writeVarBytes(this.feeStats.toRaw()); + p.writeVarBytes(this.priStats.toRaw()); + return p.render(); +}; + +/** + * Instantiate a policy estimator from serialized data. + * @param {Buffer} data + * @param {Rate} minRelay + * @param {Network|NetworkType} network + * @returns {PolicyEstimator} + */ + +PolicyEstimator.fromRaw = function fromRaw(data, minRelay, network) { + var p = new BufferReader(data); + var bestHeight = p.readU32(); + var feeStats = ConfirmStats.fromRaw(p.readVarBytes(), 'FeeRate'); + var priStats = ConfirmStats.fromRaw(p.readVarBytes(), 'Priority'); + var estimator = new PolicyEstimator(minRelay, network); + + estimator.bestHeight = bestHeight; + estimator.feeStats = feeStats; + estimator.priStats = priStats; + + return estimator; +}; + /** * DoubleMap * @private