/*!
* txmeta.js - extended transaction 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 util = require('../utils/util');
const TX = require('./tx');
/** @typedef {import('../types').Hash} Hash */
/** @typedef {import('../types').BufioWriter} BufioWriter */
/** @typedef {import('../blockchain/chainentry')} ChainEntry */
/** @typedef {import('../coins/coinview')} CoinView */
/** @typedef {import('../protocol/network')} Network */
/**
* TXMeta
* An extended transaction object.
* @alias module:primitives.TXMeta
*/
class TXMeta extends bio.Struct {
/**
* Create an extended transaction.
* @constructor
* @param {Object?} [options]
*/
constructor(options) {
super();
this.tx = new TX();
this.mtime = util.now();
this.height = -1;
/** @type {Hash} */
this.block = null;
this.time = 0;
this.index = -1;
if (options)
this.fromOptions(options);
}
/**
* Inject properties from options object.
* @param {Object} options
*/
fromOptions(options) {
if (options.tx) {
assert(options.tx instanceof TX);
this.tx = options.tx;
}
if (options.mtime != null) {
assert(util.isU64(options.mtime));
this.mtime = options.mtime;
}
if (options.height != null) {
assert(options.height === -1
|| (options.height >>> 0) === options.height);
this.height = options.height;
}
if (options.block !== undefined) {
assert(options.block === null || Buffer.isBuffer(options.block));
this.block = options.block;
}
if (options.time != null) {
assert(util.isU64(options.time));
this.time = options.time;
}
if (options.index != null) {
assert(options.index === -1 || (options.index >>> 0) === options.index);
this.index = options.index;
}
return this;
}
/**
* Inject properties from options object.
* @param {TX} tx
* @param {ChainEntry} entry
* @param {Number} index
*/
fromTX(tx, entry, index) {
this.tx = tx;
if (entry) {
this.height = entry.height;
this.block = entry.hash;
this.time = entry.time;
this.index = index;
}
return this;
}
/**
* Instantiate TXMeta from options.
* @param {TX} tx
* @param {ChainEntry} entry
* @param {Number} index
* @returns {TXMeta}
*/
static fromTX(tx, entry, index) {
return new this().fromTX(tx, entry, index);
}
/**
* Inspect the transaction.
* @param {CoinView} view
* @returns {Object}
*/
format(view) {
const data = this.tx.format(view, null, this.index);
data.mtime = this.mtime;
data.height = this.height;
data.block = this.block ? this.block.toString('hex') : null;
data.time = this.time;
return data;
}
/**
* Convert the transaction to an object suitable
* for JSON serialization.
* @param {Network} [network]
* @param {CoinView} [view]
* @param {Number} [chainHeight]
* @returns {Object}
*/
getJSON(network, view, chainHeight) {
const json = this.tx.getJSON(network, view, null, this.index);
json.mtime = this.mtime;
json.height = this.height;
json.block = this.block ? this.block.toString('hex') : null;
json.time = this.time;
json.confirmations = 0;
if (chainHeight != null && this.height !== -1)
json.confirmations = chainHeight - this.height + 1;
return json;
}
/**
* Inject properties from a json object.
* @param {Object} json
*/
fromJSON(json) {
this.tx.fromJSON(json);
assert(util.isU64(json.mtime));
assert(json.height === -1 || (json.height >>> 0) === json.height);
assert(!json.block || typeof json.block === 'string');
assert(util.isU64(json.time));
assert(json.index === -1 || (json.index >>> 0) === json.index);
this.mtime = json.mtime;
this.height = json.height;
this.block = json.block ? util.parseHex(json.block, 32) : null;
this.index = json.index;
return this;
}
/**
* Calculate serialization size.
* @returns {Number}
*/
getSize() {
let size = 0;
size += this.tx.getSize();
size += 4;
if (this.block) {
size += 1;
size += 32;
size += 4 * 3;
} else {
size += 1;
}
return size;
}
/**
* Serialize a transaction to "extended format".
* This is the serialization format we use internally
* to store transactions in the database. The extended
* serialization includes the height, block hash, index,
* timestamp, and pending-since time.
* @param {BufioWriter} bw
* @returns {BufioWriter}
*/
write(bw) {
this.tx.write(bw);
bw.writeU32(this.mtime);
if (this.block) {
bw.writeU8(1);
bw.writeHash(this.block);
bw.writeU32(this.height);
bw.writeU32(this.time);
bw.writeU32(this.index);
} else {
bw.writeU8(0);
}
return bw;
}
/**
* Inject properties from "extended" serialization format.
* @param {bio.BufferReader} br
*/
read(br) {
this.tx.read(br);
this.mtime = br.readU32();
if (br.readU8() === 1) {
this.block = br.readHash();
this.height = br.readU32();
this.time = br.readU32();
this.index = br.readU32();
if (this.index === 0xffffffff)
this.index = -1;
}
return this;
}
/**
* Test whether an object is an TXMeta.
* @param {Object} obj
* @returns {Boolean}
*/
static isTXMeta(obj) {
return obj instanceof TXMeta;
}
}
/*
* Expose
*/
module.exports = TXMeta;