Skip to content

Commit 7cfd8bf

Browse files
chaininterface: add BlockNotifier struct.
Adding this struct will allow us to remove the circular reference between ChainListeners and the ChainWatchInterface, because it separates out the responsibility of notifying listeners about new blocks from the responsibility of storing and retrieving watched transactions.
1 parent 2afd531 commit 7cfd8bf

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed

src/chain/chaininterface.rs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,75 @@ impl ChainWatchedUtil {
198198
}
199199
}
200200

201+
/// Utility for notifying listeners about new blocks, and handling block rescans if new watch
202+
/// data is registered.
203+
pub struct BlockNotifier {
204+
listeners: Mutex<Vec<Weak<ChainListener>>>, //TODO(vmw): try removing Weak
205+
chain_monitor: Arc<ChainWatchInterface>,
206+
}
207+
208+
impl BlockNotifier {
209+
/// Constructs a new BlockNotifier without any listeners.
210+
pub fn new(chain_monitor: Arc<ChainWatchInterface>) -> BlockNotifier {
211+
BlockNotifier {
212+
listeners: Mutex::new(Vec::new()),
213+
chain_monitor,
214+
}
215+
}
216+
217+
/// Register the given listener to receive events. Only a weak pointer is provided and
218+
/// the registration should be freed once that pointer expires.
219+
// TODO: unregister
220+
pub fn register_listener(&self, listener: Weak<ChainListener>) {
221+
let mut vec = self.listeners.lock().unwrap();
222+
vec.push(listener);
223+
}
224+
225+
/// Notify listeners that a block was connected given a full, unfiltered block.
226+
///
227+
/// Handles re-scanning the block and calling block_connected again if listeners register new
228+
/// watch data during the callbacks for you (see ChainListener::block_connected for more info).
229+
pub fn block_connected(&self, block: &Block, height: u32) {
230+
let mut reentered = true;
231+
while reentered {
232+
let (matched, matched_index) = self.chain_monitor.get_watched_txs(block);
233+
reentered = self.block_connected_checked(&block.header, height, matched.as_slice(), matched_index.as_slice());
234+
}
235+
}
236+
237+
/// Notify listeners that a block was connected, given pre-filtered list of transactions in the
238+
/// block which matched the filter (probably using does_match_tx).
239+
///
240+
/// Returns true if notified listeners registered additional watch data (implying that the
241+
/// block must be re-scanned and this function called again prior to further block_connected
242+
/// calls, see ChainListener::block_connected for more info).
243+
pub fn block_connected_checked(&self, header: &BlockHeader, height: u32, txn_matched: &[Transaction], indexes_of_txn_matched: &[u32]) -> bool {
244+
let last_seen = self.chain_monitor.reentered();
245+
246+
let listeners = self.listeners.lock().unwrap().clone();
247+
for listener in listeners.iter() {
248+
match listener.upgrade() {
249+
Some(arc) => arc.block_connected(header, height, txn_matched, indexes_of_txn_matched),
250+
None => ()
251+
}
252+
}
253+
return last_seen != self.chain_monitor.reentered();
254+
}
255+
256+
257+
/// Notify listeners that a block was disconnected.
258+
pub fn block_disconnected(&self, header: &BlockHeader, disconnected_height: u32) {
259+
let listeners = self.listeners.lock().unwrap().clone();
260+
for listener in listeners.iter() {
261+
match listener.upgrade() {
262+
Some(arc) => arc.block_disconnected(&header, disconnected_height),
263+
None => ()
264+
}
265+
}
266+
}
267+
268+
}
269+
201270
/// Utility to capture some common parts of ChainWatchInterface implementors.
202271
///
203272
/// Keeping a local copy of this in a ChainWatchInterface implementor is likely useful.

0 commit comments

Comments
 (0)