Skip to content

Commit be60ada

Browse files
evanlinjinLagginTimes
authored andcommitted
feat(chain)!: IndexedTxGraph::expected_unconfirmed_spk_txids
Make this method work when the indexer is `KeychainTxOutIndex`. We reintroduce the ability to get the internal `SpkTxOutIndex` from `KeychainTxOutIndex` so that `SpkTxOutIndex::relevant_spks_of_tx` is callable from `KeychainTxOutIndex`. This commit renames `iter_spks_with_expected_txids` to `expected_unconfirmed_spk_txids` for `TxGraph`, `IndexedTxGraph` and `SyncRequestBuilder`. Docs are also improved to explain how these methods are useful. Remove unused `SyncRequestBuilder` methods.
1 parent 9f6a253 commit be60ada

File tree

9 files changed

+67
-83
lines changed

9 files changed

+67
-83
lines changed

crates/bitcoind_rpc/tests/test_emitter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -808,7 +808,7 @@ fn test_expect_tx_missing() -> anyhow::Result<()> {
808808

809809
// We evict the expected txs that are missing from mempool
810810
let exp_txids = graph
811-
.iter_spks_with_expected_txids(&chain, ..)
811+
.expected_unconfirmed_spk_txids(&chain, ..)
812812
.collect::<Vec<_>>();
813813
assert_eq!(exp_txids, vec![(txid_1, spk)]);
814814
let mempool = emitter

crates/chain/src/indexed_tx_graph.rs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Contains the [`IndexedTxGraph`] and associated types. Refer to the
22
//! [`IndexedTxGraph`] documentation for more.
33
4-
use core::fmt;
54
use core::ops::RangeBounds;
65

76
use alloc::{sync::Arc, vec::Vec};
@@ -345,15 +344,15 @@ where
345344
impl<A, I> IndexedTxGraph<A, SpkTxOutIndex<I>>
346345
where
347346
A: Anchor,
348-
I: fmt::Debug + Clone + Ord,
347+
I: core::fmt::Debug + Clone + Ord,
349348
{
350-
/// Returns an iterator over unconfirmed transactions and their associated script pubkeys,
351-
/// filtered within the specified `range`.
349+
/// Iterate over unconfirmed txids that we expect to exist in a chain source's spk history
350+
/// response.
352351
///
353-
/// This function delegates the transaction filtering to [`TxGraph::iter_spks_with_expected_txids`],
354-
/// using the [`SpkTxOutIndex`] stored in [`IndexedTxGraph`]. The [`TxGraph`] internally scans
355-
/// for unconfirmed transactions relevant to the indexed outputs.
356-
pub fn iter_spks_with_expected_txids<'a, O>(
352+
/// This is used to fill [`SyncRequestBuilder::expected_unconfirmed_spk_txids`](bdk_core::spk_client::SyncRequestBuilder::expected_unconfirmed_spk_txids).
353+
///
354+
/// The spk range can be contrained with `range`.
355+
pub fn expected_unconfirmed_spk_txids<'a, O>(
357356
&'a self,
358357
chain: &'a O,
359358
range: impl RangeBounds<I> + 'a,
@@ -362,7 +361,31 @@ where
362361
O: ChainOracle<Error = core::convert::Infallible>,
363362
{
364363
self.graph
365-
.iter_spks_with_expected_txids(chain, &self.index, range)
364+
.expected_unconfirmed_spk_txids(chain, &self.index, range)
365+
}
366+
}
367+
368+
impl<A, K> IndexedTxGraph<A, crate::keychain_txout::KeychainTxOutIndex<K>>
369+
where
370+
A: Anchor,
371+
K: core::fmt::Debug + Clone + Ord,
372+
{
373+
/// Iterate over unconfirmed txids that we expect to exist in a chain source's spk history
374+
/// response.
375+
///
376+
/// This is used to fill [`SyncRequestBuilder::expected_unconfirmed_spk_txids`](bdk_core::spk_client::SyncRequestBuilder::expected_unconfirmed_spk_txids).
377+
///
378+
/// The spk range can be contrained with `range`.
379+
pub fn expected_unconfirmed_spk_txids<'a, O>(
380+
&'a self,
381+
chain: &'a O,
382+
range: impl RangeBounds<(K, u32)> + 'a,
383+
) -> impl Iterator<Item = (Txid, ScriptBuf)> + 'a
384+
where
385+
O: ChainOracle<Error = core::convert::Infallible>,
386+
{
387+
self.graph
388+
.expected_unconfirmed_spk_txids(chain, &self.index, range)
366389
}
367390
}
368391

