Skip to content

Commit 7ee34ba

Browse files
committed
Remove ChainWatchInterface from BlockNotifier
ChainListeners should be independent of each other, but in practice this is not the case because ChainWatchInterface introduces a dependency between them. Push ChainWatchInterface down into the ChainListener implementations where needed. Update ChainListener's block_connected method to take the entire block.
1 parent 8fae0c0 commit 7ee34ba

10 files changed

+246
-207
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//! channel being force-closed.
1111
1212
use bitcoin::BitcoinHash;
13-
use bitcoin::blockdata::block::BlockHeader;
13+
use bitcoin::blockdata::block::{Block, BlockHeader};
1414
use bitcoin::blockdata::transaction::{Transaction, TxOut};
1515
use bitcoin::blockdata::script::{Builder, Script};
1616
use bitcoin::blockdata::opcodes;
@@ -306,17 +306,17 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
306306

307307
macro_rules! confirm_txn {
308308
($node: expr) => { {
309-
let mut header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
310-
let mut txn = Vec::with_capacity(channel_txn.len());
311-
let mut posn = Vec::with_capacity(channel_txn.len());
312-
for i in 0..channel_txn.len() {
313-
txn.push(&channel_txn[i]);
314-
posn.push(i + 1);
315-
}
316-
$node.block_connected(&header, 1, &txn, &posn);
309+
let mut block = Block {
310+
header: BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
311+
txdata: channel_txn.clone(),
312+
};
313+
$node.block_connected(&block, 1);
317314
for i in 2..100 {
318-
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
319-
$node.block_connected(&header, i, &Vec::new(), &[0; 0]);
315+
block = Block {
316+
header: BlockHeader { version: 0x20000000, prev_blockhash: block.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 },
317+
txdata: vec![],
318+
};
319+
$node.block_connected(&block, i);
320320
}
321321
} }
322322
}

fuzz/src/full_stack.rs

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! or payments to send/ways to handle events generated.
55
//! This test has been very useful, though due to its complexity good starting inputs are critical.
66
7-
use bitcoin::blockdata::block::BlockHeader;
7+
use bitcoin::blockdata::block::{Block, BlockHeader};
88
use bitcoin::blockdata::transaction::{Transaction, TxOut};
99
use bitcoin::blockdata::script::{Builder, Script};
1010
use bitcoin::blockdata::opcodes;
@@ -175,30 +175,29 @@ impl<'a> MoneyLossDetector<'a> {
175175
}
176176

177177
fn connect_block(&mut self, all_txn: &[Transaction]) {
178-
let mut txn = Vec::with_capacity(all_txn.len());
179-
let mut txn_idxs = Vec::with_capacity(all_txn.len());
180-
for (idx, tx) in all_txn.iter().enumerate() {
178+
for tx in all_txn.iter() {
181179
let txid = tx.txid();
182180
match self.txids_confirmed.entry(txid) {
183181
hash_map::Entry::Vacant(e) => {
184182
e.insert(self.height);
185-
txn.push(tx);
186-
txn_idxs.push(idx + 1);
187183
},
188184
_ => {},
189185
}
190186
}
191187

192-
let header = BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height], merkle_root: Default::default(), time: self.blocks_connected, bits: 42, nonce: 42 };
188+
let block = Block {
189+
header: BlockHeader { version: 0x20000000, prev_blockhash: self.header_hashes[self.height], merkle_root: Default::default(), time: self.blocks_connected, bits: 42, nonce: 42 },
190+
txdata: all_txn.to_vec(),
191+
};
193192
self.height += 1;
194193
self.blocks_connected += 1;
195-
self.manager.block_connected(&header, self.height as u32, &txn[..], &txn_idxs[..]);
196-
(*self.monitor).block_connected(&header, self.height as u32, &txn[..], &txn_idxs[..]);
194+
self.manager.block_connected(&block, self.height as u32);
195+
(*self.monitor).block_connected(&block, self.height as u32);
197196
if self.header_hashes.len() > self.height {
198-
self.header_hashes[self.height] = header.bitcoin_hash();
197+
self.header_hashes[self.height] = block.bitcoin_hash();
199198
} else {
200199
assert_eq!(self.header_hashes.len(), self.height);
201-
self.header_hashes.push(header.bitcoin_hash());
200+
self.header_hashes.push(block.bitcoin_hash());
202201
}
203202
self.max_height = cmp::max(self.height, self.max_height);
204203
}

