text: /*!
   * abstractblock.js - abstract block object for bcoin
   * Copyright (c) 2014-2015, Fedor Indutny (MIT License)
   * Copyright (c) 2014-2017, Christopher Jeffrey (MIT License).
   * https://github.com/bcoin-org/bcoin
   */
  
  'use strict';
  
  const assert = require('bsert');
  const hash256 = require('bcrypto/lib/hash256');
  const bio = require('bufio');
  const util = require('../utils/util');
  const InvItem = require('./invitem');
  const consensus = require('../protocol/consensus');
  
  /**
   * Abstract Block
   * The class which all block-like objects inherit from.
   * @alias module:primitives.AbstractBlock
   * @abstract
   * @property {Number} version
   * @property {Hash} prevBlock
   * @property {Hash} merkleRoot
   * @property {Number} time
   * @property {Number} bits
   * @property {Number} nonce
   */
  
  class AbstractBlock {
    /**
     * Create an abstract block.
     * @constructor
     */
  
    constructor() {
      this.version = 1;
      this.prevBlock = consensus.ZERO_HASH;
      this.merkleRoot = consensus.ZERO_HASH;
      this.time = 0;
      this.bits = 0;
      this.nonce = 0;
  
      this.mutable = false;
  
      this._hash = null;
      this._hhash = null;
    }
  
    /**
     * Inject properties from options object.
     * @private
     * @param {Object} options
     */
  
    parseOptions(options) {
      assert(options, 'Block data is required.');
      assert((options.version >>> 0) === options.version);
      assert(Buffer.isBuffer(options.prevBlock));
      assert(Buffer.isBuffer(options.merkleRoot));
      assert((options.time >>> 0) === options.time);
      assert((options.bits >>> 0) === options.bits);
      assert((options.nonce >>> 0) === options.nonce);
  
      this.version = options.version;
      this.prevBlock = options.prevBlock;
      this.merkleRoot = options.merkleRoot;
      this.time = options.time;
      this.bits = options.bits;
      this.nonce = options.nonce;
  
      if (options.mutable != null) {
        assert(typeof options.mutable === 'boolean');
        this.mutable = options.mutable;
      }
  
      return this;
    }
  
    /**
     * Inject properties from json object.
     * @private
     * @param {Object} json
     */
  
    parseJSON(json) {
      assert(json, 'Block data is required.');
      assert((json.version >>> 0) === json.version);
      assert(typeof json.prevBlock === 'string');
      assert(typeof json.merkleRoot === 'string');
      assert((json.time >>> 0) === json.time);
      assert((json.bits >>> 0) === json.bits);
      assert((json.nonce >>> 0) === json.nonce);
  
      this.version = json.version;
      this.prevBlock = util.fromRev(json.prevBlock);
      this.merkleRoot = util.fromRev(json.merkleRoot);
      this.time = json.time;
      this.bits = json.bits;
      this.nonce = json.nonce;
  
      return this;
    }
  
    /**
     * Test whether the block is a memblock.
     * @returns {Boolean}
     */
  
    isMemory() {
      return false;
    }
  
    /**
     * Clear any cached values (abstract).
     */
  
    _refresh() {
      this._hash = null;
      this._hhash = null;
    }
  
    /**
     * Clear any cached values.
     */
  
    refresh() {
      return this._refresh();
    }
  
    /**
     * Hash the block headers.
     * @param {String?} enc - Can be `'hex'` or `null`.
     * @returns {Hash|Buffer} hash
     */
  
    hash(enc) {
      let h = this._hash;
  
      if (!h) {
        h = hash256.digest(this.toHead());
        if (!this.mutable)
          this._hash = h;
      }
  
      if (enc === 'hex') {
        let hex = this._hhash;
        if (!hex) {
          hex = h.toString('hex');
          if (!this.mutable)
            this._hhash = hex;
        }
        h = hex;
      }
  
      return h;
    }
  
    /**
     * Serialize the block headers.
     * @returns {Buffer}
     */
  
    toHead() {
      return this.writeHead(bio.write(80)).render();
    }
  
    /**
     * Inject properties from serialized data.
     * @private
     * @param {Buffer} data
     */
  
    fromHead(data) {
      return this.readHead(bio.read(data));
    }
  
    /**
     * Serialize the block headers.
     * @param {BufferWriter} bw
     */
  
    writeHead(bw) {
      bw.writeU32(this.version);
      bw.writeHash(this.prevBlock);
      bw.writeHash(this.merkleRoot);
      bw.writeU32(this.time);
      bw.writeU32(this.bits);
      bw.writeU32(this.nonce);
      return bw;
    }
  
    /**
     * Parse the block headers.
     * @param {BufferReader} br
     */
  
    readHead(br) {
      this.version = br.readU32();
      this.prevBlock = br.readHash();
      this.merkleRoot = br.readHash();
      this.time = br.readU32();
      this.bits = br.readU32();
      this.nonce = br.readU32();
      return this;
    }
  
    /**
     * Verify the block.
     * @returns {Boolean}
     */
  
    verify() {
      if (!this.verifyPOW())
        return false;
  
      if (!this.verifyBody())
        return false;
  
      return true;
    }
  
    /**
     * Verify proof-of-work.
     * @returns {Boolean}
     */
  
    verifyPOW() {
      return consensus.verifyPOW(this.hash(), this.bits);
    }
  
    /**
     * Verify the block.
     * @returns {Boolean}
     */
  
    verifyBody() {
      throw new Error('Abstract method.');
    }
  
    /**
     * Get little-endian block hash.
     * @returns {Hash}
     */
  
    rhash() {
      return util.revHex(this.hash());
    }
  
    /**
     * Convert the block to an inv item.
     * @returns {InvItem}
     */
  
    toInv() {
      return new InvItem(InvItem.types.BLOCK, this.hash());
    }
  }
  
  /*
   * Expose
   */
  
  module.exports = AbstractBlock;
  