fix: TxGraph::missing_blocks logic
Added additional tests for unnecessary duplicate heights
This commit is contained in:
@@ -38,6 +38,8 @@ impl ForEachTxOut for Transaction {
|
||||
|
||||
/// Trait that "anchors" blockchain data to a specific block of height and hash.
|
||||
///
|
||||
/// [`Anchor`] implementations must be [`Ord`] by the anchor block's [`BlockId`] first.
|
||||
///
|
||||
/// I.e. If transaction A is anchored in block B, then if block B is in the best chain, we can
|
||||
/// assume that transaction A is also confirmed in the best chain. This does not necessarily mean
|
||||
/// that transaction A is confirmed in block B. It could also mean transaction A is confirmed in a
|
||||
|
||||
@@ -601,23 +601,63 @@ impl<A: Anchor> TxGraph<A> {
|
||||
/// Find missing block heights of `chain`.
|
||||
///
|
||||
/// This works by scanning through anchors, and seeing whether the anchor block of the anchor
|
||||
/// exists in the [`LocalChain`].
|
||||
pub fn missing_blocks<'a>(&'a self, chain: &'a LocalChain) -> impl Iterator<Item = u32> + 'a {
|
||||
let mut last_block = Option::<BlockId>::None;
|
||||
/// exists in the [`LocalChain`]. The returned iterator does not output duplicate heights.
|
||||
pub fn missing_heights<'a>(&'a self, chain: &'a LocalChain) -> impl Iterator<Item = u32> + 'a {
|
||||
// Map of txids to skip.
|
||||
//
|
||||
// Usually, if a height of a tx anchor is missing from the chain, we would want to return
|
||||
// this height in the iterator. The exception is when the tx is confirmed in chain. All the
|
||||
// other missing-height anchors of this tx can be skipped.
|
||||
//
|
||||
// * Some(true) => skip all anchors of this txid
|
||||
// * Some(false) => do not skip anchors of this txid
|
||||
// * None => we do not know whether we can skip this txid
|
||||
let mut txids_to_skip = HashMap::<Txid, bool>::new();
|
||||
|
||||
// Keeps track of the last height emitted so we don't double up.
|
||||
let mut last_height_emitted = Option::<u32>::None;
|
||||
|
||||
self.anchors
|
||||
.iter()
|
||||
.map(|(a, _)| a.anchor_block())
|
||||
.filter(move |block| {
|
||||
if last_block.as_ref() == Some(block) {
|
||||
false
|
||||
} else {
|
||||
last_block = Some(*block);
|
||||
.filter(move |(_, txid)| {
|
||||
let skip = *txids_to_skip.entry(*txid).or_insert_with(|| {
|
||||
let tx_anchors = match self.txs.get(txid) {
|
||||
Some((_, anchors, _)) => anchors,
|
||||
None => return true,
|
||||
};
|
||||
let mut has_missing_height = false;
|
||||
for anchor_block in tx_anchors.iter().map(Anchor::anchor_block) {
|
||||
match chain.heights().get(&anchor_block.height) {
|
||||
None => {
|
||||
has_missing_height = true;
|
||||
continue;
|
||||
}
|
||||
Some(chain_hash) => {
|
||||
if chain_hash == &anchor_block.hash {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
!has_missing_height
|
||||
});
|
||||
#[cfg(feature = "std")]
|
||||
debug_assert!({
|
||||
println!("txid={} skip={}", txid, skip);
|
||||
true
|
||||
}
|
||||
});
|
||||
!skip
|
||||
})
|
||||
.filter_map(|block| match chain.heights().get(&block.height) {
|
||||
Some(chain_hash) if *chain_hash == block.hash => None,
|
||||
_ => Some(block.height),
|
||||
.filter_map(move |(a, _)| {
|
||||
let anchor_block = a.anchor_block();
|
||||
if Some(anchor_block.height) != last_height_emitted
|
||||
&& !chain.heights().contains_key(&anchor_block.height)
|
||||
{
|
||||
last_height_emitted = Some(anchor_block.height);
|
||||
Some(anchor_block.height)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user