text: /*!
   * coinentry.js - coin entry 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 Coin = require('../primitives/coin');
  const Output = require('../primitives/output');
  const compress = require('./compress');
  const {encoding} = bio;
  
  /*
   * Constants
   */
  
  const NUM_FLAGS = 1;
  const MAX_HEIGHT = ((1 << (32 - NUM_FLAGS)) >>> 0) - 1;
  
  /**
   * Coin Entry
   * Represents an unspent output.
   * @alias module:coins.CoinEntry
   * @property {Number} version - Transaction version.
   * @property {Number} height - Transaction height (-1 if unconfirmed).
   * @property {Boolean} coinbase - Whether the containing
   * transaction is a coinbase.
   * @property {Output} output
   * @property {Boolean} spent
   * @property {Buffer} raw
   */
  
  class CoinEntry extends bio.Struct {
    /**
     * Create a coin entry.
     * @constructor
     */
  
    constructor() {
      super();
      this.version = 1;
      this.height = -1;
      this.coinbase = false;
      this.output = new Output();
      this.spent = false;
      this.raw = null;
    }
  
    /**
     * Convert coin entry to an output.
     * @returns {Output}
     */
  
    toOutput() {
      return this.output;
    }
  
    /**
     * Convert coin entry to a coin.
     * @param {Outpoint} prevout
     * @returns {Coin}
     */
  
    toCoin(prevout) {
      const coin = new Coin();
      coin.version = this.version;
      coin.height = this.height;
      coin.coinbase = this.coinbase;
      coin.value = this.output.value;
      coin.address = this.output.address;
      coin.covenant = this.output.covenant;
      coin.hash = prevout.hash;
      coin.index = prevout.index;
      return coin;
    }
  
    /**
     * Inject properties from TX.
     * @param {TX} tx
     * @param {Number} index
     */
  
    fromOutput(output) {
      this.output = output;
      return this;
    }
  
    /**
     * Instantiate a coin from a TX
     * @param {TX} tx
     * @param {Number} index - Output index.
     * @returns {CoinEntry}
     */
  
    static fromOutput(output) {
      return new this().fromOutput(output);
    }
  
    /**
     * Inject properties from TX.
     * @param {TX} tx
     * @param {Number} index
     */
  
    fromCoin(coin) {
      this.version = coin.version;
      this.height = coin.height;
      this.coinbase = coin.coinbase;
      this.output.value = coin.value;
      this.output.address = coin.address;
      this.output.covenant = coin.covenant;
      return this;
    }
  
    /**
     * Instantiate a coin from a TX
     * @param {TX} tx
     * @param {Number} index - Output index.
     * @returns {CoinEntry}
     */
  
    static fromCoin(coin) {
      return new this().fromCoin(coin);
    }
  
    /**
     * Inject properties from TX.
     * @param {TX} tx
     * @param {Number} index
     */
  
    fromTX(tx, index, height) {
      assert(typeof index === 'number');
      assert(typeof height === 'number');
      assert(index >= 0 && index < tx.outputs.length);
      this.version = tx.version;
      this.height = height;
      this.coinbase = tx.isCoinbase();
      this.output = tx.outputs[index];
      return this;
    }
  
    /**
     * Instantiate a coin from a TX
     * @param {TX} tx
     * @param {Number} index - Output index.
     * @returns {CoinEntry}
     */
  
    static fromTX(tx, index, height) {
      return new this().fromTX(tx, index, height);
    }
  
    /**
     * Calculate size of coin.
     * @returns {Number}
     */
  
    getSize() {
      if (this.raw)
        return this.raw.length;
  
      let size = 0;
      size += encoding.sizeVarint(this.version);
      size += 4;
      size += compress.size(this.output);
  
      return size;
    }
  
    /**
     * Write the coin to a buffer writer.
     * @param {BufferWriter} bw
     */
  
    write(bw) {
      if (this.raw) {
        bw.writeBytes(this.raw);
        return bw;
      }
  
      let height = this.height;
      let field = 0;
  
      if (this.coinbase)
        field |= 1;
  
      if (height === -1)
        height = MAX_HEIGHT;
  
      field |= height << NUM_FLAGS;
  
      bw.writeVarint(this.version);
      bw.writeU32(field);
      compress.pack(this.output, bw);
  
      return bw;
    }
  
    /**
     * Serialize the coin.
     * @returns {Buffer}
     */
  
    encode() {
      if (!this.raw)
        this.raw = super.encode();
  
      return this.raw;
    }
  
    /**
     * Inject properties from serialized buffer writer.
     * @private
     * @param {BufferReader} br
     */
  
    read(br) {
      const version = br.readVarint();
      const field = br.readU32();
  
      let height = field >>> NUM_FLAGS;
  
      if (height === MAX_HEIGHT)
        height = -1;
  
      this.version = version;
      this.coinbase = (field & 1) !== 0;
      this.height = height;
  
      compress.unpack(this.output, br);
  
      return this;
    }
  
    /**
     * Inject properties from serialized data.
     * @private
     * @param {Buffer} data
     */
  
    decode(data) {
      super.decode(data);
      this.raw = data;
      return this;
    }
  }
  
  /*
   * Expose
   */
  
  module.exports = CoinEntry;
  