PHP WebShell

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

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

import { BitGoAPI } from '@bitgo/sdk-api';
import { TestBitGo, TestBitGoAPI } from '@bitgo/sdk-test';
import { randomBytes } from 'crypto';
import should = require('should');
import { Dot, Tdot, KeyPair } from '../../src';
import * as testData from '../fixtures';
import { chainName, txVersion, genesisHash, specVersion } from '../resources';
import * as sinon from 'sinon';
import { Wallet } from '@bitgo/sdk-core';

describe('DOT:', function () {
  let bitgo: TestBitGoAPI;
  let basecoin;
  let prodCoin;

  before(function () {
    bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' });
    bitgo.safeRegister('dot', Dot.createInstance);
    bitgo.safeRegister('tdot', Tdot.createInstance);
    bitgo.initializeTestVars();
    basecoin = bitgo.coin('tdot');
    prodCoin = bitgo.coin('dot');
  });

  describe('Sign Message', () => {
    it('should be performed', async () => {
      const keyPair = new KeyPair();
      const messageToSign = Buffer.from(randomBytes(32)).toString('hex');
      const signature = await basecoin.signMessage(keyPair.getKeys(), messageToSign);
      keyPair.verifySignature(messageToSign, Uint8Array.from(signature)).should.equals(true);
    });

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

  describe('Sign transaction', () => {
    const transaction = {
      id: '0x19de156328eea66bd1ec45843569c168e0bb2f2898221029b403df3f23a5489d',
      sender: '5EGoFA95omzemRssELLDjVenNZ68aXyUeqtKQScXSEBvVJkr',
      referenceBlock: '0x149799bc9602cb5cf201f3425fb8d253b2d4e61fc119dcab3249f307f594754d',
      blockNumber: 3933,
      genesisHash: '0xe143f23803ac50e8f6f8e62695d1ce9e4e1d68aa36c1cd2cfd15340213f3423e',
      nonce: 200,
      specVersion: 9150,
      transactionVersion: 8,
      eraPeriod: 64,
      chainName: 'Westend',
      tip: 0,
      to: '5Ffp1wJCPu4hzVDTo7XaMLqZSvSadyUQmxWPDw74CBjECSoq',
      amount: '10000000000',
    };

    // TODO: BG-43197
    xit('should sign transaction', async function () {
      const signed = await basecoin.signTransaction({
        txPrebuild: {
          txHex: testData.rawTx.transfer.unsigned,
          transaction,
        },
        pubs: [testData.accounts.account1.publicKey],
        prv: testData.accounts.account1.secretKey,
      });
      signed.txHex.should.equal(testData.rawTx.transfer.signed);
    });

    // TODO: BG-43197
    xit('should fail to sign transaction with an invalid key', async function () {
      try {
        await basecoin.signTransaction({
          txPrebuild: {
            txHex: testData.rawTx.transfer.unsigned,
            transaction,
          },
          pubs: [testData.accounts.account2.publicKey],
          prv: testData.accounts.account1.secretKey,
        });
      } catch (e) {
        should.equal(e.message, 'Private key cannot sign the transaction');
      }
    });

    it('should fail to build transaction with missing params', async function () {
      try {
        await basecoin.signTransaction({
          txPrebuild: {
            txHex: testData.rawTx.transfer.unsigned,
            key: testData.accounts.account1.publicKey,
          },
          prv: testData.accounts.account1.secretKey,
        });
      } catch (e) {
        should.notEqual(e, null);
      }
    });
  });

  describe('Generate wallet key pair: ', () => {
    it('should generate key pair', () => {
      const kp = basecoin.generateKeyPair();
      basecoin.isValidPub(kp.pub).should.equal(true);
      basecoin.isValidPrv(kp.prv).should.equal(true);
    });

    it('should generate key pair from seed', () => {
      const seed = Buffer.from('9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60', 'hex');
      const kp = basecoin.generateKeyPair(seed);
      basecoin.isValidPub(kp.pub).should.equal(true);
      basecoin.isValidPrv(kp.prv).should.equal(true);
    });
  });

  describe('Balance Conversion', () => {
    it('should return 10000000000 as tdot base factor', () => {
      // mainnet uses 10 decimal places
      const baseFactor = prodCoin.getBaseFactor();
      baseFactor.should.equal(10000000000);
    });

    it('should return 1000000000000 as dot base factor', () => {
      // westend (test polkadot) uses 12 decimal places
      const baseFactor = basecoin.getBaseFactor();
      baseFactor.should.equal(1000000000000);
    });

    it('should return 4 Dot when base unit is 40000000000 for dot', () => {
      const bigUnit = prodCoin.baseUnitsToBigUnits('40000000000');
      bigUnit.should.equal('4');
    });

    it('should return 0.04 Dot when base unit is 400000000 for dot', () => {
      const bigUnit = prodCoin.baseUnitsToBigUnits('400000000');
      bigUnit.should.equal('0.04');
    });

    it('should return 4 test Dot when base unit is 4000000000000 for tdot', () => {
      const bigUnit = basecoin.baseUnitsToBigUnits('4000000000000');
      bigUnit.should.equal('4');
    });

    it('should return 0.04 test Dot when base unit is 400000000 for tdot', () => {
      const bigUnit = basecoin.baseUnitsToBigUnits('40000000000');
      bigUnit.should.equal('0.04');
    });
  });

  describe('Explain Transactions:', () => {
    it('should explain an unsigned transfer transaction', async function () {
      const explainedTransaction = await basecoin.explainTransaction(testData.unsignedTransaction);
      explainedTransaction.should.deepEqual({
        displayOrder: [
          'outputAmount',
          'changeAmount',
          'outputs',
          'changeOutputs',
          'fee',
          'type',
          'sequenceId',
          'id',
          'blockNumber',
        ],
        sequenceId: 0,
        fee: '10',
        id: '0x0e5751c026e543b2e8ab2eb06099daa1d1e5df47778f7787faab45cdf12fe3a8',
        type: '0',
        outputs: [
          {
            address: '5DkddSfPsWojjfuH9iJEcUV7ZseQ9EJ6RjtNmCR1w3CEb8S9',
            valueString: '90034235235350',
            coinName: 'tdot',
            wallet: '62a1205751675b2f0fe72328',
          },
        ],
        blockNumber: 8619307,
        outputAmount: 90034235235350,
        changeOutputs: [],
        changeAmount: '0',
      });
    });
  });

  describe('Recover Transactions:', () => {
    const sandBox = sinon.createSandbox();
    const destAddr = testData.accounts.account1.address;
    const nonce = 123;

    beforeEach(function () {
      const accountInfoCB = sandBox.stub(Dot.prototype, 'getAccountInfo' as keyof Dot);
      accountInfoCB.withArgs(testData.wrwUser.walletAddress0).resolves({
        nonce: nonce,
        freeBalance: 8888,
      });
      const headerInfoCB = sandBox.stub(Dot.prototype, 'getHeaderInfo' as keyof Dot);
      headerInfoCB.resolves({
        headerNumber: testData.westendBlock.blockNumber,
        headerHash: testData.westendBlock.hash,
      });
      accountInfoCB.withArgs('5Dtg2zKjVEL8p9keSM4dQ7nD26sVtMfCsyxkhvZZ9fqBbhw6').resolves({
        nonce: 0,
        freeBalance: 1510000000000,
      });
      const getFeeCB = sandBox.stub(Dot.prototype, 'getFee' as keyof Dot);
      getFeeCB.withArgs(destAddr, testData.wrwUser.walletAddress0, 1510000000000).resolves(15783812856);
    });

    afterEach(function () {
      sandBox.restore();
    });

    it('should generate unsigned sweep correctly', async function () {
      const commonKeychain =
        '3cd14f5d60744287cd3a50510e2964746b6feaad4b2300088eaae60d1a35f0abc518534d43b2614370d9a263aadb57edb5d0b78f816a519cd5896e7352920b67';
      const recoveryDestination = '5GPAveMvmDsjxVT6Q2xiu5kYPbFAmdebHaiNZK14FBaSaAKh';

      const unsigned = await basecoin.recover({ bitgoKey: commonKeychain, recoveryDestination });
      unsigned.txRequests.should.not.be.undefined();
      unsigned.txRequests.length.should.equal(1);
      unsigned.txRequests[0].transactions.length.should.equal(1);
      unsigned.txRequests[0].walletCoin.should.equal('tdot');
      unsigned.txRequests[0].transactions[0].unsignedTx.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.serializedTx.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.scanIndex.should.equal(0);
      unsigned.txRequests[0].transactions[0].unsignedTx.coin.should.equal('tdot');
      unsigned.txRequests[0].transactions[0].unsignedTx.signableHex.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.derivationPath.should.equal('m/0');
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.inputs.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.inputs.length.should.equal(1);
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.inputs[0].address.should.equal(
        '5Dtg2zKjVEL8p9keSM4dQ7nD26sVtMfCsyxkhvZZ9fqBbhw6'
      );
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.inputs[0].valueString.should.equal('1510000000000');
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.inputs[0].value.should.equal(1510000000000);
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.outputs.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.outputs.length.should.equal(1);
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.outputs[0].address.should.equal(
        '5GPAveMvmDsjxVT6Q2xiu5kYPbFAmdebHaiNZK14FBaSaAKh'
      );
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.outputs[0].valueString.should.equal('1510000000000');
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.outputs[0].coinName.should.equal('tdot');
      unsigned.txRequests[0].transactions[0].unsignedTx.parsedTx.type.should.equal('');
      unsigned.txRequests[0].transactions[0].unsignedTx.feeInfo.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.feeInfo.fee.should.equal(0);
      unsigned.txRequests[0].transactions[0].unsignedTx.feeInfo.feeString.should.equal('0');
      unsigned.txRequests[0].transactions[0].unsignedTx.coinSpecific.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.coinSpecific.firstValid.should.not.be.undefined();
      unsigned.txRequests[0].transactions[0].unsignedTx.coinSpecific.maxDuration.should.equal(2400);
      unsigned.txRequests[0].transactions[0].unsignedTx.coinSpecific.commonKeychain.should.equal(
        '3cd14f5d60744287cd3a50510e2964746b6feaad4b2300088eaae60d1a35f0abc518534d43b2614370d9a263aadb57edb5d0b78f816a519cd5896e7352920b67'
      );
    });

    // TODO(COIN-919): Fix failing test
    xit('should take OVC output and generate a signed sweep transaction', async function () {
      const params = testData.ovcResponse;

      const recoveryTxn = await basecoin.createBroadcastableSweepTransaction(params);
      recoveryTxn.transactions[0].serializedTx.should.equal(
        '0x2d02840050d1e116cdb32e61ba3ece275b620f503f0c5ae4e7690d9f9aa7e0b50303976c00057c131cde2be39f4ff23eeba11139c46dd8c6d69ff517abede37381d8d24f0bb30fd8805b2e51a9e2a75338d7fb7edb290be9638debedbfc001d10b8132be0b2b4a0400040400bf0678d312b2b2c7effd2b1f214ee13928d339391d5f5f7056608aa0d32b9edf01'
      );
      recoveryTxn.transactions[0].scanIndex.should.equal(0);
      recoveryTxn.lastScanIndex.should.equal(0);
    });

    // TODO(COIN-919): Fix failing test
    xit('should take consolidation OVC output and generate multiple signed sweep transactions', async function () {
      const params = testData.ovcResponse2;

      const recoveryTxn = await basecoin.createBroadcastableSweepTransaction(params);
      recoveryTxn.transactions[0].serializedTx.should.equal(
        '0x2d028400f053d177371f4919b71017421aa34841ac87c926a14e8a7e75f092693665cb4a009871feb8389e12191b460ebea1f9e1716ada8a31d94ac08f5c15cb5f7be24683cdfc300ae3e6802c6f4b044dd92ea03d15b92399ab45803e43beb5cbff582a031b37000004040050d1e116cdb32e61ba3ece275b620f503f0c5ae4e7690d9f9aa7e0b50303976c01'
      );
      recoveryTxn.transactions[0].scanIndex.should.equal(1);
      recoveryTxn.transactions[1].serializedTx.should.equal(
        '0x2d028400cf7ed9f536373c8f874e780a4269ec1bd6799ed7d4a854c670b0c72805fac87600d153cbb683706ddf320f7b225af14ce4b59b868c07440aefa4c4e1297e49bcd5f6ee84f696b0c2e456e2a7f8779a78a2c832dfb47d5b283eba8408b003c7dd031b37000004040050d1e116cdb32e61ba3ece275b620f503f0c5ae4e7690d9f9aa7e0b50303976c01'
      );
      recoveryTxn.transactions[1].scanIndex.should.equal(2);
      recoveryTxn.transactions[2].serializedTx.should.equal(
        '0x2d0284009482ad7e43b40b7df3383244daafed32b3b9fd7541016b5c907f2d9052f85f8600e255698d110977faf33efb3336ba98ad7eaa2a1a926487fc696a277442d4414b2a441ba92d598f2b1f96f13b28785f5e572984035a608d63c48bd35ba955090e1b37000004040050d1e116cdb32e61ba3ece275b620f503f0c5ae4e7690d9f9aa7e0b50303976c01'
      );
      recoveryTxn.transactions[2].scanIndex.should.equal(3);
      recoveryTxn.lastScanIndex.should.equal(20);
    });

    // TODO(COIN-919): Fix failing test
    xit('should recover a txn for non-bitgo recoveries', async function () {
      const res = await basecoin.recover({
        userKey: testData.wrwUser.userKey,
        backupKey: testData.wrwUser.backupKey,
        bitgoKey: testData.wrwUser.bitgoKey,
        walletPassphrase: testData.wrwUser.walletPassphrase,
        recoveryDestination: destAddr,
      });
      res.should.not.be.empty();
      res.should.hasOwnProperty('serializedTx');
      res.should.hasOwnProperty('scanIndex');
      sandBox.assert.calledOnce(basecoin.getAccountInfo);
      sandBox.assert.calledOnce(basecoin.getHeaderInfo);

      // deserialize the txn and verify the fields are what we expect
      const txBuilder = basecoin.getBuilder().from(res.serializedTx);
      // some information isn't deserialized by the from method, so we will
      // supply it again in order to re-build the txn
      txBuilder
        .validity({
          firstValid: testData.westendBlock.blockNumber,
          maxDuration: basecoin.SWEEP_TXN_DURATION,
        })
        .referenceBlock(testData.westendBlock.hash);
      const tx = await txBuilder.build();
      const txJson = tx.toJson();
      should.deepEqual(txJson.sender, testData.wrwUser.walletAddress0);
      should.deepEqual(txJson.blockNumber, testData.westendBlock.blockNumber);
      should.deepEqual(txJson.referenceBlock, testData.westendBlock.hash);
      should.deepEqual(txJson.genesisHash, genesisHash);
      should.deepEqual(txJson.specVersion, specVersion);
      should.deepEqual(txJson.nonce, nonce);
      should.deepEqual(txJson.tip, 0);
      should.deepEqual(txJson.transactionVersion, txVersion);
      should.deepEqual(txJson.chainName, chainName);
      should.deepEqual(txJson.eraPeriod, basecoin.SWEEP_TXN_DURATION);
    });

    it('should recover a txn for unsigned-sweep recoveries', async function () {
      const res = await basecoin.recover({
        bitgoKey: testData.wrwUser.bitgoKey,
        recoveryDestination: destAddr,
      });
      res.should.not.be.empty();
      res.txRequests[0].transactions[0].unsignedTx.should.hasOwnProperty('serializedTx');
      res.txRequests[0].transactions[0].unsignedTx.should.hasOwnProperty('scanIndex');
      sandBox.assert.calledOnce(basecoin.getAccountInfo);
      sandBox.assert.calledOnce(basecoin.getHeaderInfo);

      // deserialize the txn and verify the fields are what we expect
      const txBuilder = basecoin.getBuilder().from(res.txRequests[0].transactions[0].unsignedTx.serializedTx);
      // some information isn't deserialized by the from method, so we will
      // supply it again in order to re-build the txn
      txBuilder
        .validity({
          firstValid: testData.westendBlock.blockNumber,
          maxDuration: basecoin.SWEEP_TXN_DURATION,
        })
        .referenceBlock(testData.westendBlock.hash)
        .sender({ address: testData.wrwUser.walletAddress0 });
      const tx = await txBuilder.build();
      const txJson = tx.toJson();
      should.deepEqual(txJson.sender, testData.wrwUser.walletAddress0);
      should.deepEqual(txJson.blockNumber, testData.westendBlock.blockNumber);
      should.deepEqual(txJson.referenceBlock, testData.westendBlock.hash);
      should.deepEqual(txJson.genesisHash, genesisHash);
      should.deepEqual(txJson.specVersion, specVersion);
      should.deepEqual(txJson.nonce, nonce);
      should.deepEqual(txJson.tip, 0);
      should.deepEqual(txJson.transactionVersion, txVersion);
      should.deepEqual(txJson.chainName, chainName);
      should.deepEqual(txJson.eraPeriod, basecoin.SWEEP_TXN_DURATION);
    });
  });

  describe('Build Consolidation Recoveries:', () => {
    const sandBox = sinon.createSandbox();
    const baseAddr = testData.consolidationWrwUser.walletAddress0;
    const nonce = 123;

    beforeEach(function () {
      const accountInfoCB = sandBox.stub(Dot.prototype, 'getAccountInfo' as keyof Dot);
      accountInfoCB.withArgs(testData.consolidationWrwUser.walletAddress1).resolves({
        nonce: nonce,
        freeBalance: 10000000000,
      });
      accountInfoCB.withArgs(testData.consolidationWrwUser.walletAddress2).resolves({
        nonce: nonce,
        freeBalance: 1510000000000,
      });
      accountInfoCB.withArgs(testData.consolidationWrwUser.walletAddress3).resolves({
        nonce: nonce,
        freeBalance: 1510000000000,
      });
      const headerInfoCB = sandBox.stub(Dot.prototype, 'getHeaderInfo' as keyof Dot);
      headerInfoCB.resolves({
        headerNumber: testData.westendBlock.blockNumber,
        headerHash: testData.westendBlock.hash,
      });
      const getFeeCB = sandBox.stub(Dot.prototype, 'getFee' as keyof Dot);
      getFeeCB.withArgs(baseAddr, testData.consolidationWrwUser.walletAddress1, 10000000000).resolves(15783812856);
      getFeeCB.withArgs(baseAddr, testData.consolidationWrwUser.walletAddress2, 1510000000000).resolves(15783812856);
      getFeeCB.withArgs(baseAddr, testData.consolidationWrwUser.walletAddress3, 1510000000000).resolves(15783812856);
    });

    afterEach(function () {
      sandBox.restore();
    });

    // TODO(COIN-919): Fix failing test
    xit('should build signed consolidation recoveries', async function () {
      const res = await basecoin.recoverConsolidations({
        userKey: testData.consolidationWrwUser.userKey,
        backupKey: testData.consolidationWrwUser.backupKey,
        bitgoKey: testData.consolidationWrwUser.bitgoKey,
        walletPassphrase: testData.consolidationWrwUser.walletPassphrase,
        startingScanIndex: 1,
        endingScanIndex: 4,
      });
      res.should.not.be.empty();
      res.transactions.length.should.equal(2);
      sandBox.assert.calledThrice(basecoin.getAccountInfo);
      sandBox.assert.calledTwice(basecoin.getHeaderInfo);

      const txn1 = res.transactions[0];
      txn1.should.hasOwnProperty('serializedTx');
      txn1.should.hasOwnProperty('scanIndex');
      txn1.scanIndex.should.equal(2);
      // deserialize the txn and verify the fields are what we expect
      const txBuilder1 = basecoin.getBuilder().from(txn1.serializedTx);
      // some information isn't deserialized by the from method, so we will
      // supply it again in order to re-build the txn
      txBuilder1
        .validity({
          firstValid: testData.westendBlock.blockNumber,
          maxDuration: basecoin.MAX_VALIDITY_DURATION,
        })
        .referenceBlock(testData.westendBlock.hash);
      const tx1 = await txBuilder1.build();
      const txJson1 = tx1.toJson();
      should.deepEqual(txJson1.sender, testData.consolidationWrwUser.walletAddress2);
      should.deepEqual(txJson1.blockNumber, testData.westendBlock.blockNumber);
      should.deepEqual(txJson1.referenceBlock, testData.westendBlock.hash);
      should.deepEqual(txJson1.genesisHash, genesisHash);
      should.deepEqual(txJson1.specVersion, specVersion);
      should.deepEqual(txJson1.nonce, nonce);
      should.deepEqual(txJson1.tip, 0);
      should.deepEqual(txJson1.transactionVersion, txVersion);
      should.deepEqual(txJson1.chainName, chainName);
      // eraPeriod will always round to the next upper power of 2 for any input value, in this case 2400.
      // 4096 is the "highest" value you can set, but the txn still may fail after 2400 blocks.
      const eraPeriod = 4096;
      should.deepEqual(txJson1.eraPeriod, eraPeriod);
      should.deepEqual(txJson1.to, baseAddr);

      res.lastScanIndex.should.equal(3);
      const txn2 = res.transactions[1];
      txn2.should.hasOwnProperty('serializedTx');
      txn2.should.hasOwnProperty('scanIndex');
      txn2.scanIndex.should.equal(3);
      // deserialize the txn and verify the fields are what we expect
      const txBuilder2 = basecoin.getBuilder().from(txn2.serializedTx);
      // some information isn't deserialized by the from method, so we will
      // supply it again in order to re-build the txn
      txBuilder2
        .validity({
          firstValid: testData.westendBlock.blockNumber,
          maxDuration: basecoin.MAX_VALIDITY_DURATION,
        })
        .referenceBlock(testData.westendBlock.hash);
      const tx2 = await txBuilder2.build();
      const txJson2 = tx2.toJson();
      should.deepEqual(txJson2.sender, testData.consolidationWrwUser.walletAddress3);
      should.deepEqual(txJson2.blockNumber, testData.westendBlock.blockNumber);
      should.deepEqual(txJson2.referenceBlock, testData.westendBlock.hash);
      should.deepEqual(txJson2.genesisHash, genesisHash);
      should.deepEqual(txJson2.specVersion, specVersion);
      should.deepEqual(txJson2.nonce, nonce);
      should.deepEqual(txJson2.tip, 0);
      should.deepEqual(txJson2.transactionVersion, txVersion);
      should.deepEqual(txJson2.chainName, chainName);
      should.deepEqual(txJson2.eraPeriod, eraPeriod);
      should.deepEqual(txJson2.to, baseAddr);
    });

    it('should build unsigned consolidation recoveries', async function () {
      const res = await basecoin.recoverConsolidations({
        bitgoKey: testData.consolidationWrwUser.bitgoKey,
        startingScanIndex: 1,
        endingScanIndex: 4,
      });
      res.should.not.be.empty();
      res.txRequests.length.should.equal(2);
      sandBox.assert.calledThrice(basecoin.getAccountInfo);
      sandBox.assert.calledTwice(basecoin.getHeaderInfo);

      const txn1 = res.txRequests[0].transactions[0].unsignedTx;
      txn1.should.hasOwnProperty('serializedTx');
      txn1.should.hasOwnProperty('signableHex');
      txn1.should.hasOwnProperty('scanIndex');
      txn1.scanIndex.should.equal(2);
      txn1.should.hasOwnProperty('coin');
      txn1.coin.should.equal('tdot');
      txn1.should.hasOwnProperty('derivationPath');
      txn1.derivationPath.should.equal('m/2');

      txn1.should.hasOwnProperty('coinSpecific');
      const coinSpecific1 = txn1.coinSpecific;
      coinSpecific1.should.hasOwnProperty('commonKeychain');
      coinSpecific1.should.hasOwnProperty('firstValid');
      coinSpecific1.firstValid.should.equal(testData.westendBlock.blockNumber);
      coinSpecific1.should.hasOwnProperty('maxDuration');
      coinSpecific1.maxDuration.should.equal(basecoin.MAX_VALIDITY_DURATION);

      // deserialize the txn and verify the fields are what we expect
      const txBuilder1 = basecoin.getBuilder().from(txn1.serializedTx);
      // some information isn't deserialized by the from method, so we will
      // supply it again in order to re-build the txn
      txBuilder1
        .validity({
          firstValid: testData.westendBlock.blockNumber,
          maxDuration: basecoin.MAX_VALIDITY_DURATION,
        })
        .referenceBlock(testData.westendBlock.hash)
        .sender({ address: testData.consolidationWrwUser.walletAddress2 });
      const tx1 = await txBuilder1.build();
      const txJson1 = tx1.toJson();
      should.deepEqual(txJson1.sender, testData.consolidationWrwUser.walletAddress2);
      should.deepEqual(txJson1.blockNumber, testData.westendBlock.blockNumber);
      should.deepEqual(txJson1.referenceBlock, testData.westendBlock.hash);
      should.deepEqual(txJson1.genesisHash, genesisHash);
      should.deepEqual(txJson1.specVersion, specVersion);
      should.deepEqual(txJson1.nonce, nonce);
      should.deepEqual(txJson1.tip, 0);
      should.deepEqual(txJson1.transactionVersion, txVersion);
      should.deepEqual(txJson1.chainName, chainName);
      // eraPeriod will always round to the next upper power of 2 for any input value, in this case 2400.
      // 4096 is the "highest" value you can set, but the txn still may fail after 2400 blocks.
      const eraPeriod = 4096;
      should.deepEqual(txJson1.eraPeriod, eraPeriod);
      should.deepEqual(txJson1.to, baseAddr);

      const txn2 = res.txRequests[1].transactions[0].unsignedTx;
      txn2.should.hasOwnProperty('serializedTx');
      txn2.should.hasOwnProperty('signableHex');
      txn2.should.hasOwnProperty('scanIndex');
      txn2.scanIndex.should.equal(3);
      txn2.should.hasOwnProperty('coin');
      txn2.coin.should.equal('tdot');
      txn2.should.hasOwnProperty('derivationPath');
      txn2.derivationPath.should.equal('m/3');

      txn2.should.hasOwnProperty('coinSpecific');
      const coinSpecific2 = txn2.coinSpecific;
      coinSpecific2.should.hasOwnProperty('commonKeychain');
      coinSpecific2.should.hasOwnProperty('firstValid');
      coinSpecific2.firstValid.should.equal(testData.westendBlock.blockNumber);
      coinSpecific2.should.hasOwnProperty('maxDuration');
      coinSpecific2.maxDuration.should.equal(basecoin.MAX_VALIDITY_DURATION);
      coinSpecific2.should.hasOwnProperty('commonKeychain');
      coinSpecific2.should.hasOwnProperty('lastScanIndex');
      coinSpecific2.lastScanIndex.should.equal(3);

      // deserialize the txn and verify the fields are what we expect
      const txBuilder2 = basecoin.getBuilder().from(txn2.serializedTx);
      // some information isn't deserialized by the from method, so we will
      // supply it again in order to re-build the txn
      txBuilder2
        .validity({
          firstValid: testData.westendBlock.blockNumber,
          maxDuration: basecoin.MAX_VALIDITY_DURATION,
        })
        .referenceBlock(testData.westendBlock.hash)
        .sender({ address: testData.consolidationWrwUser.walletAddress3 });
      const tx2 = await txBuilder2.build();
      const txJson2 = tx2.toJson();
      should.deepEqual(txJson2.sender, testData.consolidationWrwUser.walletAddress3);
      should.deepEqual(txJson2.blockNumber, testData.westendBlock.blockNumber);
      should.deepEqual(txJson2.referenceBlock, testData.westendBlock.hash);
      should.deepEqual(txJson2.genesisHash, genesisHash);
      should.deepEqual(txJson2.specVersion, specVersion);
      should.deepEqual(txJson2.nonce, nonce);
      should.deepEqual(txJson2.tip, 0);
      should.deepEqual(txJson2.transactionVersion, txVersion);
      should.deepEqual(txJson2.chainName, chainName);
      should.deepEqual(txJson2.eraPeriod, eraPeriod);
      should.deepEqual(txJson2.to, baseAddr);
    });

    it('should skip building consolidate transaction if balance is equal to zero', async function () {
      await basecoin
        .recoverConsolidations({
          userKey: testData.consolidationWrwUser.userKey,
          backupKey: testData.consolidationWrwUser.backupKey,
          bitgoKey: testData.consolidationWrwUser.bitgoKey,
          walletPassphrase: testData.consolidationWrwUser.walletPassphrase,
          startingScanIndex: 1,
          endingScanIndex: 2,
        })
        .should.rejectedWith('Did not find an address with funds to recover');
    });

    it('should throw if startingScanIndex is not ge to 1', async () => {
      await basecoin
        .recoverConsolidations({
          userKey: testData.consolidationWrwUser.userKey,
          backupKey: testData.consolidationWrwUser.backupKey,
          bitgoKey: testData.consolidationWrwUser.bitgoKey,
          startingScanIndex: -1,
        })
        .should.be.rejectedWith(
          'Invalid starting or ending index to scan for addresses. startingScanIndex: -1, endingScanIndex: 19.'
        );
    });

    it('should throw if scan factor is too high', async () => {
      await basecoin
        .recoverConsolidations({
          userKey: testData.consolidationWrwUser.userKey,
          backupKey: testData.consolidationWrwUser.backupKey,
          bitgoKey: testData.consolidationWrwUser.bitgoKey,
          startingScanIndex: 1,
          endingScanIndex: 300,
        })
        .should.be.rejectedWith(
          'Invalid starting or ending index to scan for addresses. startingScanIndex: 1, endingScanIndex: 300.'
        );
    });
  });

  describe('Recover Transaction Failures:', () => {
    const sandBox = sinon.createSandbox();
    const destAddr = testData.accounts.account1.address;
    const nonce = 123;

    beforeEach(function () {
      const accountInfoCB = sandBox.stub(Dot.prototype, 'getAccountInfo' as keyof Dot);
      accountInfoCB.resolves({
        nonce: nonce,
        freeBalance: 0,
      });
    });

    afterEach(function () {
      sandBox.restore();
    });

    it('should fail to recover due to not finding an address with funds', async function () {
      await basecoin
        .recover({
          userKey: testData.wrwUser.userKey,
          backupKey: testData.wrwUser.backupKey,
          bitgoKey: testData.wrwUser.bitgoKey,
          walletPassphrase: testData.wrwUser.walletPassphrase,
          recoveryDestination: destAddr,
        })
        .should.rejectedWith('Did not find address with funds to recover');
    });
  });

  describe('Verify Transaction', function () {
    const address1 = '5Ge59qRnZa8bxyhVFE6BDoY3kuhSrNVETRxXYLty1Hh6XTaf';
    const address2 = '5DiMLZugmcKEH3igPZP367FqummZkWeW5Z6zDCHLfxRjnPXe';
    it('should reject a txPrebuild with more than one recipient', async function () {
      const wallet = new Wallet(bitgo, basecoin, {});

      const txParams = {
        recipients: [
          { amount: '1000000000000', address: address1 },
          { amount: '2500000000000', address: address2 },
        ],
        wallet: wallet,
        walletPassphrase: 'fakeWalletPassphrase',
      };

      await basecoin
        .verifyTransaction({ txParams })
        .should.be.rejectedWith(
          `tdot doesn't support sending to more than 1 destination address within a single transaction. Try again, using only a single recipient.`
        );
    });
  });
});

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


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