]> Untitled Git - bdk/commitdiff
[wallet_redesign] Update `Wallet` with redesigned structures
author志宇 <hello@evanlinjin.me>
Tue, 9 May 2023 13:49:33 +0000 (21:49 +0800)
committer志宇 <hello@evanlinjin.me>
Sat, 3 Jun 2023 19:32:14 +0000 (03:32 +0800)
crates/bdk/src/wallet/export.rs
crates/bdk/src/wallet/mod.rs
crates/bdk/src/wallet/tx_builder.rs
crates/bdk/tests/common.rs
crates/bdk/tests/wallet.rs
crates/chain/src/chain_data.rs
example-crates/wallet_electrum/src/main.rs
example-crates/wallet_esplora/src/main.rs
example-crates/wallet_esplora_async/src/main.rs

index 905638449e9763759e9824f1c7ad4af7a84a2330..36b75ea2000da98a82c4ac775c45ef7879620a43 100644 (file)
@@ -56,7 +56,6 @@
 use core::str::FromStr;
 
 use alloc::string::{String, ToString};
-use bdk_chain::sparse_chain::ChainPosition;
 use serde::{Deserialize, Serialize};
 
 use miniscript::descriptor::{ShInner, WshInner};
@@ -130,8 +129,10 @@ impl FullyNodedExport {
             wallet
                 .transactions()
                 .next()
-                .and_then(|(pos, _)| pos.height().into())
-                .unwrap_or(0)
+                .map_or(0, |canonical_tx| match canonical_tx.observed_as {
+                    bdk_chain::ObservedAs::Confirmed(a) => a.confirmation_height,
+                    bdk_chain::ObservedAs::Unconfirmed(_) => 0,
+                })
         } else {
             0
         };
@@ -246,6 +247,7 @@ mod test {
                     height: 5000,
                     time: 0,
                 },
+                None,
             )
             .unwrap();
         wallet
index 95197545f83b259211d79be50f23739f9c534bbf..c894d6ba2a0454b8f9ffb71764d28772ce663674 100644 (file)
@@ -21,9 +21,12 @@ use alloc::{
 };
 pub use bdk_chain::keychain::Balance;
 use bdk_chain::{
-    chain_graph,
-    keychain::{persist, KeychainChangeSet, KeychainScan, KeychainTracker},
-    sparse_chain, BlockId, ConfirmationTime,
+    indexed_tx_graph::{IndexedAdditions, IndexedTxGraph},
+    keychain::{DerivationAdditions, KeychainTxOutIndex},
+    local_chain::{self, LocalChain, UpdateNotConnectedError},
+    tx_graph::{CanonicalTx, TxGraph},
+    Anchor, Append, BlockId, ConfirmationTime, ConfirmationTimeAnchor, FullTxOut, ObservedAs,
+    Persist, PersistBackend,
 };
 use bitcoin::consensus::encode::serialize;
 use bitcoin::secp256k1::Secp256k1;
@@ -83,19 +86,83 @@ const COINBASE_MATURITY: u32 = 100;
 pub struct Wallet<D = ()> {
     signers: Arc<SignersContainer>,
     change_signers: Arc<SignersContainer>,
-    keychain_tracker: KeychainTracker<KeychainKind, ConfirmationTime>,
-    persist: persist::Persist<KeychainKind, ConfirmationTime, D>,
+    chain: LocalChain,
+    indexed_graph: IndexedTxGraph<ConfirmationTimeAnchor, KeychainTxOutIndex<KeychainKind>>,
+    persist: Persist<D, ChangeSet>, // [TODO] Use a different `ChangeSet`
     network: Network,
     secp: SecpCtx,
 }
 
 /// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
 /// The type parameter `T` indicates the kind of transaction contained in the update. It's usually a [`bitcoin::Transaction`].
-pub type Update = KeychainScan<KeychainKind, ConfirmationTime>;
-/// Error indicating that something was wrong with an [`Update<T>`].
-pub type UpdateError = chain_graph::UpdateError<ConfirmationTime>;
+#[derive(Debug, Default, PartialEq)]
+pub struct Update<K = KeychainKind, A = ConfirmationTimeAnchor> {
+    keychain: BTreeMap<K, u32>,
+    graph: TxGraph<A>,
+    chain: LocalChain,
+}
+
 /// The changeset produced internally by applying an update
-pub(crate) type ChangeSet = KeychainChangeSet<KeychainKind, ConfirmationTime>;
+#[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
+#[serde(bound(
+    deserialize = "A: Ord + serde::Deserialize<'de>, K: Ord + serde::Deserialize<'de>",
+    serialize = "A: Ord + serde::Serialize, K: Ord + serde::Serialize"
+))]
+// #[cfg_attr(predicate, attr)]
+pub struct ChangeSet<K = KeychainKind, A = ConfirmationTimeAnchor> {
+    pub chain_changeset: local_chain::ChangeSet,
+    pub indexed_additions: IndexedAdditions<A, DerivationAdditions<K>>,
+}
+
+impl<K, A> Default for ChangeSet<K, A> {
+    fn default() -> Self {
+        Self {
+            chain_changeset: Default::default(),
+            indexed_additions: Default::default(),
+        }
+    }
+}
+
+impl<K: Ord, A: Anchor> Append for ChangeSet<K, A> {
+    fn append(&mut self, other: Self) {
+        Append::append(&mut self.chain_changeset, other.chain_changeset);
+        Append::append(&mut self.indexed_additions, other.indexed_additions);
+    }
+
+    fn is_empty(&self) -> bool {
+        self.chain_changeset.is_empty() && self.indexed_additions.is_empty()
+    }
+}
+
+impl<K, A> From<IndexedAdditions<A, DerivationAdditions<K>>> for ChangeSet<K, A> {
+    fn from(indexed_additions: IndexedAdditions<A, DerivationAdditions<K>>) -> Self {
+        Self {
+            indexed_additions,
+            ..Default::default()
+        }
+    }
+}
+
+impl<K, A> From<DerivationAdditions<K>> for ChangeSet<K, A> {
+    fn from(index_additions: DerivationAdditions<K>) -> Self {
+        Self {
+            indexed_additions: IndexedAdditions {
+                index_additions,
+                ..Default::default()
+            },
+            ..Default::default()
+        }
+    }
+}
+
+impl<K, A> From<local_chain::ChangeSet> for ChangeSet<K, A> {
+    fn from(chain_changeset: local_chain::ChangeSet) -> Self {
+        Self {
+            chain_changeset,
+            ..Default::default()
+        }
+    }
+}
 
 /// The address index selection strategy to use to derived an address from the wallet's external
 /// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
@@ -182,6 +249,14 @@ where
     }
 }
 
+#[derive(Debug)]
+pub enum InsertTxError {
+    ConfirmationHeightCannotBeGreaterThanTip {
+        tip_height: Option<u32>,
+        tx_height: u32,
+    },
+}
+
 #[cfg(feature = "std")]
 impl<P: core::fmt::Display + core::fmt::Debug> std::error::Error for NewError<P> {}
 
@@ -195,15 +270,17 @@ impl<D> Wallet<D> {
         network: Network,
     ) -> Result<Self, NewError<D::LoadError>>
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
         let secp = Secp256k1::new();
+        let mut chain = LocalChain::default();
+        let mut indexed_graph =
+            IndexedTxGraph::<ConfirmationTimeAnchor, KeychainTxOutIndex<KeychainKind>>::default();
 
-        let mut keychain_tracker = KeychainTracker::default();
         let (descriptor, keymap) = into_wallet_descriptor_checked(descriptor, &secp, network)
             .map_err(NewError::Descriptor)?;
-        keychain_tracker
-            .txout_index
+        indexed_graph
+            .index
             .add_keychain(KeychainKind::External, descriptor.clone());
         let signers = Arc::new(SignersContainer::build(keymap, &descriptor, &secp));
         let change_signers = match change_descriptor {
@@ -218,8 +295,8 @@ impl<D> Wallet<D> {
                     &secp,
                 ));
 
-                keychain_tracker
-                    .txout_index
+                indexed_graph
+                    .index
                     .add_keychain(KeychainKind::Internal, change_descriptor);
 
                 change_signers
@@ -227,18 +304,20 @@ impl<D> Wallet<D> {
             None => Arc::new(SignersContainer::new()),
         };
 
-        db.load_into_keychain_tracker(&mut keychain_tracker)
-            .map_err(NewError::Persist)?;
+        let changeset = db.load_from_persistence().map_err(NewError::Persist)?;
+        chain.apply_changeset(changeset.chain_changeset);
+        indexed_graph.apply_additions(changeset.indexed_additions);
 
-        let persist = persist::Persist::new(db);
+        let persist = Persist::new(db);
 
         Ok(Wallet {
             signers,
             change_signers,
             network,
+            chain,
+            indexed_graph,
             persist,
             secp,
-            keychain_tracker,
         })
     }
 
