PHP WebShell

Текущая директория: /opt/BitGoJS/modules/bitgo/test/v2/unit

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

//
// Test for Keychains
//

import { CoinFamily, CoinKind, coins, KeyCurve, UnderlyingAsset } from '@bitgo/statics';
import * as assert from 'assert';
import * as _ from 'lodash';
import * as nock from 'nock';
import * as should from 'should';
import * as sinon from 'sinon';

import { common, decodeOrElse, ECDSAUtils, EDDSAUtils, Keychains, OvcShare } from '@bitgo/sdk-core';
import { TestBitGo } from '@bitgo/sdk-test';
import { BitGo } from '../../../src/bitgo';

describe('V2 Keychains', function () {
  let bitgo;
  let basecoin;
  let keychains;
  let bgUrl;

  before(function () {
    bitgo = TestBitGo.decorate(BitGo, { env: 'mock' });
    bitgo.initializeTestVars();
    bitgo.setValidate(false);
    basecoin = bitgo.coin('tltc');
    keychains = basecoin.keychains();

    bgUrl = common.Environments[bitgo.getEnv()].uri;
  });

  describe('Add Keychain', function () {
    it('should add a keychain', async function () {
      const scope = nock(bgUrl)
        .post('/api/v2/tltc/key', function (body) {
          body.pub.should.equal('pub');
          body.derivedFromParentWithSeed.should.equal('derivedFromParentWithSeed');
          return true;
        })
        .reply(200, {});
      await keychains.add({ pub: 'pub', derivedFromParentWithSeed: 'derivedFromParentWithSeed' });
      scope.done();
    });
  });

  /**
   * This section's intention is to provide some key generation sanity checking. generateKeyPair is a general surface
   * for key generation but the keys are treated the same by BitGo down the line. Any SECP256K1 based coins key-pairs can
   * be re-used so need to be the same.
   **/
  describe('Key generation enforcement for SECP256K1', function () {
    // iterate over non-fiat crypto secp coins
    const coinFamilyValues = Object.keys(CoinFamily).map((n) => n.toLowerCase());
    const cryptoSecpCoins = coins.filter(
      (n) =>
        n.primaryKeyCurve === KeyCurve.Secp256k1 &&
        n.kind === CoinKind.CRYPTO &&
        n.asset !== UnderlyingAsset.USD &&
        n.asset !== UnderlyingAsset.AVAXP &&
        n.asset !== UnderlyingAsset.DOGE &&
        n.asset !== UnderlyingAsset.ETHW &&
        n.asset !== UnderlyingAsset.KAVA &&
        n.asset !== UnderlyingAsset.COREUM &&
        n.asset !== UnderlyingAsset.BERA &&
        n.asset !== UnderlyingAsset.ISLM &&
        n.asset !== UnderlyingAsset.ARBETH && // TODO(WIN-692): remove this once coin-specific module for arbeth is added
        n.asset !== UnderlyingAsset.OPETH && // TODO(WIN-692): remove this once coin-specific module for opeth is added
        n.asset !== UnderlyingAsset.ZKETH && // TODO(WIN-1427): remove this once coin-specific module for zketh is added
        n.asset !== UnderlyingAsset.OAS && // TODO(WIN-3696): remove this once coin-specific module for oas is added
        n.asset !== UnderlyingAsset.COREDAO && // TODO(WIN-3696): remove this once coin-specific module for coredao is added
        n.asset !== UnderlyingAsset.FLR && // TODO(WIN-4215): remove this once coin-specific module for FLR is added
        n.asset !== UnderlyingAsset.SGB && // TODO(WIN-4216): remove this once coin-specific module for SGB is added
        n.asset !== UnderlyingAsset.WEMIX && // TODO(WIN-4177): remove this once coin-specific module for WEMIX is added
        n.asset !== UnderlyingAsset.XDC && // TODO(WIN-4173): remove this once coin-specific module for XDC is added
        n.asset !== UnderlyingAsset.RUNE &&
        n.asset !== UnderlyingAsset.BABY &&
        n.asset !== UnderlyingAsset.ICP &&
        n.asset !== UnderlyingAsset.MANTRA &&
        n.asset !== UnderlyingAsset.MON &&
        n.asset !== UnderlyingAsset.WORLD &&
        n.asset !== UnderlyingAsset.STT &&
        n.asset !== UnderlyingAsset.SONEIUM &&
        n.asset !== UnderlyingAsset.VET &&
        n.asset !== UnderlyingAsset.CRONOS &&
        n.asset !== UnderlyingAsset.FETCH &&
        n.asset !== UnderlyingAsset.INIT &&
        coinFamilyValues.includes(n.name)
    );

    const expectedXpub =
      'xpub661MyMwAqRbcGpZf8mxNWhSPdWaLGvQzzage6vq2oQFzq8toVzmkjygYZ3HcZw6eCzAfn9ZdyGjKoKkcpKwackdgznVbiunpq7rkxDu7quS';
    const expectedXprv =
      'xprv9s21ZrQH143K4LVC2kRN9ZVf5UjqsTh9dMm3JYRRF4j1xLZexTTWCBN4hmdZUHeT3vCJPL181ErVaY489ArBKSWaB7Du7vyVS6XC43WtK7A';

    const seed = Buffer.from('this is some random seed we will use', 'utf-8');

    cryptoSecpCoins.forEach((coin) => {
      it(`should create the same ${coin.name} key with the same seed`, function () {
        const currentCoin = bitgo.coin(coin.name);
        const keyPair = currentCoin.generateKeyPair(seed);

        should.exist(keyPair.pub);
        should.exist(keyPair.prv);

        keyPair.pub.should.equal(expectedXpub);
        keyPair.prv.should.equal(expectedXprv);
      });
    });
  });

  describe('Update Password', function () {
    const oldPassword = 'oldPassword';
    const newPassword = 'newPassword';
    const otherPassword = 'otherPassword';

    describe('should fail', function () {
      let sandbox;
      beforeEach(function () {
        sandbox = sinon.createSandbox();
      });

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

      it('to update the password', async function () {
        await keychains
          .updatePassword({ newPassword: '5678' })
          .should.be.rejectedWith('Missing parameter: oldPassword');

        await keychains
          .updatePassword({ oldPassword: 1234, newPassword: '5678' })
          .should.be.rejectedWith('Expecting parameter string: oldPassword but found number');

        await keychains
          .updatePassword({ oldPassword: '1234' })
          .should.be.rejectedWith('Missing parameter: newPassword');

        await keychains
          .updatePassword({ oldPassword: '1234', newPassword: 5678 })
          .should.be.rejectedWith('Expecting parameter string: newPassword but found number');
      });

      it('to update the password for a single keychain', function () {
        (() => keychains.updateSingleKeychainPassword({ newPassword: '5678' })).should.throw(
          'expected old password to be a string'
        );

        (() => keychains.updateSingleKeychainPassword({ oldPassword: 1234, newPassword: '5678' })).should.throw(
          'expected old password to be a string'
        );

        (() => keychains.updateSingleKeychainPassword({ oldPassword: '1234' })).should.throw(
          'expected new password to be a string'
        );

        (() => keychains.updateSingleKeychainPassword({ oldPassword: '1234', newPassword: 5678 })).should.throw(
          'expected new password to be a string'
        );

        (() => keychains.updateSingleKeychainPassword({ oldPassword: '1234', newPassword: '5678' })).should.throw(
          'expected keychain to be an object with an encryptedPrv property'
        );

        (() =>
          keychains.updateSingleKeychainPassword({
            oldPassword: '1234',
            newPassword: '5678',
            keychain: {},
          })).should.throw('expected keychain to be an object with an encryptedPrv property');

        (() =>
          keychains.updateSingleKeychainPassword({
            oldPassword: '1234',
            newPassword: '5678',
            keychain: { encryptedPrv: 123 },
          })).should.throw('expected keychain to be an object with an encryptedPrv property');

        const keychain = { encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: otherPassword }) };
        (() => keychains.updateSingleKeychainPassword({ oldPassword, newPassword, keychain })).should.throw(
          'password used to decrypt keychain private key is incorrect'
        );
      });

      it('on any other error', async function () {
        nock(bgUrl)
          .get('/api/v2/tltc/key')
          .query(true)
          .reply(200, {
            keys: [
              {
                pub: 'xpub1',
                encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }),
              },
            ],
          });

        sandbox.stub(keychains, 'updateSingleKeychainPassword').throws('error', 'some random error');

        await keychains.updatePassword({ oldPassword, newPassword }).should.be.rejectedWith('some random error');
      });
    });

    describe('successful password update', function () {
      const validateKeys = function (keys, newPassword, expectedLength) {
        assert.ok(Object.keys(keys).length === expectedLength, 'should have the expected number of keys');
        _.each(keys, function (value, key) {
          assert.ok(key.includes('xpub') || key.includes('randomid'), 'key should be xpub or randomid');
          const decryptedPrv = bitgo.decrypt({ input: value, password: newPassword });
          decryptedPrv.should.startWith('xprv');
        });
      };

      it('receive only one page when listing keychains', async function () {
        nock(bgUrl)
          .get('/api/v2/tltc/key')
          .query(true)
          .reply(200, {
            keys: [
              {
                pub: 'xpub1',
                encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }),
              },
              {
                pub: 'xpub2',
                encryptedPrv: bitgo.encrypt({ input: 'xprv2', password: otherPassword }),
              },
              {
                id: 'randomid1',
                type: 'tss',
                encryptedPrv: bitgo.encrypt({ input: 'xprv3', password: oldPassword }),
              },
            ],
          });

        const keys = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword });
        validateKeys(keys, newPassword, 2);
      });

      it('receive multiple pages when listing keychains', async function () {
        const prevId = 'prevId';
        nock(bgUrl)
          .get('/api/v2/tltc/key')
          .query(true)
          .reply(200, {
            nextBatchPrevId: prevId,
            keys: [
              {
                pub: 'xpub1',
                encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }),
              },
              {
                pub: 'xpub2',
                encryptedPrv: bitgo.encrypt({ input: 'xprv2', password: otherPassword }),
              },
              {
                id: 'randomid1',
                type: 'tss',
                encryptedPrv: bitgo.encrypt({ input: 'xprv3', password: oldPassword }),
              },
            ],
          });

        nock(bgUrl)
          .get('/api/v2/tltc/key')
          .query(function queryNextPageMatch(queryObject) {
            return queryObject.prevId === prevId;
          })
          .reply(200, {
            keys: [
              {
                pub: 'xpub3',
                encryptedPrv: bitgo.encrypt({ input: 'xprv3', password: oldPassword }),
              },
              {
                pub: 'xpub4',
                encryptedPrv: bitgo.encrypt({ input: 'xprv4', password: otherPassword }),
              },
            ],
          });

        const keys = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword });
        validateKeys(keys, newPassword, 3);
      });

      it('single keychain password update', () => {
        const prv = 'xprvtest';
        const keychain = {
          xpub: 'xpub123',
          encryptedPrv: bitgo.encrypt({ input: prv, password: oldPassword }),
        };

        const newKeychain = keychains.updateSingleKeychainPassword({ keychain, oldPassword, newPassword });

        const decryptedPrv = bitgo.decrypt({ input: newKeychain.encryptedPrv, password: newPassword });
        decryptedPrv.should.equal(prv);
      });

      it('should return the updated keys with ids', async function () {
        nock(bgUrl)
          .get('/api/v2/tltc/key')
          .query(true)
          .reply(200, {
            keys: [
              {
                id: 'randomid1',
                type: 'tss',
                encryptedPrv: bitgo.encrypt({ input: 'xprv1', password: oldPassword }),
              },
              {
                id: 'randomid2',
                type: 'tss',
                encryptedPrv: bitgo.encrypt({ input: 'xprv2', password: otherPassword }),
              },
            ],
          });

        const keys = await keychains.updatePassword({ oldPassword: oldPassword, newPassword: newPassword });
        validateKeys(keys, newPassword, 1);
      });
    });

    describe('Create TSS Keychains', function () {
      const stubbedKeychainsTriplet = {
        userKeychain: {
          id: '1',
          pub: 'userPub',
        },
        backupKeychain: {
          id: '2',
          pub: 'userPub',
        },
        bitgoKeychain: {
          id: '3',
          pub: 'userPub',
        },
      };

      let sandbox;
      beforeEach(function () {
        sandbox = sinon.createSandbox();
      });

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

      ['tsol', 'tdot', 'tnear'].forEach((coin) => {
        it('should create EDDSA TSS Keychains', async function () {
          sandbox.stub(EDDSAUtils.default.prototype, 'createKeychains').resolves(stubbedKeychainsTriplet);
          const keychains = await bitgo.coin(coin).keychains().createMpc({
            multisigType: 'tss',
            passphrase: 'password',
            enterprise: 'enterprise',
            originalPasscodeEncryptionCode: 'originalPasscodeEncryptionCode',
          });
          keychains.should.deepEqual(stubbedKeychainsTriplet);
        });
      });

      ['tbsc'].forEach((coin) => {
        it('should create ECDSA TSS Keychains', async function () {
          nock(bgUrl).get('/api/v2/tss/settings').reply(200, {
            coinSettings: {},
          });
          sandbox.stub(ECDSAUtils.EcdsaUtils.prototype, 'createKeychains').resolves(stubbedKeychainsTriplet);
          const keychains = await bitgo.coin(coin).keychains().createMpc({
            multisigType: 'tss',
            passphrase: 'password',
            enterprise: 'enterprise',
            originalPasscodeEncryptionCode: 'originalPasscodeEncryptionCode',
          });
          keychains.should.deepEqual(stubbedKeychainsTriplet);
        });
      });
    });

    describe('Recreate Keychains from MPCV1 to MPCV2', async function () {
      const coin = 'hteth';
      const walletId = 'walletId';
      const otp = '000000';
      const sandbox = sinon.createSandbox();

      const decryptResult = JSON.stringify({ key: 'decrypted' });

      beforeEach(function () {
        nock(bgUrl)
          .get('/api/v2/tss/settings')
          .reply(200, {
            coinSettings: {
              eth: {
                walletCreationSettings: {
                  multiSigTypeVersion: 'MPCv2',
                },
              },
            },
          });

        sandbox.stub(BitGo.prototype, 'decrypt').returns(decryptResult);
      });

      afterEach(function () {
        sandbox.restore();
        nock.cleanAll();
      });

      it('should fail if empty strings are provided', async function () {
        const params: Parameters<Keychains['recreateMpc']>[0] = {
          coin,
          walletId,
          otp,
          passphrase: 'password',
          enterprise: 'enterprise',
          encryptedMaterial: {
            encryptedUserKey: 'encrypted',
            encryptedBackupKey: 'encrypted',
            encryptedWalletPassphrase: 'passphrase',
          },
        };

        await keychains.recreateMpc({ ...params, coin: '' }).should.be.rejectedWith('missing required param coin');

        await keychains
          .recreateMpc({ ...params, walletId: '' })
          .should.be.rejectedWith('missing required param walletId');

        await keychains.recreateMpc({ ...params, otp: '' }).should.be.rejectedWith('missing required param otp');

        await keychains
          .recreateMpc({ ...params, passphrase: '' })
          .should.be.rejectedWith('missing required param passphrase');

        await keychains
          .recreateMpc({
            ...params,
            encryptedMaterial: {
              ...params.encryptedMaterial,
              encryptedWalletPassphrase: '',
            },
          })
          .should.be.rejectedWith('missing required param encryptedWalletPassphrase');
        await keychains
          .recreateMpc({
            ...params,
            encryptedMaterial: {
              ...params.encryptedMaterial,
              encryptedUserKey: '',
            },
          })
          .should.be.rejectedWith('missing required param encryptedUserKey');
        await keychains
          .recreateMpc({
            ...params,
            encryptedMaterial: {
              ...params.encryptedMaterial,
              encryptedBackupKey: '',
            },
          })
          .should.be.rejectedWith('missing required param encryptedBackupKey');
      });

      it('should fail if otp unlock fails', async function () {
        nock(bgUrl).post('/api/v1/user/unlock').replyWithError('otp error');
        const params = {
          coin,
          walletId,
          otp,
          passphrase: 'password',
          encryptedMaterial: {
            encryptedUserKey: 'encryptedUserKey',
            encryptedBackupKey: 'encryptedBackupKey',
            encryptedWalletPassphrase: 'encryptedWalletPassphrase',
          },
        };

        await keychains.recreateMpc(params).should.be.rejectedWith('otp error');
      });

      it('should fail if passcode recovery api call fails', async function () {
        nock(bgUrl).post('/api/v1/user/unlock').reply(200, { otp });
        nock(bgUrl)
          .post(`/api/v2/${coin}/wallet/${walletId}/passcoderecovery`)
          .replyWithError('passcode recovery error');

        const params = {
          coin,
          walletId,
          otp,
          passphrase: 'password',
          encryptedMaterial: {
            encryptedUserKey: 'encryptedUserKey',
            encryptedBackupKey: 'encryptedBackupKey',
            encryptedWalletPassphrase: 'encryptedWalletPassphrase',
          },
        };

        await keychains.recreateMpc(params).should.be.rejectedWith('passcode recovery error');
      });

      it('should fail if passcode recovery api call returns invalid recovery info', async function () {
        nock(bgUrl).post('/api/v1/user/unlock').reply(200, { otp });
        nock(bgUrl).post(`/api/v2/${coin}/wallet/${walletId}/passcoderecovery`).reply(200, { recoveryInfo: {} });

        const params = {
          coin,
          walletId,
          otp,
          passphrase: 'password',
          encryptedMaterial: {
            encryptedUserKey: 'encryptedUserKey',
            encryptedBackupKey: 'encryptedBackupKey',
            encryptedWalletPassphrase: 'encryptedWalletPassphrase',
          },
        };

        await keychains.recreateMpc(params).should.be.rejectedWith('failed to get recovery info');
      });

      it('should call createMpc with the correct parameters', async function () {
        nock(bgUrl).post('/api/v1/user/unlock').reply(200, { otp });
        nock(bgUrl)
          .post(`/api/v2/${coin}/wallet/${walletId}/passcoderecovery`)
          .reply(200, { recoveryInfo: { passcodeEncryptionCode: '123' } });

        const params = {
          coin,
          walletId: 'walletId',
          otp,
          passphrase: 'password',
          enterprise: 'enterprise',
          originalPasscodeEncryptionCode: 'originalPasscodeEncryptionCode',
          encryptedMaterial: {
            encryptedUserKey: 'encryptedUserKey',
            encryptedBackupKey: 'encryptedBackupKey',
            encryptedWalletPassphrase: 'encryptedWalletPassphrase',
          },
        };
        const createMpcStub = sinon.stub(keychains, 'createMpc').resolves();
        await keychains.recreateMpc(params);

        assert.ok(
          createMpcStub.calledOnceWith({
            ...params,
            multisigType: 'tss',
            retrofit: {
              decryptedUserKey: decryptResult,
              decryptedBackupKey: decryptResult,
              walletId: params.walletId,
            },
          })
        );
      });
    });

    after(function afterUpdatePassword() {
      nock.pendingMocks().should.be.empty();
    });
  });

  describe('Create BitGo Key from OVC JSON', function () {
    it('Parses the OVC JSON file properly and creates the next input for OVC', async function () {
      const bitGoKeyResult = {
        id: '6421eb755fea9e0006c2c040072f74bb',
        commonKeychain: '123',
        walletHSMGPGPublicKeySigs: '123',
        verifiedVssProof: true,
        keyShares: [
          {
            from: 'bitgo',
            to: 'user',
            publicShare: 'ccc',
            privateShare: 'ccc private',
            paillierPublicKey: 'ccc paillier',
            vssProof: 'ccc vss proof',
          },
          {
            from: 'bitgo',
            to: 'backup',
            publicShare: 'fff',
            privateShare: 'fff private',
            paillierPublicKey: 'fff paillier',
            vssProof: 'fff vss proof',
          },
        ],
      };
      nock(bgUrl)
        .post('/api/v2/tsol/key', _.matches({ source: 'bitgo', keyType: 'tss' }))
        .reply(200, bitGoKeyResult);

      const ovcOutputJson = {
        tssVersion: '0.0.1',
        walletType: 'tss',
        coin: 'sol',
        state: 1,
        ovc: {
          1: {
            gpgPubKey:
              '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsq9RMFK4EEAAoCAwRgDpgm1bptRT5yLOMadcGAHuvkxISL8/3xPy/D\nYA+1NgBqIFK/3OOXxp73Tv86bt1dgH7OD1ACO0mVXAoX5EaVzWZvdmMtMS11\nc2VyLWRmMjQwOWY0NWNhMTZmNTU3OGNkMzAxMyA8b3ZjLTEtdXNlci1kZjI0\nMDlmNDVjYTE2ZjU1NzhjZDMwMTNAZGYyNDA5ZjQ1Y2ExNmY1NTc4Y2QzMDEz\nLmNvbT7CjAQQEwgAHQUCZBsq9QQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJ\nEOUeC7QrGL+2FiEEjN6vPUFRweVtDKFZ5R4LtCsYv7Z7oAEAvNKaJrTIs3ky\nbxjkUicsuxOyA2oWGarJH6TGlWF6WD8A/3EIeZzPKSLaW+3enpbCPiU8RVDp\nC6yo6NMAqKp4XlyQzlMEZBsq9RIFK4EEAAoCAwQWlWPNMpk3afEzfgG0xFg4\nCEjY7fGO35n47nZ4qxjEXz2XN4I23xFMiwZbXbptDXlqm7W9ZAHKi892h+Yt\nU4B6AwEIB8J4BBgTCAAJBQJkGyr1AhsMACEJEOUeC7QrGL+2FiEEjN6vPUFR\nweVtDKFZ5R4LtCsYv7YqZwD8DLRuneB0HEzBCyrE4YL+UHaofoHQX1+nRh0j\n82qaDzAA/i3U1SoEIi32YMrorNG50vb4vaEPbbYmfglb+JQ1Yx7o\n=vjgl\n-----END PGP PUBLIC KEY BLOCK-----\n',
            ovcToBitgoShare: {
              publicShare:
                '07bfbb052a1f4b106b315bd5a9d6a71604653289f861320d9801881e952f8550753c89511a360e3b599d9f239a8d36a4956437ed0b152180e22cb94fb08fdc81',
              privateShare:
                '-----BEGIN PGP MESSAGE-----\n\nwX4DUQ8XhGVGcTISAgMEd/wQ/AhwIRsSf/6iHwmHKvEYAwfaLtTQotS6BwZm\naX00T7LumGIrNZzBgcggBKMl+8Omom8mX5sP8FUE451iZjBmXlkpHlpAqskS\nEi5SfjlsT31utoLaBLA7NjNSmyYHIiyfh6YnwfV5U72k6hhfpuDSsQGy8Yxp\nM2dVlN4uoO31zgQPf+fgJkZvAPwvjYLBL4O8hs21HGf0VvG99brk4xlFVRhw\nQnuPpM51GOs4vUtbLNSYnAuhU4ReXwwDV9Xu0MXAjfQMk2E3wQLGQ+82uv2B\n1hPD9nDHTtAdWrXd6ZjXFrG4Mf2MQBxySFvVSnFi6yStjfZeiKcD5Vg38PjG\nWg1O5N3/paq98HfH0Q4qNKGZljwo3oEQZzlYm2kVnI0PULoMYg==\n=ovAx\n-----END PGP MESSAGE-----\n',
              vssProof: 'be8198c2cef94cb381aa8aa9277a0a46ba5a54b0ac9930034b4af66b1b805234',
              i: 3,
              j: 1,
              uSig: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsq9RMFK4EEAAoCAwRgDpgm1bptRT5yLOMadcGAHuvkxISL8/3xPy/D\nYA+1NgBqIFK/3OOXxp73Tv86bt1dgH7OD1ACO0mVXAoX5EaVzWZvdmMtMS11\nc2VyLWRmMjQwOWY0NWNhMTZmNTU3OGNkMzAxMyA8b3ZjLTEtdXNlci1kZjI0\nMDlmNDVjYTE2ZjU1NzhjZDMwMTNAZGYyNDA5ZjQ1Y2ExNmY1NTc4Y2QzMDEz\nLmNvbT7CjAQQEwgAHQUCZBsq9QQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJ\nEOUeC7QrGL+2FiEEjN6vPUFRweVtDKFZ5R4LtCsYv7Z7oAEAvNKaJrTIs3ky\nbxjkUicsuxOyA2oWGarJH6TGlWF6WD8A/3EIeZzPKSLaW+3enpbCPiU8RVDp\nC6yo6NMAqKp4XlyQzlMEZBsq9RIFK4EEAAoCAwQWlWPNMpk3afEzfgG0xFg4\nCEjY7fGO35n47nZ4qxjEXz2XN4I23xFMiwZbXbptDXlqm7W9ZAHKi892h+Yt\nU4B6AwEIB8J4BBgTCAAJBQJkGyr1AhsMACEJEOUeC7QrGL+2FiEEjN6vPUFR\nweVtDKFZ5R4LtCsYv7YqZwD8DLRuneB0HEzBCyrE4YL+UHaofoHQX1+nRh0j\n82qaDzAA/i3U1SoEIi32YMrorNG50vb4vaEPbbYmfglb+JQ1Yx7ozjMEZBsq\n9RYJKwYBBAHaRw8BAQdAS1534LdCC60ASpEgnEBC8pWPSOWWBLXyWaBhEOW2\n/ijCeAQYEwgACQUCZBsq9QIbIAAhCRDlHgu0Kxi/thYhBIzerz1BUcHlbQyh\nWeUeC7QrGL+2FUYA/AgMU4V7C7I/5HwK73mctaGXNdfVr/muRMNOGW/CAxVL\nAP9KaBr95o32oEN5eo7tWFByoOCCk9JMjs36Cq7o+sR31A==\n=eZ3B\n-----END PGP PUBLIC KEY BLOCK-----\n',
            },
          },
          2: {
            gpgPubKey:
              '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsrNBMFK4EEAAoCAwT2xXv/mG/daPdKGD/fHIUEE2ZtcK+njtCZtMEr\nkIubmUwe3Dj8+hQxt3SKaTxQbuD+WaLSj986QYUr4Zw4T+W3zWpvdmMtMi1i\nYWNrdXAtNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4IDxvdmMtMi1iYWNrdXAt\nNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4QDU0N2JiZGYzMDhjMWU2MWNkOWRi\nODZlOC5jb20+wowEEBMIAB0FAmQbKzQECwkHCAMVCAoEFgACAQIZAQIbAwIe\nAQAhCRC2Ak5G2Sk7ZRYhBLTpSkFOe3ajbqouerYCTkbZKTtlxtYBALLIBnAa\n4AS77XvXmmznNCWO/HNuDPD2ugRqVhqU4SxmAQDma14APuMNSVyi17xomZjW\n0nimtaMIBjc0A4MXE6G4Xc5TBGQbKzQSBSuBBAAKAgMEUHoQDY98y8CAVouu\ny4y3Q2QnBpnUBK/0PELV7VANXd9lY7CShyogJScgPliUm97Au9FC17vqZa1B\n5bBMJAdXVwMBCAfCeAQYEwgACQUCZBsrNAIbDAAhCRC2Ak5G2Sk7ZRYhBLTp\nSkFOe3ajbqouerYCTkbZKTtlR5wA/3zOFTSeXXRRemXl3hY10spxabE4O2J8\nZD2VRfjbDpZjAQDWg1W+jPbM2Htikd1N01V+zweNDhJEH65r0Qr8BlXh2w==\n=Ettu\n-----END PGP PUBLIC KEY BLOCK-----\n',
            ovcToBitgoShare: {
              publicShare:
                'ecf95e9ba6f02c0bc4c2ab254565d21a1562059c8a1ab70d286fa04ae2c511e0491410b6d6fa8ab22e691bed7938a7adaabfa8d9d59aab9cc2c078cd37dde01b',
              privateShare:
                '-----BEGIN PGP MESSAGE-----\n\nwX4DUQ8XhGVGcTISAgMEe1nYNv9DnHl8HX8jNs3reGs5nPT2bNzGMCFL0N3P\nKLwGd8DBkDJIMpoClaAS/HTUTPTRzj4U1GShV79I+bbfTDBV6uhyfZ7a88dV\nRDTXl6rchFl/fmwLoXnqH+BDPEXytpAvKwDrAg55T/H7aop5QbfSsQErEv7X\nTpTLHHHflEEQL62p1Rnnq2kKvwJK7TY1g5RtPgYXH6GEs33DLulPeUs8UkKq\nTPLf2+1KCLYyzMwmSi8KEC8HJb0vAkXZAlZ1+DoZ2PfiIJcVnz7a3omjiP77\nhmC8phTcJ718UxIB09f8uic0/fM7FNu0JHFgRDA69zzeFYnZc2XNQg5hs/dZ\nZ8AitP4rnaAfprsq4rAEE/c3Trm9tzQHZPCqr/2s4LluyUMVJw==\n=IJZH\n-----END PGP MESSAGE-----\n',
              vssProof: '309d2fef2272ad31375954b99a862742f22d8023eef9855b8ae26ea84e08bf5e',
              i: 3,
              j: 2,
              uSig: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsrNBMFK4EEAAoCAwT2xXv/mG/daPdKGD/fHIUEE2ZtcK+njtCZtMEr\nkIubmUwe3Dj8+hQxt3SKaTxQbuD+WaLSj986QYUr4Zw4T+W3zWpvdmMtMi1i\nYWNrdXAtNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4IDxvdmMtMi1iYWNrdXAt\nNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4QDU0N2JiZGYzMDhjMWU2MWNkOWRi\nODZlOC5jb20+wowEEBMIAB0FAmQbKzQECwkHCAMVCAoEFgACAQIZAQIbAwIe\nAQAhCRC2Ak5G2Sk7ZRYhBLTpSkFOe3ajbqouerYCTkbZKTtlxtYBALLIBnAa\n4AS77XvXmmznNCWO/HNuDPD2ugRqVhqU4SxmAQDma14APuMNSVyi17xomZjW\n0nimtaMIBjc0A4MXE6G4Xc5TBGQbKzQSBSuBBAAKAgMEUHoQDY98y8CAVouu\ny4y3Q2QnBpnUBK/0PELV7VANXd9lY7CShyogJScgPliUm97Au9FC17vqZa1B\n5bBMJAdXVwMBCAfCeAQYEwgACQUCZBsrNAIbDAAhCRC2Ak5G2Sk7ZRYhBLTp\nSkFOe3ajbqouerYCTkbZKTtlR5wA/3zOFTSeXXRRemXl3hY10spxabE4O2J8\nZD2VRfjbDpZjAQDWg1W+jPbM2Htikd1N01V+zweNDhJEH65r0Qr8BlXh284z\nBGQbKzQWCSsGAQQB2kcPAQEHQIVTmlt8a/fjJCY9UyP8J51eTPX9PMRF3/Hr\nBYwtfOZYwngEGBMIAAkFAmQbKzQCGyAAIQkQtgJORtkpO2UWIQS06UpBTnt2\no26qLnq2Ak5G2Sk7ZcthAQD4ek3aqUDEbhfRHolm4jFhIPIbb9hUJwuBR0R4\n+gvaeAEAk8uk5cEACeqmpdEF52oGx7jmGCoX73AIILYVcf9iMkI=\n=92Hf\n-----END PGP PUBLIC KEY BLOCK-----\n',
            },
            ovcToOvcShare: {
              publicShare:
                'ecf95e9ba6f02c0bc4c2ab254565d21a1562059c8a1ab70d286fa04ae2c511e0491410b6d6fa8ab22e691bed7938a7adaabfa8d9d59aab9cc2c078cd37dde01b',
              privateShare:
                '-----BEGIN PGP MESSAGE-----\n\nwX4D+F3vI962zVMSAgMENy2X9xDIOm1gwOfMdL/1vZjeMW/ile/f2dRIP2GV\nv4w60K92+mHI9yooU1VT77jZrVOcNK41DFNCF/fuoUU+VzBON0I1hHyXm2QJ\nwL+nNdvAXwRv7nSFD3ABUmRYdG71AFIWjW2L+1C2hxpsNlmdxffSsQHHT9uP\nBNpFYgQN77OUxcgrweSVrPyer1rCQ8o+QnoK2/hIx9PlJZkcl3uwF6TpBtjs\nqvRxThomoHyo0hJHG0vmI42Gq5u0tcSeU902efpCDaeJhPVOiqISoJ96wn0r\nFB3lYLSflOpCLAE7nsZJRoXA6akU7xMCMNA++d4tv6p98FQ+MZ6e9x6kiSw1\nM+VWj+PvN1dtQ8fNYwpJf7KBfD1w6ISMFDmewkDE7iBOXF6FLw==\n=+3Yc\n-----END PGP MESSAGE-----\n',
              vssProof: '309d2fef2272ad31375954b99a862742f22d8023eef9855b8ae26ea84e08bf5e',
              i: 1,
              j: 2,
              uSig: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsrNBMFK4EEAAoCAwT2xXv/mG/daPdKGD/fHIUEE2ZtcK+njtCZtMEr\nkIubmUwe3Dj8+hQxt3SKaTxQbuD+WaLSj986QYUr4Zw4T+W3zWpvdmMtMi1i\nYWNrdXAtNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4IDxvdmMtMi1iYWNrdXAt\nNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4QDU0N2JiZGYzMDhjMWU2MWNkOWRi\nODZlOC5jb20+wowEEBMIAB0FAmQbKzQECwkHCAMVCAoEFgACAQIZAQIbAwIe\nAQAhCRC2Ak5G2Sk7ZRYhBLTpSkFOe3ajbqouerYCTkbZKTtlxtYBALLIBnAa\n4AS77XvXmmznNCWO/HNuDPD2ugRqVhqU4SxmAQDma14APuMNSVyi17xomZjW\n0nimtaMIBjc0A4MXE6G4Xc5TBGQbKzQSBSuBBAAKAgMEUHoQDY98y8CAVouu\ny4y3Q2QnBpnUBK/0PELV7VANXd9lY7CShyogJScgPliUm97Au9FC17vqZa1B\n5bBMJAdXVwMBCAfCeAQYEwgACQUCZBsrNAIbDAAhCRC2Ak5G2Sk7ZRYhBLTp\nSkFOe3ajbqouerYCTkbZKTtlR5wA/3zOFTSeXXRRemXl3hY10spxabE4O2J8\nZD2VRfjbDpZjAQDWg1W+jPbM2Htikd1N01V+zweNDhJEH65r0Qr8BlXh284z\nBGQbKzQWCSsGAQQB2kcPAQEHQNVGg0vPsfK1yJjFuDYB8Tj7CQRHKGAVEDcj\nh/QIqr0KwngEGBMIAAkFAmQbKzQCGyAAIQkQtgJORtkpO2UWIQS06UpBTnt2\no26qLnq2Ak5G2Sk7ZS9oAQCe2zJD5ItW2GEgAitnY+NrLBjbXI6+LY29GjRa\nf6yFTQD/cP0E1DleqXMjgfR+ewt6quVFMZebhr3tBr/H0Zv/9EA=\n=8ai+\n-----END PGP PUBLIC KEY BLOCK-----\n',
            },
          },
        },
      };
      const expectedBitGoOutput = {
        wallet: {
          ...ovcOutputJson,
          state: 2,
          platform: {
            commonKeychain: bitGoKeyResult.commonKeychain,
            walletGpgPubKeySigs: bitGoKeyResult.walletHSMGPGPublicKeySigs,
            ovc: {
              1: {
                bitgoToOvcShare: {
                  i: 1,
                  j: 3,
                  publicShare: bitGoKeyResult.keyShares[0].publicShare,
                  privateShare: bitGoKeyResult.keyShares[0].privateShare,
                  paillierPublicKey: bitGoKeyResult.keyShares[0].paillierPublicKey,
                  vssProof: bitGoKeyResult.keyShares[0].vssProof,
                },
              },
              2: {
                bitgoToOvcShare: {
                  i: 2,
                  j: 3,
                  publicShare: bitGoKeyResult.keyShares[1].publicShare,
                  privateShare: bitGoKeyResult.keyShares[1].privateShare,
                  paillierPublicKey: bitGoKeyResult.keyShares[1].paillierPublicKey,
                  vssProof: bitGoKeyResult.keyShares[1].vssProof,
                },
              },
            },
          },
        },
      };
      const platformOutput = await bitgo.coin('tsol').keychains().createTssBitGoKeyFromOvcShares(ovcOutputJson);
      should.equal(platformOutput.bitGoKeyId, bitGoKeyResult.id);
      should.deepEqual(platformOutput.bitGoOutputJsonForOvc, expectedBitGoOutput);
    });

    it('Should fail if the state is not 1', async function () {
      const ovcOutputJson = {
        tssVersion: '0.0.1',
        walletType: 'tss',
        coin: 'sol',
        state: 0,
        ovc: {
          1: {
            gpgPubKey:
              '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsq9RMFK4EEAAoCAwRgDpgm1bptRT5yLOMadcGAHuvkxISL8/3xPy/D\nYA+1NgBqIFK/3OOXxp73Tv86bt1dgH7OD1ACO0mVXAoX5EaVzWZvdmMtMS11\nc2VyLWRmMjQwOWY0NWNhMTZmNTU3OGNkMzAxMyA8b3ZjLTEtdXNlci1kZjI0\nMDlmNDVjYTE2ZjU1NzhjZDMwMTNAZGYyNDA5ZjQ1Y2ExNmY1NTc4Y2QzMDEz\nLmNvbT7CjAQQEwgAHQUCZBsq9QQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJ\nEOUeC7QrGL+2FiEEjN6vPUFRweVtDKFZ5R4LtCsYv7Z7oAEAvNKaJrTIs3ky\nbxjkUicsuxOyA2oWGarJH6TGlWF6WD8A/3EIeZzPKSLaW+3enpbCPiU8RVDp\nC6yo6NMAqKp4XlyQzlMEZBsq9RIFK4EEAAoCAwQWlWPNMpk3afEzfgG0xFg4\nCEjY7fGO35n47nZ4qxjEXz2XN4I23xFMiwZbXbptDXlqm7W9ZAHKi892h+Yt\nU4B6AwEIB8J4BBgTCAAJBQJkGyr1AhsMACEJEOUeC7QrGL+2FiEEjN6vPUFR\nweVtDKFZ5R4LtCsYv7YqZwD8DLRuneB0HEzBCyrE4YL+UHaofoHQX1+nRh0j\n82qaDzAA/i3U1SoEIi32YMrorNG50vb4vaEPbbYmfglb+JQ1Yx7o\n=vjgl\n-----END PGP PUBLIC KEY BLOCK-----\n',
            ovcToBitgoShare: {
              publicShare:
                '07bfbb052a1f4b106b315bd5a9d6a71604653289f861320d9801881e952f8550753c89511a360e3b599d9f239a8d36a4956437ed0b152180e22cb94fb08fdc81',
              privateShare:
                '-----BEGIN PGP MESSAGE-----\n\nwX4DUQ8XhGVGcTISAgMEd/wQ/AhwIRsSf/6iHwmHKvEYAwfaLtTQotS6BwZm\naX00T7LumGIrNZzBgcggBKMl+8Omom8mX5sP8FUE451iZjBmXlkpHlpAqskS\nEi5SfjlsT31utoLaBLA7NjNSmyYHIiyfh6YnwfV5U72k6hhfpuDSsQGy8Yxp\nM2dVlN4uoO31zgQPf+fgJkZvAPwvjYLBL4O8hs21HGf0VvG99brk4xlFVRhw\nQnuPpM51GOs4vUtbLNSYnAuhU4ReXwwDV9Xu0MXAjfQMk2E3wQLGQ+82uv2B\n1hPD9nDHTtAdWrXd6ZjXFrG4Mf2MQBxySFvVSnFi6yStjfZeiKcD5Vg38PjG\nWg1O5N3/paq98HfH0Q4qNKGZljwo3oEQZzlYm2kVnI0PULoMYg==\n=ovAx\n-----END PGP MESSAGE-----\n',
              vssProof: 'be8198c2cef94cb381aa8aa9277a0a46ba5a54b0ac9930034b4af66b1b805234',
              i: 3,
              j: 1,
              uSig: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsq9RMFK4EEAAoCAwRgDpgm1bptRT5yLOMadcGAHuvkxISL8/3xPy/D\nYA+1NgBqIFK/3OOXxp73Tv86bt1dgH7OD1ACO0mVXAoX5EaVzWZvdmMtMS11\nc2VyLWRmMjQwOWY0NWNhMTZmNTU3OGNkMzAxMyA8b3ZjLTEtdXNlci1kZjI0\nMDlmNDVjYTE2ZjU1NzhjZDMwMTNAZGYyNDA5ZjQ1Y2ExNmY1NTc4Y2QzMDEz\nLmNvbT7CjAQQEwgAHQUCZBsq9QQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJ\nEOUeC7QrGL+2FiEEjN6vPUFRweVtDKFZ5R4LtCsYv7Z7oAEAvNKaJrTIs3ky\nbxjkUicsuxOyA2oWGarJH6TGlWF6WD8A/3EIeZzPKSLaW+3enpbCPiU8RVDp\nC6yo6NMAqKp4XlyQzlMEZBsq9RIFK4EEAAoCAwQWlWPNMpk3afEzfgG0xFg4\nCEjY7fGO35n47nZ4qxjEXz2XN4I23xFMiwZbXbptDXlqm7W9ZAHKi892h+Yt\nU4B6AwEIB8J4BBgTCAAJBQJkGyr1AhsMACEJEOUeC7QrGL+2FiEEjN6vPUFR\nweVtDKFZ5R4LtCsYv7YqZwD8DLRuneB0HEzBCyrE4YL+UHaofoHQX1+nRh0j\n82qaDzAA/i3U1SoEIi32YMrorNG50vb4vaEPbbYmfglb+JQ1Yx7ozjMEZBsq\n9RYJKwYBBAHaRw8BAQdAS1534LdCC60ASpEgnEBC8pWPSOWWBLXyWaBhEOW2\n/ijCeAQYEwgACQUCZBsq9QIbIAAhCRDlHgu0Kxi/thYhBIzerz1BUcHlbQyh\nWeUeC7QrGL+2FUYA/AgMU4V7C7I/5HwK73mctaGXNdfVr/muRMNOGW/CAxVL\nAP9KaBr95o32oEN5eo7tWFByoOCCk9JMjs36Cq7o+sR31A==\n=eZ3B\n-----END PGP PUBLIC KEY BLOCK-----\n',
            },
          },
          2: {
            gpgPubKey:
              '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsrNBMFK4EEAAoCAwT2xXv/mG/daPdKGD/fHIUEE2ZtcK+njtCZtMEr\nkIubmUwe3Dj8+hQxt3SKaTxQbuD+WaLSj986QYUr4Zw4T+W3zWpvdmMtMi1i\nYWNrdXAtNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4IDxvdmMtMi1iYWNrdXAt\nNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4QDU0N2JiZGYzMDhjMWU2MWNkOWRi\nODZlOC5jb20+wowEEBMIAB0FAmQbKzQECwkHCAMVCAoEFgACAQIZAQIbAwIe\nAQAhCRC2Ak5G2Sk7ZRYhBLTpSkFOe3ajbqouerYCTkbZKTtlxtYBALLIBnAa\n4AS77XvXmmznNCWO/HNuDPD2ugRqVhqU4SxmAQDma14APuMNSVyi17xomZjW\n0nimtaMIBjc0A4MXE6G4Xc5TBGQbKzQSBSuBBAAKAgMEUHoQDY98y8CAVouu\ny4y3Q2QnBpnUBK/0PELV7VANXd9lY7CShyogJScgPliUm97Au9FC17vqZa1B\n5bBMJAdXVwMBCAfCeAQYEwgACQUCZBsrNAIbDAAhCRC2Ak5G2Sk7ZRYhBLTp\nSkFOe3ajbqouerYCTkbZKTtlR5wA/3zOFTSeXXRRemXl3hY10spxabE4O2J8\nZD2VRfjbDpZjAQDWg1W+jPbM2Htikd1N01V+zweNDhJEH65r0Qr8BlXh2w==\n=Ettu\n-----END PGP PUBLIC KEY BLOCK-----\n',
            ovcToBitgoShare: {
              publicShare:
                'ecf95e9ba6f02c0bc4c2ab254565d21a1562059c8a1ab70d286fa04ae2c511e0491410b6d6fa8ab22e691bed7938a7adaabfa8d9d59aab9cc2c078cd37dde01b',
              privateShare:
                '-----BEGIN PGP MESSAGE-----\n\nwX4DUQ8XhGVGcTISAgMEe1nYNv9DnHl8HX8jNs3reGs5nPT2bNzGMCFL0N3P\nKLwGd8DBkDJIMpoClaAS/HTUTPTRzj4U1GShV79I+bbfTDBV6uhyfZ7a88dV\nRDTXl6rchFl/fmwLoXnqH+BDPEXytpAvKwDrAg55T/H7aop5QbfSsQErEv7X\nTpTLHHHflEEQL62p1Rnnq2kKvwJK7TY1g5RtPgYXH6GEs33DLulPeUs8UkKq\nTPLf2+1KCLYyzMwmSi8KEC8HJb0vAkXZAlZ1+DoZ2PfiIJcVnz7a3omjiP77\nhmC8phTcJ718UxIB09f8uic0/fM7FNu0JHFgRDA69zzeFYnZc2XNQg5hs/dZ\nZ8AitP4rnaAfprsq4rAEE/c3Trm9tzQHZPCqr/2s4LluyUMVJw==\n=IJZH\n-----END PGP MESSAGE-----\n',
              vssProof: '309d2fef2272ad31375954b99a862742f22d8023eef9855b8ae26ea84e08bf5e',
              i: 3,
              j: 2,
              uSig: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsrNBMFK4EEAAoCAwT2xXv/mG/daPdKGD/fHIUEE2ZtcK+njtCZtMEr\nkIubmUwe3Dj8+hQxt3SKaTxQbuD+WaLSj986QYUr4Zw4T+W3zWpvdmMtMi1i\nYWNrdXAtNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4IDxvdmMtMi1iYWNrdXAt\nNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4QDU0N2JiZGYzMDhjMWU2MWNkOWRi\nODZlOC5jb20+wowEEBMIAB0FAmQbKzQECwkHCAMVCAoEFgACAQIZAQIbAwIe\nAQAhCRC2Ak5G2Sk7ZRYhBLTpSkFOe3ajbqouerYCTkbZKTtlxtYBALLIBnAa\n4AS77XvXmmznNCWO/HNuDPD2ugRqVhqU4SxmAQDma14APuMNSVyi17xomZjW\n0nimtaMIBjc0A4MXE6G4Xc5TBGQbKzQSBSuBBAAKAgMEUHoQDY98y8CAVouu\ny4y3Q2QnBpnUBK/0PELV7VANXd9lY7CShyogJScgPliUm97Au9FC17vqZa1B\n5bBMJAdXVwMBCAfCeAQYEwgACQUCZBsrNAIbDAAhCRC2Ak5G2Sk7ZRYhBLTp\nSkFOe3ajbqouerYCTkbZKTtlR5wA/3zOFTSeXXRRemXl3hY10spxabE4O2J8\nZD2VRfjbDpZjAQDWg1W+jPbM2Htikd1N01V+zweNDhJEH65r0Qr8BlXh284z\nBGQbKzQWCSsGAQQB2kcPAQEHQIVTmlt8a/fjJCY9UyP8J51eTPX9PMRF3/Hr\nBYwtfOZYwngEGBMIAAkFAmQbKzQCGyAAIQkQtgJORtkpO2UWIQS06UpBTnt2\no26qLnq2Ak5G2Sk7ZcthAQD4ek3aqUDEbhfRHolm4jFhIPIbb9hUJwuBR0R4\n+gvaeAEAk8uk5cEACeqmpdEF52oGx7jmGCoX73AIILYVcf9iMkI=\n=92Hf\n-----END PGP PUBLIC KEY BLOCK-----\n',
            },
            ovcToOvcShare: {
              publicShare:
                'ecf95e9ba6f02c0bc4c2ab254565d21a1562059c8a1ab70d286fa04ae2c511e0491410b6d6fa8ab22e691bed7938a7adaabfa8d9d59aab9cc2c078cd37dde01b',
              privateShare:
                '-----BEGIN PGP MESSAGE-----\n\nwX4D+F3vI962zVMSAgMENy2X9xDIOm1gwOfMdL/1vZjeMW/ile/f2dRIP2GV\nv4w60K92+mHI9yooU1VT77jZrVOcNK41DFNCF/fuoUU+VzBON0I1hHyXm2QJ\nwL+nNdvAXwRv7nSFD3ABUmRYdG71AFIWjW2L+1C2hxpsNlmdxffSsQHHT9uP\nBNpFYgQN77OUxcgrweSVrPyer1rCQ8o+QnoK2/hIx9PlJZkcl3uwF6TpBtjs\nqvRxThomoHyo0hJHG0vmI42Gq5u0tcSeU902efpCDaeJhPVOiqISoJ96wn0r\nFB3lYLSflOpCLAE7nsZJRoXA6akU7xMCMNA++d4tv6p98FQ+MZ6e9x6kiSw1\nM+VWj+PvN1dtQ8fNYwpJf7KBfD1w6ISMFDmewkDE7iBOXF6FLw==\n=+3Yc\n-----END PGP MESSAGE-----\n',
              vssProof: '309d2fef2272ad31375954b99a862742f22d8023eef9855b8ae26ea84e08bf5e',
              i: 1,
              j: 2,
              uSig: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsrNBMFK4EEAAoCAwT2xXv/mG/daPdKGD/fHIUEE2ZtcK+njtCZtMEr\nkIubmUwe3Dj8+hQxt3SKaTxQbuD+WaLSj986QYUr4Zw4T+W3zWpvdmMtMi1i\nYWNrdXAtNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4IDxvdmMtMi1iYWNrdXAt\nNTQ3YmJkZjMwOGMxZTYxY2Q5ZGI4NmU4QDU0N2JiZGYzMDhjMWU2MWNkOWRi\nODZlOC5jb20+wowEEBMIAB0FAmQbKzQECwkHCAMVCAoEFgACAQIZAQIbAwIe\nAQAhCRC2Ak5G2Sk7ZRYhBLTpSkFOe3ajbqouerYCTkbZKTtlxtYBALLIBnAa\n4AS77XvXmmznNCWO/HNuDPD2ugRqVhqU4SxmAQDma14APuMNSVyi17xomZjW\n0nimtaMIBjc0A4MXE6G4Xc5TBGQbKzQSBSuBBAAKAgMEUHoQDY98y8CAVouu\ny4y3Q2QnBpnUBK/0PELV7VANXd9lY7CShyogJScgPliUm97Au9FC17vqZa1B\n5bBMJAdXVwMBCAfCeAQYEwgACQUCZBsrNAIbDAAhCRC2Ak5G2Sk7ZRYhBLTp\nSkFOe3ajbqouerYCTkbZKTtlR5wA/3zOFTSeXXRRemXl3hY10spxabE4O2J8\nZD2VRfjbDpZjAQDWg1W+jPbM2Htikd1N01V+zweNDhJEH65r0Qr8BlXh284z\nBGQbKzQWCSsGAQQB2kcPAQEHQNVGg0vPsfK1yJjFuDYB8Tj7CQRHKGAVEDcj\nh/QIqr0KwngEGBMIAAkFAmQbKzQCGyAAIQkQtgJORtkpO2UWIQS06UpBTnt2\no26qLnq2Ak5G2Sk7ZS9oAQCe2zJD5ItW2GEgAitnY+NrLBjbXI6+LY29GjRa\nf6yFTQD/cP0E1DleqXMjgfR+ewt6quVFMZebhr3tBr/H0Zv/9EA=\n=8ai+\n-----END PGP PUBLIC KEY BLOCK-----\n',
            },
          },
        },
      };

      await bitgo
        .coin('tsol')
        .keychains()
        .createTssBitGoKeyFromOvcShares(ovcOutputJson)
        .should.be.rejectedWith('State expected to be "1". Please complete the first two OVC operations');
    });

    it('Fails to decode if the OVC json is missing fields', async function () {
      const ovcOutputJson = {
        tssVersion: '0.0.1',
        state: 1,
        coin: 'tsol',
        ovc: {
          1: {
            gpgPubKey:
              '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsq9RMFK4EEAAoCAwRgDpgm1bptRT5yLOMadcGAHuvkxISL8/3xPy/D\nYA+1NgBqIFK/3OOXxp73Tv86bt1dgH7OD1ACO0mVXAoX5EaVzWZvdmMtMS11\nc2VyLWRmMjQwOWY0NWNhMTZmNTU3OGNkMzAxMyA8b3ZjLTEtdXNlci1kZjI0\nMDlmNDVjYTE2ZjU1NzhjZDMwMTNAZGYyNDA5ZjQ1Y2ExNmY1NTc4Y2QzMDEz\nLmNvbT7CjAQQEwgAHQUCZBsq9QQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJ\nEOUeC7QrGL+2FiEEjN6vPUFRweVtDKFZ5R4LtCsYv7Z7oAEAvNKaJrTIs3ky\nbxjkUicsuxOyA2oWGarJH6TGlWF6WD8A/3EIeZzPKSLaW+3enpbCPiU8RVDp\nC6yo6NMAqKp4XlyQzlMEZBsq9RIFK4EEAAoCAwQWlWPNMpk3afEzfgG0xFg4\nCEjY7fGO35n47nZ4qxjEXz2XN4I23xFMiwZbXbptDXlqm7W9ZAHKi892h+Yt\nU4B6AwEIB8J4BBgTCAAJBQJkGyr1AhsMACEJEOUeC7QrGL+2FiEEjN6vPUFR\nweVtDKFZ5R4LtCsYv7YqZwD8DLRuneB0HEzBCyrE4YL+UHaofoHQX1+nRh0j\n82qaDzAA/i3U1SoEIi32YMrorNG50vb4vaEPbbYmfglb+JQ1Yx7o\n=vjgl\n-----END PGP PUBLIC KEY BLOCK-----\n',
            ovcToBitgoShare: {
              publicShare:
                '07bfbb052a1f4b106b315bd5a9d6a71604653289f861320d9801881e952f8550753c89511a360e3b599d9f239a8d36a4956437ed0b152180e22cb94fb08fdc81',
              privateShare:
                '-----BEGIN PGP MESSAGE-----\n\nwX4DUQ8XhGVGcTISAgMEd/wQ/AhwIRsSf/6iHwmHKvEYAwfaLtTQotS6BwZm\naX00T7LumGIrNZzBgcggBKMl+8Omom8mX5sP8FUE451iZjBmXlkpHlpAqskS\nEi5SfjlsT31utoLaBLA7NjNSmyYHIiyfh6YnwfV5U72k6hhfpuDSsQGy8Yxp\nM2dVlN4uoO31zgQPf+fgJkZvAPwvjYLBL4O8hs21HGf0VvG99brk4xlFVRhw\nQnuPpM51GOs4vUtbLNSYnAuhU4ReXwwDV9Xu0MXAjfQMk2E3wQLGQ+82uv2B\n1hPD9nDHTtAdWrXd6ZjXFrG4Mf2MQBxySFvVSnFi6yStjfZeiKcD5Vg38PjG\nWg1O5N3/paq98HfH0Q4qNKGZljwo3oEQZzlYm2kVnI0PULoMYg==\n=ovAx\n-----END PGP MESSAGE-----\n',
              vssProof: 'be8198c2cef94cb381aa8aa9277a0a46ba5a54b0ac9930034b4af66b1b805234',
              i: 3,
              j: 1,
              uSig: '-----BEGIN PGP PUBLIC KEY BLOCK-----\n\nxk8EZBsq9RMFK4EEAAoCAwRgDpgm1bptRT5yLOMadcGAHuvkxISL8/3xPy/D\nYA+1NgBqIFK/3OOXxp73Tv86bt1dgH7OD1ACO0mVXAoX5EaVzWZvdmMtMS11\nc2VyLWRmMjQwOWY0NWNhMTZmNTU3OGNkMzAxMyA8b3ZjLTEtdXNlci1kZjI0\nMDlmNDVjYTE2ZjU1NzhjZDMwMTNAZGYyNDA5ZjQ1Y2ExNmY1NTc4Y2QzMDEz\nLmNvbT7CjAQQEwgAHQUCZBsq9QQLCQcIAxUICgQWAAIBAhkBAhsDAh4BACEJ\nEOUeC7QrGL+2FiEEjN6vPUFRweVtDKFZ5R4LtCsYv7Z7oAEAvNKaJrTIs3ky\nbxjkUicsuxOyA2oWGarJH6TGlWF6WD8A/3EIeZzPKSLaW+3enpbCPiU8RVDp\nC6yo6NMAqKp4XlyQzlMEZBsq9RIFK4EEAAoCAwQWlWPNMpk3afEzfgG0xFg4\nCEjY7fGO35n47nZ4qxjEXz2XN4I23xFMiwZbXbptDXlqm7W9ZAHKi892h+Yt\nU4B6AwEIB8J4BBgTCAAJBQJkGyr1AhsMACEJEOUeC7QrGL+2FiEEjN6vPUFR\nweVtDKFZ5R4LtCsYv7YqZwD8DLRuneB0HEzBCyrE4YL+UHaofoHQX1+nRh0j\n82qaDzAA/i3U1SoEIi32YMrorNG50vb4vaEPbbYmfglb+JQ1Yx7ozjMEZBsq\n9RYJKwYBBAHaRw8BAQdAS1534LdCC60ASpEgnEBC8pWPSOWWBLXyWaBhEOW2\n/ijCeAQYEwgACQUCZBsq9QIbIAAhCRDlHgu0Kxi/thYhBIzerz1BUcHlbQyh\nWeUeC7QrGL+2FUYA/AgMU4V7C7I/5HwK73mctaGXNdfVr/muRMNOGW/CAxVL\nAP9KaBr95o32oEN5eo7tWFByoOCCk9JMjs36Cq7o+sR31A==\n=eZ3B\n-----END PGP PUBLIC KEY BLOCK-----\n',
            },
          },
        },
      };
      await bitgo.coin('tsol').keychains().createTssBitGoKeyFromOvcShares(ovcOutputJson).should.be.rejected();
    });

    it('OvcShare codec decodes correctly', async function () {
      const test = {
        publicShare:
          '07bfbb052a1f4b106b315bd5a9d6a71604653289f861320d9801881e952f8550753c89511a360e3b599d9f239a8d36a4956437ed0b152180e22cb94fb08fdc81',
        privateShare:
          '-----BEGIN PGP MESSAGE-----\n\nwX4DUQ8XhGVGcTISAgMEd/wQ/AhwIRsSf/6iHwmHKvEYAwfaLtTQotS6BwZm\naX00T7LumGIrNZzBgcggBKMl+8Omom8mX5sP8FUE451iZjBmXlkpHlpAqskS\nEi5SfjlsT31utoLaBLA7NjNSmyYHIiyfh6YnwfV5U72k6hhfpuDSsQGy8Yxp\nM2dVlN4uoO31zgQPf+fgJkZvAPwvjYLBL4O8hs21HGf0VvG99brk4xlFVRhw\nQnuPpM51GOs4vUtbLNSYnAuhU4ReXwwDV9Xu0MXAjfQMk2E3wQLGQ+82uv2B\n1hPD9nDHTtAdWrXd6ZjXFrG4Mf2MQBxySFvVSnFi6yStjfZeiKcD5Vg38PjG\nWg1O5N3/paq98HfH0Q4qNKGZljwo3oEQZzlYm2kVnI0PULoMYg==\n=ovAx\n-----END PGP MESSAGE-----\n',
        vssProof: 'be8198c2cef94cb381aa8aa9277a0a46ba5a54b0ac9930034b4af66b1b805234',
        i: 3,
        j: 1,
      };
      const decodedRes = decodeOrElse(OvcShare.name, OvcShare, test, (errors) => {
        throw new Error(`error(s) parsing parsing json: ${errors}`);
      });
      assert(decodedRes);
    });
  });

  it('should generate backup without encryptedPrv when passphrase not provided', async () => {
    const scope = nock(bgUrl)
      .post('/api/v2/tltc/key', (body) => {
        return body.encryptedPrv === undefined && body.prv === undefined;
      })
      .reply(200);

    const backup = await keychains.createBackup({});
    scope.isDone().should.be.true();
    backup.should.not.have.property('encryptedPrv');
    backup.should.have.property('prv');
  });

  it('should generate backup and call endpoint with encryptedPrv when passphrase is provided', async () => {
    const scope = nock(bgUrl)
      .post('/api/v2/tltc/key', (body) => {
        return body.encryptedPrv !== undefined && body.prv === undefined;
      })
      .reply(200);

    const backup = await keychains.createBackup({ passphrase: 't3stSicretly!' });
    scope.isDone().should.be.true();
    backup.should.have.property('encryptedPrv');
    const decryptedPrv = bitgo.decrypt({ input: backup.encryptedPrv, password: 't3stSicretly!' });
    decryptedPrv.should.startWith('xprv');
  });
});

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


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