]> Untitled Git - bdk/commitdiff
make fee in TransactionDetails Option, add confirmation_time field as Option
authorRiccardo Casatta <riccardo@casatta.it>
Sat, 12 Jun 2021 13:01:44 +0000 (15:01 +0200)
committerRiccardo Casatta <riccardo@casatta.it>
Mon, 14 Jun 2021 13:29:24 +0000 (15:29 +0200)
confirmation_time contains both a block height and block timestamp and is
Some only for confirmed transaction

.github/workflows/cont_integration.yml
src/blockchain/compact_filters/mod.rs
src/blockchain/rpc.rs
src/blockchain/utils.rs
src/database/memory.rs
src/database/mod.rs
src/testutils/blockchain_tests.rs
src/types.rs
src/wallet/export.rs
src/wallet/mod.rs

index de4c96bd0b59d51e6cd3014bbf05ba06ff5e7dca..5ecc7e9ddf3a710e3181e0aeaf721674e04783d7 100644 (file)
@@ -78,6 +78,7 @@ jobs:
     name: Test ${{ matrix.blockchain.name }}
     runs-on: ubuntu-16.04
     strategy:
+      fail-fast: false
       matrix:
         blockchain:
           - name: electrum
index 9754891615e2d0c2d32b816e8fe22dd2e44a07e6..fd138297410d0221c192af5e33c4884a6469f275 100644 (file)
@@ -71,7 +71,7 @@ use super::{Blockchain, Capability, ConfigurableBlockchain, Progress};
 use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
 use crate::error::Error;
 use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
-use crate::FeeRate;
+use crate::{ConfirmationTime, FeeRate};
 
 use peer::*;
 use store::*;
