PHP WebShell

Текущая директория: /opt/BitGoJS/node_modules/avalanche/src/common

Просмотр файла: utxos.ts

/**
 * @packageDocumentation
 * @module Common-UTXOs
 */
import { Buffer } from "buffer/"
import BinTools from "../utils/bintools"
import BN from "bn.js"
import { Output, StandardAmountOutput } from "./output"
import { UnixNow } from "../utils/helperfunctions"
import { MergeRule } from "../utils/constants"
import {
  Serializable,
  Serialization,
  SerializedEncoding
} from "../utils/serialization"
import { MergeRuleError } from "../utils/errors"

/**
 * @ignore
 */
const bintools: BinTools = BinTools.getInstance()
const serialization: Serialization = Serialization.getInstance()

/**
 * Class for representing a single StandardUTXO.
 */
export abstract class StandardUTXO extends Serializable {
  protected _typeName = "StandardUTXO"
  protected _typeID = undefined

  serialize(encoding: SerializedEncoding = "hex"): object {
    let fields: object = super.serialize(encoding)
    return {
      ...fields,
      codecID: serialization.encoder(
        this.codecID,
        encoding,
        "Buffer",
        "decimalString"
      ),
      txid: serialization.encoder(this.txid, encoding, "Buffer", "cb58"),
      outputidx: serialization.encoder(
        this.outputidx,
        encoding,
        "Buffer",
        "decimalString"
      ),
      assetID: serialization.encoder(this.assetID, encoding, "Buffer", "cb58"),
      output: this.output.serialize(encoding)
    }
  }
  deserialize(fields: object, encoding: SerializedEncoding = "hex") {
    super.deserialize(fields, encoding)
    this.codecID = serialization.decoder(
      fields["codecID"],
      encoding,
      "decimalString",
      "Buffer",
      2
    )
    this.txid = serialization.decoder(
      fields["txid"],
      encoding,
      "cb58",
      "Buffer",
      32
    )
    this.outputidx = serialization.decoder(
      fields["outputidx"],
      encoding,
      "decimalString",
      "Buffer",
      4
    )
    this.assetID = serialization.decoder(
      fields["assetID"],
      encoding,
      "cb58",
      "Buffer",
      32
    )
  }

  protected codecID: Buffer = Buffer.alloc(2)
  protected txid: Buffer = Buffer.alloc(32)
  protected outputidx: Buffer = Buffer.alloc(4)
  protected assetID: Buffer = Buffer.alloc(32)
  protected output: Output = undefined

  /**
   * Returns the numeric representation of the CodecID.
   */
  getCodecID = (): /* istanbul ignore next */ number =>
    this.codecID.readUInt8(0)

  /**
   * Returns the {@link https://github.com/feross/buffer|Buffer} representation of the CodecID
   */
  getCodecIDBuffer = (): Buffer => this.codecID

  /**
   * Returns a {@link https://github.com/feross/buffer|Buffer} of the TxID.
   */
  getTxID = (): /* istanbul ignore next */ Buffer => this.txid

  /**
   * Returns a {@link https://github.com/feross/buffer|Buffer}  of the OutputIdx.
   */
  getOutputIdx = (): /* istanbul ignore next */ Buffer => this.outputidx

  /**
   * Returns the assetID as a {@link https://github.com/feross/buffer|Buffer}.
   */
  getAssetID = (): Buffer => this.assetID

  /**
   * Returns the UTXOID as a base-58 string (UTXOID is a string )
   */
  getUTXOID = (): /* istanbul ignore next */ string =>
    bintools.bufferToB58(Buffer.concat([this.getTxID(), this.getOutputIdx()]))

  /**
   * Returns a reference to the output
   */
  getOutput = (): Output => this.output

  /**
   * Takes a {@link https://github.com/feross/buffer|Buffer} containing an [[StandardUTXO]], parses it, populates the class, and returns the length of the StandardUTXO in bytes.
   *
   * @param bytes A {@link https://github.com/feross/buffer|Buffer} containing a raw [[StandardUTXO]]
   */
  abstract fromBuffer(bytes: Buffer, offset?: number): number

  /**
   * Returns a {@link https://github.com/feross/buffer|Buffer} representation of the [[StandardUTXO]].
   */
  toBuffer(): Buffer {
    const outbuff: Buffer = this.output.toBuffer()
    const outputidbuffer: Buffer = Buffer.alloc(4)
    outputidbuffer.writeUInt32BE(this.output.getOutputID(), 0)
    const barr: Buffer[] = [
      this.codecID,
      this.txid,
      this.outputidx,
      this.assetID,
      outputidbuffer,
      outbuff
    ]
    return Buffer.concat(
      barr,
      this.codecID.length +
        this.txid.length +
        this.outputidx.length +
        this.assetID.length +
        outputidbuffer.length +
        outbuff.length
    )
  }