crates/chain/src/indexer/keychain_txout.rs

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,11 @@ use crate::{
77
spk_client::{FullScanRequestBuilder, SyncRequestBuilder},
88
spk_iter::BIP32_MAX_INDEX,
99
spk_txout::SpkTxOutIndex,
10-
Anchor, CanonicalIter, CanonicalReason, ChainOracle, DescriptorExt, DescriptorId, Indexed,
11-
Indexer, KeychainIndexed, SpkIterator,
10+
DescriptorExt, DescriptorId, Indexed, Indexer, KeychainIndexed, SpkIterator,
1211
};
1312
use alloc::{borrow::ToOwned, vec::Vec};
1413
use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
1514
use core::{
16-
convert::Infallible,
1715
fmt::Debug,
1816
ops::{Bound, RangeBounds},
1917
};
@@ -138,6 +136,12 @@ impl<K> Default for KeychainTxOutIndex<K> {
138136
}
139137
}
140138

139+
impl<K> AsRef<SpkTxOutIndex<(K, u32)>> for KeychainTxOutIndex<K> {
140+
fn as_ref(&self) -> &SpkTxOutIndex<(K, u32)> {
141+
self.inner()
142+
}
143+
}
144+
141145
impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
142146
type ChangeSet = ChangeSet;
143147

@@ -202,6 +206,11 @@ impl<K> KeychainTxOutIndex<K> {
202206
lookahead,
203207
}
204208
}
209+
210+
/// Get a reference to the internal [`SpkTxOutIndex`].
211+
pub fn inner(&self) -> &SpkTxOutIndex<(K, u32)> {
212+
&self.inner
213+
}
205214
}
206215

207216
/// Methods that are *re-exposed* from the internal [`SpkTxOutIndex`].
@@ -881,20 +890,6 @@ pub trait SyncRequestBuilderExt<K> {
881890

882891
/// Add [`Script`](bitcoin::Script)s that are revealed by the `indexer` but currently unused.
883892
fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self;
884-
885-
/// Add unconfirmed txids and their associated spks.
886-
///
887-
/// We expect that the chain source should include these txids in their spk histories. If not,
888-
/// the transaction has been evicted for some reason and we will inform the receiving
889-
/// structures in the response.
890-
fn check_unconfirmed_statuses<A, C>(
891-
self,
892-
indexer: &KeychainTxOutIndex<K>,
893-
canonical_iter: CanonicalIter<A, C>,
894-
) -> Self
895-
where
896-
A: Anchor,
897-
C: ChainOracle<Error = Infallible>;
898893
}
899894

