]> Untitled Git - bdk/commitdiff
[wallet] Add an option to change the assumed current height
authorAlekos Filini <alekos.filini@gmail.com>
Wed, 6 May 2020 15:17:14 +0000 (17:17 +0200)
committerAlekos Filini <alekos.filini@gmail.com>
Wed, 6 May 2020 15:17:14 +0000 (17:17 +0200)
examples/repl.rs
src/blockchain/electrum.rs
src/blockchain/mod.rs
src/wallet/mod.rs

index 66598514d37f6f1caaf59dbb26bda71d2de0d27f..13ee095120b1831de8cdba31f629e789b08fd4c1 100644 (file)
@@ -161,6 +161,15 @@ fn main() {
                         .takes_value(true)
                         .number_of_values(1)
                         .required(true),
+                )
+                .arg(
+                    Arg::with_name("assume_height")
+                        .long("assume_height")
+                        .value_name("HEIGHT")
+                        .help("Assume the blockchain has reached a specific height. This affects the transaction finalization, if there are timelocks in the descriptor")
+                        .takes_value(true)
+                        .number_of_values(1)
+                        .required(false),
                 ))
         .subcommand(
             SubCommand::with_name("broadcast")
@@ -322,7 +331,10 @@ fn main() {
         } else if let Some(sub_matches) = matches.subcommand_matches("sign") {
             let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
             let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
-            let (psbt, finalized) = wallet.sign(psbt).unwrap();
+            let assume_height = sub_matches
+                .value_of("assume_height")
+                .and_then(|s| Some(s.parse().unwrap()));
+            let (psbt, finalized) = wallet.sign(psbt, assume_height).unwrap();
 
             println!("PSBT: {}", base64::encode(&serialize(&psbt)));
             println!("Finalized: {}", finalized);
index 63903fad37bfddc29ab28a42ee073c29fa8fc5ad..9195beb870d71c5b75d06e623324ed87666d67df 100644 (file)
@@ -29,6 +29,10 @@ impl<T: Read + Write> Blockchain for ElectrumBlockchain<T> {
     fn offline() -> Self {
         ElectrumBlockchain(None)
     }
+
+    fn is_online(&self) -> bool {
+        self.0.is_some()
+    }
 }
 
 impl<T: Read + Write> OnlineBlockchain for ElectrumBlockchain<T> {
@@ -190,6 +194,8 @@ impl<T: Read + Write> OnlineBlockchain for ElectrumBlockchain<T> {
     }
 
     fn get_height(&mut self) -> Result<usize, Error> {
+        // TODO: unsubscribe when added to the client, or is there a better call to use here?
+
         Ok(self
             .0
             .as_mut()
index 5ea3fdb1449cb9239e353a71f3d9cdac52620a41..1060f095cebb3b9ca6a812b09f8fc7c9f82b1161 100644 (file)
@@ -18,6 +18,8 @@ pub enum Capability {
 }
 
 pub trait Blockchain {
+    fn is_online(&self) -> bool;
+
     fn offline() -> Self;
 }
 
@@ -26,6 +28,10 @@ impl Blockchain for OfflineBlockchain {
     fn offline() -> Self {
         OfflineBlockchain
     }
+
+    fn is_online(&self) -> bool {
+        false
+    }
 }
 
 pub trait OnlineBlockchain: Blockchain {
index 2350441dc422916dca9b23e2a1b0850602664f60..b0332c139e0bebbf774bf3a399d3ff6d8bee453f 100644 (file)
@@ -31,14 +31,13 @@ use crate::types::*;
 
 pub type OfflineWallet<D> = Wallet<OfflineBlockchain, D>;
 
-//#[cfg(feature = "electrum")]
-//pub type ElectrumWallet<S, D> = Wallet<crate::blockchain::ElectrumBlockchain<electrum_client::Client<S>>, D>;
-
 pub struct Wallet<B: Blockchain, D: BatchDatabase> {
     descriptor: ExtendedDescriptor,
     change_descriptor: Option<ExtendedDescriptor>,
     network: Network,
 
+    current_height: Option<u32>,
+
     client: RefCell<B>,
     database: RefCell<D>,
 }
@@ -82,6 +81,8 @@ where
             change_descriptor,
             network,
 
+            current_height: None,
+
             client: RefCell::new(B::offline()),
             database: RefCell::new(database),
         })
@@ -342,7 +343,7 @@ where
     }
 
     // TODO: define an enum for signing errors
-    pub fn sign(&self, mut psbt: PSBT) -> Result<(PSBT, bool), Error> {
+    pub fn sign(&self, mut psbt: PSBT, assume_height: Option<u32>) -> Result<(PSBT, bool), Error> {
         // this helps us doing our job later
         self.add_hd_keypaths(&mut psbt)?;
 
@@ -482,7 +483,7 @@ where
         }
 
         // attempt to finalize
-        let finalized = self.finalize_psbt(tx.clone(), &mut psbt);
+        let finalized = self.finalize_psbt(tx.clone(), &mut psbt, assume_height)?;
 
         Ok((psbt, finalized))
     }
@@ -635,7 +636,12 @@ where
         Ok((answer, paths, selected_amount, fee_val))
     }
 
-    fn finalize_psbt(&self, mut tx: Transaction, psbt: &mut PSBT) -> bool {
+    fn finalize_psbt(
+        &self,
+        mut tx: Transaction,
+        psbt: &mut PSBT,
+        assume_height: Option<u32>,
+    ) -> Result<bool, Error> {
         for (n, input) in tx.input.iter_mut().enumerate() {
             // safe to run only on the descriptor because we assume the change descriptor also has
             // the same structure
@@ -643,18 +649,33 @@ where
             debug!("reconstructed descriptor is {:?}", desc);
 
             let desc = match desc {
-                Err(_) => return false,
+                Err(_) => return Ok(false),
                 Ok(desc) => desc,
             };
 
+            // if the height is None in the database it means it's still unconfirmed, so consider
+            // that as a very high value
+            let create_height = self
+                .database
+                .borrow()
+                .get_tx(&input.previous_output.txid, false)?
+                .and_then(|tx| Some(tx.height.unwrap_or(std::u32::MAX)));
+            let current_height = assume_height.or(self.current_height);
+
+            debug!(
+                "Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}",
+                n, input.previous_output, create_height, current_height
+            );
+
             // TODO: use height once we sync headers
-            let satisfier = PSBTSatisfier::new(&psbt.inputs[n], true, None, None);
+            let satisfier =
+                PSBTSatisfier::new(&psbt.inputs[n], false, create_height, current_height);
 
             match desc.satisfy(input, satisfier) {
                 Ok(_) => continue,
                 Err(e) => {
                     debug!("satisfy error {:?} for input {}", e, n);
-                    return false;
+                    return Ok(false);
                 }
             }
         }
@@ -665,7 +686,7 @@ where
             psbt_input.final_script_witness = Some(input.witness);
         }
 
-        true
+        Ok(true)
     }
 }
 
@@ -679,7 +700,7 @@ where
         change_descriptor: Option<&str>,
         network: Network,
         mut database: D,
-        client: B,
+        mut client: B,
     ) -> Result<Self, Error> {
         database.check_descriptor_checksum(
             ScriptType::External,
@@ -703,11 +724,15 @@ where
             None => None,
         };
 
+        let current_height = Some(client.get_height()? as u32);
+
         Ok(Wallet {
             descriptor,
             change_descriptor,
             network,
 
+            current_height,
+
             client: RefCell::new(client),
             database: RefCell::new(database),
         })