Skip to content

Commit a2a6019

Browse files
Merge pull request #6621 from BitGo/BTC-2361
feat(utxo-core): add utility functions for PSBT message proofs
2 parents fef9aef + f6d0d8b commit a2a6019

File tree

6 files changed

+101
-12
lines changed

6 files changed

+101
-12
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export * from './toSpend';
22
export * from './toSign';
3+
export * from './utils';
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import * as utxolib from '@bitgo/utxo-lib';
2+
3+
export function addBip322ProofMessage(psbt: utxolib.bitgo.UtxoPsbt, inputIndex: number, message: Buffer): void {
4+
utxolib.bitgo.addProprietaryKeyValuesFromUnknownKeyValues(psbt, 'input', inputIndex, {
5+
key: {
6+
identifier: utxolib.bitgo.PSBT_PROPRIETARY_IDENTIFIER,
7+
subtype: utxolib.bitgo.ProprietaryKeySubtype.BIP322_MESSAGE,
8+
keydata: Buffer.alloc(0),
9+
},
10+
value: message,
11+
});
12+
}
13+
14+
export function getBip322ProofInputIndex(psbt: utxolib.Psbt): number | undefined {
15+
const res = psbt.data.inputs.flatMap((input, inputIndex) => {
16+
const proprietaryKeyVals = utxolib.bitgo.getPsbtInputProprietaryKeyVals(input, {
17+
identifier: utxolib.bitgo.PSBT_PROPRIETARY_IDENTIFIER,
18+
subtype: utxolib.bitgo.ProprietaryKeySubtype.BIP322_MESSAGE,
19+
});
20+
if (proprietaryKeyVals.length > 1) {
21+
throw new Error(`Multiple BIP322 messages found at input index ${inputIndex}`);
22+
}
23+
return proprietaryKeyVals.length === 0 ? [] : [inputIndex];
24+
});
25+
return res.length === 0 ? undefined : res[0];
26+
}
27+
28+
export function psbtIsBip322Proof(psbt: utxolib.Psbt): boolean {
29+
return getBip322ProofInputIndex(psbt) !== undefined;
30+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { payments, ECPair } from '@bitgo/utxo-lib';
2+
3+
import { buildToSignPsbt, buildToSpendTransaction } from '../../src/bip322';
4+
5+
export const BIP322_WIF_FIXTURE = 'L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k';
6+
export const BIP322_PRV_FIXTURE = ECPair.fromWIF(BIP322_WIF_FIXTURE);
7+
export const BIP322_PAYMENT_P2WPKH_FIXTURE = payments.p2wpkh({
8+
address: 'bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l',
9+
});
10+
11+
export const BIP322_FIXTURE_HELLO_WORLD_TOSPEND_TX = buildToSpendTransaction(
12+
BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer,
13+
Buffer.from('Hello World')
14+
);
15+
16+
export const BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT = buildToSignPsbt(BIP322_FIXTURE_HELLO_WORLD_TOSPEND_TX, {
17+
witnessScript: BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer,
18+
});

modules/utxo-core/test/bip322/toSign.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import assert from 'assert';
22

3-
import { payments, ECPair, Transaction } from '@bitgo/utxo-lib';
3+
import { Transaction } from '@bitgo/utxo-lib';
44

55
import * as bip322 from '../../src/bip322';
66

7+
import { BIP322_PAYMENT_P2WPKH_FIXTURE, BIP322_PRV_FIXTURE as prv } from './bip322.utils';
78
describe('BIP322 toSign', function () {
89
describe('buildToSignPsbt', function () {
9-
const WIF = 'L3VFeEujGtevx9w18HD1fhRbCH67Az2dpCymeRE1SoPK6XQtaN2k';
10-
const prv = ECPair.fromWIF(WIF);
11-
const scriptPubKey = payments.p2wpkh({
12-
address: 'bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l',
13-
}).output as Buffer;
14-
10+
const scriptPubKey = BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer;
1511
// Source: https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#transaction-hashes
1612
const fixtures = [
1713
{

modules/utxo-core/test/bip322/toSpend.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import assert from 'assert';
22

3-
import { payments } from '@bitgo/utxo-lib';
4-
53
import { buildToSpendTransaction, hashMessageWithTag } from '../../src/bip322';
64

5+
import { BIP322_PAYMENT_P2WPKH_FIXTURE } from './bip322.utils';
6+
77
describe('to_spend', function () {
88
describe('Message hashing', function () {
99
// Test vectors from BIP322
@@ -31,9 +31,7 @@ describe('to_spend', function () {
3131
});
3232

3333
describe('build to_spend transaction', function () {
34-
const scriptPubKey = payments.p2wpkh({
35-
address: 'bc1q9vza2e8x573nczrlzms0wvx3gsqjx7vavgkx0l',
36-
}).output as Buffer;
34+
const scriptPubKey = BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer;
3735

3836
// Source: https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#transaction-hashes
3937
const fixtures = [
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import assert from 'assert';
2+
3+
import * as utxolib from '@bitgo/utxo-lib';
4+
5+
import { addBip322ProofMessage, getBip322ProofInputIndex, psbtIsBip322Proof } from '../../src/bip322';
6+
7+
import { BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT } from './bip322.utils';
8+
9+
describe('BIP322 Proof utils', function () {
10+
it('should add a BIP322 proof message to a PSBT input', function () {
11+
const psbt = utxolib.bitgo.createPsbtFromBuffer(
12+
BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT.toBuffer(),
13+
utxolib.networks.bitcoin
14+
);
15+
const inputIndex = 0;
16+
const message = Buffer.from('Hello World');
17+
addBip322ProofMessage(psbt, inputIndex, message);
18+
19+
const proprietaryKeyVals = utxolib.bitgo.getPsbtInputProprietaryKeyVals(psbt.data.inputs[inputIndex], {
20+
identifier: utxolib.bitgo.PSBT_PROPRIETARY_IDENTIFIER,
21+
subtype: utxolib.bitgo.ProprietaryKeySubtype.BIP322_MESSAGE,
22+
});
23+
24+
assert.ok(proprietaryKeyVals.length === 1);
25+
assert.ok(proprietaryKeyVals[0].value.equals(message));
26+
assert.ok(proprietaryKeyVals[0].key.keydata.length === 0); // keydata should be empty
27+
assert.ok(proprietaryKeyVals[0].key.identifier === utxolib.bitgo.PSBT_PROPRIETARY_IDENTIFIER);
28+
assert.ok(proprietaryKeyVals[0].key.subtype === utxolib.bitgo.ProprietaryKeySubtype.BIP322_MESSAGE);
29+
});
30+
31+
it('should return the input index of a BIP322 proof message', function () {
32+
const psbt = utxolib.bitgo.createPsbtFromBuffer(
33+
BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT.toBuffer(),
34+
utxolib.networks.bitcoin
35+
);
36+
assert.ok(!psbtIsBip322Proof(psbt)); // initially should not be a BIP322 proof
37+
38+
const inputIndex = 0;
39+
const message = Buffer.from('Hello World');
40+
addBip322ProofMessage(psbt, inputIndex, message);
41+
42+
const resultIndex = getBip322ProofInputIndex(psbt);
43+
assert.strictEqual(resultIndex, inputIndex);
44+
assert.ok(psbtIsBip322Proof(psbt));
45+
});
46+
});

0 commit comments

Comments
 (0)