diff --git a/crates/bdk/src/wallet/mod.rs b/crates/bdk/src/wallet/mod.rs index b436d1fd9f..1d63003bdc 100644 --- a/crates/bdk/src/wallet/mod.rs +++ b/crates/bdk/src/wallet/mod.rs @@ -372,9 +372,8 @@ impl Wallet { pub fn list_unspent(&self) -> impl Iterator + '_ { self.indexed_graph .graph() - .filter_subchain_unspents( + .filter_chain_unspents( &self.chain, - self.chain.tip().map(|cp| cp.block_id()), self.indexed_graph.index.outpoints().iter().cloned(), ) .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo)) @@ -422,11 +421,7 @@ impl Wallet { let (&spk_i, _) = self.indexed_graph.index.txout(op)?; self.indexed_graph .graph() - .filter_subchain_unspents( - &self.chain, - self.chain.tip().map(|cp| cp.block_id()), - core::iter::once((spk_i, op)), - ) + .filter_chain_unspents(&self.chain, core::iter::once((spk_i, op))) .map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo)) .next() } @@ -595,11 +590,7 @@ impl Wallet { let graph = self.indexed_graph.graph(); Some(CanonicalTx { - chain_position: graph.get_subchain_position( - &self.chain, - self.chain.tip().map(|cp| cp.block_id()), - txid, - )?, + chain_position: graph.get_chain_position(&self.chain, txid)?, tx_node: graph.get_tx_node(txid)?, }) } @@ -685,17 +676,14 @@ impl Wallet { pub fn transactions( &self, ) -> impl Iterator> + '_ { - self.indexed_graph - .graph() - .list_subchain_txs(&self.chain, self.chain.tip().map(|cp| cp.block_id())) + self.indexed_graph.graph().list_chain_txs(&self.chain) } /// Return the balance, separated into available, trusted-pending, untrusted-pending and immature /// values. pub fn get_balance(&self) -> Balance { - self.indexed_graph.graph().subchain_balance( + self.indexed_graph.graph().balance( &self.chain, - self.chain.tip().map(|cp| cp.block_id()), self.indexed_graph.index.outpoints().iter().cloned(), |&(k, _), _| k == KeychainKind::Internal, ) @@ -1175,7 +1163,6 @@ impl Wallet { ) -> Result, Error> { let graph = self.indexed_graph.graph(); let txout_index = &self.indexed_graph.index; - let chain_tip = self.chain.tip().map(|cp| cp.block_id()); let mut tx = graph .get_tx(txid) @@ -1183,7 +1170,7 @@ impl Wallet { .clone(); let pos = graph - .get_subchain_position(&self.chain, chain_tip, txid) + .get_chain_position(&self.chain, txid) .ok_or(Error::TransactionNotFound)?; if let ChainPosition::Confirmed(_) = pos { return Err(Error::TransactionConfirmed); @@ -1215,7 +1202,7 @@ impl Wallet { let txout = &prev_tx.output[txin.previous_output.vout as usize]; let confirmation_time: ConfirmationTime = graph - .get_subchain_position(&self.chain, chain_tip, txin.previous_output.txid) + .get_chain_position(&self.chain, txin.previous_output.txid) .ok_or(Error::UnknownUtxo)? .cloned() .into(); @@ -1410,8 +1397,6 @@ impl Wallet { psbt: &mut psbt::PartiallySignedTransaction, sign_options: SignOptions, ) -> Result { - let chain_tip = self.chain.tip().map(|cp| cp.block_id()); - let tx = &psbt.unsigned_tx; let mut finished = true; @@ -1426,7 +1411,7 @@ impl Wallet { let confirmation_height = self .indexed_graph .graph() - .get_subchain_position(&self.chain, chain_tip, input.previous_output.txid) + .get_chain_position(&self.chain, input.previous_output.txid) .map(|chain_position| match chain_position { ChainPosition::Confirmed(a) => a.confirmation_height, ChainPosition::Unconfirmed(_) => u32::MAX, @@ -1579,7 +1564,6 @@ impl Wallet { must_only_use_confirmed_tx: bool, current_height: Option, ) -> (Vec, Vec) { - let chain_tip = self.chain.tip().map(|cp| cp.block_id()); // must_spend <- manually selected utxos // may_spend <- all other available utxos let mut may_spend = self.get_available_utxos(); @@ -1608,7 +1592,7 @@ impl Wallet { let confirmation_time: ConfirmationTime = match self .indexed_graph .graph() - .get_subchain_position(&self.chain, chain_tip, txid) + .get_chain_position(&self.chain, txid) { Some(chain_position) => chain_position.cloned().into(), None => return false, diff --git a/crates/chain/src/chain_oracle.rs b/crates/chain/src/chain_oracle.rs index 27f04fe4c3..2515adf8e1 100644 --- a/crates/chain/src/chain_oracle.rs +++ b/crates/chain/src/chain_oracle.rs @@ -14,10 +14,10 @@ pub trait ChainOracle { /// /// If `None` is returned, it means the implementation cannot determine whether `block` exists /// under `chain_tip`. - fn is_block_in_chain>( + fn is_block_in_chain( &self, block: BlockId, - chain_tip: Option, + chain_tip: Option<&BlockId>, ) -> Result, Self::Error>; /// Get the best chain's chain tip. diff --git a/crates/chain/src/local_chain.rs b/crates/chain/src/local_chain.rs index f38cc95131..d7e1ff650f 100644 --- a/crates/chain/src/local_chain.rs +++ b/crates/chain/src/local_chain.rs @@ -171,13 +171,13 @@ impl From> for LocalChain { impl ChainOracle for LocalChain { type Error = Infallible; - fn is_block_in_chain>( + fn is_block_in_chain( &self, block: BlockId, - chain_tip: Option, + chain_tip: Option<&BlockId>, ) -> Result, Self::Error> { - let chain_tip: BlockId = match chain_tip { - Some(x) => x.into(), + let chain_tip = match chain_tip { + Some(x) => x, None => return Ok(None), }; diff --git a/crates/chain/src/tx_graph.rs b/crates/chain/src/tx_graph.rs index 9951b35c7b..a3fd2663ef 100644 --- a/crates/chain/src/tx_graph.rs +++ b/crates/chain/src/tx_graph.rs @@ -644,6 +644,11 @@ impl TxGraph { /// Get the position of the transaction in `chain` with tip `chain_tip`. /// + /// This method is like [`try_get_chain_position`] except it restricts the + /// chain to a custom tip. The tip doesn't even need to be in the same chain as the tip. + /// You can use this to find information about a point in the past or on a fork if your + /// chain oracle supports that. + /// /// If the given transaction of `txid` does not exist in the chain of `chain_tip`, `None` is /// returned. /// @@ -652,11 +657,12 @@ impl TxGraph { /// An error will occur if the [`ChainOracle`] implementation (`chain`) fails. If the /// [`ChainOracle`] is infallible, [`get_subchain_position`] can be used instead. /// + /// [`try_get_chain_position`]: Self::try_get_chain_position /// [`get_subchain_position`]: Self::get_subchain_position - pub fn try_get_subchain_position + Clone, C: ChainOracle>( + pub fn try_get_subchain_position( &self, chain: &C, - chain_tip: Option, + chain_tip: Option, txid: Txid, ) -> Result>, C::Error> { let (tx_node, anchors, last_seen) = match self.txs.get(&txid) { @@ -665,7 +671,7 @@ impl TxGraph { }; for anchor in anchors { - match chain.is_block_in_chain(anchor.anchor_block(), chain_tip.clone())? { + match chain.is_block_in_chain(anchor.anchor_block(), chain_tip.as_ref())? { Some(true) => return Ok(Some(ChainPosition::Confirmed(anchor))), _ => continue, } @@ -685,7 +691,7 @@ impl TxGraph { // this tx cannot exist in the best chain for conflicting_tx in self.walk_conflicts(tx, |_, txid| self.get_tx_node(txid)) { for block in conflicting_tx.anchors.iter().map(A::anchor_block) { - if chain.is_block_in_chain(block, chain_tip.clone())? == Some(true) { + if chain.is_block_in_chain(block, chain_tip.as_ref())? == Some(true) { // conflicting tx is in best chain, so the current tx cannot be in best chain! return Ok(None); } @@ -722,10 +728,10 @@ impl TxGraph { /// This is the infallible version of [`try_get_subchain_position`]. /// /// [`try_get_subchain_position`]: Self::try_get_subchain_position - pub fn get_subchain_position + Clone, C: ChainOracle>( + pub fn get_subchain_position>( &self, chain: &C, - chain_tip: Option, + chain_tip: Option, txid: Txid, ) -> Option> { self.try_get_subchain_position(chain, chain_tip, txid) @@ -751,30 +757,33 @@ impl TxGraph { /// /// If no in-chain transaction spends `outpoint`, `None` will be returned. /// + /// This method is like [`try_get_chain_spend`] except it restricts the chain to a custom tip. + /// The tip doesn't even need to be in the same chain as the tip. You can use this to find information + /// about a point in the past or on a fork if your chain oracle supports that. + /// /// # Error /// /// An error will occur only if the [`ChainOracle`] implementation (`chain`) fails. /// /// If the [`ChainOracle`] is infallible, [`get_subchain_spend`] can be used instead. /// + /// [`try_get_chain_spend`]: Self::try_get_chain_spend /// [`get_subchain_spend`]: Self::get_subchain_spend - pub fn try_get_subchain_spend + Clone, C: ChainOracle>( + pub fn try_get_subchain_spend( &self, chain: &C, - chain_tip: Option, + chain_tip: Option, outpoint: OutPoint, ) -> Result, Txid)>, C::Error> { if self - .try_get_subchain_position(chain, chain_tip.clone(), outpoint.txid)? + .try_get_subchain_position(chain, chain_tip, outpoint.txid)? .is_none() { return Ok(None); } if let Some(spends) = self.spends.get(&outpoint) { for &txid in spends { - if let Some(observed_at) = - self.try_get_subchain_position(chain, chain_tip.clone(), txid)? - { + if let Some(observed_at) = self.try_get_subchain_position(chain, chain_tip, txid)? { return Ok(Some((observed_at, txid))); } } @@ -808,10 +817,10 @@ impl TxGraph { /// This is the infallible version of [`try_get_subchain_spend`] /// /// [`try_get_subchain_spend`]: Self::try_get_subchain_spend - pub fn get_subchain_spend + Clone, C: ChainOracle>( + pub fn get_subchain_spend>( &self, chain: &C, - chain_tip: Option, + chain_tip: Option, outpoint: OutPoint, ) -> Option<(ChainPosition<&A>, Txid)> { self.try_get_subchain_spend(chain, chain_tip, outpoint) @@ -835,6 +844,10 @@ impl TxGraph { /// List graph transactions that are in `chain` with `chain_tip`. /// + /// This method is like `try_list_chain_txs` except it restricts the chain to a custom tip. + /// The tip doesn't even need to be in the same chain as the tip. You can use this to find information + /// about a point in the past or on a fork if your chain oracle supports that. + /// /// Each transaction is represented as a [`CanonicalTx`] that contains where the transaction is /// observed in-chain, and the [`TxNode`]. /// @@ -845,14 +858,15 @@ impl TxGraph { /// /// If the [`ChainOracle`] is infallible, [`list_subchain_txs`] can be used instead. /// + /// [`try_list_chain_txs`]: Self::try_list_chain_txs /// [`list_subchain_txs`]: Self::list_subchain_txs - pub fn try_list_subchain_txs<'a, B: Into + Clone, C: ChainOracle + 'a>( + pub fn try_list_subchain_txs<'a, C: ChainOracle + 'a>( &'a self, chain: &'a C, - chain_tip: Option, + chain_tip: Option, ) -> impl Iterator, C::Error>> { self.full_txs().filter_map(move |tx| { - self.try_get_subchain_position(chain, chain_tip.clone(), tx.txid) + self.try_get_subchain_position(chain, chain_tip, tx.txid) .map(|v| { v.map(|observed_in| CanonicalTx { chain_position: observed_in, @@ -892,10 +906,10 @@ impl TxGraph { /// This is the infallible version of [`try_list_subchain_txs`]. /// /// [`try_list_subchain_txs`]: Self::try_list_subchain_txs - pub fn list_subchain_txs<'a, B: Into + Clone, C: ChainOracle + 'a>( + pub fn list_subchain_txs<'a, C: ChainOracle + 'a>( &'a self, chain: &'a C, - chain_tip: Option, + chain_tip: Option, ) -> impl Iterator> { self.try_list_subchain_txs(chain, chain_tip) .map(|r| r.expect("oracle is infallible")) @@ -917,6 +931,10 @@ impl TxGraph { /// Get a filtered list of outputs from the given `outpoints` that are in `chain` with /// `chain_tip`. /// + /// This method is like [`try_filter_chain_txouts`] except it restricts the chain to a custom tip. + /// The tip doesn't even need to be in the same chain as the tip. You can use this to find information + /// about a point in the past or on a fork if your chain oracle supports that. + /// /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or /// [`Iterator::enumerate`] over a list of [`OutPoint`]s. @@ -931,16 +949,12 @@ impl TxGraph { /// If the [`ChainOracle`] implementation is infallible, [`filter_subchain_txouts`] can be used /// instead. /// + /// [`try_filter_chain_txouts`]: Self::try_filter_chain_txouts /// [`filter_subchain_txouts`]: Self::filter_subchain_txouts - pub fn try_filter_subchain_txouts< - 'a, - B: Into + Clone + 'a, - C: ChainOracle + 'a, - OI: Clone + 'a, - >( + pub fn try_filter_subchain_txouts<'a, C: ChainOracle + 'a, OI: Clone + 'a>( &'a self, chain: &'a C, - chain_tip: Option, + chain_tip: Option, outpoints: impl IntoIterator + 'a, ) -> impl Iterator), C::Error>> + 'a { outpoints @@ -958,13 +972,13 @@ impl TxGraph { }; let chain_position = - match self.try_get_subchain_position(chain, chain_tip.clone(), op.txid)? { + match self.try_get_subchain_position(chain, chain_tip, op.txid)? { Some(pos) => pos.cloned(), None => return Ok(None), }; let spent_by = self - .try_get_subchain_spend(chain, chain_tip.clone(), op)? + .try_get_subchain_spend(chain, chain_tip, op)? .map(|(a, txid)| (a.cloned(), txid)); Ok(Some(( @@ -1017,15 +1031,10 @@ impl TxGraph { /// This is the infallible version of [`try_filter_subchain_txouts`]. /// /// [`try_filter_subchain_txouts`]: Self::try_filter_subchain_txouts - pub fn filter_subchain_txouts< - 'a, - B: Into + Clone + 'a, - C: ChainOracle + 'a, - OI: Clone + 'a, - >( + pub fn filter_subchain_txouts<'a, C: ChainOracle + 'a, OI: Clone + 'a>( &'a self, chain: &'a C, - chain_tip: Option, + chain_tip: Option, outpoints: impl IntoIterator + 'a, ) -> impl Iterator)> + 'a { self.try_filter_subchain_txouts(chain, chain_tip, outpoints) @@ -1049,6 +1058,10 @@ impl TxGraph { /// Get a filtered list of unspent outputs (UTXOs) from the given `outpoints` that are in /// `chain` with `chain_tip`. /// + /// This method is like [`try_filter_chain_unspents`] except it restricts the chain to a custom tip. + /// The tip doesn't even need to be in the same chain as the tip. You can use this to find information + /// about a point in the past or on a fork if your chain oracle supports that. + /// /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier /// (`OI`) for convenience. If `OI` is not necessary, the caller can use `()`, or /// [`Iterator::enumerate`] over a list of [`OutPoint`]s. @@ -1063,16 +1076,12 @@ impl TxGraph { /// If the [`ChainOracle`] implementation is infallible, [`filter_subchain_unspents`] can be used /// instead. /// + /// [`try_filter_chain_unspents`]: Self::try_filter_chain_unspents /// [`filter_subchain_unspents`]: Self::filter_subchain_unspents - pub fn try_filter_subchain_unspents< - 'a, - B: Into + Clone + 'a, - C: ChainOracle + 'a, - OI: Clone + 'a, - >( + pub fn try_filter_subchain_unspents<'a, C: ChainOracle + 'a, OI: Clone + 'a>( &'a self, chain: &'a C, - chain_tip: Option, + chain_tip: Option, outpoints: impl IntoIterator + 'a, ) -> impl Iterator), C::Error>> + 'a { self.try_filter_subchain_txouts(chain, chain_tip, outpoints) @@ -1120,15 +1129,10 @@ impl TxGraph { /// This is the infallible version of [`try_filter_subchain_unspents`]. /// /// [`try_filter_subchain_unspents`]: Self::try_filter_subchain_unspents - pub fn filter_subchain_unspents< - 'a, - B: Into + Clone + 'a, - C: ChainOracle + 'a, - OI: Clone + 'a, - >( + pub fn filter_subchain_unspents<'a, C: ChainOracle + 'a, OI: Clone + 'a>( &'a self, chain: &'a C, - chain_tip: Option, + chain_tip: Option, txouts: impl IntoIterator + 'a, ) -> impl Iterator)> + 'a { self.try_filter_subchain_unspents(chain, chain_tip, txouts) @@ -1152,6 +1156,10 @@ impl TxGraph { /// Get the total balance of `outpoints` that are in `chain` of `chain_tip`. /// + /// This method is like [`try_balance`] except it restricts the chain to a custom tip. + /// The tip doesn't even need to be in the same chain as the tip. You can use this to find information + /// about a point in the past or on a fork if your chain oracle supports that. + /// /// The output of `trust_predicate` should return `true` for scripts that we trust. /// /// `outpoints` is a list of outpoints we are interested in, coupled with an outpoint identifier @@ -1161,11 +1169,12 @@ impl TxGraph { /// If the provided [`ChainOracle`] implementation (`chain`) is infallible, [`subchain_balance`] can be /// used instead. /// + /// [`try_balance`]: Self::try_balance /// [`subchain_balance`]: Self::subchain_balance - pub fn try_subchain_balance + Clone, C: ChainOracle, OI: Clone>( + pub fn try_subchain_balance( &self, chain: &C, - chain_tip: Option, + chain_tip: Option, outpoints: impl IntoIterator, mut trust_predicate: impl FnMut(&OI, &Script) -> bool, ) -> Result { @@ -1174,14 +1183,12 @@ impl TxGraph { let mut untrusted_pending = 0; let mut confirmed = 0; - let chain_tip_block_id = chain_tip.map(|block| block.into()); - - for res in self.try_filter_subchain_unspents(chain, chain_tip_block_id, outpoints) { + for res in self.try_filter_subchain_unspents(chain, chain_tip, outpoints) { let (spk_i, txout) = res?; match &txout.chain_position { ChainPosition::Confirmed(_) => { - if let Some(block_id) = chain_tip_block_id { + if let Some(block_id) = chain_tip { if txout.is_confirmed_and_spendable(block_id.height) { confirmed += txout.txout.value; } else if !txout.is_mature(block_id.height) { @@ -1233,14 +1240,10 @@ impl TxGraph { /// This is the infallible version of [`try_subchain_balance`]. /// /// [`try_subchain_balance`]: Self::try_subchain_balance - pub fn subchain_balance< - B: Into + Clone, - C: ChainOracle, - OI: Clone, - >( + pub fn subchain_balance, OI: Clone>( &self, chain: &C, - chain_tip: Option, + chain_tip: Option, outpoints: impl IntoIterator, trust_predicate: impl FnMut(&OI, &Script) -> bool, ) -> Balance { diff --git a/example-crates/example_cli/src/lib.rs b/example-crates/example_cli/src/lib.rs index e7daa1b8bd..b4373d5c1d 100644 --- a/example-crates/example_cli/src/lib.rs +++ b/example-crates/example_cli/src/lib.rs @@ -243,9 +243,8 @@ pub fn run_balance_cmd( } } - let balance = graph.graph().try_subchain_balance( + let balance = graph.graph().try_balance( chain, - chain.get_chain_tip()?, graph.index.outpoints().iter().cloned(), |(k, _), _| k == &Keychain::Internal, )?; @@ -282,7 +281,6 @@ pub fn run_txo_cmd( where O::Error: std::error::Error + Send + Sync + 'static, { - let chain_tip = chain.get_chain_tip()?; let outpoints = graph.index.outpoints().iter().cloned(); match cmd { @@ -294,7 +292,7 @@ where } => { let txouts = graph .graph() - .try_filter_subchain_txouts(chain, chain_tip, outpoints) + .try_filter_chain_txouts(chain, outpoints) .filter(|r| match r { Ok((_, full_txo)) => match (spent, unspent) { (true, false) => full_txo.spent_by.is_some(), @@ -608,11 +606,10 @@ pub fn planned_utxos, ) -> Result, FullTxOut)>, O::Error> { - let chain_tip = chain.get_chain_tip()?; let outpoints = graph.index.outpoints().iter().cloned(); graph .graph() - .try_filter_subchain_unspents(chain, chain_tip, outpoints) + .try_filter_chain_unspents(chain, outpoints) .filter_map( #[allow(clippy::type_complexity)] |r| -> Option, FullTxOut), _>> { diff --git a/example-crates/example_electrum/src/main.rs b/example-crates/example_electrum/src/main.rs index dc6b81ef41..57defe0be8 100644 --- a/example-crates/example_electrum/src/main.rs +++ b/example-crates/example_electrum/src/main.rs @@ -163,7 +163,6 @@ fn main() -> anyhow::Result<()> { // Get a short lock on the tracker to get the spks we're interested in let graph = graph.lock().unwrap(); let chain = chain.lock().unwrap(); - let chain_tip = chain.tip().map(|cp| cp.block_id()); if !(all_spks || unused_spks || utxos || unconfirmed) { unused_spks = true; @@ -211,7 +210,7 @@ fn main() -> anyhow::Result<()> { let utxos = graph .graph() - .filter_subchain_unspents(&*chain, chain_tip, init_outpoints) + .filter_chain_unspents(&*chain, init_outpoints) .map(|(_, utxo)| utxo) .collect::>(); @@ -233,7 +232,7 @@ fn main() -> anyhow::Result<()> { if unconfirmed { let unconfirmed_txids = graph .graph() - .list_subchain_txs(&*chain, chain_tip) + .list_chain_txs(&*chain) .filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed()) .map(|canonical_tx| canonical_tx.tx_node.txid) .collect::>(); diff --git a/example-crates/example_esplora/src/main.rs b/example-crates/example_esplora/src/main.rs index f790ba817b..887c94826c 100644 --- a/example-crates/example_esplora/src/main.rs +++ b/example-crates/example_esplora/src/main.rs @@ -201,7 +201,6 @@ fn main() -> anyhow::Result<()> { { let graph = graph.lock().unwrap(); let chain = chain.lock().unwrap(); - let chain_tip = chain.tip().map(|cp| cp.block_id()); if *all_spks { let all_spks = graph @@ -241,7 +240,7 @@ fn main() -> anyhow::Result<()> { let init_outpoints = graph.index.outpoints().iter().cloned(); let utxos = graph .graph() - .filter_subchain_unspents(&*chain, chain_tip, init_outpoints) + .filter_chain_unspents(&*chain, init_outpoints) .map(|(_, utxo)| utxo) .collect::>(); outpoints = Box::new( @@ -264,7 +263,7 @@ fn main() -> anyhow::Result<()> { // `EsploraExt::update_tx_graph_without_keychain`. let unconfirmed_txids = graph .graph() - .list_subchain_txs(&*chain, chain_tip) + .list_chain_txs(&*chain) .filter(|canonical_tx| !canonical_tx.chain_position.is_confirmed()) .map(|canonical_tx| canonical_tx.tx_node.txid) .collect::>();