PHP WebShell

Текущая директория: /opt/BitGoJS/modules/sdk-coin-avaxp/src/lib

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

import { Signature as AvaxSignature, TransferableOutput, TransferOutput, TypeSymbols } from '@bitgo-forks/avalanchejs';
import {
  BaseUtils,
  Entry,
  InvalidTransactionError,
  isValidXprv,
  isValidXpub,
  NotImplementedError,
  ParseTransactionError,
} from '@bitgo/sdk-core';
import { AvalancheNetwork } from '@bitgo/statics';
import { BinTools, BN, Buffer as BufferAvax } from 'avalanche';
import { EVMOutput } from 'avalanche/dist/apis/evm';
import {
  AmountOutput,
  BaseTx,
  TransferableOutput as DeprecatedTransferableOutput,
  SelectCredentialClass,
} from 'avalanche/dist/apis/platformvm';
import { KeyPair as KeyPairAvax } from 'avalanche/dist/apis/platformvm/keychain';
import { Signature } from 'avalanche/dist/common';
import { Credential } from 'avalanche/dist/common/credentials';
import { NodeIDStringToBuffer } from 'avalanche/dist/utils';
import * as createHash from 'create-hash';
import { secp256k1 } from '@noble/curves/secp256k1';
import { ADDRESS_SEPARATOR, DeprecatedOutput, DeprecatedTx, Output } from './iface';

export class Utils implements BaseUtils {
  private binTools = BinTools.getInstance();
  public cb58Decode = this.binTools.cb58Decode;
  public cb58Encode = this.binTools.cb58Encode;
  public stringToBuffer = this.binTools.stringToBuffer;
  public bufferToString = this.binTools.bufferToString;
  public NodeIDStringToBuffer = NodeIDStringToBuffer;
  public addressToString = this.binTools.addressToString;

  public includeIn(walletAddresses: string[], otxoOutputAddresses: string[]): boolean {
    return walletAddresses.map((a) => otxoOutputAddresses.includes(a)).reduce((a, b) => a && b, true);
  }

  /**
   * Checks if it is a valid address no illegal characters
   *
   * @param {string} address - address to be validated
   * @returns {boolean} - the validation result
   */
  /** @inheritdoc */
  isValidAddress(address: string | string[]): boolean {
    const addressArr: string[] = Array.isArray(address) ? address : address.split('~');

    for (const address of addressArr) {
      if (!this.isValidAddressRegex(address)) {
        return false;
      }
    }

    return true;
  }

  private isValidAddressRegex(address: string): boolean {
    return /^(^P||NodeID)-[a-zA-Z0-9]+$/.test(address);
  }

  /**
   * Checks if it is a valid blockId with length 66 including 0x
   *
   * @param {string} hash - blockId to be validated
   * @returns {boolean} - the validation result
   */
  /** @inheritdoc */
  isValidBlockId(hash: string): boolean {
    return this.binTools.isCB58(hash) && this.binTools.b58ToBuffer(hash).length === 36;
  }

  /**
   * Checks if the string is a valid protocol public key or
   * extended public key.
   *
   * @param {string} pub - the  public key to be validated
   * @returns {boolean} - the validation result
   */
  isValidPublicKey(pub: string): boolean {
    if (isValidXpub(pub)) return true;

    let pubBuf;
    if (pub.length === 50) {
      try {
        pubBuf = utils.cb58Decode(pub);
      } catch {
        return false;
      }
    } else {
      if (pub.length !== 66 && pub.length !== 130) return false;

      const firstByte = pub.slice(0, 2);

      // uncompressed public key
      if (pub.length === 130 && firstByte !== '04') return false;

      // compressed public key
      if (pub.length === 66 && firstByte !== '02' && firstByte !== '03') return false;

      if (!this.allHexChars(pub)) return false;
      pubBuf = BufferAvax.from(pub, 'hex');
    }
    // validate the public key
    try {
      secp256k1.ProjectivePoint.fromHex(pubBuf.toString('hex'));
      return true;
    } catch (e) {
      return false;
    }
  }

  public parseAddress = (pub: string): BufferAvax => this.binTools.stringToAddress(pub);

