307 lines
6.1 KiB
JavaScript
307 lines
6.1 KiB
JavaScript
/*!
|
|
* 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;
|