Skip to content

Commit b902be2

Browse files
committed
chore: add tests using legacy PKH wallet
1 parent e6185d6 commit b902be2

File tree

2 files changed

+202
-0
lines changed

2 files changed

+202
-0
lines changed

crates/bdk/tests/common.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ pub fn get_funded_wallet(descriptor: &str) -> (Wallet, bitcoin::Txid) {
108108
get_funded_wallet_with_change(descriptor, None)
109109
}
110110

111+
pub fn get_test_pkh() -> &'static str {
112+
"pkh(0275d93539d503d824ad0c69a4c23ae480700489de09378ba32c1776cf86d6bb93)"
113+
}
114+
111115
pub fn get_test_wpkh() -> &'static str {
112116
"wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)"
113117
}

crates/bdk/tests/wallet.rs

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,78 @@ fn test_create_tx_global_xpubs_with_origin() {
10311031
assert_eq!(psbt.xpub.get(&key), Some(&(fingerprint, path)));
10321032
}
10331033

1034+
#[test]
1035+
fn test_legacy_add_foreign_utxo() {
1036+
let (mut wallet1, _) = get_funded_wallet(get_test_pkh()); // legacy wallet using PKH
1037+
let (wallet2, _) =
1038+
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
1039+
1040+
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1041+
.unwrap()
1042+
.assume_checked();
1043+
let utxo = wallet2.list_unspent().next().expect("must take!");
1044+
let foreign_utxo_satisfaction = wallet2
1045+
.get_descriptor_for_keychain(KeychainKind::External)
1046+
.max_weight_to_satisfy()
1047+
.unwrap();
1048+
1049+
let psbt_input = psbt::Input {
1050+
witness_utxo: Some(utxo.txout.clone()),
1051+
..Default::default()
1052+
};
1053+
1054+
let mut builder = wallet1.build_tx();
1055+
builder
1056+
.add_recipient(addr.script_pubkey(), 60_000)
1057+
.only_witness_utxo()
1058+
.add_foreign_utxo(utxo.outpoint, psbt_input, foreign_utxo_satisfaction)
1059+
.unwrap();
1060+
let mut psbt = builder.finish().unwrap();
1061+
wallet1.insert_txout(utxo.outpoint, utxo.txout);
1062+
let fee = check_fee!(wallet1, psbt);
1063+
let sent_received = wallet1.sent_and_received(&psbt.clone().extract_tx());
1064+
1065+
assert_eq!(
1066+
sent_received.0 - sent_received.1,
1067+
10_000 + fee.unwrap_or(0),
1068+
"we should have only net spent ~10_000"
1069+
);
1070+
1071+
assert!(
1072+
psbt.unsigned_tx
1073+
.input
1074+
.iter()
1075+
.any(|input| input.previous_output == utxo.outpoint),
1076+
"foreign_utxo should be in there"
1077+
);
1078+
1079+
let finished = wallet1
1080+
.sign(
1081+
&mut psbt,
1082+
SignOptions {
1083+
trust_witness_utxo: true,
1084+
..Default::default()
1085+
},
1086+
)
1087+
.unwrap();
1088+
1089+
assert!(
1090+
!finished,
1091+
"only one of the inputs should have been signed so far"
1092+
);
1093+
1094+
let finished = wallet2
1095+
.sign(
1096+
&mut psbt,
1097+
SignOptions {
1098+
trust_witness_utxo: true,
1099+
..Default::default()
1100+
},
1101+
)
1102+
.unwrap();
1103+
assert!(finished, "all the inputs should have been signed now");
1104+
}
1105+
10341106
#[test]
10351107
fn test_add_foreign_utxo() {
10361108
let (mut wallet1, _) = get_funded_wallet(get_test_wpkh());
@@ -1713,6 +1785,73 @@ fn test_bump_fee_remove_output_manually_selected_only() {
17131785
builder.finish().unwrap();
17141786
}
17151787

1788+
#[test]
1789+
fn test_legacy_bump_fee_add_input() {
1790+
let (mut wallet, _) = get_funded_wallet(get_test_pkh()); // legacy wallet PKH
1791+
let init_tx = Transaction {
1792+
version: 1,
1793+
lock_time: absolute::LockTime::ZERO,
1794+
input: vec![],
1795+
output: vec![TxOut {
1796+
script_pubkey: wallet.get_address(New).script_pubkey(),
1797+
value: 25_000,
1798+
}],
1799+
};
1800+
let pos = wallet
1801+
.transactions()
1802+
.last()
1803+
.unwrap()
1804+
.chain_position
1805+
.cloned()
1806+
.into();
1807+
wallet.insert_tx(init_tx, pos).unwrap();
1808+
1809+
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
1810+
.unwrap()
1811+
.assume_checked();
1812+
let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
1813+
builder
1814+
.add_recipient(addr.script_pubkey(), 45_000)
1815+
.enable_rbf();
1816+
let psbt = builder.finish().unwrap();
1817+
let tx = psbt.extract_tx();
1818+
let original_details = wallet.sent_and_received(&tx);
1819+
let txid = tx.txid();
1820+
wallet
1821+
.insert_tx(tx, ConfirmationTime::Unconfirmed { last_seen: 0 })
1822+
.unwrap();
1823+
1824+
let mut builder = wallet.build_fee_bump(txid).unwrap();
1825+
builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
1826+
let psbt = builder.finish().unwrap();
1827+
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
1828+
let fee = check_fee!(wallet, psbt);
1829+
assert_eq!(sent_received.0, original_details.0 + 25_000);
1830+
assert_eq!(fee.unwrap_or(0) + sent_received.1, 30_000);
1831+
1832+
let tx = &psbt.unsigned_tx;
1833+
assert_eq!(tx.input.len(), 2);
1834+
assert_eq!(tx.output.len(), 2);
1835+
assert_eq!(
1836+
tx.output
1837+
.iter()
1838+
.find(|txout| txout.script_pubkey == addr.script_pubkey())
1839+
.unwrap()
1840+
.value,
1841+
45_000
1842+
);
1843+
assert_eq!(
1844+
tx.output
1845+
.iter()
1846+
.find(|txout| txout.script_pubkey != addr.script_pubkey())
1847+
.unwrap()
1848+
.value,
1849+
sent_received.1
1850+
);
1851+
1852+
assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature);
1853+
}
1854+
17161855
#[test]
17171856
fn test_bump_fee_add_input() {
17181857
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
@@ -1963,6 +2102,65 @@ fn test_bump_fee_add_input_change_dust() {
19632102
assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(140.0), @dust_change, @add_signature);
19642103
}
19652104

