Skip to content

Commit 093f4c8

Browse files
feat(utxo-core): add buildToSpendTransactionFromChainAndIndex function
Add function to create BIP322 to_spend transaction by deriving output script from wallet keys, chain and index. Includes validation to prevent usage with Taproot script types which are not supported by BIP322. Co-authored-by: llm-git <[email protected]> TICKET: BTC-2372
1 parent c3c0bfa commit 093f4c8

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

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/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)