Skip to content

Commit 2c54be1

Browse files
committed
wip: Introduce ReplaceParams
1 parent a70f638 commit 2c54be1

File tree

7 files changed

+750
-131
lines changed

7 files changed

+750
-131
lines changed

wallet/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,7 @@ required-features = ["compiler"]
6464
[[example]]
6565
name = "psbt"
6666
required-features = ["test-utils"]
67+
68+
[[example]]
69+
name = "replace_by_fee"
70+
required-features = ["test-utils"]

wallet/examples/psbt.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![allow(unused_imports)]
21
#![allow(clippy::print_stdout)]
32

43
use std::collections::HashMap;
@@ -11,14 +10,11 @@ use bdk_chain::TxUpdate;
1110
use bdk_wallet::psbt::{PsbtParams, SelectionStrategy::*};
1211
use bdk_wallet::test_utils::*;
1312
use bdk_wallet::{KeychainKind::*, Update, Wallet};
14-
use bitcoin::FeeRate;
1513
use bitcoin::{
1614
bip32, consensus,
1715
secp256k1::{self, rand},
1816
Address, Amount, OutPoint, TxIn, TxOut,
1917
};
20-
use miniscript::descriptor::Descriptor;
21-
use miniscript::descriptor::KeyMap;
2218
use rand::Rng;
2319

