]> Untitled Git - bdk/commitdiff
Incorporate RBF rules into utxo selection function
authorLLFourn <lloyd.fourn@gmail.com>
Fri, 23 Oct 2020 01:13:05 +0000 (12:13 +1100)
committerLLFourn <lloyd.fourn@gmail.com>
Fri, 23 Oct 2020 02:54:59 +0000 (13:54 +1100)
src/wallet/mod.rs
src/wallet/rbf.rs [deleted file]

index 25482eadfc76ff08bed6bf11ccfdd613eb7d6a70..845da0f0e2a05817fee33af31472579a870b2ad3 100644 (file)
@@ -45,7 +45,6 @@ use log::{debug, error, info, trace};
 pub mod address_validator;
 pub mod coin_selection;
 pub mod export;
-mod rbf;
 pub mod signer;
 pub mod time;
 pub mod tx_builder;
@@ -355,6 +354,7 @@ where
             &builder.utxos,
             builder.send_all,
             builder.manually_selected_only,
+            false, // we don't mind using unconfirmed outputs here, hopefully coin selection will sort this out?
         )?;
 
         let coin_selection::CoinSelectionResult {
@@ -604,18 +604,15 @@ where
             .cloned()
             .collect::<Vec<_>>();
 
-        let (must_use_utxos, may_use_utxos) = self.get_must_may_use_utxos(
+        let (mut must_use_utxos, may_use_utxos) = self.get_must_may_use_utxos(
             builder.change_policy,
             &builder.unspendable,
             &builder_extra_utxos[..],
             false, // when doing bump_fee `send_all` does not mean use all available utxos
             builder.manually_selected_only,
+            true, // we only want confirmed transactions for RBF
         )?;
 
-        let mut must_use_utxos =
-            rbf::filter_available(self.database.borrow().deref(), must_use_utxos.into_iter())?;
-        let may_use_utxos =
-            rbf::filter_available(self.database.borrow().deref(), may_use_utxos.into_iter())?;
         must_use_utxos.append(&mut original_utxos);
 
         let amount_needed = tx.output.iter().fold(0, |acc, out| acc + out.value);
@@ -995,6 +992,7 @@ where
         manually_selected: &[OutPoint],
         must_use_all_available: bool,
         manual_only: bool,
+        must_only_use_confirmed_tx: bool,
     ) -> Result<(Vec<(UTXO, usize)>, Vec<(UTXO, usize)>), Error> {
         //    must_spend <- manually selected utxos
         //    may_spend  <- all other available utxos
@@ -1022,8 +1020,31 @@ where
             return Ok((must_spend, vec![]));
         }
 
+        let satisfies_confirmed = match must_only_use_confirmed_tx {
+            true => {
+                let database = self.database.borrow_mut();
+                may_spend
+                    .iter()
+                    .map(|u| {
+                        database
+                            .get_tx(&u.0.outpoint.txid, true)
+                            .map(|tx| match tx {
+                                None => false,
+                                Some(tx) => tx.height.is_some(),
+                            })
+                    })
+                    .collect::<Result<Vec<_>, _>>()?
+            }
+            false => vec![true; may_spend.len()],
+        };
+
+        let mut i = 0;
         may_spend.retain(|u| {
-            change_policy.is_satisfied_by(&u.0) && !unspendable.contains(&u.0.outpoint)
+            let retain = change_policy.is_satisfied_by(&u.0)
+                && !unspendable.contains(&u.0.outpoint)
+                && satisfies_confirmed[i];
+            i += 1;
+            retain
         });
 
         if must_use_all_available {
diff --git a/src/wallet/rbf.rs b/src/wallet/rbf.rs
deleted file mode 100644 (file)
index 6080641..0000000
+++ /dev/null
@@ -1,134 +0,0 @@
-// Magical Bitcoin Library
-// Written in 2020 by
-//     Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020 Magical Bitcoin
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
-use crate::database::Database;
-use crate::error::Error;
-use crate::types::*;
-
-/// Filters unspent utxos
-pub(super) fn filter_available<I: Iterator<Item = (UTXO, usize)>, D: Database>(
-    database: &D,
-    iter: I,
-) -> Result<Vec<(UTXO, usize)>, Error> {
-    Ok(iter
-        .map(|(utxo, weight)| {
-            Ok(match database.get_tx(&utxo.outpoint.txid, true)? {
-                None => None,
-                Some(tx) if tx.height.is_none() => None,
-                Some(_) => Some((utxo, weight)),
-            })
-        })
-        .collect::<Result<Vec<_>, Error>>()?
-        .into_iter()
-        .filter_map(|x| x)
-        .collect())
-}
-
-#[cfg(test)]
-mod test {
-    use std::str::FromStr;
-
-    use bitcoin::{OutPoint, Transaction, TxIn, TxOut, Txid};
-
-    use super::*;
-    use crate::database::{BatchOperations, MemoryDatabase};
-
-    fn add_transaction(
-        database: &mut MemoryDatabase,
-        spend: Vec<OutPoint>,
-        outputs: Vec<u64>,
-    ) -> Txid {
-        let tx = Transaction {
-            version: 1,
-            lock_time: 0,
-            input: spend
-                .iter()
-                .cloned()
-                .map(|previous_output| TxIn {
-                    previous_output,
-                    ..Default::default()
-                })
-                .collect(),
-            output: outputs
-                .iter()
-                .cloned()
-                .map(|value| TxOut {
-                    value,
-                    ..Default::default()
-                })
-                .collect(),
-        };
-        let txid = tx.txid();
-
-        for input in &spend {
-            database.del_utxo(input).unwrap();
-        }
-        for vout in 0..outputs.len() {
-            database
-                .set_utxo(&UTXO {
-                    txout: tx.output[vout].clone(),
-                    outpoint: OutPoint {
-                        txid,
-                        vout: vout as u32,
-                    },
-                    is_internal: true,
-                })
-                .unwrap();
-        }
-        database
-            .set_tx(&TransactionDetails {
-                txid,
-                transaction: Some(tx),
-                height: None,
-                ..Default::default()
-            })
-            .unwrap();
-
-        txid
-    }
-
-    #[test]
-    fn test_filter_available() {
-        let mut database = MemoryDatabase::new();
-        add_transaction(
-            &mut database,
-            vec![OutPoint::from_str(
-                "aad194c72fd5cfd16d23da9462930ca91e35df1cfee05242b62f4034f50c3d41:5",
-            )
-            .unwrap()],
-            vec![50_000],
-        );
-
-        let filtered = filter_available(
-            &database,
-            database
-                .iter_utxos()
-                .unwrap()
-                .into_iter()
-                .map(|utxo| (utxo, 0)),
-        )
-        .unwrap();
-        assert_eq!(filtered, &[]);
-    }
-}