Skip to content

Commit 2743446

Browse files
Merge pull request #8104 from BitGo/BTC-2650.rewrite-address-tests
refactor(abstract-utxo): replace redundant address test
2 parents 3d31e1f + 73f29a5 commit 2743446

21 files changed

Lines changed: 141 additions & 1719 deletions
Lines changed: 141 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,167 +1,165 @@
1-
import 'should';
21
import * as assert from 'assert';
32

4-
import * as utxolib from '@bitgo/utxo-lib';
5-
const { chainCodes } = utxolib.bitgo;
3+
import { InvalidAddressDerivationPropertyError, UnexpectedAddressError } from '@bitgo/sdk-core';
64

7-
import { AbstractUtxoCoin, GenerateFixedScriptAddressOptions, generateAddress } from '../../src';
5+
import { assertFixedScriptWalletAddress, generateAddress } from '../../src';
86

9-
import { utxoCoins, keychains as keychainsBip32, getFixture, shouldEqualJSON } from './util';
7+
import { keychainsBase58 } from './util';
108

11-
// TODO (@rushilbg): Delete these tests because they are redundant (similar tests are in utxo-lib)
12-
function isCompatibleAddress(a: AbstractUtxoCoin, b: AbstractUtxoCoin): boolean {
13-
if (a === b) {
14-
return true;
15-
}
16-
switch (a.getChain()) {
17-
case 'btc':
18-
case 'bsv':
19-
case 'bch':
20-
case 'bcha':
21-
return ['btc', 'bsv', 'bch', 'bcha'].includes(b.getChain());
22-
case 'tbtc':
23-
case 'tbtcsig':
24-
case 'tbtc4':
25-
case 'tbtcbgsig':
26-
case 'tbsv':
27-
case 'tbch':
28-
case 'tdoge':
29-
case 'tbcha':
30-
return ['tbtc', 'tbtcsig', 'tbtc4', 'tbtcbgsig', 'tbsv', 'tbch', 'tbcha', 'tdoge'].includes(b.getChain());
31-
default:
32-
return false;
33-
}
34-
}
9+
const keychains = keychainsBase58.map((k) => ({ pub: k.pub }));
3510

