Skip to content

Commit b73057e

Browse files
OttoAllmendingerllm-git
andcommitted
feat(abstract-utxo): include script type in input signing error
Include PSBT script type in InputSigningError to improve debugging of transaction signing issues. This provides better visibility into which input types are failing during the signing process. Issue: BTC-2806 Co-authored-by: llm-git <[email protected]>
1 parent ef88bda commit b73057e

File tree

1 file changed

+19
-12
lines changed
  • modules/abstract-utxo/src/transaction/fixedScript

1 file changed

+19
-12
lines changed

modules/abstract-utxo/src/transaction/fixedScript/sign.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ type Unspent<TNumber extends number | bigint = number> = utxolib.bitgo.Unspent<T
1111

1212
type RootWalletKeys = utxolib.bitgo.RootWalletKeys;
1313

14-
type PsbtParsedScriptTypes =
14+
type PsbtParsedScriptType =
1515
| 'p2sh'
1616
| 'p2wsh'
1717
| 'p2shP2wsh'
@@ -22,17 +22,24 @@ type PsbtParsedScriptTypes =
2222
export class InputSigningError<TNumber extends number | bigint = number> extends Error {
2323
static expectedWalletUnspent<TNumber extends number | bigint>(
2424
inputIndex: number,
25+
inputType: PsbtParsedScriptType | null, // null for legacy transaction format
2526
unspent: Unspent<TNumber> | { id: string }
2627
): InputSigningError<TNumber> {
27-
return new InputSigningError(inputIndex, unspent, `not a wallet unspent, not a replay protection unspent`);
28+
return new InputSigningError(
29+
inputIndex,
30+
inputType,
31+
unspent,
32+
`not a wallet unspent, not a replay protection unspent`
33+
);
2834
}
2935

3036
constructor(
3137
public inputIndex: number,
38+
public inputType: PsbtParsedScriptType | null, // null for legacy transaction format
3239
public unspent: Unspent<TNumber> | { id: string },
3340
public reason: Error | string
3441
) {
35-
super(`signing error at input ${inputIndex}: unspentId=${unspent.id}: ${reason}`);
42+
super(`signing error at input ${inputIndex}: type=${inputType} unspentId=${unspent.id}: ${reason}`);
3643
}
3744
}
3845

@@ -70,7 +77,7 @@ export function signAndVerifyPsbt(
7077
): utxolib.bitgo.UtxoPsbt | utxolib.bitgo.UtxoTransaction<bigint> {
7178
const txInputs = psbt.txInputs;
7279
const outputIds: string[] = [];
73-
const scriptTypes: PsbtParsedScriptTypes[] = [];
80+
const scriptTypes: PsbtParsedScriptType[] = [];
7481

7582
const signErrors: InputSigningError<bigint>[] = psbt.data.inputs
7683
.map((input, inputIndex: number) => {
@@ -89,7 +96,7 @@ export function signAndVerifyPsbt(
8996
psbt.signInputHD(inputIndex, signerKeychain);
9097
debug('Successfully signed input %d of %d', inputIndex + 1, psbt.data.inputs.length);
9198
} catch (e) {
92-
return new InputSigningError<bigint>(inputIndex, { id: outputId }, e);
99+
return new InputSigningError<bigint>(inputIndex, scriptType, { id: outputId }, e);
93100
}
94101
})
95102
.filter((e): e is InputSigningError<bigint> => e !== undefined);
@@ -109,11 +116,11 @@ export function signAndVerifyPsbt(
109116
const outputId = outputIds[inputIndex];
110117
try {
111118
if (!psbt.validateSignaturesOfInputHD(inputIndex, signerKeychain)) {
112-
return new InputSigningError(inputIndex, { id: outputId }, new Error(`invalid signature`));
119+
return new InputSigningError(inputIndex, scriptType, { id: outputId }, new Error(`invalid signature`));
113120
}
114121
} catch (e) {
115122
debug('Invalid signature');
116-
return new InputSigningError<bigint>(inputIndex, { id: outputId }, e);
123+
return new InputSigningError<bigint>(inputIndex, scriptType, { id: outputId }, e);
117124
}
118125
})
119126
.filter((e): e is InputSigningError<bigint> => e !== undefined);
@@ -178,13 +185,13 @@ export function signAndVerifyWalletTransaction<TNumber extends number | bigint>(
178185
return;
179186
}
180187
if (!isWalletUnspent<TNumber>(unspent)) {
181-
return InputSigningError.expectedWalletUnspent<TNumber>(inputIndex, unspent);
188+
return InputSigningError.expectedWalletUnspent<TNumber>(inputIndex, null, unspent);
182189
}
183190
try {
184191
signInputWithUnspent<TNumber>(txBuilder, inputIndex, unspent, walletSigner);
185192
debug('Successfully signed input %d of %d', inputIndex + 1, unspents.length);
186193
} catch (e) {
187-
return new InputSigningError<TNumber>(inputIndex, unspent, e);
194+
return new InputSigningError<TNumber>(inputIndex, null, unspent, e);
188195
}
189196
})
190197
.filter((e): e is InputSigningError<TNumber> => e !== undefined);
@@ -203,18 +210,18 @@ export function signAndVerifyWalletTransaction<TNumber extends number | bigint>(
203210
return;
204211
}
205212
if (!isWalletUnspent<TNumber>(unspent)) {
206-
return InputSigningError.expectedWalletUnspent<TNumber>(inputIndex, unspent);
213+
return InputSigningError.expectedWalletUnspent<TNumber>(inputIndex, null, unspent);
207214
}
208215
try {
209216
const publicKey = walletSigner.deriveForChainAndIndex(unspent.chain, unspent.index).signer.publicKey;
210217
if (
211218
!utxolib.bitgo.verifySignatureWithPublicKey<TNumber>(signedTransaction, inputIndex, prevOutputs, publicKey)
212219
) {
213-
return new InputSigningError(inputIndex, unspent, new Error(`invalid signature`));
220+
return new InputSigningError(inputIndex, null, unspent, new Error(`invalid signature`));
214221
}
215222
} catch (e) {
216223
debug('Invalid signature');
217-
return new InputSigningError<TNumber>(inputIndex, unspent, e);
224+
return new InputSigningError<TNumber>(inputIndex, null, unspent, e);
218225
}
219226
})
220227
.filter((e): e is InputSigningError<TNumber> => e !== undefined);

0 commit comments

Comments
 (0)