]> Untitled Git - bdk/commitdiff
Allow creating transactions with dust outputs
authorLiam <liam@scalzulli.com>
Fri, 29 Jul 2022 19:30:43 +0000 (15:30 -0400)
committerLiam <liam@scalzulli.com>
Tue, 30 Aug 2022 15:25:34 +0000 (11:25 -0400)
Add TxBuilder::allow_dust() that skips checking the dust limit

CHANGELOG.md
src/wallet/mod.rs
src/wallet/tx_builder.rs

index 2859b7ee528f567fbb55a2932b1ad8c0b013083b..98cbd0dae26097930ac69580a9cd68764a5f482e 100644 (file)
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Add capacity to create FeeRate from sats/kvbytes and sats/kwu.
 - Rename `as_sat_vb` to `as_sat_per_vb`. Move all `FeeRate` test to `types.rs`.
 - Add custom Harware Wallet Signer `HwiSigner` in `src/wallet/harwaresigner/` module.
+- Add `allow_dust` method on `TxBuilder`.
 
 ## [v0.21.0] - [v0.20.0]
 
@@ -22,14 +23,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - New `RpcBlockchain` implementation with various fixes.
 - Return balance in separate categories, namely `confirmed`, `trusted_pending`, `untrusted_pending` & `immature`.
 
-
 ## [v0.20.0] - [v0.19.0]
 
 - New MSRV set to `1.56.1`
 - Fee sniping discouraging through nLockTime - if the user specifies a `current_height`, we use that as a nlocktime, otherwise we use the last sync height (or 0 if we never synced)
 - Fix hang when `ElectrumBlockchainConfig::stop_gap` is zero.
 - Set coin type in BIP44, BIP49, and BIP84 templates
-- Get block hash given a block height - A `get_block_hash` method is now defined on the `GetBlockHash` trait and implemented on every blockchain backend. This method expects a block height and returns the corresponding block hash. 
+- Get block hash given a block height - A `get_block_hash` method is now defined on the `GetBlockHash` trait and implemented on every blockchain backend. This method expects a block height and returns the corresponding block hash.
 - Add `remove_partial_sigs` and `try_finalize` to `SignOptions`
 - Deprecate `AddressValidator`
 - Fix Electrum wallet sync potentially causing address index decrement - compare proposed index and current index before applying batch operations during sync.
@@ -41,7 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Unpinned tokio to `1`
 - Add traits to reuse `Blockchain`s across multiple wallets (`BlockchainFactory` and `StatelessBlockchain`).
 - Upgrade to rust-bitcoin `0.28`
-- If using the `sqlite-db` feature all cached wallet data is deleted due to a possible UTXO inconsistency, a wallet.sync will recreate it  
+- If using the `sqlite-db` feature all cached wallet data is deleted due to a possible UTXO inconsistency, a wallet.sync will recreate it
 - Update `PkOrF` in the policy module to become an enum
 - Add experimental support for Taproot, including:
   - Support for `tr()` descriptors with complex tapscript trees
@@ -58,7 +58,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Add `keychain: KeychainKind` to `wallet::AddressInfo`.
 - Improve key generation traits
 - Rename `WalletExport` to `FullyNodedExport`, deprecate the former.
-- Bump `miniscript` dependency version to `^6.1`. 
+- Bump `miniscript` dependency version to `^6.1`.
 
 ## [v0.17.0] - [v0.16.1]
 
index 5e413a6340eced769390ece864ad93c70b3f0fc4..c01b8b17723cabf9a334148f3d7a3109ba407ed9 100644 (file)
@@ -790,7 +790,10 @@ 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) && !script_pubkey.is_provably_unspendable() {
+            if !params.allow_dust
+                && value.is_dust(script_pubkey)
+                && !script_pubkey.is_provably_unspendable()
+            {
                 return Err(Error::OutputBelowDustLimit(index));
             }
 
@@ -5408,6 +5411,30 @@ pub(crate) mod test {
         builder.finish().unwrap();
     }
 
+    #[test]
+    fn test_allow_dust_limit() {
+        let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
+
+        let addr = wallet.get_address(New).unwrap();
+
+        let mut builder = wallet.build_tx();
+
+        builder.add_recipient(addr.script_pubkey(), 0);
+
+        assert!(matches!(
+            builder.finish().unwrap_err(),
+            Error::OutputBelowDustLimit(0)
+        ));
+
+        let mut builder = wallet.build_tx();
+
+        builder
+            .allow_dust(true)
+            .add_recipient(addr.script_pubkey(), 0);
+
+        assert!(builder.finish().is_ok());
+    }
+
     #[test]
     fn test_fee_rate_sign_no_grinding_high_r() {
         // Our goal is to obtain a transaction with a signature with high-R (71 bytes
index 9e93e551c57e9565e061b166f93ace0dd187fb47..c02ff3a27c05b031f848815aa95bd641a78b0aa7 100644 (file)
@@ -148,6 +148,7 @@ pub(crate) struct TxParams {
     pub(crate) include_output_redeem_witness_script: bool,
     pub(crate) bumping_fee: Option<PreviousFee>,
     pub(crate) current_height: Option<u32>,
+    pub(crate) allow_dust: bool,
 }
 
 #[derive(Clone, Copy, Debug)]
@@ -560,6 +561,14 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
         self.params.current_height = Some(height);
         self
     }
+
+    /// Set whether or not the dust limit is checked.
+    ///
+    /// **Note**: by avoiding a dust limit check you may end up with a transaction that is non-standard.
+    pub fn allow_dust(&mut self, allow_dust: bool) -> &mut Self {
+        self.params.allow_dust = allow_dust;
+        self
+    }
 }
 
 impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, D, Cs, CreateTx> {