UNPKG

hsd

Version:
308 lines (242 loc) 6.21 kB
/*! * output.js - output object for hsd * Copyright (c) 2017-2018, Christopher Jeffrey (MIT License). * https://github.com/handshake-org/hsd */ 'use strict'; const assert = require('bsert'); const bio = require('bufio'); const Amount = require('../ui/amount'); const Network = require('../protocol/network'); const Address = require('../primitives/address'); const consensus = require('../protocol/consensus'); const policy = require('../protocol/policy'); const util = require('../utils/util'); const Covenant = require('./covenant'); /** @typedef {import('../types').Amount} AmountValue */ /** @typedef {import('../types').Rate} Rate */ /** @typedef {import('../types').Hash} Hash */ /** @typedef {import('../types').BufioWriter} BufioWriter */ /** @typedef {import('./covenant').CovenantJSON} CovenantJSON */ /** * @typedef {Object} OutputJSON * @property {AmountValue} value * @property {String} address * @property {CovenantJSON} covenant */ /** * Represents a transaction output. * @alias module:primitives.Output * @property {AmountValue} value * @property {Address} address */ class Output extends bio.Struct { /** * Create an output. * @constructor * @param {Object?} [options] */ constructor(options) { super(); this.value = 0; this.address = new Address(); this.covenant = new Covenant(); if (options) this.fromOptions(options); } /** * Inject properties from options object. * @param {Object} options */ fromOptions(options) { assert(options, 'Output data is required.'); if (options.value != null) { assert(util.isU64(options.value), 'Value must be a uint64.'); this.value = options.value; } if (options.address) this.address.fromOptions(options.address); if (options.covenant) this.covenant.fromOptions(options.covenant); return this; } /** * Inject properties from address/value pair. * @param {Address} address * @param {AmountValue} value * @returns {Output} */ fromScript(address, value) { assert(util.isU64(value), 'Value must be a uint64.'); this.address = Address.fromOptions(address); this.value = value; return this; } /** * Instantiate output from address/value pair. * @param {Address} address * @param {AmountValue} value * @returns {Output} */ static fromScript(address, value) { return new this().fromScript(address, value); } /** * Clone the output. * @param {this} output * @returns {this} */ inject(output) { assert(output instanceof this.constructor); this.value = output.value; this.address.inject(output.address); this.covenant.inject(output.covenant); return this; } /** * Test equality against another output. * @param {Output} output * @returns {Boolean} */ equals(output) { assert(output instanceof this.constructor); return this.value === output.value && this.address.equals(output.address); } /** * Compare against another output (BIP69). * @param {Output} output * @returns {Number} */ compare(output) { assert(output instanceof this.constructor); const cmp = this.value - output.value; if (cmp !== 0) return cmp; return this.address.compare(output.address); } /** * Get the address. * @returns {Address} address */ getAddress() { return this.address; } /** * Get the address hash. * @returns {Hash} */ getHash() { return this.address.getHash(); } /** * Convert the input to a more user-friendly object. * @returns {Object} */ format() { return { value: Amount.coin(this.value), address: this.address, covenant: this.covenant }; } /** * Convert the output to an object suitable * for JSON serialization. * @param {Network} [network] * @returns {OutputJSON} */ getJSON(network) { network = Network.get(network); return { value: this.value, address: this.address.toString(network), covenant: this.covenant.toJSON() }; } /** * Calculate the dust threshold for this * output, based on serialize size and rate. * @param {Rate?} [rate] * @returns {AmountValue} */ getDustThreshold(rate) { if (!this.covenant.isDustworthy()) return 0; if (this.address.isUnspendable()) return 0; const scale = consensus.WITNESS_SCALE_FACTOR; let size = this.getSize(); size += 32 + 4 + 1 + (107 / scale | 0) + 4; return 3 * policy.getMinFee(size, rate); } /** * Calculate size of serialized output. * @returns {Number} */ getSize() { return 8 + this.address.getSize() + this.covenant.getVarSize(); } /** * Test whether the output should be considered dust. * @param {Rate?} [rate] * @returns {Boolean} */ isDust(rate) { return this.value < this.getDustThreshold(rate); } /** * Test whether the output is unspendable. * @returns {Boolean} */ isUnspendable() { return this.address.isUnspendable() || this.covenant.isUnspendable(); } /** * Inject properties from a JSON object. * @param {OutputJSON} json */ fromJSON(json) { assert(json, 'Output data is required.'); assert(util.isU64(json.value), 'Value must be a uint64.'); this.value = json.value; this.address.fromString(json.address); if (json.covenant != null) this.covenant.fromJSON(json.covenant); return this; } /** * Write the output to a buffer writer. * @param {BufioWriter} bw * @returns {BufioWriter} */ write(bw) { bw.writeU64(this.value); this.address.write(bw); this.covenant.write(bw); return bw; } /** * Inject properties from buffer reader. * @param {bio.BufferReader} br * @returns {this} */ read(br) { this.value = br.readU64(); this.address.read(br); this.covenant.read(br); return this; } /** * Test an object to see if it is an Output. * @param {Object} obj * @returns {Boolean} */ static isOutput(obj) { return obj instanceof Output; } } /* * Expose */ module.exports = Output;