Skip to content

Commit 048daa6

Browse files
ValuedMammalnymius
authored andcommitted
test(tx_builder): update precedence check of local UTXOs over add_foreign_utxos
1 parent 77b5e0f commit 048daa6

File tree

1 file changed

+35
-156
lines changed

1 file changed

+35
-156
lines changed

wallet/src/wallet/tx_builder.rs

Lines changed: 35 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,180 +1259,59 @@ mod test {
12591259
);
12601260
}
12611261

1262+
// Test that local outputs have precedence over utxos added via `add_foreign_utxo`
12621263
#[test]
1263-
fn test_prexisting_local_utxo_have_precedence_over_foreign_utxo_with_same_outpoint() {
1264-
// In this test we are assuming a setup where there are two wallets using the same
1265-
// descriptor, but only one is tracking transactions, while the other is not.
1266-
// Within this conditions we want the second wallet to be able to consider the unknown
1267-
// LocalOutputs provided by the first wallet with greater precedence than any foreign UTXO,
1268-
// even if the foreign UTXO shares the same outpoint.
1269-
//
1270-
// Remember the second wallet does not know about any UTXOs, so in principle, an unknown
1271-
// local UTXO could be added as foreign.
1272-
//
1273-
// In this case, somehow the wallet has knowledge of one local UTXO and it tries to add the
1274-
// same UTXO as a foreign one, but the API ignores this, because local UTXOs have higher
1275-
// precedence.
1276-
use crate::test_utils::{get_funded_wallet_wpkh, get_test_wpkh_and_change_desc};
1277-
use bitcoin::Network;
1278-
1279-
// Use the same wallet twice
1280-
let (mut wallet1, txid1) = get_funded_wallet_wpkh();
1281-
// But the second one has no knowledge of tx associated with txid1
1282-
let (main_descriptor, change_descriptor) = get_test_wpkh_and_change_desc();
1283-
let mut wallet2 = Wallet::create(main_descriptor, change_descriptor)
1284-
.network(Network::Regtest)
1285-
.create_wallet_no_persist()
1286-
.expect("descriptors must be valid");
1287-
1288-
let utxo1 = wallet1.list_unspent().next().unwrap();
1289-
let tx1 = wallet1.get_tx(txid1).unwrap().tx_node.as_ref().clone();
1290-
1291-
let satisfaction_weight = wallet1
1292-
.public_descriptor(KeychainKind::External)
1293-
.max_weight_to_satisfy()
1294-
.unwrap();
1295-
1296-
// Here we are copying old_params to simulate, the very unlikely behavior where a wallet
1297-
// becomes aware of a local UTXO while it is creating a transaction using the local UTXO
1298-
// it just became aware of as a foreign UTXO
1299-
let old_params = {
1300-
let mut builder = wallet1.build_tx();
1301-
1302-
builder
1303-
.add_utxo(utxo1.outpoint)
1304-
.expect("should add local utxo");
1264+
fn test_local_utxos_have_precedence_over_foreign_utxos() {
1265+
use crate::test_utils::get_funded_wallet_wpkh;
1266+
let (mut wallet, _) = get_funded_wallet_wpkh();
13051267

1306-
builder.params.clone()
1307-
};
1268+
let utxo = wallet.list_unspent().next().unwrap();
1269+
let outpoint = utxo.outpoint;
13081270

1309-
// Build a transaction as wallet2 was aware of utxo1 but not tracking transactions
1310-
let mut new_builder = wallet2.build_tx();
1311-
new_builder.params = old_params;
1271+
// case 1: add foreign after local, expect local is kept
1272+
let mut builder = wallet.build_tx();
1273+
builder.add_utxo(outpoint).unwrap();
13121274

1313-
// Check the local UTXO is still there
1314-
// UTXO should still be LocalOutput
1315-
assert!(matches!(
1316-
new_builder.params.utxos[0],
1317-
WeightedUtxo {
1318-
utxo: Utxo::Local { .. },
1319-
..
1320-
}
1321-
));
1275+
assert_eq!(builder.params.utxos[0].utxo.outpoint(), outpoint);
13221276

1323-
// add foreign UTXO
1324-
assert!(new_builder
1277+
builder
13251278
.add_foreign_utxo(
1326-
utxo1.outpoint,
1279+
outpoint,
13271280
psbt::Input {
1328-
non_witness_utxo: Some(tx1),
1281+
witness_utxo: Some(utxo.txout.clone()),
13291282
..Default::default()
13301283
},
1331-
satisfaction_weight,
1284+
Weight::from_wu(107),
13321285
)
1333-
.is_ok());
1286+
.unwrap();
13341287

1335-
assert_eq!(new_builder.params.utxos.len(), 1);
1336-
assert_eq!(new_builder.params.utxos[0].utxo.outpoint(), utxo1.outpoint);
1337-
// UTXO should still be LocalOutput
1288+
assert_eq!(builder.params.utxos.len(), 1);
13381289
assert!(matches!(
1339-
new_builder.params.utxos[0],
1340-
WeightedUtxo {
1341-
utxo: Utxo::Local(..),
1342-
..
1343-
}
1290+
&builder.params.utxos[0].utxo,
1291+
Utxo::Local(output) if output.outpoint == outpoint,
13441292
));
1345-
}
1346-
1347-
#[test]
1348-
fn test_prexisting_foreign_utxo_have_no_precedence_over_local_utxo_with_same_outpoint() {
1349-
// In this test we are assuming a setup where there are two wallets using the same
1350-
// descriptor, but only one is tracking transactions, while the other is not.
1351-
// Within this conditions we want the second wallet to be able to consider the unknown
1352-
// LocalOutputs provided by the first wallet with greater precedence than any foreign UTXO,
1353-
// even if the foreign UTXO shares the same outpoint.
1354-
//
1355-
// Remember the second wallet does not know about any UTXOs, so in principle, an unknown
1356-
// local UTXO could be added as foreign.
1357-
//
1358-
// In this case, the wallet adds a local UTXO as if it were foreign and after this it adds
1359-
// it as local UTXO. In this case the local UTXO should still have precedence over the
1360-
// foreign UTXO.
1361-
use crate::test_utils::{get_funded_wallet_wpkh, get_test_wpkh_and_change_desc, insert_tx};
1362-
use bitcoin::Network;
1363-
1364-
// Use the same wallet twice
1365-
let (wallet1, txid1) = get_funded_wallet_wpkh();
1366-
// But the second one has no knowledge of tx associated with txid1
1367-
let (main_descriptor, change_descriptor) = get_test_wpkh_and_change_desc();
1368-
let mut wallet2 = Wallet::create(main_descriptor, change_descriptor)
1369-
.network(Network::Regtest)
1370-
.create_wallet_no_persist()
1371-
.expect("descriptors must be valid");
13721293

1373-
let utxo1 = wallet1.list_unspent().next().unwrap();
1374-
let tx1 = wallet1.get_tx(txid1).unwrap().tx_node.as_ref().clone();
1294+
// case 2: add local after foreign, expect foreign is removed
1295+
builder.params = TxParams::default();
13751296

1376-
let satisfaction_weight = wallet1
1377-
.public_descriptor(KeychainKind::External)
1378-
.max_weight_to_satisfy()
1297+
builder
1298+
.add_foreign_utxo(
1299+
outpoint,
1300+
psbt::Input {
1301+
witness_utxo: Some(utxo.txout),
1302+
..Default::default()
1303+
},
1304+
Weight::from_wu(107),
1305+
)
13791306
.unwrap();
13801307

1381-
// Here we are copying old_params to simulate, the very unlikely behavior where a wallet
1382-
// becomes aware of a local UTXO while it is creating a transaction using the local UTXO
1383-
// it just became aware of as a foreign UTXO
1384-
let old_params = {
1385-
let mut builder = wallet2.build_tx();
1386-
1387-
// add foreign UTXO
1388-
assert!(builder
1389-
.add_foreign_utxo(
1390-
utxo1.outpoint,
1391-
psbt::Input {
1392-
non_witness_utxo: Some(tx1.clone()),
1393-
..Default::default()
1394-
},
1395-
satisfaction_weight,
1396-
)
1397-
.is_ok());
1308+
assert_eq!(builder.params.utxos[0].utxo.outpoint(), outpoint);
13981309

1399-
builder.params.clone()
1400-
};
1401-
1402-
// The wallet becomes aware of the new UTXO
1403-
insert_tx(&mut wallet2, tx1.clone());
1404-
1405-
// Keep building the old transaction as we wouldn't have stopped of doing so
1406-
let mut new_builder = wallet2.build_tx();
1407-
new_builder.params = old_params;
1408-
1409-
// Check the foreign UTXO is still there
1410-
// UTXO should still be LocalOutput
1411-
assert!(matches!(
1412-
new_builder.params.utxos[0],
1413-
WeightedUtxo {
1414-
utxo: Utxo::Foreign { .. },
1415-
..
1416-
}
1417-
));
1310+
builder.add_utxo(outpoint).unwrap();
14181311

1419-
// This method is the correct way of adding UTXOs to the builder.
1420-
// It checks the local availability of the UTXO, so a precondition for this method to work
1421-
// is that the transaction creating this UTXO must be already known by the wallet.
1422-
new_builder
1423-
.add_utxo(utxo1.outpoint)
1424-
.expect("should add local utxo");
1425-
1426-
assert_eq!(new_builder.params.utxos.len(), 1);
1427-
assert_eq!(new_builder.params.utxos[0].utxo.outpoint(), utxo1.outpoint);
1428-
1429-
// UTXO should still be LocalOutput
1430-
assert!(matches!(
1431-
new_builder.params.utxos[0],
1432-
WeightedUtxo {
1433-
utxo: Utxo::Local(..),
1434-
..
1435-
}
1436-
));
1312+
assert_eq!(builder.params.utxos.len(), 1);
1313+
assert!(
1314+
matches!(&builder.params.utxos[0].utxo, Utxo::Local(output) if output.outpoint == outpoint)
1315+
);
14371316
}
14381317
}

0 commit comments

Comments
 (0)