Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/abstract-utxo/src/address/fixedScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ export function generateAddress(coinName: UtxoCoinName, params: GenerateFixedScr

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

if (_.isNumber(chain) && _.isInteger(chain) && !fixedScriptWallet.ChainCode.is(chain)) {
throw new InvalidAddressDerivationPropertyError(`address validation failure: unrecognised chain code (${chain})`);
}

let derivationChain: ChainCode = fixedScriptWallet.ChainCode.value('p2sh', 'external');
if (_.isNumber(chain) && _.isInteger(chain) && fixedScriptWallet.ChainCode.is(chain)) {
derivationChain = chain;
Expand Down
39 changes: 39 additions & 0 deletions modules/abstract-utxo/test/unit/address.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import * as assert from 'assert';

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

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

import { keychainsBase58 } from './util';

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

// A chain code that no released SDK version has ever defined — simulates a future
// server-side address type unknown to an older client.
const unknownChainCode = 99;

describe('assertFixedScriptWalletAddress', function () {
describe('input validation', function () {
it('throws InvalidAddressDerivationPropertyError when both chain and index are undefined', function () {
Expand Down Expand Up @@ -121,6 +126,40 @@ describe('assertFixedScriptWalletAddress', function () {
);
});

// Regression guard for T1-3386 / T1-3385: an unknown chain code must throw
// immediately with a clear message rather than silently falling back to P2SH
// and producing a confusing "expected <P2SH> but got <P2TR>" error.
it('throws InvalidAddressDerivationPropertyError for an unknown chain code', function () {
assert.ok(!fixedScriptWallet.ChainCode.is(unknownChainCode), 'test prerequisite: chain must be unknown');
const serverAddress = generateAddress('btc', { keychains, chain: 40 });
assert.throws(
() =>
assertFixedScriptWalletAddress('btc', {
chain: unknownChainCode,
index: 0,
keychains,
format: 'base58',
address: serverAddress,
}),
(err: unknown) => {
assert.ok(err instanceof InvalidAddressDerivationPropertyError);
assert.ok(
err.message.includes(String(unknownChainCode)),
`expected error to name the unrecognised chain code, got: ${err.message}`
);
return true;
}
);
});

it('generateAddress throws InvalidAddressDerivationPropertyError for an unknown chain code', function () {
assert.ok(!fixedScriptWallet.ChainCode.is(unknownChainCode), 'test prerequisite: chain must be unknown');
assert.throws(
() => generateAddress('btc', { keychains, chain: unknownChainCode }),
InvalidAddressDerivationPropertyError
);
});

it('succeeds for bch cashaddr (chain 0)', function () {
const address = generateAddress('bch', { keychains, chain: 0, format: 'cashaddr' });
assert.doesNotThrow(() =>
Expand Down
Loading