Skip to content

Commit 2c9f8d2

Browse files
OttoAllmendingerllm-git
andcommitted
feat(abstract-utxo): filter input script types for legacy tx format
When using legacy tx format, restrict input selection to script types that are compatible with the legacy signing flow (p2sh, p2shP2wsh, p2wsh). This prevents errors when using newer script types like p2tr with legacy transaction format. Issue: BTC-2768 Co-authored-by: llm-git <llm-git@ttll.de>
1 parent 3afe6b1 commit 2c9f8d2

3 files changed

Lines changed: 64 additions & 1 deletion

File tree

modules/abstract-utxo/src/abstractUtxoCoin.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1005,8 +1005,10 @@ export abstract class AbstractUtxoCoin
10051005
async getExtraPrebuildParams(buildParams: ExtraPrebuildParamsOptions & { wallet: Wallet }): Promise<{
10061006
txFormat?: TxFormat;
10071007
changeAddressType?: ScriptType2Of3[] | ScriptType2Of3;
1008+
allowedInputScriptTypes?: ScriptType2Of3[];
10081009
}> {
1009-
const txFormat = this.getDefaultTxFormat(buildParams.wallet, buildParams.txFormat as TxFormat | undefined);
1010+
const requestedFormat = buildParams.txFormat as TxFormat | undefined;
1011+
const txFormat = this.getDefaultTxFormat(buildParams.wallet, requestedFormat);
10101012
let changeAddressType = buildParams.changeAddressType as ScriptType2Of3[] | ScriptType2Of3 | undefined;
10111013

10121014
// if the addressType is not specified, we need to default to p2trMusig2 for testnet hot wallets for staged rollout of p2trMusig2
@@ -1019,9 +1021,26 @@ export abstract class AbstractUtxoCoin
10191021
changeAddressType = ['p2trMusig2', 'p2wsh', 'p2shP2wsh', 'p2sh', 'p2tr'];
10201022
}
10211023

1024+
// getHalfSignedLegacyFormat() only supports p2ms-based types (p2sh, p2shP2wsh, p2wsh).
1025+
// Filter change outputs and restrict input selection to these types.
1026+
const legacyCompatibleTypes: ScriptType2Of3[] = ['p2sh', 'p2shP2wsh', 'p2wsh'];
1027+
let allowedInputScriptTypes: ScriptType2Of3[] | undefined;
1028+
1029+
if (requestedFormat === 'legacy') {
1030+
allowedInputScriptTypes = legacyCompatibleTypes;
1031+
if (Array.isArray(changeAddressType)) {
1032+
changeAddressType = changeAddressType.filter((t): t is ScriptType2Of3 =>
1033+
legacyCompatibleTypes.includes(t as ScriptType2Of3)
1034+
);
1035+
} else if (changeAddressType !== undefined && !legacyCompatibleTypes.includes(changeAddressType)) {
1036+
changeAddressType = legacyCompatibleTypes;
1037+
}
1038+
}
1039+
10221040
return {
10231041
txFormat,
10241042
changeAddressType,
1043+
allowedInputScriptTypes,
10251044
};
10261045
}
10271046

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,46 @@ describe('txFormat', function () {
136136
requestedTxFormat: 'psbt-lite',
137137
});
138138
});
139+
140+
describe('getExtraPrebuildParams with legacy format', function () {
141+
const legacyCompatibleTypes = ['p2sh', 'p2shP2wsh', 'p2wsh'];
142+
143+
it('should filter changeAddressType to legacy-compatible types for hot wallets', async function () {
144+
for (const coin of utxoCoins) {
145+
const wallet = createMockWallet(coin, { type: 'hot' });
146+
const result = await coin.getExtraPrebuildParams({ txFormat: 'legacy', wallet } as any);
147+
assert.ok(Array.isArray(result.changeAddressType), `${coin.getChain()}: changeAddressType should be an array`);
148+
for (const t of result.changeAddressType as string[]) {
149+
assert.ok(
150+
legacyCompatibleTypes.includes(t),
151+
`${coin.getChain()}: changeAddressType contains ${t} which is not legacy-compatible`
152+
);
153+
}
154+
}
155+
});
156+
157+
it('should set allowedInputScriptTypes to legacy-compatible types', async function () {
158+
for (const coin of utxoCoins) {
159+
const wallet = createMockWallet(coin, { type: 'hot' });
160+
const result = await coin.getExtraPrebuildParams({ txFormat: 'legacy', wallet } as any);
161+
assert.deepStrictEqual(
162+
result.allowedInputScriptTypes,
163+
legacyCompatibleTypes,
164+
`${coin.getChain()}: allowedInputScriptTypes should be legacy-compatible`
165+
);
166+
}
167+
});
168+
169+
it('should not set allowedInputScriptTypes when txFormat is not legacy', async function () {
170+
for (const coin of utxoCoins) {
171+
const wallet = createMockWallet(coin, { type: 'hot' });
172+
const result = await coin.getExtraPrebuildParams({ wallet } as any);
173+
assert.strictEqual(
174+
result.allowedInputScriptTypes,
175+
undefined,
176+
`${coin.getChain()}: allowedInputScriptTypes should be undefined for default format`
177+
);
178+
}
179+
});
180+
});
139181
});

modules/sdk-core/src/bitgo/wallet/BuildParams.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export const BuildParamsUTXO = t.partial({
3131
enforceMinConfirmsForChange: t.unknown,
3232
/* legacy or psbt */
3333
txFormat: t.unknown,
34+
/* restrict which input script types WP may select (e.g. for legacy format compatibility) */
35+
allowedInputScriptTypes: t.unknown,
3436
maxChangeOutputs: t.unknown,
3537
/* rbf */
3638
rbfTxIds: t.array(t.string),

0 commit comments

Comments
 (0)