1
- use bitcoin:: blockdata:: block:: BlockHeader ;
1
+ use bitcoin:: blockdata:: block:: { Block , BlockHeader } ;
2
2
use bitcoin:: blockdata:: transaction:: Transaction ;
3
3
use bitcoin:: blockdata:: script:: Script ;
4
4
use bitcoin:: util:: hash:: Sha256dHash ;
5
-
6
- use std:: sync:: { Weak , Mutex } ;
5
+ use std :: sync :: { Mutex , Weak , MutexGuard } ;
6
+ use std:: sync:: atomic :: { AtomicUsize , Ordering } ;
7
7
8
8
/// An interface to request notification of certain scripts as they appear the
9
9
/// chain.
@@ -21,13 +21,18 @@ pub trait ChainWatchInterface: Sync + Send {
21
21
/// Indicates that a listener needs to see all transactions.
22
22
fn watch_all_txn ( & self ) ;
23
23
24
- /// Sends a transaction out to (hopefully) be mined
25
- fn broadcast_transaction ( & self , tx : & Transaction ) ;
26
-
27
24
fn register_listener ( & self , listener : Weak < ChainListener > ) ;
28
25
//TODO: unregister
29
26
}
30
27
28
+ /// An interface to send a transaction to connected Bitcoin peers.
29
+ /// This is for final settlement. An error might indicate that no peers can be reached or
30
+ /// that peers rejected the transaction.
31
+ pub trait BroadcasterInterface : Sync + Send {
32
+ /// Sends a transaction out to (hopefully) be mined
33
+ fn broadcast_transaction ( & self , tx : & Transaction ) ;
34
+ }
35
+
31
36
/// A trait indicating a desire to listen for events from the chain
32
37
pub trait ChainListener : Sync + Send {
33
38
/// Notifies a listener that a block was connected.
@@ -54,67 +59,105 @@ pub enum ConfirmationTarget {
54
59
/// called from inside the library in response to ChainListener events, P2P events, or timer
55
60
/// events).
56
61
pub trait FeeEstimator : Sync + Send {
57
- fn get_est_sat_per_vbyte ( & self , ConfirmationTarget ) -> u64 ;
62
+ fn get_est_sat_per_vbyte ( & self , confirmation_target : ConfirmationTarget ) -> u64 ;
58
63
}
59
64
60
65
/// Utility to capture some common parts of ChainWatchInterface implementors.
61
66
/// Keeping a local copy of this in a ChainWatchInterface implementor is likely useful.
62
67
pub struct ChainWatchInterfaceUtil {
63
68
watched : Mutex < ( Vec < Script > , Vec < ( Sha256dHash , u32 ) > , bool ) > , //TODO: Something clever to optimize this
64
69
listeners : Mutex < Vec < Weak < ChainListener > > > ,
70
+ reentered : AtomicUsize
65
71
}
66
72
67
- impl ChainWatchInterfaceUtil {
68
- pub fn new ( ) -> ChainWatchInterfaceUtil {
69
- ChainWatchInterfaceUtil {
70
- watched : Mutex :: new ( ( Vec :: new ( ) , Vec :: new ( ) , false ) ) ,
71
- listeners : Mutex :: new ( Vec :: new ( ) ) ,
72
- }
73
- }
74
-
75
- pub fn install_watch_script ( & self , spk : Script ) {
73
+ /// Register listener
74
+ impl ChainWatchInterface for ChainWatchInterfaceUtil {
75
+ fn install_watch_script ( & self , script_pub_key : Script ) {
76
76
let mut watched = self . watched . lock ( ) . unwrap ( ) ;
77
- watched. 0 . push ( Script :: from ( spk) ) ;
77
+ watched. 0 . push ( Script :: from ( script_pub_key) ) ;
78
+ self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
78
79
}
79
80
80
- pub fn install_watch_outpoint ( & self , outpoint : ( Sha256dHash , u32 ) ) {
81
+ fn install_watch_outpoint ( & self , outpoint : ( Sha256dHash , u32 ) ) {
81
82
let mut watched = self . watched . lock ( ) . unwrap ( ) ;
82
83
watched. 1 . push ( outpoint) ;
84
+ self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
83
85
}
84
86
85
- pub fn watch_all_txn ( & self ) { //TODO: refcnt this?
87
+ fn watch_all_txn ( & self ) {
86
88
let mut watched = self . watched . lock ( ) . unwrap ( ) ;
87
89
watched. 2 = true ;
90
+ self . reentered . fetch_add ( 1 , Ordering :: Relaxed ) ;
88
91
}
89
92
90
- pub fn register_listener ( & self , listener : Weak < ChainListener > ) {
93
+ fn register_listener ( & self , listener : Weak < ChainListener > ) {
91
94
let mut vec = self . listeners . lock ( ) . unwrap ( ) ;
92
95
vec. push ( listener) ;
93
96
}
97
+ }
94
98
95
- pub fn do_call_block_connected ( & self , header : & BlockHeader , height : u32 , txn_matched : & [ & Transaction ] , indexes_of_txn_matched : & [ u32 ] ) {
99
+ impl ChainWatchInterfaceUtil {
100
+ pub fn new ( ) -> ChainWatchInterfaceUtil {
101
+ ChainWatchInterfaceUtil {
102
+ watched : Mutex :: new ( ( Vec :: new ( ) , Vec :: new ( ) , false ) ) ,
103
+ listeners : Mutex :: new ( Vec :: new ( ) ) ,
104
+ reentered : AtomicUsize :: new ( 1 )
105
+ }
106
+ }
107
+
108
+ /// notify listener that a block was connected
109
+ /// notification will repeat if notified listener register new listeners
110
+ pub fn block_connected_with_filtering ( & self , block : & Block , height : u32 ) {
111
+ let mut reentered = true ;
112
+ while reentered {
113
+ let mut matched = Vec :: new ( ) ;
114
+ let mut matched_index = Vec :: new ( ) ;
115
+ {
116
+ let watched = self . watched . lock ( ) . unwrap ( ) ;
117
+ for ( index, transaction) in block. txdata . iter ( ) . enumerate ( ) {
118
+ if self . does_match_tx_unguarded ( transaction, & watched) {
119
+ matched. push ( transaction) ;
120
+ matched_index. push ( index as u32 ) ;
121
+ }
122
+ }
123
+ }
124
+ reentered = self . block_connected_checked ( & block. header , height, matched. as_slice ( ) , matched_index. as_slice ( ) ) ;
125
+ }
126
+ }
127
+
128
+ /// notify listener that a block was disconnected
129
+ pub fn block_disconnected ( & self , header : & BlockHeader ) {
96
130
let listeners = self . listeners . lock ( ) . unwrap ( ) . clone ( ) ;
97
131
for listener in listeners. iter ( ) {
98
132
match listener. upgrade ( ) {
99
- Some ( arc) => arc. block_connected ( header, height , txn_matched , indexes_of_txn_matched ) ,
133
+ Some ( arc) => arc. block_disconnected ( header) ,
100
134
None => ( )
101
135
}
102
136
}
103
137
}
104
138
105
- pub fn do_call_block_disconnected ( & self , header : & BlockHeader ) {
139
+ /// call listeners for connected blocks if they are still around.
140
+ /// returns true if notified listeners registered additional listener
141
+ pub fn block_connected_checked ( & self , header : & BlockHeader , height : u32 , txn_matched : & [ & Transaction ] , indexes_of_txn_matched : & [ u32 ] ) -> bool {
142
+ let last_seen = self . reentered . load ( Ordering :: Relaxed ) ;
143
+
106
144
let listeners = self . listeners . lock ( ) . unwrap ( ) . clone ( ) ;
107
145
for listener in listeners. iter ( ) {
108
146
match listener. upgrade ( ) {
109
- Some ( arc) => arc. block_disconnected ( header) ,
147
+ Some ( arc) => arc. block_connected ( header, height , txn_matched , indexes_of_txn_matched ) ,
110
148
None => ( )
111
149
}
112
150
}
151
+ return last_seen != self . reentered . load ( Ordering :: Relaxed ) ;
113
152
}
114
153
115
154
/// Checks if a given transaction matches the current filter
116
155
pub fn does_match_tx ( & self , tx : & Transaction ) -> bool {
117
156
let watched = self . watched . lock ( ) . unwrap ( ) ;
157
+ self . does_match_tx_unguarded ( tx, & watched)
158
+ }
159
+
160
+ fn does_match_tx_unguarded ( & self , tx : & Transaction , watched : & MutexGuard < ( Vec < Script > , Vec < ( Sha256dHash , u32 ) > , bool ) > ) -> bool {
118
161
if watched. 2 {
119
162
return true ;
120
163
}
0 commit comments