feat(wallet): add back TxBuilder finish() and sort_tx() with thread_rng()

This commit is contained in:
Steve Myers 2024-06-19 16:35:04 -05:00 committed by Rob N
parent 45c0cae0a4
commit 4bddb0de62
No known key found for this signature in database
GPG Key ID: F4DD8F8486EC0F1F
19 changed files with 243 additions and 240 deletions

View File

@ -1,6 +1,5 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::thread::JoinHandle; use std::thread::JoinHandle;
use std::usize;
use bdk_chain::collections::BTreeMap; use bdk_chain::collections::BTreeMap;
use bdk_chain::spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult}; use bdk_chain::spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult};

View File

@ -25,7 +25,7 @@ bip39 = { version = "2.0", optional = true }
[features] [features]
default = ["std"] default = ["std"]
std = ["bitcoin/std", "miniscript/std", "bdk_chain/std"] std = ["bitcoin/std", "bitcoin/rand-std", "miniscript/std", "bdk_chain/std"]
compiler = ["miniscript/compiler"] compiler = ["miniscript/compiler"]
all-keys = ["keys-bip39"] all-keys = ["keys-bip39"]
keys-bip39 = ["bip39"] keys-bip39 = ["bip39"]

View File

@ -70,30 +70,28 @@ To persist `Wallet` state data use a data store crate that reads and writes [`bd
```rust,no_run ```rust,no_run
use bdk_wallet::{bitcoin::Network, KeychainKind, wallet::{ChangeSet, Wallet}}; use bdk_wallet::{bitcoin::Network, KeychainKind, wallet::{ChangeSet, Wallet}};
fn main() { // Open or create a new file store for wallet data.
// Open or create a new file store for wallet data. let mut db =
let mut db =
bdk_file_store::Store::<ChangeSet>::open_or_create_new(b"magic_bytes", "/tmp/my_wallet.db") bdk_file_store::Store::<ChangeSet>::open_or_create_new(b"magic_bytes", "/tmp/my_wallet.db")
.expect("create store"); .expect("create store");
// Create a wallet with initial wallet data read from the file store. // Create a wallet with initial wallet data read from the file store.
let descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)"; let descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/0/*)";
let change_descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/1/*)"; let change_descriptor = "wpkh(tprv8ZgxMBicQKsPdcAqYBpzAFwU5yxBUo88ggoBqu1qPcHUfSbKK1sKMLmC7EAk438btHQrSdu3jGGQa6PA71nvH5nkDexhLteJqkM4dQmWF9g/84'/1'/0'/1/*)";
let changeset = db.aggregate_changesets().expect("changeset loaded"); let changeset = db.aggregate_changesets().expect("changeset loaded");
let mut wallet = let mut wallet =
Wallet::new_or_load(descriptor, change_descriptor, changeset, Network::Testnet) Wallet::new_or_load(descriptor, change_descriptor, changeset, Network::Testnet)
.expect("create or load wallet"); .expect("create or load wallet");
// Get a new address to receive bitcoin. // Get a new address to receive bitcoin.
let receive_address = wallet.reveal_next_address(KeychainKind::External); let receive_address = wallet.reveal_next_address(KeychainKind::External);
// Persist staged wallet data changes to the file store. // Persist staged wallet data changes to the file store.
let staged_changeset = wallet.take_staged(); let staged_changeset = wallet.take_staged();
if let Some(changeset) = staged_changeset { if let Some(changeset) = staged_changeset {
db.append_changeset(&changeset) db.append_changeset(&changeset)
.expect("must commit changes to database"); .expect("must commit changes to database");
}
println!("Your new receive address is: {}", receive_address.address);
} }
println!("Your new receive address is: {}", receive_address.address);
``` ```
<!-- ### Sync the balance of a descriptor --> <!-- ### Sync the balance of a descriptor -->
@ -154,7 +152,6 @@ fn main() {
<!-- use bitcoin::base64; --> <!-- use bitcoin::base64; -->
<!-- use bdk_wallet::bitcoin::consensus::serialize; --> <!-- use bdk_wallet::bitcoin::consensus::serialize; -->
<!-- use bdk_wallet::bitcoin::Network; --> <!-- use bdk_wallet::bitcoin::Network; -->
<!-- use rand::thread_rng(); -->
<!-- fn main() -> Result<(), bdk_wallet::Error> { --> <!-- fn main() -> Result<(), bdk_wallet::Error> { -->
<!-- let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); --> <!-- let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?); -->
@ -174,7 +171,7 @@ fn main() {
<!-- .enable_rbf() --> <!-- .enable_rbf() -->
<!-- .do_not_spend_change() --> <!-- .do_not_spend_change() -->
<!-- .fee_rate(FeeRate::from_sat_per_vb(5.0)); --> <!-- .fee_rate(FeeRate::from_sat_per_vb(5.0)); -->
<!-- builder.finish_with_aux_rand(&mut thread_rng())? --> <!-- builder.finish()? -->
<!-- }; --> <!-- }; -->
<!-- println!("Transaction details: {:#?}", details); --> <!-- println!("Transaction details: {:#?}", details); -->

View File

@ -15,7 +15,6 @@ use bdk_wallet::descriptor::IntoWalletDescriptor;
use bdk_wallet::keys::bip39::{Language, Mnemonic, WordCount}; use bdk_wallet::keys::bip39::{Language, Mnemonic, WordCount};
use bdk_wallet::keys::{GeneratableKey, GeneratedKey}; use bdk_wallet::keys::{GeneratableKey, GeneratedKey};
use bdk_wallet::miniscript::Tap; use bdk_wallet::miniscript::Tap;
use rand::thread_rng;
use std::str::FromStr; use std::str::FromStr;
/// This example demonstrates how to generate a mnemonic phrase /// This example demonstrates how to generate a mnemonic phrase
@ -26,9 +25,8 @@ fn main() -> Result<(), anyhow::Error> {
// In this example we are generating a 12 words mnemonic phrase // In this example we are generating a 12 words mnemonic phrase
// but it is also possible generate 15, 18, 21 and 24 words // but it is also possible generate 15, 18, 21 and 24 words
// using their respective `WordCount` variant. // using their respective `WordCount` variant.
let mut rng = thread_rng();
let mnemonic: GeneratedKey<_, Tap> = let mnemonic: GeneratedKey<_, Tap> =
Mnemonic::generate((WordCount::Words12, Language::English), &mut rng) Mnemonic::generate((WordCount::Words12, Language::English))
.map_err(|_| anyhow!("Mnemonic generation error"))?; .map_err(|_| anyhow!("Mnemonic generation error"))?;
println!("Mnemonic phrase: {}", *mnemonic); println!("Mnemonic phrase: {}", *mnemonic);

View File

@ -158,8 +158,6 @@ mod test {
use bip39::{Language, Mnemonic}; use bip39::{Language, Mnemonic};
use rand::thread_rng;
use crate::keys::{any_network, GeneratableKey, GeneratedKey}; use crate::keys::{any_network, GeneratableKey, GeneratedKey};
use super::WordCount; use super::WordCount;
@ -218,12 +216,12 @@ mod test {
#[test] #[test]
fn test_keys_generate_bip39_random() { fn test_keys_generate_bip39_random() {
let mut rng = thread_rng();
let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> = let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> =
Mnemonic::generate((WordCount::Words12, Language::English), &mut rng).unwrap(); Mnemonic::generate((WordCount::Words12, Language::English)).unwrap();
assert_eq!(generated_mnemonic.valid_networks, any_network()); assert_eq!(generated_mnemonic.valid_networks, any_network());
let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> = let generated_mnemonic: GeneratedKey<_, miniscript::Segwitv0> =
Mnemonic::generate((WordCount::Words24, Language::English), &mut rng).unwrap(); Mnemonic::generate((WordCount::Words24, Language::English)).unwrap();
assert_eq!(generated_mnemonic.valid_networks, any_network()); assert_eq!(generated_mnemonic.valid_networks, any_network());
} }
} }

View File

@ -633,8 +633,18 @@ pub trait GeneratableKey<Ctx: ScriptContext>: Sized {
entropy: Self::Entropy, entropy: Self::Entropy,
) -> Result<GeneratedKey<Self, Ctx>, Self::Error>; ) -> Result<GeneratedKey<Self, Ctx>, Self::Error>;
/// Generate a key given the options with a random entropy /// Generate a key given the options with random entropy.
fn generate( ///
/// Uses the thread-local random number generator.
#[cfg(feature = "std")]
fn generate(options: Self::Options) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
Self::generate_with_aux_rand(options, &mut bitcoin::key::rand::thread_rng())
}
/// Generate a key given the options with random entropy.
///
/// Uses a provided random number generator (rng).
fn generate_with_aux_rand(
options: Self::Options, options: Self::Options,
rng: &mut (impl CryptoRng + RngCore), rng: &mut (impl CryptoRng + RngCore),
) -> Result<GeneratedKey<Self, Ctx>, Self::Error> { ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
@ -660,10 +670,20 @@ where
} }
/// Generate a key with the default options and a random entropy /// Generate a key with the default options and a random entropy
fn generate_default( ///
/// Uses the thread-local random number generator.
#[cfg(feature = "std")]
fn generate_default() -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
Self::generate_with_aux_rand(Default::default(), &mut bitcoin::key::rand::thread_rng())
}
/// Generate a key with the default options and a random entropy
///
/// Uses a provided random number generator (rng).
fn generate_default_with_aux_rand(
rng: &mut (impl CryptoRng + RngCore), rng: &mut (impl CryptoRng + RngCore),
) -> Result<GeneratedKey<Self, Ctx>, Self::Error> { ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
Self::generate(Default::default(), rng) Self::generate_with_aux_rand(Default::default(), rng)
} }
} }

View File

@ -31,8 +31,6 @@
//! # use bdk_wallet::*; //! # use bdk_wallet::*;
//! # use bdk_wallet::wallet::coin_selection::decide_change; //! # use bdk_wallet::wallet::coin_selection::decide_change;
//! # use anyhow::Error; //! # use anyhow::Error;
//! # use rand::{thread_rng, RngCore};
//!
//! #[derive(Debug)] //! #[derive(Debug)]
//! struct AlwaysSpendEverything; //! struct AlwaysSpendEverything;
//! //!
@ -94,7 +92,7 @@
//! let psbt = { //! let psbt = {
//! let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything); //! let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything);
//! builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)); //! builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
//! builder.finish_with_aux_rand(&mut thread_rng())? //! builder.finish()?
//! }; //! };
//! //!
//! // inspect, sign, broadcast, ... //! // inspect, sign, broadcast, ...

View File

@ -44,9 +44,9 @@ impl fmt::Display for MiniscriptPsbtError {
impl std::error::Error for MiniscriptPsbtError {} impl std::error::Error for MiniscriptPsbtError {}
#[derive(Debug)] #[derive(Debug)]
/// Error returned from [`TxBuilder::finish_with_aux_rand`] /// Error returned from [`TxBuilder::finish`]
/// ///
/// [`TxBuilder::finish_with_aux_rand`]: crate::wallet::tx_builder::TxBuilder::finish_with_aux_rand /// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
pub enum CreateTxError { pub enum CreateTxError {
/// There was a problem with the descriptors passed in /// There was a problem with the descriptors passed in
Descriptor(DescriptorError), Descriptor(DescriptorError),

View File

@ -1212,7 +1212,6 @@ impl Wallet {
/// # use bdk_wallet::wallet::ChangeSet; /// # use bdk_wallet::wallet::ChangeSet;
/// # use bdk_wallet::wallet::error::CreateTxError; /// # use bdk_wallet::wallet::error::CreateTxError;
/// # use anyhow::Error; /// # use anyhow::Error;
/// # use rand::thread_rng;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@ -1220,7 +1219,7 @@ impl Wallet {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder /// builder
/// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)); /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
/// builder.finish_with_aux_rand(&mut thread_rng())? /// builder.finish()?
/// }; /// };
/// ///
/// // sign and broadcast ... /// // sign and broadcast ...
@ -1577,7 +1576,6 @@ impl Wallet {
/// # use bdk_wallet::wallet::ChangeSet; /// # use bdk_wallet::wallet::ChangeSet;
/// # use bdk_wallet::wallet::error::CreateTxError; /// # use bdk_wallet::wallet::error::CreateTxError;
/// # use anyhow::Error; /// # use anyhow::Error;
/// # use rand::thread_rng;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
@ -1586,7 +1584,7 @@ impl Wallet {
/// builder /// builder
/// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)) /// .add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000))
/// .enable_rbf(); /// .enable_rbf();
/// builder.finish_with_aux_rand(&mut thread_rng())? /// builder.finish()?
/// }; /// };
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?; /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
/// let tx = psbt.clone().extract_tx().expect("tx"); /// let tx = psbt.clone().extract_tx().expect("tx");
@ -1595,7 +1593,7 @@ impl Wallet {
/// let mut builder = wallet.build_fee_bump(tx.compute_txid())?; /// let mut builder = wallet.build_fee_bump(tx.compute_txid())?;
/// builder /// builder
/// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")); /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"));
/// builder.finish_with_aux_rand(&mut thread_rng())? /// builder.finish()?
/// }; /// };
/// ///
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?; /// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
@ -1755,14 +1753,13 @@ impl Wallet {
/// # use bdk_wallet::*; /// # use bdk_wallet::*;
/// # use bdk_wallet::wallet::ChangeSet; /// # use bdk_wallet::wallet::ChangeSet;
/// # use bdk_wallet::wallet::error::CreateTxError; /// # use bdk_wallet::wallet::error::CreateTxError;
/// # use rand::thread_rng;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// let mut psbt = { /// let mut psbt = {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000)); /// builder.add_recipient(to_address.script_pubkey(), Amount::from_sat(50_000));
/// builder.finish_with_aux_rand(&mut thread_rng())? /// builder.finish()?
/// }; /// };
/// let finalized = wallet.sign(&mut psbt, SignOptions::default())?; /// let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
/// assert!(finalized, "we should have signed all the inputs"); /// assert!(finalized, "we should have signed all the inputs");
@ -1807,6 +1804,7 @@ impl Wallet {
{ {
signer.sign_transaction(psbt, &sign_options, &self.secp)?; signer.sign_transaction(psbt, &sign_options, &self.secp)?;
} }
// attempt to finalize // attempt to finalize
if sign_options.try_finalize { if sign_options.try_finalize {
self.finalize_psbt(psbt, sign_options) self.finalize_psbt(psbt, sign_options)

View File

@ -20,7 +20,6 @@
//! # use bdk_wallet::wallet::ChangeSet; //! # use bdk_wallet::wallet::ChangeSet;
//! # use bdk_wallet::wallet::error::CreateTxError; //! # use bdk_wallet::wallet::error::CreateTxError;
//! # use anyhow::Error; //! # use anyhow::Error;
//! # use rand::thread_rng;
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked(); //! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
//! # let mut wallet = doctest_wallet!(); //! # let mut wallet = doctest_wallet!();
//! // create a TxBuilder from a wallet //! // create a TxBuilder from a wallet
@ -35,7 +34,7 @@
//! .do_not_spend_change() //! .do_not_spend_change()
//! // Turn on RBF signaling //! // Turn on RBF signaling
//! .enable_rbf(); //! .enable_rbf();
//! let psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?; //! let psbt = tx_builder.finish()?;
//! # Ok::<(), anyhow::Error>(()) //! # Ok::<(), anyhow::Error>(())
//! ``` //! ```
@ -57,7 +56,7 @@ use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
/// A transaction builder /// A transaction builder
/// ///
/// A `TxBuilder` is created by calling [`build_tx`] or [`build_fee_bump`] on a wallet. After /// A `TxBuilder` is created by calling [`build_tx`] or [`build_fee_bump`] on a wallet. After
/// assigning it, you set options on it until finally calling [`finish_with_aux_rand`] to consume the builder and /// assigning it, you set options on it until finally calling [`finish`] to consume the builder and
/// generate the transaction. /// generate the transaction.
/// ///
/// Each option setting method on `TxBuilder` takes and returns `&mut self` so you can chain calls /// Each option setting method on `TxBuilder` takes and returns `&mut self` so you can chain calls
@ -71,7 +70,6 @@ use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
/// # use bdk_wallet::wallet::ChangeSet; /// # use bdk_wallet::wallet::ChangeSet;
/// # use bdk_wallet::wallet::error::CreateTxError; /// # use bdk_wallet::wallet::error::CreateTxError;
/// # use anyhow::Error; /// # use anyhow::Error;
/// # use rand::thread_rng;
/// # let mut wallet = doctest_wallet!(); /// # let mut wallet = doctest_wallet!();
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked(); /// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// # let addr2 = addr1.clone(); /// # let addr2 = addr1.clone();
@ -82,7 +80,7 @@ use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
/// .ordering(TxOrdering::Untouched) /// .ordering(TxOrdering::Untouched)
/// .add_recipient(addr1.script_pubkey(), Amount::from_sat(50_000)) /// .add_recipient(addr1.script_pubkey(), Amount::from_sat(50_000))
/// .add_recipient(addr2.script_pubkey(), Amount::from_sat(50_000)); /// .add_recipient(addr2.script_pubkey(), Amount::from_sat(50_000));
/// builder.finish_with_aux_rand(&mut thread_rng())? /// builder.finish()?
/// }; /// };
/// ///
/// // non-chaining /// // non-chaining
@ -92,7 +90,7 @@ use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
/// for addr in &[addr1, addr2] { /// for addr in &[addr1, addr2] {
/// builder.add_recipient(addr.script_pubkey(), Amount::from_sat(50_000)); /// builder.add_recipient(addr.script_pubkey(), Amount::from_sat(50_000));
/// } /// }
/// builder.finish_with_aux_rand(&mut thread_rng())? /// builder.finish()?
/// }; /// };
/// ///
/// assert_eq!(psbt1.unsigned_tx.output[..2], psbt2.unsigned_tx.output[..2]); /// assert_eq!(psbt1.unsigned_tx.output[..2], psbt2.unsigned_tx.output[..2]);
@ -106,7 +104,7 @@ use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
/// ///
/// [`build_tx`]: Wallet::build_tx /// [`build_tx`]: Wallet::build_tx
/// [`build_fee_bump`]: Wallet::build_fee_bump /// [`build_fee_bump`]: Wallet::build_fee_bump
/// [`finish_with_aux_rand`]: Self::finish_with_aux_rand /// [`finish`]: Self::finish
/// [`coin_selection`]: Self::coin_selection /// [`coin_selection`]: Self::coin_selection
#[derive(Debug)] #[derive(Debug)]
pub struct TxBuilder<'a, Cs> { pub struct TxBuilder<'a, Cs> {
@ -358,11 +356,11 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
/// 2. The data in `non_witness_utxo` does not match what is in `outpoint`. /// 2. The data in `non_witness_utxo` does not match what is in `outpoint`.
/// ///
/// Note unless you set [`only_witness_utxo`] any non-taproot `psbt_input` you pass to this /// Note unless you set [`only_witness_utxo`] any non-taproot `psbt_input` you pass to this
/// method must have `non_witness_utxo` set otherwise you will get an error when [`finish_with_aux_rand`] /// method must have `non_witness_utxo` set otherwise you will get an error when [`finish`]
/// is called. /// is called.
/// ///
/// [`only_witness_utxo`]: Self::only_witness_utxo /// [`only_witness_utxo`]: Self::only_witness_utxo
/// [`finish_with_aux_rand`]: Self::finish_with_aux_rand /// [`finish`]: Self::finish
/// [`max_weight_to_satisfy`]: miniscript::Descriptor::max_weight_to_satisfy /// [`max_weight_to_satisfy`]: miniscript::Descriptor::max_weight_to_satisfy
pub fn add_foreign_utxo( pub fn add_foreign_utxo(
&mut self, &mut self,
@ -643,7 +641,6 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
/// # use bdk_wallet::wallet::ChangeSet; /// # use bdk_wallet::wallet::ChangeSet;
/// # use bdk_wallet::wallet::error::CreateTxError; /// # use bdk_wallet::wallet::error::CreateTxError;
/// # use anyhow::Error; /// # use anyhow::Error;
/// # use rand::thread_rng;
/// # let to_address = /// # let to_address =
/// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt") /// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
/// .unwrap() /// .unwrap()
@ -658,7 +655,7 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
/// .drain_to(to_address.script_pubkey()) /// .drain_to(to_address.script_pubkey())
/// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate")) /// .fee_rate(FeeRate::from_sat_per_vb(5).expect("valid feerate"))
/// .enable_rbf(); /// .enable_rbf();
/// let psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?; /// let psbt = tx_builder.finish()?;
/// # Ok::<(), anyhow::Error>(()) /// # Ok::<(), anyhow::Error>(())
/// ``` /// ```
/// ///
@ -674,6 +671,23 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs> { impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs> {
/// Finish building the transaction. /// Finish building the transaction.
/// ///
/// Uses the thread-local random number generator (rng).
///
/// Returns a new [`Psbt`] per [`BIP174`].
///
/// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
///
/// **WARNING**: To avoid change address reuse you must persist the changes resulting from one
/// or more calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
#[cfg(feature = "std")]
pub fn finish(self) -> Result<Psbt, CreateTxError> {
self.finish_with_aux_rand(&mut bitcoin::key::rand::thread_rng())
}
/// Finish building the transaction.
///
/// Uses a provided random number generator (rng).
///
/// Returns a new [`Psbt`] per [`BIP174`]. /// Returns a new [`Psbt`] per [`BIP174`].
/// ///
/// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki /// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
@ -762,7 +776,17 @@ pub enum TxOrdering {
} }
impl TxOrdering { impl TxOrdering {
/// Sort transaction inputs and outputs by [`TxOrdering`] variant /// Sort transaction inputs and outputs by [`TxOrdering`] variant.
///
/// Uses the thread-local random number generator (rng).
#[cfg(feature = "std")]
pub fn sort_tx(self, tx: &mut Transaction) {
self.sort_tx_with_aux_rand(tx, &mut bitcoin::key::rand::thread_rng())
}
/// Sort transaction inputs and outputs by [`TxOrdering`] variant.
///
/// Uses a provided random number generator (rng).
pub fn sort_tx_with_aux_rand(self, tx: &mut Transaction, rng: &mut impl RngCore) { pub fn sort_tx_with_aux_rand(self, tx: &mut Transaction, rng: &mut impl RngCore) {
match self { match self {
TxOrdering::Untouched => {} TxOrdering::Untouched => {}
@ -852,16 +876,14 @@ mod test {
use bitcoin::consensus::deserialize; use bitcoin::consensus::deserialize;
use bitcoin::hex::FromHex; use bitcoin::hex::FromHex;
use bitcoin::TxOut; use bitcoin::TxOut;
use rand::thread_rng;
use super::*; use super::*;
#[test] #[test]
fn test_output_ordering_untouched() { fn test_output_ordering_untouched() {
let original_tx = ordering_test_tx!(); let original_tx = ordering_test_tx!();
let mut tx = original_tx.clone(); let mut tx = original_tx.clone();
let mut rng = thread_rng();
TxOrdering::Untouched.sort_tx_with_aux_rand(&mut tx, &mut rng); TxOrdering::Untouched.sort_tx(&mut tx);
assert_eq!(original_tx, tx); assert_eq!(original_tx, tx);
} }
@ -870,11 +892,10 @@ mod test {
fn test_output_ordering_shuffle() { fn test_output_ordering_shuffle() {
let original_tx = ordering_test_tx!(); let original_tx = ordering_test_tx!();
let mut tx = original_tx.clone(); let mut tx = original_tx.clone();
let mut rng = thread_rng();
(0..40) (0..40)
.find(|_| { .find(|_| {
TxOrdering::Shuffle.sort_tx_with_aux_rand(&mut tx, &mut rng); TxOrdering::Shuffle.sort_tx(&mut tx);
original_tx.input != tx.input original_tx.input != tx.input
}) })
.expect("it should have moved the inputs at least once"); .expect("it should have moved the inputs at least once");
@ -882,7 +903,7 @@ mod test {
let mut tx = original_tx.clone(); let mut tx = original_tx.clone();
(0..40) (0..40)
.find(|_| { .find(|_| {
TxOrdering::Shuffle.sort_tx_with_aux_rand(&mut tx, &mut rng); TxOrdering::Shuffle.sort_tx(&mut tx);
original_tx.output != tx.output original_tx.output != tx.output
}) })
.expect("it should have moved the outputs at least once"); .expect("it should have moved the outputs at least once");
@ -894,9 +915,8 @@ mod test {
let original_tx = ordering_test_tx!(); let original_tx = ordering_test_tx!();
let mut tx = original_tx; let mut tx = original_tx;
let mut rng = thread_rng();
TxOrdering::Bip69Lexicographic.sort_tx_with_aux_rand(&mut tx, &mut rng); TxOrdering::Bip69Lexicographic.sort_tx(&mut tx);
assert_eq!( assert_eq!(
tx.input[0].previous_output, tx.input[0].previous_output,

View File

@ -201,12 +201,14 @@ mod test {
} }
#[test] #[test]
#[cfg(feature = "std")]
fn test_shuffle_slice_empty_vec() { fn test_shuffle_slice_empty_vec() {
let mut test: Vec<u8> = vec![]; let mut test: Vec<u8> = vec![];
shuffle_slice(&mut test, &mut thread_rng()); shuffle_slice(&mut test, &mut thread_rng());
} }
#[test] #[test]
#[cfg(feature = "std")]
fn test_shuffle_slice_single_vec() { fn test_shuffle_slice_single_vec() {
let mut test: Vec<u8> = vec![0]; let mut test: Vec<u8> = vec![0];
shuffle_slice(&mut test, &mut thread_rng()); shuffle_slice(&mut test, &mut thread_rng());

View File

@ -1,7 +1,6 @@
use bdk_wallet::bitcoin::{Amount, FeeRate, Psbt, TxIn}; use bdk_wallet::bitcoin::{Amount, FeeRate, Psbt, TxIn};
use bdk_wallet::{psbt, KeychainKind, SignOptions}; use bdk_wallet::{psbt, KeychainKind, SignOptions};
use core::str::FromStr; use core::str::FromStr;
use rand::thread_rng;
mod common; mod common;
use common::*; use common::*;
@ -16,7 +15,7 @@ fn test_psbt_malformed_psbt_input_legacy() {
let send_to = wallet.peek_address(KeychainKind::External, 0); let send_to = wallet.peek_address(KeychainKind::External, 0);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut psbt = builder.finish().unwrap();
psbt.inputs.push(psbt_bip.inputs[0].clone()); psbt.inputs.push(psbt_bip.inputs[0].clone());
let options = SignOptions { let options = SignOptions {
trust_witness_utxo: true, trust_witness_utxo: true,
@ -33,7 +32,7 @@ fn test_psbt_malformed_psbt_input_segwit() {
let send_to = wallet.peek_address(KeychainKind::External, 0); let send_to = wallet.peek_address(KeychainKind::External, 0);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut psbt = builder.finish().unwrap();
psbt.inputs.push(psbt_bip.inputs[1].clone()); psbt.inputs.push(psbt_bip.inputs[1].clone());
let options = SignOptions { let options = SignOptions {
trust_witness_utxo: true, trust_witness_utxo: true,
@ -49,7 +48,7 @@ fn test_psbt_malformed_tx_input() {
let send_to = wallet.peek_address(KeychainKind::External, 0); let send_to = wallet.peek_address(KeychainKind::External, 0);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut psbt = builder.finish().unwrap();
psbt.unsigned_tx.input.push(TxIn::default()); psbt.unsigned_tx.input.push(TxIn::default());
let options = SignOptions { let options = SignOptions {
trust_witness_utxo: true, trust_witness_utxo: true,
@ -65,7 +64,7 @@ fn test_psbt_sign_with_finalized() {
let send_to = wallet.peek_address(KeychainKind::External, 0); let send_to = wallet.peek_address(KeychainKind::External, 0);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000)); builder.add_recipient(send_to.script_pubkey(), Amount::from_sat(10_000));
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut psbt = builder.finish().unwrap();
// add a finalized input // add a finalized input
psbt.inputs.push(psbt_bip.inputs[0].clone()); psbt.inputs.push(psbt_bip.inputs[0].clone());
@ -87,7 +86,7 @@ fn test_psbt_fee_rate_with_witness_utxo() {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.drain_to(addr.script_pubkey()).drain_wallet();
builder.fee_rate(expected_fee_rate); builder.fee_rate(expected_fee_rate);
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut psbt = builder.finish().unwrap();
let fee_amount = psbt.fee_amount(); let fee_amount = psbt.fee_amount();
assert!(fee_amount.is_some()); assert!(fee_amount.is_some());
@ -112,7 +111,7 @@ fn test_psbt_fee_rate_with_nonwitness_utxo() {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.drain_to(addr.script_pubkey()).drain_wallet();
builder.fee_rate(expected_fee_rate); builder.fee_rate(expected_fee_rate);
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut psbt = builder.finish().unwrap();
let fee_amount = psbt.fee_amount(); let fee_amount = psbt.fee_amount();
assert!(fee_amount.is_some()); assert!(fee_amount.is_some());
let unfinalized_fee_rate = psbt.fee_rate().unwrap(); let unfinalized_fee_rate = psbt.fee_rate().unwrap();
@ -136,7 +135,7 @@ fn test_psbt_fee_rate_with_missing_txout() {
let mut builder = wpkh_wallet.build_tx(); let mut builder = wpkh_wallet.build_tx();
builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.drain_to(addr.script_pubkey()).drain_wallet();
builder.fee_rate(expected_fee_rate); builder.fee_rate(expected_fee_rate);
let mut wpkh_psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut wpkh_psbt = builder.finish().unwrap();
wpkh_psbt.inputs[0].witness_utxo = None; wpkh_psbt.inputs[0].witness_utxo = None;
wpkh_psbt.inputs[0].non_witness_utxo = None; wpkh_psbt.inputs[0].non_witness_utxo = None;
@ -150,7 +149,7 @@ fn test_psbt_fee_rate_with_missing_txout() {
let mut builder = pkh_wallet.build_tx(); let mut builder = pkh_wallet.build_tx();
builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.drain_to(addr.script_pubkey()).drain_wallet();
builder.fee_rate(expected_fee_rate); builder.fee_rate(expected_fee_rate);
let mut pkh_psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut pkh_psbt = builder.finish().unwrap();
pkh_psbt.inputs[0].non_witness_utxo = None; pkh_psbt.inputs[0].non_witness_utxo = None;
assert!(pkh_psbt.fee_amount().is_none()); assert!(pkh_psbt.fee_amount().is_none());
@ -179,7 +178,7 @@ fn test_psbt_multiple_internalkey_signers() {
let send_to = wallet.peek_address(KeychainKind::External, 0); let send_to = wallet.peek_address(KeychainKind::External, 0);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.drain_to(send_to.script_pubkey()).drain_wallet(); builder.drain_to(send_to.script_pubkey()).drain_wallet();
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap(); let mut psbt = builder.finish().unwrap();
let unsigned_tx = psbt.unsigned_tx.clone(); let unsigned_tx = psbt.unsigned_tx.clone();
// Adds a signer for the wrong internal key, bdk should not use this key to sign // Adds a signer for the wrong internal key, bdk should not use this key to sign

File diff suppressed because it is too large Load Diff

View File

@ -8,4 +8,3 @@ bdk_wallet = { path = "../../crates/wallet" }
bdk_electrum = { path = "../../crates/electrum" } bdk_electrum = { path = "../../crates/electrum" }
bdk_file_store = { path = "../../crates/file_store" } bdk_file_store = { path = "../../crates/file_store" }
anyhow = "1" anyhow = "1"
rand = "0.8.0"

View File

@ -14,7 +14,6 @@ use bdk_wallet::bitcoin::{Address, Amount};
use bdk_wallet::chain::collections::HashSet; use bdk_wallet::chain::collections::HashSet;
use bdk_wallet::{bitcoin::Network, Wallet}; use bdk_wallet::{bitcoin::Network, Wallet};
use bdk_wallet::{KeychainKind, SignOptions}; use bdk_wallet::{KeychainKind, SignOptions};
use rand::thread_rng;
fn main() -> Result<(), anyhow::Error> { fn main() -> Result<(), anyhow::Error> {
let db_path = std::env::temp_dir().join("bdk-electrum-example"); let db_path = std::env::temp_dir().join("bdk-electrum-example");
@ -97,7 +96,7 @@ fn main() -> Result<(), anyhow::Error> {
.add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT) .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
.enable_rbf(); .enable_rbf();
let mut psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?; let mut psbt = tx_builder.finish()?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?; let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
assert!(finalized); assert!(finalized);

View File

@ -11,4 +11,3 @@ bdk_esplora = { path = "../../crates/esplora", features = ["async-https"] }
bdk_sqlite = { path = "../../crates/sqlite" } bdk_sqlite = { path = "../../crates/sqlite" }
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] } tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
anyhow = "1" anyhow = "1"
rand = "0.8.0"

View File

@ -5,7 +5,6 @@ use bdk_wallet::{
bitcoin::{Address, Amount, Network, Script}, bitcoin::{Address, Amount, Network, Script},
KeychainKind, SignOptions, Wallet, KeychainKind, SignOptions, Wallet,
}; };
use rand::thread_rng;
use bdk_sqlite::{rusqlite::Connection, Store}; use bdk_sqlite::{rusqlite::Connection, Store};
@ -104,7 +103,7 @@ async fn main() -> Result<(), anyhow::Error> {
.add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT) .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
.enable_rbf(); .enable_rbf();
let mut psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?; let mut psbt = tx_builder.finish()?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?; let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
assert!(finalized); assert!(finalized);

View File

@ -11,4 +11,3 @@ bdk_wallet = { path = "../../crates/wallet" }
bdk_esplora = { path = "../../crates/esplora", features = ["blocking"] } bdk_esplora = { path = "../../crates/esplora", features = ["blocking"] }
bdk_file_store = { path = "../../crates/file_store" } bdk_file_store = { path = "../../crates/file_store" }
anyhow = "1" anyhow = "1"
rand = "0.8.0"

View File

@ -11,7 +11,6 @@ use bdk_wallet::{
bitcoin::{Address, Amount, Network}, bitcoin::{Address, Amount, Network},
KeychainKind, SignOptions, Wallet, KeychainKind, SignOptions, Wallet,
}; };
use rand::thread_rng;
fn main() -> Result<(), anyhow::Error> { fn main() -> Result<(), anyhow::Error> {
let db_path = std::env::temp_dir().join("bdk-esplora-example"); let db_path = std::env::temp_dir().join("bdk-esplora-example");
@ -81,7 +80,7 @@ fn main() -> Result<(), anyhow::Error> {
.add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT) .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
.enable_rbf(); .enable_rbf();
let mut psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?; let mut psbt = tx_builder.finish()?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?; let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
assert!(finalized); assert!(finalized);