X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fchannelmonitor.rs;h=0e87f3569e605d0c25736a9d203bc1fe6170655f;hb=07d991c82fadf10f59ae01b2b5aef6bc8797fd9a;hp=29f7130fbdeaca2e807e09cba9a8e86e9537f1db;hpb=bfda1b683bb5a87b123c3986a029e043addef159;p=rust-lightning diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 29f7130f..0e87f356 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -1812,6 +1812,12 @@ impl ChannelMonitor { ); } + /// Returns true if the monitor has pending claim requests that are not fully confirmed yet. + pub fn has_pending_claims(&self) -> bool + { + self.inner.lock().unwrap().onchain_tx_handler.has_pending_claims() + } + /// Triggers rebroadcasts of pending claims from a force-closed channel after a transaction /// signature generation failure. pub fn signer_unblocked( @@ -1924,9 +1930,9 @@ impl ChannelMonitor { } #[cfg(test)] - pub fn do_signer_call ()>(&self, mut f: F) { - let inner = self.inner.lock().unwrap(); - f(&inner.onchain_tx_handler.signer); + pub fn do_mut_signer_call ()>(&self, mut f: F) { + let mut inner = self.inner.lock().unwrap(); + f(&mut inner.onchain_tx_handler.signer); } } @@ -2431,8 +2437,8 @@ macro_rules! fail_unbroadcast_htlcs { debug_assert_eq!($commitment_tx_confirmed.txid(), $commitment_txid_confirmed); macro_rules! check_htlc_fails { - ($txid: expr, $commitment_tx: expr) => { - if let Some(ref latest_outpoints) = $self.counterparty_claimable_outpoints.get($txid) { + ($txid: expr, $commitment_tx: expr, $per_commitment_outpoints: expr) => { + if let Some(ref latest_outpoints) = $per_commitment_outpoints { for &(ref htlc, ref source_option) in latest_outpoints.iter() { if let &Some(ref source) = source_option { // Check if the HTLC is present in the commitment transaction that was @@ -2492,10 +2498,10 @@ macro_rules! fail_unbroadcast_htlcs { } } if let Some(ref txid) = $self.current_counterparty_commitment_txid { - check_htlc_fails!(txid, "current"); + check_htlc_fails!(txid, "current", $self.counterparty_claimable_outpoints.get(txid)); } if let Some(ref txid) = $self.prev_counterparty_commitment_txid { - check_htlc_fails!(txid, "previous"); + check_htlc_fails!(txid, "previous", $self.counterparty_claimable_outpoints.get(txid)); } } } } @@ -2764,15 +2770,15 @@ impl ChannelMonitorImpl { // If the channel is force closed, try to claim the output from this preimage. // First check if a counterparty commitment transaction has been broadcasted: macro_rules! claim_htlcs { - ($commitment_number: expr, $txid: expr) => { - let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None); + ($commitment_number: expr, $txid: expr, $htlcs: expr) => { + let (htlc_claim_reqs, _) = self.get_counterparty_output_claim_info($commitment_number, $txid, None, $htlcs); self.onchain_tx_handler.update_claims_view_from_requests(htlc_claim_reqs, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger); } } if let Some(txid) = self.current_counterparty_commitment_txid { if txid == confirmed_spend_txid { if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) { - claim_htlcs!(*commitment_number, txid); + claim_htlcs!(*commitment_number, txid, self.counterparty_claimable_outpoints.get(&txid)); } else { debug_assert!(false); log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number"); @@ -2783,7 +2789,7 @@ impl ChannelMonitorImpl { if let Some(txid) = self.prev_counterparty_commitment_txid { if txid == confirmed_spend_txid { if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) { - claim_htlcs!(*commitment_number, txid); + claim_htlcs!(*commitment_number, txid, self.counterparty_claimable_outpoints.get(&txid)); } else { debug_assert!(false); log_error!(logger, "Detected counterparty commitment tx on-chain without tracking commitment number"); @@ -2873,7 +2879,7 @@ impl ChannelMonitorImpl { F::Target: FeeEstimator, L::Target: Logger, { - let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HolderForceClosed); + let (claimable_outpoints, _) = self.generate_claimable_outpoints_and_watch_outputs(ClosureReason::HolderForceClosed { broadcasted_latest_txn: Some(true) }); self.onchain_tx_handler.update_claims_view_from_requests( claimable_outpoints, self.best_block.height, self.best_block.height, broadcaster, fee_estimator, logger @@ -3105,9 +3111,7 @@ impl ChannelMonitorImpl { }, commitment_txid: htlc.commitment_txid, per_commitment_number: htlc.per_commitment_number, - per_commitment_point: self.onchain_tx_handler.signer.get_per_commitment_point( - htlc.per_commitment_number, &self.onchain_tx_handler.secp_ctx, - ), + per_commitment_point: htlc.per_commitment_point, feerate_per_kw: 0, htlc: htlc.htlc, preimage: htlc.preimage, @@ -3233,16 +3237,14 @@ impl ChannelMonitorImpl { /// height > height + CLTV_SHARED_CLAIM_BUFFER. In any case, will install monitoring for /// HTLC-Success/HTLC-Timeout transactions. /// - /// Returns packages to claim the revoked output(s), as well as additional outputs to watch and - /// general information about the output that is to the counterparty in the commitment - /// transaction. + /// Returns packages to claim the revoked output(s) and general information about the output that + /// is to the counterparty in the commitment transaction. fn check_spend_counterparty_transaction(&mut self, tx: &Transaction, height: u32, block_hash: &BlockHash, logger: &L) - -> (Vec, TransactionOutputs, CommitmentTxCounterpartyOutputInfo) + -> (Vec, CommitmentTxCounterpartyOutputInfo) where L::Target: Logger { // Most secp and related errors trying to create keys means we have no hope of constructing // a spend transaction...so we return no transactions to broadcast let mut claimable_outpoints = Vec::new(); - let mut watch_outputs = Vec::new(); let mut to_counterparty_output_info = None; let commitment_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers! @@ -3252,7 +3254,7 @@ impl ChannelMonitorImpl { ( $thing : expr ) => { match $thing { Ok(a) => a, - Err(_) => return (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info) + Err(_) => return (claimable_outpoints, to_counterparty_output_info) } }; } @@ -3280,14 +3282,13 @@ impl ChannelMonitorImpl { } // Then, try to find revoked htlc outputs - if let Some(ref per_commitment_data) = per_commitment_option { - for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() { + if let Some(per_commitment_claimable_data) = per_commitment_option { + for (htlc, _) in per_commitment_claimable_data { if let Some(transaction_output_index) = htlc.transaction_output_index { if transaction_output_index as usize >= tx.output.len() || tx.output[transaction_output_index as usize].value != htlc.to_bitcoin_amount() { // per_commitment_data is corrupt or our commitment signing key leaked! - return (claimable_outpoints, (commitment_txid, watch_outputs), - to_counterparty_output_info); + return (claimable_outpoints, to_counterparty_output_info); } let revk_htlc_outp = RevokedHTLCOutput::build(per_commitment_point, self.counterparty_commitment_params.counterparty_delayed_payment_base_key, self.counterparty_commitment_params.counterparty_htlc_base_key, per_commitment_key, htlc.amount_msat / 1000, htlc.clone(), &self.onchain_tx_handler.channel_transaction_parameters.channel_type_features); let justice_package = PackageTemplate::build_package(commitment_txid, transaction_output_index, PackageSolvingData::RevokedHTLCOutput(revk_htlc_outp), htlc.cltv_expiry, height); @@ -3300,14 +3301,11 @@ impl ChannelMonitorImpl { if !claimable_outpoints.is_empty() || per_commitment_option.is_some() { // ie we're confident this is actually ours // We're definitely a counterparty commitment transaction! log_error!(logger, "Got broadcast of revoked counterparty commitment transaction, going to generate general spend tx with {} inputs", claimable_outpoints.len()); - for (idx, outp) in tx.output.iter().enumerate() { - watch_outputs.push((idx as u32, outp.clone())); - } self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number); - if let Some(per_commitment_data) = per_commitment_option { + if let Some(per_commitment_claimable_data) = per_commitment_option { fail_unbroadcast_htlcs!(self, "revoked_counterparty", commitment_txid, tx, height, - block_hash, per_commitment_data.iter().map(|(htlc, htlc_source)| + block_hash, per_commitment_claimable_data.iter().map(|(htlc, htlc_source)| (htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref())) ), logger); } else { @@ -3320,7 +3318,7 @@ impl ChannelMonitorImpl { block_hash, [].iter().map(|reference| *reference), logger); } } - } else if let Some(per_commitment_data) = per_commitment_option { + } else if let Some(per_commitment_claimable_data) = per_commitment_option { // While this isn't useful yet, there is a potential race where if a counterparty // revokes a state at the same time as the commitment transaction for that state is // confirmed, and the watchtower receives the block before the user, the user could @@ -3328,35 +3326,31 @@ impl ChannelMonitorImpl { // already processed the block, resulting in the counterparty_commitment_txn_on_chain entry // not being generated by the above conditional. Thus, to be safe, we go ahead and // insert it here. - for (idx, outp) in tx.output.iter().enumerate() { - watch_outputs.push((idx as u32, outp.clone())); - } self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number); log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid); fail_unbroadcast_htlcs!(self, "counterparty", commitment_txid, tx, height, block_hash, - per_commitment_data.iter().map(|(htlc, htlc_source)| + per_commitment_claimable_data.iter().map(|(htlc, htlc_source)| (htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref())) ), logger); - let (htlc_claim_reqs, counterparty_output_info) = - self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx)); + self.get_counterparty_output_claim_info(commitment_number, commitment_txid, Some(tx), per_commitment_option); to_counterparty_output_info = counterparty_output_info; for req in htlc_claim_reqs { claimable_outpoints.push(req); } } - (claimable_outpoints, (commitment_txid, watch_outputs), to_counterparty_output_info) + (claimable_outpoints, to_counterparty_output_info) } /// Returns the HTLC claim package templates and the counterparty output info - fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) + fn get_counterparty_output_claim_info(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>, per_commitment_option: Option<&Vec<(HTLCOutputInCommitment, Option>)>>) -> (Vec, CommitmentTxCounterpartyOutputInfo) { let mut claimable_outpoints = Vec::new(); let mut to_counterparty_output_info: CommitmentTxCounterpartyOutputInfo = None; - let htlc_outputs = match self.counterparty_claimable_outpoints.get(&commitment_txid) { + let per_commitment_claimable_data = match per_commitment_option { Some(outputs) => outputs, None => return (claimable_outpoints, to_counterparty_output_info), }; @@ -3395,7 +3389,7 @@ impl ChannelMonitorImpl { } } - for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() { + for &(ref htlc, _) in per_commitment_claimable_data.iter() { if let Some(transaction_output_index) = htlc.transaction_output_index { if let Some(transaction) = tx { if transaction_output_index as usize >= transaction.output.len() || @@ -3585,6 +3579,8 @@ impl ChannelMonitorImpl { if counterparty_commitment_txid == confirmed_commitment_txid { continue; } + // If we have generated claims for counterparty_commitment_txid earlier, we can rely on always + // having claim related htlcs for counterparty_commitment_txid in counterparty_claimable_outpoints. for (htlc, _) in self.counterparty_claimable_outpoints.get(counterparty_commitment_txid).unwrap_or(&vec![]) { log_trace!(logger, "Canceling claims for previously confirmed counterparty commitment {}", counterparty_commitment_txid); @@ -3777,24 +3773,25 @@ impl ChannelMonitorImpl { self.funding_spend_seen = true; let mut commitment_tx_to_counterparty_output = None; if (tx.input[0].sequence.0 >> 8*3) as u8 == 0x80 && (tx.lock_time.to_consensus_u32() >> 8*3) as u8 == 0x20 { - let (mut new_outpoints, new_outputs, counterparty_output_idx_sats) = - self.check_spend_counterparty_transaction(&tx, height, &block_hash, &logger); - commitment_tx_to_counterparty_output = counterparty_output_idx_sats; - if !new_outputs.1.is_empty() { - watch_outputs.push(new_outputs); - } - claimable_outpoints.append(&mut new_outpoints); - if new_outpoints.is_empty() { - if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &block_hash, &logger) { - #[cfg(not(fuzzing))] - debug_assert!(commitment_tx_to_counterparty_output.is_none(), - "A commitment transaction matched as both a counterparty and local commitment tx?"); - if !new_outputs.1.is_empty() { - watch_outputs.push(new_outputs); - } - claimable_outpoints.append(&mut new_outpoints); - balance_spendable_csv = Some(self.on_holder_tx_csv); + if let Some((mut new_outpoints, new_outputs)) = self.check_spend_holder_transaction(&tx, height, &block_hash, &logger) { + if !new_outputs.1.is_empty() { + watch_outputs.push(new_outputs); + } + + claimable_outpoints.append(&mut new_outpoints); + balance_spendable_csv = Some(self.on_holder_tx_csv); + } else { + let mut new_watch_outputs = Vec::new(); + for (idx, outp) in tx.output.iter().enumerate() { + new_watch_outputs.push((idx as u32, outp.clone())); } + watch_outputs.push((txid, new_watch_outputs)); + + let (mut new_outpoints, counterparty_output_idx_sats) = + self.check_spend_counterparty_transaction(&tx, height, &block_hash, &logger); + commitment_tx_to_counterparty_output = counterparty_output_idx_sats; + + claimable_outpoints.append(&mut new_outpoints); } } self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { @@ -4220,9 +4217,8 @@ impl ChannelMonitorImpl { } macro_rules! check_htlc_valid_counterparty { - ($counterparty_txid: expr, $htlc_output: expr) => { - if let Some(txid) = $counterparty_txid { - for &(ref pending_htlc, ref pending_source) in self.counterparty_claimable_outpoints.get(&txid).unwrap() { + ($htlc_output: expr, $per_commitment_data: expr) => { + for &(ref pending_htlc, ref pending_source) in $per_commitment_data { if pending_htlc.payment_hash == $htlc_output.payment_hash && pending_htlc.amount_msat == $htlc_output.amount_msat { if let &Some(ref source) = pending_source { log_claim!("revoked counterparty commitment tx", false, pending_htlc, true); @@ -4231,7 +4227,6 @@ impl ChannelMonitorImpl { } } } - } } } @@ -4248,9 +4243,13 @@ impl ChannelMonitorImpl { // resolve the source HTLC with the original sender. payment_data = Some(((*source).clone(), htlc_output.payment_hash, htlc_output.amount_msat)); } else if !$holder_tx { - check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output); + if let Some(current_counterparty_commitment_txid) = &self.current_counterparty_commitment_txid { + check_htlc_valid_counterparty!(htlc_output, self.counterparty_claimable_outpoints.get(current_counterparty_commitment_txid).unwrap()); + } if payment_data.is_none() { - check_htlc_valid_counterparty!(self.prev_counterparty_commitment_txid, htlc_output); + if let Some(prev_counterparty_commitment_txid) = &self.prev_counterparty_commitment_txid { + check_htlc_valid_counterparty!(htlc_output, self.counterparty_claimable_outpoints.get(prev_counterparty_commitment_txid).unwrap()); + } } } if payment_data.is_none() { @@ -4288,7 +4287,7 @@ impl ChannelMonitorImpl { } } if let Some(ref htlc_outputs) = self.counterparty_claimable_outpoints.get(&input.previous_output.txid) { - scan_commitment!(htlc_outputs.iter().map(|&(ref a, ref b)| (a, (b.as_ref().clone()).map(|boxed| &**boxed))), + scan_commitment!(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))), "counterparty commitment tx", false); }