Merge pull request #2825 from tnull/2024-01-upstream-output-sweeper
authorMatt Corallo <649246+TheBlueMatt@users.noreply.github.com>
Thu, 18 Apr 2024 15:24:47 +0000 (08:24 -0700)
committerGitHub <noreply@github.com>
Thu, 18 Apr 2024 15:24:47 +0000 (08:24 -0700)
Add `OutputSweeper` utility persisting and sweeping spendable outputs

1  2 
lightning/src/events/mod.rs
lightning/src/ln/functional_tests.rs

index d34db885f7c4e0d24ad52281f143f5fb08c385ad,4d644fe43f8e48c318c754ff6ebb968cd31b326d..c696f49a2245ad81a719865f283e51c75f653fab
@@@ -18,7 -18,6 +18,7 @@@ pub mod bump_transaction
  
  pub use bump_transaction::BumpTransactionEvent;
  
 +use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef};
  use crate::sign::SpendableOutputDescriptor;
  use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
  use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS;
@@@ -50,12 -49,11 +50,12 @@@ use crate::prelude::*
  /// spontaneous payment or a "conventional" lightning payment that's paying an invoice.
  #[derive(Clone, Debug, PartialEq, Eq)]
  pub enum PaymentPurpose {
 -      /// Information for receiving a payment that we generated an invoice for.
 -      InvoicePayment {
 +      /// A payment for a BOLT 11 invoice.
 +      Bolt11InvoicePayment {
                /// The preimage to the payment_hash, if the payment hash (and secret) were fetched via
 -              /// [`ChannelManager::create_inbound_payment`]. If provided, this can be handed directly to
 -              /// [`ChannelManager::claim_funds`].
 +              /// [`ChannelManager::create_inbound_payment`]. When handling [`Event::PaymentClaimable`],
 +              /// this can be passed directly to [`ChannelManager::claim_funds`] to claim the payment. No
 +              /// action is needed when seen in [`Event::PaymentClaimed`].
                ///
                /// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
                /// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
                /// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
                payment_secret: PaymentSecret,
        },
 +      /// A payment for a BOLT 12 [`Offer`].
 +      ///
 +      /// [`Offer`]: crate::offers::offer::Offer
 +      Bolt12OfferPayment {
 +              /// The preimage to the payment hash. When handling [`Event::PaymentClaimable`], this can be
 +              /// passed directly to [`ChannelManager::claim_funds`], if provided. No action is needed
 +              /// when seen in [`Event::PaymentClaimed`].
 +              ///
 +              /// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
 +              payment_preimage: Option<PaymentPreimage>,
 +              /// The secret used to authenticate the sender to the recipient, preventing a number of
 +              /// de-anonymization attacks while routing a payment.
 +              ///
 +              /// See [`PaymentPurpose::Bolt11InvoicePayment::payment_secret`] for further details.
 +              payment_secret: PaymentSecret,
 +              /// The context of the payment such as information about the corresponding [`Offer`] and
 +              /// [`InvoiceRequest`].
 +              ///
 +              /// [`Offer`]: crate::offers::offer::Offer
 +              /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
 +              payment_context: Bolt12OfferContext,
 +      },
 +      /// A payment for a BOLT 12 [`Refund`].
 +      ///
 +      /// [`Refund`]: crate::offers::refund::Refund
 +      Bolt12RefundPayment {
 +              /// The preimage to the payment hash. When handling [`Event::PaymentClaimable`], this can be
 +              /// passed directly to [`ChannelManager::claim_funds`], if provided. No action is needed
 +              /// when seen in [`Event::PaymentClaimed`].
 +              ///
 +              /// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
 +              payment_preimage: Option<PaymentPreimage>,
 +              /// The secret used to authenticate the sender to the recipient, preventing a number of
 +              /// de-anonymization attacks while routing a payment.
 +              ///
 +              /// See [`PaymentPurpose::Bolt11InvoicePayment::payment_secret`] for further details.
 +              payment_secret: PaymentSecret,
 +              /// The context of the payment such as information about the corresponding [`Refund`].
 +              ///
 +              /// [`Refund`]: crate::offers::refund::Refund
 +              payment_context: Bolt12RefundContext,
 +      },
        /// Because this is a spontaneous payment, the payer generated their own preimage rather than us
        /// (the payee) providing a preimage.
        SpontaneousPayment(PaymentPreimage),
@@@ -123,67 -79,17 +123,67 @@@ impl PaymentPurpose 
        /// Returns the preimage for this payment, if it is known.
        pub fn preimage(&self) -> Option<PaymentPreimage> {
                match self {
 -                      PaymentPurpose::InvoicePayment { payment_preimage, .. } => *payment_preimage,
 +                      PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => *payment_preimage,
 +                      PaymentPurpose::Bolt12OfferPayment { payment_preimage, .. } => *payment_preimage,
 +                      PaymentPurpose::Bolt12RefundPayment { payment_preimage, .. } => *payment_preimage,
                        PaymentPurpose::SpontaneousPayment(preimage) => Some(*preimage),
                }
        }
 +
 +      pub(crate) fn is_keysend(&self) -> bool {
 +              match self {
 +                      PaymentPurpose::Bolt11InvoicePayment { .. } => false,
 +                      PaymentPurpose::Bolt12OfferPayment { .. } => false,
 +                      PaymentPurpose::Bolt12RefundPayment { .. } => false,
 +                      PaymentPurpose::SpontaneousPayment(..) => true,
 +              }
 +      }
 +
 +      pub(crate) fn from_parts(
 +              payment_preimage: Option<PaymentPreimage>, payment_secret: PaymentSecret,
 +              payment_context: Option<PaymentContext>,
 +      ) -> Self {
 +              match payment_context {
 +                      Some(PaymentContext::Unknown(_)) | None => {
 +                              PaymentPurpose::Bolt11InvoicePayment {
 +                                      payment_preimage,
 +                                      payment_secret,
 +                              }
 +                      },
 +                      Some(PaymentContext::Bolt12Offer(context)) => {
 +                              PaymentPurpose::Bolt12OfferPayment {
 +                                      payment_preimage,
 +                                      payment_secret,
 +                                      payment_context: context,
 +                              }
 +                      },
 +                      Some(PaymentContext::Bolt12Refund(context)) => {
 +                              PaymentPurpose::Bolt12RefundPayment {
 +                                      payment_preimage,
 +                                      payment_secret,
 +                                      payment_context: context,
 +                              }
 +                      },
 +              }
 +      }
  }
  
  impl_writeable_tlv_based_enum!(PaymentPurpose,
 -      (0, InvoicePayment) => {
 +      (0, Bolt11InvoicePayment) => {
                (0, payment_preimage, option),
                (2, payment_secret, required),
 -      };
 +      },
 +      (4, Bolt12OfferPayment) => {
 +              (0, payment_preimage, option),
 +              (2, payment_secret, required),
 +              (4, payment_context, required),
 +      },
 +      (6, Bolt12RefundPayment) => {
 +              (0, payment_preimage, option),
 +              (2, payment_secret, required),
 +              (4, payment_context, required),
 +      },
 +      ;
        (2, SpontaneousPayment)
  );
  
@@@ -886,9 -792,15 +886,15 @@@ pub enum Event 
        },
        /// Used to indicate that an output which you should know how to spend was confirmed on chain
        /// and is now spendable.
