1
1
//! Helper types for spk-based blockchain clients.
2
2
use crate :: {
3
3
alloc:: { boxed:: Box , collections:: VecDeque , vec:: Vec } ,
4
- collections:: BTreeMap ,
4
+ collections:: { BTreeMap , HashMap , HashSet } ,
5
5
CheckPoint , ConfirmationBlockTime , Indexed ,
6
6
} ;
7
7
use bitcoin:: { OutPoint , Script , ScriptBuf , Txid } ;
@@ -86,6 +86,28 @@ impl SyncProgress {
86
86
}
87
87
}
88
88
89
+ /// [`Script`] with expected [`Txid`] histories.
90
+ #[ derive( Debug , Clone ) ]
91
+ pub struct SpkWithExpectedTxids {
92
+ /// Script pubkey.
93
+ pub spk : ScriptBuf ,
94
+
95
+ /// [`Txid`]s that we expect to appear in the chain source's spk history response.
96
+ ///
97
+ /// Any transaction listed here that is missing form the spk history response shoud be
98
+ /// considred evicted from the mempool.
99
+ pub expected_txids : HashSet < Txid > ,
100
+ }
101
+
102
+ impl From < ScriptBuf > for SpkWithExpectedTxids {
103
+ fn from ( spk : ScriptBuf ) -> Self {
104
+ Self {
105
+ spk,
106
+ expected_txids : HashSet :: new ( ) ,
107
+ }
108
+ }
109
+ }
110
+
89
111
/// Builds a [`SyncRequest`].
90
112
///
91
113
/// Construct with [`SyncRequest::builder`].
@@ -153,6 +175,20 @@ impl<I> SyncRequestBuilder<I> {
153
175
self
154
176
}
155
177
178
+ /// Add transactions that are expected to exist under then given spks.
179
+ ///
180
+ /// This is useful for detecting a malicious replacement of an incoming transaction.
181
+ pub fn expected_spk_txids ( mut self , txs : impl IntoIterator < Item = ( ScriptBuf , Txid ) > ) -> Self {
182
+ for ( spk, txid) in txs {
183
+ self . inner
184
+ . spk_expected_txids
185
+ . entry ( spk)
186
+ . or_default ( )
187
+ . insert ( txid) ;
188
+ }
189
+ self
190
+ }
191
+
156
192
/// Add [`Txid`]s that will be synced against.
157
193
pub fn txids ( mut self , txids : impl IntoIterator < Item = Txid > ) -> Self {
158
194
self . inner . txids . extend ( txids) ;
@@ -208,6 +244,7 @@ pub struct SyncRequest<I = ()> {
208
244
chain_tip : Option < CheckPoint > ,
209
245
spks : VecDeque < ( I , ScriptBuf ) > ,
210
246
spks_consumed : usize ,
247
+ spk_expected_txids : HashMap < ScriptBuf , HashSet < Txid > > ,
211
248
txids : VecDeque < Txid > ,
212
249
txids_consumed : usize ,
213
250
outpoints : VecDeque < OutPoint > ,
@@ -237,6 +274,7 @@ impl<I> SyncRequest<I> {
237
274
chain_tip : None ,
238
275
spks : VecDeque :: new ( ) ,
239
276
spks_consumed : 0 ,
277
+ spk_expected_txids : HashMap :: new ( ) ,
240
278
txids : VecDeque :: new ( ) ,
241
279
txids_consumed : 0 ,
242
280
outpoints : VecDeque :: new ( ) ,
@@ -292,6 +330,23 @@ impl<I> SyncRequest<I> {
292
330
Some ( spk)
293
331
}
294
332
333
+ /// Advances the sync request and returns the next [`ScriptBuf`] with corresponding [`Txid`]
334
+ /// history.
335
+ ///
336
+ /// Returns [`None`] when there are no more scripts remaining in the request.
337
+ pub fn next_spk_with_expected_txids ( & mut self ) -> Option < SpkWithExpectedTxids > {
338
+ let next_spk = self . next_spk ( ) ?;
339
+ let spk_history = self
340
+ . spk_expected_txids
341
+ . get ( & next_spk)
342
+ . cloned ( )
343
+ . unwrap_or_default ( ) ;
344
+ Some ( SpkWithExpectedTxids {
345
+ spk : next_spk,
346
+ expected_txids : spk_history,
347
+ } )
348
+ }
349
+
295
350
/// Advances the sync request and returns the next [`Txid`].
296
351
///
297
352
/// Returns [`None`] when there are no more txids remaining in the request.
@@ -317,6 +372,13 @@ impl<I> SyncRequest<I> {
317
372
SyncIter :: < I , ScriptBuf > :: new ( self )
318
373
}
319
374
375
+ /// Iterate over [`ScriptBuf`]s with corresponding [`Txid`] histories contained in this request.
376
+ pub fn iter_spks_with_expected_txids (
377
+ & mut self ,
378
+ ) -> impl ExactSizeIterator < Item = SpkWithExpectedTxids > + ' _ {
379
+ SyncIter :: < I , SpkWithExpectedTxids > :: new ( self )
380
+ }
381
+
320
382
/// Iterate over [`Txid`]s contained in this request.
321
383
pub fn iter_txids ( & mut self ) -> impl ExactSizeIterator < Item = Txid > + ' _ {
322
384
SyncIter :: < I , Txid > :: new ( self )
@@ -556,6 +618,19 @@ impl<I> Iterator for SyncIter<'_, I, ScriptBuf> {
556
618
}
557
619
}
558
620
621
+ impl < I > Iterator for SyncIter < ' _ , I , SpkWithExpectedTxids > {
622
+ type Item = SpkWithExpectedTxids ;
623
+
624
+ fn next ( & mut self ) -> Option < Self :: Item > {
625
+ self . request . next_spk_with_expected_txids ( )
626
+ }
627
+
628
+ fn size_hint ( & self ) -> ( usize , Option < usize > ) {
629
+ let remaining = self . request . spks . len ( ) ;
630
+ ( remaining, Some ( remaining) )
631
+ }
632
+ }
633
+
559
634
impl < I > Iterator for SyncIter < ' _ , I , Txid > {
560
635
type Item = Txid ;
561
636
0 commit comments