  abstract fromString(serialized: string): number

  abstract toString(): string

  abstract clone(): this

  abstract create(
    codecID?: number,
    txid?: Buffer,
    outputidx?: Buffer | number,
    assetID?: Buffer,
    output?: Output
  ): this

  /**
   * Class for representing a single StandardUTXO.
   *
   * @param codecID Optional number which specifies the codeID of the UTXO. Default 0
   * @param txID Optional {@link https://github.com/feross/buffer|Buffer} of transaction ID for the StandardUTXO
   * @param txidx Optional {@link https://github.com/feross/buffer|Buffer} or number for the index of the transaction's [[Output]]
   * @param assetID Optional {@link https://github.com/feross/buffer|Buffer} of the asset ID for the StandardUTXO
   * @param outputid Optional {@link https://github.com/feross/buffer|Buffer} or number of the output ID for the StandardUTXO
   */
  constructor(
    codecID: number = 0,
    txID: Buffer = undefined,
    outputidx: Buffer | number = undefined,
    assetID: Buffer = undefined,
    output: Output = undefined
  ) {
    super()
    if (typeof codecID !== "undefined") {
      this.codecID.writeUInt8(codecID, 0)
    }
    if (typeof txID !== "undefined") {
      this.txid = txID
    }
    if (typeof outputidx === "number") {
      this.outputidx.writeUInt32BE(outputidx, 0)
    } else if (outputidx instanceof Buffer) {
      this.outputidx = outputidx
    }

    if (typeof assetID !== "undefined") {
      this.assetID = assetID
    }
    if (typeof output !== "undefined") {
      this.output = output
    }
  }
}
/**
 * Class representing a set of [[StandardUTXO]]s.
 */
export abstract class StandardUTXOSet<
  UTXOClass extends StandardUTXO