  /**
   * Returns whether or not the string is a valid protocol private key, or extended
   * private key.
   *
   * The protocol key format is described in the @stacks/transactions npm package, in the
   * createStacksPrivateKey function:
   * https://github.com/blockstack/stacks.js/blob/master/packages/transactions/src/keys.ts#L125
   *
   * @param {string} prv - the private key (or extended private key) to be validated
   * @returns {boolean} - the validation result
   */
  isValidPrivateKey(prv: string): boolean {
    if (isValidXprv(prv)) return true;

    if (prv.length !== 64 && prv.length !== 66) return false;

    if (prv.length === 66 && prv.slice(64) !== '01') return false;

    return this.allHexChars(prv);
  }

  /**
   * Returns whether or not the string is a composed of hex chars only
   *
   * @param {string} maybe - the  string to be validated
   * @returns {boolean} - the validation result
   */
  allHexChars(maybe: string): boolean {
    return /^(0x){0,1}([0-9a-f])+$/i.test(maybe);
  }

  /** @inheritdoc */
  isValidSignature(signature: string): boolean {
    throw new NotImplementedError('isValidSignature not implemented');
  }

  /** @inheritdoc */
  isValidTransactionId(txId: string): boolean {
    throw new NotImplementedError('isValidTransactionId not implemented');
  }

  getCredentials(tx: BaseTx): Credential[] {
    return tx.getIns().map((ins) => SelectCredentialClass(ins.getInput().getCredentialID()));
  }

  /**
   * Avaxp wrapper to create signature and return it for credentials using Avalanche's buffer
   * @param network
   * @param message
   * @param prv
   * @return signature
   */
  createSignatureAvaxBuffer(network: AvalancheNetwork, message: BufferAvax, prv: BufferAvax): BufferAvax {
    const ky = new KeyPairAvax(network.hrp, network.networkID.toString());
    ky.importKey(prv);
    return ky.sign(message);
  }

  /**
   * Avaxp wrapper to create signature and return it for credentials
   * @param network
   * @param message
   * @param prv
   * @return signature
   */
  createSignature(network: AvalancheNetwork, message: Buffer, prv: Buffer): Buffer {
    return Buffer.from(this.createSignatureAvaxBuffer(network, BufferAvax.from(message), BufferAvax.from(prv)));
  }

  /**
   * Avaxp wrapper to verify signature using Avalanche's buffer
   * @param network
   * @param message
   * @param signature
   * @param prv
   * @return true if it's verify successful
   */
  verifySignatureAvaxBuffer(
    network: AvalancheNetwork,
    message: BufferAvax,
    signature: BufferAvax,
    prv: BufferAvax
  ): boolean {
    const ky = new KeyPairAvax(network.hrp, network.networkID.toString());
    ky.importKey(prv);
    return ky.verify(message, signature);
  }

  /**
   * Avaxp wrapper to verify signature
   * @param network
   * @param message
   * @param signature
   * @param prv
   * @return true if it's verify successful
   */
  verifySignature(network: AvalancheNetwork, message: Buffer, signature: Buffer, prv: Buffer): boolean {
    return this.verifySignatureAvaxBuffer(
      network,
      BufferAvax.from(message),
      BufferAvax.from(signature),
      BufferAvax.from(prv)
    );
  }

  createSig(sigHex: string): Signature {
    const sig = new Signature();
    sig.fromBuffer(BufferAvax.from(sigHex.padStart(130, '0'), 'hex'));
    return sig;
  }

  createNewSig(sigHex: string): AvaxSignature {
    const buffer = BufferAvax.from(sigHex.padStart(130, '0'), 'hex');
    return new AvaxSignature(buffer);
  }

  /**
   * Avaxp wrapper to recovery signature using Avalanche's buffer
   * @param network
   * @param message
   * @param signature
   * @return
   */
  recoverySignatureAvaxBuffer(network: AvalancheNetwork, message: BufferAvax, signature: BufferAvax): BufferAvax {
    const ky = new KeyPairAvax(network.hrp, network.networkID.toString());
    return ky.recover(message, signature);
  }

  /**
   * Avaxp wrapper to verify signature
   * @param network
   * @param message
   * @param signature
   * @return true if it's verify successful
   */
  recoverySignature(network: AvalancheNetwork, message: Buffer, signature: Buffer): Buffer {
    return Buffer.from(this.recoverySignatureAvaxBuffer(network, BufferAvax.from(message), BufferAvax.from(signature)));
  }

