Source: primitives/filter.js

/*!
 * filters.js - filter object for bcoin
 * Copyright (c) 2019, the bcoin developers (MIT License).
 * https://github.com/bcoin-org/bcoin
 */

'use strict';

const assert = require('bsert');
const bio = require('bufio');
const util = require('../utils/util');
const consensus = require('../protocol/consensus');
const {inspectSymbol} = require('../utils');

/*
 * Constants
 */

const EMPTY_BUFFER = Buffer.alloc(0);

/**
 * Filter
 * Represents a GCSFilter.
 * @alias module:primitives.Filter
 * @property {Hash} hash
 * @property {Number} index
 */

class Filter {
  /**
   * Create an filter.
   * @constructor
   * @param {Object?} options
   */

  constructor(options) {
    this.header = consensus.ZERO_HASH;
    this.filter = EMPTY_BUFFER;

    if (options)
      this.fromOptions(options);
  }

  /**
   * Inject properties from options object.
   * @private
   * @param {Object} options
   */

  fromOptions(options) {
    assert(options, 'Filter data is required.');
    assert(Buffer.isBuffer(options.header));
    assert(Buffer.isBuffer(options.filter));
    this.header = options.header;
    this.filter = options.filter;
    return this;
  }

  /**
   * Instantate outpoint from options object.
   * @param {Object} options
   * @returns {Filter}
   */

  static fromOptions(options) {
    return new this().fromOptions(options);
  }

  /**
   * Write filter to a buffer writer.
   * @param {BufferWriter} bw
   */

  toWriter(bw) {
    bw.writeHash(this.header);
    bw.writeBytes(this.filter);
    return bw;
  }

  /**
   * Calculate size of filter.
   * @returns {Number}
   */

  getSize() {
    let size = 0;
    size += 32;
    size += this.filter.length;
    return size;
  }

  /**
   * Serialize filter.
   * @returns {Buffer}
   */

  toRaw() {
    const size = this.getSize();
    return this.toWriter(bio.write(size)).render();
  }

  /**
   * Inject properties from buffer reader.
   * @private
   * @param {BufferReader} br
   */

  fromReader(br) {
    this.header = br.readHash();
    this.filter = br.readBytes(br.getSize() - br.offset);
    return this;
  }

  /**
   * Inject properties from serialized data.
   * @private
   * @param {Buffer} data
   */

  fromRaw(data) {
    return this.fromReader(bio.read(data));
  }

  /**
   * Instantiate filter from a buffer reader.
   * @param {BufferReader} br
   * @returns {Filter}
   */

  static fromReader(br) {
    return new this().fromReader(br);
  }

  /**
   * Instantiate filter from serialized data.
   * @param {Buffer} data
   * @returns {Filter}
   */

  static fromRaw(data) {
    return new this().fromRaw(data);
  }

  /**
   * Inject properties from json object.
   * @private
   * @params {Object} json
   */

  fromJSON(json) {
    assert(json, 'Filter data is required.');
    assert(typeof json.filter === 'string', 'Filter must be a string.');
    assert(typeof json.header === 'string', 'Header must be a string.');
    this.filter = Buffer.from(json.filter);
    this.header = Buffer.from(json.header);
    return this;
  }

  /**
   * Convert the filter to an object suitable
   * for JSON serialization.
   * @returns {Object}
   */

  toJSON() {
    return {
      filter: this.filter.toString('hex'),
      header: util.revHex(this.header)
    };
  }

  /**
   * Instantiate filter from json object.
   * @param {Object} json
   * @returns {Filter}
   */

  static fromJSON(json) {
    return new this().fromJSON(json);
  }

  /**
   * Convert the filter to a user-friendly string.
   * @returns {String}
   */

  [inspectSymbol]() {
    return `<Filter: ${this.filter.toString('hex')}>`;
  }

  /**
   * Test an object to see if it is an filter.
   * @param {Object} obj
   * @returns {Boolean}
   */

  static isFilter(obj) {
    return obj instanceof Filter;
  }
}

/*
 * Expose
 */

module.exports = Filter;