Add rescan logic to ChainMonitor::block_connected
authorJeffrey Czyz <jkczyz@gmail.com>
Wed, 10 Mar 2021 22:39:34 +0000 (14:39 -0800)
committerJeffrey Czyz <jkczyz@gmail.com>
Sat, 27 Mar 2021 22:20:55 +0000 (18:20 -0400)
Electrum clients will only provide transaction data for outputs that
have been explicitly registered. Hence, upon registering new outputs,
recursively register any outputs to watch contained within dependent
transactions from the same block.

lightning/src/chain/chainmonitor.rs

index 234e60b4c5967d7335711cbed1fd20da73e3c245..65b5feb8ccedac81ce803d6c9f708dc3535b2714 100644 (file)
@@ -82,23 +82,40 @@ where C::Target: chain::Filter,
        /// descendants of such transactions. It is not necessary to re-fetch the block to obtain
        /// updated `txdata`.
        pub fn block_connected(&self, header: &BlockHeader, txdata: &TransactionData, height: u32) {
+               let mut dependent_txdata = Vec::new();
                let monitors = self.monitors.read().unwrap();
                for monitor in monitors.values() {
                        let mut txn_outputs = monitor.block_connected(header, txdata, height, &*self.broadcaster, &*self.fee_estimator, &*self.logger);
 
+                       // Register any new outputs with the chain source for filtering, storing any dependent
+                       // transactions from within the block that previously had not been included in txdata.
                        if let Some(ref chain_source) = self.chain_source {
                                let block_hash = header.block_hash();
                                for (txid, outputs) in txn_outputs.drain(..) {
                                        for (idx, output) in outputs.iter() {
-                                               chain_source.register_output(WatchedOutput {
+                                               // Register any new outputs with the chain source for filtering and recurse
+                                               // if it indicates that there are dependent transactions within the block
+                                               // that had not been previously included in txdata.
+                                               let output = WatchedOutput {
                                                        block_hash: Some(block_hash),
                                                        outpoint: OutPoint { txid, index: *idx as u16 },
                                                        script_pubkey: output.script_pubkey.clone(),
-                                               });
+                                               };
+                                               if let Some(tx) = chain_source.register_output(output) {
+                                                       dependent_txdata.push(tx);
+                                               }
                                        }
                                }
                        }
                }
+
+               // Recursively call for any dependent transactions that were identified by the chain source.
+               if !dependent_txdata.is_empty() {
+                       dependent_txdata.sort_unstable_by_key(|(index, _tx)| *index);
+                       dependent_txdata.dedup_by_key(|(index, _tx)| *index);
+                       let txdata: Vec<_> = dependent_txdata.iter().map(|(index, tx)| (*index, tx)).collect();
+                       self.block_connected(header, &txdata, height);
+               }
        }
 
        /// Dispatches to per-channel monitors, which are responsible for updating their on-chain view