Skip to content

Commit 70e60f4

Browse files
OttoAllmendingerllm-git
andcommitted
refactor(abstract-utxo): move custom change wallet tests to separate file
Move tests related to custom change wallets to their own test file for better organization. These tests were previously part of the general abstract UTXO coin test file. Issue: BTC-2652 Co-authored-by: llm-git <[email protected]>
1 parent d717184 commit 70e60f4

File tree

2 files changed

+138
-132
lines changed

2 files changed

+138
-132
lines changed

modules/abstract-utxo/test/unit/abstractUtxoCoin.ts

Lines changed: 1 addition & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import should = require('should');
33
import * as sinon from 'sinon';
44
import { Wallet, UnexpectedAddressError, VerificationOptions, Triple } from '@bitgo/sdk-core';
55

6-
import { UtxoWallet, Output, TransactionExplanation, TransactionParams, generateAddress } from '../../src';
6+
import { UtxoWallet, Output, TransactionExplanation, TransactionParams } from '../../src';
77

88
import { bip322Fixtures } from './fixtures/bip322/fixtures';
99
import { psbtTxHex } from './fixtures/psbtHexProof';
@@ -116,137 +116,6 @@ describe('Abstract UTXO Coin:', () => {
116116
});
117117
});
118118

119-
describe('Custom Change Wallets', () => {
120-
const coin = getUtxoCoin('tbtc');
121-
122-
const keys = {
123-
send: {
124-
user: { id: '0', key: coin.keychains().create() },
125-
backup: { id: '1', key: coin.keychains().create() },
126-
bitgo: { id: '2', key: coin.keychains().create() },
127-
},
128-
change: {
129-
user: { id: '3', key: coin.keychains().create() },
130-
backup: { id: '4', key: coin.keychains().create() },
131-
bitgo: { id: '5', key: coin.keychains().create() },
132-
},
133-
};
134-
135-
const customChangeKeySignatures = {
136-
user: '',
137-
backup: '',
138-
bitgo: '',
139-
};
140-
141-
const addressData = {
142-
chain: 11,
143-
index: 1,
144-
addressType: 'p2shP2wsh' as const,
145-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
146-
keychains: [
147-
{ pub: keys.change.user.key.pub! },
148-
{ pub: keys.change.backup.key.pub! },
149-
{ pub: keys.change.bitgo.key.pub! },
150-
],
151-
threshold: 2,
152-
};
153-
154-
const { address: changeAddress, coinSpecific } = generateAddress(coin.network, coin.getChain(), addressData);
155-
156-
const changeWalletId = 'changeWalletId';
157-
const stubData = {
158-
signedSendingWallet: {
159-
keyIds: sinon.stub().returns([keys.send.user.id, keys.send.backup.id, keys.send.bitgo.id]),
160-
coinSpecific: sinon.stub().returns({ customChangeWalletId: changeWalletId }),
161-
},
162-
changeWallet: {
163-
keyIds: sinon.stub().returns([keys.change.user.id, keys.change.backup.id, keys.change.bitgo.id]),
164-
createAddress: sinon.stub().resolves(changeAddress),
165-
},
166-
};
167-
168-
before(async () => {
169-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
170-
const sign = async ({ key }) =>
171-
(await coin.signMessage({ prv: keys.send.user.key.prv }, key.pub!)).toString('hex');
172-
customChangeKeySignatures.user = await sign(keys.change.user);
173-
customChangeKeySignatures.backup = await sign(keys.change.backup);
174-
customChangeKeySignatures.bitgo = await sign(keys.change.bitgo);
175-
});
176-
177-
it('should consider addresses derived from the custom change keys as internal spends', async () => {
178-
const signedSendingWallet = sinon.createStubInstance(Wallet, stubData.signedSendingWallet as any);
179-
const changeWallet = sinon.createStubInstance(Wallet, stubData.changeWallet as any);
180-
181-
sinon.stub(coin, 'keychains').returns({
182-
get: sinon.stub().callsFake(({ id }) => {
183-
switch (id) {
184-
case keys.send.user.id:
185-
return Promise.resolve({ id, ...keys.send.user.key });
186-
case keys.send.backup.id:
187-
return Promise.resolve({ id, ...keys.send.backup.key });
188-
case keys.send.bitgo.id:
189-
return Promise.resolve({ id, ...keys.send.bitgo.key });
190-
case keys.change.user.id:
191-
return Promise.resolve({ id, ...keys.change.user.key });
192-
case keys.change.backup.id:
193-
return Promise.resolve({ id, ...keys.change.backup.key });
194-
case keys.change.bitgo.id:
195-
return Promise.resolve({ id, ...keys.change.bitgo.key });
196-
}
197-
}),
198-
} as any);
199-
200-
sinon.stub(coin, 'wallets').returns({
201-
get: sinon.stub().callsFake(() => Promise.resolve(changeWallet)),
202-
} as any);
203-
204-
const outputAmount = 10000;
205-
const recipients = [];
206-
207-
sinon.stub(coin, 'explainTransaction').resolves({
208-
outputs: [],
209-
changeOutputs: [
210-
{
211-
address: changeAddress,
212-
amount: outputAmount,
213-
},
214-
],
215-
} as any);
216-
217-
signedSendingWallet._wallet = signedSendingWallet._wallet || {
218-
customChangeKeySignatures,
219-
};
220-
221-
const parsedTransaction = await coin.parseTransaction({
222-
txParams: { changeAddress, recipients },
223-
txPrebuild: { txHex: '' },
224-
wallet: signedSendingWallet as any,
225-
verification: {
226-
addresses: {
227-
[changeAddress]: {
228-
coinSpecific,
229-
chain: addressData.chain,
230-
index: addressData.index,
231-
},
232-
},
233-
},
234-
});
235-
236-
should.exist(parsedTransaction.outputs[0]);
237-
parsedTransaction.outputs[0].should.deepEqual({
238-
address: changeAddress,
239-
amount: outputAmount,
240-
external: false,
241-
needsCustomChangeKeySignatureVerification: true,
242-
});
243-
244-
(coin.explainTransaction as any).restore();
245-
(coin.wallets as any).restore();
246-
(coin.keychains as any).restore();
247-
});
248-
});
249-
250119
describe('Verify Transaction', () => {
251120
const coin = getUtxoCoin('tbtc');
252121

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import should = require('should');
2+
import * as sinon from 'sinon';
3+
import { Wallet } from '@bitgo/sdk-core';
4+
5+
import { generateAddress } from '../../src';
6+
7+
import { getUtxoCoin } from './util';
8+
9+
describe('Custom Change Wallets', () => {
10+
const coin = getUtxoCoin('tbtc');
11+
12+
const keys = {
13+
send: {
14+
user: { id: '0', key: coin.keychains().create() },
15+
backup: { id: '1', key: coin.keychains().create() },
16+
bitgo: { id: '2', key: coin.keychains().create() },
17+
},
18+
change: {
19+
user: { id: '3', key: coin.keychains().create() },
20+
backup: { id: '4', key: coin.keychains().create() },
21+
bitgo: { id: '5', key: coin.keychains().create() },
22+
},
23+
};
24+
25+
const customChangeKeySignatures = {
26+
user: '',
27+
backup: '',
28+
bitgo: '',
29+
};
30+
31+
const addressData = {
32+
chain: 11,
33+
index: 1,
34+
addressType: 'p2shP2wsh' as const,
35+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
36+
keychains: [
37+
{ pub: keys.change.user.key.pub! },
38+
{ pub: keys.change.backup.key.pub! },
39+
{ pub: keys.change.bitgo.key.pub! },
40+
],
41+
threshold: 2,
42+
};
43+
44+
const { address: changeAddress, coinSpecific } = generateAddress(coin.network, coin.getChain(), addressData);
45+
46+
const changeWalletId = 'changeWalletId';
47+
const stubData = {
48+
signedSendingWallet: {
49+
keyIds: sinon.stub().returns([keys.send.user.id, keys.send.backup.id, keys.send.bitgo.id]),
50+
coinSpecific: sinon.stub().returns({ customChangeWalletId: changeWalletId }),
51+
},
52+
changeWallet: {
53+
keyIds: sinon.stub().returns([keys.change.user.id, keys.change.backup.id, keys.change.bitgo.id]),
54+
createAddress: sinon.stub().resolves(changeAddress),
55+
},
56+
};
57+
58+
before(async () => {
59+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
60+
const sign = async ({ key }) => (await coin.signMessage({ prv: keys.send.user.key.prv }, key.pub!)).toString('hex');
61+
customChangeKeySignatures.user = await sign(keys.change.user);
62+
customChangeKeySignatures.backup = await sign(keys.change.backup);
63+
customChangeKeySignatures.bitgo = await sign(keys.change.bitgo);
64+
});
65+
66+
it('should consider addresses derived from the custom change keys as internal spends', async () => {
67+
const signedSendingWallet = sinon.createStubInstance(Wallet, stubData.signedSendingWallet as any);
68+
const changeWallet = sinon.createStubInstance(Wallet, stubData.changeWallet as any);
69+
70+
sinon.stub(coin, 'keychains').returns({
71+
get: sinon.stub().callsFake(({ id }) => {
72+
switch (id) {
73+
case keys.send.user.id:
74+
return Promise.resolve({ id, ...keys.send.user.key });
75+
case keys.send.backup.id:
76+
return Promise.resolve({ id, ...keys.send.backup.key });
77+
case keys.send.bitgo.id:
78+
return Promise.resolve({ id, ...keys.send.bitgo.key });
79+
case keys.change.user.id:
80+
return Promise.resolve({ id, ...keys.change.user.key });
81+
case keys.change.backup.id:
82+
return Promise.resolve({ id, ...keys.change.backup.key });
83+
case keys.change.bitgo.id:
84+
return Promise.resolve({ id, ...keys.change.bitgo.key });
85+
}
86+
}),
87+
} as any);
88+
89+
sinon.stub(coin, 'wallets').returns({
90+
get: sinon.stub().callsFake(() => Promise.resolve(changeWallet)),
91+
} as any);
92+
93+
const outputAmount = 10000;
94+
const recipients = [];
95+
96+
sinon.stub(coin, 'explainTransaction').resolves({
97+
outputs: [],
98+
changeOutputs: [
99+
{
100+
address: changeAddress,
101+
amount: outputAmount,
102+
},
103+
],
104+
} as any);
105+
106+
signedSendingWallet._wallet = signedSendingWallet._wallet || {
107+
customChangeKeySignatures,
108+
};
109+
110+
const parsedTransaction = await coin.parseTransaction({
111+
txParams: { changeAddress, recipients },
112+
txPrebuild: { txHex: '' },
113+
wallet: signedSendingWallet as any,
114+
verification: {
115+
addresses: {
116+
[changeAddress]: {
117+
coinSpecific,
118+
chain: addressData.chain,
119+
index: addressData.index,
120+
},
121+
},
122+
},
123+
});
124+
125+
should.exist(parsedTransaction.outputs[0]);
126+
parsedTransaction.outputs[0].should.deepEqual({
127+
address: changeAddress,
128+
amount: outputAmount,
129+
external: false,
130+
needsCustomChangeKeySignatureVerification: true,
131+
});
132+
133+
(coin.explainTransaction as any).restore();
134+
(coin.wallets as any).restore();
135+
(coin.keychains as any).restore();
136+
});
137+
});

0 commit comments

Comments
 (0)