Skip to content

Commit 0d38e0d

Browse files
Merge pull request #6635 from BitGo/BTC-2375
feat(utxo-core): simplify signature process with direct message param
2 parents 676b258 + 2641f25 commit 0d38e0d

File tree

4 files changed

+25
-40
lines changed

4 files changed

+25
-40
lines changed

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

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { Psbt, Transaction, bitgo } from '@bitgo/utxo-lib';
1+
import { Psbt, bitgo } from '@bitgo/utxo-lib';
22

3-
import { isTaprootChain } from './utils';
3+
import { addBip322ProofMessage, isTaprootChain } from './utils';
4+
import { BIP322_TAG, buildToSpendTransaction } from './toSpend';
45

56
export type AddressDetails = {
67
redeemScript?: Buffer;
@@ -13,11 +14,14 @@ export type AddressDetails = {
1314
* Source implementation:
1415
* https://github.com/bitcoin/bips/blob/master/bip-0322.mediawiki#full
1516
*
16-
* @param {string} toSpendTxHex - The hex representation of the `toSpend` transaction.
17+
* @param {string} message - The message that is hashed into the `to_spend` transaction.
1718
* @param {AddressDetails} addressDetails - The details of the address, including redeemScript and/or witnessScript.
18-
* @returns {string} - The hex representation of the constructed PSBT.
19+
* @param {string} [tag=BIP322_TAG] - The tag to use for hashing, defaults to BIP322_TAG.
20+
* @returns {Psbt} - The hex representation of the constructed PSBT.
1921
*/
20-
export function buildToSignPsbt(toSpendTx: Transaction<bigint>, addressDetails: AddressDetails): Psbt {
22+
export function buildToSignPsbt(message: string, addressDetails: AddressDetails, tag = BIP322_TAG): Psbt {
23+
const toSpendTx = buildToSpendTransaction(addressDetails.scriptPubKey, message, tag);
24+
2125
// Create PSBT object for constructing the transaction
2226
const psbt = new Psbt();
2327
// Set default value for nVersion and nLockTime
@@ -41,6 +45,9 @@ export function buildToSignPsbt(toSpendTx: Transaction<bigint>, addressDetails:
4145
psbt.updateInput(0, { witnessScript: addressDetails.witnessScript });
4246
}
4347

48+
// Add the message as a proprietary key value to the PSBT so we can verify it later
49+
addBip322ProofMessage(psbt as bitgo.UtxoPsbt, 0, Buffer.from(message));
50+
4451
// Set the output
4552
psbt.addOutput({
4653
value: BigInt(0), // vout[0].nValue = 0
@@ -50,7 +57,7 @@ export function buildToSignPsbt(toSpendTx: Transaction<bigint>, addressDetails:
5057
}
5158

5259
export function buildToSignPsbtForChainAndIndex(
53-
toSpendTx: Transaction<bigint>,
60+
message: string,
5461
rootWalletKeys: bitgo.RootWalletKeys,
5562
chain: bitgo.ChainCode,
5663
index: number
@@ -63,12 +70,7 @@ export function buildToSignPsbtForChainAndIndex(
6370
bitgo.scriptTypeForChain(chain)
6471
);
6572

66-
const toSpendScriptPubKey = toSpendTx.outs[0].script;
67-
if (!toSpendScriptPubKey.equals(output.scriptPubKey)) {
68-
throw new Error('Output scriptPubKey does not match the expected output script for the chain and index.');
69-
}
70-
71-
return buildToSignPsbt(toSpendTx, {
73+
return buildToSignPsbt(message, {
7274
scriptPubKey: output.scriptPubKey,
7375
redeemScript: output.redeemScript,
7476
witnessScript: output.witnessScript,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ export const BIP322_FIXTURE_HELLO_WORLD_TOSPEND_TX = buildToSpendTransaction(
1313
Buffer.from('Hello World')
1414
);
1515

16-
export const BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT = buildToSignPsbt(BIP322_FIXTURE_HELLO_WORLD_TOSPEND_TX, {
16+
export const BIP322_FIXTURE_HELLO_WORLD_TOSIGN_PSBT = buildToSignPsbt('Hello World', {
1717
scriptPubKey: BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer,
1818
});

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

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,9 @@ describe('BIP322 toSign', function () {
2323

2424
fixtures.forEach(({ message, txid }) => {
2525
it(`should build a to_sign PSBT for message "${message}"`, function () {
26-
const toSpendTx = bip322.buildToSpendTransaction(scriptPubKey, Buffer.from(message));
27-
const addressDetails = {
26+
const result = bip322.buildToSignPsbt(message, {
2827
scriptPubKey,
29-
};
30-
const result = bip322.buildToSignPsbt(toSpendTx, addressDetails);
28+
});
3129
const computedTxid = result
3230
.signAllInputs(prv, [utxolib.Transaction.SIGHASH_ALL])
3331
.finalizeAllInputs()
@@ -41,13 +39,6 @@ describe('BIP322 toSign', function () {
4139
describe('buildToSignPsbtForChainAndIndex', function () {
4240
const rootWalletKeys = utxolib.testutil.getDefaultWalletKeys();
4341

44-
it('should fail when scriptPubKey of to_spend is different than to_sign', function () {
45-
const toSpendTx = bip322.buildToSpendTransaction(BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer, 'Hello World');
46-
assert.throws(() => {
47-
bip322.buildToSignPsbtForChainAndIndex(toSpendTx, rootWalletKeys, 0, 0);
48-
}, /Output scriptPubKey does not match the expected output script for the chain and index./);
49-
});
50-
5142
function run(chain: utxolib.bitgo.ChainCode, shouldFail: boolean, index: number) {
5243
it(`should${
5344
shouldFail ? ' fail to' : ''
@@ -60,7 +51,7 @@ describe('BIP322 toSign', function () {
6051
return;
6152
}
6253
const toSpendTx = bip322.buildToSpendTransactionFromChainAndIndex(rootWalletKeys, chain, index, message);
63-
const toSignPsbt = bip322.buildToSignPsbtForChainAndIndex(toSpendTx, rootWalletKeys, chain, index);
54+
const toSignPsbt = bip322.buildToSignPsbtForChainAndIndex(message, rootWalletKeys, chain, index);
6455

6556
const derivedKeys = rootWalletKeys.deriveForChainAndIndex(chain, index);
6657
const prv1 = derivedKeys.triple[0];

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

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,37 @@ import assert from 'assert';
22

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

5-
import { addBip322ProofMessage, getBip322ProofInputIndex, psbtIsBip322Proof } from '../../src/bip322';
5+
import { getBip322ProofInputIndex, psbtIsBip322Proof } from '../../src/bip322';
66

7-
import { BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT } from './bip322.utils';
7+
import { BIP322_FIXTURE_HELLO_WORLD_TOSIGN_PSBT } from './bip322.utils';
88

99
describe('BIP322 Proof utils', function () {
1010
it('should add a BIP322 proof message to a PSBT input', function () {
1111
const psbt = utxolib.bitgo.createPsbtFromBuffer(
12-
BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT.toBuffer(),
12+
BIP322_FIXTURE_HELLO_WORLD_TOSIGN_PSBT.toBuffer(),
1313
utxolib.networks.bitcoin
1414
);
15-
const inputIndex = 0;
16-
const message = Buffer.from('Hello World');
17-
addBip322ProofMessage(psbt, inputIndex, message);
1815

19-
const proprietaryKeyVals = utxolib.bitgo.getPsbtInputProprietaryKeyVals(psbt.data.inputs[inputIndex], {
16+
const proprietaryKeyVals = utxolib.bitgo.getPsbtInputProprietaryKeyVals(psbt.data.inputs[0], {
2017
identifier: utxolib.bitgo.PSBT_PROPRIETARY_IDENTIFIER,
2118
subtype: utxolib.bitgo.ProprietaryKeySubtype.BIP322_MESSAGE,
2219
});
2320

2421
assert.ok(proprietaryKeyVals.length === 1);
25-
assert.ok(proprietaryKeyVals[0].value.equals(message));
22+
assert.ok(proprietaryKeyVals[0].value.equals(Buffer.from('Hello World')));
2623
assert.ok(proprietaryKeyVals[0].key.keydata.length === 0); // keydata should be empty
2724
assert.ok(proprietaryKeyVals[0].key.identifier === utxolib.bitgo.PSBT_PROPRIETARY_IDENTIFIER);
2825
assert.ok(proprietaryKeyVals[0].key.subtype === utxolib.bitgo.ProprietaryKeySubtype.BIP322_MESSAGE);
2926
});
3027

3128
it('should return the input index of a BIP322 proof message', function () {
3229
const psbt = utxolib.bitgo.createPsbtFromBuffer(
33-
BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT.toBuffer(),
30+
BIP322_FIXTURE_HELLO_WORLD_TOSIGN_PSBT.toBuffer(),
3431
utxolib.networks.bitcoin
3532
);
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);
4133

4234
const resultIndex = getBip322ProofInputIndex(psbt);
43-
assert.strictEqual(resultIndex, inputIndex);
35+
assert.strictEqual(resultIndex, 0);
4436
assert.ok(psbtIsBip322Proof(psbt));
4537
});
4638
});

0 commit comments

Comments
 (0)