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