* Remove `chain_oracle::CacheBackend` for now as it is not used.
* `SparseChain` does not need to implement `ChainOracle`.
* Remove filter predicate for `list..` methods of `TxGraph` and
`IndexedTxGraph` as this is premature optimisation.
* `Append` can be implemented for all `BTreeMap`s and `BTreeSet`s,
instead of only `local_chain::ChangeSet`.
-use crate::collections::HashSet;
-use core::marker::PhantomData;
-
-use alloc::{collections::VecDeque, vec::Vec};
-use bitcoin::BlockHash;
-
use crate::BlockId;
/// Represents a service that tracks the blockchain.
static_block: BlockId,
) -> Result<Option<bool>, Self::Error>;
}
-
-/// A cache structure increases the performance of getting chain data.
-///
-/// A simple FIFO cache replacement policy is used. Something more efficient and advanced can be
-/// implemented later.
-#[derive(Debug, Default)]
-pub struct CacheBackend<C> {
- cache: HashSet<(BlockHash, BlockHash)>,
- fifo: VecDeque<(BlockHash, BlockHash)>,
- marker: PhantomData<C>,
-}
-
-impl<C> CacheBackend<C> {
- /// Get the number of elements in the cache.
- pub fn cache_size(&self) -> usize {
- self.cache.len()
- }
-
- /// Prunes the cache to reach the `max_size` target.
- ///
- /// Returns pruned elements.
- pub fn prune(&mut self, max_size: usize) -> Vec<(BlockHash, BlockHash)> {
- let prune_count = self.cache.len().saturating_sub(max_size);
- (0..prune_count)
- .filter_map(|_| self.fifo.pop_front())
- .filter(|k| self.cache.remove(k))
- .collect()
- }
-
- pub fn contains(&self, static_block: BlockId, block: BlockId) -> bool {
- if static_block.height < block.height
- || static_block.height == block.height && static_block.hash != block.hash
- {
- return false;
- }
-
- self.cache.contains(&(static_block.hash, block.hash))
- }
-
- pub fn insert(&mut self, static_block: BlockId, block: BlockId) -> bool {
- let cache_key = (static_block.hash, block.hash);
-
- if self.cache.insert(cache_key) {
- self.fifo.push_back(cache_key);
- true
- } else {
- false
- }
- }
-}
///
/// `anchors` can be provided to anchor the transactions to blocks. `seen_at` is a unix
/// timestamp of when the transactions are last seen.
- pub fn insert_relevant_txs<'t, T>(
+ pub fn insert_relevant_txs<'t, T: Iterator<Item = &'t Transaction>>(
&mut self,
txs: T,
anchors: impl IntoIterator<Item = A> + Clone,
seen_at: Option<u64>,
- ) -> IndexedAdditions<A, I::Additions>
- where
- T: Iterator<Item = &'t Transaction>,
- {
+ ) -> IndexedAdditions<A, I::Additions> {
txs.filter_map(|tx| match self.index.is_tx_relevant(tx) {
true => Some(self.insert_tx(tx, anchors.clone(), seen_at)),
false => None,
}
impl<A: Anchor, I: OwnedIndexer> IndexedTxGraph<A, I> {
- pub fn try_list_owned_txs<'a, C>(
+ pub fn try_list_owned_txs<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = Result<CanonicalTx<'a, Transaction, A>, C::Error>>
- where
- C: ChainOracle + 'a,
- {
+ ) -> impl Iterator<Item = Result<CanonicalTx<'a, Transaction, A>, C::Error>> {
self.graph
.full_txs()
.filter(|node| tx_alters_owned_utxo_set(&self.graph, &self.index, node.txid, node.tx))
})
}
- pub fn list_owned_txs<'a, C>(
+ pub fn list_owned_txs<'a, C: ChainOracle<Error = Infallible> + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = CanonicalTx<'a, Transaction, A>>
- where
- C: ChainOracle<Error = Infallible> + 'a,
- {
+ ) -> impl Iterator<Item = CanonicalTx<'a, Transaction, A>> {
self.try_list_owned_txs(chain, chain_tip)
.map(|r| r.expect("chain oracle is infallible"))
}
- pub fn try_list_owned_txouts<'a, C>(
+ pub fn try_list_owned_txouts<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a
- where
- C: ChainOracle + 'a,
- {
+ ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a {
self.graph()
- .try_list_chain_txouts(chain, chain_tip, |_, txout| {
- self.index.is_spk_owned(&txout.script_pubkey)
+ .try_list_chain_txouts(chain, chain_tip)
+ .filter(|r| {
+ if let Ok(full_txout) = r {
+ if !self.index.is_spk_owned(&full_txout.txout.script_pubkey) {
+ return false;
+ }
+ }
+ true
})
}
- pub fn list_owned_txouts<'a, C>(
+ pub fn list_owned_txouts<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a
- where
- C: ChainOracle + 'a,
- {
+ ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a {
self.try_list_owned_txouts(chain, chain_tip)
.map(|r| r.expect("oracle is infallible"))
}
- pub fn try_list_owned_unspents<'a, C>(
+ pub fn try_list_owned_unspents<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a
- where
- C: ChainOracle + 'a,
- {
+ ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a {
self.graph()
- .try_list_chain_unspents(chain, chain_tip, |_, txout| {
- self.index.is_spk_owned(&txout.script_pubkey)
+ .try_list_chain_unspents(chain, chain_tip)
+ .filter(|r| {
+ if let Ok(full_txout) = r {
+ if !self.index.is_spk_owned(&full_txout.txout.script_pubkey) {
+ return false;
+ }
+ }
+ true
})
}
})
}
- pub fn balance<C, F>(
- &self,
- chain: &C,
- static_block: BlockId,
- tip: u32,
- should_trust: F,
- ) -> Balance
+ pub fn balance<C, F>(&self, chain: &C, chain_tip: BlockId, tip: u32, should_trust: F) -> Balance
where
C: ChainOracle<Error = Infallible>,
F: FnMut(&Script) -> bool,
{
- self.try_balance(chain, static_block, tip, should_trust)
+ self.try_balance(chain, chain_tip, tip, should_trust)
.expect("error is infallible")
}
pub fn try_balance_at<C>(
&self,
chain: &C,
- static_block: BlockId,
+ chain_tip: BlockId,
height: u32,
) -> Result<u64, C::Error>
where
C: ChainOracle,
{
let mut sum = 0;
- for txo_res in self
- .graph()
- .try_list_chain_txouts(chain, static_block, |_, _| true)
- {
+ for txo_res in self.try_list_owned_unspents(chain, chain_tip) {
let txo = txo_res?;
if txo.is_observed_as_confirmed_and_spendable(height) {
sum += txo.txout.value;
Ok(sum)
}
- pub fn balance_at<C>(&self, chain: &C, static_block: BlockId, height: u32) -> u64
+ pub fn balance_at<C>(&self, chain: &C, chain_tip: BlockId, height: u32) -> u64
where
C: ChainOracle<Error = Infallible>,
{
- self.try_balance_at(chain, static_block, height)
+ self.try_balance_at(chain, chain_tip, height)
.expect("error is infallible")
}
}
use alloc::collections::{BTreeMap, BTreeSet};
use bitcoin::BlockHash;
-use crate::{Append, BlockId, ChainOracle};
+use crate::{BlockId, ChainOracle};
/// This is a local implementation of [`ChainOracle`].
///
/// [`determine_changeset`]: LocalChain::determine_changeset
pub type ChangeSet = BTreeMap<u32, Option<BlockHash>>;
-impl Append for ChangeSet {
- fn append(&mut self, mut other: Self) {
- BTreeMap::append(self, &mut other)
- }
-}
-
/// Represents an update failure of [`LocalChain`] due to the update not connecting to the original
/// chain.
///
//! );
//! ```
use core::{
- convert::Infallible,
fmt::Debug,
ops::{Bound, RangeBounds},
};
-use crate::{collections::*, tx_graph::TxGraph, BlockId, ChainOracle, FullTxOut, TxHeight};
+use crate::{collections::*, tx_graph::TxGraph, BlockId, FullTxOut, TxHeight};
use bitcoin::{hashes::Hash, BlockHash, OutPoint, Txid};
/// This is a non-monotone structure that tracks relevant [`Txid`]s that are ordered by chain
#[cfg(feature = "std")]
impl<P: core::fmt::Debug> std::error::Error for UpdateError<P> {}
-impl<P> ChainOracle for SparseChain<P> {
- type Error = Infallible;
-
- fn is_block_in_chain(
- &self,
- block: BlockId,
- static_block: BlockId,
- ) -> Result<Option<bool>, Self::Error> {
- Ok(
- match (
- self.checkpoint_at(block.height),
- self.checkpoint_at(static_block.height),
- ) {
- (Some(b), Some(static_b)) => Some(b == block && static_b == static_block),
- _ => None,
- },
- )
- }
-}
-
impl<P> SparseChain<P> {
/// Creates a new chain from a list of block hashes and heights. The caller must guarantee they
/// are in the same chain.
-use bitcoin::{Block, BlockHash, OutPoint, Transaction, TxOut};
-
+use crate::collections::BTreeMap;
+use crate::collections::BTreeSet;
use crate::BlockId;
+use bitcoin::{Block, BlockHash, OutPoint, Transaction, TxOut};
/// Trait to do something with every txout contained in a structure.
///
impl Append for () {
fn append(&mut self, _other: Self) {}
}
+
+impl<K: Ord, V> Append for BTreeMap<K, V> {
+ fn append(&mut self, mut other: Self) {
+ BTreeMap::append(self, &mut other)
+ }
+}
+
+impl<T: Ord> Append for BTreeSet<T> {
+ fn append(&mut self, mut other: Self) {
+ BTreeSet::append(self, &mut other)
+ }
+}
/// [`ChainOracle`] is infallible, [`get_chain_position`] can be used instead.
///
/// [`get_chain_position`]: Self::get_chain_position
- pub fn try_get_chain_position<C>(
+ pub fn try_get_chain_position<C: ChainOracle>(
&self,
chain: &C,
chain_tip: BlockId,
txid: Txid,
- ) -> Result<Option<ObservedAs<&A>>, C::Error>
- where
- C: ChainOracle,
- {
+ ) -> Result<Option<ObservedAs<&A>>, C::Error> {
let (tx_node, anchors, &last_seen) = match self.txs.get(&txid) {
Some((tx, anchors, last_seen)) if !(anchors.is_empty() && *last_seen == 0) => {
(tx, anchors, last_seen)
/// This is the infallible version of [`try_get_chain_position`].
///
/// [`try_get_chain_position`]: Self::try_get_chain_position
- pub fn get_chain_position<C>(
+ pub fn get_chain_position<C: ChainOracle<Error = Infallible>>(
&self,
chain: &C,
chain_tip: BlockId,
txid: Txid,
- ) -> Option<ObservedAs<&A>>
- where
- C: ChainOracle<Error = Infallible>,
- {
+ ) -> Option<ObservedAs<&A>> {
self.try_get_chain_position(chain, chain_tip, txid)
.expect("error is infallible")
}
/// If the [`ChainOracle`] is infallible, [`get_chain_spend`] can be used instead.
///
/// [`get_chain_spend`]: Self::get_chain_spend
- pub fn try_get_chain_spend<C>(
+ pub fn try_get_chain_spend<C: ChainOracle>(
&self,
chain: &C,
chain_tip: BlockId,
outpoint: OutPoint,
- ) -> Result<Option<(ObservedAs<&A>, Txid)>, C::Error>
- where
- C: ChainOracle,
- {
+ ) -> Result<Option<(ObservedAs<&A>, Txid)>, C::Error> {
if self
.try_get_chain_position(chain, chain_tip, outpoint.txid)?
.is_none()
/// This is the infallible version of [`try_get_chain_spend`]
///
/// [`try_get_chain_spend`]: Self::try_get_chain_spend
- pub fn get_chain_spend<C>(
+ pub fn get_chain_spend<C: ChainOracle<Error = Infallible>>(
&self,
chain: &C,
static_block: BlockId,
outpoint: OutPoint,
- ) -> Option<(ObservedAs<&A>, Txid)>
- where
- C: ChainOracle<Error = Infallible>,
- {
+ ) -> Option<(ObservedAs<&A>, Txid)> {
self.try_get_chain_spend(chain, static_block, outpoint)
.expect("error is infallible")
}
/// If the [`ChainOracle`] is infallible, [`list_chain_txs`] can be used instead.
///
/// [`list_chain_txs`]: Self::list_chain_txs
- pub fn try_list_chain_txs<'a, C>(
+ pub fn try_list_chain_txs<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = Result<CanonicalTx<'a, Transaction, A>, C::Error>>
- where
- C: ChainOracle + 'a,
- {
+ ) -> impl Iterator<Item = Result<CanonicalTx<'a, Transaction, A>, C::Error>> {
self.full_txs().filter_map(move |tx| {
self.try_get_chain_position(chain, chain_tip, tx.txid)
.map(|v| {
/// This is the infallible version of [`try_list_chain_txs`].
///
/// [`try_list_chain_txs`]: Self::try_list_chain_txs
- pub fn list_chain_txs<'a, C>(
+ pub fn list_chain_txs<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = CanonicalTx<'a, Transaction, A>>
- where
- C: ChainOracle + 'a,
- {
+ ) -> impl Iterator<Item = CanonicalTx<'a, Transaction, A>> {
self.try_list_chain_txs(chain, chain_tip)
.map(|r| r.expect("oracle is infallible"))
}
/// If the [`ChainOracle`] is infallible, [`list_chain_txouts`] can be used instead.
///
/// [`list_chain_txouts`]: Self::list_chain_txouts
- pub fn try_list_chain_txouts<'a, C, P>(
+ pub fn try_list_chain_txouts<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- mut filter_predicate: P,
- ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a
- where
- C: ChainOracle + 'a,
- P: FnMut(OutPoint, &TxOut) -> bool + 'a,
- {
+ ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a {
self.try_list_chain_txs(chain, chain_tip)
.flat_map(move |tx_res| match tx_res {
Ok(canonical_tx) => canonical_tx
.output
.iter()
.enumerate()
- .filter_map(|(vout, txout)| {
+ .map(|(vout, txout)| {
let outpoint = OutPoint::new(canonical_tx.node.txid, vout as _);
- if filter_predicate(outpoint, txout) {
- Some(Ok((outpoint, txout.clone(), canonical_tx.clone())))
- } else {
- None
- }
+ Ok((outpoint, txout.clone(), canonical_tx.clone()))
})
.collect::<Vec<_>>(),
Err(err) => vec![Err(err)],
/// This is the infallible version of [`try_list_chain_txouts`].
///
/// [`try_list_chain_txouts`]: Self::try_list_chain_txouts
- pub fn list_chain_txouts<'a, C, P>(
+ pub fn list_chain_txouts<'a, C: ChainOracle<Error = Infallible> + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- filter_predicate: P,
- ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a
- where
- C: ChainOracle<Error = Infallible> + 'a,
- P: FnMut(OutPoint, &TxOut) -> bool + 'a,
- {
- self.try_list_chain_txouts(chain, chain_tip, filter_predicate)
+ ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a {
+ self.try_list_chain_txouts(chain, chain_tip)
.map(|r| r.expect("error in infallible"))
}
/// infallible, [`list_chain_unspents`] can be used instead.
///
/// [`list_chain_unspents`]: Self::list_chain_unspents
- pub fn try_list_chain_unspents<'a, C, P>(
+ pub fn try_list_chain_unspents<'a, C: ChainOracle + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- filter_txout: P,
- ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a
- where
- C: ChainOracle + 'a,
- P: FnMut(OutPoint, &TxOut) -> bool + 'a,
- {
- self.try_list_chain_txouts(chain, chain_tip, filter_txout)
+ ) -> impl Iterator<Item = Result<FullTxOut<ObservedAs<A>>, C::Error>> + 'a {
+ self.try_list_chain_txouts(chain, chain_tip)
.filter(|r| !matches!(r, Ok(txo) if txo.spent_by.is_none()))
}
/// This is the infallible version of [`try_list_chain_unspents`].
///
/// [`try_list_chain_unspents`]: Self::try_list_chain_unspents
- pub fn list_chain_unspents<'a, C, P>(
+ pub fn list_chain_unspents<'a, C: ChainOracle<Error = Infallible> + 'a>(
&'a self,
chain: &'a C,
static_block: BlockId,
- filter_txout: P,
- ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a
- where
- C: ChainOracle<Error = Infallible> + 'a,
- P: FnMut(OutPoint, &TxOut) -> bool + 'a,
- {
- self.try_list_chain_unspents(chain, static_block, filter_txout)
+ ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a {
+ self.try_list_chain_unspents(chain, static_block)
.map(|r| r.expect("error is infallible"))
}
}