|
| 1 | +import nock = require('nock'); |
| 2 | +import * as sinon from 'sinon'; |
| 3 | +import * as assert from 'assert'; |
| 4 | +import 'should'; |
| 5 | + |
| 6 | +import { BitGoAPI } from '@bitgo/sdk-api'; |
| 7 | +import { TestBitGo } from '@bitgo/sdk-test'; |
| 8 | +import { Tbtc } from '@bitgo/sdk-coin-btc'; |
| 9 | +import { common, BaseCoin, BitGoBase, Wallet, WalletSignTransactionOptions } from '@bitgo/sdk-core'; |
| 10 | + |
| 11 | +describe('Wallet signTransaction with verifyTxParams', function () { |
| 12 | + let realWallet: Wallet; |
| 13 | + let basecoin: BaseCoin; |
| 14 | + |
| 15 | + beforeEach(function () { |
| 16 | + const bitgo = TestBitGo.decorate(BitGoAPI, { env: 'mock' }); |
| 17 | + bitgo.initializeTestVars(); |
| 18 | + bitgo.safeRegister('tbtc', Tbtc.createInstance); |
| 19 | + basecoin = bitgo.coin('tbtc'); |
| 20 | + |
| 21 | + // Real wallet data from tbtc testnet |
| 22 | + const realWalletData = { |
| 23 | + id: '6840948b17e91662b782d55bbf988c4e', |
| 24 | + coin: 'tbtc', |
| 25 | + label: 'Test: User & Backup Signing', |
| 26 | + m: 2, |
| 27 | + n: 3, |
| 28 | + keys: [ |
| 29 | + '6840947d037fdb798e0bf860e52cc4a8', |
| 30 | + '6840947e7c18efe3b0b77e9a75308aab', |
| 31 | + '68409480bdf143f4a1d32474acc09baa', |
| 32 | + ], |
| 33 | + multisigType: 'onchain', |
| 34 | + type: 'hot', |
| 35 | + balance: 329034, |
| 36 | + balanceString: '329034', |
| 37 | + confirmedBalance: 329034, |
| 38 | + confirmedBalanceString: '329034', |
| 39 | + spendableBalance: 329034, |
| 40 | + spendableBalanceString: '329034', |
| 41 | + }; |
| 42 | + sinon.stub(basecoin, 'signTransaction').resolves({ txHex: 'signed' }); |
| 43 | + |
| 44 | + realWallet = new Wallet(bitgo as unknown as BitGoBase, basecoin as unknown as BaseCoin, realWalletData); |
| 45 | + }); |
| 46 | + |
| 47 | + afterEach(function () { |
| 48 | + nock.cleanAll(); |
| 49 | + }); |
| 50 | + |
| 51 | + it('should fail verification when expected recipient does not match actual transaction recipient', async function () { |
| 52 | + // Create a spy for verifyUserPublicKey to always return true |
| 53 | + |
| 54 | + // Real transaction hex that sends to tb1p3dcj78muwwac8fvas43zr48vca70pue3eku6txvju0nxnhm0rgwsgv0k3v |
| 55 | + const realTxHex = |
| 56 | + '70736274ff0100890100000001e082ee5f3be60a260bd181d86cbc3ed1f2f53f6e33572138c3220a461d64e13d0100000000fdffffff0210270000000000002251208b712f1f7c73bb83a59d856221d4ecc77cf0f331cdb9a59992e3e669df6f1a1de3d5040000000000225120135ddb1d9e6e1987adacd341cb2e146544e69e9b96eed7aec27a4a03d83ac324000000004f010488b21e000000000000000000940a6f6c2d84214ba69e48354858dd8e4df2b0f36d51b6721516172c4b56922402c8d26710504a5a7965c8fce430057418802bc5a06e1987ede6897fb40e0a66b5044b8de8914f010488b21e0000000000000000009c6426c55cb8e0b186f7f776b42cb4de8118be401cce288bcb1ef70457f4072c0397f25ebd3f03c85333b1ffc14e04d051b476a5e48e4e0a5e999c0cf163c3178704758af64b4f010488b21e000000000000000000ab9a6eea233e963b74fd79afd9c71d467fb1ce1d91fe1681e2b7332e203baae6033e70fb09c6eb45d08aff2f067060c6345143918961d5bee0e1d8037b77001925047b156bd00001012b8e02050000000000225120a385cb8dc799daaffb15a3e311e1465b3ce990017034ec4e1e2056575a3f609001030400000000211686f3450713d04e8ac343e576c1df80c5a1fca4e45aff6ee6447ceeb4d0d51d671500758af64b000000000000000029000000110000002116b7d96086e8b6763162b7deb2a0149d104d01a2ddad547f290822d304dd6bd14815004b8de89100000000000000002900000011000000011720cd77b37b43fe3ae9cdcb266faaf4b443766269f3182f6bb40f3a9db93f8384d7011820b558f0176c61865e960bbd14bff1e251b99cebb81fea61c0f931452dc029778648fc05424954474f01a385cb8dc799daaffb15a3e311e1465b3ce990017034ec4e1e2056575a3f6090cd77b37b43fe3ae9cdcb266faaf4b443766269f3182f6bb40f3a9db93f8384d7420286f3450713d04e8ac343e576c1df80c5a1fca4e45aff6ee6447ceeb4d0d51d6703b7d96086e8b6763162b7deb2a0149d104d01a2ddad547f290822d304dd6bd148000001052054977fb64552af2ac9901f8ca6d43e9980c64af82fa0bc24f01a7a95328f4d6901068e01c044202a24989f02cd1e9cf2560b48b3c7122c8a643e9ce22c6406a0d65b8f75053c9fad200665676f5990a63c2c73e626b3fc74156c4ef9a909ebd66ca83f7797602dcd26ac01c044200665676f5990a63c2c73e626b3fc74156c4ef9a909ebd66ca83f7797602dcd26ad2002617797bd49c31b5aa0c954b88b6f0cf366d876863db7a94285a2ed1f22b3c3ac210702617797bd49c31b5aa0c954b88b6f0cf366d876863db7a94285a2ed1f22b3c33501df60ca0827941d266e210000354802a610ce1c59b91616c99c795c74eb230ef94b8de8910000000000000000290000001500000021070665676f5990a63c2c73e626b3fc74156c4ef9a909ebd66ca83f7797602dcd26550246660e42faa7f1e691d65610dca0ca5e16a9c2230ac1188e164539092e81d885df60ca0827941d266e210000354802a610ce1c59b91616c99c795c74eb230ef97b156bd00000000000000000290000001500000021072a24989f02cd1e9cf2560b48b3c7122c8a643e9ce22c6406a0d65b8f75053c9f350146660e42faa7f1e691d65610dca0ca5e16a9c2230ac1188e164539092e81d885758af64b0000000000000000290000001500000000'; |
| 57 | + |
| 58 | + const txPrebuild = { |
| 59 | + txHex: realTxHex, |
| 60 | + walletId: '6840948b17e91662b782d55bbf988c4e', |
| 61 | + }; |
| 62 | + |
| 63 | + // Verification parameters with wrong expected recipient |
| 64 | + const verifyTxParams = { |
| 65 | + txParams: { |
| 66 | + recipients: [ |
| 67 | + { |
| 68 | + address: '2Muux9UnVFCiGaYbX8D8FTsKaErkhLXRX5n', // Expected recipient |
| 69 | + amount: '10000', // Expected amount |
| 70 | + }, |
| 71 | + ], |
| 72 | + type: 'send', |
| 73 | + }, |
| 74 | + }; |
| 75 | + const bgUrl = common.Environments['mock'].uri; |
| 76 | + nock(bgUrl).get(`/api/v2/tbtc/key/6840947d037fdb798e0bf860e52cc4a8`).reply(200, { |
| 77 | + id: '6840947d037fdb798e0bf860e52cc4a8', |
| 78 | + pub: 'xpub661MyMwAqRbcG6k1Jmauo273gsqQSv8z94FHSkkPVbxZCPcCkVvCTVCBe1VF6A8rc4Dzo31LCB8rx2L3LedefymRcsmQTVNbH7QNvj2HY9B', |
| 79 | + }); |
| 80 | + |
| 81 | + nock(bgUrl).get(`/api/v2/tbtc/key/6840947e7c18efe3b0b77e9a75308aab`).reply(200, { |
| 82 | + id: '6840947e7c18efe3b0b77e9a75308aab', |
| 83 | + pub: 'xpub661MyMwAqRbcGFXVKNXUMN5UEy7kYYGjg8AN4HXVndnkXRrFnWPGtagUmcFDUhMy9RcxC65eQp5cSaGMhRfEfkt7sfU6cYmiakafzwGLRkq', |
| 84 | + }); |
| 85 | + |
| 86 | + nock(bgUrl).get(`/api/v2/tbtc/key/68409480bdf143f4a1d32474acc09baa`).reply(200, { |
| 87 | + id: '68409480bdf143f4a1d32474acc09baa', |
| 88 | + pub: 'xpub661MyMwAqRbcG1vLCx1SHbu94HL7LwAtguFD7bcwYSPbPMexPZbKEgNcWk3QREc8s4mV5MPh9ZX8UbQVxzY3no9GUo4HwCQdY8u3U4Hx5vR', |
| 89 | + }); |
| 90 | + |
| 91 | + const signParams: WalletSignTransactionOptions = { |
| 92 | + txPrebuild, |
| 93 | + verifyTxParams, |
| 94 | + }; |
| 95 | + |
| 96 | + try { |
| 97 | + await realWallet.signTransaction(signParams); |
| 98 | + assert.fail('Should have thrown verification error'); |
| 99 | + } catch (error) { |
| 100 | + assert.ok( |
| 101 | + error.message.includes('expected outputs missing in transaction prebuild'), |
| 102 | + `Error message should contain 'expected outputs missing in transaction prebuild', got: ${error.message}` |
| 103 | + ); |
| 104 | + } |
| 105 | + }); |
| 106 | + |
| 107 | + it('should pass verification when expected recipient matches actual transaction recipient', async function () { |
| 108 | + // Same real transaction hex |
| 109 | + const realTxHex = |
| 110 | + '70736274ff0100890100000001c30076456d211a7c951bdfce1ee3c51d4fbba63effe7b72a7e598355360258810100000000fdffffff02e8030000000000002251203c8294de47961d271c0cc3a1d2a45f9debb4ff1414a58b1c63fb685ab27e47bd60cc0400000000002251206c59f342df3d02bdc20820489c963d38ac799f7559d6fa66635a65b017b51a55000000004f010488b21e000000000000000000940a6f6c2d84214ba69e48354858dd8e4df2b0f36d51b6721516172c4b56922402c8d26710504a5a7965c8fce430057418802bc5a06e1987ede6897fb40e0a66b5044b8de8914f010488b21e0000000000000000009c6426c55cb8e0b186f7f776b42cb4de8118be401cce288bcb1ef70457f4072c0397f25ebd3f03c85333b1ffc14e04d051b476a5e48e4e0a5e999c0cf163c3178704758af64b4f010488b21e000000000000000000ab9a6eea233e963b74fd79afd9c71d467fb1ce1d91fe1681e2b7332e203baae6033e70fb09c6eb45d08aff2f067060c6345143918961d5bee0e1d8037b77001925047b156bd00001012be3d504000000000022512056300043d90ac87b8cec52c013e4f4e005673efdb4f81187a686ab2e5312e013010304000000002116174f8b9e8e39aa567741a082d6aa3dc1c54eeef21a129cb6c835d0d00cb8f6f715004b8de89100000000000000002900000016000000211669a5e6ddfd5c5b7ef2ab67f945f2f80b4bb31975d66835dd388812bf76a2c1f91500758af64b00000000000000002900000016000000011720674c90f97968e7cb8ea607836bd032c5091ad04f6705b017c4fa640e0b977528011820b13af832b77b05ad8ec03628e865fe1543666d1db165c2c5be0eee2f2be911c048fc05424954474f0156300043d90ac87b8cec52c013e4f4e005673efdb4f81187a686ab2e5312e013674c90f97968e7cb8ea607836bd032c5091ad04f6705b017c4fa640e0b977528420269a5e6ddfd5c5b7ef2ab67f945f2f80b4bb31975d66835dd388812bf76a2c1f903174f8b9e8e39aa567741a082d6aa3dc1c54eeef21a129cb6c835d0d00cb8f6f7000001052058d7526ec06446670ed4a962867a23107515aba6c112d492a7f020eea7a85cdb01068e01c04420b7a3d89bb4d477aa4b47e393bace553aefd17bd1f8c51f1810dac3b8aa517f4ead204532ea3e0b6d89457d1ffe4b44cd9f0028f43a1f40f0e91e8306745fbcf86fb8ac01c044204532ea3e0b6d89457d1ffe4b44cd9f0028f43a1f40f0e91e8306745fbcf86fb8ad2087c6ec2bd90ee3141494456ff1525f5bf62381e9a66630ee2ce2f71398b1b4c0ac21074532ea3e0b6d89457d1ffe4b44cd9f0028f43a1f40f0e91e8306745fbcf86fb855020e054d24e61547905ebdad16202ad61cdc52ff424001d211bfec1c954f6cbcee793c053d56fe57a1d343047cb823edb85ba32c742fc0e39d921edf776a38427b7b156bd000000000000000002900000017000000210787c6ec2bd90ee3141494456ff1525f5bf62381e9a66630ee2ce2f71398b1b4c03501793c053d56fe57a1d343047cb823edb85ba32c742fc0e39d921edf776a38427b4b8de891000000000000000029000000170000002107b7a3d89bb4d477aa4b47e393bace553aefd17bd1f8c51f1810dac3b8aa517f4e35010e054d24e61547905ebdad16202ad61cdc52ff424001d211bfec1c954f6cbcee758af64b0000000000000000290000001700000000'; |
| 111 | + const bgUrl = common.Environments['mock'].uri; |
| 112 | + nock(bgUrl).get(`/api/v2/tbtc/key/6840947d037fdb798e0bf860e52cc4a8`).reply(200, { |
| 113 | + id: '6840947d037fdb798e0bf860e52cc4a8', |
| 114 | + pub: 'xpub661MyMwAqRbcG6k1Jmauo273gsqQSv8z94FHSkkPVbxZCPcCkVvCTVCBe1VF6A8rc4Dzo31LCB8rx2L3LedefymRcsmQTVNbH7QNvj2HY9B', |
| 115 | + }); |
| 116 | + |
| 117 | + nock(bgUrl).get(`/api/v2/tbtc/key/6840947e7c18efe3b0b77e9a75308aab`).reply(200, { |
| 118 | + id: '6840947e7c18efe3b0b77e9a75308aab', |
| 119 | + pub: 'xpub661MyMwAqRbcGFXVKNXUMN5UEy7kYYGjg8AN4HXVndnkXRrFnWPGtagUmcFDUhMy9RcxC65eQp5cSaGMhRfEfkt7sfU6cYmiakafzwGLRkq', |
| 120 | + }); |
| 121 | + |
| 122 | + nock(bgUrl).get(`/api/v2/tbtc/key/68409480bdf143f4a1d32474acc09baa`).reply(200, { |
| 123 | + id: '68409480bdf143f4a1d32474acc09baa', |
| 124 | + pub: 'xpub661MyMwAqRbcG1vLCx1SHbu94HL7LwAtguFD7bcwYSPbPMexPZbKEgNcWk3QREc8s4mV5MPh9ZX8UbQVxzY3no9GUo4HwCQdY8u3U4Hx5vR', |
| 125 | + }); |
| 126 | + |
| 127 | + const txPrebuild = { |
| 128 | + txHex: realTxHex, |
| 129 | + walletId: '6840948b17e91662b782d55bbf988c4e', |
| 130 | + }; |
| 131 | + |
| 132 | + // Verification parameters with correct expected recipient |
| 133 | + const verifyTxParams: WalletSignTransactionOptions['verifyTxParams'] = { |
| 134 | + txParams: { |
| 135 | + recipients: [ |
| 136 | + { |
| 137 | + address: 'tb1p8jpffhj8jcwjw8qvcwsa9fzlnh4mflc5zjjck8rrld594vn7g77slm250x', // Correct recipient |
| 138 | + amount: '1000', // Expected amount |
| 139 | + }, |
| 140 | + ], |
| 141 | + type: 'send', |
| 142 | + }, |
| 143 | + }; |
| 144 | + |
| 145 | + const signParams: WalletSignTransactionOptions = { |
| 146 | + txPrebuild, |
| 147 | + verifyTxParams, |
| 148 | + pubs: [ |
| 149 | + 'xpub661MyMwAqRbcG6k1Jmauo273gsqQSv8z94FHSkkPVbxZCPcCkVvCTVCBe1VF6A8rc4Dzo31LCB8rx2L3LedefymRcsmQTVNbH7QNvj2HY9B', |
| 150 | + 'xpub661MyMwAqRbcGFXVKNXUMN5UEy7kYYGjg8AN4HXVndnkXRrFnWPGtagUmcFDUhMy9RcxC65eQp5cSaGMhRfEfkt7sfU6cYmiakafzwGLRkq', |
| 151 | + 'xpub661MyMwAqRbcG1vLCx1SHbu94HL7LwAtguFD7bcwYSPbPMexPZbKEgNcWk3QREc8s4mV5MPh9ZX8UbQVxzY3no9GUo4HwCQdY8u3U4Hx5vR', |
| 152 | + ], |
| 153 | + prv: 'prv', |
| 154 | + }; |
| 155 | + |
| 156 | + const result = await realWallet.signTransaction(signParams); |
| 157 | + |
| 158 | + // Verify the result |
| 159 | + result.should.have.property('txHex', 'signed'); |
| 160 | + }); |
| 161 | +}); |
0 commit comments