900895
impl<K: Clone + Ord + core::fmt::Debug> SyncRequestBuilderExt<K> for SyncRequestBuilder<(K, u32)> {
@@ -908,29 +903,6 @@ impl<K: Clone + Ord + core::fmt::Debug> SyncRequestBuilderExt<K> for SyncRequest
908903
fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self {
909904
self.spks_with_indexes(indexer.unused_spks())
910905
}
911-
912-
fn check_unconfirmed_statuses<A, C>(
913-
self,
914-
indexer: &KeychainTxOutIndex<K>,
915-
canonical_iter: CanonicalIter<A, C>,
916-
) -> Self
917-
where
918-
A: Anchor,
919-
C: ChainOracle<Error = Infallible>,
920-
{
921-
self.expected_txids_of_spk(
922-
canonical_iter
923-
.map(|res| res.expect("infallible"))
924-
.filter(|(_, _, reason)| matches!(reason, CanonicalReason::ObservedIn { .. }))
925-
.flat_map(|(txid, tx, _)| {
926-
indexer
927-
.inner
928-
.relevant_spks_of_tx(tx.as_ref())
929-
.into_iter()
930-
.map(move |spk| (txid, spk))
931-
}),
932-
)
933-
}
934906
}
935907

936908
/// Trait to extend [`FullScanRequestBuilder`].

crates/chain/src/indexer/spk_txout.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ pub struct SpkTxOutIndex<I> {
4242
spk_txouts: BTreeSet<(I, OutPoint)>,
4343
}
4444

