Skip to content

Commit 7898f7e

Browse files
Merge pull request #8817 from BitGo/otto/fix-address-validation
fix(abstract-utxo): throw on unrecognised chain code
2 parents 9f85628 + edbb6be commit 7898f7e

2 files changed

Lines changed: 43 additions & 0 deletions

File tree

modules/abstract-utxo/src/address/fixedScript.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ export function generateAddress(coinName: UtxoCoinName, params: GenerateFixedScr
7878

7979
const { keychains, chain, segwit = false, bech32 = false } = params as GenerateFixedScriptAddressOptions;
8080

81+
if (_.isNumber(chain) && _.isInteger(chain) && !fixedScriptWallet.ChainCode.is(chain)) {
82+
throw new InvalidAddressDerivationPropertyError(`address validation failure: unrecognised chain code (${chain})`);
83+
}
84+
8185
let derivationChain: ChainCode = fixedScriptWallet.ChainCode.value('p2sh', 'external');
8286
if (_.isNumber(chain) && _.isInteger(chain) && fixedScriptWallet.ChainCode.is(chain)) {
8387
derivationChain = chain;

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

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
import * as assert from 'assert';
22

33
import { InvalidAddressDerivationPropertyError, UnexpectedAddressError } from '@bitgo/sdk-core';
4+
import { fixedScriptWallet } from '@bitgo/wasm-utxo';
45

56
import { assertFixedScriptWalletAddress, generateAddress } from '../../src';
67

78
import { keychainsBase58 } from './util';
89

910
const keychains = keychainsBase58.map((k) => ({ pub: k.pub }));
1011

12+
// A chain code that no released SDK version has ever defined — simulates a future
13+
// server-side address type unknown to an older client.
14+
const unknownChainCode = 99;
15+
1116
describe('assertFixedScriptWalletAddress', function () {
1217
describe('input validation', function () {
1318
it('throws InvalidAddressDerivationPropertyError when both chain and index are undefined', function () {
@@ -121,6 +126,40 @@ describe('assertFixedScriptWalletAddress', function () {
121126
);
122127
});
123128

129+
// Regression guard for T1-3386 / T1-3385: an unknown chain code must throw
130+
// immediately with a clear message rather than silently falling back to P2SH
131+
// and producing a confusing "expected <P2SH> but got <P2TR>" error.
132+
it('throws InvalidAddressDerivationPropertyError for an unknown chain code', function () {
133+
assert.ok(!fixedScriptWallet.ChainCode.is(unknownChainCode), 'test prerequisite: chain must be unknown');
134+
const serverAddress = generateAddress('btc', { keychains, chain: 40 });
135+
assert.throws(
136+
() =>
137+
assertFixedScriptWalletAddress('btc', {
138+
chain: unknownChainCode,
139+
index: 0,
140+
keychains,
141+
format: 'base58',
142+
address: serverAddress,
143+
}),
144+
(err: unknown) => {
145+
assert.ok(err instanceof InvalidAddressDerivationPropertyError);
146+
assert.ok(
147+
err.message.includes(String(unknownChainCode)),
148+
`expected error to name the unrecognised chain code, got: ${err.message}`
149+
);
150+
return true;
151+
}
152+
);
153+
});
154+
155+
it('generateAddress throws InvalidAddressDerivationPropertyError for an unknown chain code', function () {
156+
assert.ok(!fixedScriptWallet.ChainCode.is(unknownChainCode), 'test prerequisite: chain must be unknown');
157+
assert.throws(
158+
() => generateAddress('btc', { keychains, chain: unknownChainCode }),
159+
InvalidAddressDerivationPropertyError
160+
);
161+
});
162+
124163
it('succeeds for bch cashaddr (chain 0)', function () {
125164
const address = generateAddress('bch', { keychains, chain: 0, format: 'cashaddr' });
126165
assert.doesNotThrow(() =>

0 commit comments

Comments
 (0)