Skip to content

Commit 8b5f522

Browse files
Merge pull request #6631 from BitGo/BTC-2372
feat(bip322): add scriptPubKey field to AddressDetails
2 parents 6214def + 093f4c8 commit 8b5f522

File tree

5 files changed

+78
-11
lines changed

5 files changed

+78
-11
lines changed

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

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Psbt, Transaction } from '@bitgo/utxo-lib';
33
export type AddressDetails = {
44
redeemScript?: Buffer;
55
witnessScript?: Buffer;
6+
scriptPubKey: Buffer;
67
};
78

89
/**
@@ -15,10 +16,6 @@ export type AddressDetails = {
1516
* @returns {string} - The hex representation of the constructed PSBT.
1617
*/
1718
export function buildToSignPsbt(toSpendTx: Transaction<bigint>, addressDetails: AddressDetails): Psbt {
18-
if (!addressDetails.redeemScript && !addressDetails.witnessScript) {
19-
throw new Error('redeemScript and/or witnessScript must be provided');
20-
}
21-
2219
// Create PSBT object for constructing the transaction
2320
const psbt = new Psbt();
2421
// Set default value for nVersion and nLockTime
@@ -31,13 +28,15 @@ export function buildToSignPsbt(toSpendTx: Transaction<bigint>, addressDetails:
3128
sequence: 0, // vin[0].nSequence = 0
3229
nonWitnessUtxo: toSpendTx.toBuffer(), // previous transaction for us to rebuild later to verify
3330
});
31+
psbt.updateInput(0, {
32+
witnessUtxo: { value: BigInt(0), script: addressDetails.scriptPubKey },
33+
});
34+
3435
if (addressDetails.redeemScript) {
3536
psbt.updateInput(0, { redeemScript: addressDetails.redeemScript });
3637
}
3738
if (addressDetails.witnessScript) {
38-
psbt.updateInput(0, {
39-
witnessUtxo: { value: BigInt(0), script: addressDetails.witnessScript },
40-
});
39+
psbt.updateInput(0, { witnessScript: addressDetails.witnessScript });
4140
}
4241

4342
// Set the output

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Hash } from 'fast-sha256';
2-
import { Psbt, Transaction } from '@bitgo/utxo-lib';
2+
import { Psbt, Transaction, bitgo, networks } from '@bitgo/utxo-lib';
33

44
export const BIP322_TAG = 'BIP0322-signed-message';
55

@@ -66,3 +66,23 @@ export function buildToSpendTransaction(
6666
// Return transaction
6767
return psbt.extractTransaction();
6868
}
69+
70+
export function buildToSpendTransactionFromChainAndIndex(
71+
rootWalletKeys: bitgo.RootWalletKeys,
72+
chain: bitgo.ChainCode,
73+
index: number,
74+
message: string | Buffer,
75+
tag = BIP322_TAG
76+
): Transaction<bigint> {
77+
const taprootChains = [...bitgo.chainCodesP2tr, ...bitgo.chainCodesP2trMusig2];
78+
if (taprootChains.some((tc) => tc === chain)) {
79+
throw new Error('BIP322 is not supported for Taproot script types.');
80+
}
81+
82+
const outputScript = bitgo.outputScripts.createOutputScript2of3(
83+
rootWalletKeys.deriveForChainAndIndex(chain, index).publicKeys,
84+
bitgo.scriptTypeForChain(chain),
85+
networks.bitcoin
86+
);
87+
return buildToSpendTransaction(outputScript.scriptPubKey, message, tag);
88+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,5 @@ export const BIP322_FIXTURE_HELLO_WORLD_TOSPEND_TX = buildToSpendTransaction(
1414
);
1515

1616
export const BIP322_FIXTURE_HELLOW_WORLD_TOSIGN_PSBT = buildToSignPsbt(BIP322_FIXTURE_HELLO_WORLD_TOSPEND_TX, {
17-
witnessScript: BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer,
17+
scriptPubKey: BIP322_PAYMENT_P2WPKH_FIXTURE.output as Buffer,
1818
});

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('BIP322 toSign', function () {
2424
it(`should build a to_sign PSBT for message "${message}"`, function () {
2525
const toSpendTx = bip322.buildToSpendTransaction(scriptPubKey, Buffer.from(message));
2626
const addressDetails = {
27-
witnessScript: scriptPubKey,
27+
scriptPubKey,
2828
};
2929
const result = bip322.buildToSignPsbt(toSpendTx, addressDetails);
3030
const computedTxid = result

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

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import assert from 'assert';
22

3-
import { buildToSpendTransaction, hashMessageWithTag } from '../../src/bip322';
3+
import { testutil, bitgo } from '@bitgo/utxo-lib';
4+
5+
import {
6+
buildToSpendTransaction,
7+
hashMessageWithTag,
8+
buildToSpendTransactionFromChainAndIndex,
9+
} from '../../src/bip322';
410

511
import { BIP322_PAYMENT_P2WPKH_FIXTURE } from './bip322.utils';
612

@@ -53,4 +59,46 @@ describe('to_spend', function () {
5359
});
5460
});
5561
});
62+
63+
describe('buildToSpendTransactionFromChainAndIndex', function () {
64+
it('should throw an error for Taproot chains', function () {
65+
const taprootChains = [...bitgo.chainCodesP2tr, ...bitgo.chainCodesP2trMusig2];
66+
taprootChains.forEach((chain) => {
67+
assert.throws(() => {
68+
buildToSpendTransactionFromChainAndIndex(
69+
testutil.getDefaultWalletKeys(),
70+
chain,
71+
0,
72+
Buffer.from('Hello World')
73+
);
74+
}, /BIP322 is not supported for Taproot script types/);
75+
});
76+
});
77+
78+
describe('should build a to_spend transaction for a non-Taproot chain', function () {
79+
function run(chain: bitgo.ChainCode) {
80+
it(`scriptType: ${bitgo.scriptTypeForChain(chain)}, chain ${chain}`, function () {
81+
const tx = buildToSpendTransactionFromChainAndIndex(
82+
testutil.getDefaultWalletKeys(),
83+
20,
84+
0,
85+
Buffer.from('Hello World')
86+
);
87+
const expectedScriptPubKey = bitgo.outputScripts
88+
.createOutputScript2of3(testutil.getDefaultWalletKeys().deriveForChainAndIndex(20, 0).publicKeys, 'p2wsh')
89+
.scriptPubKey.toString();
90+
const scriptPubKeyFromTx = tx.outs[0].script.toString();
91+
assert.deepStrictEqual(
92+
scriptPubKeyFromTx,
93+
expectedScriptPubKey,
94+
'ScriptPubKey does not match expected value'
95+
);
96+
});
97+
}
98+
99+
([0, 1, 10, 11, 20, 21] as bitgo.ChainCode[]).forEach((chain) => {
100+
run(chain);
101+
});
102+
});
103+
});
56104
});

0 commit comments

Comments
 (0)