Skip to content

Commit

Permalink
feat(chain): Add SyncRequestExt::check_unconfirmed_statuses
Browse files Browse the repository at this point in the history
This is a convenience method for adding unconfirmed txs alongside their
associated spks the the sync request. This way, we will be able to
detect evictions of these transactions from the mempool.
  • Loading branch information
evanlinjin committed Jan 24, 2025
1 parent 4b754e1 commit 0f52ead
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 2 deletions.
41 changes: 40 additions & 1 deletion crates/chain/src/indexer/keychain_txout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ use crate::{
spk_client::{FullScanRequestBuilder, SyncRequestBuilder},
spk_iter::BIP32_MAX_INDEX,
spk_txout::SpkTxOutIndex,
DescriptorExt, DescriptorId, Indexed, Indexer, KeychainIndexed, SpkIterator,
Anchor, CanonicalIter, CanonicalReason, ChainOracle, DescriptorExt, DescriptorId, Indexed,
Indexer, KeychainIndexed, SpkIterator,
};
use alloc::{borrow::ToOwned, vec::Vec};
use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
use core::{
convert::Infallible,
fmt::Debug,
ops::{Bound, RangeBounds},
};
Expand Down Expand Up @@ -879,6 +881,20 @@ pub trait SyncRequestBuilderExt<K> {

/// Add [`Script`](bitcoin::Script)s that are revealed by the `indexer` but currently unused.
fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self;

/// Add unconfirmed txids and their associated spks.
///
/// We expect that the chain source should include these txids in their spk histories. If not,
/// the transaction has been evicted for some reason and we will inform the receiving
/// structures in the response.
fn check_unconfirmed_statuses<A, C>(
self,
indexer: &KeychainTxOutIndex<K>,
canonical_iter: CanonicalIter<A, C>,
) -> Self
where
A: Anchor,
C: ChainOracle<Error = Infallible>;
}

impl<K: Clone + Ord + core::fmt::Debug> SyncRequestBuilderExt<K> for SyncRequestBuilder<(K, u32)> {
Expand All @@ -892,6 +908,29 @@ impl<K: Clone + Ord + core::fmt::Debug> SyncRequestBuilderExt<K> for SyncRequest
fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self {
self.spks_with_indexes(indexer.unused_spks())
}

fn check_unconfirmed_statuses<A, C>(
self,
indexer: &KeychainTxOutIndex<K>,
canonical_iter: CanonicalIter<A, C>,
) -> Self
where
A: Anchor,
C: ChainOracle<Error = Infallible>,
{
self.expected_txs(
canonical_iter
.map(|res| res.expect("infallible"))
.filter(|(_, _, reason)| matches!(reason, CanonicalReason::ObservedIn { .. }))
.flat_map(|(txid, tx, _)| {
indexer
.inner
.relevant_spks_of_tx(tx.as_ref())
.into_iter()
.map(move |spk| (txid, spk))
}),
)
}
}

/// Trait to extend [`FullScanRequestBuilder`].
Expand Down
19 changes: 18 additions & 1 deletion crates/chain/src/indexer/spk_txout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::ops::RangeBounds;

use crate::{
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap},
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap, HashSet},
Indexer,
};
use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
Expand Down Expand Up @@ -334,4 +334,21 @@ impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
.any(|output| self.spk_indices.contains_key(&output.script_pubkey));
input_matches || output_matches
}

/// Find relevant script pubkeys associated with a transaction.
///
/// The script pubkeys of the transaction's outputs and previous outputs as scanned.
pub fn relevant_spks_of_tx(&self, tx: &Transaction) -> HashSet<ScriptBuf> {
let spks_from_inputs = tx.input.iter().filter_map(|txin| {
self.txouts
.get(&txin.previous_output)
.map(|(_, prev_txo)| prev_txo.script_pubkey.clone())
});
let spks_from_outputs = tx
.output
.iter()
.filter(|txout| self.spk_indices.contains_key(&txout.script_pubkey))
.map(|txo| txo.script_pubkey.clone());
spks_from_inputs.chain(spks_from_outputs).collect()
}
}

0 comments on commit 0f52ead

Please sign in to comment.