> extends Serializable {
  protected _typeName = "StandardUTXOSet"
  protected _typeID = undefined

  serialize(encoding: SerializedEncoding = "hex"): object {
    let fields: object = super.serialize(encoding)
    let utxos = {}
    for (let utxoid in this.utxos) {
      let utxoidCleaned: string = serialization.encoder(
        utxoid,
        encoding,
        "base58",
        "base58"
      )
      utxos[`${utxoidCleaned}`] = this.utxos[`${utxoid}`].serialize(encoding)
    }
    let addressUTXOs = {}
    for (let address in this.addressUTXOs) {
      let addressCleaned: string = serialization.encoder(
        address,
        encoding,
        "hex",
        "cb58"
      )
      let utxobalance = {}
      for (let utxoid in this.addressUTXOs[`${address}`]) {
        let utxoidCleaned: string = serialization.encoder(
          utxoid,
          encoding,
          "base58",
          "base58"
        )
        utxobalance[`${utxoidCleaned}`] = serialization.encoder(
          this.addressUTXOs[`${address}`][`${utxoid}`],
          encoding,
          "BN",
          "decimalString"
        )
      }
      addressUTXOs[`${addressCleaned}`] = utxobalance
    }
    return {
      ...fields,
      utxos,
      addressUTXOs
    }
  }

  protected utxos: { [utxoid: string]: UTXOClass } = {}
  protected addressUTXOs: { [address: string]: { [utxoid: string]: BN } } = {} // maps address to utxoids:locktime

  abstract parseUTXO(utxo: UTXOClass | string): UTXOClass

  /**
   * Returns true if the [[StandardUTXO]] is in the StandardUTXOSet.
   *
   * @param utxo Either a [[StandardUTXO]] a cb58 serialized string representing a StandardUTXO
   */
  includes = (utxo: UTXOClass | string): boolean => {
    let utxoX: UTXOClass = undefined
    let utxoid: string = undefined
    try {
      utxoX = this.parseUTXO(utxo)
      utxoid = utxoX.getUTXOID()
    } catch (e) {
      if (e instanceof Error) {
        console.log(e.message)
      } else {
        console.log(e)
      }
      return false
    }
    return utxoid in this.utxos
  }

  /**
   * Adds a [[StandardUTXO]] to the StandardUTXOSet.
   *
   * @param utxo Either a [[StandardUTXO]] an cb58 serialized string representing a StandardUTXO
   * @param overwrite If true, if the UTXOID already exists, overwrite it... default false
   *
   * @returns A [[StandardUTXO]] if one was added and undefined if nothing was added.
   */
  add(utxo: UTXOClass | string, overwrite: boolean = false): UTXOClass {
    let utxovar: UTXOClass = undefined
    try {
      utxovar = this.parseUTXO(utxo)
    } catch (e) {
      if (e instanceof Error) {
        console.log(e.message)
      } else {
        console.log(e)
      }
      return undefined
    }

    const utxoid: string = utxovar.getUTXOID()
    if (!(utxoid in this.utxos) || overwrite === true) {
      this.utxos[`${utxoid}`] = utxovar
      const addresses: Buffer[] = utxovar.getOutput().getAddresses()
      const locktime: BN = utxovar.getOutput().getLocktime()
      for (let i: number = 0; i < addresses.length; i++) {
        const address: string = addresses[`${i}`].toString("hex")
        if (!(address in this.addressUTXOs)) {
          this.addressUTXOs[`${address}`] = {}
        }
        this.addressUTXOs[`${address}`][`${utxoid}`] = locktime
      }
      return utxovar
    }
    return undefined
  }

  /**
   * Adds an array of [[StandardUTXO]]s to the [[StandardUTXOSet]].
   *
   * @param utxo Either a [[StandardUTXO]] an cb58 serialized string representing a StandardUTXO
   * @param overwrite If true, if the UTXOID already exists, overwrite it... default false
   *
   * @returns An array of StandardUTXOs which were added.
   */
  addArray(
    utxos: string[] | UTXOClass[],
    overwrite: boolean = false
  ): StandardUTXO[] {
    const added: UTXOClass[] = []
    for (let i: number = 0; i < utxos.length; i++) {
      let result: UTXOClass = this.add(utxos[`${i}`], overwrite)
      if (typeof result !== "undefined") {
        added.push(result)
      }
    }
    return added
  }

  /**
   * Removes a [[StandardUTXO]] from the [[StandardUTXOSet]] if it exists.
   *
   * @param utxo Either a [[StandardUTXO]] an cb58 serialized string representing a StandardUTXO
   *
   * @returns A [[StandardUTXO]] if it was removed and undefined if nothing was removed.
   */
  remove = (utxo: UTXOClass | string): UTXOClass => {
    let utxovar: UTXOClass = undefined
    try {
      utxovar = this.parseUTXO(utxo)
    } catch (e) {
      if (e instanceof Error) {
        console.log(e.message)
      } else {
        console.log(e)
      }
      return undefined
    }

    const utxoid: string = utxovar.getUTXOID()
    if (!(utxoid in this.utxos)) {
      return undefined
    }
    delete this.utxos[`${utxoid}`]
    const addresses = Object.keys(this.addressUTXOs)
    for (let i: number = 0; i < addresses.length; i++) {
      if (utxoid in this.addressUTXOs[addresses[`${i}`]]) {
        delete this.addressUTXOs[addresses[`${i}`]][`${utxoid}`]
      }
    }
    return utxovar
  }

  /**
   * Removes an array of [[StandardUTXO]]s to the [[StandardUTXOSet]].
   *
   * @param utxo Either a [[StandardUTXO]] an cb58 serialized string representing a StandardUTXO
   * @param overwrite If true, if the UTXOID already exists, overwrite it... default false
   *
   * @returns An array of UTXOs which were removed.
   */
  removeArray = (utxos: string[] | UTXOClass[]): UTXOClass[] => {
    const removed: UTXOClass[] = []
    for (let i: number = 0; i < utxos.length; i++) {
      const result: UTXOClass = this.remove(utxos[`${i}`])
      if (typeof result !== "undefined") {
        removed.push(result)
      }
    }
    return removed
  }

  /**
   * Gets a [[StandardUTXO]] from the [[StandardUTXOSet]] by its UTXOID.
   *
   * @param utxoid String representing the UTXOID
   *
   * @returns A [[StandardUTXO]] if it exists in the set.
   */
  getUTXO = (utxoid: string): UTXOClass => this.utxos[`${utxoid}`]

  /**
   * Gets all the [[StandardUTXO]]s, optionally that match with UTXOIDs in an array
   *
   * @param utxoids An optional array of UTXOIDs, returns all [[StandardUTXO]]s if not provided
   *
   * @returns An array of [[StandardUTXO]]s.
   */
  getAllUTXOs = (utxoids: string[] = undefined): UTXOClass[] => {
    let results: UTXOClass[] = []
    if (typeof utxoids !== "undefined" && Array.isArray(utxoids)) {
      results = utxoids
        .filter((utxoid) => this.utxos[`${utxoid}`])
        .map((utxoid) => this.utxos[`${utxoid}`])
    } else {
      results = Object.values(this.utxos)
    }
    return results
  }

  /**
   * Gets all the [[StandardUTXO]]s as strings, optionally that match with UTXOIDs in an array.
   *
   * @param utxoids An optional array of UTXOIDs, returns all [[StandardUTXO]]s if not provided
   *
   * @returns An array of [[StandardUTXO]]s as cb58 serialized strings.
   */
  getAllUTXOStrings = (utxoids: string[] = undefined): string[] => {
    const results: string[] = []
    const utxos = Object.keys(this.utxos)
    if (typeof utxoids !== "undefined" && Array.isArray(utxoids)) {
      for (let i: number = 0; i < utxoids.length; i++) {
        if (utxoids[`${i}`] in this.utxos) {
          results.push(this.utxos[utxoids[`${i}`]].toString())
        }
      }
    } else {
      for (const u of utxos) {
        results.push(this.utxos[`${u}`].toString())
      }
    }
    return results
  }

  /**
   * Given an address or array of addresses, returns all the UTXOIDs for those addresses
   *
   * @param address An array of address {@link https://github.com/feross/buffer|Buffer}s
   * @param spendable If true, only retrieves UTXOIDs whose locktime has passed
   *
   * @returns An array of addresses.
   */
  getUTXOIDs = (
    addresses: Buffer[] = undefined,
    spendable: boolean = true
  ): string[] => {
    if (typeof addresses !== "undefined") {
      const results: string[] = []
      const now: BN = UnixNow()
      for (let i: number = 0; i < addresses.length; i++) {
        if (addresses[`${i}`].toString("hex") in this.addressUTXOs) {
          const entries = Object.entries(
            this.addressUTXOs[addresses[`${i}`].toString("hex")]
          )
          for (const [utxoid, locktime] of entries) {
            if (
              (results.indexOf(utxoid) === -1 &&
                spendable &&
                locktime.lte(now)) ||
              !spendable
            ) {
              results.push(utxoid)
            }
          }
        }
      }
      return results
    }
    return Object.keys(this.utxos)
  }

  /**
   * Gets the addresses in the [[StandardUTXOSet]] and returns an array of {@link https://github.com/feross/buffer|Buffer}.
   */
  getAddresses = (): Buffer[] =>
    Object.keys(this.addressUTXOs).map((k) => Buffer.from(k, "hex"))

  /**
   * Returns the balance of a set of addresses in the StandardUTXOSet.
   *
   * @param addresses An array of addresses
   * @param assetID Either a {@link https://github.com/feross/buffer|Buffer} or an cb58 serialized representation of an AssetID
   * @param asOf The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN}
   *
   * @returns Returns the total balance as a {@link https://github.com/indutny/bn.js/|BN}.
   */
  getBalance = (
    addresses: Buffer[],
    assetID: Buffer | string,
    asOf: BN = undefined
  ): BN => {
    const utxoids: string[] = this.getUTXOIDs(addresses)
    const utxos: StandardUTXO[] = this.getAllUTXOs(utxoids)
    let spend: BN = new BN(0)
    let asset: Buffer
    if (typeof assetID === "string") {
      asset = bintools.cb58Decode(assetID)
    } else {
      asset = assetID
    }
    for (let i: number = 0; i < utxos.length; i++) {
      if (
        utxos[`${i}`].getOutput() instanceof StandardAmountOutput &&
        utxos[`${i}`].getAssetID().toString("hex") === asset.toString("hex") &&
        utxos[`${i}`].getOutput().meetsThreshold(addresses, asOf)
      ) {
        spend = spend.add(
          (utxos[`${i}`].getOutput() as StandardAmountOutput).getAmount()
        )
      }
    }
    return spend
  }

  /**
   * Gets all the Asset IDs, optionally that match with Asset IDs in an array
   *
   * @param utxoids An optional array of Addresses as string or Buffer, returns all Asset IDs if not provided
   *
   * @returns An array of {@link https://github.com/feross/buffer|Buffer} representing the Asset IDs.
   */
  getAssetIDs = (addresses: Buffer[] = undefined): Buffer[] => {
    const results: Set<Buffer> = new Set()
    let utxoids: string[] = []
    if (typeof addresses !== "undefined") {
      utxoids = this.getUTXOIDs(addresses)
    } else {
      utxoids = this.getUTXOIDs()
    }

    for (let i: number = 0; i < utxoids.length; i++) {
      if (utxoids[`${i}`] in this.utxos && !(utxoids[`${i}`] in results)) {
        results.add(this.utxos[utxoids[`${i}`]].getAssetID())
      }
    }

    return [...results]
  }

  abstract clone(): this

  abstract create(...args: any[]): this

  filter(
    args: any[],
    lambda: (utxo: UTXOClass, ...largs: any[]) => boolean
  ): this {
    let newset: this = this.clone()
    let utxos: UTXOClass[] = this.getAllUTXOs()
    for (let i: number = 0; i < utxos.length; i++) {
      if (lambda(utxos[`${i}`], ...args) === false) {
        newset.remove(utxos[`${i}`])
      }
    }
    return newset
  }

  /**
   * Returns a new set with copy of UTXOs in this and set parameter.
   *
   * @param utxoset The [[StandardUTXOSet]] to merge with this one
   * @param hasUTXOIDs Will subselect a set of [[StandardUTXO]]s which have the UTXOIDs provided in this array, defults to all UTXOs
   *
   * @returns A new StandardUTXOSet that contains all the filtered elements.
   */
  merge = (utxoset: this, hasUTXOIDs: string[] = undefined): this => {
    const results: this = this.create()
    const utxos1: UTXOClass[] = this.getAllUTXOs(hasUTXOIDs)
    const utxos2: UTXOClass[] = utxoset.getAllUTXOs(hasUTXOIDs)
    const process = (utxo: UTXOClass) => {
      results.add(utxo)
    }
    utxos1.forEach(process)
    utxos2.forEach(process)
    return results as this
  }

  /**
   * Set intersetion between this set and a parameter.
   *
   * @param utxoset The set to intersect
   *
   * @returns A new StandardUTXOSet containing the intersection
   */
  intersection = (utxoset: this): this => {
    const us1: string[] = this.getUTXOIDs()
    const us2: string[] = utxoset.getUTXOIDs()
    const results: string[] = us1.filter((utxoid) => us2.includes(utxoid))
    return this.merge(utxoset, results) as this
  }

  /**
   * Set difference between this set and a parameter.
   *
   * @param utxoset The set to difference
   *
   * @returns A new StandardUTXOSet containing the difference
   */
  difference = (utxoset: this): this => {
    const us1: string[] = this.getUTXOIDs()
    const us2: string[] = utxoset.getUTXOIDs()
    const results: string[] = us1.filter((utxoid) => !us2.includes(utxoid))
    return this.merge(utxoset, results) as this
  }

  /**
   * Set symmetrical difference between this set and a parameter.
   *
   * @param utxoset The set to symmetrical difference
   *
   * @returns A new StandardUTXOSet containing the symmetrical difference
   */
  symDifference = (utxoset: this): this => {
    const us1: string[] = this.getUTXOIDs()
    const us2: string[] = utxoset.getUTXOIDs()
    const results: string[] = us1
      .filter((utxoid) => !us2.includes(utxoid))
      .concat(us2.filter((utxoid) => !us1.includes(utxoid)))
    return this.merge(utxoset, results) as this
  }

  /**
   * Set union between this set and a parameter.
   *
   * @param utxoset The set to union
   *
   * @returns A new StandardUTXOSet containing the union
   */
  union = (utxoset: this): this => this.merge(utxoset) as this

  /**
   * Merges a set by the rule provided.
   *
   * @param utxoset The set to merge by the MergeRule
   * @param mergeRule The [[MergeRule]] to apply
   *
   * @returns A new StandardUTXOSet containing the merged data
   *
   * @remarks
   * The merge rules are as follows:
   *   * "intersection" - the intersection of the set
   *   * "differenceSelf" - the difference between the existing data and new set
   *   * "differenceNew" - the difference between the new data and the existing set
   *   * "symDifference" - the union of the differences between both sets of data
   *   * "union" - the unique set of all elements contained in both sets
   *   * "unionMinusNew" - the unique set of all elements contained in both sets, excluding values only found in the new set
   *   * "unionMinusSelf" - the unique set of all elements contained in both sets, excluding values only found in the existing set
   */
  mergeByRule = (utxoset: this, mergeRule: MergeRule): this => {
    let uSet: this
    switch (mergeRule) {
      case "intersection":
        return this.intersection(utxoset)
      case "differenceSelf":
        return this.difference(utxoset)
      case "differenceNew":
        return utxoset.difference(this) as this
      case "symDifference":
        return this.symDifference(utxoset)
      case "union":
        return this.union(utxoset)
      case "unionMinusNew":
        uSet = this.union(utxoset)
        return uSet.difference(utxoset) as this
      case "unionMinusSelf":
        uSet = this.union(utxoset)
        return uSet.difference(this) as this
      default:
        throw new MergeRuleError(
          "Error - StandardUTXOSet.mergeByRule: bad MergeRule"
        )
    }
  }
}

Выполнить команду


Для локальной разработки. Не используйте в интернете!