  sha256(buf: Uint8Array): Buffer {
    return createHash.default('sha256').update(buf).digest();
  }

  /**
   * Check the raw transaction has a valid format in the blockchain context, throw otherwise.
   * It's to reuse in TransactionBuilder and TransactionBuilderFactory
   *
   * @param rawTransaction Transaction as hex string
   */
  validateRawTransaction(rawTransaction: string): void {
    if (!rawTransaction) {
      throw new InvalidTransactionError('Raw transaction is empty');
    }
    if (!utils.allHexChars(rawTransaction)) {
      throw new ParseTransactionError('Raw transaction is not hex string');
    }
  }

  /**
   * Check if tx is for the blockchainId
   *
   * @param {DeprecatedTx} tx
   * @param {string} blockchainId
   * @returns true if tx is for blockchainId
   */
  isTransactionOf(tx: DeprecatedTx, blockchainId: string): boolean {
    return utils.cb58Encode((tx as DeprecatedTx).getUnsignedTx().getTransaction().getBlockchainID()) === blockchainId;
  }

  /**
   * Check if Output is from PVM.
   * Output could be EVM or PVM output.
   * @param {DeprecatedOutput} output
   * @returns {boolean} output is DeprecatedTransferableOutput
   */
  deprecatedIsTransferableOutput(output: DeprecatedOutput): output is DeprecatedTransferableOutput {
    return 'getOutput' in output;
  }

  /**
   * Check if Output is from PVM.
   * Output could be EVM or PVM output.
   * @param {Output} output
   * @returns {boolean} output is TransferableOutput
   */
  isTransferableOutput(output: Output): output is TransferableOutput {
    return output?._type === TypeSymbols.TransferableOutput;
  }

  /**
   * Return a mapper function to that network address representation.
   * @param network required to stringify addresses
   * @return mapper function
   */
  deprecatedMapOutputToEntry(network: AvalancheNetwork): (DeprecatedOutput) => Entry {
    return (output: DeprecatedOutput) => {
      if (this.deprecatedIsTransferableOutput(output)) {
        const amountOutput = output.getOutput() as AmountOutput;
        const address = amountOutput
          .getAddresses()
          .map((a) => this.addressToString(network.hrp, network.alias, a))
          .sort()
          .join(ADDRESS_SEPARATOR);
        return {
          value: amountOutput.getAmount().toString(),
          address,
        };
      } else {
        const evmOutput = output as EVMOutput;
        return {
          // it should be evmOuput.getAmount(), but it returns a 0.
          value: new BN((evmOutput as any).amount).toString(),
          // C-Chain address.
          address: '0x' + evmOutput.getAddressString(),
        };
      }
    };
  }

  /**
   * Return a mapper function to that network address representation.
   * @param network required to stringify addresses
   * @return mapper function
   */
  mapOutputToEntry(network: AvalancheNetwork): (Output) => Entry {
    return (output: Output) => {
      if (this.isTransferableOutput(output)) {
        const outputAmount = output.amount();
        const address = (output.output as TransferOutput)
          .getOwners()
          .map((a) => this.addressToString(network.hrp, network.alias, BufferAvax.from(a)))
          .sort()
          .join(ADDRESS_SEPARATOR);
        return {
          value: outputAmount.toString(),
          address,
        };
      } else {
        throw new Error('Invalid output type');
      }
    };
  }

  /**
   * remove hex prefix (0x)
   * @param hex string
   * @returns hex without 0x
   */
  removeHexPrefix(hex: string): string {
    if (hex.startsWith('0x')) {
      return hex.substring(2);
    }
    return hex;
  }

  /**
   * Outputidx convert from number (as string) to buffer.
   * @param {string} outputidx number
   * @return {BufferAvax} buffer of size 4 with that number value
   */
  outputidxNumberToBuffer(outputidx: string): BufferAvax {
    return BufferAvax.from(Number(outputidx).toString(16).padStart(8, '0'), 'hex');
  }

  /**
   * Outputidx buffer to number (as string)
   * @param {BufferAvax} outputidx
   * @return {string} outputidx number
   */
  outputidxBufferToNumber(outputidx: BufferAvax): string {
    return parseInt(outputidx.toString('hex'), 16).toString();
  }
}

const utils = new Utils();

export default utils;

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


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