@@ -146,7 +146,7 @@ impl CompactFiltersBlockchain {
         database: &mut D,
         tx: &Transaction,
         height: Option<u32>,
-        timestamp: u64,
+        timestamp: Option<u64>,
         internal_max_deriv: &mut Option<u32>,
         external_max_deriv: &mut Option<u32>,
     ) -> Result<(), Error> {
@@ -206,9 +206,8 @@ impl CompactFiltersBlockchain {
                 transaction: Some(tx.clone()),
                 received: incoming,
                 sent: outgoing,
-                height,
-                timestamp,
-                fees: inputs_sum.saturating_sub(outputs_sum),
+                confirmation_time: ConfirmationTime::new(height, timestamp),
+                fee: Some(inputs_sum.saturating_sub(outputs_sum)),
             };
 
             info!("Saving tx {}", tx.txid);
@@ -364,8 +363,8 @@ impl Blockchain for CompactFiltersBlockchain {
         );
         let mut updates = database.begin_batch();
         for details in database.iter_txs(false)? {
-            match details.height {
-                Some(height) if (height as usize) < last_synced_block => continue,
+            match details.confirmation_time {
+                Some(c) if (c.height as usize) < last_synced_block => continue,
                 _ => updates.del_tx(&details.txid, false)?,
             };
         }
@@ -387,7 +386,7 @@ impl Blockchain for CompactFiltersBlockchain {
                     database,
                     tx,
                     Some(height as u32),
-                    0,
+                    None,
                     &mut internal_max_deriv,
                     &mut external_max_deriv,
                 )?;
@@ -398,7 +397,7 @@ impl Blockchain for CompactFiltersBlockchain {
                 database,
                 tx,
                 None,
-                0,
+                None,
                 &mut internal_max_deriv,
                 &mut external_max_deriv,
             )?;
index 40ec0ae651facd9d7bfabddf7e72157c1b9deeff..ab55cc491d763015d11ebc493936f551edbbeedc 100644 (file)
@@ -33,7 +33,7 @@ use crate::blockchain::{Blockchain, Capability, ConfigurableBlockchain, Progress
 use crate::database::{BatchDatabase, DatabaseUtils};
 use crate::descriptor::{get_checksum, IntoWalletDescriptor};
 use crate::wallet::utils::SecpCtx;
-use crate::{Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
+use crate::{ConfirmationTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
 use bitcoincore_rpc::json::{
     GetAddressInfoResultLabel, ImportMultiOptions, ImportMultiRequest,
     ImportMultiRequestScriptPubkey, ImportMultiRescanSince,
@@ -189,13 +189,15 @@ impl Blockchain for RpcBlockchain {
             let txid = tx_result.info.txid;
             list_txs_ids.insert(txid);
             if let Some(mut known_tx) = known_txs.get_mut(&txid) {
-                if tx_result.info.blockheight != known_tx.height {
+                let confirmation_time =
+                    ConfirmationTime::new(tx_result.info.blockheight, tx_result.info.blocktime);
+                if confirmation_time != known_tx.confirmation_time {
                     // reorg may change tx height
                     debug!(
-                        "updating tx({}) height to: {:?}",
-                        txid, tx_result.info.blockheight
+                        "updating tx({}) confirmation time to: {:?}",
+                        txid, confirmation_time
                     );
-                    known_tx.height = tx_result.info.blockheight;
+                    known_tx.confirmation_time = confirmation_time;
                     db.set_tx(&known_tx)?;
                 }
             } else {
@@ -224,17 +226,17 @@ impl Blockchain for RpcBlockchain {
                 let td = TransactionDetails {
                     transaction: Some(tx),
                     txid: tx_result.info.txid,
-                    timestamp: tx_result.info.time,
+                    confirmation_time: ConfirmationTime::new(
+                        tx_result.info.blockheight,
+                        tx_result.info.blocktime,
+                    ),
                     received,
                     sent,
-                    //TODO it could happen according to the node situation/configuration that the
-                    // fee is not known [TransactionDetails:fee] should be made [Option]
-                    fees: tx_result.fee.map(|f| f.as_sat().abs() as u64).unwrap_or(0),
-                    height: tx_result.info.blockheight,
+                    fee: tx_result.fee.map(|f| f.as_sat().abs() as u64),
                 };
                 debug!(
                     "saving tx: {} tx_result.fee:{:?} td.fees:{:?}",
-                    td.txid, tx_result.fee, td.fees
+                    td.txid, tx_result.fee, td.fee
                 );
                 db.set_tx(&td)?;
             }
@@ -519,7 +521,7 @@ mod test {
         wallet.sync(noop_progress(), None).unwrap();
         assert_eq!(
             wallet.get_balance().unwrap(),
-            100_000 - 50_000 - details.fees
+            100_000 - 50_000 - details.fee.unwrap_or(0)
         );
         drop(wallet);
 
index ac86520ca844ac564864c14032a4bc4e12c697f2..0ce96460c5cd3ebcba5dfdbbba0cd9a84e6fec84 100644 (file)
@@ -21,7 +21,7 @@ use bitcoin::{BlockHeader, OutPoint, Script, Transaction, Txid};
 use super::*;
 use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
 use crate::error::Error;
-use crate::types::{KeychainKind, LocalUtxo, TransactionDetails};
+use crate::types::{ConfirmationTime, KeychainKind, LocalUtxo, TransactionDetails};
 use crate::wallet::time::Instant;
 use crate::wallet::utils::ChunksIterator;
 
@@ -147,13 +147,14 @@ pub trait ElectrumLikeSync {
         // save any tx details not in db but in history_txs_id or with different height/timestamp
         for txid in history_txs_id.iter() {
             let height = txid_height.get(txid).cloned().flatten();
-            let timestamp = *new_timestamps.get(txid).unwrap_or(&0u64);
+            let timestamp = new_timestamps.get(txid).cloned();
             if let Some(tx_details) = txs_details_in_db.get(txid) {
-                // check if height matches, otherwise updates it
-                if tx_details.height != height {
+                // check if tx height matches, otherwise updates it. timestamp is not in the if clause
+                // because we are not asking headers for confirmed tx we know about
+                if tx_details.confirmation_time.as_ref().map(|c| c.height) != height {
+                    let confirmation_time = ConfirmationTime::new(height, timestamp);
                     let mut new_tx_details = tx_details.clone();
-                    new_tx_details.height = height;
-                    new_tx_details.timestamp = timestamp;
+                    new_tx_details.confirmation_time = confirmation_time;
                     batch.set_tx(&new_tx_details)?;
                 }
             } else {
@@ -238,9 +239,13 @@ pub trait ElectrumLikeSync {
         chunk_size: usize,
     ) -> Result<HashMap<Txid, u64>, Error> {
         let mut txid_timestamp = HashMap::new();
+        let txid_in_db_with_conf: HashSet<_> = txs_details_in_db
+            .values()
+            .filter_map(|details| details.confirmation_time.as_ref().map(|_| details.txid))
+            .collect();
         let needed_txid_height: HashMap<&Txid, u32> = txid_height
             .iter()
-            .filter(|(t, _)| txs_details_in_db.get(*t).is_none())
+            .filter(|(t, _)| !txid_in_db_with_conf.contains(*t))
             .filter_map(|(t, o)| o.map(|h| (t, h)))
             .collect();
         let needed_heights: HashSet<u32> = needed_txid_height.values().cloned().collect();
@@ -292,7 +297,7 @@ pub trait ElectrumLikeSync {
 fn save_transaction_details_and_utxos<D: BatchDatabase>(
     txid: &Txid,
     db: &mut D,
-    timestamp: u64,
+    timestamp: Option<u64>,
     height: Option<u32>,
     updates: &mut dyn BatchOperations,
     utxo_deps: &HashMap<OutPoint, OutPoint>,
@@ -355,9 +360,8 @@ fn save_transaction_details_and_utxos<D: BatchDatabase>(
         transaction: Some(tx),
         received: incoming,
         sent: outgoing,
-        height,
-        timestamp,
-        fees: inputs_sum.saturating_sub(outputs_sum), /* if the tx is a coinbase, fees would be negative */
+        confirmation_time: ConfirmationTime::new(height, timestamp),
+        fee: Some(inputs_sum.saturating_sub(outputs_sum)), /* if the tx is a coinbase, fees would be negative */
     };
     updates.set_tx(&tx_details)?;
 
index cae53f10039219948a26c10da1a93dda42a3aadd..7ec10bf3021bf20d13ce9f4750abf0986f09868e 100644 (file)
@@ -473,18 +473,18 @@ macro_rules! populate_test_db {
         };
 
         let txid = tx.txid();
-        let height = tx_meta
-            .min_confirmations
-            .map(|conf| current_height.unwrap().checked_sub(conf as u32).unwrap());
+        let confirmation_time = tx_meta.min_confirmations.map(|conf| ConfirmationTime {
+            height: current_height.unwrap().checked_sub(conf as u32).unwrap(),
+            timestamp: 0,
+        });
 
         let tx_details = TransactionDetails {
             transaction: Some(tx.clone()),
             txid,
-            timestamp: 0,
-            height,
+            fee: Some(0),
             received: 0,
             sent: 0,
-            fees: 0,
+            confirmation_time,
         };
 
         db.set_tx(&tx_details).unwrap();
index 84eb511e1b219d6e6d4dbebf2a980dd3a39dd18a..b18ba1c05259e6e821a4046ab3950910e92e15bb 100644 (file)
@@ -314,11 +314,13 @@ pub mod test {
         let mut tx_details = TransactionDetails {
             transaction: Some(tx),
             txid,
-            timestamp: 123456,
             received: 1337,
             sent: 420420,
-            fees: 140,
-            height: Some(1000),
+            fee: Some(140),
+            confirmation_time: Some(ConfirmationTime {
+                timestamp: 123456,
+                height: 1000,
+            }),
         };
 
         tree.set_tx(&tx_details).unwrap();
index 0b2b559e826117cd782e57bb78d0a3bb244395bd..c8e12d5fa980ddc679c341f9241f7505508c95f2 100644 (file)
@@ -396,7 +396,7 @@ macro_rules! bdk_blockchain_tests {
                 assert_eq!(list_tx_item.txid, txid, "incorrect txid");
                 assert_eq!(list_tx_item.received, 50_000, "incorrect received");
                 assert_eq!(list_tx_item.sent, 0, "incorrect sent");
-                assert_eq!(list_tx_item.height, None, "incorrect height");
+                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation time");
             }
 
             #[test]
@@ -454,7 +454,7 @@ macro_rules! bdk_blockchain_tests {
                 assert_eq!(list_tx_item.txid, txid, "incorrect txid");
                 assert_eq!(list_tx_item.received, 105_000, "incorrect received");
                 assert_eq!(list_tx_item.sent, 0, "incorrect sent");
-                assert_eq!(list_tx_item.height, None, "incorrect height");
+                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation_time");
             }
 
             #[test]
@@ -515,7 +515,7 @@ macro_rules! bdk_blockchain_tests {
                 assert_eq!(list_tx_item.txid, txid, "incorrect txid");
                 assert_eq!(list_tx_item.received, 50_000, "incorrect received");
                 assert_eq!(list_tx_item.sent, 0, "incorrect sent");
-                assert_eq!(list_tx_item.height, None, "incorrect height");
+                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation_time");
 
                 let new_txid = test_client.bump_fee(&txid);
 
@@ -529,7 +529,7 @@ macro_rules! bdk_blockchain_tests {
                 assert_eq!(list_tx_item.txid, new_txid, "incorrect txid after bump");
                 assert_eq!(list_tx_item.received, 50_000, "incorrect received after bump");
                 assert_eq!(list_tx_item.sent, 0, "incorrect sent after bump");
-                assert_eq!(list_tx_item.height, None, "incorrect height after bump");
+                assert_eq!(list_tx_item.confirmation_time, None, "incorrect height after bump");
             }
 
             // FIXME: I would like this to be cfg_attr(not(feature = "test-esplora"), ignore) but it
@@ -552,7 +552,7 @@ macro_rules! bdk_blockchain_tests {
 
                 let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
                 assert_eq!(list_tx_item.txid, txid, "incorrect txid");
-                assert!(list_tx_item.height.is_some(), "incorrect height");
+                assert!(list_tx_item.confirmation_time.is_some(), "incorrect confirmation_time");
 
                 // Invalidate 1 block
                 test_client.invalidate(1);
@@ -563,7 +563,7 @@ macro_rules! bdk_blockchain_tests {
 
                 let list_tx_item = &wallet.list_transactions(false).unwrap()[0];
                 assert_eq!(list_tx_item.txid, txid, "incorrect txid after invalidate");
-                assert_eq!(list_tx_item.height, None, "incorrect height after invalidate");
+                assert_eq!(list_tx_item.confirmation_time, None, "incorrect confirmation time after invalidate");
             }
 
             #[test]
@@ -596,6 +596,34 @@ macro_rules! bdk_blockchain_tests {
                 assert_eq!(wallet.list_unspent().unwrap().len(), 1, "incorrect number of unspents");
             }
 
+            #[test]
+            #[serial]
+            fn test_update_confirmation_time_after_generate() {
+                let (wallet, descriptors, mut test_client) = init_single_sig();
+                println!("{}", descriptors.0);
+                let node_addr = test_client.get_node_address(None);
+
+                let received_txid = test_client.receive(testutils! {
+                    @tx ( (@external descriptors, 0) => 50_000 )
+                });
+
+                wallet.sync(noop_progress(), None).unwrap();
+                assert_eq!(wallet.get_balance().unwrap(), 50_000, "incorrect balance");
+
+                let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
+                let details = tx_map.get(&received_txid).unwrap();
+                assert!(details.confirmation_time.is_none());
+
+                test_client.generate(1, Some(node_addr));
+                wallet.sync(noop_progress(), None).unwrap();
+
+                let tx_map = wallet.list_transactions(false).unwrap().into_iter().map(|tx| (tx.txid, tx)).collect::<std::collections::HashMap<_, _>>();
+                let details = tx_map.get(&received_txid).unwrap();
+                assert!(details.confirmation_time.is_some());
+
+            }
+
+
             #[test]
             #[serial]
             fn test_sync_outgoing_from_scratch() {
@@ -635,7 +663,7 @@ macro_rules! bdk_blockchain_tests {
                 let sent = tx_map.get(&sent_txid).unwrap();
                 assert_eq!(sent.received, details.received, "incorrect received from sender");
                 assert_eq!(sent.sent, details.sent, "incorrect sent from sender");
-                assert_eq!(sent.fees, details.fees, "incorrect fees from sender");
+                assert_eq!(sent.fee.unwrap_or(0), details.fee.unwrap_or(0), "incorrect fees from sender");
             }
 
             #[test]
@@ -662,7 +690,7 @@ macro_rules! bdk_blockchain_tests {
 
                     wallet.sync(noop_progress(), None).unwrap();
 
-                    total_sent += 5_000 + details.fees;
+                    total_sent += 5_000 + details.fee.unwrap_or(0);
                 }
 
                 wallet.sync(noop_progress(), None).unwrap();
@@ -700,7 +728,7 @@ macro_rules! bdk_blockchain_tests {
                 assert!(finalized, "Cannot finalize transaction");
                 wallet.broadcast(psbt.extract_tx()).unwrap();
                 wallet.sync(noop_progress(), None).unwrap();
-                assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fees - 5_000, "incorrect balance from fees");
+                assert_eq!(wallet.get_balance().unwrap(), 50_000 - details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees");
                 assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect balance from received");
 
                 let mut builder = wallet.build_fee_bump(details.txid).unwrap();
@@ -710,10 +738,10 @@ macro_rules! bdk_blockchain_tests {
                 assert!(finalized, "Cannot finalize transaction");
                 wallet.broadcast(new_psbt.extract_tx()).unwrap();
                 wallet.sync(noop_progress(), None).unwrap();
-                assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fees - 5_000, "incorrect balance from fees after bump");
+                assert_eq!(wallet.get_balance().unwrap(), 50_000 - new_details.fee.unwrap_or(0) - 5_000, "incorrect balance from fees after bump");
                 assert_eq!(wallet.get_balance().unwrap(), new_details.received, "incorrect balance from received after bump");
 
-                assert!(new_details.fees > details.fees, "incorrect fees");
+                assert!(new_details.fee.unwrap_or(0) > details.fee.unwrap_or(0), "incorrect fees");
             }
 
             #[test]
@@ -736,7 +764,7 @@ macro_rules! bdk_blockchain_tests {
                 assert!(finalized, "Cannot finalize transaction");
                 wallet.broadcast(psbt.extract_tx()).unwrap();
                 wallet.sync(noop_progress(), None).unwrap();
-                assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fees, "incorrect balance after send");
+                assert_eq!(wallet.get_balance().unwrap(), 1_000 - details.fee.unwrap_or(0), "incorrect balance after send");
                 assert_eq!(wallet.get_balance().unwrap(), details.received, "incorrect received after send");
 
                 let mut builder = wallet.build_fee_bump(details.txid).unwrap();
@@ -749,12 +777,12 @@ macro_rules! bdk_blockchain_tests {
                 assert_eq!(wallet.get_balance().unwrap(), 0, "incorrect balance after change removal");
                 assert_eq!(new_details.received, 0, "incorrect received after change removal");
 
-                assert!(new_details.fees > details.fees, "incorrect fees");
+                assert!(new_details.fee.unwrap_or(0) > details.fee.unwrap_or(0), "incorrect fees");
             }
 
             #[test]
             #[serial]
-            fn test_sync_bump_fee_add_input() {
+            fn test_sync_bump_fee_add_input_simple() {
                 let (wallet, descriptors, mut test_client) = init_single_sig();
                 let node_addr = test_client.get_node_address(None);
 
@@ -772,8 +800,8 @@ macro_rules! bdk_blockchain_tests {
                 assert!(finalized, "Cannot finalize transaction");
                 wallet.broadcast(psbt.extract_tx()).unwrap();
                 wallet.sync(noop_progress(), None).unwrap();
-                assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees, "incorrect balance after send");
-                assert_eq!(details.received, 1_000 - details.fees, "incorrect received after send");
+                assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send");
+                assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");
 
                 let mut builder = wallet.build_fee_bump(details.txid).unwrap();
                 builder.fee_rate(FeeRate::from_sat_per_vb(10.0));
@@ -806,8 +834,8 @@ macro_rules! bdk_blockchain_tests {
                 assert!(finalized, "Cannot finalize transaction");
                 wallet.broadcast(psbt.extract_tx()).unwrap();
                 wallet.sync(noop_progress(), None).unwrap();
-                assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fees, "incorrect balance after send");
-                assert_eq!(details.received, 1_000 - details.fees, "incorrect received after send");
+                assert_eq!(wallet.get_balance().unwrap(), 26_000 - details.fee.unwrap_or(0), "incorrect balance after send");
+                assert_eq!(details.received, 1_000 - details.fee.unwrap_or(0), "incorrect received after send");
 
                 let mut builder = wallet.build_fee_bump(details.txid).unwrap();
                 builder.fee_rate(FeeRate::from_sat_per_vb(123.0));
index cdb5d26856e3d8b0bcba2380af2826661c70ab87..c71b87f4f19f8c0d3c08f543f34bc1495df1db62 100644 (file)
@@ -155,16 +155,35 @@ pub struct TransactionDetails {
     pub transaction: Option<Transaction>,
     /// Transaction id
     pub txid: Txid,
-    /// Timestamp
-    pub timestamp: u64,
+
     /// Received value (sats)
     pub received: u64,
     /// Sent value (sats)
     pub sent: u64,
-    /// Fee value (sats)
-    pub fees: u64,
-    /// Confirmed in block height, `None` means unconfirmed
-    pub height: Option<u32>,
+    /// Fee value (sats) if available
+    pub fee: Option<u64>,
+    /// If the transaction is confirmed, contains height and timestamp of the block containing the
+    /// transaction, unconfirmed transaction contains `None`.
+    pub confirmation_time: Option<ConfirmationTime>,
+}
+
+/// Block height and timestamp of the block containing the confirmed transaction
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
+pub struct ConfirmationTime {
+    /// confirmation block height
+    pub height: u32,
+    /// confirmation block timestamp
+    pub timestamp: u64,
+}
+
+impl ConfirmationTime {
+    /// Returns `Some` `ConfirmationTime` if both `height` and `timestamp` are `Some`
+    pub fn new(height: Option<u32>, timestamp: Option<u64>) -> Option<Self> {
+        match (height, timestamp) {
+            (Some(height), Some(timestamp)) => Some(ConfirmationTime { height, timestamp }),
+            _ => None,
+        }
+    }
 }
 
 #[cfg(test)]
index 2be646d542911e493f34de22973a587d17ae9408..58ff77d257318f4a56086038c7b4a245be79631a 100644 (file)
@@ -128,7 +128,7 @@ impl WalletExport {
             Ok(txs) => {
                 let mut heights = txs
                     .into_iter()
-                    .map(|tx| tx.height.unwrap_or(0))
+                    .map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(0))
                     .collect::<Vec<_>>();
                 heights.sort_unstable();
 
@@ -212,6 +212,7 @@ mod test {
     use crate::database::{memory::MemoryDatabase, BatchOperations};
     use crate::types::TransactionDetails;
     use crate::wallet::Wallet;
+    use crate::ConfirmationTime;
 
     fn get_test_db() -> MemoryDatabase {
         let mut db = MemoryDatabase::new();
@@ -221,11 +222,14 @@ mod test {
                 "4ddff1fa33af17f377f62b72357b43107c19110a8009b36fb832af505efed98a",
             )
             .unwrap(),
-            timestamp: 12345678,
+
             received: 100_000,
             sent: 0,
-            fees: 500,
-            height: Some(5000),
+            fee: Some(500),
+            confirmation_time: Some(ConfirmationTime {
+                timestamp: 12345678,
+                height: 5000,
+            }),
         })
         .unwrap();
 
index 135b03c2a6e434d7fa5e70956755c6692b07601a..6127b5fd3b581cf84ca0eca2b5425b92ef3a1ab6 100644 (file)
@@ -706,11 +706,10 @@ where
         let transaction_details = TransactionDetails {
             transaction: None,
             txid,
-            timestamp: time::get_timestamp(),
+            confirmation_time: None,
             received,
             sent,
-            fees: fee_amount,
-            height: None,
+            fee: Some(fee_amount),
         };
 
         Ok((psbt, transaction_details))
@@ -769,7 +768,7 @@ where
         let mut details = match self.database.borrow().get_tx(&txid, true)? {
             None => return Err(Error::TransactionNotFound),
             Some(tx) if tx.transaction.is_none() => return Err(Error::TransactionNotFound),
-            Some(tx) if tx.height.is_some() => return Err(Error::TransactionConfirmed),
+            Some(tx) if tx.confirmation_time.is_some() => return Err(Error::TransactionConfirmed),
             Some(tx) => tx,
         };
         let mut tx = details.transaction.take().unwrap();
@@ -778,7 +777,7 @@ where
         }
 
         let vbytes = tx.get_weight() as f32 / 4.0;
-        let feerate = details.fees as f32 / vbytes;
+        let feerate = details.fee.ok_or(Error::FeeRateUnavailable)? as f32 / vbytes;
 
         // remove the inputs from the tx and process them
         let original_txin = tx.input.drain(..).collect::<Vec<_>>();
@@ -854,7 +853,7 @@ where
                 .collect(),
             utxos: original_utxos,
             bumping_fee: Some(tx_builder::PreviousFee {
-                absolute: details.fees,
+                absolute: details.fee.ok_or(Error::FeeRateUnavailable)?,
                 rate: feerate,
             }),
             ..Default::default()
@@ -993,7 +992,7 @@ where
                 .database
                 .borrow()
                 .get_tx(&input.previous_output.txid, false)?
-                .map(|tx| tx.height.unwrap_or(std::u32::MAX));
+                .map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(u32::MAX));
             let current_height = sign_options.assume_height.or(self.current_height);
 
             debug!(
@@ -1231,7 +1230,7 @@ where
 
         let satisfies_confirmed = match must_only_use_confirmed_tx {
             true => {
-                let database = self.database.borrow_mut();
+                let database = self.database.borrow();
                 may_spend
                     .iter()
                     .map(|u| {
@@ -1239,7 +1238,7 @@ where
                             .get_tx(&u.0.outpoint.txid, true)
                             .map(|tx| match tx {
                                 None => false,
-                                Some(tx) => tx.height.is_some(),
+                                Some(tx) => tx.confirmation_time.is_some(),
                             })
                     })
                     .collect::<Result<Vec<_>, _>>()?
@@ -1986,7 +1985,7 @@ pub(crate) mod test {
         assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
         assert_eq!(
             psbt.global.unsigned_tx.output[0].value,
-            50_000 - details.fees
+            50_000 - details.fee.unwrap_or(0)
         );
     }
 
@@ -1998,7 +1997,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::default(), @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::default(), @add_signature);
     }
 
     #[test]
@@ -2011,7 +2010,7 @@ pub(crate) mod test {
             .fee_rate(FeeRate::from_sat_per_vb(5.0));
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature);
     }
 
     #[test]
@@ -2025,11 +2024,11 @@ pub(crate) mod test {
             .fee_absolute(100);
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_eq!(details.fees, 100);
+        assert_eq!(details.fee.unwrap_or(0), 100);
         assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
         assert_eq!(
             psbt.global.unsigned_tx.output[0].value,
-            50_000 - details.fees
+            50_000 - details.fee.unwrap_or(0)
         );
     }
 
@@ -2044,11 +2043,11 @@ pub(crate) mod test {
             .fee_absolute(0);
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_eq!(details.fees, 0);
+        assert_eq!(details.fee.unwrap_or(0), 0);
         assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
         assert_eq!(
             psbt.global.unsigned_tx.output[0].value,
-            50_000 - details.fees
+            50_000 - details.fee.unwrap_or(0)
         );
     }
 
@@ -2081,7 +2080,7 @@ pub(crate) mod test {
         assert_eq!(psbt.global.unsigned_tx.output[0].value, 25_000);
         assert_eq!(
             psbt.global.unsigned_tx.output[1].value,
-            25_000 - details.fees
+            25_000 - details.fee.unwrap_or(0)
         );
     }
 
@@ -2095,7 +2094,7 @@ pub(crate) mod test {
 
         assert_eq!(psbt.global.unsigned_tx.output.len(), 1);
         assert_eq!(psbt.global.unsigned_tx.output[0].value, 49_800);
-        assert_eq!(details.fees, 200);
+        assert_eq!(details.fee.unwrap_or(0), 200);
     }
 
     #[test]
@@ -2126,7 +2125,7 @@ pub(crate) mod test {
         assert_eq!(psbt.global.unsigned_tx.output.len(), 3);
         assert_eq!(
             psbt.global.unsigned_tx.output[0].value,
-            10_000 - details.fees
+            10_000 - details.fee.unwrap_or(0)
         );
         assert_eq!(psbt.global.unsigned_tx.output[1].value, 10_000);
         assert_eq!(psbt.global.unsigned_tx.output[2].value, 30_000);
@@ -2496,7 +2495,7 @@ pub(crate) mod test {
 
         assert_eq!(
             details.sent - details.received,
-            10_000 + details.fees,
+            10_000 + details.fee.unwrap_or(0),
             "we should have only net spent ~10_000"
         );
 
@@ -2762,7 +2761,10 @@ pub(crate) mod test {
         let txid = tx.txid();
         // skip saving the utxos, we know they can't be used anyways
         details.transaction = Some(tx);
-        details.height = Some(42);
+        details.confirmation_time = Some(ConfirmationTime {
+            timestamp: 12345678,
+            height: 42,
+        });
         wallet.database.borrow_mut().set_tx(&details).unwrap();
 
         wallet.build_fee_bump(txid).unwrap().finish().unwrap();
@@ -2867,10 +2869,10 @@ pub(crate) mod test {
 
         assert_eq!(details.sent, original_details.sent);
         assert_eq!(
-            details.received + details.fees,
-            original_details.received + original_details.fees
+            details.received + details.fee.unwrap_or(0),
+            original_details.received + original_details.fee.unwrap_or(0)
         );
-        assert!(details.fees > original_details.fees);
+        assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
 
         let tx = &psbt.global.unsigned_tx;
         assert_eq!(tx.output.len(), 2);
@@ -2891,7 +2893,7 @@ pub(crate) mod test {
             details.received
         );
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature);
     }
 
     #[test]
@@ -2928,14 +2930,14 @@ pub(crate) mod test {
 
         assert_eq!(details.sent, original_details.sent);
         assert_eq!(
-            details.received + details.fees,
-            original_details.received + original_details.fees
+            details.received + details.fee.unwrap_or(0),
+            original_details.received + original_details.fee.unwrap_or(0)
         );
         assert!(
-            details.fees > original_details.fees,
+            details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0),
             "{} > {}",
-            details.fees,
-            original_details.fees
+            details.fee.unwrap_or(0),
+            original_details.fee.unwrap_or(0)
         );
 
         let tx = &psbt.global.unsigned_tx;
@@ -2957,7 +2959,7 @@ pub(crate) mod test {
             details.received
         );
 
-        assert_eq!(details.fees, 200);
+        assert_eq!(details.fee.unwrap_or(0), 200);
     }
 
     #[test]
@@ -2995,13 +2997,13 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.sent, original_details.sent);
-        assert!(details.fees > original_details.fees);
+        assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
 
         let tx = &psbt.global.unsigned_tx;
         assert_eq!(tx.output.len(), 1);
-        assert_eq!(tx.output[0].value + details.fees, details.sent);
+        assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent);
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(2.5), @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(2.5), @add_signature);
     }
 
     #[test]
@@ -3039,13 +3041,13 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.sent, original_details.sent);
-        assert!(details.fees > original_details.fees);
+        assert!(details.fee.unwrap_or(0) > original_details.fee.unwrap_or(0));
 
         let tx = &psbt.global.unsigned_tx;
         assert_eq!(tx.output.len(), 1);
-        assert_eq!(tx.output[0].value + details.fees, details.sent);
+        assert_eq!(tx.output[0].value + details.fee.unwrap_or(0), details.sent);
 
-        assert_eq!(details.fees, 300);
+        assert_eq!(details.fee.unwrap_or(0), 300);
     }
 
     #[test]
@@ -3190,7 +3192,7 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.sent, original_details.sent + 25_000);
-        assert_eq!(details.fees + details.received, 30_000);
+        assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
         let tx = &psbt.global.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
@@ -3212,7 +3214,7 @@ pub(crate) mod test {
             details.received
         );
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(50.0), @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature);
     }
 
     #[test]
@@ -3253,7 +3255,7 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.sent, original_details.sent + 25_000);
-        assert_eq!(details.fees + details.received, 30_000);
+        assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
         let tx = &psbt.global.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
@@ -3275,7 +3277,7 @@ pub(crate) mod test {
             details.received
         );
 
-        assert_eq!(details.fees, 6_000);
+        assert_eq!(details.fee.unwrap_or(0), 6_000);
     }
 
     #[test]
@@ -3325,11 +3327,11 @@ pub(crate) mod test {
         builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
         let (psbt, details) = builder.finish().unwrap();
 
-        let original_send_all_amount = original_details.sent - original_details.fees;
+        let original_send_all_amount = original_details.sent - original_details.fee.unwrap_or(0);
         assert_eq!(details.sent, original_details.sent + 50_000);
         assert_eq!(
             details.received,
-            75_000 - original_send_all_amount - details.fees
+            75_000 - original_send_all_amount - details.fee.unwrap_or(0)
         );
 
         let tx = &psbt.global.unsigned_tx;
@@ -3349,10 +3351,10 @@ pub(crate) mod test {
                 .find(|txout| txout.script_pubkey != addr.script_pubkey())
                 .unwrap()
                 .value,
-            75_000 - original_send_all_amount - details.fees
+            75_000 - original_send_all_amount - details.fee.unwrap_or(0)
         );
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(50.0), @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(50.0), @add_signature);
     }
 
     #[test]
@@ -3394,10 +3396,13 @@ pub(crate) mod test {
         builder.fee_rate(FeeRate::from_sat_per_vb(140.0));
         let (psbt, details) = builder.finish().unwrap();
 
-        assert_eq!(original_details.received, 5_000 - original_details.fees);
+        assert_eq!(
+            original_details.received,
+            5_000 - original_details.fee.unwrap_or(0)
+        );
 
         assert_eq!(details.sent, original_details.sent + 25_000);
-        assert_eq!(details.fees, 30_000);
+        assert_eq!(details.fee.unwrap_or(0), 30_000);
         assert_eq!(details.received, 0);
 
         let tx = &psbt.global.unsigned_tx;
@@ -3412,7 +3417,7 @@ pub(crate) mod test {
             45_000
         );
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(140.0), @dust_change, @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(140.0), @dust_change, @add_signature);
     }
 
     #[test]
@@ -3461,7 +3466,7 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.sent, original_details.sent + 25_000);
-        assert_eq!(details.fees + details.received, 30_000);
+        assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
         let tx = &psbt.global.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
@@ -3483,7 +3488,7 @@ pub(crate) mod test {
             details.received
         );
 
-        assert_fee_rate!(psbt.extract_tx(), details.fees, FeeRate::from_sat_per_vb(5.0), @add_signature);
+        assert_fee_rate!(psbt.extract_tx(), details.fee.unwrap_or(0), FeeRate::from_sat_per_vb(5.0), @add_signature);
     }
 
     #[test]
@@ -3532,7 +3537,7 @@ pub(crate) mod test {
         let (psbt, details) = builder.finish().unwrap();
 
         assert_eq!(details.sent, original_details.sent + 25_000);
-        assert_eq!(details.fees + details.received, 30_000);
+        assert_eq!(details.fee.unwrap_or(0) + details.received, 30_000);
 
         let tx = &psbt.global.unsigned_tx;
         assert_eq!(tx.input.len(), 2);
@@ -3554,7 +3559,7 @@ pub(crate) mod test {
             details.received
         );
 
-        assert_eq!(details.fees, 250);
+        assert_eq!(details.fee.unwrap_or(0), 250);
     }
 
     #[test]