|
26 | 26 | use bitcoin::blockdata::block::{Block, BlockHeader};
|
27 | 27 |
|
28 | 28 | use chain;
|
29 |
| -use chain::Filter; |
| 29 | +use chain::{Filter, WatchedOutput}; |
30 | 30 | use chain::chaininterface::{BroadcasterInterface, FeeEstimator};
|
31 | 31 | use chain::channelmonitor;
|
32 | 32 | use chain::channelmonitor::{ChannelMonitor, ChannelMonitorUpdate, ChannelMonitorUpdateErr, MonitorEvent, Persist};
|
@@ -82,18 +82,40 @@ where C::Target: chain::Filter,
|
82 | 82 | /// descendants of such transactions. It is not necessary to re-fetch the block to obtain
|
83 | 83 | /// updated `txdata`.
|
84 | 84 | pub fn block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
|
| 85 | + let mut dependent_txdata = Vec::new(); |
85 | 86 | let monitors = self.monitors.read().unwrap();
|
86 | 87 | for monitor in monitors.values() {
|
87 | 88 | let mut txn_outputs = monitor.block_connected(header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger);
|
88 | 89 |
|
| 90 | + // Register any new outputs with the chain source for filtering, storing any dependent |
| 91 | + // transactions from within the block that previously had not been included in txdata. |
89 | 92 | if let Some(ref chain_source) = self.chain_source {
|
| 93 | + let block_hash = header.block_hash(); |
90 | 94 | for (txid, outputs) in txn_outputs.drain(..) {
|
91 | 95 | for (idx, output) in outputs.iter() {
|
92 |
| - chain_source.register_output(&OutPoint { txid, index: *idx as u16 }, &output.script_pubkey); |
| 96 | + // Register any new outputs with the chain source for filtering and recurse |
| 97 | + // if it indicates that there are dependent transactions within the block |
| 98 | + // that had not been previously included in txdata. |
| 99 | + let output = WatchedOutput { |
| 100 | + block_hash: Some(block_hash), |
| 101 | + outpoint: OutPoint { txid, index: *idx as u16 }, |
| 102 | + script_pubkey: output.script_pubkey.clone(), |
| 103 | + }; |
| 104 | + if let Some(tx) = chain_source.register_output(output) { |
| 105 | + dependent_txdata.push(tx); |
| 106 | + } |
93 | 107 | }
|
94 | 108 | }
|
95 | 109 | }
|
96 | 110 | }
|
| 111 | + |
| 112 | + // Recursively call for any dependent transactions that were identified by the chain source. |
| 113 | + if !dependent_txdata.is_empty() { |
| 114 | + dependent_txdata.sort_unstable_by_key(|(index, _tx)| *index); |
| 115 | + dependent_txdata.dedup_by_key(|(index, _tx)| *index); |
| 116 | + let txdata: Vec<_> = dependent_txdata.iter().map(|(index, tx)| (*index, tx)).collect(); |
| 117 | + self.block_connected(header, &txdata, height); |
| 118 | + } |
97 | 119 | }
|
98 | 120 |
|
99 | 121 | /// Dispatches to per-channel monitors, which are responsible for updating their on-chain view
|
@@ -245,3 +267,56 @@ impl<ChannelSigner: Sign, C: Deref, T: Deref, F: Deref, L: Deref, P: Deref> even
|
245 | 267 | pending_events
|
246 | 268 | }
|
247 | 269 | }
|
| 270 | + |
| 271 | +#[cfg(test)] |
| 272 | +mod tests { |
| 273 | + use ::{check_added_monitors, get_local_commitment_txn}; |
| 274 | + use ln::features::InitFeatures; |
| 275 | + use ln::functional_test_utils::*; |
| 276 | + use util::events::EventsProvider; |
| 277 | + use util::events::MessageSendEventsProvider; |
| 278 | + use util::test_utils::{OnRegisterOutput, TxOutReference}; |
| 279 | + |
| 280 | + /// Tests that in-block dependent transactions are processed by `block_connected` when not |
| 281 | + /// included in `txdata` but returned by [`chain::Filter::register_output`]. For instance, |
| 282 | + /// a (non-anchor) commitment transaction's HTLC output may be spent in the same block as the |
| 283 | + /// commitment transaction itself. An Electrum client may filter the commitment transaction but |
| 284 | + /// needs to return the HTLC transaction so it can be processed. |
| 285 | + #[test] |
| 286 | + fn connect_block_checks_dependent_transactions() { |
| 287 | + let chanmon_cfgs = create_chanmon_cfgs(2); |
| 288 | + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); |
| 289 | + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]); |
| 290 | + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); |
| 291 | + let channel = create_announced_chan_between_nodes( |
| 292 | + &nodes, 0, 1, InitFeatures::known(), InitFeatures::known()); |
| 293 | + |
| 294 | + // Send a payment, saving nodes[0]'s revoked commitment and HTLC-Timeout transactions. |
| 295 | + let (commitment_tx, htlc_tx) = { |
| 296 | + let payment_preimage = route_payment(&nodes[0], &vec!(&nodes[1])[..], 5_000_000).0; |
| 297 | + let mut txn = get_local_commitment_txn!(nodes[0], channel.2); |
| 298 | + claim_payment(&nodes[0], &vec!(&nodes[1])[..], payment_preimage, 5_000_000); |
| 299 | + |
| 300 | + assert_eq!(txn.len(), 2); |
| 301 | + (txn.remove(0), txn.remove(0)) |
| 302 | + }; |
| 303 | + |
| 304 | + // Set expectations on nodes[1]'s chain source to return dependent transactions. |
| 305 | + let htlc_output = TxOutReference(commitment_tx.clone(), 0); |
| 306 | + let to_local_output = TxOutReference(commitment_tx.clone(), 1); |
| 307 | + let htlc_timeout_output = TxOutReference(htlc_tx.clone(), 0); |
| 308 | + nodes[1].chain_source |
| 309 | + .expect(OnRegisterOutput { with: htlc_output, returns: Some((1, htlc_tx)) }) |
| 310 | + .expect(OnRegisterOutput { with: to_local_output, returns: None }) |
| 311 | + .expect(OnRegisterOutput { with: htlc_timeout_output, returns: None }); |
| 312 | + |
| 313 | + // Notify nodes[1] that nodes[0]'s revoked commitment transaction was mined. The chain |
| 314 | + // source should return the dependent HTLC transaction when the HTLC output is registered. |
| 315 | + mine_transaction(&nodes[1], &commitment_tx); |
| 316 | + |
| 317 | + // Clean up so uninteresting assertions don't fail. |
| 318 | + check_added_monitors!(nodes[1], 1); |
| 319 | + nodes[1].node.get_and_clear_pending_msg_events(); |
| 320 | + nodes[1].node.get_and_clear_pending_events(); |
| 321 | + } |
| 322 | +} |
0 commit comments