Skip to content

Commit b898f78

Browse files
feat(utxo-core): add bip322 proof checker
BTC-2408 TICKET: BTC-2408
1 parent 371fc3a commit b898f78

File tree

2 files changed

+91
-1
lines changed

2 files changed

+91
-1
lines changed

modules/utxo-core/src/bip322/utils.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,47 @@ export function getBip322ProofMessageAtIndex(psbt: utxolib.Psbt, inputIndex: num
3333
}
3434
return Buffer.from(proprietaryKeyVals[0].value);
3535
}
36+
37+
/**
38+
* checks if the transaction contains a BIP322 proof
39+
* does not check if the proof is valid
40+
* Source: https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#user-content-Full
41+
* @params tx The transaction or the PSBT to check
42+
* @returns boolean
43+
*/
44+
export function isBip322ProofCheck(tx: utxolib.bitgo.UtxoPsbt | utxolib.bitgo.UtxoTransaction<bigint>): boolean {
45+
if (tx instanceof utxolib.bitgo.UtxoPsbt) {
46+
if (tx.version !== 0 || tx.locktime !== 0 || tx.data.outputs.length !== 1) {
47+
return false;
48+
}
49+
const output = tx.txOutputs[0];
50+
if (output.script.toString('hex') !== '6a' || output.value !== 0n) {
51+
return false;
52+
}
53+
54+
// Return true if there is a message encoded in every input in the proprietary field
55+
return tx.data.inputs.every((input, index) => getBip322ProofMessageAtIndex(tx, index) !== undefined);
56+
} else if (tx instanceof utxolib.bitgo.UtxoTransaction) {
57+
if (tx.version !== 0 || tx.locktime !== 0 || tx.outs.length !== 1) {
58+
return false;
59+
}
60+
if (tx.outs.length !== 1) {
61+
return false;
62+
}
63+
const output = tx.outs[0];
64+
// check that the only output is an OP_RETURN with 0 value
65+
if (output.script.toString('hex') !== '6a' || output.value !== 0n) {
66+
return false;
67+
}
68+
69+
for (const input of tx.ins) {
70+
if (input.index !== 0 || input.sequence !== 0) {
71+
return false;
72+
}
73+
}
74+
75+
return true;
76+
} else {
77+
throw new Error('Unsupported transaction type for BIP322 proof check');
78+
}
79+
}

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

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import assert from 'assert';
22

33
import * as utxolib from '@bitgo/utxo-lib';
44

5-
import { getBip322ProofMessageAtIndex } from '../../src/bip322';
5+
import { getBip322ProofMessageAtIndex, isBip322ProofCheck } from '../../src/bip322';
66

77
import { BIP322_FIXTURE_HELLO_WORLD_TOSIGN_PSBT } from './bip322.utils';
88

@@ -35,4 +35,50 @@ describe('BIP322 Proof utils', function () {
3535
assert.ok(messageBuffer, 'Message buffer should not be undefined');
3636
assert.deepStrictEqual(messageBuffer.toString('utf-8'), 'Hello World', 'Message does not match expected value');
3737
});
38+
39+
describe('isBip322ProofCheck', function () {
40+
it('should work for PSBTs', function () {
41+
const psbt = utxolib.bitgo.createPsbtFromBuffer(
42+
BIP322_FIXTURE_HELLO_WORLD_TOSIGN_PSBT.toBuffer(),
43+
utxolib.networks.bitcoin
44+
);
45+
assert.ok(isBip322ProofCheck(psbt), 'Expected PSBT to be a valid BIP322 proof');
46+
assert.deepEqual(
47+
isBip322ProofCheck(
48+
utxolib.testutil.constructPsbt(
49+
[{ scriptType: 'taprootKeyPathSpend', value: BigInt(1000) }],
50+
[{ scriptType: 'p2sh', value: BigInt(900) }],
51+
utxolib.networks.bitcoin,
52+
utxolib.testutil.getDefaultWalletKeys(),
53+
'unsigned'
54+
)
55+
),
56+
false
57+
);
58+
});
59+
60+
it('should work for Transactions', function () {
61+
const psbt = utxolib.bitgo.createPsbtFromBuffer(
62+
BIP322_FIXTURE_HELLO_WORLD_TOSIGN_PSBT.toBuffer(),
63+
utxolib.networks.bitcoin
64+
);
65+
// Cannot extract the transaction because it has no signatures
66+
const tx = psbt.getUnsignedTx();
67+
assert.ok(isBip322ProofCheck(tx), 'Expected Transaction to be a valid BIP322 proof');
68+
assert.deepEqual(
69+
isBip322ProofCheck(
70+
utxolib.testutil
71+
.constructPsbt(
72+
[{ scriptType: 'taprootKeyPathSpend', value: BigInt(1000) }],
73+
[{ scriptType: 'p2sh', value: BigInt(900) }],
74+
utxolib.networks.bitcoin,
75+
utxolib.testutil.getDefaultWalletKeys(),
76+
'unsigned'
77+
)
78+
.getUnsignedTx()
79+
),
80+
false
81+
);
82+
});
83+
});
3884
});

0 commit comments

Comments
 (0)