45+
impl<I> AsRef<SpkTxOutIndex<I>> for SpkTxOutIndex<I> {
46+
fn as_ref(&self) -> &SpkTxOutIndex<I> {
47+
self
48+
}
49+
}
50+
4551
impl<I> Default for SpkTxOutIndex<I> {
4652
fn default() -> Self {
4753
Self {

crates/chain/src/tx_graph.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,23 +1164,24 @@ impl<A: Anchor> TxGraph<A> {
11641164
.expect("oracle is infallible")
11651165
}
11661166

1167-
/// Returns an iterator over unconfirmed transactions and their associated script pubkeys,
1168-
/// filtered within the specified `range`.
1167+
/// Iterate over unconfirmed txids that we expect to exist in a chain source's spk history
1168+
/// response.
11691169
///
1170-
/// This function scans the transaction graph for unconfirmed transactions relevant to the
1171-
/// provided [`SpkTxOutIndex`], determining which transactions should be considered based on
1172-
/// indexed outputs.
1173-
pub fn iter_spks_with_expected_txids<'a, C, I>(
1170+
/// This is used to fill [`SyncRequestBuilder::expected_unconfirmed_spk_txids`](bdk_core::spk_client::SyncRequestBuilder::expected_unconfirmed_spk_txids).
1171+
///
1172+
/// The spk range can be contrained with `range`.
1173+
pub fn expected_unconfirmed_spk_txids<'a, C, I>(
11741174
&'a self,
11751175
chain: &'a C,
1176-
indexer: &'a SpkTxOutIndex<I>,
1176+
indexer: &'a impl AsRef<SpkTxOutIndex<I>>,
11771177
range: impl RangeBounds<I> + 'a,
11781178
) -> impl Iterator<Item = (Txid, ScriptBuf)> + 'a
11791179
where
11801180
C: ChainOracle<Error = core::convert::Infallible>,
1181-
I: fmt::Debug + Clone + Ord,
1181+
I: fmt::Debug + Clone + Ord + 'a,
11821182
{
11831183
let chain_tip = chain.get_chain_tip().unwrap();
1184+
let indexer = indexer.as_ref();
11841185

11851186
self.list_canonical_txs(chain, chain_tip)
11861187
.filter(|c| !c.chain_position.is_confirmed() && indexer.is_tx_relevant(&c.tx_node))

crates/core/src/spk_client.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ impl<I> SyncRequestBuilder<I> {
170170
/// Add transactions that are expected to exist under a given spk.
171171
///
172172
/// This is useful for detecting a malicious replacement of an incoming transaction.
173-
pub fn expected_txids_of_spk(
173+
pub fn expected_unconfirmed_spk_txids(
174174
mut self,
175175
txs: impl IntoIterator<Item = (Txid, ScriptBuf)>,
176176
) -> Self {

crates/electrum/tests/test_electrum.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
135135
let sync_request = SyncRequest::builder()
136136
.chain_tip(chain.tip())
137137
.revealed_spks_from_indexer(&graph.index, ..)
138-
.check_unconfirmed_statuses(
139-
&graph.index,
140-
graph.graph().canonical_iter(&chain, chain.tip().block_id()),
141-
);
138+
.expected_unconfirmed_spk_txids(graph.expected_unconfirmed_spk_txids(&chain, ..));
142139
let sync_response = client.sync(sync_request, BATCH_SIZE, true)?;
143140
assert!(
144141
sync_response
@@ -163,10 +160,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
163160
let sync_request = SyncRequest::builder()
164161
.chain_tip(chain.tip())
165162
.revealed_spks_from_indexer(&graph.index, ..)
166-
.check_unconfirmed_statuses(
167-
&graph.index,
168-
graph.graph().canonical_iter(&chain, chain.tip().block_id()),
169-
);
163+
.expected_unconfirmed_spk_txids(graph.expected_unconfirmed_spk_txids(&chain, ..));
170164
let sync_response = client.sync(sync_request, BATCH_SIZE, true)?;
171165
assert!(
172166
sync_response.tx_update.missing.contains(&send_txid),

crates/esplora/tests/async_ext.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,7 @@ pub async fn detect_receive_tx_cancel() -> anyhow::Result<()> {
9494
let sync_request = SyncRequest::builder()
9595
.chain_tip(chain.tip())
9696
.revealed_spks_from_indexer(&graph.index, ..)
97-
.check_unconfirmed_statuses(
98-
&graph.index,
99-
graph.graph().canonical_iter(&chain, chain.tip().block_id()),
100-
);
97+
.expected_unconfirmed_spk_txids(graph.expected_unconfirmed_spk_txids(&chain, ..));
10198
let sync_response = client.sync(sync_request, 1).await?;
10299
assert!(
103100
sync_response
@@ -122,10 +119,7 @@ pub async fn detect_receive_tx_cancel() -> anyhow::Result<()> {
122119
let sync_request = SyncRequest::builder()
123120
.chain_tip(chain.tip())
124121
.revealed_spks_from_indexer(&graph.index, ..)
125-
.check_unconfirmed_statuses(
126-
&graph.index,
127-
graph.graph().canonical_iter(&chain, chain.tip().block_id()),
128-
);
122+
.expected_unconfirmed_spk_txids(graph.expected_unconfirmed_spk_txids(&chain, ..));
129123
let sync_response = client.sync(sync_request, 1).await?;
130124
assert!(
131125
sync_response.tx_update.missing.contains(&send_txid),

crates/esplora/tests/blocking_ext.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
9494
let sync_request = SyncRequest::builder()
9595
.chain_tip(chain.tip())
9696
.revealed_spks_from_indexer(&graph.index, ..)
97-
.check_unconfirmed_statuses(
98-
&graph.index,
99-
graph.graph().canonical_iter(&chain, chain.tip().block_id()),
100-
);
97+
.expected_unconfirmed_spk_txids(graph.expected_unconfirmed_spk_txids(&chain, ..));
10198
let sync_response = client.sync(sync_request, 1)?;
10299
assert!(
103100
sync_response
@@ -122,10 +119,7 @@ pub fn detect_receive_tx_cancel() -> anyhow::Result<()> {
122119
let sync_request = SyncRequest::builder()
123120
.chain_tip(chain.tip())
124121
.revealed_spks_from_indexer(&graph.index, ..)
125-
.check_unconfirmed_statuses(
126-
&graph.index,
127-
graph.graph().canonical_iter(&chain, chain.tip().block_id()),
128-
);
122+
.expected_unconfirmed_spk_txids(graph.expected_unconfirmed_spk_txids(&chain, ..));
129123
let sync_response = client.sync(sync_request, 1)?;
130124
assert!(
131125
sync_response.tx_update.missing.contains(&send_txid),

0 commit comments

Comments
 (0)