@@ -5,9 +5,12 @@ use bitcoin::blockdata::constants::genesis_block;
5
5
use bitcoin:: util:: hash:: Sha256dHash ;
6
6
use bitcoin:: network:: constants:: Network ;
7
7
use bitcoin:: network:: serialize:: BitcoinHash ;
8
+
8
9
use util:: logger:: Logger ;
10
+
9
11
use std:: sync:: { Mutex , Weak , MutexGuard , Arc } ;
10
12
use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
13
+ use std:: collections:: HashSet ;
11
14
12
15
/// Used to give chain error details upstream
13
16
pub enum ChainError {
@@ -57,6 +60,8 @@ pub trait ChainListener: Sync + Send {
57
60
/// Note that if a new transaction/outpoint is watched during a block_connected call, the block
58
61
/// *must* be re-scanned with the new transaction/outpoints and block_connected should be
59
62
/// called again with the same header and (at least) the new transactions.
63
+ /// Note that if non-new transaction/outpoints may be registered during a call, a second call
64
+ /// *must not* happen.
60
65
/// This also means those counting confirmations using block_connected callbacks should watch
61
66
/// for duplicate headers and not count them towards confirmations!
62
67
fn block_connected ( & self , header : & BlockHeader , height : u32 , txn_matched : & [ & Transaction ] , indexes_of_txn_matched : & [ u32 ] ) ;
@@ -85,34 +90,124 @@ pub trait FeeEstimator: Sync + Send {
85
90
fn get_est_sat_per_1000_weight ( & self , confirmation_target : ConfirmationTarget ) -> u64 ;
86
91
}
87
92
93
+ /// Utility for tracking registered txn/outpoints and checking for matches
94
+ pub struct ChainWatchedUtil {
95
+ watch_all : bool ,
96
+
97
+ // We are more conservative in matching during testing to ensure everything matches *exactly*,
98
+ // even though during normal runtime we take more optimized match approaches...
99
+ #[ cfg( test) ]
100
+ watched_txn : HashSet < ( Sha256dHash , Script ) > ,
101
+ #[ cfg( not( test) ) ]
102
+ watched_txn : HashSet < Script > ,
103
+
104
+ watched_outpoints : HashSet < ( Sha256dHash , u32 ) > ,
105
+ }
106
+
107
+ impl ChainWatchedUtil {
108
+ /// Constructs an empty (watches nothing) ChainWatchedUtil
109
+ pub fn new ( ) -> Self {
110
+ Self {
111
+ watch_all : false ,
112
+ watched_txn : HashSet :: new ( ) ,
113
+ watched_outpoints : HashSet :: new ( ) ,
114
+ }
115
+ }
116
+
117
+ /// Registers a tx for monitoring, returning true if it was a new tx and false if we'd already
118
+ /// been watching for it.
119
+ pub fn register_tx ( & mut self , txid : & Sha256dHash , script_pub_key : & Script ) -> bool {
120
+ if self . watch_all { return false ; }
121
+ #[ cfg( test) ]
122
+ {
123
+ self . watched_txn . insert ( ( txid. clone ( ) , script_pub_key. clone ( ) ) )
124
+ }
125
+ #[ cfg( not( test) ) ]
126
+ {
127
+ let _tx_unused = txid; // Its used in cfg(test), though
128
+ self . watched_txn . insert ( script_pub_key. clone ( ) )
129
+ }
130
+ }
131
+
132
+ /// Registers an outpoint for monitoring, returning true if it was a new outpoint and false if
133
+ /// we'd already been watching for it
134
+ pub fn register_outpoint ( & mut self , outpoint : ( Sha256dHash , u32 ) , _script_pub_key : & Script ) -> bool {
135
+ if self . watch_all { return false ; }
136
+ self . watched_outpoints . insert ( outpoint)
137
+ }
138
+
139
+ /// Sets us to match all transactions, returning true if this is a new setting anf false if
140
+ /// we'd already been set to match everything.
141
+ pub fn watch_all ( & mut self ) -> bool {
142
+ if self . watch_all { return false ; }
143
+ self . watch_all = true ;
144
+ true
145
+ }
146
+
147
+ /// Checks if a given transaction matches the current filter.
148
+ pub fn does_match_tx ( & self , tx : & Transaction ) -> bool {
149
+ if self . watch_all {
150
+ return true ;
151
+ }
152
+ for out in tx. output . iter ( ) {
153
+ #[ cfg( test) ]
154
+ for & ( ref txid, ref script) in self . watched_txn . iter ( ) {
155
+ if * script == out. script_pubkey {
156
+ if tx. txid ( ) == * txid {
157
+ return true ;
158
+ }
159
+ }
160
+ }
161
+ #[ cfg( not( test) ) ]
162
+ for script in self . watched_txn . iter ( ) {
163
+ if * script == out. script_pubkey {
164
+ return true ;
165
+ }
166
+ }
167
+ }
168
+ for input in tx. input . iter ( ) {
169
+ for outpoint in self . watched_outpoints . iter ( ) {
170
+ let & ( outpoint_hash, outpoint_index) = outpoint;
171
+ if outpoint_hash == input. previous_output . txid && outpoint_index == input. previous_output . vout {
172
+ return true ;
173
+ }
174
+ }
175
+ }
176
+ false
177
+ }
178
+ }
179
+
88
180
/// Utility to capture some common parts of ChainWatchInterface implementors.
89
181
/// Keeping a local copy of this in a ChainWatchInterface implementor is likely useful.
90
182
pub struct ChainWatchInterfaceUtil {
91
183
network : Network ,
92
- watched : Mutex < ( Vec < Script > , Vec < ( Sha256dHash , u32 ) > , bool ) > , //TODO: Something clever to optimize this
184
+ watched : Mutex < ChainWatchedUtil > ,
93
185
listeners : Mutex < Vec < Weak < ChainListener > > > ,
94
186
reentered : AtomicUsize ,
95
187
logger : Arc < Logger > ,
96
188
}
97
189
98
190
/// Register listener
99
191
impl ChainWatchInterface for ChainWatchInterfaceUtil {
100
- fn install_watch_tx ( & self , _txid : & Sha256dHash , script_pub_key : & Script ) {
192
+ fn install_watch_tx ( & self , txid : & Sha256dHash , script_pub_key : & Script ) {
101
193
let mut watched = self . watched . lock ( ) . unwrap ( ) ;
102
- watched. 0 . push ( script_pub_key. clone ( ) ) ;
103
- self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
194
+ if watched. register_tx ( txid, script_pub_key) {
195
+ self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
196
+ }
104
197
}
105
198
106
- fn install_watch_outpoint ( & self , outpoint : ( Sha256dHash , u32 ) , _out_script : & Script ) {
199
+ fn install_watch_outpoint ( & self , outpoint : ( Sha256dHash , u32 ) , out_script : & Script ) {
107
200
let mut watched = self . watched . lock ( ) . unwrap ( ) ;
108
- watched. 1 . push ( outpoint) ;
109
- self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
201
+ if watched. register_outpoint ( outpoint, out_script) {
202
+ self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
203
+ }
110
204
}
111
205
112
206
fn watch_all_txn ( & self ) {
113
207
let mut watched = self . watched . lock ( ) . unwrap ( ) ;
114
- watched. 2 = true ;
115
- self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
208
+ if watched. watch_all ( ) {
209
+ self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
210
+ }
116
211
}
117
212
118
213
fn register_listener ( & self , listener : Weak < ChainListener > ) {
@@ -132,7 +227,7 @@ impl ChainWatchInterfaceUtil {
132
227
pub fn new ( network : Network , logger : Arc < Logger > ) -> ChainWatchInterfaceUtil {
133
228
ChainWatchInterfaceUtil {
134
229
network : network,
135
- watched : Mutex :: new ( ( Vec :: new ( ) , Vec :: new ( ) , false ) ) ,
230
+ watched : Mutex :: new ( ChainWatchedUtil :: new ( ) ) ,
136
231
listeners : Mutex :: new ( Vec :: new ( ) ) ,
137
232
reentered : AtomicUsize :: new ( 1 ) ,
138
233
logger : logger,
@@ -195,25 +290,7 @@ impl ChainWatchInterfaceUtil {
195
290
self . does_match_tx_unguarded ( tx, & watched)
196
291
}
197
292
198
- fn does_match_tx_unguarded ( & self , tx : & Transaction , watched : & MutexGuard < ( Vec < Script > , Vec < ( Sha256dHash , u32 ) > , bool ) > ) -> bool {
199
- if watched. 2 {
200
- return true ;
201
- }
202
- for out in tx. output . iter ( ) {
203
- for script in watched. 0 . iter ( ) {
204
- if script[ ..] == out. script_pubkey [ ..] {
205
- return true ;
206
- }
207
- }
208
- }
209
- for input in tx. input . iter ( ) {
210
- for outpoint in watched. 1 . iter ( ) {
211
- let & ( outpoint_hash, outpoint_index) = outpoint;
212
- if outpoint_hash == input. previous_output . txid && outpoint_index == input. previous_output . vout {
213
- return true ;
214
- }
215
- }
216
- }
217
- false
293
+ fn does_match_tx_unguarded ( & self , tx : & Transaction , watched : & MutexGuard < ChainWatchedUtil > ) -> bool {
294
+ watched. does_match_tx ( tx)
218
295
}
219
296
}
0 commit comments