376 lines
7.9 KiB
JavaScript
376 lines
7.9 KiB
JavaScript
/*!
|
|
* mempoolentry.js - mempool entry object for bcoin
|
|
* Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
|
|
* https://github.com/bcoin-org/bcoin
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const bio = require('bufio');
|
|
const policy = require('../protocol/policy');
|
|
const util = require('../utils/util');
|
|
const Script = require('../script/script');
|
|
const TX = require('../primitives/tx');
|
|
|
|
/**
|
|
* 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 {
|
|
/**
|
|
* 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 {Amount} options.value - Value of on-chain coins.
|
|
*/
|
|
|
|
constructor(options) {
|
|
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.
|
|
* @private
|
|
* @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;
|
|
}
|
|
|
|
/**
|
|
* Instantiate mempool entry from options.
|
|
* @param {Object} options
|
|
* @returns {MempoolEntry}
|
|
*/
|
|
|
|
static fromOptions(options) {
|
|
return new this().fromOptions(options);
|
|
}
|
|
|
|
/**
|
|
* Inject properties from transaction.
|
|
* @private
|
|
* @param {TX} tx
|
|
* @param {Number} height
|
|
*/
|
|
|
|
fromTX(tx, view, height) {
|
|
const flags = Script.flags.STANDARD_VERIFY_FLAGS;
|
|
const value = tx.getChainValue(view);
|
|
const sigops = tx.getSigopsCost(view, flags);
|
|
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 {Number} height - Entry height.
|
|
* @returns {MempoolEntry}
|
|
*/
|
|
|
|
static fromTX(tx, view, height) {
|
|
return new this().fromTX(tx, view, height);
|
|
}
|
|
|
|
/**
|
|
* Calculate transaction hash.
|
|
* @param {String?} enc
|
|
* @returns {Hash}
|
|
*/
|
|
|
|
hash(enc) {
|
|
return this.tx.hash(enc);
|
|
}
|
|
|
|
/**
|
|
* Calculate reverse transaction hash.
|
|
* @returns {Hash}
|
|
*/
|
|
|
|
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 {Amount}
|
|
*/
|
|
|
|
getFee() {
|
|
return this.fee;
|
|
}
|
|
|
|
/**
|
|
* Get delta fee.
|
|
* @returns {Amount}
|
|
*/
|
|
|
|
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 += 88; // _hhash
|
|
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 += 40; // script
|
|
total += 80; // script raw buffer
|
|
total += 32; // script code array
|
|
total += input.script.code.length * 40; // opcodes
|
|
|
|
for (const op of input.script.code) {
|
|
if (op.data)
|
|
total += 80; // op buffers
|
|
}
|
|
|
|
total += 96; // witness
|
|
total += 32; // witness items
|
|
total += input.witness.items.length * 80; // witness buffers
|
|
}
|
|
|
|
total += 32; // output array
|
|
|
|
for (const output of tx.outputs) {
|
|
total += 104; // output
|
|
total += 40; // script
|
|
total += 80; // script raw buffer
|
|
total += 32; // script code array
|
|
total += output.script.code.length * 40; // opcodes
|
|
|
|
for (const op of output.script.code) {
|
|
if (op.data)
|
|
total += 80; // op buffers
|
|
}
|
|
}
|
|
|
|
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.
|
|
* @returns {Buffer}
|
|
*/
|
|
|
|
toRaw() {
|
|
const bw = bio.write(this.getSize());
|
|
bw.writeBytes(this.tx.toRaw());
|
|
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.render();
|
|
}
|
|
|
|
/**
|
|
* Inject properties from serialized data.
|
|
* @private
|
|
* @param {Buffer} data
|
|
* @returns {MempoolEntry}
|
|
*/
|
|
|
|
fromRaw(data) {
|
|
const br = bio.read(data);
|
|
this.tx = TX.fromReader(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;
|
|
}
|
|
|
|
/**
|
|
* Instantiate entry from serialized data.
|
|
* @param {Buffer} data
|
|
* @returns {MempoolEntry}
|
|
*/
|
|
|
|
static fromRaw(data) {
|
|
return new this().fromRaw(data);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Expose
|
|
*/
|
|
|
|
module.exports = MempoolEntry;
|