fix(bdk): remove rand dependency
This commit is contained in:
parent
0543801787
commit
45c0cae0a4
2
.github/workflows/cont_integration.yml
vendored
2
.github/workflows/cont_integration.yml
vendored
@ -92,7 +92,7 @@ jobs:
|
|||||||
uses: Swatinem/rust-cache@v2.2.1
|
uses: Swatinem/rust-cache@v2.2.1
|
||||||
- name: Check bdk wallet
|
- name: Check bdk wallet
|
||||||
working-directory: ./crates/wallet
|
working-directory: ./crates/wallet
|
||||||
run: cargo check --target wasm32-unknown-unknown --no-default-features --features miniscript/no-std,bdk_chain/hashbrown,dev-getrandom-wasm
|
run: cargo check --target wasm32-unknown-unknown --no-default-features --features miniscript/no-std,bdk_chain/hashbrown
|
||||||
- name: Check esplora
|
- name: Check esplora
|
||||||
working-directory: ./crates/esplora
|
working-directory: ./crates/esplora
|
||||||
run: cargo check --target wasm32-unknown-unknown --no-default-features --features miniscript/no-std,bdk_chain/hashbrown,async
|
run: cargo check --target wasm32-unknown-unknown --no-default-features --features miniscript/no-std,bdk_chain/hashbrown,async
|
||||||
|
@ -13,9 +13,9 @@ edition = "2021"
|
|||||||
rust-version = "1.63"
|
rust-version = "1.63"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand = "^0.8"
|
rand_core = { version = "0.6.0" }
|
||||||
miniscript = { version = "12.0.0", features = ["serde"], default-features = false }
|
miniscript = { version = "12.0.0", features = ["serde"], default-features = false }
|
||||||
bitcoin = { version = "0.32.0", features = ["serde", "base64", "rand-std"], default-features = false }
|
bitcoin = { version = "0.32.0", features = ["serde", "base64"], default-features = false }
|
||||||
serde = { version = "^1.0", features = ["derive"] }
|
serde = { version = "^1.0", features = ["derive"] }
|
||||||
serde_json = { version = "^1.0" }
|
serde_json = { version = "^1.0" }
|
||||||
bdk_chain = { path = "../chain", version = "0.16.0", features = ["miniscript", "serde"], default-features = false }
|
bdk_chain = { path = "../chain", version = "0.16.0", features = ["miniscript", "serde"], default-features = false }
|
||||||
@ -23,10 +23,6 @@ bdk_chain = { path = "../chain", version = "0.16.0", features = ["miniscript", "
|
|||||||
# Optional dependencies
|
# Optional dependencies
|
||||||
bip39 = { version = "2.0", optional = true }
|
bip39 = { version = "2.0", optional = true }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
|
||||||
getrandom = "0.2"
|
|
||||||
js-sys = "0.3"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
std = ["bitcoin/std", "miniscript/std", "bdk_chain/std"]
|
std = ["bitcoin/std", "miniscript/std", "bdk_chain/std"]
|
||||||
@ -34,11 +30,6 @@ compiler = ["miniscript/compiler"]
|
|||||||
all-keys = ["keys-bip39"]
|
all-keys = ["keys-bip39"]
|
||||||
keys-bip39 = ["bip39"]
|
keys-bip39 = ["bip39"]
|
||||||
|
|
||||||
# This feature is used to run `cargo check` in our CI targeting wasm. It's not recommended
|
|
||||||
# for libraries to explicitly include the "getrandom/js" feature, so we only do it when
|
|
||||||
# necessary for running our CI. See: https://docs.rs/getrandom/0.2.8/getrandom/#webassembly-support
|
|
||||||
dev-getrandom-wasm = ["getrandom/js"]
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
assert_matches = "1.5.0"
|
assert_matches = "1.5.0"
|
||||||
@ -46,6 +37,7 @@ tempfile = "3"
|
|||||||
bdk_sqlite = { path = "../sqlite" }
|
bdk_sqlite = { path = "../sqlite" }
|
||||||
bdk_file_store = { path = "../file_store" }
|
bdk_file_store = { path = "../file_store" }
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
rand = "^0.8"
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -154,6 +154,7 @@ 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")?); -->
|
||||||
@ -173,7 +174,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()? -->
|
<!-- builder.finish_with_aux_rand(&mut thread_rng())? -->
|
||||||
<!-- }; -->
|
<!-- }; -->
|
||||||
|
|
||||||
<!-- println!("Transaction details: {:#?}", details); -->
|
<!-- println!("Transaction details: {:#?}", details); -->
|
||||||
|
@ -15,6 +15,7 @@ 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
|
||||||
@ -25,8 +26,9 @@ 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))
|
Mnemonic::generate((WordCount::Words12, Language::English), &mut rng)
|
||||||
.map_err(|_| anyhow!("Mnemonic generation error"))?;
|
.map_err(|_| anyhow!("Mnemonic generation error"))?;
|
||||||
|
|
||||||
println!("Mnemonic phrase: {}", *mnemonic);
|
println!("Mnemonic phrase: {}", *mnemonic);
|
||||||
|
@ -158,6 +158,8 @@ 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;
|
||||||
@ -216,12 +218,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)).unwrap();
|
Mnemonic::generate((WordCount::Words12, Language::English), &mut rng).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)).unwrap();
|
Mnemonic::generate((WordCount::Words24, Language::English), &mut rng).unwrap();
|
||||||
assert_eq!(generated_mnemonic.valid_networks, any_network());
|
assert_eq!(generated_mnemonic.valid_networks, any_network());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ use core::marker::PhantomData;
|
|||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
|
||||||
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use bitcoin::secp256k1::{self, Secp256k1, Signing};
|
use bitcoin::secp256k1::{self, Secp256k1, Signing};
|
||||||
|
|
||||||
use bitcoin::bip32;
|
use bitcoin::bip32;
|
||||||
@ -632,11 +634,12 @@ pub trait GeneratableKey<Ctx: ScriptContext>: Sized {
|
|||||||
) -> 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 a random entropy
|
||||||
fn generate(options: Self::Options) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
|
fn generate(
|
||||||
use rand::{thread_rng, Rng};
|
options: Self::Options,
|
||||||
|
rng: &mut (impl CryptoRng + RngCore),
|
||||||
|
) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
|
||||||
let mut entropy = Self::Entropy::default();
|
let mut entropy = Self::Entropy::default();
|
||||||
thread_rng().fill(entropy.as_mut());
|
rng.fill_bytes(entropy.as_mut());
|
||||||
Self::generate_with_entropy(options, entropy)
|
Self::generate_with_entropy(options, entropy)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -657,8 +660,10 @@ 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() -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
|
fn generate_default(
|
||||||
Self::generate(Default::default())
|
rng: &mut (impl CryptoRng + RngCore),
|
||||||
|
) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
|
||||||
|
Self::generate(Default::default(), rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@
|
|||||||
//! # 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;
|
||||||
//!
|
//!
|
||||||
@ -92,7 +94,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()?
|
//! builder.finish_with_aux_rand(&mut thread_rng())?
|
||||||
//! };
|
//! };
|
||||||
//!
|
//!
|
||||||
//! // inspect, sign, broadcast, ...
|
//! // inspect, sign, broadcast, ...
|
||||||
@ -114,8 +116,9 @@ use bitcoin::{Script, Weight};
|
|||||||
|
|
||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use core::fmt::{self, Formatter};
|
use core::fmt::{self, Formatter};
|
||||||
use rand::seq::SliceRandom;
|
use rand_core::RngCore;
|
||||||
|
|
||||||
|
use super::utils::shuffle_slice;
|
||||||
/// Default coin selection algorithm used by [`TxBuilder`](super::tx_builder::TxBuilder) if not
|
/// Default coin selection algorithm used by [`TxBuilder`](super::tx_builder::TxBuilder) if not
|
||||||
/// overridden
|
/// overridden
|
||||||
pub type DefaultCoinSelectionAlgorithm = BranchAndBoundCoinSelection;
|
pub type DefaultCoinSelectionAlgorithm = BranchAndBoundCoinSelection;
|
||||||
@ -516,27 +519,16 @@ impl CoinSelectionAlgorithm for BranchAndBoundCoinSelection {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self
|
self.bnb(
|
||||||
.bnb(
|
required_utxos.clone(),
|
||||||
required_utxos.clone(),
|
optional_utxos.clone(),
|
||||||
optional_utxos.clone(),
|
curr_value,
|
||||||
curr_value,
|
curr_available_value,
|
||||||
curr_available_value,
|
target_amount,
|
||||||
target_amount,
|
cost_of_change,
|
||||||
cost_of_change,
|
drain_script,
|
||||||
drain_script,
|
fee_rate,
|
||||||
fee_rate,
|
)
|
||||||
)
|
|
||||||
.unwrap_or_else(|_| {
|
|
||||||
self.single_random_draw(
|
|
||||||
required_utxos,
|
|
||||||
optional_utxos,
|
|
||||||
curr_value,
|
|
||||||
target_amount,
|
|
||||||
drain_script,
|
|
||||||
fee_rate,
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -663,40 +655,6 @@ impl BranchAndBoundCoinSelection {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
|
||||||
fn single_random_draw(
|
|
||||||
&self,
|
|
||||||
required_utxos: Vec<OutputGroup>,
|
|
||||||
mut optional_utxos: Vec<OutputGroup>,
|
|
||||||
curr_value: i64,
|
|
||||||
target_amount: i64,
|
|
||||||
drain_script: &Script,
|
|
||||||
fee_rate: FeeRate,
|
|
||||||
) -> CoinSelectionResult {
|
|
||||||
optional_utxos.shuffle(&mut rand::thread_rng());
|
|
||||||
let selected_utxos = optional_utxos.into_iter().fold(
|
|
||||||
(curr_value, vec![]),
|
|
||||||
|(mut amount, mut utxos), utxo| {
|
|
||||||
if amount >= target_amount {
|
|
||||||
(amount, utxos)
|
|
||||||
} else {
|
|
||||||
amount += utxo.effective_value;
|
|
||||||
utxos.push(utxo);
|
|
||||||
(amount, utxos)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
// remaining_amount can't be negative as that would mean the
|
|
||||||
// selection wasn't successful
|
|
||||||
// target_amount = amount_needed + (fee_amount - vin_fees)
|
|
||||||
let remaining_amount = (selected_utxos.0 - target_amount) as u64;
|
|
||||||
|
|
||||||
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
|
||||||
|
|
||||||
BranchAndBoundCoinSelection::calculate_cs_result(selected_utxos.1, required_utxos, excess)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn calculate_cs_result(
|
fn calculate_cs_result(
|
||||||
mut selected_utxos: Vec<OutputGroup>,
|
mut selected_utxos: Vec<OutputGroup>,
|
||||||
mut required_utxos: Vec<OutputGroup>,
|
mut required_utxos: Vec<OutputGroup>,
|
||||||
@ -717,6 +675,58 @@ impl BranchAndBoundCoinSelection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pull UTXOs at random until we have enough to meet the target
|
||||||
|
pub(crate) fn single_random_draw(
|
||||||
|
required_utxos: Vec<WeightedUtxo>,
|
||||||
|
optional_utxos: Vec<WeightedUtxo>,
|
||||||
|
target_amount: u64,
|
||||||
|
drain_script: &Script,
|
||||||
|
fee_rate: FeeRate,
|
||||||
|
rng: &mut impl RngCore,
|
||||||
|
) -> CoinSelectionResult {
|
||||||
|
let target_amount = target_amount
|
||||||
|
.try_into()
|
||||||
|
.expect("Bitcoin amount to fit into i64");
|
||||||
|
|
||||||
|
let required_utxos: Vec<OutputGroup> = required_utxos
|
||||||
|
.into_iter()
|
||||||
|
.map(|u| OutputGroup::new(u, fee_rate))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let mut optional_utxos: Vec<OutputGroup> = optional_utxos
|
||||||
|
.into_iter()
|
||||||
|
.map(|u| OutputGroup::new(u, fee_rate))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let curr_value = required_utxos
|
||||||
|
.iter()
|
||||||
|
.fold(0, |acc, x| acc + x.effective_value);
|
||||||
|
|
||||||
|
shuffle_slice(&mut optional_utxos, rng);
|
||||||
|
|
||||||
|
let selected_utxos =
|
||||||
|
optional_utxos
|
||||||
|
.into_iter()
|
||||||
|
.fold((curr_value, vec![]), |(mut amount, mut utxos), utxo| {
|
||||||
|
if amount >= target_amount {
|
||||||
|
(amount, utxos)
|
||||||
|
} else {
|
||||||
|
amount += utxo.effective_value;
|
||||||
|
utxos.push(utxo);
|
||||||
|
(amount, utxos)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// remaining_amount can't be negative as that would mean the
|
||||||
|
// selection wasn't successful
|
||||||
|
// target_amount = amount_needed + (fee_amount - vin_fees)
|
||||||
|
let remaining_amount = (selected_utxos.0 - target_amount) as u64;
|
||||||
|
|
||||||
|
let excess = decide_change(remaining_amount, fee_rate, drain_script);
|
||||||
|
|
||||||
|
BranchAndBoundCoinSelection::calculate_cs_result(selected_utxos.1, required_utxos, excess)
|
||||||
|
}
|
||||||
|
|
||||||
/// Remove duplicate UTXOs.
|
/// Remove duplicate UTXOs.
|
||||||
///
|
///
|
||||||
/// If a UTXO appears in both `required` and `optional`, the appearance in `required` is kept.
|
/// If a UTXO appears in both `required` and `optional`, the appearance in `required` is kept.
|
||||||
@ -740,6 +750,7 @@ where
|
|||||||
mod test {
|
mod test {
|
||||||
use assert_matches::assert_matches;
|
use assert_matches::assert_matches;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
use rand::rngs::StdRng;
|
||||||
|
|
||||||
use bdk_chain::ConfirmationTime;
|
use bdk_chain::ConfirmationTime;
|
||||||
use bitcoin::{Amount, ScriptBuf, TxIn, TxOut};
|
use bitcoin::{Amount, ScriptBuf, TxIn, TxOut};
|
||||||
@ -748,8 +759,7 @@ mod test {
|
|||||||
use crate::types::*;
|
use crate::types::*;
|
||||||
use crate::wallet::coin_selection::filter_duplicates;
|
use crate::wallet::coin_selection::filter_duplicates;
|
||||||
|
|
||||||
use rand::rngs::StdRng;
|
use rand::prelude::SliceRandom;
|
||||||
use rand::seq::SliceRandom;
|
|
||||||
use rand::{Rng, RngCore, SeedableRng};
|
use rand::{Rng, RngCore, SeedableRng};
|
||||||
|
|
||||||
// signature len (1WU) + signature and sighash (72WU)
|
// signature len (1WU) + signature and sighash (72WU)
|
||||||
@ -1090,13 +1100,12 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "SRD fn was moved out of BnB"]
|
||||||
fn test_bnb_coin_selection_success() {
|
fn test_bnb_coin_selection_success() {
|
||||||
// In this case bnb won't find a suitable match and single random draw will
|
// In this case bnb won't find a suitable match and single random draw will
|
||||||
// select three outputs
|
// select three outputs
|
||||||
let utxos = generate_same_value_utxos(100_000, 20);
|
let utxos = generate_same_value_utxos(100_000, 20);
|
||||||
|
|
||||||
let drain_script = ScriptBuf::default();
|
let drain_script = ScriptBuf::default();
|
||||||
|
|
||||||
let target_amount = 250_000 + FEE_AMOUNT;
|
let target_amount = 250_000 + FEE_AMOUNT;
|
||||||
|
|
||||||
let result = BranchAndBoundCoinSelection::default()
|
let result = BranchAndBoundCoinSelection::default()
|
||||||
@ -1136,6 +1145,7 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore = "no exact match for bnb, previously fell back to SRD"]
|
||||||
fn test_bnb_coin_selection_optional_are_enough() {
|
fn test_bnb_coin_selection_optional_are_enough() {
|
||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
let drain_script = ScriptBuf::default();
|
let drain_script = ScriptBuf::default();
|
||||||
@ -1156,6 +1166,26 @@ mod test {
|
|||||||
assert_eq!(result.fee_amount, 136);
|
assert_eq!(result.fee_amount, 136);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_single_random_draw_function_success() {
|
||||||
|
let seed = [0; 32];
|
||||||
|
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||||
|
let mut utxos = generate_random_utxos(&mut rng, 300);
|
||||||
|
let target_amount = sum_random_utxos(&mut rng, &mut utxos) + FEE_AMOUNT;
|
||||||
|
let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
|
||||||
|
let drain_script = ScriptBuf::default();
|
||||||
|
let result = single_random_draw(
|
||||||
|
vec![],
|
||||||
|
utxos,
|
||||||
|
target_amount,
|
||||||
|
&drain_script,
|
||||||
|
fee_rate,
|
||||||
|
&mut rng,
|
||||||
|
);
|
||||||
|
assert!(result.selected_amount() > target_amount);
|
||||||
|
assert_eq!(result.fee_amount, (result.selected.len() * 68) as u64);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
#[ignore]
|
||||||
fn test_bnb_coin_selection_required_not_enough() {
|
fn test_bnb_coin_selection_required_not_enough() {
|
||||||
@ -1410,34 +1440,6 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_single_random_draw_function_success() {
|
|
||||||
let seed = [0; 32];
|
|
||||||
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
|
||||||
let mut utxos = generate_random_utxos(&mut rng, 300);
|
|
||||||
let target_amount = sum_random_utxos(&mut rng, &mut utxos) + FEE_AMOUNT;
|
|
||||||
|
|
||||||
let fee_rate = FeeRate::from_sat_per_vb_unchecked(1);
|
|
||||||
let utxos: Vec<OutputGroup> = utxos
|
|
||||||
.into_iter()
|
|
||||||
.map(|u| OutputGroup::new(u, fee_rate))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let drain_script = ScriptBuf::default();
|
|
||||||
|
|
||||||
let result = BranchAndBoundCoinSelection::default().single_random_draw(
|
|
||||||
vec![],
|
|
||||||
utxos,
|
|
||||||
0,
|
|
||||||
target_amount as i64,
|
|
||||||
&drain_script,
|
|
||||||
fee_rate,
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(result.selected_amount() > target_amount);
|
|
||||||
assert_eq!(result.fee_amount, (result.selected.len() * 68) as u64);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bnb_exclude_negative_effective_value() {
|
fn test_bnb_exclude_negative_effective_value() {
|
||||||
let utxos = get_test_utxos();
|
let utxos = get_test_utxos();
|
||||||
|
@ -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`]
|
/// Error returned from [`TxBuilder::finish_with_aux_rand`]
|
||||||
///
|
///
|
||||||
/// [`TxBuilder::finish`]: crate::wallet::tx_builder::TxBuilder::finish
|
/// [`TxBuilder::finish_with_aux_rand`]: crate::wallet::tx_builder::TxBuilder::finish_with_aux_rand
|
||||||
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),
|
||||||
|
@ -41,6 +41,8 @@ use bitcoin::{consensus::encode::serialize, transaction, BlockHash, Psbt};
|
|||||||
use bitcoin::{constants::genesis_block, Amount};
|
use bitcoin::{constants::genesis_block, Amount};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
use descriptor::error::Error as DescriptorError;
|
use descriptor::error::Error as DescriptorError;
|
||||||
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
|
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
|
||||||
|
|
||||||
@ -1210,6 +1212,7 @@ 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();
|
||||||
@ -1217,7 +1220,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()?
|
/// builder.finish_with_aux_rand(&mut thread_rng())?
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// // sign and broadcast ...
|
/// // sign and broadcast ...
|
||||||
@ -1237,6 +1240,7 @@ impl Wallet {
|
|||||||
&mut self,
|
&mut self,
|
||||||
coin_selection: Cs,
|
coin_selection: Cs,
|
||||||
params: TxParams,
|
params: TxParams,
|
||||||
|
rng: &mut impl RngCore,
|
||||||
) -> Result<Psbt, CreateTxError> {
|
) -> Result<Psbt, CreateTxError> {
|
||||||
let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
|
let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
|
||||||
let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
|
let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
|
||||||
@ -1463,13 +1467,31 @@ impl Wallet {
|
|||||||
let (required_utxos, optional_utxos) =
|
let (required_utxos, optional_utxos) =
|
||||||
coin_selection::filter_duplicates(required_utxos, optional_utxos);
|
coin_selection::filter_duplicates(required_utxos, optional_utxos);
|
||||||
|
|
||||||
let coin_selection = coin_selection.coin_select(
|
let coin_selection = match coin_selection.coin_select(
|
||||||
required_utxos,
|
required_utxos.clone(),
|
||||||
optional_utxos,
|
optional_utxos.clone(),
|
||||||
fee_rate,
|
fee_rate,
|
||||||
outgoing.to_sat() + fee_amount,
|
outgoing.to_sat() + fee_amount,
|
||||||
&drain_script,
|
&drain_script,
|
||||||
)?;
|
) {
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(e) => match e {
|
||||||
|
coin_selection::Error::InsufficientFunds { .. } => {
|
||||||
|
return Err(CreateTxError::CoinSelection(e));
|
||||||
|
}
|
||||||
|
coin_selection::Error::BnBNoExactMatch
|
||||||
|
| coin_selection::Error::BnBTotalTriesExceeded => {
|
||||||
|
coin_selection::single_random_draw(
|
||||||
|
required_utxos,
|
||||||
|
optional_utxos,
|
||||||
|
outgoing.to_sat() + fee_amount,
|
||||||
|
&drain_script,
|
||||||
|
fee_rate,
|
||||||
|
rng,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
fee_amount += coin_selection.fee_amount;
|
fee_amount += coin_selection.fee_amount;
|
||||||
let excess = &coin_selection.excess;
|
let excess = &coin_selection.excess;
|
||||||
|
|
||||||
@ -1533,7 +1555,7 @@ impl Wallet {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// sort input/outputs according to the chosen algorithm
|
// sort input/outputs according to the chosen algorithm
|
||||||
params.ordering.sort_tx(&mut tx);
|
params.ordering.sort_tx_with_aux_rand(&mut tx, rng);
|
||||||
|
|
||||||
let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
|
let psbt = self.complete_transaction(tx, coin_selection.selected, params)?;
|
||||||
Ok(psbt)
|
Ok(psbt)
|
||||||
@ -1555,6 +1577,7 @@ 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();
|
||||||
@ -1563,7 +1586,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()?
|
/// builder.finish_with_aux_rand(&mut thread_rng())?
|
||||||
/// };
|
/// };
|
||||||
/// 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");
|
||||||
@ -1572,7 +1595,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()?
|
/// builder.finish_with_aux_rand(&mut thread_rng())?
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
|
/// let _ = wallet.sign(&mut psbt, SignOptions::default())?;
|
||||||
@ -1732,13 +1755,14 @@ 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()?
|
/// builder.finish_with_aux_rand(&mut thread_rng())?
|
||||||
/// };
|
/// };
|
||||||
/// 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");
|
||||||
@ -1783,7 +1807,6 @@ 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)
|
||||||
|
@ -607,7 +607,7 @@ fn sign_psbt_schnorr(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let msg = &Message::from(hash);
|
let msg = &Message::from(hash);
|
||||||
let signature = secp.sign_schnorr(msg, &keypair);
|
let signature = secp.sign_schnorr_no_aux_rand(msg, &keypair);
|
||||||
secp.verify_schnorr(&signature, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
|
secp.verify_schnorr(&signature, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
|
||||||
.expect("invalid or corrupted schnorr signature");
|
.expect("invalid or corrupted schnorr signature");
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
//! # 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
|
||||||
@ -34,7 +35,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()?;
|
//! let psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?;
|
||||||
//! # Ok::<(), anyhow::Error>(())
|
//! # Ok::<(), anyhow::Error>(())
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
@ -45,8 +46,10 @@ use core::fmt;
|
|||||||
use bitcoin::psbt::{self, Psbt};
|
use bitcoin::psbt::{self, Psbt};
|
||||||
use bitcoin::script::PushBytes;
|
use bitcoin::script::PushBytes;
|
||||||
use bitcoin::{absolute, Amount, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid};
|
use bitcoin::{absolute, Amount, FeeRate, OutPoint, ScriptBuf, Sequence, Transaction, Txid};
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
use super::coin_selection::CoinSelectionAlgorithm;
|
use super::coin_selection::CoinSelectionAlgorithm;
|
||||||
|
use super::utils::shuffle_slice;
|
||||||
use super::{CreateTxError, Wallet};
|
use super::{CreateTxError, Wallet};
|
||||||
use crate::collections::{BTreeMap, HashSet};
|
use crate::collections::{BTreeMap, HashSet};
|
||||||
use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
|
use crate::{KeychainKind, LocalOutput, Utxo, WeightedUtxo};
|
||||||
@ -54,7 +57,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`] to consume the builder and
|
/// assigning it, you set options on it until finally calling [`finish_with_aux_rand`] 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
|
||||||
@ -68,6 +71,7 @@ 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();
|
||||||
@ -78,7 +82,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()?
|
/// builder.finish_with_aux_rand(&mut thread_rng())?
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// // non-chaining
|
/// // non-chaining
|
||||||
@ -88,7 +92,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()?
|
/// builder.finish_with_aux_rand(&mut thread_rng())?
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// assert_eq!(psbt1.unsigned_tx.output[..2], psbt2.unsigned_tx.output[..2]);
|
/// assert_eq!(psbt1.unsigned_tx.output[..2], psbt2.unsigned_tx.output[..2]);
|
||||||
@ -102,7 +106,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`]: Self::finish
|
/// [`finish_with_aux_rand`]: Self::finish_with_aux_rand
|
||||||
/// [`coin_selection`]: Self::coin_selection
|
/// [`coin_selection`]: Self::coin_selection
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TxBuilder<'a, Cs> {
|
pub struct TxBuilder<'a, Cs> {
|
||||||
@ -354,11 +358,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`]
|
/// method must have `non_witness_utxo` set otherwise you will get an error when [`finish_with_aux_rand`]
|
||||||
/// is called.
|
/// is called.
|
||||||
///
|
///
|
||||||
/// [`only_witness_utxo`]: Self::only_witness_utxo
|
/// [`only_witness_utxo`]: Self::only_witness_utxo
|
||||||
/// [`finish`]: Self::finish
|
/// [`finish_with_aux_rand`]: Self::finish_with_aux_rand
|
||||||
/// [`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,
|
||||||
@ -639,6 +643,7 @@ 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()
|
||||||
@ -653,7 +658,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()?;
|
/// let psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?;
|
||||||
/// # Ok::<(), anyhow::Error>(())
|
/// # Ok::<(), anyhow::Error>(())
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@ -675,10 +680,10 @@ impl<'a, Cs: CoinSelectionAlgorithm> TxBuilder<'a, Cs> {
|
|||||||
///
|
///
|
||||||
/// **WARNING**: To avoid change address reuse you must persist the changes resulting from one
|
/// **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`].
|
/// or more calls to this method before closing the wallet. See [`Wallet::reveal_next_address`].
|
||||||
pub fn finish(self) -> Result<Psbt, CreateTxError> {
|
pub fn finish_with_aux_rand(self, rng: &mut impl RngCore) -> Result<Psbt, CreateTxError> {
|
||||||
self.wallet
|
self.wallet
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.create_tx(self.coin_selection, self.params)
|
.create_tx(self.coin_selection, self.params, rng)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -758,14 +763,12 @@ pub enum TxOrdering {
|
|||||||
|
|
||||||
impl TxOrdering {
|
impl TxOrdering {
|
||||||
/// Sort transaction inputs and outputs by [`TxOrdering`] variant
|
/// Sort transaction inputs and outputs by [`TxOrdering`] variant
|
||||||
pub fn sort_tx(&self, tx: &mut Transaction) {
|
pub fn sort_tx_with_aux_rand(self, tx: &mut Transaction, rng: &mut impl RngCore) {
|
||||||
match self {
|
match self {
|
||||||
TxOrdering::Untouched => {}
|
TxOrdering::Untouched => {}
|
||||||
TxOrdering::Shuffle => {
|
TxOrdering::Shuffle => {
|
||||||
use rand::seq::SliceRandom;
|
shuffle_slice(&mut tx.input, rng);
|
||||||
let mut rng = rand::thread_rng();
|
shuffle_slice(&mut tx.output, rng);
|
||||||
tx.input.shuffle(&mut rng);
|
|
||||||
tx.output.shuffle(&mut rng);
|
|
||||||
}
|
}
|
||||||
TxOrdering::Bip69Lexicographic => {
|
TxOrdering::Bip69Lexicographic => {
|
||||||
tx.input.sort_unstable_by_key(|txin| {
|
tx.input.sort_unstable_by_key(|txin| {
|
||||||
@ -849,20 +852,16 @@ 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]
|
|
||||||
fn test_output_ordering_default_shuffle() {
|
|
||||||
assert_eq!(TxOrdering::default(), TxOrdering::Shuffle);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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(&mut tx);
|
TxOrdering::Untouched.sort_tx_with_aux_rand(&mut tx, &mut rng);
|
||||||
|
|
||||||
assert_eq!(original_tx, tx);
|
assert_eq!(original_tx, tx);
|
||||||
}
|
}
|
||||||
@ -871,10 +870,11 @@ 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(&mut tx);
|
TxOrdering::Shuffle.sort_tx_with_aux_rand(&mut tx, &mut rng);
|
||||||
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 +882,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(&mut tx);
|
TxOrdering::Shuffle.sort_tx_with_aux_rand(&mut tx, &mut rng);
|
||||||
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,8 +894,9 @@ 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(&mut tx);
|
TxOrdering::Bip69Lexicographic.sort_tx_with_aux_rand(&mut tx, &mut rng);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
tx.input[0].previous_output,
|
tx.input[0].previous_output,
|
||||||
|
@ -14,6 +14,8 @@ use bitcoin::{absolute, relative, Script, Sequence};
|
|||||||
|
|
||||||
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
|
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
|
||||||
|
|
||||||
|
use rand_core::RngCore;
|
||||||
|
|
||||||
/// Trait to check if a value is below the dust limit.
|
/// Trait to check if a value is below the dust limit.
|
||||||
/// We are performing dust value calculation for a given script public key using rust-bitcoin to
|
/// We are performing dust value calculation for a given script public key using rust-bitcoin to
|
||||||
/// keep it compatible with network dust rate
|
/// keep it compatible with network dust rate
|
||||||
@ -110,6 +112,19 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The Knuth shuffling algorithm based on the original [Fisher-Yates method](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)
|
||||||
|
pub(crate) fn shuffle_slice<T>(list: &mut [T], rng: &mut impl RngCore) {
|
||||||
|
if list.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut current_index = list.len() - 1;
|
||||||
|
while current_index > 0 {
|
||||||
|
let random_index = rng.next_u32() as usize % (current_index + 1);
|
||||||
|
list.swap(current_index, random_index);
|
||||||
|
current_index -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) type SecpCtx = Secp256k1<All>;
|
pub(crate) type SecpCtx = Secp256k1<All>;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -118,9 +133,11 @@ mod test {
|
|||||||
// otherwise it's time-based
|
// otherwise it's time-based
|
||||||
pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
|
pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
|
||||||
|
|
||||||
use super::{check_nsequence_rbf, IsDust};
|
use super::{check_nsequence_rbf, shuffle_slice, IsDust};
|
||||||
use crate::bitcoin::{Address, Network, Sequence};
|
use crate::bitcoin::{Address, Network, Sequence};
|
||||||
|
use alloc::vec::Vec;
|
||||||
use core::str::FromStr;
|
use core::str::FromStr;
|
||||||
|
use rand::{rngs::StdRng, thread_rng, SeedableRng};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_is_dust() {
|
fn test_is_dust() {
|
||||||
@ -182,4 +199,44 @@ mod test {
|
|||||||
);
|
);
|
||||||
assert!(result);
|
assert!(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shuffle_slice_empty_vec() {
|
||||||
|
let mut test: Vec<u8> = vec![];
|
||||||
|
shuffle_slice(&mut test, &mut thread_rng());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shuffle_slice_single_vec() {
|
||||||
|
let mut test: Vec<u8> = vec![0];
|
||||||
|
shuffle_slice(&mut test, &mut thread_rng());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shuffle_slice_duple_vec() {
|
||||||
|
let seed = [0; 32];
|
||||||
|
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||||
|
let mut test: Vec<u8> = vec![0, 1];
|
||||||
|
shuffle_slice(&mut test, &mut rng);
|
||||||
|
assert_eq!(test, &[0, 1]);
|
||||||
|
let seed = [6; 32];
|
||||||
|
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||||
|
let mut test: Vec<u8> = vec![0, 1];
|
||||||
|
shuffle_slice(&mut test, &mut rng);
|
||||||
|
assert_eq!(test, &[1, 0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_shuffle_slice_multi_vec() {
|
||||||
|
let seed = [0; 32];
|
||||||
|
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||||
|
let mut test: Vec<u8> = vec![0, 1, 2, 4, 5];
|
||||||
|
shuffle_slice(&mut test, &mut rng);
|
||||||
|
assert_eq!(test, &[2, 1, 0, 4, 5]);
|
||||||
|
let seed = [25; 32];
|
||||||
|
let mut rng: StdRng = SeedableRng::from_seed(seed);
|
||||||
|
let mut test: Vec<u8> = vec![0, 1, 2, 4, 5];
|
||||||
|
shuffle_slice(&mut test, &mut rng);
|
||||||
|
assert_eq!(test, &[0, 4, 1, 2, 5]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
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::*;
|
||||||
|
|
||||||
@ -15,7 +16,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().unwrap();
|
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).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,
|
||||||
@ -32,7 +33,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().unwrap();
|
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).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,
|
||||||
@ -48,7 +49,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().unwrap();
|
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).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,
|
||||||
@ -64,7 +65,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().unwrap();
|
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).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());
|
||||||
@ -86,7 +87,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().unwrap();
|
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).unwrap();
|
||||||
let fee_amount = psbt.fee_amount();
|
let fee_amount = psbt.fee_amount();
|
||||||
assert!(fee_amount.is_some());
|
assert!(fee_amount.is_some());
|
||||||
|
|
||||||
@ -111,7 +112,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().unwrap();
|
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).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();
|
||||||
@ -135,7 +136,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().unwrap();
|
let mut wpkh_psbt = builder.finish_with_aux_rand(&mut thread_rng()).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;
|
||||||
@ -149,7 +150,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().unwrap();
|
let mut pkh_psbt = builder.finish_with_aux_rand(&mut thread_rng()).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());
|
||||||
@ -178,7 +179,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().unwrap();
|
let mut psbt = builder.finish_with_aux_rand(&mut thread_rng()).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
@ -8,3 +8,4 @@ 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"
|
||||||
|
@ -14,6 +14,7 @@ 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");
|
||||||
@ -96,7 +97,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()?;
|
let mut psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?;
|
||||||
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
|
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
|
||||||
assert!(finalized);
|
assert!(finalized);
|
||||||
|
|
||||||
|
@ -11,3 +11,4 @@ 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"
|
||||||
|
@ -5,6 +5,7 @@ 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};
|
||||||
|
|
||||||
@ -103,7 +104,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()?;
|
let mut psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?;
|
||||||
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
|
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
|
||||||
assert!(finalized);
|
assert!(finalized);
|
||||||
|
|
||||||
|
@ -11,3 +11,4 @@ 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"
|
||||||
|
@ -11,6 +11,7 @@ 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");
|
||||||
@ -80,7 +81,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()?;
|
let mut psbt = tx_builder.finish_with_aux_rand(&mut thread_rng())?;
|
||||||
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
|
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
|
||||||
assert!(finalized);
|
assert!(finalized);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user