Source: primitives/taint.js

/**
 * @file A taint item.
 * @module primitives/taint
 */

'use strict'

/**
 * Private members store.
 * @private
 */
const privs = new WeakMap()

/**
 * A taint item.
 * @static
 */
class Taint {
  /**
   * @param {module:primitives/address.Address} source
   *     Source of taint.
   */
  constructor (source) {
    // Validate arguments
    arg.Address(source)

    const priv = {}
    privs.set(this, priv)
    priv.source = source
    priv.recipients = null
    priv.transactions = null
  }

  /**
   * Source of taint.
   * @type {module:primitives/address.Address}
   */
  get source () {
    const priv = privs.get(this)
    const source = priv.source
    return source
  }

  /**
   * Recipients of taint. Excludes source.
   * @type {Set<module:primitives/address.Address>}
   */
  get recipients () {
    const priv = privs.get(this)
    const recipients = priv.recipients
    if (recipients === null) {
      return new Set()
    } else {
      return new Set(recipients)
    }
  }

  /**
   * Tainted addresses. Includes source.
   * @type {Set<module:primitives/address.Address>}
   */
  get addresses () {
    const priv = privs.get(this)
    const source = priv.source
    const recipients = priv.recipients
    if (recipients === null) {
      return new Set([source])
    } else {
      return new Set([source, ...recipients])
    }
  }

  /**
   * Propagating transactions.
   * @type {Set<module:primitives/transaction.Transaction>}
   */
  get transactions () {
    const priv = privs.get(this)
    const transactions = priv.transactions
    if (transactions === null) {
      return new Set()
    } else {
      return new Set(transactions)
    }
  }

  /**
   * Propagating transactions.
   * @type {Set<module:primitives/transaction.Transaction>}
   */
  get txs () {
    return this.transactions
  }

  /**
   * Add recipient.
   * @param {module:primitives/address.Address} recipient
   *     Recipient of taint.
   * @return {module:primitives/taint.Taint}
   *     This instance for chaining.
   */
  addRecipient (recipient) {
    // Validate arguments
    arg.Address(recipient)

    const priv = privs.get(this)
    if (priv.recipients === null) {
      priv.recipients = new Set()
    }
    const recipients = priv.recipients
    recipients.add(recipient)
    return this
  }

  /**
   * Check whether has recipient.
   * @param {module:primitives/address.Address} address
   *     Queried address.
   * @return {boolean} Whether has address as recipient.
   */
  hasRecipient (address) {
    // Validate arguments
    arg.Address(address)

    const priv = privs.get(this)
    const recipients = priv.recipients
    if (recipients === null) {
      return false
    } else {
      return recipients.has(address)
    }
  }

  /**
   * Check whether has address.
   * @param {module:primitives/address.Address} address
   *     Queried address.
   * @return {boolean} Whether has address.
   */
  hasAddress (address) {
    // Validate arguments
    arg.Address(address)

    const priv = privs.get(this)
    const source = priv.source
    if (source === address) {
      return true
    }
    const recipients = priv.recipients
    if (recipients === null) {
      return false
    } else {
      return recipients.has(address)
    }
  }

  /**
   * Add propagating transaction.
   * @param {module:primitives/transaction.Transaction} tx
   *     Propagating transaction.
   * @return {module:primitives/taint.Taint}
   *     This instance for chaining.
   */
  addTransaction (tx) {
    // Validate arguments
    arg.Transaction(tx)

    const priv = privs.get(this)
    if (priv.transactions === null) {
      priv.transactions = new Set()
    }
    const transactions = priv.transactions
    transactions.add(tx)
    return this
  }

  /**
   * Check whether transaction propagated.
   * @param {module:primitives/transaction.Transaction} tx
   *     Queried transaction.
   * @return {boolean} Whether transaction propagated.
   */
  hasTransaction (tx) {
    // Validate arguments
    arg.Transaction(tx)

    const priv = privs.get(this)
    const transactions = priv.transactions
    if (transactions === null) {
      return false
    } else {
      return transactions.has(tx)
    }
  }
}

// Expose
module.exports = Taint

// Circular imports
const arg = require('../util/arg')