Skip to content

Commit 07d6718

Browse files
committed
test(wallet): add coin selection tests for TRUC spending rules
Add two tests covering BIP-431 (TRUC) coin selection behavior. `test_create_tx_non_v3_excludes_unconfirmed_v3_utxos` verifies that a non-v3 transaction cannot coin-select an unconfirmed v3 parent output, while a v3 transaction targeting the same output succeeds. `test_create_tx_non_v3_allows_unconfirmed_non_v3_utxos` verifies that unconfirmed outputs from non-v3 parents remain eligible for standard transactions, preserving existing behavior.
1 parent 0c6c1cc commit 07d6718

1 file changed

Lines changed: 125 additions & 0 deletions

File tree

tests/wallet.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,131 @@ fn test_create_tx_custom_version() {
235235
assert_eq!(psbt.unsigned_tx.version.0, 42);
236236
}
237237

238+
#[test]
239+
fn test_create_tx_non_v3_excludes_unconfirmed_v3_utxos() {
240+
let (descriptor, change_descriptor) = get_test_wpkh_and_change_desc();
241+
let mut wallet = Wallet::create(descriptor, change_descriptor)
242+
.network(Network::Regtest)
243+
.create_wallet_no_persist()
244+
.expect("wallet");
245+
246+
insert_checkpoint(
247+
&mut wallet,
248+
BlockId {
249+
height: 1,
250+
hash: BlockHash::all_zeros(),
251+
},
252+
);
253+
254+
let confirmed_tx = Transaction {
255+
version: transaction::Version::ONE,
256+
lock_time: absolute::LockTime::ZERO,
257+
input: vec![],
258+
output: vec![TxOut {
259+
script_pubkey: wallet
260+
.next_unused_address(KeychainKind::External)
261+
.script_pubkey(),
262+
value: Amount::from_sat(10_000),
263+
}],
264+
};
265+
let confirmed_txid = confirmed_tx.compute_txid();
266+
insert_tx(&mut wallet, confirmed_tx);
267+
let block_id = wallet.latest_checkpoint().block_id();
268+
insert_anchor(
269+
&mut wallet,
270+
confirmed_txid,
271+
ConfirmationBlockTime {
272+
block_id,
273+
confirmation_time: 1,
274+
},
275+
);
276+
277+
let truc_tx = Transaction {
278+
version: transaction::Version(3),
279+
lock_time: absolute::LockTime::ZERO,
280+
input: vec![],
281+
output: vec![TxOut {
282+
script_pubkey: wallet
283+
.next_unused_address(KeychainKind::External)
284+
.script_pubkey(),
285+
value: Amount::from_sat(30_000),
286+
}],
287+
};
288+
let truc_txid = truc_tx.compute_txid();
289+
insert_tx(&mut wallet, truc_tx);
290+
291+
let recipient = wallet.next_unused_address(KeychainKind::External);
292+
293+
let mut builder = wallet.build_tx();
294+
builder
295+
.fee_rate(FeeRate::ZERO)
296+
.add_recipient(recipient.script_pubkey(), Amount::from_sat(20_000));
297+
assert_matches!(
298+
builder.finish(),
299+
Err(CreateTxError::CoinSelection(
300+
coin_selection::InsufficientFunds { .. }
301+
))
302+
);
303+
304+
let mut builder = wallet.build_tx();
305+
builder
306+
.fee_rate(FeeRate::ZERO)
307+
.version(3)
308+
.add_recipient(recipient.script_pubkey(), Amount::from_sat(20_000));
309+
let psbt = builder
310+
.finish()
311+
.expect("v3 transaction should be buildable");
312+
313+
assert_eq!(psbt.unsigned_tx.version, transaction::Version(3));
314+
assert!(
315+
psbt.unsigned_tx
316+
.input
317+
.iter()
318+
.any(|input| input.previous_output.txid == truc_txid),
319+
"version=3 transaction should be able to spend the unconfirmed v3 output"
320+
);
321+
}
322+
323+
#[test]
324+
fn test_create_tx_non_v3_allows_unconfirmed_non_v3_utxos() {
325+
let (descriptor, change_descriptor) = get_test_wpkh_and_change_desc();
326+
let mut wallet = Wallet::create(descriptor, change_descriptor)
327+
.network(Network::Regtest)
328+
.create_wallet_no_persist()
329+
.expect("wallet");
330+
331+
let unconfirmed_tx = Transaction {
332+
version: transaction::Version::ONE,
333+
lock_time: absolute::LockTime::ZERO,
334+
input: vec![],
335+
output: vec![TxOut {
336+
script_pubkey: wallet
337+
.next_unused_address(KeychainKind::External)
338+
.script_pubkey(),
339+
value: Amount::from_sat(30_000),
340+
}],
341+
};
342+
let unconfirmed_txid = unconfirmed_tx.compute_txid();
343+
insert_tx(&mut wallet, unconfirmed_tx);
344+
345+
let recipient = wallet.next_unused_address(KeychainKind::External);
346+
let mut builder = wallet.build_tx();
347+
builder
348+
.fee_rate(FeeRate::ZERO)
349+
.add_recipient(recipient.script_pubkey(), Amount::from_sat(20_000));
350+
let psbt = builder
351+
.finish()
352+
.expect("non-v3 unconfirmed outputs should remain spendable");
353+
354+
assert!(
355+
psbt.unsigned_tx
356+
.input
357+
.iter()
358+
.any(|input| input.previous_output.txid == unconfirmed_txid),
359+
"expected the unconfirmed non-v3 output to be available for selection"
360+
);
361+
}
362+
238363
#[test]
239364
fn test_create_tx_default_locktime_is_last_sync_height() {
240365
let (mut wallet, _) = get_funded_wallet_wpkh();

0 commit comments

Comments
 (0)