2105+
#[test]
2106+
fn test_legacy_bump_fee_force_add_input() {
2107+
let (mut wallet, _) = get_funded_wallet(get_test_pkh()); // legacy wallet using PKH
2108+
let incoming_op = receive_output_in_latest_block(&mut wallet, 25_000);
2109+
2110+
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
2111+
.unwrap()
2112+
.assume_checked();
2113+
let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
2114+
builder
2115+
.add_recipient(addr.script_pubkey(), 45_000)
2116+
.enable_rbf();
2117+
let psbt = builder.finish().unwrap();
2118+
let mut tx = psbt.extract_tx();
2119+
let original_sent_received = wallet.sent_and_received(&tx);
2120+
let txid = tx.txid();
2121+
for txin in &mut tx.input {
2122+
txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
2123+
}
2124+
wallet
2125+
.insert_tx(tx.clone(), ConfirmationTime::Unconfirmed { last_seen: 0 })
2126+
.unwrap();
2127+
// the new fee_rate is low enough that just reducing the change would be fine, but we force
2128+
// the addition of an extra input with `add_utxo()`
2129+
let mut builder = wallet.build_fee_bump(txid).unwrap();
2130+
builder
2131+
.add_utxo(incoming_op)
2132+
.unwrap()
2133+
.fee_rate(FeeRate::from_sat_per_vb(5.0));
2134+
let psbt = builder.finish().unwrap();
2135+
let sent_received = wallet.sent_and_received(&psbt.clone().extract_tx());
2136+
let fee = check_fee!(wallet, psbt);
2137+
2138+
assert_eq!(sent_received.0, original_sent_received.0 + 25_000);
2139+
assert_eq!(fee.unwrap_or(0) + sent_received.1, 30_000);
2140+
2141+
let tx = &psbt.unsigned_tx;
2142+
assert_eq!(tx.input.len(), 2);
2143+
assert_eq!(tx.output.len(), 2);
2144+
assert_eq!(
2145+
tx.output
2146+
.iter()
2147+
.find(|txout| txout.script_pubkey == addr.script_pubkey())
2148+
.unwrap()
2149+
.value,
2150+
45_000
2151+
);
2152+
assert_eq!(
2153+
tx.output
2154+
.iter()
2155+
.find(|txout| txout.script_pubkey != addr.script_pubkey())
2156+
.unwrap()
2157+
.value,
2158+
sent_received.1
2159+
);
2160+
2161+
assert_fee_rate!(psbt, fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature);
2162+
}
2163+
19662164
#[test]
19672165
fn test_bump_fee_force_add_input() {
19682166
let (mut wallet, _) = get_funded_wallet(get_test_wpkh());

0 commit comments

Comments
 (0)