2420
// This example shows how to create a PSBT using BDK Wallet.
@@ -31,7 +27,6 @@ const FEERATE: f64 = 2.0; // sat/vb
3127
fn main() -> anyhow::Result<()> {
3228
let (desc, change_desc) = get_test_wpkh_and_change_desc();
3329
let secp = secp256k1::Secp256k1::new();
34-
let mut rng = rand::thread_rng();
3530

3631
// Xpriv to be used for signing the PSBT
3732
let xprv = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L")?;
@@ -41,7 +36,7 @@ fn main() -> anyhow::Result<()> {
4136
.network(NETWORK)
4237
.create_wallet_no_persist()?;
4338

44-
fund_wallet(&mut wallet, &mut rng)?;
39+
fund_wallet(&mut wallet)?;
4540

4641
let utxos = wallet
4742
.list_unspent()
@@ -58,7 +53,7 @@ fn main() -> anyhow::Result<()> {
5853
.coin_selection(SingleRandomDraw);
5954

6055
// Create PSBT (which also returns the Finalizer).
61-
let (mut psbt, finalizer) = wallet.create_psbt(params, &mut rng)?;
56+
let (mut psbt, finalizer) = wallet.create_psbt(params)?;
6257

6358
dbg!(&psbt);
6459

@@ -72,23 +67,21 @@ fn main() -> anyhow::Result<()> {
7267
println!("TxOut: {}", txout.value);
7368
}
7469

75-
let sign_res = psbt.sign(&xprv, &secp);
76-
println!("Signed: {}", sign_res.is_ok());
77-
70+
let _ = psbt.sign(&xprv, &secp);
71+
println!("Signed: {}", !psbt.inputs[0].partial_sigs.is_empty());
7872
let finalize_res = finalizer.finalize(&mut psbt);
7973
println!("Finalized: {}", finalize_res.is_finalized());
8074

8175
let tx = psbt.extract_tx()?;
82-
8376
let feerate = wallet.calculate_fee_rate(&tx)?;
84-
println!("Feerate: {} sat/vb", bdk_wallet::floating_rate!(feerate));
77+
println!("Fee rate: {} sat/vb", bdk_wallet::floating_rate!(feerate));
8578

8679
println!("{}", consensus::encode::serialize_hex(&tx));
8780

8881
Ok(())
8982
}
9083

91-
fn fund_wallet(wallet: &mut Wallet, rng: &mut impl Rng) -> anyhow::Result<()> {
84+
fn fund_wallet(wallet: &mut Wallet) -> anyhow::Result<()> {
9285
let anchor = ConfirmationBlockTime {
9386
block_id: BlockId {
9487
height: 260071,
@@ -98,6 +91,8 @@ fn fund_wallet(wallet: &mut Wallet, rng: &mut impl Rng) -> anyhow::Result<()> {
9891
};
9992
insert_checkpoint(wallet, anchor.block_id);
10093

94+
let mut rng = rand::thread_rng();
95+
10196
// Fund wallet with several random utxos
10297
for i in 0..21 {
10398
let value = 10_000 * (i + 1) + (100 * rng.gen_range(0..10));
@@ -110,6 +105,12 @@ fn fund_wallet(wallet: &mut Wallet, rng: &mut impl Rng) -> anyhow::Result<()> {
110105
);
111106
}
112107

108+
let tip = BlockId {
109+
height: 260171,
110+
hash: "0000000b9efb77450e753ae9fd7be9f69219511c27b6e95c28f4126f3e1591c3".parse()?,
111+
};
112+
insert_checkpoint(wallet, tip);
113+
113114
Ok(())
114115
}
115116

wallet/examples/replace_by_fee.rs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
#![allow(clippy::print_stdout)]
2+
3+
use std::str::FromStr;
4+
use std::sync::Arc;
5+
6+
use bdk_chain::BlockId;
7+
use bdk_chain::ConfirmationBlockTime;
8+
use bdk_chain::TxUpdate;
9+
use bdk_wallet::test_utils::*;
10+
use bdk_wallet::{Update, Wallet};
11+
use bitcoin::{bip32, consensus, secp256k1, Address, FeeRate, Transaction};
12+
13+
// This example shows how to create a Replace-By-Fee (RBF) transaction using BDK Wallet.
14+
15+
const NETWORK: bitcoin::Network = bitcoin::Network::Regtest;
16+
const SEND_TO: &str = "bcrt1q3yfqg2v9d605r45y5ddt5unz5n8v7jl5yk4a4f";
17+
18+
fn main() -> anyhow::Result<()> {
19+
let desc = "wpkh(tprv8ZgxMBicQKsPe5tkv8BYJRupCNULhJYDv6qrtVAK9fNVheU6TbscSedVi8KQk8vVZqXMnsGomtVkR4nprbgsxTS5mAQPV4dpPXNvsmYcgZU/84h/1h/0h/0/*)";
20+
let change_desc = "wpkh(tprv8ZgxMBicQKsPe5tkv8BYJRupCNULhJYDv6qrtVAK9fNVheU6TbscSedVi8KQk8vVZqXMnsGomtVkR4nprbgsxTS5mAQPV4dpPXNvsmYcgZU/84h/1h/0h/1/*)";
21+
let secp = secp256k1::Secp256k1::new();
22+
23+
// Xpriv to be used for signing the PSBT
24+
let xprv = bip32::Xpriv::from_str("tprv8ZgxMBicQKsPe5tkv8BYJRupCNULhJYDv6qrtVAK9fNVheU6TbscSedVi8KQk8vVZqXMnsGomtVkR4nprbgsxTS5mAQPV4dpPXNvsmYcgZU")?;
25+
26+
// Create wallet and "fund" it.
27+
let mut wallet = Wallet::create(desc, change_desc)
28+
.network(NETWORK)
29+
.create_wallet_no_persist()?;
30+
31+
// `tx_1` is the unconfirmed wallet tx that we want to replace.
32+
let tx_1 = fund_wallet(&mut wallet)?;
33+
wallet.apply_unconfirmed_txs([(tx_1.clone(), 1234567000)]);
34+
35+
// We'll need to fill in the original recipient details.
36+
let addr = Address::from_str(SEND_TO)?.require_network(NETWORK)?;
37+
let txo = tx_1
38+
.output
39+
.iter()
40+
.find(|txo| txo.script_pubkey == addr.script_pubkey())
41+
.expect("failed to find orginal recipient")
42+
.clone();
43+
44+
// Now build fee bump.
45+
let (mut psbt, finalizer) = wallet.replace_by_fee_and_recipients(
46+
&[tx_1.clone()],
47+
FeeRate::from_sat_per_vb_unchecked(5),
48+
vec![(txo.script_pubkey, txo.value)],
49+
)?;
50+
51+
let _ = psbt.sign(&xprv, &secp);
52+
println!("Signed: {}", !psbt.inputs[0].partial_sigs.is_empty());
53+
let finalize_res = finalizer.finalize(&mut psbt);
54+
println!("Finalized: {}", finalize_res.is_finalized());
55+
56+
let tx = psbt.extract_tx()?;
57+
let feerate = wallet.calculate_fee_rate(&tx)?;
58+
println!("Fee rate: {} sat/vb", bdk_wallet::floating_rate!(feerate));
59+
60+
println!("{}", consensus::encode::serialize_hex(&tx));
61+
62+
wallet.apply_unconfirmed_txs([(tx.clone(), 1234567001)]);
63+
64+
let txid_2 = tx.compute_txid();
65+
66+
assert!(
67+
wallet
68+
.tx_graph()
69+
.direct_conflicts(&tx_1)
70+
.any(|(_, txid)| txid == txid_2),
71+
"ERROR: RBF tx does not conflict with `tx_1`",
72+
);
73+
74+
Ok(())
75+
}
76+
77+
fn fund_wallet(wallet: &mut Wallet) -> anyhow::Result<Arc<Transaction>> {
78+
// The parent of `tx`. This is needed to compute the original fee.
79+
let tx0: Arc<Transaction> = consensus::encode::deserialize_hex(
80+
"020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff025100ffffffff0200f2052a010000001600144d34238b9c4c59b9e2781e2426a142a75b8901ab0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
81+
)
82+
.map(Arc::new)?;
83+
84+
let anchor_block = BlockId {
85+
height: 101,
86+
hash: "3bcc1c447c6b3886f43e416b5c21cf5c139dc4829a71dc78609bc8f6235611c5".parse()?,
87+
};
88+
insert_tx_anchor(wallet, tx0, anchor_block);
89+
90+
let tx: Arc<Transaction> = consensus::encode::deserialize_hex(
91+
"020000000001014cb96536e94ba3f840cb5c2c965c8f9a306209de63fcd02060219aaf14f1d7b30000000000fdffffff0280de80020000000016001489120429856e9f41d684a35aba7262a4cecf4bf4f312852701000000160014757a57b3009c0e9b2b9aa548434dc295e21aeb05024730440220400c0a767ce42e0ea02b72faabb7f3433e607b475111285e0975bba1e6fd2e13022059453d83cbacb6652ba075f59ca0437036f3f94cae1959c7c5c0f96a8954707a012102c0851c2d2bddc1dd0b05caeac307703ec0c4b96ecad5a85af47f6420e2ef6c661b000000",
92+
)
93+
.map(Arc::new)?;
94+
95+
Ok(tx)
96+
}
97+
98+
fn insert_tx_anchor(wallet: &mut Wallet, tx: Arc<Transaction>, block_id: BlockId) {
99+
insert_checkpoint(wallet, block_id);
100+
let anchor = ConfirmationBlockTime {
101+
block_id,
102+
confirmation_time: 1234567000,
103+
};
104+
let txid = tx.compute_txid();
105+
106+
let mut tx_update = TxUpdate::default();
107+
tx_update.txs = vec![tx];
108+
tx_update.anchors = [(anchor, txid)].into();
109+
110+
wallet
111+
.apply_update(Update {
112+
tx_update,
113+
..Default::default()
114+
})
115+
.expect("failed to apply update");
116+
}

0 commit comments

Comments
 (0)