Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 24 additions & 27 deletions crates/chain/src/canonical_iter.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::collections::{HashMap, HashSet, VecDeque};
use crate::tx_graph::{TxAncestors, TxDescendants};
use crate::{Anchor, ChainOracle, TxGraph};
use crate::{Anchor, TxGraph};
use alloc::boxed::Box;
use alloc::collections::BTreeSet;
use alloc::sync::Arc;
Expand All @@ -22,10 +22,12 @@ pub struct CanonicalizationParams {
}

/// Iterates over canonical txs.
pub struct CanonicalIter<'g, A, C> {
pub struct CanonicalIter<'g, A, F>
where
F: FnMut(BlockId) -> Option<bool>,
{
tx_graph: &'g TxGraph<A>,
chain: &'g C,
chain_tip: BlockId,
is_block_in_chain: F,

unprocessed_assumed_txs: Box<dyn Iterator<Item = (Txid, Arc<Transaction>)> + 'g>,
unprocessed_anchored_txs:
Expand All @@ -39,13 +41,15 @@ pub struct CanonicalIter<'g, A, C> {
queue: VecDeque<Txid>,
}

impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
impl<'g, A: Anchor, F> CanonicalIter<'g, A, F>
where
F: FnMut(BlockId) -> Option<bool>,
{
/// Constructs [`CanonicalIter`].
pub fn new(
tx_graph: &'g TxGraph<A>,
chain: &'g C,
chain_tip: BlockId,
params: CanonicalizationParams,
is_block_in_chain: F,
) -> Self {
let anchors = tx_graph.all_anchors();
let unprocessed_assumed_txs = Box::new(
Expand All @@ -67,8 +71,7 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
);
Self {
tx_graph,
chain,
chain_tip,
is_block_in_chain,
unprocessed_assumed_txs,
unprocessed_anchored_txs,
unprocessed_seen_txs,
Expand All @@ -85,19 +88,13 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
}

/// Mark transaction as canonical if it is anchored in the best chain.
fn scan_anchors(
&mut self,
txid: Txid,
tx: Arc<Transaction>,
anchors: &BTreeSet<A>,
) -> Result<(), C::Error> {
fn scan_anchors(&mut self, txid: Txid, tx: Arc<Transaction>, anchors: &BTreeSet<A>) {
for anchor in anchors {
let in_chain_opt = self
.chain
.is_block_in_chain(anchor.anchor_block(), self.chain_tip)?;
if in_chain_opt == Some(true) {
let block_id = anchor.anchor_block();
let is_block_in_chain = (self.is_block_in_chain)(block_id);
if is_block_in_chain == Some(true) {
self.mark_canonical(txid, tx, CanonicalReason::from_anchor(anchor.clone()));
return Ok(());
return;
}
}
// cannot determine
Expand All @@ -112,7 +109,6 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
)
.confirmation_height_upper_bound(),
));
Ok(())
}

/// Marks `tx` and it's ancestors as canonical and mark all conflicts of these as
Expand Down Expand Up @@ -200,8 +196,11 @@ impl<'g, A: Anchor, C: ChainOracle> CanonicalIter<'g, A, C> {
}
}