@@ -249,7 +328,7 @@ impl<D> Wallet<D> {
 
     /// Iterator over all keychains in this wallet
     pub fn keychains(&self) -> &BTreeMap<KeychainKind, ExtendedDescriptor> {
-        self.keychain_tracker.txout_index.keychains()
+        self.indexed_graph.index.keychains()
     }
 
     /// Return a derived address using the external descriptor, see [`AddressIndex`] for
@@ -257,7 +336,7 @@ impl<D> Wallet<D> {
     /// (i.e. does not end with /*) then the same address will always be returned for any [`AddressIndex`].
     pub fn get_address(&mut self, address_index: AddressIndex) -> AddressInfo
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
         self._get_address(address_index, KeychainKind::External)
     }
@@ -271,17 +350,17 @@ impl<D> Wallet<D> {
     /// be returned for any [`AddressIndex`].
     pub fn get_internal_address(&mut self, address_index: AddressIndex) -> AddressInfo
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
         self._get_address(address_index, KeychainKind::Internal)
     }
 
     fn _get_address(&mut self, address_index: AddressIndex, keychain: KeychainKind) -> AddressInfo
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
         let keychain = self.map_keychain(keychain);
-        let txout_index = &mut self.keychain_tracker.txout_index;
+        let txout_index = &mut self.indexed_graph.index;
         let (index, spk) = match address_index {
             AddressIndex::New => {
                 let ((index, spk), changeset) = txout_index.reveal_next_spk(&keychain);
@@ -320,42 +399,36 @@ impl<D> Wallet<D> {
 
     /// Return whether or not a `script` is part of this wallet (either internal or external)
     pub fn is_mine(&self, script: &Script) -> bool {
-        self.keychain_tracker
-            .txout_index
-            .index_of_spk(script)
-            .is_some()
+        self.indexed_graph.index.index_of_spk(script).is_some()
     }
 
     /// Finds how the wallet derived the script pubkey `spk`.
     ///
     /// Will only return `Some(_)` if the wallet has given out the spk.
     pub fn derivation_of_spk(&self, spk: &Script) -> Option<(KeychainKind, u32)> {
-        self.keychain_tracker.txout_index.index_of_spk(spk).copied()
+        self.indexed_graph.index.index_of_spk(spk).copied()
     }
 
     /// Return the list of unspent outputs of this wallet
-    pub fn list_unspent(&self) -> Vec<LocalUtxo> {
-        self.keychain_tracker
-            .full_utxos()
-            .map(|(&(keychain, derivation_index), utxo)| LocalUtxo {
-                outpoint: utxo.outpoint,
-                txout: utxo.txout,
-                keychain,
-                is_spent: false,
-                derivation_index,
-                confirmation_time: utxo.chain_position,
-            })
-            .collect()
+    pub fn list_unspent(&self) -> impl Iterator<Item = LocalUtxo> + '_ {
+        self.indexed_graph
+            .graph()
+            .filter_chain_unspents(
+                &self.chain,
+                self.chain.tip().unwrap_or_default(),
+                self.indexed_graph.index.outpoints().iter().cloned(),
+            )
+            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
     }
 
     /// Get all the checkpoints the wallet is currently storing indexed by height.
     pub fn checkpoints(&self) -> &BTreeMap<u32, BlockHash> {
-        self.keychain_tracker.chain().checkpoints()
+        self.chain.blocks()
     }
 
     /// Returns the latest checkpoint.
     pub fn latest_checkpoint(&self) -> Option<BlockId> {
-        self.keychain_tracker.chain().latest_checkpoint()
+        self.chain.tip()
     }
 
     /// Returns a iterators of all the script pubkeys for the `Internal` and External` variants in `KeychainKind`.
@@ -369,7 +442,7 @@ impl<D> Wallet<D> {
     pub fn spks_of_all_keychains(
         &self,
     ) -> BTreeMap<KeychainKind, impl Iterator<Item = (u32, Script)> + Clone> {
-        self.keychain_tracker.txout_index.spks_of_all_keychains()
+        self.indexed_graph.index.spks_of_all_keychains()
     }
 
     /// Gets an iterator over all the script pubkeys in a single keychain.
@@ -381,30 +454,22 @@ impl<D> Wallet<D> {
         &self,
         keychain: KeychainKind,
     ) -> impl Iterator<Item = (u32, Script)> + Clone {
-        self.keychain_tracker
-            .txout_index
-            .spks_of_keychain(&keychain)
+        self.indexed_graph.index.spks_of_keychain(&keychain)
     }
 
     /// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
     /// wallet's database.
     pub fn get_utxo(&self, op: OutPoint) -> Option<LocalUtxo> {
-        self.keychain_tracker
-            .full_utxos()
-            .find_map(|(&(keychain, derivation_index), txo)| {
-                if op == txo.outpoint {
-                    Some(LocalUtxo {
-                        outpoint: txo.outpoint,
-                        txout: txo.txout,
-                        keychain,
-                        is_spent: txo.spent_by.is_none(),
-                        derivation_index,
-                        confirmation_time: txo.chain_position,
-                    })
-                } else {
-                    None
-                }
-            })
+        let (&spk_i, _) = self.indexed_graph.index.txout(op)?;
+        self.indexed_graph
+            .graph()
+            .filter_chain_unspents(
+                &self.chain,
+                self.chain.tip().unwrap_or_default(),
+                core::iter::once((spk_i, op)),
+            )
+            .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
+            .next()
     }
 
     /// Return a single transactions made and received by the wallet
@@ -412,54 +477,22 @@ impl<D> Wallet<D> {
     /// Optionally fill the [`TransactionDetails::transaction`] field with the raw transaction if
     /// `include_raw` is `true`.
     pub fn get_tx(&self, txid: Txid, include_raw: bool) -> Option<TransactionDetails> {
-        let (&confirmation_time, tx) = self.keychain_tracker.chain_graph().get_tx_in_chain(txid)?;
-        let graph = self.keychain_tracker.graph();
-        let txout_index = &self.keychain_tracker.txout_index;
-
-        let received = tx
-            .output
-            .iter()
-            .map(|txout| {
-                if txout_index.index_of_spk(&txout.script_pubkey).is_some() {
-                    txout.value
-                } else {
-                    0
-                }
-            })
-            .sum();
-
-        let sent = tx
-            .input
-            .iter()
-            .map(|txin| {
-                if let Some((_, txout)) = txout_index.txout(txin.previous_output) {
-                    txout.value
-                } else {
-                    0
-                }
-            })
-            .sum();
-
-        let inputs = tx
-            .input
-            .iter()
-            .map(|txin| {
-                graph
-                    .get_txout(txin.previous_output)
-                    .map(|txout| txout.value)
-            })
-            .sum::<Option<u64>>();
-        let outputs = tx.output.iter().map(|txout| txout.value).sum();
-        let fee = inputs.map(|inputs| inputs.saturating_sub(outputs));
+        let graph = self.indexed_graph.graph();
+
+        let canonical_tx = CanonicalTx {
+            observed_as: graph.get_chain_position(
+                &self.chain,
+                self.chain.tip().unwrap_or_default(),
+                txid,
+            )?,
+            node: graph.get_tx_node(txid)?,
+        };
 
-        Some(TransactionDetails {
-            transaction: if include_raw { Some(tx.clone()) } else { None },
-            txid,
-            received,
-            sent,
-            fee,
-            confirmation_time,
-        })
+        Some(new_tx_details(
+            &self.indexed_graph,
+            canonical_tx,
+            include_raw,
+        ))
     }
 
     /// Add a new checkpoint to the wallet's internal view of the chain.
@@ -472,10 +505,15 @@ impl<D> Wallet<D> {
     pub fn insert_checkpoint(
         &mut self,
         block_id: BlockId,
-    ) -> Result<bool, sparse_chain::InsertCheckpointError> {
-        let changeset = self.keychain_tracker.insert_checkpoint(block_id)?;
-        let changed = changeset.is_empty();
-        self.persist.stage(changeset);
+    ) -> Result<bool, local_chain::InsertBlockNotMatchingError>
+    where
+        D: PersistBackend<ChangeSet>,
+    {
+        let changeset = self.chain.insert_block(block_id)?;
+        let changed = !changeset.is_empty();
+        if changed {
+            self.persist.stage(changeset.into());
+        }
         Ok(changed)
     }
 
@@ -497,41 +535,80 @@ impl<D> Wallet<D> {
         &mut self,
         tx: Transaction,
         position: ConfirmationTime,
-    ) -> Result<bool, chain_graph::InsertTxError<ConfirmationTime>> {
-        let changeset = self.keychain_tracker.insert_tx(tx, position)?;
-        let changed = changeset.is_empty();
-        self.persist.stage(changeset);
+        seen_at: Option<u64>,
+    ) -> Result<bool, InsertTxError>
+    where
+        D: PersistBackend<ChangeSet>,
+    {
+        let tip = self.chain.tip();
+
+        if let ConfirmationTime::Confirmed { height, .. } = position {
+            let tip_height = tip.map(|b| b.height);
+            if Some(height) > tip_height {
+                return Err(InsertTxError::ConfirmationHeightCannotBeGreaterThanTip {
+                    tip_height,
+                    tx_height: height,
+                });
+            }
+        }
+
+        let anchor = match position {
+            ConfirmationTime::Confirmed { height, time } => {
+                let tip_height = tip.map(|b| b.height);
+                if Some(height) > tip_height {
+                    return Err(InsertTxError::ConfirmationHeightCannotBeGreaterThanTip {
+                        tip_height,
+                        tx_height: height,
+                    });
+                }
+                Some(ConfirmationTimeAnchor {
+                    anchor_block: tip.expect("already checked if tip_height > height"),
+                    confirmation_height: height,
+                    confirmation_time: time,
+                })
+            }
+            ConfirmationTime::Unconfirmed => None,
+        };
+
+        let changeset: ChangeSet = self.indexed_graph.insert_tx(&tx, anchor, seen_at).into();
+        let changed = !changeset.is_empty();
+        if changed {
+            self.persist.stage(changeset);
+        }
         Ok(changed)
     }
 
     #[deprecated(note = "use Wallet::transactions instead")]
     /// Deprecated. use `Wallet::transactions` instead.
-    pub fn list_transactions(&self, include_raw: bool) -> Vec<TransactionDetails> {
-        self.keychain_tracker
-            .chain()
-            .txids()
-            .map(|&(_, txid)| self.get_tx(txid, include_raw).expect("must exist"))
-            .collect()
+    pub fn list_transactions(
+        &self,
+        include_raw: bool,
+    ) -> impl Iterator<Item = TransactionDetails> + '_ {
+        self.indexed_graph
+            .graph()
+            .list_chain_txs(&self.chain, self.chain.tip().unwrap_or_default())
+            .map(move |canonical_tx| new_tx_details(&self.indexed_graph, canonical_tx, include_raw))
     }
 
     /// Iterate over the transactions in the wallet in order of ascending confirmation time with
     /// unconfirmed transactions last.
     pub fn transactions(
         &self,
-    ) -> impl DoubleEndedIterator<Item = (ConfirmationTime, &Transaction)> + '_ {
-        self.keychain_tracker
-            .chain_graph()
-            .transactions_in_chain()
-            .map(|(pos, tx)| (*pos, tx))
+    ) -> impl Iterator<Item = CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>> + '_ {
+        self.indexed_graph
+            .graph()
+            .list_chain_txs(&self.chain, self.chain.tip().unwrap_or_default())
     }
 
     /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature
     /// values.
     pub fn get_balance(&self) -> Balance {
-        self.keychain_tracker.balance(|keychain| match keychain {
-            KeychainKind::External => false,
-            KeychainKind::Internal => true,
-        })
+        self.indexed_graph.graph().balance(
+            &self.chain,
+            self.chain.tip().unwrap_or_default(),
+            self.indexed_graph.index.outpoints().iter().cloned(),
+            |&(k, _), _| k == KeychainKind::Internal,
+        )
     }
 
     /// Add an external signer
@@ -613,17 +690,17 @@ impl<D> Wallet<D> {
         params: TxParams,
     ) -> Result<(psbt::PartiallySignedTransaction, TransactionDetails), Error>
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
         let external_descriptor = self
-            .keychain_tracker
-            .txout_index
+            .indexed_graph
+            .index
             .keychains()
             .get(&KeychainKind::External)
             .expect("must exist");
         let internal_descriptor = self
-            .keychain_tracker
-            .txout_index
+            .indexed_graph
+            .index
             .keychains()
             .get(&KeychainKind::Internal);
 
@@ -700,9 +777,8 @@ impl<D> Wallet<D> {
         let current_height = match params.current_height {
             // If they didn't tell us the current height, we assume it's the latest sync height.
             None => self
-                .keychain_tracker
-                .chain()
-                .latest_checkpoint()
+                .chain
+                .tip()
                 .and_then(|cp| cp.height.into())
                 .map(|height| LockTime::from_height(height).expect("Invalid height")),
             h => h,
@@ -874,14 +950,10 @@ impl<D> Wallet<D> {
             Some(ref drain_recipient) => drain_recipient.clone(),
             None => {
                 let change_keychain = self.map_keychain(KeychainKind::Internal);
-                let ((index, spk), changeset) = self
-                    .keychain_tracker
-                    .txout_index
-                    .next_unused_spk(&change_keychain);
+                let ((index, spk), changeset) =
+                    self.indexed_graph.index.next_unused_spk(&change_keychain);
                 let spk = spk.clone();
-                self.keychain_tracker
-                    .txout_index
-                    .mark_used(&change_keychain, index);
+                self.indexed_graph.index.mark_used(&change_keychain, index);
                 self.persist.stage(changeset.into());
                 self.persist.commit().expect("TODO");
                 spk
@@ -1019,16 +1091,21 @@ impl<D> Wallet<D> {
         &mut self,
         txid: Txid,
     ) -> Result<TxBuilder<'_, D, DefaultCoinSelectionAlgorithm, BumpFee>, Error> {
-        let graph = self.keychain_tracker.graph();
-        let txout_index = &self.keychain_tracker.txout_index;
-        let tx_and_height = self.keychain_tracker.chain_graph().get_tx_in_chain(txid);
-        let mut tx = match tx_and_height {
-            None => return Err(Error::TransactionNotFound),
-            Some((ConfirmationTime::Confirmed { .. }, _tx)) => {
-                return Err(Error::TransactionConfirmed)
-            }
-            Some((_, tx)) => tx.clone(),
-        };
+        let graph = self.indexed_graph.graph();
+        let txout_index = &self.indexed_graph.index;
+        let chain_tip = self.chain.tip().unwrap_or_default();
+
+        let mut tx = graph
+            .get_tx(txid)
+            .ok_or(Error::TransactionNotFound)?
+            .clone();
+
+        let pos = graph
+            .get_chain_position(&self.chain, chain_tip, txid)
+            .ok_or(Error::TransactionNotFound)?;
+        if let ObservedAs::Confirmed(_) = pos {
+            return Err(Error::TransactionConfirmed);
+        }
 
         if !tx
             .input
@@ -1051,13 +1128,17 @@ impl<D> Wallet<D> {
         let original_utxos = original_txin
             .iter()
             .map(|txin| -> Result<_, Error> {
-                let (&confirmation_time, prev_tx) = self
-                    .keychain_tracker
-                    .chain_graph()
-                    .get_tx_in_chain(txin.previous_output.txid)
+                let prev_tx = graph
+                    .get_tx(txin.previous_output.txid)
                     .ok_or(Error::UnknownUtxo)?;
                 let txout = &prev_tx.output[txin.previous_output.vout as usize];
 
+                let confirmation_time: ConfirmationTime = graph
+                    .get_chain_position(&self.chain, chain_tip, txin.previous_output.txid)
+                    .ok_or(Error::UnknownUtxo)?
+                    .cloned()
+                    .into();
+
                 let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
                     Some(&(keychain, derivation_index)) => {
                         let satisfaction_weight = self
@@ -1231,7 +1312,7 @@ impl<D> Wallet<D> {
     ///
     /// This can be used to build a watch-only version of a wallet
     pub fn public_descriptor(&self, keychain: KeychainKind) -> Option<&ExtendedDescriptor> {
-        self.keychain_tracker.txout_index.keychains().get(&keychain)
+        self.indexed_graph.index.keychains().get(&keychain)
     }
 
     /// Finalize a PSBT, i.e., for each input determine if sufficient data is available to pass
@@ -1247,6 +1328,8 @@ impl<D> Wallet<D> {
         psbt: &mut psbt::PartiallySignedTransaction,
         sign_options: SignOptions,
     ) -> Result<bool, Error> {
+        let chain_tip = self.chain.tip().unwrap_or_default();
+
         let tx = &psbt.unsigned_tx;
         let mut finished = true;
 
@@ -1259,19 +1342,16 @@ impl<D> Wallet<D> {
                 continue;
             }
             let confirmation_height = self
-                .keychain_tracker
-                .chain()
-                .tx_position(input.previous_output.txid)
-                .map(|conftime| match conftime {
-                    &ConfirmationTime::Confirmed { height, .. } => height,
-                    ConfirmationTime::Unconfirmed => u32::MAX,
+                .indexed_graph
+                .graph()
+                .get_chain_position(&self.chain, chain_tip, input.previous_output.txid)
+                .map(|observed_as| match observed_as {
+                    ObservedAs::Confirmed(a) => a.confirmation_height,
+                    ObservedAs::Unconfirmed(_) => u32::MAX,
                 });
-            let last_sync_height = self
-                .keychain_tracker
-                .chain()
-                .latest_checkpoint()
-                .map(|block_id| block_id.height);
-            let current_height = sign_options.assume_height.or(last_sync_height);
+            let current_height = sign_options
+                .assume_height
+                .or(self.chain.tip().map(|b| b.height));
 
             debug!(
                 "Input #{} - {}, using `confirmation_height` = {:?}, `current_height` = {:?}",
@@ -1288,8 +1368,8 @@ impl<D> Wallet<D> {
                 .get_utxo_for(n)
                 .and_then(|txout| self.get_descriptor_for_txout(&txout))
                 .or_else(|| {
-                    self.keychain_tracker
-                        .txout_index
+                    self.indexed_graph
+                        .index
                         .keychains()
                         .iter()
                         .find_map(|(_, desc)| {
@@ -1347,14 +1427,12 @@ impl<D> Wallet<D> {
     /// The derivation index of this wallet. It will return `None` if it has not derived any addresses.
     /// Otherwise, it will return the index of the highest address it has derived.
     pub fn derivation_index(&self, keychain: KeychainKind) -> Option<u32> {
-        self.keychain_tracker
-            .txout_index
-            .last_revealed_index(&keychain)
+        self.indexed_graph.index.last_revealed_index(&keychain)
     }
 
     /// The index of the next address that you would get if you were to ask the wallet for a new address
     pub fn next_derivation_index(&self, keychain: KeychainKind) -> u32 {
-        self.keychain_tracker.txout_index.next_index(&keychain).0
+        self.indexed_graph.index.next_index(&keychain).0
     }
 
     /// Informs the wallet that you no longer intend to broadcast a tx that was built from it.
@@ -1362,7 +1440,7 @@ impl<D> Wallet<D> {
     /// This frees up the change address used when creating the tx for use in future transactions.
     // TODO: Make this free up reserved utxos when that's implemented
     pub fn cancel_tx(&mut self, tx: &Transaction) {
-        let txout_index = &mut self.keychain_tracker.txout_index;
+        let txout_index = &mut self.indexed_graph.index;
         for txout in &tx.output {
             if let Some(&(keychain, index)) = txout_index.index_of_spk(&txout.script_pubkey) {
                 // NOTE: unmark_used will **not** make something unused if it has actually been used
@@ -1384,8 +1462,8 @@ impl<D> Wallet<D> {
 
     fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
         let &(keychain, child) = self
-            .keychain_tracker
-            .txout_index
+            .indexed_graph
+            .index
             .index_of_spk(&txout.script_pubkey)?;
         let descriptor = self.get_descriptor_for_keychain(keychain);
         Some(descriptor.at_derivation_index(child))
@@ -1393,7 +1471,6 @@ impl<D> Wallet<D> {
 
     fn get_available_utxos(&self) -> Vec<(LocalUtxo, usize)> {
         self.list_unspent()
-            .into_iter()
             .map(|utxo| {
                 let keychain = utxo.keychain;
                 (
@@ -1419,6 +1496,7 @@ impl<D> Wallet<D> {
         must_only_use_confirmed_tx: bool,
         current_height: Option<u32>,
     ) -> (Vec<WeightedUtxo>, Vec<WeightedUtxo>) {
+        let chain_tip = self.chain.tip().unwrap_or_default();
         //    must_spend <- manually selected utxos
         //    may_spend  <- all other available utxos
         let mut may_spend = self.get_available_utxos();
@@ -1438,39 +1516,43 @@ impl<D> Wallet<D> {
 
         let satisfies_confirmed = may_spend
             .iter()
-            .map(|u| {
+            .map(|u| -> bool {
                 let txid = u.0.outpoint.txid;
-                let tx = self.keychain_tracker.chain_graph().get_tx_in_chain(txid);
-                match tx {
-                    // We don't have the tx in the db for some reason,
-                    // so we can't know for sure if it's mature or not.
-                    // We prefer not to spend it.
-                    None => false,
-                    Some((confirmation_time, tx)) => {
-                        // Whether the UTXO is mature and, if needed, confirmed
-                        let mut spendable = true;
-                        if must_only_use_confirmed_tx && !confirmation_time.is_confirmed() {
-                            return false;
-                        }
-                        if tx.is_coin_base() {
-                            debug_assert!(
-                                confirmation_time.is_confirmed(),
-                                "coinbase must always be confirmed"
-                            );
-                            if let Some(current_height) = current_height {
-                                match confirmation_time {
-                                    ConfirmationTime::Confirmed { height, .. } => {
-                                        // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
-                                        spendable &= (current_height.saturating_sub(*height))
-                                            >= COINBASE_MATURITY;
-                                    }
-                                    ConfirmationTime::Unconfirmed => spendable = false,
-                                }
+                let tx = match self.indexed_graph.graph().get_tx(txid) {
+                    Some(tx) => tx,
+                    None => return false,
+                };
+                let confirmation_time: ConfirmationTime = match self
+                    .indexed_graph
+                    .graph()
+                    .get_chain_position(&self.chain, chain_tip, txid)
+                {
+                    Some(observed_as) => observed_as.cloned().into(),
+                    None => return false,
+                };
+
+                // Whether the UTXO is mature and, if needed, confirmed
+                let mut spendable = true;
+                if must_only_use_confirmed_tx && !confirmation_time.is_confirmed() {
+                    return false;
+                }
+                if tx.is_coin_base() {
+                    debug_assert!(
+                        confirmation_time.is_confirmed(),
+                        "coinbase must always be confirmed"
+                    );
+                    if let Some(current_height) = current_height {
+                        match confirmation_time {
+                            ConfirmationTime::Confirmed { height, .. } => {
+                                // https://github.com/bitcoin/bitcoin/blob/c5e67be03bb06a5d7885c55db1f016fbf2333fe3/src/validation.cpp#L373-L375
+                                spendable &=
+                                    (current_height.saturating_sub(height)) >= COINBASE_MATURITY;
                             }
+                            ConfirmationTime::Unconfirmed => spendable = false,
                         }
-                        spendable
                     }
                 }
+                spendable
             })
             .collect::<Vec<_>>();
 
@@ -1590,8 +1672,8 @@ impl<D> Wallet<D> {
         // Try to find the prev_script in our db to figure out if this is internal or external,
         // and the derivation index
         let &(keychain, child) = self
-            .keychain_tracker
-            .txout_index
+            .indexed_graph
+            .index
             .index_of_spk(&utxo.txout.script_pubkey)
             .ok_or(Error::UnknownUtxo)?;
 
@@ -1608,7 +1690,7 @@ impl<D> Wallet<D> {
             .map_err(MiniscriptPsbtError::Conversion)?;
 
         let prev_output = utxo.outpoint;
-        if let Some(prev_tx) = self.keychain_tracker.graph().get_tx(prev_output.txid) {
+        if let Some(prev_tx) = self.indexed_graph.graph().get_tx(prev_output.txid) {
             if desc.is_witness() || desc.is_taproot() {
                 psbt_input.witness_utxo = Some(prev_tx.output[prev_output.vout as usize].clone());
             }
@@ -1641,10 +1723,8 @@ impl<D> Wallet<D> {
 
         // Try to figure out the keychain and derivation for every input and output
         for (is_input, index, out) in utxos.into_iter() {
-            if let Some(&(keychain, child)) = self
-                .keychain_tracker
-                .txout_index
-                .index_of_spk(&out.script_pubkey)
+            if let Some(&(keychain, child)) =
+                self.indexed_graph.index.index_of_spk(&out.script_pubkey)
             {
                 debug!(
                     "Found descriptor for input #{} {:?}/{}",
@@ -1685,52 +1765,62 @@ impl<D> Wallet<D> {
     /// transactions related to your wallet into it.
     ///
     /// [`commit`]: Self::commit
-    pub fn apply_update(&mut self, update: Update) -> Result<(), UpdateError>
+    pub fn apply_update(&mut self, update: Update) -> Result<bool, UpdateNotConnectedError>
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
-        let changeset = self.keychain_tracker.apply_update(update)?;
-        self.persist.stage(changeset);
-        Ok(())
+        let mut changeset: ChangeSet = self.chain.apply_update(update.chain)?.into();
+        let (_, derivation_additions) = self
+            .indexed_graph
+            .index
+            .reveal_to_target_multi(&update.keychain);
+        changeset.append(derivation_additions.into());
+        changeset.append(self.indexed_graph.apply_update(update.graph).into());
+
+        let changed = !changeset.is_empty();
+        if changed {
+            self.persist.stage(changeset);
+        }
+        Ok(changed)
     }
 
     /// Commits all curently [`staged`] changed to the persistence backend returning and error when this fails.
     ///
     /// [`staged`]: Self::staged
-    pub fn commit(&mut self) -> Result<(), D::WriteError>
+    pub fn commit(&mut self) -> Result<bool, D::WriteError>
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
-        self.persist.commit()
+        self.persist.commit().map(|c| c.is_some())
     }
 
     /// Returns the changes that will be staged with the next call to [`commit`].
     ///
     /// [`commit`]: Self::commit
-    pub fn staged(&self) -> &ChangeSet {
+    pub fn staged(&self) -> &ChangeSet
+    where
+        D: PersistBackend<ChangeSet>,
+    {
         self.persist.staged()
     }
 
     /// Get a reference to the inner [`TxGraph`](bdk_chain::tx_graph::TxGraph).
-    pub fn as_graph(&self) -> &bdk_chain::tx_graph::TxGraph {
-        self.keychain_tracker.graph()
+    pub fn as_graph(&self) -> &TxGraph<ConfirmationTimeAnchor> {
+        self.indexed_graph.graph()
     }
 
-    /// Get a reference to the inner [`ChainGraph`](bdk_chain::chain_graph::ChainGraph).
-    pub fn as_chain_graph(&self) -> &bdk_chain::chain_graph::ChainGraph<ConfirmationTime> {
-        self.keychain_tracker.chain_graph()
+    pub fn as_index(&self) -> &KeychainTxOutIndex<KeychainKind> {
+        &self.indexed_graph.index
     }
-}
 
-impl<D> AsRef<bdk_chain::tx_graph::TxGraph> for Wallet<D> {
-    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph {
-        self.keychain_tracker.graph()
+    pub fn as_chain(&self) -> &LocalChain {
+        &self.chain
     }
 }
 
-impl<D> AsRef<bdk_chain::chain_graph::ChainGraph<ConfirmationTime>> for Wallet<D> {
-    fn as_ref(&self) -> &bdk_chain::chain_graph::ChainGraph<ConfirmationTime> {
-        self.keychain_tracker.chain_graph()
+impl<D> AsRef<bdk_chain::tx_graph::TxGraph<ConfirmationTimeAnchor>> for Wallet<D> {
+    fn as_ref(&self) -> &bdk_chain::tx_graph::TxGraph<ConfirmationTimeAnchor> {
+        self.indexed_graph.graph()
     }
 }
 
@@ -1765,6 +1855,76 @@ where
     Ok(wallet_name)
 }
 
+fn new_local_utxo(
+    keychain: KeychainKind,
+    derivation_index: u32,
+    full_txo: FullTxOut<ObservedAs<ConfirmationTimeAnchor>>,
+) -> LocalUtxo {
+    LocalUtxo {
+        outpoint: full_txo.outpoint,
+        txout: full_txo.txout,
+        is_spent: full_txo.spent_by.is_some(),
+        confirmation_time: full_txo.chain_position.into(),
+        keychain,
+        derivation_index,
+    }
+}
+
+fn new_tx_details(
+    indexed_graph: &IndexedTxGraph<ConfirmationTimeAnchor, KeychainTxOutIndex<KeychainKind>>,
+    canonical_tx: CanonicalTx<'_, Transaction, ConfirmationTimeAnchor>,
+    include_raw: bool,
+) -> TransactionDetails {
+    let graph = indexed_graph.graph();
+    let index = &indexed_graph.index;
+    let tx = canonical_tx.node.tx;
+
+    let received = tx
+        .output
+        .iter()
+        .map(|txout| {
+            if index.index_of_spk(&txout.script_pubkey).is_some() {
+                txout.value
+            } else {
+                0
+            }
+        })
+        .sum();
+
+    let sent = tx
+        .input
+        .iter()
+        .map(|txin| {
+            if let Some((_, txout)) = index.txout(txin.previous_output) {
+                txout.value
+            } else {
+                0
+            }
+        })
+        .sum();
+
+    let inputs = tx
+        .input
+        .iter()
+        .map(|txin| {
+            graph
+                .get_txout(txin.previous_output)
+                .map(|txout| txout.value)
+        })
+        .sum::<Option<u64>>();
+    let outputs = tx.output.iter().map(|txout| txout.value).sum();
+    let fee = inputs.map(|inputs| inputs.saturating_sub(outputs));
+
+    TransactionDetails {
+        transaction: if include_raw { Some(tx.clone()) } else { None },
+        txid: canonical_tx.node.txid,
+        received,
+        sent,
+        fee,
+        confirmation_time: canonical_tx.observed_as.cloned().into(),
+    }
+}
+
 #[macro_export]
 #[doc(hidden)]
 /// Macro for getting a wallet for use in a doctest
@@ -1796,7 +1956,7 @@ macro_rules! doctest_wallet {
         let _ = wallet.insert_tx(tx.clone(), ConfirmationTime::Confirmed {
             height: 500,
             time: 50_000
-        });
+        }, None);
 
         wallet
     }}
index 6e812e595b74bf584d1375143e508436bb349816..5d10626098e8d5f1571ad9f4c669af6fdb7b9174 100644 (file)
@@ -39,7 +39,7 @@
 use crate::collections::BTreeMap;
 use crate::collections::HashSet;
 use alloc::{boxed::Box, rc::Rc, string::String, vec::Vec};
-use bdk_chain::ConfirmationTime;
+use bdk_chain::PersistBackend;
 use core::cell::RefCell;
 use core::marker::PhantomData;
 
@@ -47,7 +47,7 @@ use bitcoin::util::psbt::{self, PartiallySignedTransaction as Psbt};
 use bitcoin::{LockTime, OutPoint, Script, Sequence, Transaction};
 
 use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
-use super::persist;
+use super::ChangeSet;
 use crate::{
     types::{FeeRate, KeychainKind, LocalUtxo, WeightedUtxo},
     TransactionDetails,
@@ -529,7 +529,7 @@ impl<'a, D, Cs: CoinSelectionAlgorithm, Ctx: TxBuilderContext> TxBuilder<'a, D,
     /// [`BIP174`]: https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki
     pub fn finish(self) -> Result<(Psbt, TransactionDetails), Error>
     where
-        D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
+        D: PersistBackend<ChangeSet>,
     {
         self.wallet
             .borrow_mut()
index de94670321465d8372dd3557cb1ad63f5e6981eb..cbf74f249e68f90c8d595399e238ba19ba6fcdfd 100644 (file)
@@ -35,6 +35,7 @@ pub fn get_funded_wallet_with_change(
                 height: 1_000,
                 time: 100,
             },
+            None,
         )
         .unwrap();
 
index 94c5ad1e3472d91bed258b3911618de551dfe125..6291df1d9e42be9bea3a7413354c9c21c983d2a5 100644 (file)
@@ -44,6 +44,7 @@ fn receive_output(wallet: &mut Wallet, value: u64, height: TxHeight) -> OutPoint
                 },
                 TxHeight::Unconfirmed => ConfirmationTime::Unconfirmed,
             },
+            None,
         )
         .unwrap();
 
@@ -811,7 +812,7 @@ fn test_create_tx_add_utxo() {
         lock_time: PackedLockTime(0),
     };
     wallet
-        .insert_tx(small_output_tx.clone(), ConfirmationTime::Unconfirmed)
+        .insert_tx(small_output_tx.clone(), ConfirmationTime::Unconfirmed, None)
         .unwrap();
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
@@ -848,7 +849,7 @@ fn test_create_tx_manually_selected_insufficient() {
     };
 
     wallet
-        .insert_tx(small_output_tx.clone(), ConfirmationTime::Unconfirmed)
+        .insert_tx(small_output_tx.clone(), ConfirmationTime::Unconfirmed, None)
         .unwrap();
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
@@ -889,7 +890,9 @@ fn test_create_tx_policy_path_no_csv() {
             script_pubkey: wallet.get_address(New).script_pubkey(),
         }],
     };
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let external_policy = wallet.policies(KeychainKind::External).unwrap().unwrap();
     let root_id = external_policy.id;
@@ -972,7 +975,7 @@ fn test_add_foreign_utxo() {
         get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
-    let utxo = wallet2.list_unspent().remove(0);
+    let utxo = wallet2.list_unspent().next().expect("must take!");
     let foreign_utxo_satisfaction = wallet2
         .get_descriptor_for_keychain(KeychainKind::External)
         .max_satisfaction_weight()
@@ -1036,7 +1039,7 @@ fn test_add_foreign_utxo() {
 #[should_panic(expected = "Generic(\"Foreign utxo missing witness_utxo or non_witness_utxo\")")]
 fn test_add_foreign_utxo_invalid_psbt_input() {
     let (mut wallet, _) = get_funded_wallet(get_test_wpkh());
-    let outpoint = wallet.list_unspent()[0].outpoint;
+    let outpoint = wallet.list_unspent().next().expect("must exist").outpoint;
     let foreign_utxo_satisfaction = wallet
         .get_descriptor_for_keychain(KeychainKind::External)
         .max_satisfaction_weight()
@@ -1054,7 +1057,7 @@ fn test_add_foreign_utxo_where_outpoint_doesnt_match_psbt_input() {
     let (wallet2, txid2) =
         get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
 
-    let utxo2 = wallet2.list_unspent().remove(0);
+    let utxo2 = wallet2.list_unspent().next().unwrap();
     let tx1 = wallet1.get_tx(txid1, true).unwrap().transaction.unwrap();
     let tx2 = wallet2.get_tx(txid2, true).unwrap().transaction.unwrap();
 
@@ -1098,7 +1101,7 @@ fn test_add_foreign_utxo_only_witness_utxo() {
     let (wallet2, txid2) =
         get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
-    let utxo2 = wallet2.list_unspent().remove(0);
+    let utxo2 = wallet2.list_unspent().next().unwrap();
 
     let satisfaction_weight = wallet2
         .get_descriptor_for_keychain(KeychainKind::External)
@@ -1214,7 +1217,9 @@ fn test_bump_fee_irreplaceable_tx() {
 
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
     wallet.build_fee_bump(txid).unwrap().finish().unwrap();
 }
 
@@ -1237,6 +1242,7 @@ fn test_bump_fee_confirmed_tx() {
                 height: 42,
                 time: 42_000,
             },
+            None,
         )
         .unwrap();
 
@@ -1257,7 +1263,9 @@ fn test_bump_fee_low_fee_rate() {
     let tx = psbt.extract_tx();
     let txid = tx.txid();
 
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_rate(FeeRate::from_sat_per_vb(1.0));
@@ -1278,7 +1286,9 @@ fn test_bump_fee_low_abs() {
     let tx = psbt.extract_tx();
     let txid = tx.txid();
 
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_absolute(10);
@@ -1298,7 +1308,9 @@ fn test_bump_fee_zero_abs() {
 
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_absolute(0);
@@ -1316,7 +1328,9 @@ fn test_bump_fee_reduce_change() {
     let (psbt, original_details) = builder.finish().unwrap();
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_rate(FeeRate::from_sat_per_vb(2.5)).enable_rbf();
@@ -1401,7 +1415,9 @@ fn test_bump_fee_reduce_single_recipient() {
     let (psbt, original_details) = builder.finish().unwrap();
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder
@@ -1432,7 +1448,9 @@ fn test_bump_fee_absolute_reduce_single_recipient() {
     let (psbt, original_details) = builder.finish().unwrap();
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder
@@ -1471,6 +1489,7 @@ fn test_bump_fee_drain_wallet() {
                 height: wallet.latest_checkpoint().unwrap().height,
                 time: 42_000,
             },
+            None,
         )
         .unwrap();
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
@@ -1488,7 +1507,9 @@ fn test_bump_fee_drain_wallet() {
     let (psbt, original_details) = builder.finish().unwrap();
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
     assert_eq!(original_details.sent, 25_000);
 
     // for the new feerate, it should be enough to reduce the output, but since we specify
@@ -1523,7 +1544,17 @@ fn test_bump_fee_remove_output_manually_selected_only() {
         }],
     };
     wallet
-        .insert_tx(init_tx.clone(), wallet.transactions().last().unwrap().0)
+        .insert_tx(
+            init_tx.clone(),
+            wallet
+                .transactions()
+                .last()
+                .unwrap()
+                .observed_as
+                .cloned()
+                .into(),
+            None,
+        )
         .unwrap();
     let outpoint = OutPoint {
         txid: init_tx.txid(),
@@ -1540,7 +1571,9 @@ fn test_bump_fee_remove_output_manually_selected_only() {
     let (psbt, original_details) = builder.finish().unwrap();
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
     assert_eq!(original_details.sent, 25_000);
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
@@ -1562,9 +1595,14 @@ fn test_bump_fee_add_input() {
             value: 25_000,
         }],
     };
-    wallet
-        .insert_tx(init_tx, wallet.transactions().last().unwrap().0)
-        .unwrap();
+    let pos = wallet
+        .transactions()
+        .last()
+        .unwrap()
+        .observed_as
+        .cloned()
+        .into();
+    wallet.insert_tx(init_tx, pos, None).unwrap();
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
     let mut builder = wallet.build_tx().coin_selection(LargestFirstCoinSelection);
@@ -1574,7 +1612,9 @@ fn test_bump_fee_add_input() {
     let (psbt, original_details) = builder.finish().unwrap();
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_rate(FeeRate::from_sat_per_vb(50.0));
@@ -1618,7 +1658,9 @@ fn test_bump_fee_absolute_add_input() {
     let (psbt, original_details) = builder.finish().unwrap();
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_absolute(6_000);
@@ -1668,7 +1710,9 @@ fn test_bump_fee_no_change_add_input_and_change() {
 
     let tx = psbt.extract_tx();
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     // now bump the fees without using `allow_shrinking`. the wallet should add an
     // extra input and a change output, and leave the original output untouched
@@ -1724,7 +1768,9 @@ fn test_bump_fee_add_input_change_dust() {
     assert_eq!(tx.input.len(), 1);
     assert_eq!(tx.output.len(), 2);
     let txid = tx.txid();
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     // We set a fee high enough that during rbf we are forced to add
@@ -1784,7 +1830,7 @@ fn test_bump_fee_force_add_input() {
         txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
     }
     wallet
-        .insert_tx(tx.clone(), ConfirmationTime::Unconfirmed)
+        .insert_tx(tx.clone(), ConfirmationTime::Unconfirmed, None)
         .unwrap();
     // the new fee_rate is low enough that just reducing the change would be fine, but we force
     // the addition of an extra input with `add_utxo()`
@@ -1839,7 +1885,7 @@ fn test_bump_fee_absolute_force_add_input() {
         txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
     }
     wallet
-        .insert_tx(tx.clone(), ConfirmationTime::Unconfirmed)
+        .insert_tx(tx.clone(), ConfirmationTime::Unconfirmed, None)
         .unwrap();
 
     // the new fee_rate is low enough that just reducing the change would be fine, but we force
@@ -1899,7 +1945,9 @@ fn test_bump_fee_unconfirmed_inputs_only() {
     for txin in &mut tx.input {
         txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
     }
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder.fee_rate(FeeRate::from_sat_per_vb(25.0));
     builder.finish().unwrap();
@@ -1928,7 +1976,9 @@ fn test_bump_fee_unconfirmed_input() {
     for txin in &mut tx.input {
         txin.witness.push([0x00; P2WPKH_FAKE_WITNESS_SIZE]); // fake signature
     }
-    wallet.insert_tx(tx, ConfirmationTime::Unconfirmed).unwrap();
+    wallet
+        .insert_tx(tx, ConfirmationTime::Unconfirmed, None)
+        .unwrap();
 
     let mut builder = wallet.build_fee_bump(txid).unwrap();
     builder
@@ -2660,7 +2710,7 @@ fn test_taproot_foreign_utxo() {
     let (wallet2, _) = get_funded_wallet(get_test_tr_single_sig());
 
     let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap();
-    let utxo = wallet2.list_unspent().remove(0);
+    let utxo = wallet2.list_unspent().next().unwrap();
     let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap();
     let foreign_utxo_satisfaction = wallet2
         .get_descriptor_for_keychain(KeychainKind::External)
@@ -3022,6 +3072,7 @@ fn test_spend_coinbase() {
                 height: confirmation_height,
                 time: 30_000,
             },
+            None,
         )
         .unwrap();
 
index 3252febc2563d39f18464779f2622b5e0cc2dd03..9baa643e237211d54a06f1e659d00f5f9bd2db35 100644 (file)
@@ -137,6 +137,18 @@ impl ConfirmationTime {
     }
 }
 
+impl From<ObservedAs<ConfirmationTimeAnchor>> for ConfirmationTime {
+    fn from(observed_as: ObservedAs<ConfirmationTimeAnchor>) -> Self {
+        match observed_as {
+            ObservedAs::Confirmed(a) => Self::Confirmed {
+                height: a.confirmation_height,
+                time: a.confirmation_time,
+            },
+            ObservedAs::Unconfirmed(_) => Self::Unconfirmed,
+        }
+    }
+}
+
 /// A reference to a block in the canonical chain.
 #[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
 #[cfg_attr(
index 5145d593ba3027d9444c369cda3c81094bab46e5..7eb882642dec730f0f6ec733053cde20956edb2a 100644 (file)
-use std::{io::Write, str::FromStr};
+// use std::{io::Write, str::FromStr};
 
-use bdk::{
-    bitcoin::{Address, Network},
-    SignOptions, Wallet,
-};
-use bdk_electrum::{
-    electrum_client::{self, ElectrumApi},
-    ElectrumExt,
-};
-use bdk_file_store::KeychainStore;
+// use bdk::{
+//     bitcoin::{Address, Network},
+//     SignOptions, Wallet,
+// };
+// use bdk_electrum::{
+//     electrum_client::{self, ElectrumApi},
+//     ElectrumExt,
+// };
+// use bdk_file_store::KeychainStore;
 
-const SEND_AMOUNT: u64 = 5000;
-const STOP_GAP: usize = 50;
-const BATCH_SIZE: usize = 5;
+// const SEND_AMOUNT: u64 = 5000;
+// const STOP_GAP: usize = 50;
+// const BATCH_SIZE: usize = 5;
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
-    println!("Hello, world!");
+    todo!("update this example!");
+    // println!("Hello, world!");
 
-    let db_path = std::env::temp_dir().join("bdk-electrum-example");
-    let db = KeychainStore::new_from_path(db_path)?;
-    let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)";
-    let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)";
+    // let db_path = std::env::temp_dir().join("bdk-electrum-example");
+    // let db = KeychainStore::new_from_path(db_path)?;
+    // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)";
+    // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)";
 
-    let mut wallet = Wallet::new(
-        external_descriptor,
-        Some(internal_descriptor),
-        db,
-        Network::Testnet,
-    )?;
+    // let mut wallet = Wallet::new(
+    //     external_descriptor,
+    //     Some(internal_descriptor),
+    //     db,
+    //     Network::Testnet,
+    // )?;
 
-    let address = wallet.get_address(bdk::wallet::AddressIndex::New);
-    println!("Generated Address: {}", address);
+    // let address = wallet.get_address(bdk::wallet::AddressIndex::New);
+    // println!("Generated Address: {}", address);
 
-    let balance = wallet.get_balance();
-    println!("Wallet balance before syncing: {} sats", balance.total());
+    // let balance = wallet.get_balance();
+    // println!("Wallet balance before syncing: {} sats", balance.total());
 
-    print!("Syncing...");
-    // Scanning the chain...
-    let electrum_url = "ssl://electrum.blockstream.info:60002";
-    let client = electrum_client::Client::new(electrum_url)?;
-    let local_chain = wallet.checkpoints();
-    let spks = wallet
-        .spks_of_all_keychains()
-        .into_iter()
-        .map(|(k, spks)| {
-            let mut first = true;
-            (
-                k,
-                spks.inspect(move |(spk_i, _)| {
-                    if first {
-                        first = false;
-                        print!("\nScanning keychain [{:?}]:", k);
-                    }
-                    print!(" {}", spk_i);
-                    let _ = std::io::stdout().flush();
-                }),
-            )
-        })
-        .collect();
-    let electrum_update = client
-        .scan(
-            local_chain,
-            spks,
-            core::iter::empty(),
-            core::iter::empty(),
-            STOP_GAP,
-            BATCH_SIZE,
-        )?
-        .into_confirmation_time_update(&client)?;
-    println!();
-    let new_txs = client.batch_transaction_get(electrum_update.missing_full_txs(&wallet))?;
-    let update = electrum_update.into_keychain_scan(new_txs, &wallet)?;
-    wallet.apply_update(update)?;
-    wallet.commit()?;
+    // print!("Syncing...");
+    // // Scanning the chain...
+    // let electrum_url = "ssl://electrum.blockstream.info:60002";
+    // let client = electrum_client::Client::new(electrum_url)?;
+    // let local_chain = wallet.checkpoints();
+    // let spks = wallet
+    //     .spks_of_all_keychains()
+    //     .into_iter()
+    //     .map(|(k, spks)| {
+    //         let mut first = true;
+    //         (
+    //             k,
+    //             spks.inspect(move |(spk_i, _)| {
+    //                 if first {
+    //                     first = false;
+    //                     print!("\nScanning keychain [{:?}]:", k);
+    //                 }
+    //                 print!(" {}", spk_i);
+    //                 let _ = std::io::stdout().flush();
+    //             }),
+    //         )
+    //     })
+    //     .collect();
+    // let electrum_update = client
+    //     .scan(
+    //         local_chain,
+    //         spks,
+    //         core::iter::empty(),
+    //         core::iter::empty(),
+    //         STOP_GAP,
+    //         BATCH_SIZE,
+    //     )?
+    //     .into_confirmation_time_update(&client)?;
+    // println!();
+    // let new_txs = client.batch_transaction_get(electrum_update.missing_full_txs(&wallet))?;
+    // let update = electrum_update.into_keychain_scan(new_txs, &wallet)?;
+    // wallet.apply_update(update)?;
+    // wallet.commit()?;
 
-    let balance = wallet.get_balance();
-    println!("Wallet balance after syncing: {} sats", balance.total());
+    // let balance = wallet.get_balance();
+    // println!("Wallet balance after syncing: {} sats", balance.total());
 
-    if balance.total() < SEND_AMOUNT {
-        println!(
-            "Please send at least {} sats to the receiving address",
-            SEND_AMOUNT
-        );
-        std::process::exit(0);
-    }
+    // if balance.total() < SEND_AMOUNT {
+    //     println!(
+    //         "Please send at least {} sats to the receiving address",
+    //         SEND_AMOUNT
+    //     );
+    //     std::process::exit(0);
+    // }
 
-    let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?;
+    // let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?;
 
-    let mut tx_builder = wallet.build_tx();
-    tx_builder
-        .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
-        .enable_rbf();
+    // let mut tx_builder = wallet.build_tx();
+    // tx_builder
+    //     .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
+    //     .enable_rbf();
 
-    let (mut psbt, _) = tx_builder.finish()?;
-    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-    assert!(finalized);
+    // let (mut psbt, _) = tx_builder.finish()?;
+    // let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
+    // assert!(finalized);
 
-    let tx = psbt.extract_tx();
-    client.transaction_broadcast(&tx)?;
-    println!("Tx broadcasted! Txid: {}", tx.txid());
+    // let tx = psbt.extract_tx();
+    // client.transaction_broadcast(&tx)?;
+    // println!("Tx broadcasted! Txid: {}", tx.txid());
 
-    Ok(())
+    // Ok(())
 }
index d8eda32a29cb3c7a42732012b9764266bcbdb4d7..d9d07c7a5fc5049eb7a791b7d7aaa3c4b32c7f8f 100644 (file)
@@ -1,96 +1,97 @@
-use bdk::{
-    bitcoin::{Address, Network},
-    wallet::AddressIndex,
-    SignOptions, Wallet,
-};
-use bdk_esplora::esplora_client;
-use bdk_esplora::EsploraExt;
-use bdk_file_store::KeychainStore;
-use std::{io::Write, str::FromStr};
+// use bdk::{
+//     bitcoin::{Address, Network},
+//     wallet::AddressIndex,
+//     SignOptions, Wallet,
+// };
+// use bdk_esplora::esplora_client;
+// use bdk_esplora::EsploraExt;
+// use bdk_file_store::KeychainStore;
+// use std::{io::Write, str::FromStr};
 
-const SEND_AMOUNT: u64 = 5000;
-const STOP_GAP: usize = 50;
-const PARALLEL_REQUESTS: usize = 5;
+// const SEND_AMOUNT: u64 = 5000;
+// const STOP_GAP: usize = 50;
+// const PARALLEL_REQUESTS: usize = 5;
 
 fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let db_path = std::env::temp_dir().join("bdk-esplora-example");
-    let db = KeychainStore::new_from_path(db_path)?;
-    let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)";
-    let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)";
+    todo!("update this exampe!");
+    // let db_path = std::env::temp_dir().join("bdk-esplora-example");
+    // let db = KeychainStore::new_from_path(db_path)?;
+    // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)";
+    // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)";
 
-    let mut wallet = Wallet::new(
-        external_descriptor,
-        Some(internal_descriptor),
-        db,
-        Network::Testnet,
-    )?;
+    // let mut wallet = Wallet::new(
+    //     external_descriptor,
+    //     Some(internal_descriptor),
+    //     db,
+    //     Network::Testnet,
+    // )?;
 
-    let address = wallet.get_address(AddressIndex::New);
-    println!("Generated Address: {}", address);
+    // let address = wallet.get_address(AddressIndex::New);
+    // println!("Generated Address: {}", address);
 
-    let balance = wallet.get_balance();
-    println!("Wallet balance before syncing: {} sats", balance.total());
+    // let balance = wallet.get_balance();
+    // println!("Wallet balance before syncing: {} sats", balance.total());
 
-    print!("Syncing...");
-    // Scanning the chain...
-    let esplora_url = "https://mempool.space/testnet/api";
-    let client = esplora_client::Builder::new(esplora_url).build_blocking()?;
-    let checkpoints = wallet.checkpoints();
-    let spks = wallet
-        .spks_of_all_keychains()
-        .into_iter()
-        .map(|(k, spks)| {
-            let mut first = true;
-            (
-                k,
-                spks.inspect(move |(spk_i, _)| {
-                    if first {
-                        first = false;
-                        print!("\nScanning keychain [{:?}]:", k);
-                    }
-                    print!(" {}", spk_i);
-                    let _ = std::io::stdout().flush();
-                }),
-            )
-        })
-        .collect();
-    let update = client.scan(
-        checkpoints,
-        spks,
-        core::iter::empty(),
-        core::iter::empty(),
-        STOP_GAP,
-        PARALLEL_REQUESTS,
-    )?;
-    println!();
-    wallet.apply_update(update)?;
-    wallet.commit()?;
+    // print!("Syncing...");
+    // // Scanning the chain...
+    // let esplora_url = "https://mempool.space/testnet/api";
+    // let client = esplora_client::Builder::new(esplora_url).build_blocking()?;
+    // let checkpoints = wallet.checkpoints();
+    // let spks = wallet
+    //     .spks_of_all_keychains()
+    //     .into_iter()
+    //     .map(|(k, spks)| {
+    //         let mut first = true;
+    //         (
+    //             k,
+    //             spks.inspect(move |(spk_i, _)| {
+    //                 if first {
+    //                     first = false;
+    //                     print!("\nScanning keychain [{:?}]:", k);
+    //                 }
+    //                 print!(" {}", spk_i);
+    //                 let _ = std::io::stdout().flush();
+    //             }),
+    //         )
+    //     })
+    //     .collect();
+    // let update = client.scan(
+    //     checkpoints,
+    //     spks,
+    //     core::iter::empty(),
+    //     core::iter::empty(),
+    //     STOP_GAP,
+    //     PARALLEL_REQUESTS,
+    // )?;
+    // println!();
+    // wallet.apply_update(update)?;
+    // wallet.commit()?;
 
-    let balance = wallet.get_balance();
-    println!("Wallet balance after syncing: {} sats", balance.total());
+    // let balance = wallet.get_balance();
+    // println!("Wallet balance after syncing: {} sats", balance.total());
 
-    if balance.total() < SEND_AMOUNT {
-        println!(
-            "Please send at least {} sats to the receiving address",
-            SEND_AMOUNT
-        );
-        std::process::exit(0);
-    }
+    // if balance.total() < SEND_AMOUNT {
+    //     println!(
+    //         "Please send at least {} sats to the receiving address",
+    //         SEND_AMOUNT
+    //     );
+    //     std::process::exit(0);
+    // }
 
-    let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?;
+    // let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?;
 
-    let mut tx_builder = wallet.build_tx();
-    tx_builder
-        .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
-        .enable_rbf();
+    // let mut tx_builder = wallet.build_tx();
+    // tx_builder
+    //     .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
+    //     .enable_rbf();
 
-    let (mut psbt, _) = tx_builder.finish()?;
-    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-    assert!(finalized);
+    // let (mut psbt, _) = tx_builder.finish()?;
+    // let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
+    // assert!(finalized);
 
-    let tx = psbt.extract_tx();
-    client.broadcast(&tx)?;
-    println!("Tx broadcasted! Txid: {}", tx.txid());
+    // let tx = psbt.extract_tx();
+    // client.broadcast(&tx)?;
+    // println!("Tx broadcasted! Txid: {}", tx.txid());
 
-    Ok(())
+    // Ok(())
 }
index b78b09dfa08369b97544ddbea63180db4a68e36e..66cdf68917e1395dc6cf9ba2a63e8972bc8fed24 100644 (file)
-use std::{io::Write, str::FromStr};
+// use std::{io::Write, str::FromStr};
 
-use bdk::{
-    bitcoin::{Address, Network},
-    wallet::AddressIndex,
-    SignOptions, Wallet,
-};
-use bdk_esplora::{esplora_client, EsploraAsyncExt};
-use bdk_file_store::KeychainStore;
+// use bdk::{
+//     bitcoin::{Address, Network},
+//     wallet::AddressIndex,
+//     SignOptions, Wallet,
+// };
+// use bdk_esplora::{esplora_client, EsploraAsyncExt};
+// use bdk_file_store::KeychainStore;
 
-const SEND_AMOUNT: u64 = 5000;
-const STOP_GAP: usize = 50;
-const PARALLEL_REQUESTS: usize = 5;
+// const SEND_AMOUNT: u64 = 5000;
+// const STOP_GAP: usize = 50;
+// const PARALLEL_REQUESTS: usize = 5;
 
 #[tokio::main]
 async fn main() -> Result<(), Box<dyn std::error::Error>> {
-    let db_path = std::env::temp_dir().join("bdk-esplora-example");
-    let db = KeychainStore::new_from_path(db_path)?;
-    let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)";
-    let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)";
+    todo!("update this example!");
+    // let db_path = std::env::temp_dir().join("bdk-esplora-example");
+    // let db = KeychainStore::new_from_path(db_path)?;
+    // let external_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/0/*)";
+    // let internal_descriptor = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/0'/0'/1/*)";
 
-    let mut wallet = Wallet::new(
-        external_descriptor,
-        Some(internal_descriptor),
-        db,
-        Network::Testnet,
-    )?;
+    // let mut wallet = Wallet::new(
+    //     external_descriptor,
+    //     Some(internal_descriptor),
+    //     db,
+    //     Network::Testnet,
+    // )?;
 
-    let address = wallet.get_address(AddressIndex::New);
-    println!("Generated Address: {}", address);
+    // let address = wallet.get_address(AddressIndex::New);
+    // println!("Generated Address: {}", address);
 
-    let balance = wallet.get_balance();
-    println!("Wallet balance before syncing: {} sats", balance.total());
+    // let balance = wallet.get_balance();
+    // println!("Wallet balance before syncing: {} sats", balance.total());
 
-    print!("Syncing...");
-    // Scanning the blockchain
-    let esplora_url = "https://mempool.space/testnet/api";
-    let client = esplora_client::Builder::new(esplora_url).build_async()?;
-    let checkpoints = wallet.checkpoints();
-    let spks = wallet
-        .spks_of_all_keychains()
-        .into_iter()
-        .map(|(k, spks)| {
-            let mut first = true;
-            (
-                k,
-                spks.inspect(move |(spk_i, _)| {
-                    if first {
-                        first = false;
-                        print!("\nScanning keychain [{:?}]:", k);
-                    }
-                    print!(" {}", spk_i);
-                    let _ = std::io::stdout().flush();
-                }),
-            )
-        })
-        .collect();
-    let update = client
-        .scan(
-            checkpoints,
-            spks,
-            std::iter::empty(),
-            std::iter::empty(),
-            STOP_GAP,
-            PARALLEL_REQUESTS,
-        )
-        .await?;
-    println!();
-    wallet.apply_update(update)?;
-    wallet.commit()?;
+    // print!("Syncing...");
+    // // Scanning the blockchain
+    // let esplora_url = "https://mempool.space/testnet/api";
+    // let client = esplora_client::Builder::new(esplora_url).build_async()?;
+    // let checkpoints = wallet.checkpoints();
+    // let spks = wallet
+    //     .spks_of_all_keychains()
+    //     .into_iter()
+    //     .map(|(k, spks)| {
+    //         let mut first = true;
+    //         (
+    //             k,
+    //             spks.inspect(move |(spk_i, _)| {
+    //                 if first {
+    //                     first = false;
+    //                     print!("\nScanning keychain [{:?}]:", k);
+    //                 }
+    //                 print!(" {}", spk_i);
+    //                 let _ = std::io::stdout().flush();
+    //             }),
+    //         )
+    //     })
+    //     .collect();
+    // let update = client
+    //     .scan(
+    //         checkpoints,
+    //         spks,
+    //         std::iter::empty(),
+    //         std::iter::empty(),
+    //         STOP_GAP,
+    //         PARALLEL_REQUESTS,
+    //     )
+    //     .await?;
+    // println!();
+    // wallet.apply_update(update)?;
+    // wallet.commit()?;
 
-    let balance = wallet.get_balance();
-    println!("Wallet balance after syncing: {} sats", balance.total());
+    // let balance = wallet.get_balance();
+    // println!("Wallet balance after syncing: {} sats", balance.total());
 
-    if balance.total() < SEND_AMOUNT {
-        println!(
-            "Please send at least {} sats to the receiving address",
-            SEND_AMOUNT
-        );
-        std::process::exit(0);
-    }
+    // if balance.total() < SEND_AMOUNT {
+    //     println!(
+    //         "Please send at least {} sats to the receiving address",
+    //         SEND_AMOUNT
+    //     );
+    //     std::process::exit(0);
+    // }
 
-    let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?;
+    // let faucet_address = Address::from_str("mkHS9ne12qx9pS9VojpwU5xtRd4T7X7ZUt")?;
 
-    let mut tx_builder = wallet.build_tx();
-    tx_builder
-        .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
-        .enable_rbf();
+    // let mut tx_builder = wallet.build_tx();
+    // tx_builder
+    //     .add_recipient(faucet_address.script_pubkey(), SEND_AMOUNT)
+    //     .enable_rbf();
 
-    let (mut psbt, _) = tx_builder.finish()?;
-    let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
-    assert!(finalized);
+    // let (mut psbt, _) = tx_builder.finish()?;
+    // let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
+    // assert!(finalized);
 
-    let tx = psbt.extract_tx();
-    client.broadcast(&tx).await?;
-    println!("Tx broadcasted! Txid: {}", tx.txid());
+    // let tx = psbt.extract_tx();
+    // client.broadcast(&tx).await?;
+    // println!("Tx broadcasted! Txid: {}", tx.txid());
 
-    Ok(())
+    // Ok(())
 }