-       /// Such an output will *not* ever be spent by rust-lightning, and are not at risk of your
+       ///
+       /// Such an output will *never* be spent directly by LDK, and are not at risk of your
        /// counterparty spending them due to some kind of timeout. Thus, you need to store them
        /// somewhere and spend them when you create on-chain transactions.
+       ///
+       /// You may hand them to the [`OutputSweeper`] utility which will store and (re-)generate spending
+       /// transactions for you.
+       ///
+       /// [`OutputSweeper`]: crate::util::sweep::OutputSweeper
        SpendableOutputs {
                /// The outputs which you should store as spendable by you.
                outputs: Vec<SpendableOutputDescriptor>,
@@@ -1152,27 -1064,10 +1158,27 @@@ impl Writeable for Event 
                                1u8.write(writer)?;
                                let mut payment_secret = None;
                                let payment_preimage;
 +                              let mut payment_context = None;
                                match &purpose {
 -                                      PaymentPurpose::InvoicePayment { payment_preimage: preimage, payment_secret: secret } => {
 +                                      PaymentPurpose::Bolt11InvoicePayment {
 +                                              payment_preimage: preimage, payment_secret: secret
 +                                      } => {
 +                                              payment_secret = Some(secret);
 +                                              payment_preimage = *preimage;
 +                                      },
 +                                      PaymentPurpose::Bolt12OfferPayment {
 +                                              payment_preimage: preimage, payment_secret: secret, payment_context: context
 +                                      } => {
 +                                              payment_secret = Some(secret);
 +                                              payment_preimage = *preimage;
 +                                              payment_context = Some(PaymentContextRef::Bolt12Offer(context));
 +                                      },
 +                                      PaymentPurpose::Bolt12RefundPayment {
 +                                              payment_preimage: preimage, payment_secret: secret, payment_context: context
 +                                      } => {
                                                payment_secret = Some(secret);
                                                payment_preimage = *preimage;
 +                                              payment_context = Some(PaymentContextRef::Bolt12Refund(context));
                                        },
                                        PaymentPurpose::SpontaneousPayment(preimage) => {
                                                payment_preimage = Some(*preimage);
                                        (8, payment_preimage, option),
                                        (9, onion_fields, option),
                                        (10, skimmed_fee_opt, option),
 +                                      (11, payment_context, option),
                                });
                        },
                        &Event::PaymentSent { ref payment_id, ref payment_preimage, ref payment_hash, ref fee_paid_msat } => {
@@@ -1423,7 -1317,6 +1429,7 @@@ impl MaybeReadable for Event 
                                        let mut claim_deadline = None;
                                        let mut via_user_channel_id = None;
                                        let mut onion_fields = None;
 +                                      let mut payment_context = None;
                                        read_tlv_fields!(reader, {
                                                (0, payment_hash, required),
                                                (1, receiver_node_id, option),
                                                (8, payment_preimage, option),
                                                (9, onion_fields, option),
                                                (10, counterparty_skimmed_fee_msat_opt, option),
 +                                              (11, payment_context, option),
                                        });
                                        let purpose = match payment_secret {
 -                                              Some(secret) => PaymentPurpose::InvoicePayment {
 -                                                      payment_preimage,
 -                                                      payment_secret: secret
 -                                              },
 +                                              Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context),
                                                None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
                                                None => return Err(msgs::DecodeError::InvalidValue),
                                        };
index e271daccfb43b6004e042339e2cfa937837c88c9,17616c5d82192806fbfb4dbdc703b62a4e86a083..465d6288d9d3e764662edb2276d7bb5bef477cb3
@@@ -17,7 -17,7 +17,7 @@@ use crate::chain::chaininterface::Lower
  use crate::chain::channelmonitor;
  use crate::chain::channelmonitor::{CLOSED_CHANNEL_UPDATE_ID, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
  use crate::chain::transaction::OutPoint;
- use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, SignerProvider};
+ use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider};
  use crate::events::{Event, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
  use crate::ln::{ChannelId, PaymentPreimage, PaymentSecret, PaymentHash};
  use crate::ln::channel::{commitment_tx_base_weight, COMMITMENT_TX_WEIGHT_PER_HTLC, CONCURRENT_INBOUND_HTLC_FEE_BUFFER, FEE_SPIKE_BUFFER_FEE_INCREASE_MULTIPLE, MIN_AFFORDABLE_HTLC_COUNT, get_holder_selected_channel_reserve_satoshis, OutboundV1Channel, InboundV1Channel, COINBASE_MATURITY, ChannelPhase};
@@@ -2039,11 -2039,11 +2039,11 @@@ fn test_channel_reserve_holding_cell_ht
                        assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
                        assert_eq!(via_channel_id, Some(chan_2.2));
                        match &purpose {
 -                              PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
 +                              PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(our_payment_secret_21, *payment_secret);
                                },
 -                              _ => panic!("expected PaymentPurpose::InvoicePayment")
 +                              _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
                        assert_eq!(nodes[2].node.get_our_node_id(), receiver_node_id.unwrap());
                        assert_eq!(via_channel_id, Some(chan_2.2));
                        match &purpose {
 -                              PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
 +                              PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(our_payment_secret_22, *payment_secret);
                                },
 -                              _ => panic!("expected PaymentPurpose::InvoicePayment")
 +                              _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@@ -3954,11 -3954,11 +3954,11 @@@ fn do_test_drop_messages_peer_disconnec
                        assert_eq!(receiver_node_id.unwrap(), nodes[1].node.get_our_node_id());
                        assert_eq!(via_channel_id, Some(channel_id));
                        match &purpose {
 -                              PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
 +                              PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_1, *payment_secret);
                                },
 -                              _ => panic!("expected PaymentPurpose::InvoicePayment")
 +                              _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@@ -4319,11 -4319,11 +4319,11 @@@ fn test_drop_messages_peer_disconnect_d
                Event::PaymentClaimable { ref payment_hash, ref purpose, .. } => {
                        assert_eq!(payment_hash_2, *payment_hash);
                        match &purpose {
 -                              PaymentPurpose::InvoicePayment { payment_preimage, payment_secret, .. } => {
 +                              PaymentPurpose::Bolt11InvoicePayment { payment_preimage, payment_secret, .. } => {
                                        assert!(payment_preimage.is_none());
                                        assert_eq!(payment_secret_2, *payment_secret);
                                },
 -                              _ => panic!("expected PaymentPurpose::InvoicePayment")
 +                              _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@@ -8388,10 -8388,10 +8388,10 @@@ fn test_preimage_storage() 
        match events[0] {
                Event::PaymentClaimable { ref purpose, .. } => {
                        match &purpose {
 -                              PaymentPurpose::InvoicePayment { payment_preimage, .. } => {
 +                              PaymentPurpose::Bolt11InvoicePayment { payment_preimage, .. } => {
                                        claim_payment(&nodes[0], &[&nodes[1]], payment_preimage.unwrap());
                                },
 -                              _ => panic!("expected PaymentPurpose::InvoicePayment")
 +                              _ => panic!("expected PaymentPurpose::Bolt11InvoicePayment")
                        }
                },
                _ => panic!("Unexpected event"),
@@@ -9951,9 -9951,9 +9951,9 @@@ fn do_test_max_dust_htlc_exposure(dust_
        let dust_outbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_outbound_htlc_on_holder_tx_msat;
  
        // Substract 3 sats for multiplier and 2 sats for fixed limit to make sure we are 50% below the dust limit.
-       // This is to make sure we fully use the dust limit. If we don't, we could end up with `dust_ibd_htlc_on_holder_tx` being 1 
+       // This is to make sure we fully use the dust limit. If we don't, we could end up with `dust_ibd_htlc_on_holder_tx` being 1
        // while `max_dust_htlc_exposure_msat` is not equal to `dust_outbound_htlc_on_holder_tx_msat`.
-       let dust_inbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_success_tx_weight(&channel_type_features) / 1000 + open_channel.common_fields.dust_limit_satoshis - if multiplier_dust_limit { 3 } else { 2 }) * 1000; 
+       let dust_inbound_htlc_on_holder_tx_msat: u64 = (dust_buffer_feerate * htlc_success_tx_weight(&channel_type_features) / 1000 + open_channel.common_fields.dust_limit_satoshis - if multiplier_dust_limit { 3 } else { 2 }) * 1000;
        let dust_inbound_htlc_on_holder_tx: u64 = max_dust_htlc_exposure_msat / dust_inbound_htlc_on_holder_tx_msat;
  
        let dust_htlc_on_counterparty_tx: u64 = 4;