Skip to content

Commit 531a83c

Browse files
feat(abstract-utxo): add BIP322 message verification functionality
Add functions to verify BIP322 message signatures in transaction proofs. Implement support for both PSBT and regular transaction formats, with verification against provided message information. Co-authored-by: llm-git <[email protected]> TICKET: BTC-2375
1 parent f71aa51 commit 531a83c

File tree

1 file changed

+62
-0
lines changed
  • modules/abstract-utxo/src/transaction

1 file changed

+62
-0
lines changed

modules/abstract-utxo/src/transaction/bip322.ts

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { decodeOrElse } from '@bitgo/sdk-core';
2+
import { bip322 } from '@bitgo/utxo-core';
3+
import { bitgo, networks, Network } from '@bitgo/utxo-lib';
24
import * as t from 'io-ts';
35

46
const BIP322MessageInfo = t.type({
@@ -33,3 +35,63 @@ export function deserializeBIP322BroadcastableMessage(hex: string): BIP322Messag
3335
throw new Error(`Failed to decode ${BIP322MessageBroadcastable.name}: ${error}`);
3436
});
3537
}
38+
39+
export function verifyTransactionFromBroadcastableMessage(
40+
message: BIP322MessageBroadcastable,
41+
coinName: string
42+
): boolean {
43+
let network: Network = networks.bitcoin;
44+
if (coinName === 'tbtc4') {
45+
network = networks.bitcoinTestnet4;
46+
} else if (coinName !== 'btc') {
47+
throw new Error('Only tbtc4 or btc coinNames are supported.');
48+
}
49+
if (bitgo.isPsbt(message.txHex)) {
50+
const psbt = bitgo.createPsbtFromBuffer(Buffer.from(message.txHex, 'hex'), network);
51+
try {
52+
bip322.assertBip322PsbtProof(psbt, message.messageInfo);
53+
return true;
54+
} catch (error) {
55+
return false;
56+
}
57+
} else {
58+
const tx = bitgo.createTransactionFromBuffer(Buffer.from(message.txHex, 'hex'), network, { amountType: 'bigint' });
59+
try {
60+
bip322.assertBip322TxProof(tx, message.messageInfo);
61+
return true;
62+
} catch (error) {
63+
return false;
64+
}
65+
}
66+
}
67+
68+
export function generateBIP322MessageListAndVerifyFromMessageBroadcastable(
69+
messageBroadcastables: BIP322MessageBroadcastable[],
70+
coinName: string
71+
): { address: string; message: string }[] {
72+
// Map from the address to the message. If there are duplicates of the address, make sure that the
73+
// message is the same. If there are duplicate addresses and the messages are not the same, throw an error.
74+
const addressMap = new Map<string, string>();
75+
76+
messageBroadcastables.forEach((message, index) => {
77+
if (verifyTransactionFromBroadcastableMessage(message, coinName)) {
78+
message.messageInfo.forEach((info) => {
79+
const { address, message: msg } = info;
80+
if (addressMap.has(address)) {
81+
if (addressMap.get(address) !== msg) {
82+
throw new Error(`Duplicate address ${address} has different messages`);
83+
}
84+
} else {
85+
addressMap.set(address, msg);
86+
}
87+
});
88+
} else {
89+
throw new Error(`Message Broadcastable ${index} did not have a successful validation`);
90+
}
91+
});
92+
93+
return Array.from(addressMap.entries()).map(([address, message]) => ({
94+
address,
95+
message,
96+
}));
97+
}

0 commit comments

Comments
 (0)