X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fchain%2Fchannelmonitor.rs;h=c483d01f37d129697a25d7a369f9f4b8cddce21f;hb=1b6a7c1314a3840f57af4457977a5c98a864edd1;hp=22708f18b401c7ad3b836cc66fcfcfe93831560b;hpb=73ee30d9da29ebe9e87b00db27c03f8830a754b1;p=rust-lightning diff --git a/lightning/src/chain/channelmonitor.rs b/lightning/src/chain/channelmonitor.rs index 22708f18..c483d01f 100644 --- a/lightning/src/chain/channelmonitor.rs +++ b/lightning/src/chain/channelmonitor.rs @@ -115,67 +115,6 @@ impl Readable for ChannelMonitorUpdate { } } -/// An error enum representing a failure to persist a channel monitor update. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum ChannelMonitorUpdateErr { - /// Used to indicate a temporary failure (eg connection to a watchtower or remote backup of - /// our state failed, but is expected to succeed at some point in the future). - /// - /// Such a failure will "freeze" a channel, preventing us from revoking old states or - /// submitting new commitment transactions to the counterparty. Once the update(s) which failed - /// have been successfully applied, ChannelManager::channel_monitor_updated can be used to - /// restore the channel to an operational state. - /// - /// Note that a given ChannelManager will *never* re-generate a given ChannelMonitorUpdate. If - /// you return a TemporaryFailure you must ensure that it is written to disk safely before - /// writing out the latest ChannelManager state. - /// - /// Even when a channel has been "frozen" updates to the ChannelMonitor can continue to occur - /// (eg if an inbound HTLC which we forwarded was claimed upstream resulting in us attempting - /// to claim it on this channel) and those updates must be applied wherever they can be. At - /// least one such updated ChannelMonitor must be persisted otherwise PermanentFailure should - /// be returned to get things on-chain ASAP using only the in-memory copy. Obviously updates to - /// the channel which would invalidate previous ChannelMonitors are not made when a channel has - /// been "frozen". - /// - /// Note that even if updates made after TemporaryFailure succeed you must still call - /// channel_monitor_updated to ensure you have the latest monitor and re-enable normal channel - /// operation. - /// - /// Note that the update being processed here will not be replayed for you when you call - /// ChannelManager::channel_monitor_updated, so you must store the update itself along - /// with the persisted ChannelMonitor on your own local disk prior to returning a - /// TemporaryFailure. You may, of course, employ a journaling approach, storing only the - /// ChannelMonitorUpdate on disk without updating the monitor itself, replaying the journal at - /// reload-time. - /// - /// For deployments where a copy of ChannelMonitors and other local state are backed up in a - /// remote location (with local copies persisted immediately), it is anticipated that all - /// updates will return TemporaryFailure until the remote copies could be updated. - TemporaryFailure, - /// Used to indicate no further channel monitor updates will be allowed (eg we've moved on to a - /// different watchtower and cannot update with all watchtowers that were previously informed - /// of this channel). - /// - /// At reception of this error, ChannelManager will force-close the channel and return at - /// least a final ChannelMonitorUpdate::ChannelForceClosed which must be delivered to at - /// least one ChannelMonitor copy. Revocation secret MUST NOT be released and offchain channel - /// update must be rejected. - /// - /// This failure may also signal a failure to update the local persisted copy of one of - /// the channel monitor instance. - /// - /// Note that even when you fail a holder commitment transaction update, you must store the - /// update to ensure you can claim from it in case of a duplicate copy of this ChannelMonitor - /// broadcasts it (e.g distributed channel-monitor deployment) - /// - /// In case of distributed watchtowers deployment, the new version must be written to disk, as - /// state may have been stored but rejected due to a block forcing a commitment broadcast. This - /// storage is used to claim outputs of rejected state confirmed onchain by another watchtower, - /// lagging behind on block processing. - PermanentFailure, -} - /// General Err type for ChannelMonitor actions. Generally, this implies that the data provided is /// inconsistent with the ChannelMonitor being called. eg for ChannelMonitor::update_monitor this /// means you tried to update a monitor for a different channel or the ChannelMonitorUpdate was @@ -190,8 +129,8 @@ pub enum MonitorEvent { /// A monitor event containing an HTLCUpdate. HTLCEvent(HTLCUpdate), - /// A monitor event that the Channel's commitment transaction was broadcasted. - CommitmentTxBroadcasted(OutPoint), + /// A monitor event that the Channel's commitment transaction was confirmed. + CommitmentTxConfirmed(OutPoint), } /// Simple structure sent back by `chain::Watch` when an HTLC from a forward channel is detected on @@ -232,8 +171,13 @@ pub(crate) const CLTV_CLAIM_BUFFER: u32 = 18; /// with at worst this delay, so we are not only using this value as a mercy for them but also /// us as a safeguard to delay with enough time. pub(crate) const LATENCY_GRACE_PERIOD_BLOCKS: u32 = 3; -/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding inbound -/// HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us losing money. +/// Number of blocks we wait on seeing a HTLC output being solved before we fail corresponding +/// inbound HTLCs. This prevents us from failing backwards and then getting a reorg resulting in us +/// losing money. +/// +/// Note that this is a library-wide security assumption. If a reorg deeper than this number of +/// blocks occurs, counterparties may be able to steal funds or claims made by and balances exposed +/// by a [`ChannelMonitor`] may be incorrect. // We also use this delay to be sure we can remove our in-flight claim txn from bump candidates buffer. // It may cause spurious generation of bumped claim txn but that's alright given the outpoint is already // solved by a previous claim tx. What we want to avoid is reorg evicting our claim tx and us not @@ -364,7 +308,8 @@ impl OnchainEventEntry { // it's broadcastable when we see the previous block. conf_threshold = cmp::max(conf_threshold, self.height + descriptor.to_self_delay as u32 - 1); }, - OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } => { + OnchainEvent::FundingSpendConfirmation { on_local_output_csv: Some(csv), .. } | + OnchainEvent::HTLCSpendConfirmation { on_to_local_output_csv: Some(csv), .. } => { // A CSV'd transaction is confirmable in block (input height) + CSV delay, which means // it's broadcastable when we see the previous block. conf_threshold = cmp::max(conf_threshold, self.height + csv as u32 - 1); @@ -383,13 +328,19 @@ impl OnchainEventEntry { /// once they mature to enough confirmations (ANTI_REORG_DELAY) #[derive(PartialEq)] enum OnchainEvent { - /// HTLC output getting solved by a timeout, at maturation we pass upstream payment source information to solve - /// inbound HTLC in backward channel. Note, in case of preimage, we pass info to upstream without delay as we can - /// only win from it, so it's never an OnchainEvent + /// An outbound HTLC failing after a transaction is confirmed. Used + /// * when an outbound HTLC output is spent by us after the HTLC timed out + /// * an outbound HTLC which was not present in the commitment transaction which appeared + /// on-chain (either because it was not fully committed to or it was dust). + /// Note that this is *not* used for preimage claims, as those are passed upstream immediately, + /// appearing only as an `HTLCSpendConfirmation`, below. HTLCUpdate { source: HTLCSource, payment_hash: PaymentHash, onchain_value_satoshis: Option, + /// None in the second case, above, ie when there is no relevant output in the commitment + /// transaction which appeared on chain. + input_idx: Option, }, MaturingOutput { descriptor: SpendableOutputDescriptor, @@ -397,11 +348,27 @@ enum OnchainEvent { /// A spend of the funding output, either a commitment transaction or a cooperative closing /// transaction. FundingSpendConfirmation { - txid: Txid, /// The CSV delay for the output of the funding spend transaction (implying it is a local /// commitment transaction, and this is the delay on the to_self output). on_local_output_csv: Option, }, + /// A spend of a commitment transaction HTLC output, set in the cases where *no* `HTLCUpdate` + /// is constructed. This is used when + /// * an outbound HTLC is claimed by our counterparty with a preimage, causing us to + /// immediately claim the HTLC on the inbound edge and track the resolution here, + /// * an inbound HTLC is claimed by our counterparty (with a timeout), + /// * an inbound HTLC is claimed by us (with a preimage). + /// * a revoked-state HTLC transaction was broadcasted, which was claimed by the revocation + /// signature. + HTLCSpendConfirmation { + input_idx: u32, + /// If the claim was made by either party with a preimage, this is filled in + preimage: Option, + /// If the claim was made by us on an inbound HTLC against a local commitment transaction, + /// we set this to the output CSV value which we will have to wait until to spend the + /// output (and generate a SpendableOutput event). + on_to_local_output_csv: Option, + }, } impl Writeable for OnchainEventEntry { @@ -438,14 +405,20 @@ impl_writeable_tlv_based_enum_upgradable!(OnchainEvent, (0, source, required), (1, onchain_value_satoshis, option), (2, payment_hash, required), + (3, input_idx, option), }, (1, MaturingOutput) => { (0, descriptor, required), }, (3, FundingSpendConfirmation) => { - (0, txid, required), - (2, on_local_output_csv, option), + (0, on_local_output_csv, option), }, + (5, HTLCSpendConfirmation) => { + (0, input_idx, required), + (2, preimage, option), + (4, on_to_local_output_csv, option), + }, + ); #[cfg_attr(any(test, feature = "fuzztarget", feature = "_test_utils"), derive(PartialEq))] @@ -506,6 +479,72 @@ impl_writeable_tlv_based_enum_upgradable!(ChannelMonitorUpdateStep, }, ); +/// Details about the balance(s) available for spending once the channel appears on chain. +/// +/// See [`ChannelMonitor::get_claimable_balances`] for more details on when these will or will not +/// be provided. +#[derive(Clone, Debug, PartialEq, Eq)] +#[cfg_attr(test, derive(PartialOrd, Ord))] +pub enum Balance { + /// The channel is not yet closed (or the commitment or closing transaction has not yet + /// appeared in a block). The given balance is claimable (less on-chain fees) if the channel is + /// force-closed now. + ClaimableOnChannelClose { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + }, + /// The channel has been closed, and the given balance is ours but awaiting confirmations until + /// we consider it spendable. + ClaimableAwaitingConfirmations { + /// The amount available to claim, in satoshis, possibly excluding the on-chain fees which + /// were spent in broadcasting the transaction. + claimable_amount_satoshis: u64, + /// The height at which an [`Event::SpendableOutputs`] event will be generated for this + /// amount. + confirmation_height: u32, + }, + /// The channel has been closed, and the given balance should be ours but awaiting spending + /// transaction confirmation. If the spending transaction does not confirm in time, it is + /// possible our counterparty can take the funds by broadcasting an HTLC timeout on-chain. + /// + /// Once the spending transaction confirms, before it has reached enough confirmations to be + /// considered safe from chain reorganizations, the balance will instead be provided via + /// [`Balance::ClaimableAwaitingConfirmations`]. + ContentiousClaimable { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + /// The height at which the counterparty may be able to claim the balance if we have not + /// done so. + timeout_height: u32, + }, + /// HTLCs which we sent to our counterparty which are claimable after a timeout (less on-chain + /// fees) if the counterparty does not know the preimage for the HTLCs. These are somewhat + /// likely to be claimed by our counterparty before we do. + MaybeClaimableHTLCAwaitingTimeout { + /// The amount available to claim, in satoshis, excluding the on-chain fees which will be + /// required to do so. + claimable_amount_satoshis: u64, + /// The height at which we will be able to claim the balance if our counterparty has not + /// done so. + claimable_height: u32, + }, +} + +/// An HTLC which has been irrevocably resolved on-chain, and has reached ANTI_REORG_DELAY. +#[derive(PartialEq)] +struct IrrevocablyResolvedHTLC { + input_idx: u32, + /// Only set if the HTLC claim was ours using a payment preimage + payment_preimage: Option, +} + +impl_writeable_tlv_based!(IrrevocablyResolvedHTLC, { + (0, input_idx, required), + (2, payment_preimage, option), +}); + /// A ChannelMonitor handles chain events (blocks connected and disconnected) and generates /// on-chain transactions to ensure no loss of funds occurs. /// @@ -619,6 +658,10 @@ pub(crate) struct ChannelMonitorImpl { holder_tx_signed: bool, funding_spend_confirmed: Option, + /// The set of HTLCs which have been either claimed or failed on chain and have reached + /// the requisite confirmations on the claim/fail transaction (either ANTI_REORG_DELAY or the + /// spending CSV for revocable outputs). + htlcs_resolved_on_chain: Vec, // We simply modify best_block in Channel's block_connected so that serialization is // consistent but hopefully the users' copy handles block_connected in a consistent way. @@ -679,7 +722,8 @@ impl PartialEq for ChannelMonitorImpl { self.outputs_to_watch != other.outputs_to_watch || self.lockdown_from_offchain != other.lockdown_from_offchain || self.holder_tx_signed != other.holder_tx_signed || - self.funding_spend_confirmed != other.funding_spend_confirmed + self.funding_spend_confirmed != other.funding_spend_confirmed || + self.htlcs_resolved_on_chain != other.htlcs_resolved_on_chain { false } else { @@ -813,7 +857,7 @@ impl Writeable for ChannelMonitorImpl { 0u8.write(writer)?; upd.write(writer)?; }, - MonitorEvent::CommitmentTxBroadcasted(_) => 1u8.write(writer)? + MonitorEvent::CommitmentTxConfirmed(_) => 1u8.write(writer)? } } @@ -846,6 +890,7 @@ impl Writeable for ChannelMonitorImpl { write_tlv_fields!(writer, { (1, self.funding_spend_confirmed, option), + (3, self.htlcs_resolved_on_chain, vec_type), }); Ok(()) @@ -945,6 +990,7 @@ impl ChannelMonitor { lockdown_from_offchain: false, holder_tx_signed: false, funding_spend_confirmed: None, + htlcs_resolved_on_chain: Vec::new(), best_block, @@ -1253,6 +1299,173 @@ impl ChannelMonitor { pub fn current_best_block(&self) -> BestBlock { self.inner.lock().unwrap().best_block.clone() } + + /// Gets the balances in this channel which are either claimable by us if we were to + /// force-close the channel now or which are claimable on-chain (possibly awaiting + /// confirmation). + /// + /// Any balances in the channel which are available on-chain (excluding on-chain fees) are + /// included here until an [`Event::SpendableOutputs`] event has been generated for the + /// balance, or until our counterparty has claimed the balance and accrued several + /// confirmations on the claim transaction. + /// + /// Note that the balances available when you or your counterparty have broadcasted revoked + /// state(s) may not be fully captured here. + // TODO, fix that ^ + /// + /// See [`Balance`] for additional details on the types of claimable balances which + /// may be returned here and their meanings. + pub fn get_claimable_balances(&self) -> Vec { + let mut res = Vec::new(); + let us = self.inner.lock().unwrap(); + + let mut confirmed_txid = us.funding_spend_confirmed; + let mut pending_commitment_tx_conf_thresh = None; + let funding_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::FundingSpendConfirmation { .. } = event.event { + Some((event.txid, event.confirmation_threshold())) + } else { None } + }); + if let Some((txid, conf_thresh)) = funding_spend_pending { + debug_assert!(us.funding_spend_confirmed.is_none(), + "We have a pending funding spend awaiting anti-reorg confirmation, we can't have confirmed it already!"); + confirmed_txid = Some(txid); + pending_commitment_tx_conf_thresh = Some(conf_thresh); + } + + macro_rules! walk_htlcs { + ($holder_commitment: expr, $htlc_iter: expr) => { + for htlc in $htlc_iter { + if let Some(htlc_input_idx) = htlc.transaction_output_index { + if us.htlcs_resolved_on_chain.iter().any(|v| v.input_idx == htlc_input_idx) { + assert!(us.funding_spend_confirmed.is_some()); + } else if htlc.offered == $holder_commitment { + // If the payment was outbound, check if there's an HTLCUpdate + // indicating we have spent this HTLC with a timeout, claiming it back + // and awaiting confirmations on it. + let htlc_update_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::HTLCUpdate { input_idx: Some(input_idx), .. } = event.event { + if input_idx == htlc_input_idx { Some(event.confirmation_threshold()) } else { None } + } else { None } + }); + if let Some(conf_thresh) = htlc_update_pending { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: htlc.amount_msat / 1000, + confirmation_height: conf_thresh, + }); + } else { + res.push(Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: htlc.amount_msat / 1000, + claimable_height: htlc.cltv_expiry, + }); + } + } else if us.payment_preimages.get(&htlc.payment_hash).is_some() { + // Otherwise (the payment was inbound), only expose it as claimable if + // we know the preimage. + // Note that if there is a pending claim, but it did not use the + // preimage, we lost funds to our counterparty! We will then continue + // to show it as ContentiousClaimable until ANTI_REORG_DELAY. + let htlc_spend_pending = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } = event.event { + if input_idx == htlc_input_idx { + Some((event.confirmation_threshold(), preimage.is_some())) + } else { None } + } else { None } + }); + if let Some((conf_thresh, true)) = htlc_spend_pending { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: htlc.amount_msat / 1000, + confirmation_height: conf_thresh, + }); + } else { + res.push(Balance::ContentiousClaimable { + claimable_amount_satoshis: htlc.amount_msat / 1000, + timeout_height: htlc.cltv_expiry, + }); + } + } + } + } + } + } + + if let Some(txid) = confirmed_txid { + let mut found_commitment_tx = false; + if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid { + walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().map(|(a, _)| a)); + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + if let Some(value) = us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| { + if let OnchainEvent::MaturingOutput { + descriptor: SpendableOutputDescriptor::StaticPaymentOutput(descriptor) + } = &event.event { + Some(descriptor.output.value) + } else { None } + }) { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: value, + confirmation_height: conf_thresh, + }); + } else { + // If a counterparty commitment transaction is awaiting confirmation, we + // should either have a StaticPaymentOutput MaturingOutput event awaiting + // confirmation with the same height or have never met our dust amount. + } + } + found_commitment_tx = true; + } else if txid == us.current_holder_commitment_tx.txid { + walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, _)| a)); + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat, + confirmation_height: conf_thresh, + }); + } + found_commitment_tx = true; + } else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx { + if txid == prev_commitment.txid { + walk_htlcs!(true, prev_commitment.htlc_outputs.iter().map(|(a, _, _)| a)); + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: prev_commitment.to_self_value_sat, + confirmation_height: conf_thresh, + }); + } + found_commitment_tx = true; + } + } + if !found_commitment_tx { + if let Some(conf_thresh) = pending_commitment_tx_conf_thresh { + // We blindly assume this is a cooperative close transaction here, and that + // neither us nor our counterparty misbehaved. At worst we've under-estimated + // the amount we can claim as we'll punish a misbehaving counterparty. + res.push(Balance::ClaimableAwaitingConfirmations { + claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat, + confirmation_height: conf_thresh, + }); + } + } + // TODO: Add logic to provide claimable balances for counterparty broadcasting revoked + // outputs. + } else { + let mut claimable_inbound_htlc_value_sat = 0; + for (htlc, _, _) in us.current_holder_commitment_tx.htlc_outputs.iter() { + if htlc.transaction_output_index.is_none() { continue; } + if htlc.offered { + res.push(Balance::MaybeClaimableHTLCAwaitingTimeout { + claimable_amount_satoshis: htlc.amount_msat / 1000, + claimable_height: htlc.cltv_expiry, + }); + } else if us.payment_preimages.get(&htlc.payment_hash).is_some() { + claimable_inbound_htlc_value_sat += htlc.amount_msat / 1000; + } + } + res.push(Balance::ClaimableOnChannelClose { + claimable_amount_satoshis: us.current_holder_commitment_tx.to_self_value_sat + claimable_inbound_htlc_value_sat, + }); + } + + res + } } /// Compares a broadcasted commitment transaction's HTLCs with those in the latest state, @@ -1311,6 +1524,7 @@ macro_rules! fail_unbroadcast_htlcs { source: (**source).clone(), payment_hash: htlc.payment_hash.clone(), onchain_value_satoshis: Some(htlc.amount_msat / 1000), + input_idx: None, }, }; log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction, waiting for confirmation (at height {})", @@ -1512,7 +1726,7 @@ impl ChannelMonitorImpl { log_info!(logger, "Broadcasting local {}", log_tx!(tx)); broadcaster.broadcast_transaction(tx); } - self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0)); + self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(self.funding_info.0)); } pub fn update_monitor(&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &L) -> Result<(), MonitorUpdateError> @@ -1560,7 +1774,7 @@ impl ChannelMonitorImpl { } else if !self.holder_tx_signed { log_error!(logger, "You have a toxic holder commitment transaction avaible in channel monitor, read comment in ChannelMonitor::get_latest_holder_commitment_txn to be informed of manual action to take"); } else { - // If we generated a MonitorEvent::CommitmentTxBroadcasted, the ChannelManager + // If we generated a MonitorEvent::CommitmentTxConfirmed, the ChannelManager // will still give us a ChannelForceClosed event with !should_broadcast, but we // shouldn't print the scary warning above. log_info!(logger, "Channel off-chain state closed after we broadcasted our latest commitment transaction."); @@ -2024,7 +2238,6 @@ impl ChannelMonitorImpl { txid, height: height, event: OnchainEvent::FundingSpendConfirmation { - txid, on_local_output_csv: balance_spendable_csv, }, }); @@ -2083,7 +2296,7 @@ impl ChannelMonitorImpl { let funding_outp = HolderFundingOutput::build(self.funding_redeemscript.clone()); let commitment_package = PackageTemplate::build_package(self.funding_info.0.txid.clone(), self.funding_info.0.index as u32, PackageSolvingData::HolderFundingOutput(funding_outp), self.best_block.height(), false, self.best_block.height()); claimable_outpoints.push(commitment_package); - self.pending_monitor_events.push(MonitorEvent::CommitmentTxBroadcasted(self.funding_info.0)); + self.pending_monitor_events.push(MonitorEvent::CommitmentTxConfirmed(self.funding_info.0)); let commitment_tx = self.onchain_tx_handler.get_fully_signed_holder_tx(&self.funding_redeemscript); self.holder_tx_signed = true; // Because we're broadcasting a commitment transaction, we should construct the package @@ -2115,8 +2328,7 @@ impl ChannelMonitorImpl { .iter() .filter_map(|entry| match &entry.event { OnchainEvent::HTLCUpdate { source, .. } => Some(source), - OnchainEvent::MaturingOutput { .. } => None, - OnchainEvent::FundingSpendConfirmation { .. } => None, + _ => None, }) .collect(); #[cfg(debug_assertions)] @@ -2125,7 +2337,7 @@ impl ChannelMonitorImpl { // Produce actionable events from on-chain events having reached their threshold. for entry in onchain_events_reaching_threshold_conf.drain(..) { match entry.event { - OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis } => { + OnchainEvent::HTLCUpdate { ref source, payment_hash, onchain_value_satoshis, input_idx } => { // Check for duplicate HTLC resolutions. #[cfg(debug_assertions)] { @@ -2149,6 +2361,9 @@ impl ChannelMonitorImpl { source: source.clone(), onchain_value_satoshis, })); + if let Some(idx) = input_idx { + self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx: idx, payment_preimage: None }); + } }, OnchainEvent::MaturingOutput { descriptor } => { log_debug!(logger, "Descriptor {} has got enough confirmations to be passed upstream", log_spendable!(descriptor)); @@ -2156,8 +2371,11 @@ impl ChannelMonitorImpl { outputs: vec![descriptor] }); }, - OnchainEvent::FundingSpendConfirmation { txid, .. } => { - self.funding_spend_confirmed = Some(txid); + OnchainEvent::HTLCSpendConfirmation { input_idx, preimage, .. } => { + self.htlcs_resolved_on_chain.push(IrrevocablyResolvedHTLC { input_idx, payment_preimage: preimage }); + }, + OnchainEvent::FundingSpendConfirmation { .. } => { + self.funding_spend_confirmed = Some(entry.txid); }, } } @@ -2336,15 +2554,34 @@ impl ChannelMonitorImpl { let revocation_sig_claim = (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && input.witness[1].len() == 33) || (input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && input.witness[1].len() == 33); let accepted_preimage_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::AcceptedHTLC); - let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC); + #[cfg(not(fuzzing))] + let accepted_timeout_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::AcceptedHTLC) && !revocation_sig_claim; + let offered_preimage_claim = input.witness.len() == 3 && HTLCType::scriptlen_to_htlctype(input.witness[2].len()) == Some(HTLCType::OfferedHTLC) && !revocation_sig_claim; + #[cfg(not(fuzzing))] + let offered_timeout_claim = input.witness.len() == 5 && HTLCType::scriptlen_to_htlctype(input.witness[4].len()) == Some(HTLCType::OfferedHTLC); + + let mut payment_preimage = PaymentPreimage([0; 32]); + if accepted_preimage_claim { + payment_preimage.0.copy_from_slice(&input.witness[3]); + } else if offered_preimage_claim { + payment_preimage.0.copy_from_slice(&input.witness[1]); + } macro_rules! log_claim { ($tx_info: expr, $holder_tx: expr, $htlc: expr, $source_avail: expr) => { - // We found the output in question, but aren't failing it backwards - // as we have no corresponding source and no valid counterparty commitment txid - // to try a weak source binding with same-hash, same-value still-valid offered HTLC. - // This implies either it is an inbound HTLC or an outbound HTLC on a revoked transaction. let outbound_htlc = $holder_tx == $htlc.offered; + // HTLCs must either be claimed by a matching script type or through the + // revocation path: + #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures" + debug_assert!(!$htlc.offered || offered_preimage_claim || offered_timeout_claim || revocation_sig_claim); + #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures" + debug_assert!($htlc.offered || accepted_preimage_claim || accepted_timeout_claim || revocation_sig_claim); + // Further, only exactly one of the possible spend paths should have been + // matched by any HTLC spend: + #[cfg(not(fuzzing))] // Note that the fuzzer is not bound by pesky things like "signatures" + debug_assert_eq!(accepted_preimage_claim as u8 + accepted_timeout_claim as u8 + + offered_preimage_claim as u8 + offered_timeout_claim as u8 + + revocation_sig_claim as u8, 1); if ($holder_tx && revocation_sig_claim) || (outbound_htlc && !$source_avail && (accepted_preimage_claim || offered_preimage_claim)) { log_error!(logger, "Input spending {} ({}:{}) in {} resolves {} HTLC with payment hash {} with {}!", @@ -2389,13 +2626,37 @@ 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); + check_htlc_valid_counterparty!(self.current_counterparty_commitment_txid, htlc_output); if payment_data.is_none() { check_htlc_valid_counterparty!(self.prev_counterparty_commitment_txid, htlc_output); } } if payment_data.is_none() { log_claim!($tx_info, $holder_tx, htlc_output, false); + let outbound_htlc = $holder_tx == htlc_output.offered; + if !outbound_htlc || revocation_sig_claim { + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid: tx.txid(), height, + event: OnchainEvent::HTLCSpendConfirmation { + input_idx: input.previous_output.vout, + preimage: if accepted_preimage_claim || offered_preimage_claim { + Some(payment_preimage) } else { None }, + // If this is a payment to us (!outbound_htlc, above), + // wait for the CSV delay before dropping the HTLC from + // claimable balance if the claim was an HTLC-Success + // transaction. + on_to_local_output_csv: if accepted_preimage_claim { + Some(self.on_holder_tx_csv) } else { None }, + }, + }); + } else { + // Outbound claims should always have payment_data, unless + // we've already failed the HTLC as the commitment transaction + // which was broadcasted was revoked. In that case, we should + // spend the HTLC output here immediately, and expose that fact + // as a Balance, something which we do not yet do. + // TODO: Track the above as claimable! + } continue 'outer_loop; } } @@ -2421,11 +2682,18 @@ impl ChannelMonitorImpl { // Check that scan_commitment, above, decided there is some source worth relaying an // HTLC resolution backwards to and figure out whether we learned a preimage from it. if let Some((source, payment_hash, amount_msat)) = payment_data { - let mut payment_preimage = PaymentPreimage([0; 32]); if accepted_preimage_claim { if !self.pending_monitor_events.iter().any( |update| if let &MonitorEvent::HTLCEvent(ref upd) = update { upd.source == source } else { false }) { - payment_preimage.0.copy_from_slice(&input.witness[3]); + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid: tx.txid(), + height, + event: OnchainEvent::HTLCSpendConfirmation { + input_idx: input.previous_output.vout, + preimage: Some(payment_preimage), + on_to_local_output_csv: None, + }, + }); self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate { source, payment_preimage: Some(payment_preimage), @@ -2438,7 +2706,15 @@ impl ChannelMonitorImpl { |update| if let &MonitorEvent::HTLCEvent(ref upd) = update { upd.source == source } else { false }) { - payment_preimage.0.copy_from_slice(&input.witness[1]); + self.onchain_events_awaiting_threshold_conf.push(OnchainEventEntry { + txid: tx.txid(), + height, + event: OnchainEvent::HTLCSpendConfirmation { + input_idx: input.previous_output.vout, + preimage: Some(payment_preimage), + on_to_local_output_csv: None, + }, + }); self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate { source, payment_preimage: Some(payment_preimage), @@ -2462,6 +2738,7 @@ impl ChannelMonitorImpl { event: OnchainEvent::HTLCUpdate { source, payment_hash, onchain_value_satoshis: Some(amount_msat / 1000), + input_idx: Some(input.previous_output.vout), }, }; log_info!(logger, "Failing HTLC with payment_hash {} timeout by a spend tx, waiting for confirmation (at height {})", log_bytes!(payment_hash.0), entry.confirmation_threshold()); @@ -2539,53 +2816,6 @@ impl ChannelMonitorImpl { } } -/// `Persist` defines behavior for persisting channel monitors: this could mean -/// writing once to disk, and/or uploading to one or more backup services. -/// -/// Note that for every new monitor, you **must** persist the new `ChannelMonitor` -/// to disk/backups. And, on every update, you **must** persist either the -/// `ChannelMonitorUpdate` or the updated monitor itself. Otherwise, there is risk -/// of situations such as revoking a transaction, then crashing before this -/// revocation can be persisted, then unintentionally broadcasting a revoked -/// transaction and losing money. This is a risk because previous channel states -/// are toxic, so it's important that whatever channel state is persisted is -/// kept up-to-date. -pub trait Persist { - /// Persist a new channel's data. The data can be stored any way you want, but - /// the identifier provided by Rust-Lightning is the channel's outpoint (and - /// it is up to you to maintain a correct mapping between the outpoint and the - /// stored channel data). Note that you **must** persist every new monitor to - /// disk. See the `Persist` trait documentation for more details. - /// - /// See [`ChannelMonitor::write`] for writing out a `ChannelMonitor`, - /// and [`ChannelMonitorUpdateErr`] for requirements when returning errors. - fn persist_new_channel(&self, id: OutPoint, data: &ChannelMonitor) -> Result<(), ChannelMonitorUpdateErr>; - - /// Update one channel's data. The provided `ChannelMonitor` has already - /// applied the given update. - /// - /// Note that on every update, you **must** persist either the - /// `ChannelMonitorUpdate` or the updated monitor itself to disk/backups. See - /// the `Persist` trait documentation for more details. - /// - /// If an implementer chooses to persist the updates only, they need to make - /// sure that all the updates are applied to the `ChannelMonitors` *before* - /// the set of channel monitors is given to the `ChannelManager` - /// deserialization routine. See [`ChannelMonitor::update_monitor`] for - /// applying a monitor update to a monitor. If full `ChannelMonitors` are - /// persisted, then there is no need to persist individual updates. - /// - /// Note that there could be a performance tradeoff between persisting complete - /// channel monitors on every update vs. persisting only updates and applying - /// them in batches. The size of each monitor grows `O(number of state updates)` - /// whereas updates are small and `O(1)`. - /// - /// See [`ChannelMonitor::write`] for writing out a `ChannelMonitor`, - /// [`ChannelMonitorUpdate::write`] for writing out an update, and - /// [`ChannelMonitorUpdateErr`] for requirements when returning errors. - fn update_persisted_channel(&self, id: OutPoint, update: &ChannelMonitorUpdate, data: &ChannelMonitor) -> Result<(), ChannelMonitorUpdateErr>; -} - impl chain::Listen for (ChannelMonitor, T, F, L) where T::Target: BroadcasterInterface, @@ -2774,7 +3004,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> for _ in 0..pending_monitor_events_len { let ev = match ::read(reader)? { 0 => MonitorEvent::HTLCEvent(Readable::read(reader)?), - 1 => MonitorEvent::CommitmentTxBroadcasted(funding_info.0), + 1 => MonitorEvent::CommitmentTxConfirmed(funding_info.0), _ => return Err(DecodeError::InvalidValue) }; pending_monitor_events.push(ev); @@ -2834,8 +3064,10 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> } let mut funding_spend_confirmed = None; + let mut htlcs_resolved_on_chain = Some(Vec::new()); read_tlv_fields!(reader, { (1, funding_spend_confirmed, option), + (3, htlcs_resolved_on_chain, vec_type), }); let mut secp_ctx = Secp256k1::new(); @@ -2886,6 +3118,7 @@ impl<'a, Signer: Sign, K: KeysInterface> ReadableArgs<&'a K> lockdown_from_offchain, holder_tx_signed, funding_spend_confirmed, + htlcs_resolved_on_chain: htlcs_resolved_on_chain.unwrap(), best_block,