lightning/src/chain/chaininterface.rs

Lines changed: 14 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -72,21 +72,7 @@ pub trait BroadcasterInterface: Sync + Send {
7272
/// A trait indicating a desire to listen for events from the chain
7373
pub trait ChainListener: Sync + Send {
7474
/// Notifies a listener that a block was connected.
75-
///
76-
/// The txn_matched array should be set to references to transactions which matched the
77-
/// relevant installed watch outpoints/txn, or the full set of transactions in the block.
78-
///
79-
/// Note that if txn_matched includes only matched transactions, and a new
80-
/// transaction/outpoint is watched during a block_connected call, the block *must* be
81-
/// re-scanned with the new transaction/outpoints and block_connected should be called
82-
/// again with the same header and (at least) the new transactions.
83-
///
84-
/// Note that if non-new transaction/outpoints are be registered during a call, a second call
85-
/// *must not* happen.
86-
///
87-
/// This also means those counting confirmations using block_connected callbacks should watch
88-
/// for duplicate headers and not count them towards confirmations!
89-
fn block_connected(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[usize]);
75+
fn block_connected(&self, block: &Block, height: u32);
9076
/// Notifies a listener that a block was disconnected.
9177
/// Unlike block_connected, this *must* never be called twice for the same disconnect event.
9278
/// Height must be the one of the block which was disconnected (not new height of the best chain)
@@ -218,7 +204,7 @@ impl ChainWatchedUtil {
218204
/// parameters with static lifetimes). Other times you can afford a reference, which is more
219205
/// efficient, in which case BlockNotifierRef is a more appropriate type. Defining these type
220206
/// aliases prevents issues such as overly long function definitions.
221-
pub type BlockNotifierArc<C> = Arc<BlockNotifier<'static, Arc<ChainListener>, C>>;
207+
pub type BlockNotifierArc = Arc<BlockNotifier<'static, Arc<ChainListener>>>;
222208

223209
/// BlockNotifierRef is useful when you want a BlockNotifier that points to ChainListeners
224210
/// with nonstatic lifetimes. This is useful for when static lifetimes are not needed. Nonstatic
@@ -227,27 +213,24 @@ pub type BlockNotifierArc<C> = Arc<BlockNotifier<'static, Arc<ChainListener>, C>
227213
/// requires parameters with static lifetimes), in which case BlockNotifierArc is a more
228214
/// appropriate type. Defining these type aliases for common usages prevents issues such as
229215
/// overly long function definitions.
230-
pub type BlockNotifierRef<'a, C> = BlockNotifier<'a, &'a ChainListener, C>;
216+
pub type BlockNotifierRef<'a> = BlockNotifier<'a, &'a ChainListener>;
231217

232-
/// Utility for notifying listeners about new blocks, and handling block rescans if new watch
233-
/// data is registered.
218+
/// Utility for notifying listeners when blocks are connected or disconnected.
234219
///
235220
/// Rather than using a plain BlockNotifier, it is preferable to use either a BlockNotifierArc
236221
/// or a BlockNotifierRef for conciseness. See their documentation for more details, but essentially
237222
/// you should default to using a BlockNotifierRef, and use a BlockNotifierArc instead when you
238223
/// require ChainListeners with static lifetimes, such as when you're using lightning-net-tokio.
239-
pub struct BlockNotifier<'a, CL: Deref<Target = ChainListener + 'a> + 'a, C: Deref> where C::Target: ChainWatchInterface {
224+
pub struct BlockNotifier<'a, CL: Deref<Target = ChainListener + 'a> + 'a> {
240225
listeners: Mutex<Vec<CL>>,
241-
chain_monitor: C,
242226
phantom: PhantomData<&'a ()>,
243227
}
244228

245-
impl<'a, CL: Deref<Target = ChainListener + 'a> + 'a, C: Deref> BlockNotifier<'a, CL, C> where C::Target: ChainWatchInterface {
229+
impl<'a, CL: Deref<Target = ChainListener + 'a> + 'a> BlockNotifier<'a, CL> {
246230
/// Constructs a new BlockNotifier without any listeners.
247-
pub fn new(chain_monitor: C) -> BlockNotifier<'a, CL, C> {
231+
pub fn new() -> BlockNotifier<'a, CL> {
248232
BlockNotifier {
249233
listeners: Mutex::new(Vec::new()),
250-
chain_monitor,
251234
phantom: PhantomData,
252235
}
253236
}
@@ -270,36 +253,12 @@ impl<'a, CL: Deref<Target = ChainListener + 'a> + 'a, C: Deref> BlockNotifier<'a
270253
vec.retain(|item | !ptr::eq(&(**item), &(*listener)));
271254
}
272255

273-
/// Notify listeners that a block was connected given a full, unfiltered block.
274-
///
275-
/// Handles re-scanning the block and calling block_connected again if listeners register new
276-
/// watch data during the callbacks for you (see ChainListener::block_connected for more info).
277-
pub fn block_connected(&self, block: &Block, height: u32) {
278-
let mut reentered = true;
279-
while reentered {
280-
let matched_indexes = self.chain_monitor.filter_block(block);
281-
let mut matched_txn = Vec::new();
282-
for index in matched_indexes.iter() {
283-
matched_txn.push(&block.txdata[*index]);
284-
}
285-
reentered = self.block_connected_checked(&block.header, height, matched_txn.as_slice(), matched_indexes.as_slice());
286-
}
287-
}
288-
289-
/// Notify listeners that a block was connected, given pre-filtered list of transactions in the
290-
/// block which matched the filter (probably using does_match_tx).
291-
///
292-
/// Returns true if notified listeners registered additional watch data (implying that the
293-
/// block must be re-scanned and this function called again prior to further block_connected
294-
/// calls, see ChainListener::block_connected for more info).
295-
pub fn block_connected_checked(&self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[usize]) -> bool {
296-
let last_seen = self.chain_monitor.reentered();
297-
256+
/// Notify listeners that a block was connected.
257+
pub fn block_connected<'b>(&self, block: &'b Block, height: u32) {
298258
let listeners = self.listeners.lock().unwrap();
299259
for listener in listeners.iter() {
300-
listener.block_connected(header, height, txn_matched, indexes_of_txn_matched);
260+
listener.block_connected(block, height);
301261
}
302-
return last_seen != self.chain_monitor.reentered();
303262
}
304263

305264
/// Notify listeners that a block was disconnected.
@@ -410,7 +369,7 @@ mod tests {
410369
fn register_listener_test() {
411370
let chanmon_cfgs = create_chanmon_cfgs(1);
412371
let node_cfgs = create_node_cfgs(1, &chanmon_cfgs);
413-
let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor);
372+
let block_notifier = BlockNotifier::new();
414373
assert_eq!(block_notifier.listeners.lock().unwrap().len(), 0);
415374
let listener = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener;
416375
block_notifier.register_listener(listener);
@@ -424,7 +383,7 @@ mod tests {
424383
fn unregister_single_listener_test() {
425384
let chanmon_cfgs = create_chanmon_cfgs(2);
426385
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
427-
let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor);
386+
let block_notifier = BlockNotifier::new();
428387
let listener1 = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener;
429388
let listener2 = &node_cfgs[1].chan_monitor.simple_monitor as &ChainListener;
430389
block_notifier.register_listener(listener1);
@@ -443,7 +402,7 @@ mod tests {
443402
fn unregister_single_listener_ref_test() {
444403
let chanmon_cfgs = create_chanmon_cfgs(2);
445404
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
446-
let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor);
405+
let block_notifier = BlockNotifier::new();
447406
block_notifier.register_listener(&node_cfgs[0].chan_monitor.simple_monitor as &ChainListener);
448407
block_notifier.register_listener(&node_cfgs[1].chan_monitor.simple_monitor as &ChainListener);
449408
let vec = block_notifier.listeners.lock().unwrap();
@@ -460,7 +419,7 @@ mod tests {
460419
fn unregister_multiple_of_the_same_listeners_test() {
461420
let chanmon_cfgs = create_chanmon_cfgs(2);
462421
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
463-
let block_notifier = BlockNotifier::new(node_cfgs[0].chain_monitor);
422+
let block_notifier = BlockNotifier::new();
464423
let listener1 = &node_cfgs[0].chan_monitor.simple_monitor as &ChainListener;
465424
let listener2 = &node_cfgs[1].chan_monitor.simple_monitor as &ChainListener;
466425
block_notifier.register_listener(listener1);

lightning/src/ln/chanmon_update_fail_tests.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1747,11 +1747,11 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
17471747
};
17481748

17491749
if confirm_a_first {
1750-
confirm_transaction(&nodes[0].block_notifier, &nodes[0].chain_monitor, &funding_tx, funding_tx.version);
1750+
confirm_transaction(&nodes[0].block_notifier, &funding_tx);
17511751
nodes[1].node.handle_funding_locked(&nodes[0].node.get_our_node_id(), &get_event_msg!(nodes[0], MessageSendEvent::SendFundingLocked, nodes[1].node.get_our_node_id()));
17521752
} else {
17531753
assert!(!restore_b_before_conf);
1754-
confirm_transaction(&nodes[1].block_notifier, &nodes[1].chain_monitor, &funding_tx, funding_tx.version);
1754+
confirm_transaction(&nodes[1].block_notifier, &funding_tx);
17551755
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
17561756
}
17571757

@@ -1763,7 +1763,7 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
17631763
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
17641764

17651765
if !restore_b_before_conf {
1766-
confirm_transaction(&nodes[1].block_notifier, &nodes[1].chain_monitor, &funding_tx, funding_tx.version);
1766+
confirm_transaction(&nodes[1].block_notifier, &funding_tx);
17671767
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
17681768
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
17691769
}
@@ -1776,12 +1776,12 @@ fn do_during_funding_monitor_fail(confirm_a_first: bool, restore_b_before_conf:
17761776
let (channel_id, (announcement, as_update, bs_update)) = if !confirm_a_first {
17771777
nodes[0].node.handle_funding_locked(&nodes[1].node.get_our_node_id(), &get_event_msg!(nodes[1], MessageSendEvent::SendFundingLocked, nodes[0].node.get_our_node_id()));
17781778

1779-
confirm_transaction(&nodes[0].block_notifier, &nodes[0].chain_monitor, &funding_tx, funding_tx.version);
1779+
confirm_transaction(&nodes[0].block_notifier, &funding_tx);
17801780
let (funding_locked, channel_id) = create_chan_between_nodes_with_value_confirm_second(&nodes[1], &nodes[0]);
17811781
(channel_id, create_chan_between_nodes_with_value_b(&nodes[0], &nodes[1], &funding_locked))
17821782
} else {
17831783
if restore_b_before_conf {
1784-
confirm_transaction(&nodes[1].block_notifier, &nodes[1].chain_monitor, &funding_tx, funding_tx.version);
1784+
confirm_transaction(&nodes[1].block_notifier, &funding_tx);
17851785
}
17861786
let (funding_locked, channel_id) = create_chan_between_nodes_with_value_confirm_second(&nodes[0], &nodes[1]);
17871787
(channel_id, create_chan_between_nodes_with_value_b(&nodes[1], &nodes[0], &funding_locked))

lightning/src/ln/channel.rs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use bitcoin::blockdata::block::BlockHeader;
1+
use bitcoin::blockdata::block::{Block, BlockHeader};
22
use bitcoin::blockdata::script::{Script,Builder};
33
use bitcoin::blockdata::transaction::{TxIn, TxOut, Transaction, SigHashType};
44
use bitcoin::blockdata::opcodes;
@@ -3299,7 +3299,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
32993299
///
33003300
/// May return some HTLCs (and their payment_hash) which have timed out and should be failed
33013301
/// back.
3302-
pub fn block_connected(&mut self, header: &BlockHeader, height: u32, txn_matched: &[&Transaction], indexes_of_txn_matched: &[usize]) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
3302+
pub fn block_connected(&mut self, block: &Block, height: u32) -> Result<(Option<msgs::FundingLocked>, Vec<(HTLCSource, PaymentHash)>), msgs::ErrorMessage> {
33033303
let mut timed_out_htlcs = Vec::new();
33043304
self.holding_cell_htlc_updates.retain(|htlc_update| {
33053305
match htlc_update {
@@ -3313,13 +3313,13 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
33133313
}
33143314
});
33153315
let non_shutdown_state = self.channel_state & (!MULTI_STATE_FLAGS);
3316-
if header.bitcoin_hash() != self.last_block_connected {
3316+
if block.bitcoin_hash() != self.last_block_connected {
33173317
if self.funding_tx_confirmations > 0 {
33183318
self.funding_tx_confirmations += 1;
33193319
}
33203320
}
33213321
if non_shutdown_state & !(ChannelState::TheirFundingLocked as u32) == ChannelState::FundingSent as u32 {
3322-
for (ref tx, index_in_block) in txn_matched.iter().zip(indexes_of_txn_matched) {
3322+
for (index_in_block, ref tx) in block.txdata.iter().enumerate() {
33233323
if tx.txid() == self.funding_txo.unwrap().txid {
33243324
let txo_idx = self.funding_txo.unwrap().index as usize;
33253325
if txo_idx >= tx.output.len() || tx.output[txo_idx].script_pubkey != self.get_funding_redeemscript().to_v0_p2wsh() ||
@@ -3350,21 +3350,21 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
33503350
}
33513351
}
33523352
}
3353-
if height > 0xff_ff_ff || (*index_in_block) > 0xff_ff_ff {
3353+
if height > 0xff_ff_ff || (index_in_block) > 0xff_ff_ff {
33543354
panic!("Block was bogus - either height 16 million or had > 16 million transactions");
33553355
}
33563356
assert!(txo_idx <= 0xffff); // txo_idx is a (u16 as usize), so this is just listed here for completeness
33573357
self.funding_tx_confirmations = 1;
3358-
self.short_channel_id = Some(((height as u64) << (5*8)) |
3359-
((*index_in_block as u64) << (2*8)) |
3360-
((txo_idx as u64) << (0*8)));
3358+
self.short_channel_id = Some(((height as u64) << (5*8)) |
3359+
((index_in_block as u64) << (2*8)) |
3360+
((txo_idx as u64) << (0*8)));
33613361
}
33623362
}
33633363
}
33643364
}
3365-
if header.bitcoin_hash() != self.last_block_connected {
3366-
self.last_block_connected = header.bitcoin_hash();
3367-
self.update_time_counter = cmp::max(self.update_time_counter, header.time);
3365+
if block.bitcoin_hash() != self.last_block_connected {
3366+
self.last_block_connected = block.bitcoin_hash();
3367+
self.update_time_counter = cmp::max(self.update_time_counter, block.header.time);
33683368
if let Some(channel_monitor) = self.channel_monitor.as_mut() {
33693369
channel_monitor.last_block_hash = self.last_block_connected;
33703370
}
@@ -3388,7 +3388,7 @@ impl<ChanSigner: ChannelKeys> Channel<ChanSigner> {
33883388
// funding_tx_confirmed_in and return.
33893389
false
33903390
};
3391-
self.funding_tx_confirmed_in = Some(header.bitcoin_hash());
3391+
self.funding_tx_confirmed_in = Some(block.bitcoin_hash());
33923392

33933393
//TODO: Note that this must be a duplicate of the previous commitment point they sent us,
33943394
//as otherwise we will have a commitment transaction that they can't revoke (well, kinda,

0 commit comments

Comments
 (0)