Skip to content

Commit 23af05f

Browse files
evanlinjinLagginTimes
authored andcommitted
fix(chain,wallet): Provide unconfirmed spends to chain src sync request
Unconfirmed outputs can now be easily provided to the `SyncRequest` via `SyncRequestExt::unconfirmed_spends`. This allows the chain src to detect receiving txs being replaced/cancelled. `Wallet::start_sync_with_revealed_spks` has been deprecated in favor of `start_sync` which included unconfirmed spends.
1 parent 72eb620 commit 23af05f

File tree

2 files changed

+72
-1
lines changed

2 files changed

+72
-1
lines changed

crates/chain/src/indexer/keychain_txout.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,13 @@ use crate::{
77
spk_client::{FullScanRequestBuilder, SyncRequestBuilder},
88
spk_iter::BIP32_MAX_INDEX,
99
spk_txout::SpkTxOutIndex,
10-
DescriptorExt, DescriptorId, Indexed, Indexer, KeychainIndexed, SpkIterator,
10+
Anchor, CanonicalIter, ChainOracle, DescriptorExt, DescriptorId, Indexed, Indexer,
11+
KeychainIndexed, SpkIterator,
1112
};
1213
use alloc::{borrow::ToOwned, vec::Vec};
1314
use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
1415
use core::{
16+
convert::Infallible,
1517
fmt::Debug,
1618
ops::{Bound, RangeBounds},
1719
};
@@ -879,6 +881,18 @@ pub trait SyncRequestBuilderExt<K> {
879881

880882
/// Add [`Script`](bitcoin::Script)s that are revealed by the `indexer` but currently unused.
881883
fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self;
884+
885+
/// Add [`OutPoint`]s which are spent by unconfirmed transactions.
886+
///
887+
/// This allows the chain source to detect transactions which are cancelled/replaced.
888+
fn unconfirmed_outpoints<A, C>(
889+
self,
890+
canonical_iter: CanonicalIter<A, C>,
891+
indexer: &KeychainTxOutIndex<K>,
892+
) -> Self
893+
where
894+
A: Anchor,
895+
C: ChainOracle<Error = Infallible>;
882896
}
883897

884898
impl<K: Clone + Ord + core::fmt::Debug> SyncRequestBuilderExt<K> for SyncRequestBuilder<(K, u32)> {
@@ -892,6 +906,37 @@ impl<K: Clone + Ord + core::fmt::Debug> SyncRequestBuilderExt<K> for SyncRequest
892906
fn unused_spks_from_indexer(self, indexer: &KeychainTxOutIndex<K>) -> Self {
893907
self.spks_with_indexes(indexer.unused_spks())
894908
}
909+
910+
fn unconfirmed_outpoints<A, C>(
911+
self,
912+
canonical_iter: CanonicalIter<A, C>,
913+
indexer: &KeychainTxOutIndex<K>,
914+
) -> Self
915+
where
916+
A: Anchor,
917+
C: ChainOracle<Error = Infallible>,
918+
{
919+
self.outpoints(
920+
canonical_iter
921+
.filter_map(|r| {
922+
let (_, tx, reason) = r.expect("infallible");
923+
match reason {
924+
crate::CanonicalReason::ObservedIn { .. }
925+
if indexer.is_tx_relevant(&tx) =>
926+
{
927+
Some(tx)
928+
}
929+
_ => None,
930+
}
931+
})
932+
.flat_map(|tx| {
933+
tx.input
934+
.iter()
935+
.map(|txin| txin.previous_output)
936+
.collect::<Vec<_>>()
937+
}),
938+
)
939+
}
895940
}
896941

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

crates/wallet/src/wallet/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2445,13 +2445,39 @@ impl Wallet {
24452445
/// This is the first step when performing a spk-based wallet partial sync, the returned
24462446
/// [`SyncRequest`] collects all revealed script pubkeys from the wallet keychain needed to
24472447
/// start a blockchain sync with a spk based blockchain client.
2448+
#[deprecated(
2449+
since = "1.1.0",
2450+
note = "start_sync_with_revealed_spks could not detect receiving transactions being replaced. Use start_sync instead"
2451+
)]
24482452
pub fn start_sync_with_revealed_spks(&self) -> SyncRequestBuilder<(KeychainKind, u32)> {
24492453
use bdk_chain::keychain_txout::SyncRequestBuilderExt;
24502454
SyncRequest::builder()
24512455
.chain_tip(self.chain.tip())
24522456
.revealed_spks_from_indexer(&self.indexed_graph.index, ..)
24532457
}
24542458

2459+
/// Create a [`SyncRequest`] for this wallet.
2460+
///
2461+
/// This assembles a request which initiates a sync against a spk-based chain source. This
2462+
/// request contains all revealed script pubkeys and unconfirmed spends.
2463+
///
2464+
/// This request can detect when transactions get cancelled/replaced.
2465+
pub fn start_sync(&self) -> SyncRequestBuilder<(KeychainKind, u32)> {
2466+
use bdk_chain::keychain_txout::SyncRequestBuilderExt;
2467+
2468+
let chain = &self.chain;
2469+
let tx_graph = &self.indexed_graph.graph();
2470+
let tx_index = &self.indexed_graph.index;
2471+
2472+
SyncRequest::builder()
2473+
.chain_tip(chain.tip())
2474+
.revealed_spks_from_indexer(tx_index, ..)
2475+
.unconfirmed_outpoints(
2476+
tx_graph.canonical_iter(chain, chain.tip().block_id()),
2477+
tx_index,
2478+
)
2479+
}
2480+
24552481
/// Create a [`FullScanRequest] for this wallet.
24562482
///
24572483
/// This is the first step when performing a spk-based wallet full scan, the returned

0 commit comments

Comments
 (0)