UNPKG

hsd

Version:
341 lines (283 loc) 7.49 kB
/*! * mempoolentry.js - mempool entry object for hsd * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License). * https://github.com/handshake-org/hsd */ 'use strict'; const bio = require('bufio'); const policy = require('../protocol/policy'); const util = require('../utils/util'); const TX = require('../primitives/tx'); /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').HexHash} HexHash */ /** @typedef {import('../types').Amount} AmountValue */ /** @typedef {import('../types').Rate} Rate */ /** @typedef {import('../types').BufioWriter} BufioWriter */ /** @typedef {import('../coins/coinview')} CoinView */ /** * Mempool Entry * Represents a mempool entry. * @alias module:mempool.MempoolEntry * @property {TX} tx * @property {Number} height * @property {Number} priority * @property {Number} time * @property {Amount} value */ class MempoolEntry extends bio.Struct { /** * Create a mempool entry. * @constructor * @param {Object} [options] * @param {TX} options.tx - Transaction in mempool. * @param {Number} options.height - Entry height. * @param {Number} options.priority - Entry priority. * @param {Number} options.time - Entry time. * @param {AmountValue} options.value - Value of on-chain coins. */ constructor(options) { super(); this.tx = null; this.height = -1; this.size = 0; this.sigops = 0; this.priority = 0; this.fee = 0; this.deltaFee = 0; this.time = 0; this.value = 0; this.coinbase = false; this.dependencies = false; this.descFee = 0; this.descSize = 0; if (options) this.fromOptions(options); } /** * Inject properties from options object. * @param {Object} options */ fromOptions(options) { this.tx = options.tx; this.height = options.height; this.size = options.size; this.sigops = options.sigops; this.priority = options.priority; this.fee = options.fee; this.deltaFee = options.deltaFee; this.time = options.time; this.value = options.value; this.coinbase = options.coinbase; this.dependencies = options.dependencies; this.descFee = options.descFee; this.descSize = options.descSize; return this; } /** * Inject properties from transaction. * @param {TX} tx * @param {CoinView} view * @param {Number} height */ fromTX(tx, view, height) { const value = tx.getChainValue(view); const sigops = tx.getSigops(view); const size = tx.getSigopsSize(sigops); const priority = tx.getPriority(view, height, size); const fee = tx.getFee(view); let dependencies = false; let coinbase = false; for (const {prevout} of tx.inputs) { if (view.isCoinbase(prevout)) coinbase = true; if (view.getHeight(prevout) === -1) dependencies = true; } this.tx = tx; this.height = height; this.size = size; this.sigops = sigops; this.priority = priority; this.fee = fee; this.deltaFee = fee; this.time = util.now(); this.value = value; this.coinbase = coinbase; this.dependencies = dependencies; this.descFee = fee; this.descSize = size; return this; } /** * Create a mempool entry from a TX. * @param {TX} tx * @param {CoinView} view * @param {Number} height - Entry height. * @returns {MempoolEntry} */ static fromTX(tx, view, height) { return new this().fromTX(tx, view, height); } /** * Calculate transaction hash. * @returns {Hash} */ hash() { return this.tx.hash(); } /** * Calculate reverse transaction hash. * @returns {HexHash} */ txid() { return this.tx.txid(); } /** * Calculate priority, taking into account * the entry height delta, modified size, * and chain value. * @param {Number} height * @returns {Number} Priority. */ getPriority(height) { const delta = height - this.height; const priority = (delta * this.value) / this.size; let result = this.priority + Math.floor(priority); if (result < 0) result = 0; return result; } /** * Get fee. * @returns {AmountValue} */ getFee() { return this.fee; } /** * Get delta fee. * @returns {AmountValue} */ getDeltaFee() { return this.deltaFee; } /** * Calculate fee rate. * @returns {Rate} */ getRate() { return policy.getRate(this.size, this.fee); } /** * Calculate delta fee rate. * @returns {Rate} */ getDeltaRate() { return policy.getRate(this.size, this.deltaFee); } /** * Calculate fee cumulative descendant rate. * @returns {Rate} */ getDescRate() { return policy.getRate(this.descSize, this.descFee); } /** * Calculate the memory usage of a transaction. * Note that this only calculates the JS heap * size. Sizes of buffers are ignored (the v8 * heap is what we care most about). All numbers * are based on the output of v8 heap snapshots * of TX objects. * @returns {Number} Usage in bytes. */ memUsage() { const tx = this.tx; let total = 0; total += 176; // mempool entry total += 48; // coinbase total += 48; // dependencies total += 208; // tx total += 80; // _hash total += 80; // _raw total += 80; // _whash total += 48; // mutable total += 32; // input array for (const input of tx.inputs) { total += 120; // input total += 104; // prevout total += 88; // prevout hash total += 96; // witness total += 32; // witness items total += input.witness.items.length * 80; // witness buffers } total += 32; // output array total += 104 * tx.outputs.length; // output total += 40 * tx.outputs.length; // script total += 80 * tx.outputs.length; // script raw buffer total += 32 * tx.outputs.length; // script code array total += 40 * tx.outputs.length; return total; } /** * Test whether the entry is free with * the current priority (calculated by * current height). * @param {Number} height * @returns {Boolean} */ isFree(height) { const priority = this.getPriority(height); return priority > policy.FREE_THRESHOLD; } /** * Get entry serialization size. * @returns {Number} */ getSize() { return this.tx.getSize() + 42; } /** * Serialize entry to a buffer. * @param {BufioWriter} bw * @returns {BufioWriter} */ write(bw) { bw.writeBytes(this.tx.encode()); bw.writeU32(this.height); bw.writeU32(this.size); bw.writeU32(this.sigops); bw.writeDouble(this.priority); bw.writeU64(this.fee); bw.writeU32(this.time); bw.writeU64(this.value); bw.writeU8(this.coinbase ? 1 : 0); bw.writeU8(this.dependencies ? 1 : 0); return bw; } /** * Inject properties from serialized data. * @param {bio.BufferReader} br * @returns {this} */ read(br) { this.tx = TX.read(br); this.height = br.readU32(); this.size = br.readU32(); this.sigops = br.readU32(); this.priority = br.readDouble(); this.fee = br.readU64(); this.deltaFee = this.fee; this.time = br.readU32(); this.value = br.readU64(); this.coinbase = br.readU8() === 1; this.dependencies = br.readU8() === 1; this.descFee = this.fee; this.descSize = this.size; return this; } } /* * Expose */ module.exports = MempoolEntry;