impl<A: Anchor, C: ChainOracle> Iterator for CanonicalIter<'_, A, C> {
type Item = Result<(Txid, Arc<Transaction>, CanonicalReason<A>), C::Error>;
impl<A: Anchor, F> Iterator for CanonicalIter<'_, A, F>
where
F: FnMut(BlockId) -> Option<bool>,
{
type Item = (Txid, Arc<Transaction>, CanonicalReason<A>);

fn next(&mut self) -> Option<Self::Item> {
loop {
Expand All @@ -211,7 +210,7 @@ impl<A: Anchor, C: ChainOracle> Iterator for CanonicalIter<'_, A, C> {
.get(&txid)
.cloned()
.expect("reason must exist");
return Some(Ok((txid, tx, reason)));
return Some((txid, tx, reason));
}

if let Some((txid, tx)) = self.unprocessed_assumed_txs.next() {
Expand All @@ -222,9 +221,7 @@ impl<A: Anchor, C: ChainOracle> Iterator for CanonicalIter<'_, A, C> {

if let Some((txid, tx, anchors)) = self.unprocessed_anchored_txs.next() {
if !self.is_canonicalized(txid) {
if let Err(err) = self.scan_anchors(txid, tx, anchors) {
return Some(Err(err));
}
self.scan_anchors(txid, tx, anchors);
}
continue;
}
Expand Down
129 changes: 66 additions & 63 deletions crates/chain/src/tx_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,7 +998,7 @@ impl<A: Anchor> TxGraph<A> {
chain: &'a C,
chain_tip: BlockId,
params: CanonicalizationParams,
) -> impl Iterator<Item = Result<CanonicalTx<'a, Arc<Transaction>, A>, C::Error>> {
) -> impl Iterator<Item = CanonicalTx<'a, Arc<Transaction>, A>> {
fn find_direct_anchor<A: Anchor, C: ChainOracle>(
tx_node: &TxNode<'_, Arc<Transaction>, A>,
chain: &C,
Expand All @@ -1016,60 +1016,61 @@ impl<A: Anchor> TxGraph<A> {
})
.transpose()
}
self.canonical_iter(chain, chain_tip, params)
.flat_map(move |res| {
res.map(|(txid, _, canonical_reason)| {
let tx_node = self.get_tx_node(txid).expect("must contain tx");
let chain_position = match canonical_reason {
CanonicalReason::Assumed { descendant } => match descendant {
Some(_) => match find_direct_anchor(&tx_node, chain, chain_tip)? {
Some(anchor) => ChainPosition::Confirmed {
anchor,
transitively: None,
},
None => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: tx_node.last_seen,
},
},
None => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: tx_node.last_seen,
},
self.canonical_iter(params, move |block_id: BlockId| -> Option<bool> {
// FIXME: (@leonardolima) how should we handle the error from ChainOracle ?
chain.is_block_in_chain(block_id, chain_tip).unwrap_or_default()
})
.flat_map(move |(txid, _, canonical_reason)| -> Result<CanonicalTx<'_, Arc<Transaction>, A>, C::Error> {
let tx_node = self.get_tx_node(txid).expect("must contain tx");
let chain_position = match canonical_reason {
CanonicalReason::Assumed { descendant } => match descendant {
Some(_) => match find_direct_anchor(&tx_node, chain, chain_tip)? {
Some(anchor) => ChainPosition::Confirmed {
anchor,
transitively: None,
},
CanonicalReason::Anchor { anchor, descendant } => match descendant {
Some(_) => match find_direct_anchor(&tx_node, chain, chain_tip)? {
Some(anchor) => ChainPosition::Confirmed {
anchor,
transitively: None,
},
None => ChainPosition::Confirmed {
anchor,
transitively: descendant,
},
},
None => ChainPosition::Confirmed {
anchor,
transitively: None,
},
None => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: tx_node.last_seen,
},
CanonicalReason::ObservedIn { observed_in, .. } => match observed_in {
ObservedIn::Mempool(last_seen) => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: Some(last_seen),
},
ObservedIn::Block(_) => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: None,
},
},
None => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: tx_node.last_seen,
},
},
CanonicalReason::Anchor { anchor, descendant } => match descendant {
Some(_) => match find_direct_anchor(&tx_node, chain, chain_tip)? {
Some(anchor) => ChainPosition::Confirmed {
anchor,
transitively: None,
},
};
Ok(CanonicalTx {
chain_position,
tx_node,
})
})
None => ChainPosition::Confirmed {
anchor,
transitively: descendant,
},
},
None => ChainPosition::Confirmed {
anchor,
transitively: None,
},
},
CanonicalReason::ObservedIn { observed_in, .. } => match observed_in {
ObservedIn::Mempool(last_seen) => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: Some(last_seen),
},
ObservedIn::Block(_) => ChainPosition::Unconfirmed {
first_seen: tx_node.first_seen,
last_seen: None,
},
},
};
Ok(CanonicalTx {
chain_position,
tx_node,
})
})
}

/// List graph transactions that are in `chain` with `chain_tip`.
Expand All @@ -1084,7 +1085,6 @@ impl<A: Anchor> TxGraph<A> {
params: CanonicalizationParams,
) -> impl Iterator<Item = CanonicalTx<'a, Arc<Transaction>, A>> {
self.try_list_canonical_txs(chain, chain_tip, params)
.map(|res| res.expect("infallible"))
}

/// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
Expand Down Expand Up @@ -1116,7 +1116,7 @@ impl<A: Anchor> TxGraph<A> {
let mut canon_txs = HashMap::<Txid, CanonicalTx<Arc<Transaction>, A>>::new();
let mut canon_spends = HashMap::<OutPoint, Txid>::new();
for r in self.try_list_canonical_txs(chain, chain_tip, params) {
let canonical_tx = r?;
let canonical_tx = r;
let txid = canonical_tx.tx_node.txid;

if !canonical_tx.tx_node.tx.is_coinbase() {
Expand Down Expand Up @@ -1183,13 +1183,15 @@ impl<A: Anchor> TxGraph<A> {
}

/// Returns a [`CanonicalIter`].
pub fn canonical_iter<'a, C: ChainOracle>(
pub fn canonical_iter<'a, F>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
params: CanonicalizationParams,
) -> CanonicalIter<'a, A, C> {
CanonicalIter::new(self, chain, chain_tip, params)
is_block_in_chain: F,
) -> CanonicalIter<'a, A, F>
where
F: FnMut(BlockId) -> Option<bool>,
{
CanonicalIter::new(self, params, is_block_in_chain)
}

/// Get a filtered list of outputs from the given `outpoints` that are in `chain` with
Expand Down Expand Up @@ -1416,12 +1418,13 @@ impl<A: Anchor> TxGraph<A> {
{
let indexer = indexer.as_ref();
self.try_list_canonical_txs(chain, chain_tip, CanonicalizationParams::default())
.flat_map(move |res| -> Vec<Result<(ScriptBuf, Txid), C::Error>> {
.flat_map(move |c_tx| -> Vec<Result<(ScriptBuf, Txid), C::Error>> {
let range = &spk_index_range;
let c_tx = match res {
Ok(c_tx) => c_tx,
Err(err) => return vec![Err(err)],
};
// FIXME: (@oleonardolima) should we handle the error here somehow ?
// let c_tx = match res {
// Ok(c_tx) => c_tx,
// Err(err) => return vec![Err(err)],
// };
let relevant_spks = indexer.relevant_spks_of_tx(&c_tx.tx_node);
relevant_spks
.into_iter()
Expand Down
Loading