PHP WebShell

Текущая директория: /usr/lib/node_modules/bitgo/node_modules/@bitgo-forks/avalanchejs/src/vms/common

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

import { sha256 } from '@noble/hashes/sha256';
import { emptySignature } from '../../constants/zeroValue';
import { SignedTx } from '../../serializable/avax';
import { Utxo } from '../../serializable/avax/utxo';
import type { VM } from '../../serializable/constants';
import { ValidVMs } from '../../serializable/constants';
import { Address } from '../../serializable/fxs/common';
import { Credential } from '../../serializable/fxs/secp256k1';
import { bufferToHex, hexToBuffer } from '../../utils';
import { secp256k1 } from '../../crypto';
import { AddressMaps } from '../../utils/addressMap';
import { getManagerForVM, packTx } from '../../utils/packTx';
import type { Transaction } from './transaction';

type UnsingedTxSerialize = {
  txBytes: string;
  utxos: string[];
  addressMaps: [string, number][][];
  vm: string;
  codecId: string;
  credentials: string[][];
};
export class UnsignedTx {
  credentials: Credential[];
  constructor(
    readonly tx: Transaction,
    readonly utxos: Utxo[],
    readonly addressMaps: AddressMaps,
    credentials?: Credential[],
  ) {
    if (credentials) {
      this.credentials = credentials;
      return;
    }
    this.credentials = this.tx
      .getSigIndices()
      .map((indicies) => new Credential(indicies.map(() => emptySignature)));
  }

  toJSON() {
    const codec = getManagerForVM(this.tx.vm).getDefaultCodec();
    const codecId = getManagerForVM(this.tx.vm).getDefaultCodecId();
    return {
      codecId: codecId,
      vm: this.tx.vm,
      txBytes: bufferToHex(this.toBytes()),
      utxos: this.utxos.map((utxo) => bufferToHex(utxo.toBytes(codec))),
      addressMaps: this.addressMaps,
      credentials: this.credentials,
    };
  }

  static fromJSON(jsonString: string) {
    const res = JSON.parse(jsonString) as UnsingedTxSerialize;

    const fields = [
      'txBytes',
      'utxos',
      'addressMaps',
      'vm',
      'codecId',
      'credentials',
    ];

    fields.forEach((field) => {
      if (!res[field]) {
        throw new Error(
          `invalid structure. must have ${fields.join(', ')}, missing ${field}`,
        );
      }
    });

    const vm = res.vm as VM;
    if (!ValidVMs.includes(vm)) {
      throw new Error('invalid VM');
    }

    const manager = getManagerForVM(vm);
    const [codec, rest] = manager.getCodecFromBuffer(hexToBuffer(res.txBytes));
    const tx = codec.UnpackPrefix<Transaction>(rest)[0];

    const utxos = res.utxos.map(
      (utxo) => Utxo.fromBytes(hexToBuffer(utxo), codec)[0],
    );

    const addressMaps = AddressMaps.fromJSON(res.addressMaps);

    const credentials = res.credentials.map((credStr) =>
      Credential.fromJSON(credStr),
    );
    return new UnsignedTx(tx, utxos, addressMaps, credentials);
  }

  getSigIndices() {
    return this.tx.getSigIndices();
  }

  hasAddress(address: Address) {
    return this.addressMaps.has(address);
  }

  hasPubkey(pubKey: Uint8Array) {
    return this.hasAddress(new Address(this.publicKeyBytesToAddress(pubKey)));
  }

  getAddresses() {
    return this.addressMaps.getAddresses();
  }

  getSigIndicesForAddress(address: Address) {
    const useReorderedIndices = this.getSigIndices().some(
      (sigIndices, credIndex) => {
        const signaturesLength = this.credentials[credIndex].toJSON().length;
        const maxSigIndex = Math.max(...sigIndices);

        return maxSigIndex > signaturesLength - 1;
      },
    );

    return this.addressMaps.getSigIndicesForAddress(
      address,
      useReorderedIndices,
    );
  }

  getSigIndicesForPubKey(pubkey: Uint8Array) {
    const addrAvax = this.publicKeyBytesToAddress(pubkey);
    const addrEvm = secp256k1.publicKeyToEthAddress(pubkey);

    // Check against both addresses
    const coordinatesAvax = this.getSigIndicesForAddress(new Address(addrAvax));
    const coordinatesEvm = this.getSigIndicesForAddress(new Address(addrEvm));

    return coordinatesAvax || coordinatesEvm;
  }

  getInputUtxos() {
    return this.utxos;
  }

  toBytes(): Uint8Array {
    return packTx(this.tx);
  }

  getBlockchainId() {
    return this.tx.getBlockchainId();
  }

  getTx() {
    return this.tx;
  }

  getSignedTx() {
    return new SignedTx(this.tx, this.credentials);
  }

  getCredentials(): Credential[] {
    return this.credentials as Credential[];
  }

  addSignatureAt(sig: Uint8Array, index: number, subIndex: number) {
    if (index >= this.getCredentials().length) {
      throw new Error('index out of bounds');
    }
    this.getCredentials()[index].setSignature(subIndex, sig);
  }

  addSignature(sig: Uint8Array) {
    const unsignedHash = sha256(this.toBytes());
    const publicKey = secp256k1.recoverPublicKey(unsignedHash, sig);
    this.addSignatureForPubKey(sig, publicKey);
  }

  private addSignatureForPubKey(sig: Uint8Array, publicKey: Uint8Array) {
    const coordinates = this.getSigIndicesForPubKey(publicKey);
    if (coordinates) {
      coordinates.forEach(([index, subIndex]) => {
        this.addSignatureAt(sig, index, subIndex);
      });
    }
  }

  protected publicKeyBytesToAddress(pubKey: Uint8Array) {
    return secp256k1.publicKeyBytesToAddress(pubKey);
  }

  hasAllSignatures() {
    const allSigsHex = this.credentials.map((cred) => cred.getSignatures());
    const emptySignatureHex = emptySignature.toString();
    const unsignedHash = sha256(this.toBytes());

    const hasNoPlaceholders = allSigsHex.every((cred) => {
      return cred.every((sig) => {
        return sig !== emptySignatureHex;
      });
    });

    if (!hasNoPlaceholders) return false;
    let valid = true;

    this.addressMaps.forEach((coordinates) => {
      coordinates.forEach(([index, subIndex]) => {
        const sig = allSigsHex[index]?.[subIndex];
        if (!sig) {
          throw new Error('error: incorrect structure for credentials');
        }
        const sigBytes = hexToBuffer(sig);
        const publicKey = secp256k1.recoverPublicKey(unsignedHash, sigBytes);
        if (!this.hasPubkey(publicKey)) {
          valid = false;
        }
      });
    }, true);
    return valid;
  }

  getVM() {
    return this.tx.getVM();
  }
}

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


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