36-
function run(coin: AbstractUtxoCoin) {
37-
const keychains = keychainsBip32.map((k) => ({ pub: k.neutered().toBase58() }));
38-
39-
function getParameters(): GenerateFixedScriptAddressOptions[] {
40-
return [undefined, ...chainCodes].map((chain) => ({ keychains, chain }));
41-
}
42-
43-
describe(`UTXO Addresses ${coin.getChain()}`, function () {
44-
it('address support', function () {
45-
const supportedAddressTypes = utxolib.bitgo.outputScripts.scriptTypes2Of3.filter((t) =>
46-
coin.supportsAddressType(t)
11+
describe('assertFixedScriptWalletAddress', function () {
12+
describe('input validation', function () {
13+
it('throws InvalidAddressDerivationPropertyError when both chain and index are undefined', function () {
14+
assert.throws(
15+
() =>
16+
assertFixedScriptWalletAddress('btc', {
17+
chain: undefined,
18+
index: undefined as unknown as number,
19+
keychains,
20+
format: 'base58',
21+
address: 'anything',
22+
}),
23+
InvalidAddressDerivationPropertyError
4724
);
48-
switch (coin.getChain()) {
49-
case 'btc':
50-
case 'tbtc':
51-
case 'tbtcsig':
52-
case 'tbtc4':
53-
case 'tbtcbgsig':
54-
supportedAddressTypes.should.eql(['p2sh', 'p2shP2wsh', 'p2wsh', 'p2tr', 'p2trMusig2']);
55-
break;
56-
case 'btg':
57-
case 'tbtg':
58-
case 'ltc':
59-
case 'tltc':
60-
supportedAddressTypes.should.eql(['p2sh', 'p2shP2wsh', 'p2wsh']);
61-
break;
62-
case 'bch':
63-
case 'tbch':
64-
case 'bcha':
65-
case 'tbcha':
66-
case 'bsv':
67-
case 'tbsv':
68-
case 'dash':
69-
case 'tdash':
70-
case 'doge':
71-
case 'tdoge':
72-
case 'zec':
73-
case 'tzec':
74-
supportedAddressTypes.should.eql(['p2sh']);
75-
break;
76-
default:
77-
throw new Error(`unexpected coin ${coin.getChain()}`);
78-
}
7925
});
8026

81-
it('generates address matching the fixtures', async function () {
82-
const addresses = getParameters().map((p) => {
83-
const label = { chain: p.chain === undefined ? 'default' : p.chain };
84-
try {
85-
return [label, generateAddress(coin.name, p)];
86-
} catch (e) {
87-
return [label, { error: e.message }];
88-
}
89-
});
90-
91-
shouldEqualJSON(addresses, await getFixture(coin, 'addresses-by-chain', addresses));
27+
it('throws InvalidAddressDerivationPropertyError when chain is non-finite', function () {
28+
assert.throws(
29+
() =>
30+
assertFixedScriptWalletAddress('btc', {
31+
chain: Infinity,
32+
index: 0,
33+
keychains,
34+
format: 'base58',
35+
address: 'anything',
36+
}),
37+
InvalidAddressDerivationPropertyError
38+
);
9239
});
9340

94-
it('validates and verifies generated addresses', function () {
95-
getParameters().forEach((p) => {
96-
if (p.chain && !coin.supportsAddressChain(p.chain)) {
97-
assert.throws(() => generateAddress(coin.name, p));
98-
return;
99-
}
100-
101-
const address = generateAddress(coin.name, p);
102-
coin.isValidAddress(address).should.eql(true);
103-
if (address !== address.toUpperCase()) {
104-
coin.isValidAddress(address.toUpperCase()).should.eql(false);
105-
}
106-
coin.verifyAddress({ address, keychains });
107-
});
41+
it('throws InvalidAddressDerivationPropertyError when index is non-finite', function () {
42+
assert.throws(
43+
() =>
44+
assertFixedScriptWalletAddress('btc', {
45+
chain: 0,
46+
index: NaN,
47+
keychains,
48+
format: 'base58',
49+
address: 'anything',
50+
}),
51+
InvalidAddressDerivationPropertyError
52+
);
10853
});
10954

110-
it('defaults to canonical address', function () {
111-
getParameters().forEach((p) => {
112-
if (!p.chain || coin.supportsAddressChain(p.chain)) {
113-
const address = generateAddress(coin.name, p);
114-
coin.canonicalAddress(address).should.eql(address);
115-
}
116-
});
55+
it('throws when keychains is missing', function () {
56+
assert.throws(
57+
() =>
58+
assertFixedScriptWalletAddress('btc', {
59+
chain: 0,
60+
index: 0,
61+
keychains: undefined as unknown as { pub: string }[],
62+
format: 'base58',
63+
address: 'anything',
64+
}),
65+
/missing required param keychains/
66+
);
11767
});
68+
});
11869

119-
it('respects format parameter', function () {
120-
// Only test coins that actually support multiple address formats (BCH/BCHA)
121-
// These are the only coins where the format parameter matters
122-
const cashaddrPrefixes: Record<string, string> = {
123-
bch: 'bitcoincash:',
124-
tbch: 'bchtest:',
125-
bcha: 'ecash:',
126-
tbcha: 'ectest:',
127-
};
70+
describe('address matching', function () {
71+
it('throws UnexpectedAddressError when address does not match derived address', function () {
72+
assert.throws(
73+
() =>
74+
assertFixedScriptWalletAddress('btc', {
75+
chain: 0,
76+
index: 0,
77+
keychains,
78+
format: 'base58',
79+
address: '3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
80+
}),
81+
UnexpectedAddressError
82+
);
83+
});
12884

129-
const expectedPrefix = cashaddrPrefixes[coin.getChain()];
130-
if (!expectedPrefix) {
131-
this.skip();
132-
}
85+
it('succeeds for btc p2sh (chain 0)', function () {
86+
const address = generateAddress('btc', { keychains, chain: 0 });
87+
assert.doesNotThrow(() =>
88+
assertFixedScriptWalletAddress('btc', {
89+
chain: 0,
90+
index: 0,
91+
keychains,
92+
format: 'base58',
93+
address,
94+
})
95+
);
96+
});
13397

134-
const chain = chainCodes[0];
135-
const params = { keychains, chain };
98+
it('succeeds for btc p2wsh (chain 20)', function () {
99+
const address = generateAddress('btc', { keychains, chain: 20 });
100+
assert.doesNotThrow(() =>
101+
assertFixedScriptWalletAddress('btc', {
102+
chain: 20,
103+
index: 0,
104+
keychains,
105+
format: 'base58',
106+
address,
107+
})
108+
);
109+
});
136110

137-
// Generate with cashaddr format
138-
const addressCashaddr = generateAddress(coin.name, { ...params, format: 'cashaddr' });
139-
coin.isValidAddress(addressCashaddr).should.eql(true);
140-
addressCashaddr.should.startWith(expectedPrefix, `cashaddr should start with ${expectedPrefix}`);
111+
it('succeeds for btc p2tr (chain 40)', function () {
112+
const address = generateAddress('btc', { keychains, chain: 40 });
113+
assert.doesNotThrow(() =>
114+
assertFixedScriptWalletAddress('btc', {
115+
chain: 40,
116+
index: 0,
117+
keychains,
118+
format: 'base58',
119+
address,
120+
})
121+
);
122+
});
141123

142-
// Generate with base58 format explicitly
143-
const addressBase58 = generateAddress(coin.name, { ...params, format: 'base58' });
144-
coin.isValidAddress(addressBase58).should.eql(true);
145-
addressBase58.should.not.match(/.*:.*/, 'base58 should not contain colon separator');
124+
it('succeeds for bch cashaddr (chain 0)', function () {
125+
const address = generateAddress('bch', { keychains, chain: 0, format: 'cashaddr' });
126+
assert.doesNotThrow(() =>
127+
assertFixedScriptWalletAddress('bch', {
128+
chain: 0,
129+
index: 0,
130+
keychains,
131+
format: 'cashaddr',
132+
address,
133+
})
134+
);
135+
});
146136

147-
// Verify formats produce different strings
148-
addressCashaddr.should.not.equal(addressBase58, 'cashaddr and base58 should produce different address strings');
137+
it('succeeds for a non-zero index', function () {
138+
const address = generateAddress('btc', { keychains, chain: 0, index: 5 });
139+
assert.doesNotThrow(() =>
140+
assertFixedScriptWalletAddress('btc', {
141+
chain: 0,
142+
index: 5,
143+
keychains,
144+
format: 'base58',
145+
address,
146+
})
147+
);
149148
});
150149

151-
utxoCoins.forEach((otherCoin) => {
152-
it(`has expected address compatability with ${otherCoin.getChain()}`, async function () {
153-
getParameters().forEach((p) => {
154-
if (p.chain && (!coin.supportsAddressChain(p.chain) || !otherCoin.supportsAddressChain(p.chain))) {
155-
return;
156-
}
157-
const address = generateAddress(coin.name, p);
158-
const otherAddress = generateAddress(otherCoin.name, p);
159-
(address === otherAddress).should.eql(isCompatibleAddress(coin, otherCoin));
160-
coin.isValidAddress(otherAddress).should.eql(isCompatibleAddress(coin, otherCoin));
161-
});
162-
});
150+
it('throws UnexpectedAddressError when index does not match', function () {
151+
const address = generateAddress('btc', { keychains, chain: 0, index: 0 });
152+
assert.throws(
153+
() =>
154+
assertFixedScriptWalletAddress('btc', {
155+
chain: 0,
156+
index: 1,
157+
keychains,
158+
format: 'base58',
159+
address,
160+
}),
161+
UnexpectedAddressError
162+
);
163163
});
164164
});
165-
}
166-
167-
utxoCoins.forEach((c) => run(c));
165+
});

modules/abstract-utxo/test/unit/fixtures/bch/addresses-by-chain.json

Lines changed: 0 additions & 84 deletions
This file was deleted.

0 commit comments

Comments
 (0)