diff --git a/crates/chain/src/spk_client.rs b/crates/chain/src/spk_client.rs index eefa211c6a..616a0f5c1a 100644 --- a/crates/chain/src/spk_client.rs +++ b/crates/chain/src/spk_client.rs @@ -1,11 +1,18 @@ //! Helper types for spk-based blockchain clients. +use crate::{ + collections::{BTreeMap, HashMap}, + local_chain::CheckPoint, + ConfirmationTimeHeightAnchor, TxGraph, +}; +use alloc::{boxed::Box, sync::Arc, vec::Vec}; +use bitcoin::{OutPoint, Script, ScriptBuf, Transaction, Txid}; use core::{fmt::Debug, marker::PhantomData, ops::RangeBounds}; -use alloc::{boxed::Box, collections::BTreeMap, vec::Vec}; -use bitcoin::{OutPoint, Script, ScriptBuf, Txid}; - -use crate::{local_chain::CheckPoint, ConfirmationTimeHeightAnchor, TxGraph}; +/// A cache of [`Arc`]-wrapped full transactions, identified by their [`Txid`]s. +/// +/// This is used by the chain-source to avoid re-fetching full transactions. +pub type TxCache = HashMap>; /// Data required to perform a spk-based blockchain client sync. /// @@ -17,6 +24,8 @@ pub struct SyncRequest { /// /// [`LocalChain::tip`]: crate::local_chain::LocalChain::tip pub chain_tip: CheckPoint, + /// Cache of full transactions, so the chain-source can avoid re-fetching. + pub tx_cache: TxCache, /// Transactions that spend from or to these indexed script pubkeys. pub spks: Box + Send>, /// Transactions with these txids. @@ -30,12 +39,37 @@ impl SyncRequest { pub fn from_chain_tip(cp: CheckPoint) -> Self { Self { chain_tip: cp, + tx_cache: TxCache::new(), spks: Box::new(core::iter::empty()), txids: Box::new(core::iter::empty()), outpoints: Box::new(core::iter::empty()), } } + /// Add to the [`Transaction`] cache that allows the chain source to avoid re-fetching full + /// transactions. + /// + /// This consumes the [`SyncRequest`] and returns the updated one. + #[must_use] + pub fn cache_txs(mut self, full_txs: impl IntoIterator) -> Self + where + T: Into>, + { + self.tx_cache = full_txs + .into_iter() + .map(|(txid, tx)| (txid, tx.into())) + .collect(); + self + } + + /// Add all transactions from [`TxGraph`] into the [`Transaction`] cache. + /// + /// This consumes the [`SyncRequest`] and returns the updated one. + #[must_use] + pub fn cache_graph_txs(self, graph: &TxGraph) -> Self { + self.cache_txs(graph.full_txs().map(|tx_node| (tx_node.txid, tx_node.tx))) + } + /// Set the [`Script`]s that will be synced against. /// /// This consumes the [`SyncRequest`] and returns the updated one. @@ -194,6 +228,8 @@ pub struct FullScanRequest { /// /// [`LocalChain::tip`]: crate::local_chain::LocalChain::tip pub chain_tip: CheckPoint, + /// Cache of full transactions, so the chain-source can avoid re-fetching. + pub tx_cache: TxCache, /// Iterators of script pubkeys indexed by the keychain index. pub spks_by_keychain: BTreeMap + Send>>, } @@ -204,10 +240,35 @@ impl FullScanRequest { pub fn from_chain_tip(chain_tip: CheckPoint) -> Self { Self { chain_tip, + tx_cache: TxCache::new(), spks_by_keychain: BTreeMap::new(), } } + /// Add to the [`Transaction`] cache that allows the chain source to avoid re-fetching full + /// transactions. + /// + /// This consumes the [`SyncRequest`] and returns the updated one. + #[must_use] + pub fn cache_txs(mut self, full_txs: impl IntoIterator) -> Self + where + T: Into>, + { + self.tx_cache = full_txs + .into_iter() + .map(|(txid, tx)| (txid, tx.into())) + .collect(); + self + } + + /// Add all transactions from [`TxGraph`] into the [`Transaction`] cache. + /// + /// This consumes the [`SyncRequest`] and returns the updated one. + #[must_use] + pub fn cache_graph_txs(self, graph: &TxGraph) -> Self { + self.cache_txs(graph.full_txs().map(|tx_node| (tx_node.txid, tx_node.tx))) + } + /// Construct a new [`FullScanRequest`] from a given `chain_tip` and `index`. /// /// Unbounded script pubkey iterators for each keychain (`K`) are extracted using