-
Notifications
You must be signed in to change notification settings - Fork 302
Expand file tree
/
Copy pathverifyTransaction.ts
More file actions
96 lines (85 loc) · 3.25 KB
/
verifyTransaction.ts
File metadata and controls
96 lines (85 loc) · 3.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
import * as utxolib from '@bitgo/utxo-lib';
import { ITransactionRecipient, TxIntentMismatchError, IBaseCoin } from '@bitgo/sdk-core';
import { DescriptorMap } from '@bitgo/utxo-core/descriptor';
import { AbstractUtxoCoin, VerifyTransactionOptions } from '../../abstractUtxoCoin';
import { BaseOutput, BaseParsedTransactionOutputs } from '../types';
import { toBaseParsedTransactionOutputsFromPsbt } from './parse';
export class ValidationError extends Error {
constructor(message: string) {
super(message);
}
}
export class ErrorMissingOutputs extends ValidationError {
constructor(public missingOutputs: BaseOutput<bigint | 'max'>[]) {
super(`missing outputs (count=${missingOutputs.length})`);
}
}
export class ErrorImplicitExternalOutputs extends ValidationError {
constructor(public implicitExternalOutputs: BaseOutput<bigint | 'max'>[]) {
super(`unexpected implicit external outputs (count=${implicitExternalOutputs.length})`);
}
}
export class AggregateValidationError extends ValidationError {
constructor(public errors: ValidationError[]) {
super(`aggregate validation error (count=${errors.length})`);
}
}
export function assertExpectedOutputDifference(
parsedOutputs: BaseParsedTransactionOutputs<bigint, BaseOutput<bigint | 'max'>>
): void {
const errors: ValidationError[] = [];
if (parsedOutputs.missingOutputs.length > 0) {
errors.push(new ErrorMissingOutputs(parsedOutputs.missingOutputs));
}
if (parsedOutputs.implicitExternalOutputs.length > 0) {
// FIXME: for paygo we need to relax this a little bit
errors.push(new ErrorImplicitExternalOutputs(parsedOutputs.implicitExternalOutputs));
}
if (errors.length > 0) {
// FIXME(BTC-1688): enable ES2021
// throw new AggregateError(errors);
throw new AggregateValidationError(errors);
}
}
export function assertValidTransaction(
psbt: utxolib.bitgo.UtxoPsbt,
descriptors: DescriptorMap,
recipients: ITransactionRecipient[],
network: utxolib.Network
): void {
assertExpectedOutputDifference(toBaseParsedTransactionOutputsFromPsbt(psbt, descriptors, recipients, network));
}
/**
* Wrapper around assertValidTransaction that returns a boolean instead of throwing.
*
* We follow the AbstractUtxoCoin interface here which is a bit confused - the return value is a boolean but we
* also throw errors (because we actually want to know what went wrong).
*
* @param coin
* @param params
* @param descriptorMap
* @returns {boolean} True if verification passes
* @throws {TxIntentMismatchError} if transaction validation fails
*/
export async function verifyTransaction<TNumber extends number | bigint>(
coin: AbstractUtxoCoin,
params: VerifyTransactionOptions<TNumber>,
descriptorMap: DescriptorMap
): Promise<boolean> {
const tx = coin.decodeTransactionFromPrebuild(params.txPrebuild);
if (!(tx instanceof utxolib.bitgo.UtxoPsbt)) {
const txExplanation = await TxIntentMismatchError.tryGetTxExplanation(
coin as unknown as IBaseCoin,
params.txPrebuild
);
throw new TxIntentMismatchError(
'unexpected transaction type',
params.reqId,
[params.txParams],
params.txPrebuild.txHex,
txExplanation
);
}
assertValidTransaction(tx, descriptorMap, params.txParams.recipients ?? [], tx.network);
return true;
}