[bdk_chain_redesign] List chain data methods can be try/non-try

Methods that list chain data have try and non-try versions. Both of
these versions now return an `Iterator`.

* Try versions return `Iterator<Item = Result>`.
* Non-try versions require the `ChainOracle` implementation to be
  `ChainOracle<Error = Infallible>`.
This commit is contained in:
志宇 2023-03-27 13:59:51 +08:00
parent 43b648fee0
commit 784cd34e3d
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8

View File

@ -61,7 +61,10 @@ use crate::{
}; };
use alloc::vec::Vec; use alloc::vec::Vec;
use bitcoin::{OutPoint, Transaction, TxOut, Txid}; use bitcoin::{OutPoint, Transaction, TxOut, Txid};
use core::ops::{Deref, RangeInclusive}; use core::{
convert::Infallible,
ops::{Deref, RangeInclusive},
};
/// A graph of transactions and spends. /// A graph of transactions and spends.
/// ///
@ -492,7 +495,7 @@ impl<A: BlockAnchor> TxGraph<A> {
/// Determines whether a transaction of `txid` is in the best chain. /// Determines whether a transaction of `txid` is in the best chain.
/// ///
/// TODO: Also return conflicting tx list, ordered by last_seen. /// TODO: Also return conflicting tx list, ordered by last_seen.
pub fn get_position_in_chain<C>( pub fn try_get_chain_position<C>(
&self, &self,
chain: C, chain: C,
txid: Txid, txid: Txid,
@ -544,7 +547,15 @@ impl<A: BlockAnchor> TxGraph<A> {
} }
} }
pub fn get_spend_in_chain<C>( pub fn get_chain_position<C>(&self, chain: C, txid: Txid) -> Option<ObservedIn<&A>>
where
C: ChainOracle<Error = Infallible>,
{
self.try_get_chain_position(chain, txid)
.expect("error is infallible")
}
pub fn try_get_spend_in_chain<C>(
&self, &self,
chain: C, chain: C,
outpoint: OutPoint, outpoint: OutPoint,
@ -552,12 +563,15 @@ impl<A: BlockAnchor> TxGraph<A> {
where where
C: ChainOracle, C: ChainOracle,
{ {
if self.get_position_in_chain(&chain, outpoint.txid)?.is_none() { if self
.try_get_chain_position(&chain, outpoint.txid)?
.is_none()
{
return Ok(None); return Ok(None);
} }
if let Some(spends) = self.spends.get(&outpoint) { if let Some(spends) = self.spends.get(&outpoint) {
for &txid in spends { for &txid in spends {
if let Some(observed_at) = self.get_position_in_chain(&chain, txid)? { if let Some(observed_at) = self.try_get_chain_position(&chain, txid)? {
return Ok(Some((observed_at, txid))); return Ok(Some((observed_at, txid)));
} }
} }
@ -565,20 +579,12 @@ impl<A: BlockAnchor> TxGraph<A> {
Ok(None) Ok(None)
} }
pub fn transactions_in_chain<C>( pub fn get_chain_spend<C>(&self, chain: C, outpoint: OutPoint) -> Option<(ObservedIn<&A>, Txid)>
&self,
chain: C,
) -> Result<BTreeSet<TxInChain<'_, Transaction, A>>, C::Error>
where where
C: ChainOracle, C: ChainOracle<Error = Infallible>,
{ {
self.full_transactions() self.try_get_spend_in_chain(chain, outpoint)
.filter_map(|tx| { .expect("error is infallible")
self.get_position_in_chain(&chain, tx.txid)
.map(|v| v.map(|observed_in| TxInChain { observed_in, tx }))
.transpose()
})
.collect()
} }
} }
@ -802,41 +808,56 @@ impl<A: BlockAnchor, I: TxIndex> IndexedTxGraph<A, I> {
self.graph.relevant_heights() self.graph.relevant_heights()
} }
pub fn txs_in_chain<C>( pub fn try_list_chain_txs<'a, C>(
&self, &'a self,
chain: C, chain: C,
) -> Result<BTreeSet<TxInChain<'_, Transaction, A>>, C::Error> ) -> impl Iterator<Item = Result<TxInChain<'a, Transaction, A>, C::Error>>
where where
C: ChainOracle, C: ChainOracle + 'a,
{ {
let mut tx_set = self.graph.transactions_in_chain(chain)?; self.graph
tx_set.retain(|tx| self.index.is_tx_relevant(&tx.tx)); .full_transactions()
Ok(tx_set) .filter(|tx| self.index.is_tx_relevant(tx))
.filter_map(move |tx| {
self.graph
.try_get_chain_position(&chain, tx.txid)
.map(|v| v.map(|observed_in| TxInChain { observed_in, tx }))
.transpose()
})
} }
pub fn txouts_in_chain<C>( pub fn list_chain_txs<'a, C>(
&self, &'a self,
chain: C, chain: C,
) -> Result<Vec<TxOutInChain<'_, I::SpkIndex, A>>, C::Error> ) -> impl Iterator<Item = TxInChain<'a, Transaction, A>>
where where
C: ChainOracle, C: ChainOracle<Error = Infallible> + 'a,
{
self.try_list_chain_txs(chain)
.map(|r| r.expect("error is infallible"))
}
pub fn try_list_chain_txouts<'a, C>(
&'a self,
chain: C,
) -> impl Iterator<Item = Result<TxOutInChain<'a, I::SpkIndex, A>, C::Error>>
where
C: ChainOracle + 'a,
ObservedIn<A>: ChainPosition, ObservedIn<A>: ChainPosition,
{ {
self.index self.index.relevant_txouts().iter().filter_map(
.relevant_txouts() move |(op, (spk_i, txout))| -> Option<Result<_, C::Error>> {
.iter()
.filter_map(|(op, (spk_i, txout))| -> Option<Result<_, C::Error>> {
let graph_tx = self.graph.get_tx(op.txid)?; let graph_tx = self.graph.get_tx(op.txid)?;
let is_on_coinbase = graph_tx.is_coin_base(); let is_on_coinbase = graph_tx.is_coin_base();
let chain_position = match self.graph.get_position_in_chain(&chain, op.txid) { let chain_position = match self.graph.try_get_chain_position(&chain, op.txid) {
Ok(Some(observed_at)) => observed_at, Ok(Some(observed_at)) => observed_at,
Ok(None) => return None, Ok(None) => return None,
Err(err) => return Some(Err(err)), Err(err) => return Some(Err(err)),
}; };
let spent_by = match self.graph.get_spend_in_chain(&chain, *op) { let spent_by = match self.graph.try_get_spend_in_chain(&chain, *op) {
Ok(spent_by) => spent_by, Ok(spent_by) => spent_by,
Err(err) => return Some(Err(err)), Err(err) => return Some(Err(err)),
}; };
@ -855,22 +876,45 @@ impl<A: BlockAnchor, I: TxIndex> IndexedTxGraph<A, I> {
}; };
Some(Ok(txout_in_chain)) Some(Ok(txout_in_chain))
}) },
.collect() )
}
pub fn list_chain_txouts<'a, C>(
&'a self,
chain: C,
) -> impl Iterator<Item = TxOutInChain<'a, I::SpkIndex, A>>
where
C: ChainOracle<Error = Infallible> + 'a,
ObservedIn<A>: ChainPosition,
{
self.try_list_chain_txouts(chain)
.map(|r| r.expect("error in infallible"))
} }
/// Return relevant unspents. /// Return relevant unspents.
pub fn utxos_in_chain<C>( pub fn try_list_chain_utxos<'a, C>(
&self, &'a self,
chain: C, chain: C,
) -> Result<Vec<TxOutInChain<'_, I::SpkIndex, A>>, C::Error> ) -> impl Iterator<Item = Result<TxOutInChain<'a, I::SpkIndex, A>, C::Error>>
where where
C: ChainOracle, C: ChainOracle + 'a,
ObservedIn<A>: ChainPosition, ObservedIn<A>: ChainPosition,
{ {
let mut txouts = self.txouts_in_chain(chain)?; self.try_list_chain_txouts(chain)
txouts.retain(|txo| txo.txout.spent_by.is_none()); .filter(|r| !matches!(r, Ok(txo) if txo.txout.spent_by.is_none()))
Ok(txouts) }
pub fn list_chain_utxos<'a, C>(
&'a self,
chain: C,
) -> impl Iterator<Item = TxOutInChain<'a, I::SpkIndex, A>>
where
C: ChainOracle<Error = Infallible> + 'a,
ObservedIn<A>: ChainPosition,
{
self.try_list_chain_utxos(chain)
.map(|r| r.expect("error is infallible"))
} }
} }