From ca682819b396c44c9139086b1537d32b212499a1 Mon Sep 17 00:00:00 2001 From: James Taylor <54148103+jatayl@users.noreply.github.com> Date: Sun, 19 Dec 2021 02:55:24 -0500 Subject: [PATCH 1/4] using dust value from rust-bitcoin --- src/wallet/mod.rs | 12 ++++++------ src/wallet/utils.rs | 10 ++++------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/src/wallet/mod.rs b/src/wallet/mod.rs index 8062ea9d..bf7993d4 100644 --- a/src/wallet/mod.rs +++ b/src/wallet/mod.rs @@ -53,7 +53,7 @@ use address_validator::AddressValidator; use coin_selection::DefaultCoinSelectionAlgorithm; use signer::{SignOptions, Signer, SignerOrdering, SignersContainer}; use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams}; -use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx, DUST_LIMIT_SATOSHI}; +use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx}; use crate::blockchain::{Blockchain, Progress}; use crate::database::memory::MemoryDatabase; @@ -601,7 +601,7 @@ where let recipients = params.recipients.iter().map(|(r, v)| (r, *v)); for (index, (script_pubkey, value)) in recipients.enumerate() { - if value.is_dust() && !script_pubkey.is_provably_unspendable() { + if value.is_dust(script_pubkey) && !script_pubkey.is_provably_unspendable() { return Err(Error::OutputBelowDustLimit(index)); } @@ -677,9 +677,9 @@ where if tx.output.is_empty() { if params.drain_to.is_some() { - if drain_val.is_dust() { + if drain_val.is_dust(&drain_output.script_pubkey) { return Err(Error::InsufficientFunds { - needed: DUST_LIMIT_SATOSHI, + needed: drain_output.script_pubkey.dust_value().as_sat(), available: drain_val, }); } @@ -688,7 +688,7 @@ where } } - if drain_val.is_dust() { + if drain_val.is_dust(&drain_output.script_pubkey) { fee_amount += drain_val; } else { drain_output.value = drain_val; @@ -3424,7 +3424,7 @@ pub(crate) mod test { .unwrap(); let mut builder = wallet.build_fee_bump(txid).unwrap(); - builder.fee_rate(FeeRate::from_sat_per_vb(140.0)); + builder.fee_rate(FeeRate::from_sat_per_vb(141.0)); let (psbt, details) = builder.finish().unwrap(); assert_eq!( diff --git a/src/wallet/utils.rs b/src/wallet/utils.rs index 2b19eb80..50163dce 100644 --- a/src/wallet/utils.rs +++ b/src/wallet/utils.rs @@ -9,13 +9,11 @@ // You may not use this file except in accordance with one or both of these // licenses. +use bitcoin::blockdata::script::Script; use bitcoin::secp256k1::{All, Secp256k1}; use miniscript::{MiniscriptKey, Satisfier, ToPublicKey}; -// De-facto standard "dust limit" (even though it should change based on the output type) -pub const DUST_LIMIT_SATOSHI: u64 = 546; - // MSB of the nSequence. If set there's no consensus-constraint, so it must be disabled when // spending using CSV in order to enforce CSV rules pub(crate) const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1 << 31; @@ -34,12 +32,12 @@ pub(crate) const BLOCKS_TIMELOCK_THRESHOLD: u32 = 500000000; // encourage the usage of this trait. pub trait IsDust { /// Check whether or not a value is below dust limit - fn is_dust(&self) -> bool; + fn is_dust(&self, script: &Script) -> bool; } impl IsDust for u64 { - fn is_dust(&self) -> bool { - *self <= DUST_LIMIT_SATOSHI + fn is_dust(&self, script: &Script) -> bool { + *self <= script.dust_value().as_sat() } } From bf5994b14ade1fc0c37d3e3cda6946c26ceea631 Mon Sep 17 00:00:00 2001 From: James Taylor <54148103+jatayl@users.noreply.github.com> Date: Sun, 19 Dec 2021 18:37:05 -0500 Subject: [PATCH 2/4] fixed fee in test, removed unnecessary comment --- src/testutils/blockchain_tests.rs | 2 +- src/wallet/utils.rs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/testutils/blockchain_tests.rs b/src/testutils/blockchain_tests.rs index d0893610..4229f5df 100644 --- a/src/testutils/blockchain_tests.rs +++ b/src/testutils/blockchain_tests.rs @@ -843,7 +843,7 @@ macro_rules! bdk_blockchain_tests { assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect received after send"); let mut builder = wallet.build_fee_bump(details.txid).unwrap(); - builder.fee_rate(FeeRate::from_sat_per_vb(5.0)); + builder.fee_rate(FeeRate::from_sat_per_vb(5.1)); let (mut new_psbt, new_details) = builder.finish().unwrap(); let finalized = wallet.sign(&mut new_psbt, Default::default()).unwrap(); assert!(finalized, "Cannot finalize transaction"); diff --git a/src/wallet/utils.rs b/src/wallet/utils.rs index 50163dce..e9475232 100644 --- a/src/wallet/utils.rs +++ b/src/wallet/utils.rs @@ -28,8 +28,7 @@ pub(crate) const BLOCKS_TIMELOCK_THRESHOLD: u32 = 500000000; /// Trait to check if a value is below the dust limit // we implement this trait to make sure we don't mess up the comparison with off-by-one like a < -// instead of a <= etc. The constant value for the dust limit is not public on purpose, to -// encourage the usage of this trait. +// instead of a <= etc. pub trait IsDust { /// Check whether or not a value is below dust limit fn is_dust(&self, script: &Script) -> bool; From a0c140bb29ab37103e4a24d296ac6fcea91da69e Mon Sep 17 00:00:00 2001 From: James Taylor <54148103+jatayl@users.noreply.github.com> Date: Wed, 22 Dec 2021 01:50:17 -0500 Subject: [PATCH 3/4] add doc comment for IsDust trait --- src/wallet/utils.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/wallet/utils.rs b/src/wallet/utils.rs index e9475232..8c7ee0ac 100644 --- a/src/wallet/utils.rs +++ b/src/wallet/utils.rs @@ -26,7 +26,9 @@ pub(crate) const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000FFFF; // Threshold for nLockTime to be considered a block-height-based timelock rather than time-based pub(crate) const BLOCKS_TIMELOCK_THRESHOLD: u32 = 500000000; -/// 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 +/// keep it compatible with network dust rate // we implement this trait to make sure we don't mess up the comparison with off-by-one like a < // instead of a <= etc. pub trait IsDust { From 5ac51dfe74c51c83fe270570d7ffc50888cf554f Mon Sep 17 00:00:00 2001 From: James Taylor <54148103+jatayl@users.noreply.github.com> Date: Tue, 11 Jan 2022 18:21:35 -0500 Subject: [PATCH 4/4] fix and test is_dust --- src/wallet/utils.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/wallet/utils.rs b/src/wallet/utils.rs index 8c7ee0ac..cee72e40 100644 --- a/src/wallet/utils.rs +++ b/src/wallet/utils.rs @@ -38,7 +38,7 @@ pub trait IsDust { impl IsDust for u64 { fn is_dust(&self, script: &Script) -> bool { - *self <= script.dust_value().as_sat() + *self < script.dust_value().as_sat() } } @@ -140,10 +140,29 @@ pub(crate) type SecpCtx = Secp256k1; #[cfg(test)] mod test { use super::{ - check_nlocktime, check_nsequence_rbf, BLOCKS_TIMELOCK_THRESHOLD, + check_nlocktime, check_nsequence_rbf, IsDust, BLOCKS_TIMELOCK_THRESHOLD, SEQUENCE_LOCKTIME_TYPE_FLAG, }; + use crate::bitcoin::Address; use crate::types::FeeRate; + use std::str::FromStr; + + #[test] + fn test_is_dust() { + let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe") + .unwrap() + .script_pubkey(); + assert!(script_p2pkh.is_p2pkh()); + assert!(545.is_dust(&script_p2pkh)); + assert!(!546.is_dust(&script_p2pkh)); + + let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8") + .unwrap() + .script_pubkey(); + assert!(script_p2wpkh.is_v0_p2wpkh()); + assert!(293.is_dust(&script_p2wpkh)); + assert!(!294.is_dust(&script_p2wpkh)); + } #[test] fn test_fee_from_btc_per_kb() {