PHP WebShell

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

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

import * as AvaxpLib from '../../src/lib';
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { AvaxP, TavaxP } from '../../src/';
import { randomBytes } from 'crypto';
import * as should from 'should';
import { BitGoAPI } from '@bitgo/sdk-api';
import { coins } from '@bitgo/statics';
import * as testData from '../resources/avaxp';
import { keychains } from '../resources/keychains';
import { Utils as KeyPairUtils } from '../../src/lib/utils';
import { KeyPair } from '../../src/lib';
import { Buffer as BufferAvax } from 'avalanche';
import * as _ from 'lodash';

import { HalfSignedAccountTransaction, TransactionType } from '@bitgo/sdk-core';
import { IMPORT_P } from '../resources/tx/importP';
import {
  ADDVALIDATOR_SAMPLES,
  EXPORT_P_2_C,
  EXPORT_P_2_C_VERIFY,
  EXPORT_P_2_C_WITHOUT_CHANGEOUTPUT,
} from '../resources/avaxp';
import { IMPORT_C } from '../resources/tx/importC';
import { EXPORT_C } from '../resources/tx/exportC';
import assert from 'assert';

describe('Avaxp', function () {
  const coinName = 'avaxp';
  const tcoinName = 't' + coinName;
  let bitgo: TestBitGoAPI;
  let basecoin;
  let newTxPrebuild;
  let newTxParams;

  const txPrebuild = {
    txHex: testData.ADDVALIDATOR_SAMPLES.unsignedTxHex,
    txInfo: {},
  };

  const txParams = {
    recipients: [],
    type: 'AddValidator',
    stakingOptions: {
      startTime: testData.ADDVALIDATOR_SAMPLES.startTime,
      endTime: testData.ADDVALIDATOR_SAMPLES.endTime,
      nodeID: testData.ADDVALIDATOR_SAMPLES.nodeID,
      amount: testData.ADDVALIDATOR_SAMPLES.minValidatorStake,
      delegationFeeRate: testData.ADDVALIDATOR_SAMPLES.delegationFee,
    },
    locktime: 0,
  };

  before(function () {
    bitgo = TestBitGo.decorate(BitGoAPI, {
      env: 'mock',
    });
    bitgo.initializeTestVars();
    bitgo.safeRegister(coinName, AvaxP.createInstance);
    bitgo.safeRegister(tcoinName, TavaxP.createInstance);
    basecoin = bitgo.coin(tcoinName);
    newTxPrebuild = () => {
      return _.cloneDeep(txPrebuild);
    };
    newTxParams = () => {
      return _.cloneDeep(txParams);
    };
  });

  it('should instantiate the coin', function () {
    let localBasecoin = bitgo.coin(tcoinName);
    localBasecoin.should.be.an.instanceof(TavaxP);

    localBasecoin = bitgo.coin(coinName);
    localBasecoin.should.be.an.instanceof(AvaxP);
  });

  it('should return ' + tcoinName, function () {
    basecoin.getChain().should.equal(tcoinName);
  });

  it('should return full name', function () {
    basecoin.getFullName().should.equal('Testnet Avalanche P-Chain');
  });

  describe('Keypairs:', () => {
    it('should generate a keypair from random seed', function () {
      const keyPair = basecoin.generateKeyPair();
      keyPair.should.have.property('pub');
      keyPair.should.have.property('prv');
    });

    it('should generate a keypair from a seed', function () {
      const seedText = testData.SEED_ACCOUNT.seed;
      const seed = Buffer.from(seedText, 'hex');
      const keyPair = basecoin.generateKeyPair(seed);
      keyPair.pub.should.equal(testData.SEED_ACCOUNT.publicKey);
      keyPair.prv.should.equal(testData.SEED_ACCOUNT.privateKey);
    });

    it('should validate a public key', function () {
      const keyPair = basecoin.generateKeyPair();
      keyPair.should.have.property('pub');
      keyPair.should.have.property('prv');

      basecoin.isValidPub(keyPair.pub).should.equal(true);
    });

    it('should validate a private key', function () {
      const keyPair = basecoin.generateKeyPair();
      keyPair.should.have.property('pub');
      keyPair.should.have.property('prv');

      basecoin.isValidPrv(keyPair.prv).should.equal(true);
    });
  });

  describe('Sign Transaction', () => {
    const factory = new AvaxpLib.TransactionBuilderFactory(coins.get(tcoinName));

    it('build and sign a transaction in regular mode', async () => {
      const recoveryMode = false;
      const txBuilder = new AvaxpLib.TransactionBuilderFactory(coins.get(tcoinName))
        .getValidatorBuilder()
        .threshold(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.threshold)
        .locktime(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.locktime)
        .recoverMode(recoveryMode)
        .fromPubKey(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.bitgoAddresses)
        .startTime(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.startTime)
        .endTime(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.endTime)
        .stakeAmount(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.stakeAmount)
        .delegationFeeRate(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.delegationFeeRate)
        .nodeID(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.nodeId)
        .utxos(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.utxos);
      const tx = await txBuilder.build();

      let txHex = tx.toBroadcastFormat();
      txHex.should.equal(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.unsignedRawTxNonRecovery);

      const privateKey = recoveryMode
        ? testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.backupPrivateKey
        : testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.userPrivateKey;

      const params = {
        txPrebuild: {
          txHex: tx.toBroadcastFormat(),
        },
        prv: privateKey,
      };

      const halfSignedTransaction = await basecoin.signTransaction(params);
      txHex = (halfSignedTransaction as HalfSignedAccountTransaction)?.halfSigned?.txHex;
      txHex.should.equal(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.halfSignedRawTxNonRecovery);
    });
    it('build and sign a transaction in recovery mode', async () => {
      const recoveryMode = true;
      const txBuilder = new AvaxpLib.TransactionBuilderFactory(coins.get(tcoinName))
        .getValidatorBuilder()
        .threshold(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.threshold)
        .locktime(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.locktime)
        .recoverMode(recoveryMode)
        .fromPubKey(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.bitgoAddresses)
        .startTime(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.startTime)
        .endTime(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.endTime)
        .stakeAmount(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.stakeAmount)
        .delegationFeeRate(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.delegationFeeRate)
        .nodeID(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.nodeId)
        .utxos(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.utxos);
      const tx = await txBuilder.build();

      let txHex = tx.toBroadcastFormat();
      txHex.should.equal(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.unsignedRawtxRecovery);

      const privateKey = recoveryMode
        ? testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.backupPrivateKey
        : testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.userPrivateKey;

      const params = {
        txPrebuild: {
          txHex: tx.toBroadcastFormat(),
        },
        prv: privateKey,
      };

      const halfSignedTransaction = await basecoin.signTransaction(params);
      txHex = (halfSignedTransaction as HalfSignedAccountTransaction)?.halfSigned?.txHex;
      txHex.should.equal(testData.BUILD_AND_SIGN_ADD_VALIDATOR_SAMPLE.halfSignedRawTxRecovery);
    });

    it('should be rejected if invalid key', async () => {
      const invalidPrivateKey = 'AAAAA';
      const builder = factory.from(testData.ADDVALIDATOR_SAMPLES.unsignedTxHex);

      const tx = await builder.build();
      const params = {
        txPrebuild: {
          txHex: tx.toBroadcastFormat(),
        },
        prv: invalidPrivateKey,
      };

      await basecoin.signTransaction(params).should.be.rejected();
    });
    it('should return the same mainnet address', () => {
      const utils = new KeyPairUtils();
      const xprv = testData.SEED_ACCOUNT.xPrivateKey;
      const kp1 = new KeyPair({ prv: xprv });
      const addressBuffer1 = kp1.getAddressBuffer();
      const address1 = utils.addressToString('avax', 'P', BufferAvax.from(addressBuffer1));

      const kp2 = new KeyPair({ prv: xprv });
      const addressBuffer2 = kp2.getAddressSafeBuffer();
      const address2 = utils.addressToString('avax', 'P', BufferAvax.from(addressBuffer2));

      const kp3 = new KeyPair({ prv: xprv });
      const address3 = kp3.getAvaxPAddress('avax');

      address1.should.equal(address2);
      address1.should.equal(address3);
    });
    it('should return the same testnet address', () => {
      const utils = new KeyPairUtils();
      const xprv = testData.SEED_ACCOUNT.xPrivateKey;
      const kp1 = new KeyPair({ prv: xprv });
      const addressBuffer1 = kp1.getAddressBuffer();
      const address1 = utils.addressToString('fuji', 'P', BufferAvax.from(addressBuffer1));

      const kp2 = new KeyPair({ prv: xprv });
      const addressBuffer2 = kp2.getAddressSafeBuffer();
      const address2 = utils.addressToString('fuji', 'P', BufferAvax.from(addressBuffer2));

      const kp3 = new KeyPair({ prv: xprv });
      const address3 = kp3.getAvaxPAddress('fuji');

      address1.should.equal(address2);
      address1.should.equal(address3);
    });
    it('should not be the same address from same key', () => {
      const utils = new KeyPairUtils();
      const kp1 = new KeyPair({ prv: testData.ACCOUNT_1.privkey });
      const addressBuffer1 = kp1.getAddressBuffer();
      const address1 = utils.addressToString('avax', 'P', BufferAvax.from(addressBuffer1));

      const kp2 = new KeyPair({ prv: testData.ACCOUNT_1.privkey });
      const addressBuffer2 = kp2.getAddressSafeBuffer();
      const address2 = utils.addressToString('fuji', 'P', BufferAvax.from(addressBuffer2));

      address1.should.not.equal(address2);
    });
    it('should not be the same address from different keys', () => {
      const utils = new KeyPairUtils();
      const kp1 = new KeyPair({ prv: testData.ACCOUNT_1.privkey });
      const addressBuffer1 = kp1.getAddressBuffer();
      const address1 = utils.addressToString('avax', 'P', BufferAvax.from(addressBuffer1));

      const kp2 = new KeyPair({ prv: testData.ACCOUNT_3.privkey });
      const addressBuffer2 = kp2.getAddressSafeBuffer();
      const address2 = utils.addressToString('avax', 'P', BufferAvax.from(addressBuffer2));

      address1.should.not.equal(address2);
    });
  });

  describe('Sign Message', () => {
    it('should be performed', async () => {
      const keyPairToSign = new AvaxpLib.KeyPair();
      const prvKey = keyPairToSign.getPrivateKey();
      const keyPair = keyPairToSign.getKeys();
      const messageToSign = Buffer.from(randomBytes(32));
      const signature = await basecoin.signMessage(keyPair, messageToSign.toString('hex'));

      const verify = AvaxpLib.Utils.verifySignature(basecoin._staticsCoin.network, messageToSign, signature, prvKey!);
      verify.should.be.true();
    });

    it('should fail with missing private key', async () => {
      const keyPair = new AvaxpLib.KeyPair({
        pub: testData.SEED_ACCOUNT.publicKeyCb58,
      }).getKeys();
      const messageToSign = Buffer.from(randomBytes(32)).toString('hex');
      await basecoin.signMessage(keyPair, messageToSign).should.be.rejectedWith('Invalid key pair options');
    });
  });

  describe('Explain Transaction', () => {
    it('should explain a half signed AddValidator transaction', async () => {
      const testData = ADDVALIDATOR_SAMPLES;
      const txExplain = await basecoin.explainTransaction({ halfSigned: { txHex: testData.halfsigntxHex } });
      txExplain.outputAmount.should.equal(testData.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddValidator);
      txExplain.outputs[0].address.should.equal(testData.nodeID);
      txExplain.changeOutputs[0].address.split('~').length.should.equal(3);
      should.not.exist(txExplain.memo);
    });

    it('should explain a signed AddValidator transaction', async () => {
      const testData = ADDVALIDATOR_SAMPLES;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.fullsigntxHex });
      txExplain.outputAmount.should.equal(testData.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddValidator);
      txExplain.outputs[0].address.should.equal(testData.nodeID);
      txExplain.changeOutputs[0].address.split('~').length.should.equal(3);
      should.not.exist(txExplain.memo);
    });

    // TODO(CR-1073): find unsiged, signed and half signed transactions for AddPermissionlessValidator
    it('should explain a half signed AddPermissionlessValidator transaction', async () => {
      const testData = ADDVALIDATOR_SAMPLES;
      const txExplain = await basecoin.explainTransaction({ halfSigned: { txHex: testData.halfsigntxHex } });
      txExplain.outputAmount.should.equal(testData.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddValidator);
      txExplain.outputs[0].address.should.equal(testData.nodeID);
      txExplain.changeOutputs[0].address.split('~').length.should.equal(3);
      should.not.exist(txExplain.memo);
    });

    it('should explain a signed AddPermissionlessValidator transaction', async () => {
      const testData = ADDVALIDATOR_SAMPLES;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.fullsigntxHex });
      txExplain.outputAmount.should.equal(testData.minValidatorStake);
      txExplain.type.should.equal(TransactionType.AddValidator);
      txExplain.outputs[0].address.should.equal(testData.nodeID);
      txExplain.changeOutputs[0].address.split('~').length.should.equal(3);
      should.not.exist(txExplain.memo);
    });

    it('should explain a half signed export transaction', async () => {
      const testData = EXPORT_P_2_C;
      const txExplain = await basecoin.explainTransaction({ halfSigned: { txHex: testData.halfsigntxHex } });
      txExplain.outputAmount.should.equal(testData.amount);
      txExplain.type.should.equal(TransactionType.Export);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      txExplain.changeOutputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      should.not.exist(txExplain.memo);
    });

    it('should explain a signed export transaction', async () => {
      const testData = EXPORT_P_2_C;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.fullsigntxHex });
      txExplain.outputAmount.should.equal(testData.amount);
      txExplain.type.should.equal(TransactionType.Export);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      txExplain.changeOutputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      should.not.exist(txExplain.memo);
    });

    it('should explain a half signed export transaction without cahngeoutput ', async () => {
      const testData = EXPORT_P_2_C_WITHOUT_CHANGEOUTPUT;
      const txExplain = await basecoin.explainTransaction({
        halfSigned: { txHex: testData.halfsigntxHex },
      });
      txExplain.outputAmount.should.equal(testData.amount);
      txExplain.type.should.equal(TransactionType.Export);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should explain a signed export transaction without cahngeoutput ', async () => {
      const testData = EXPORT_P_2_C_WITHOUT_CHANGEOUTPUT;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.fullsigntxHex });
      txExplain.outputAmount.should.equal(testData.amount);
      txExplain.type.should.equal(TransactionType.Export);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should explain a half signed import transaction', async () => {
      const testData = IMPORT_P;
      const txExplain = await basecoin.explainTransaction({
        halfSigned: { txHex: testData.halfsigntxHex },
      });
      txExplain.outputAmount.should.equal((Number(testData.amount) - txExplain.fee?.fee).toString());
      txExplain.type.should.equal(TransactionType.Import);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should explain a signed import transaction', async () => {
      const testData = IMPORT_P;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.fullsigntxHex });
      txExplain.outputAmount.should.equal((Number(testData.amount) - txExplain.fee?.fee).toString());
      txExplain.type.should.equal(TransactionType.Import);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.sort().join('~'));
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should explain a half signed import in C transaction', async () => {
      const testData = IMPORT_C;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.halfsigntxHex });
      txExplain.outputAmount.should.equal((Number(testData.amount) - txExplain.fee?.fee).toString());
      txExplain.type.should.equal(TransactionType.Import);
      txExplain.outputs[0].address.should.equal(testData.to);
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should explain a signed import in C transaction', async () => {
      const testData = IMPORT_C;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.fullsigntxHex });
      txExplain.outputAmount.should.equal((Number(testData.amount) - txExplain.fee?.fee).toString());
      txExplain.type.should.equal(TransactionType.Import);
      txExplain.outputs[0].address.should.equal(testData.to);
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should explain a unsigned export in C transaction', async () => {
      const testData = EXPORT_C;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.unsignedTxHex });
      txExplain.outputAmount.should.equal(Number(testData.amount).toString());
      txExplain.type.should.equal(TransactionType.Export);
      txExplain.inputs[0].address.should.equal(testData.cHexAddress);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.slice().sort().join('~'));
      txExplain.fee.feeRate.should.equal(Number(testData.fee));
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should explain a signed export in C transaction', async () => {
      const testData = EXPORT_C;
      const txExplain = await basecoin.explainTransaction({ txHex: testData.fullsigntxHex });
      txExplain.outputAmount.should.equal(Number(testData.amount).toString());
      txExplain.type.should.equal(TransactionType.Export);
      txExplain.inputs[0].address.should.equal(testData.cHexAddress);
      txExplain.outputs[0].address.should.equal(testData.pAddresses.slice().sort().join('~'));
      txExplain.fee.feeRate.should.equal(Number(testData.fee));
      txExplain.changeOutputs.should.be.empty();
      should.not.exist(txExplain.memo);
    });

    it('should fail when a tx is not passed as parameter', async () => {
      await basecoin.explainTransaction({}).should.be.rejectedWith('missing transaction hex');
    });
  });

  describe('Verify transaction', function () {
    it('should fail to verify P > C import transaction without recipients', async () => {
      const txPrebuild = {
        txHex: IMPORT_C.fullsigntxHex,
        txInfo: {},
      };
      const txParams = {
        recipients: [],
        type: 'Import',
        locktime: 0,
      };
      try {
        await basecoin.verifyTransaction({ txParams, txPrebuild });
      } catch (e) {
        e.message.should.equal('Expected 1 recipient in import transaction');
      }
    });

    it('should succeed to verify signed add validator transaction', async () => {
      const txPrebuild = {
        txHex: testData.ADDVALIDATOR_SAMPLES.fullsigntxHex,
        txInfo: {},
      };
      const txParams = newTxParams();
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should succeed to verify half signed add validator transaction', async () => {
      const txPrebuild = {
        txHex: testData.ADDVALIDATOR_SAMPLES.halfsigntxHex,
        txInfo: {},
      };
      const txParams = newTxParams();
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should succeed to verify unsigned add validator transaction', async () => {
      const txPrebuild = newTxPrebuild();
      const txParams = newTxParams();
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    // TODO(CR-1073): find unsiged, signed and half signed transactions for AddPermissionlessValidator
    it('should succeed to verify signed add permissionless validator transaction', async () => {
      const txPrebuild = {
        txHex: testData.ADDVALIDATOR_SAMPLES.fullsigntxHex,
        txInfo: {},
      };
      const txParams = newTxParams();
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should succeed to verify half signed add permissionless validator transaction', async () => {
      const txPrebuild = {
        txHex: testData.ADDVALIDATOR_SAMPLES.halfsigntxHex,
        txInfo: {},
      };
      const txParams = newTxParams();
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should succeed to verify unsigned add permissionless validator transaction', async () => {
      const txPrebuild = newTxPrebuild();
      const txParams = newTxParams();
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should succeed to verify add validator transactions when recipients has extra data ', async function () {
      const txPrebuild = newTxPrebuild();
      const txParams = newTxParams();
      txParams.data = 'data';

      const validTransaction = await basecoin.verifyTransaction({ txParams, txPrebuild });
      validTransaction.should.equal(true);
    });

    it('should succeed to verify import in C transaction', async () => {
      const txPrebuild = {
        txHex: IMPORT_C.fullsigntxHex,
        txInfo: {},
      };
      const txParams = {
        recipients: [
          {
            address: EXPORT_P_2_C_VERIFY.receiveAddress,
            amount: '1',
          },
        ],
        type: 'Import',
        locktime: 0,
      };
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should succeed to verify import to P transaction', async () => {
      const txPrebuild = {
        txHex: IMPORT_P.fullsigntxHex,
        txInfo: {},
      };
      const txParams = {
        recipients: [],
        type: 'Import',
        locktime: 0,
        unspents: ['e8ixKnba52yufXrTVKrTXVQTj5cd5e6o6Lc3rVkhahDGEs72L:0'],
      };
      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should fail to verify import to P transaction with wrong unspents', async () => {
      const txPrebuild = {
        txHex: IMPORT_P.fullsigntxHex,
        txInfo: {},
      };
      const txParams = {
        recipients: [],
        type: 'Import',
        locktime: 0,
        unspents: ['test:1'],
      };
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.be.rejectedWith(
          'Transaction should not contain the UTXO: e8ixKnba52yufXrTVKrTXVQTj5cd5e6o6Lc3rVkhahDGEs72L:0'
        );
    });

    it('should succeed to verify export transaction', async () => {
      const txPrebuild = {
        txHex: EXPORT_P_2_C_VERIFY.txHex,
        txInfo: {},
      };
      const txParams = {
        recipients: [
          {
            address: '',
            amount: EXPORT_P_2_C_VERIFY.amount,
          },
        ],
        type: 'Export',
        locktime: 0,
      };

      const isTransactionVerified = await basecoin.verifyTransaction({ txParams, txPrebuild });
      isTransactionVerified.should.equal(true);
    });

    it('should fail verify export transaction with wrong amount', async () => {
      const txPrebuild = {
        txHex: EXPORT_P_2_C_VERIFY.txHex,
        txInfo: {},
      };
      const txParams = {
        recipients: [
          {
            address: EXPORT_P_2_C_VERIFY.receiveAddress,
            amount: '9999999',
          },
        ],
        type: 'Export',
        locktime: 0,
      };

      await basecoin
        .verifyTransaction({ txParams, txPrebuild })
        .should.be.rejectedWith(
          `Tx total amount ${EXPORT_P_2_C_VERIFY.amount} does not match with expected total amount field 9999999 and max import fee 10000000`
        );
    });

    it('should fail verify transactions when have different type', async function () {
      const txParams = newTxParams();
      const txPrebuild = newTxPrebuild();
      txParams.type = 'addDelegator';
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.be.rejectedWith('Tx type does not match with expected txParams type');
    });

    it('should fail verify transactions when have different nodeId', async function () {
      const txParams = newTxParams();
      const txPrebuild = newTxPrebuild();
      txParams.stakingOptions.nodeID = 'NodeID-MdteS9U987PY7iwA5Pcz3sKVprJAbAvE7';
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.be.rejectedWith('Tx outputs does not match with expected txParams');
    });
    it('should fail verify when input `nodeId` is absent', async function () {
      const txPrebuild = newTxPrebuild();
      const txParams = newTxParams();
      txParams.stakingOptions.nodeID = undefined;
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.be.rejectedWith('Tx outputs does not match with expected txParams');
    });

    it('should fail verify transactions when have different amount', async function () {
      const txParams = newTxParams();
      const txPrebuild = newTxPrebuild();
      txParams.stakingOptions.amount = '2000000000';
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.be.rejectedWith('Tx outputs does not match with expected txParams');
    });

    it('should fail verify transactions when amount is number', async function () {
      const txParams = newTxParams();
      const txPrebuild = newTxPrebuild();
      txParams.stakingOptions.amount = 1000000000;
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.be.rejectedWith('Tx outputs does not match with expected txParams');
    });

    it('should fail verify transactions when amount is absent', async function () {
      const txParams = newTxParams();
      const txPrebuild = newTxPrebuild();
      txParams.stakingOptions.amount = undefined;
      await basecoin
        .verifyTransaction({
          txParams,
          txPrebuild,
        })
        .should.be.rejectedWith('Tx outputs does not match with expected txParams');
    });
  });

  describe('Validation', function () {
    it('should validate address', function () {
      const validAddress = 'P-fuji15jamwukfqkwhe8z26tjqxejtjd3jk9vj4kmxwa';
      basecoin.isValidAddress(validAddress).should.be.true();
    });

    it('should fail to validate invalid address', function () {
      const invalidAddresses = [undefined, '', 'asdadsaaf', '15x3z4rvk8e7vwa6g9lkyg89v5dwknp44858uex'];
      for (const address of invalidAddresses) {
        basecoin.isValidAddress(address).should.be.false();
      }
    });

    it('should validate an array address', function () {
      const validAddresses = [
        'P-fuji15x3z4rvk8e7vwa6g9lkyg89v5dwknp44858uex',
        'P-avax143q8lsy3y4ke9d6zeltre8u2ateed6uk9ka0nu',
        'NodeID-143q8lsy3y4ke9d6zeltre8u2ateed6uk9ka0nu',
      ];

      basecoin.isValidAddress(validAddresses).should.be.true();
    });

    it('should fail to validate an array address with invalid addresss', function () {
      const validAddresses = [
        'P-fuji15x3z4rvk8e7vwa6g9lkyg89v5dwknp44858uex',
        'P-avax143q8lsy3y4ke9d6zeltre8u2ateed6uk9ka0nu',
        'invalid-address',
      ];

      basecoin.isValidAddress(validAddresses).should.be.false();
    });

    it('should validate a multsig address string', function () {
      const multiSigValidAddress =
        'P-fuji1yzpfsdalhfwkq2ceewgs9wv7k0uft40ydpuj59~P-fuji103cmntssp6qnucejahddy42wcy4qty0uj42822~P-fuji1hdk7ntw0huhqmlhlheme9t7scsy9lhfhw3ywy4';
      basecoin.isValidAddress(multiSigValidAddress).should.be.true();
    });

    it('should fail to validate a multsig address string with invalid address', function () {
      const multiSigValidAddress =
        'P-fuji1yzpfsdalhfwkq2ceewgs9wv7k0uft40ydpuj59~invalid-address~P-fuji1hdk7ntw0huhqmlhlheme9t7scsy9lhfhw3ywy4';
      basecoin.isValidAddress(multiSigValidAddress).should.be.false();
    });

    it('should validate valid c-chain address', () => {
      const address = '0x1374a2046661f914d1687d85dbbceb9ac7910a29';
      basecoin.isValidAddress(address).should.be.true();
    });

    it('should throw when verifying address if address length doesnt match keychain length', async function () {
      const validAddresses = [
        {
          address: 'P-fuji15x3z4rvk8e7vwa6g9lkyg89v5dwknp44858uex',
          keychains,
        },
        {
          address: 'P-fuji1wq0d56pu54sgc5xpevm3ur6sf3l6kke70dz0l4',
          keychains,
        },
      ];

      for (const addressParams of validAddresses) {
        await assert.rejects(async () => basecoin.verifyAddress(addressParams));
      }
    });

    it('should fail to verify invalid address', async function () {
      const invalidAddresses = [
        {
          address: 'P-fuji103cmntssp6qnucejahddy42wcy4qty0uj42822',
          keychains,
        },
        {
          address: 'P-avax143q8lsy3y4ke9d6zeltre8u2ateed6uk9ka0nu',
          keychains,
        },
      ];

      for (const address of invalidAddresses) {
        await assert.rejects(async () => basecoin.verifyAddress(address));
      }
    });

    it('should successfully verify is wallet address', async function () {
      (
        await basecoin.isWalletAddress({
          address:
            'P-fuji15x3z4rvk8e7vwa6g9lkyg89v5dwknp44858uex~P-fuji1wq0d56pu54sgc5xpevm3ur6sf3l6kke70dz0l4~P-fuji1cjk4cvdfy6ffd4fh8umpnnrmjt0xdap02tcep6',
          keychains,
        })
      ).should.be.true();
    });

    it('should throw when address length and keychain length dont match', async function () {
      await assert.rejects(async () =>
        basecoin.isWalletAddress({
          address: 'P-fuji1wq0d56pu54sgc5xpevm3ur6sf3l6kke70dz0l4~P-fuji1cjk4cvdfy6ffd4fh8umpnnrmjt0xdap02tcep6',
          keychains,
        })
      );
    });

    it('should throw when keychain is not of length 3', async function () {
      await assert.rejects(async () =>
        basecoin.isWalletAddress({
          address: 'P-fuji1wq0d56pu54sgc5xpevm3ur6sf3l6kke70dz0l4',
          keychains: keychains[0],
        })
      );
    });
  });
});

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


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