chaininterface: add BlockNotifier struct
[rust-lightning] / lightning / src / chain / chaininterface.rs
1 //! Traits and utility impls which allow other parts of rust-lightning to interact with the
2 //! blockchain.
3 //!
4 //! Includes traits for monitoring and receiving notifications of new blocks and block
5 //! disconnections, transaction broadcasting, and feerate information requests.
6
7 use bitcoin::blockdata::block::{Block, BlockHeader};
8 use bitcoin::blockdata::transaction::Transaction;
9 use bitcoin::blockdata::script::Script;
10 use bitcoin::blockdata::constants::genesis_block;
11 use bitcoin::util::hash::BitcoinHash;
12 use bitcoin_hashes::sha256d::Hash as Sha256dHash;
13 use bitcoin::network::constants::Network;
14
15 use util::logger::Logger;
16
17 use std::sync::{Mutex,Weak,MutexGuard,Arc};
18 use std::sync::atomic::{AtomicUsize, Ordering};
19 use std::collections::HashSet;
20
21 /// Used to give chain error details upstream
22 pub enum ChainError {
23         /// Client doesn't support UTXO lookup (but the chain hash matches our genesis block hash)
24         NotSupported,
25         /// Chain isn't the one watched
26         NotWatched,
27         /// Tx doesn't exist or is unconfirmed
28         UnknownTx,
29 }
30
31 /// An interface to request notification of certain scripts as they appear the
32 /// chain.
33 ///
34 /// Note that all of the functions implemented here *must* be reentrant-safe (obviously - they're
35 /// called from inside the library in response to ChainListener events, P2P events, or timer
36 /// events).
37 pub trait ChainWatchInterface: Sync + Send {
38         /// Provides a txid/random-scriptPubKey-in-the-tx which much be watched for.
39         fn install_watch_tx(&self, txid: &Sha256dHash, script_pub_key: &Script);
40
41         /// Provides an outpoint which must be watched for, providing any transactions which spend the
42         /// given outpoint.
43         fn install_watch_outpoint(&self, outpoint: (Sha256dHash, u32), out_script: &Script);
44
45         /// Indicates that a listener needs to see all transactions.
46         fn watch_all_txn(&self);
47
48         /// Gets the script and value in satoshis for a given unspent transaction output given a
49         /// short_channel_id (aka unspent_tx_output_identier). For BTC/tBTC channels the top three
50         /// bytes are the block height, the next 3 the transaction index within the block, and the
51         /// final two the output within the transaction.
52         fn get_chain_utxo(&self, genesis_hash: Sha256dHash, unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError>;
53 }
54
55 /// An interface to send a transaction to the Bitcoin network.
56 pub trait BroadcasterInterface: Sync + Send {
57         /// Sends a transaction out to (hopefully) be mined.
58         fn broadcast_transaction(&self, tx: &Transaction);
59 }
60
61 /// A trait indicating a desire to listen for events from the chain
62 pub trait ChainListener: Sync + Send {
63         /// Notifies a listener that a block was connected.
64         /// Note that if a new transaction/outpoint is watched during a block_connected call, the block
65         /// *must* be re-scanned with the new transaction/outpoints and block_connected should be
66         /// called again with the same header and (at least) the new transactions.
67         ///
68         /// Note that if non-new transaction/outpoints may be registered during a call, a second call
69         /// *must not* happen.
70         ///
71         /// This also means those counting confirmations using block_connected callbacks should watch
72         /// for duplicate headers and not count them towards confirmations!
73         fn block_connected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]);
74         /// Notifies a listener that a block was disconnected.
75         /// Unlike block_connected, this *must* never be called twice for the same disconnect event.
76         /// Height must be the one of the block which was disconnected (not new height of the best chain)
77         fn block_disconnected(&self, header: &BlockHeader, disconnected_height: u32);
78 }
79
80 /// An enum that represents the speed at which we want a transaction to confirm used for feerate
81 /// estimation.
82 pub enum ConfirmationTarget {
83         /// We are happy with this transaction confirming slowly when feerate drops some.
84         Background,
85         /// We'd like this transaction to confirm without major delay, but 12-18 blocks is fine.
86         Normal,
87         /// We'd like this transaction to confirm in the next few blocks.
88         HighPriority,
89 }
90
91 /// A trait which should be implemented to provide feerate information on a number of time
92 /// horizons.
93 ///
94 /// Note that all of the functions implemented here *must* be reentrant-safe (obviously - they're
95 /// called from inside the library in response to ChainListener events, P2P events, or timer
96 /// events).
97 pub trait FeeEstimator: Sync + Send {
98         /// Gets estimated satoshis of fee required per 1000 Weight-Units.
99         ///
100         /// Must be no smaller than 253 (ie 1 satoshi-per-byte rounded up to ensure later round-downs
101         /// don't put us below 1 satoshi-per-byte).
102         ///
103         /// This translates to:
104         ///  * satoshis-per-byte * 250
105         ///  * ceil(satoshis-per-kbyte / 4)
106         fn get_est_sat_per_1000_weight(&self, confirmation_target: ConfirmationTarget) -> u64;
107 }
108
109 /// Utility for tracking registered txn/outpoints and checking for matches
110 pub struct ChainWatchedUtil {
111         watch_all: bool,
112
113         // We are more conservative in matching during testing to ensure everything matches *exactly*,
114         // even though during normal runtime we take more optimized match approaches...
115         #[cfg(test)]
116         watched_txn: HashSet<(Sha256dHash, Script)>,
117         #[cfg(not(test))]
118         watched_txn: HashSet<Script>,
119
120         watched_outpoints: HashSet<(Sha256dHash, u32)>,
121 }
122
123 impl ChainWatchedUtil {
124         /// Constructs an empty (watches nothing) ChainWatchedUtil
125         pub fn new() -> Self {
126                 Self {
127                         watch_all: false,
128                         watched_txn: HashSet::new(),
129                         watched_outpoints: HashSet::new(),
130                 }
131         }
132
133         /// Registers a tx for monitoring, returning true if it was a new tx and false if we'd already
134         /// been watching for it.
135         pub fn register_tx(&mut self, txid: &Sha256dHash, script_pub_key: &Script) -> bool {
136                 if self.watch_all { return false; }
137                 #[cfg(test)]
138                 {
139                         self.watched_txn.insert((txid.clone(), script_pub_key.clone()))
140                 }
141                 #[cfg(not(test))]
142                 {
143                         let _tx_unused = txid; // It's used in cfg(test), though
144                         self.watched_txn.insert(script_pub_key.clone())
145                 }
146         }
147
148         /// Registers an outpoint for monitoring, returning true if it was a new outpoint and false if
149         /// we'd already been watching for it
150         pub fn register_outpoint(&mut self, outpoint: (Sha256dHash, u32), _script_pub_key: &Script) -> bool {
151                 if self.watch_all { return false; }
152                 self.watched_outpoints.insert(outpoint)
153         }
154
155         /// Sets us to match all transactions, returning true if this is a new setting and false if
156         /// we'd already been set to match everything.
157         pub fn watch_all(&mut self) -> bool {
158                 if self.watch_all { return false; }
159                 self.watch_all = true;
160                 true
161         }
162
163         /// Checks if a given transaction matches the current filter.
164         pub fn does_match_tx(&self, tx: &Transaction) -> bool {
165                 if self.watch_all {
166                         return true;
167                 }
168                 for out in tx.output.iter() {
169                         #[cfg(test)]
170                         for &(ref txid, ref script) in self.watched_txn.iter() {
171                                 if *script == out.script_pubkey {
172                                         if tx.txid() == *txid {
173                                                 return true;
174                                         }
175                                 }
176                         }
177                         #[cfg(not(test))]
178                         for script in self.watched_txn.iter() {
179                                 if *script == out.script_pubkey {
180                                         return true;
181                                 }
182                         }
183                 }
184                 for input in tx.input.iter() {
185                         for outpoint in self.watched_outpoints.iter() {
186                                 let &(outpoint_hash, outpoint_index) = outpoint;
187                                 if outpoint_hash == input.previous_output.txid && outpoint_index == input.previous_output.vout {
188                                         return true;
189                                 }
190                         }
191                 }
192                 false
193         }
194 }
195
196 /// Utility for notifying listeners about new blocks, and handling block rescans if new watch
197 /// data is registered.
198 pub struct BlockNotifier<'a> {
199         listeners: Mutex<Vec<Weak<ChainListener + 'a>>>, //TODO(vmw): try removing Weak
200         chain_monitor: Arc<ChainWatchInterface>,
201 }
202
203 impl<'a> BlockNotifier<'a> {
204         /// Constructs a new BlockNotifier without any listeners.
205         pub fn new(chain_monitor: Arc<ChainWatchInterface>) -> BlockNotifier<'a> {
206                 BlockNotifier {
207                         listeners: Mutex::new(Vec::new()),
208                         chain_monitor,
209                 }
210         }
211
212         /// Register the given listener to receive events. Only a weak pointer is provided and
213         /// the registration should be freed once that pointer expires.
214         // TODO: unregister
215         pub fn register_listener(&self, listener: Weak<ChainListener + 'a>) {
216                 let mut vec = self.listeners.lock().unwrap();
217                 vec.push(listener);
218         }
219
220         /// Notify listeners that a block was connected given a full, unfiltered block.
221         ///
222         /// Handles re-scanning the block and calling block_connected again if listeners register new
223         /// watch data during the callbacks for you (see ChainListener::block_connected for more info).
224         pub fn block_connected<'b>(&self, block: &'b Block, height: u32) {
225                 let mut reentered = true;
226                 while reentered {
227                         let (matched, matched_index) = self.chain_monitor.filter_block(block);
228                         reentered = self.block_connected_checked(&block.header, height, matched.as_slice(), matched_index.as_slice());
229                 }
230         }
231
232         /// Notify listeners that a block was connected, given pre-filtered list of transactions in the
233         /// block which matched the filter (probably using does_match_tx).
234         ///
235         /// Returns true if notified listeners registered additional watch data (implying that the
236         /// block must be re-scanned and this function called again prior to further block_connected
237         /// calls, see ChainListener::block_connected for more info).
238         pub fn block_connected_checked(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[u32]) -> bool {
239                 let last_seen = self.chain_monitor.reentered();
240
241                 let listeners = self.listeners.lock().unwrap().clone();
242                 for listener in listeners.iter() {
243                         match listener.upgrade() {
244                                 Some(arc) => arc.block_connected(header, height, txn_matched, indexes_of_txn_matched),
245                                 None => ()
246                         }
247                 }
248                 return last_seen != self.chain_monitor.reentered();
249         }
250
251
252         /// Notify listeners that a block was disconnected.
253         pub fn block_disconnected(&self, header: &BlockHeader, disconnected_height: u32) {
254                 let listeners = self.listeners.lock().unwrap().clone();
255                 for listener in listeners.iter() {
256                         match listener.upgrade() {
257                                 Some(arc) => arc.block_disconnected(&header, disconnected_height),
258                                 None => ()
259                         }
260                 }
261         }
262
263 }
264
265 /// Utility to capture some common parts of ChainWatchInterface implementors.
266 ///
267 /// Keeping a local copy of this in a ChainWatchInterface implementor is likely useful.
268 pub struct ChainWatchInterfaceUtil {
269         network: Network,
270         watched: Mutex<ChainWatchedUtil>,
271         reentered: AtomicUsize,
272         logger: Arc<Logger>,
273 }
274
275 /// Register listener
276 impl ChainWatchInterface for ChainWatchInterfaceUtil {
277         fn install_watch_tx(&self, txid: &Sha256dHash, script_pub_key: &Script) {
278                 let mut watched = self.watched.lock().unwrap();
279                 if watched.register_tx(txid, script_pub_key) {
280                         self.reentered.fetch_add(1, Ordering::Relaxed);
281                 }
282         }
283
284         fn install_watch_outpoint(&self, outpoint: (Sha256dHash, u32), out_script: &Script) {
285                 let mut watched = self.watched.lock().unwrap();
286                 if watched.register_outpoint(outpoint, out_script) {
287                         self.reentered.fetch_add(1, Ordering::Relaxed);
288                 }
289         }
290
291         fn watch_all_txn(&self) {
292                 let mut watched = self.watched.lock().unwrap();
293                 if watched.watch_all() {
294                         self.reentered.fetch_add(1, Ordering::Relaxed);
295                 }
296         }
297
298         fn get_chain_utxo(&self, genesis_hash: Sha256dHash, _unspent_tx_output_identifier: u64) -> Result<(Script, u64), ChainError> {
299                 if genesis_hash != genesis_block(self.network).header.bitcoin_hash() {
300                         return Err(ChainError::NotWatched);
301                 }
302                 Err(ChainError::NotSupported)
303         }
304 }
305
306 impl ChainWatchInterfaceUtil {
307         /// Creates a new ChainWatchInterfaceUtil for the given network
308         pub fn new(network: Network, logger: Arc<Logger>) -> ChainWatchInterfaceUtil {
309                 ChainWatchInterfaceUtil {
310                         network: network,
311                         watched: Mutex::new(ChainWatchedUtil::new()),
312                         reentered: AtomicUsize::new(1),
313                         logger: logger,
314                 }
315         }
316
317
318         /// Checks if a given transaction matches the current filter.
319         pub fn does_match_tx(&self, tx: &Transaction) -> bool {
320                 let watched = self.watched.lock().unwrap();
321                 self.does_match_tx_unguarded (tx, &watched)
322         }
323
324         fn does_match_tx_unguarded(&self, tx: &Transaction, watched: &MutexGuard<ChainWatchedUtil>) -> bool {
325                 watched.does_match_tx(tx)
326         }
327 }