From bccdf0d8e73730d99ad42279cdc2a35775c68a85 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 16 May 2024 16:02:42 -0500 Subject: [PATCH 01/10] Use implicit returns from match expressions --- lightning/src/ln/channelmanager.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 80b98b2b6f5..815c0b46003 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -10272,8 +10272,8 @@ where }; match response { - Ok(invoice) => return responder.respond(OffersMessage::Invoice(invoice)), - Err(error) => return responder.respond(OffersMessage::InvoiceError(error.into())), + Ok(invoice) => responder.respond(OffersMessage::Invoice(invoice)), + Err(error) => responder.respond(OffersMessage::InvoiceError(error.into())), } }, OffersMessage::Invoice(invoice) => { @@ -10300,14 +10300,14 @@ where self.logger, "A response was generated, but there is no reply_path specified for sending the response." ); - return ResponseInstruction::NoResponse; + ResponseInstruction::NoResponse } - _ => return ResponseInstruction::NoResponse, + _ => ResponseInstruction::NoResponse, } }, OffersMessage::InvoiceError(invoice_error) => { log_trace!(self.logger, "Received invoice_error: {}", invoice_error); - return ResponseInstruction::NoResponse; + ResponseInstruction::NoResponse }, } } From 1173e06f38bc1f3143883a478341ab4657057a29 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Thu, 16 May 2024 16:14:59 -0500 Subject: [PATCH 02/10] Clean up invoice handling result logic --- lightning/src/ln/channelmanager.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 815c0b46003..9ef63973ddc 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -10277,7 +10277,7 @@ where } }, OffersMessage::Invoice(invoice) => { - let response = invoice + let result = invoice .verify(expanded_key, secp_ctx) .map_err(|()| InvoiceError::from_string("Unrecognized invoice".to_owned())) .and_then(|payment_id| { @@ -10293,16 +10293,15 @@ where } }); - match (responder, response) { - (Some(responder), Err(e)) => responder.respond(OffersMessage::InvoiceError(e)), - (None, Err(_)) => { - log_trace!( - self.logger, - "A response was generated, but there is no reply_path specified for sending the response." - ); - ResponseInstruction::NoResponse - } - _ => ResponseInstruction::NoResponse, + match result { + Ok(()) => ResponseInstruction::NoResponse, + Err(e) => match responder { + Some(responder) => responder.respond(OffersMessage::InvoiceError(e)), + None => { + log_trace!(self.logger, "No reply path for sending invoice error: {:?}", e); + ResponseInstruction::NoResponse + }, + }, } }, OffersMessage::InvoiceError(invoice_error) => { From 3edbe76317a2a262d866bffe9ddd259633376739 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 21 May 2024 13:49:38 -0500 Subject: [PATCH 03/10] Re-order imports in events module --- lightning/src/events/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index f054a50ceb8..bfe21991590 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -19,18 +19,18 @@ pub mod bump_transaction; pub use bump_transaction::BumpTransactionEvent; use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef}; -use crate::sign::SpendableOutputDescriptor; +use crate::chain::transaction; use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields}; use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS; use crate::ln::features::ChannelTypeFeatures; use crate::ln::msgs; use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret}; -use crate::chain::transaction; use crate::routing::gossip::NetworkUpdate; +use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters}; +use crate::sign::SpendableOutputDescriptor; use crate::util::errors::APIError; use crate::util::ser::{BigSize, FixedLengthReader, Writeable, Writer, MaybeReadable, Readable, RequiredWrapper, UpgradableRequired, WithoutLength}; use crate::util::string::UntrustedString; -use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters}; use bitcoin::{Transaction, OutPoint}; use bitcoin::blockdata::locktime::absolute::LockTime; From bfdfb9da2762fdc71bc88c889c34d9e70ae27c36 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 21 May 2024 14:28:56 -0500 Subject: [PATCH 04/10] Implement Readable for Bolt12Invoice A future InvoiceReceived event will include a Bolt12Invoice. Since Event implements Readable, so must Bolt12Invoice. --- lightning/src/offers/invoice.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lightning/src/offers/invoice.rs b/lightning/src/offers/invoice.rs index 5482e6f83ea..0d5d2caaa7e 100644 --- a/lightning/src/offers/invoice.rs +++ b/lightning/src/offers/invoice.rs @@ -124,7 +124,7 @@ use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage} use crate::offers::payer::{PAYER_METADATA_TYPE, PayerTlvStream, PayerTlvStreamRef}; use crate::offers::refund::{IV_BYTES as REFUND_IV_BYTES, Refund, RefundContents}; use crate::offers::signer; -use crate::util::ser::{HighZeroBytesDroppedBigSize, Iterable, SeekReadable, WithoutLength, Writeable, Writer}; +use crate::util::ser::{HighZeroBytesDroppedBigSize, Iterable, Readable, SeekReadable, WithoutLength, Writeable, Writer}; use crate::util::string::PrintableString; #[allow(unused_imports)] @@ -1205,6 +1205,13 @@ impl Writeable for Bolt12Invoice { } } +impl Readable for Bolt12Invoice { + fn read(reader: &mut R) -> Result { + let bytes: WithoutLength> = Readable::read(reader)?; + Self::try_from(bytes.0).map_err(|_| DecodeError::InvalidValue) + } +} + impl Writeable for InvoiceContents { fn write(&self, writer: &mut W) -> Result<(), io::Error> { self.as_tlv_stream().write(writer) From 4c297c9a3ae72b4b2d4a041e80ad1608b58f4415 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 21 May 2024 14:32:40 -0500 Subject: [PATCH 05/10] Implement Readable for Responder A future InvoiceReceived event will include a Responder. Since Event implements Readable, so must Responder. --- lightning/src/onion_message/messenger.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index ee49d00e99d..0fb72c52d27 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -325,12 +325,18 @@ impl OnionMessageRecipient { /// The `Responder` struct creates an appropriate [`ResponseInstruction`] /// for responding to a message. +#[derive(Clone, Debug, Eq, PartialEq)] pub struct Responder { /// The path along which a response can be sent. reply_path: BlindedPath, path_id: Option<[u8; 32]> } +impl_writeable_tlv_based!(Responder, { + (0, reply_path, required), + (2, path_id, option), +}); + impl Responder { /// Creates a new [`Responder`] instance with the provided reply path. pub(super) fn new(reply_path: BlindedPath, path_id: Option<[u8; 32]>) -> Self { From 4666c33c0f9918ec054ac9befbafe2c492551804 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 21 May 2024 14:33:56 -0500 Subject: [PATCH 06/10] Add an InvoiceReceived event Some users may want to handle a Bolt12Invoice asynchronously, either in a different process or by first performing additional verification before paying the invoice. Add an InvoiceReceived event to facilitate this. --- lightning/src/events/mod.rs | 44 ++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index bfe21991590..d6d2da936e4 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -25,6 +25,8 @@ use crate::ln::channel::FUNDING_CONF_DEADLINE_BLOCKS; use crate::ln::features::ChannelTypeFeatures; use crate::ln::msgs; use crate::ln::types::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret}; +use crate::offers::invoice::Bolt12Invoice; +use crate::onion_message::messenger::Responder; use crate::routing::gossip::NetworkUpdate; use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters}; use crate::sign::SpendableOutputDescriptor; @@ -715,6 +717,23 @@ pub enum Event { /// The `payment_id` to have been associated with payment for the requested invoice. payment_id: PaymentId, }, + /// Indicates a [`Bolt12Invoice`] in response to an [`InvoiceRequest`] or a [`Refund`] was + /// received. + /// + /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest + /// [`Refund`]: crate::offers::refund::Refund + InvoiceReceived { + /// The `payment_id` associated with payment for the invoice. + payment_id: PaymentId, + /// The invoice to pay. + invoice: Bolt12Invoice, + /// A responder for replying with an [`InvoiceError`] if needed. + /// + /// `None` if the invoice wasn't sent with a reply path. + /// + /// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError + responder: Option, + }, /// Indicates an outbound payment we made succeeded (i.e. it made it all the way to its target /// and we got back the payment preimage for it). /// @@ -1471,7 +1490,15 @@ impl Writeable for Event { write_tlv_fields!(writer, { (0, peer_node_id, required), }); - } + }, + &Event::InvoiceReceived { ref payment_id, ref invoice, ref responder } => { + 41u8.write(writer)?; + write_tlv_fields!(writer, { + (0, payment_id, required), + (2, invoice, required), + (4, responder, option), + }) + }, // Note that, going forward, all new events must only write data inside of // `write_tlv_fields`. Versions 0.0.101+ will ignore odd-numbered events that write // data via `write_tlv_fields`. @@ -1908,6 +1935,21 @@ impl MaybeReadable for Event { }; f() }, + 41u8 => { + let mut f = || { + _init_and_read_len_prefixed_tlv_fields!(reader, { + (0, payment_id, required), + (2, invoice, required), + (4, responder, option), + }); + Ok(Some(Event::InvoiceReceived { + payment_id: payment_id.0.unwrap(), + invoice: invoice.0.unwrap(), + responder, + })) + }; + f() + }, // Versions prior to 0.0.100 did not ignore odd types, instead returning InvalidValue. // Version 0.0.100 failed to properly ignore odd types, possibly resulting in corrupt // reads. From a9dcfaf952584ed835d733cb4688d5f96e86349d Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 21 May 2024 16:58:10 -0500 Subject: [PATCH 07/10] Add UserConfig::manually_handle_bolt12_invoices BOLT12 invoices are automatically paid once they have been verified. Users may want to manually pay them by first performing additional checks. Add a manually_handle_bolt12_invoices configuration option that when set generates an Event::InvoiceReceived instead of paying the invoice. --- fuzz/src/full_stack.rs | 4 ++-- lightning/src/ln/channelmanager.rs | 14 +++++++++----- lightning/src/util/config.rs | 12 ++++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index 4591888e3f0..10328cdce81 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -870,7 +870,7 @@ mod tests { // our network key ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test); // config - ext_from_hex("0000000000900000000000000000640001000000000001ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000", &mut test); + ext_from_hex("0000000000900000000000000000640001000000000001ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff000100000000", &mut test); // new outbound connection with id 0 ext_from_hex("00", &mut test); @@ -1383,7 +1383,7 @@ mod tests { // our network key ext_from_hex("0100000000000000000000000000000000000000000000000000000000000000", &mut test); // config - ext_from_hex("0000000000900000000000000000640001000000000001ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff0001000000", &mut test); + ext_from_hex("0000000000900000000000000000640001000000000001ffff0000000000000000ffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff000000ffffffff00ffff1a000400010000020400000000040200000a08ffffffffffffffff000100000000", &mut test); // new outbound connection with id 0 ext_from_hex("00", &mut test); diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 9ef63973ddc..fbff775d361 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -10277,13 +10277,15 @@ where } }, OffersMessage::Invoice(invoice) => { - let result = invoice - .verify(expanded_key, secp_ctx) - .map_err(|()| InvoiceError::from_string("Unrecognized invoice".to_owned())) - .and_then(|payment_id| { + let result = match invoice.verify(expanded_key, secp_ctx) { + Ok(payment_id) => { let features = self.bolt12_invoice_features(); if invoice.invoice_features().requires_unknown_bits_from(&features) { Err(InvoiceError::from(Bolt12SemanticError::UnknownRequiredFeatures)) + } else if self.default_configuration.manually_handle_bolt12_invoices { + let event = Event::InvoiceReceived { payment_id, invoice, responder }; + self.pending_events.lock().unwrap().push_back((event, None)); + return ResponseInstruction::NoResponse; } else { self.send_payment_for_bolt12_invoice(&invoice, payment_id) .map_err(|e| { @@ -10291,7 +10293,9 @@ where InvoiceError::from_string(format!("{:?}", e)) }) } - }); + }, + Err(()) => Err(InvoiceError::from_string("Unrecognized invoice".to_owned())), + }; match result { Ok(()) => ResponseInstruction::NoResponse, diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index 2473eea2627..3520e219c45 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -847,6 +847,16 @@ pub struct UserConfig { /// /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager pub accept_mpp_keysend: bool, + /// If this is set to true, the user needs to manually pay [`Bolt12Invoice`]s when received. + /// + /// When set to true, [`Event::InvoiceReceived`] will be generated for each received + /// [`Bolt12Invoice`] instead of being automatically paid after verification. + /// + /// Default value: false. + /// + /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice + /// [`Event::InvoiceReceived`]: crate::events::Event::InvoiceReceived + pub manually_handle_bolt12_invoices: bool, } impl Default for UserConfig { @@ -860,6 +870,7 @@ impl Default for UserConfig { manually_accept_inbound_channels: false, accept_intercept_htlcs: false, accept_mpp_keysend: false, + manually_handle_bolt12_invoices: false, } } } @@ -879,6 +890,7 @@ impl Readable for UserConfig { manually_accept_inbound_channels: Readable::read(reader)?, accept_intercept_htlcs: Readable::read(reader)?, accept_mpp_keysend: Readable::read(reader)?, + manually_handle_bolt12_invoices: Readable::read(reader)?, }) } } From 232959cd27c3ef8ab84d857cbee434a2a1f8dd10 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 12 Jun 2024 11:51:23 -0500 Subject: [PATCH 08/10] Reformat and tick config docs --- lightning/src/util/config.rs | 143 +++++++++++++++++++---------------- 1 file changed, 76 insertions(+), 67 deletions(-) diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index 3520e219c45..d2e038b833d 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -18,19 +18,19 @@ use crate::util::ser::Readable; /// Configuration we set when applicable. /// -/// Default::default() provides sane defaults. +/// `Default::default()` provides sane defaults. #[derive(Copy, Clone, Debug)] pub struct ChannelHandshakeConfig { /// Confirmations we will wait for before considering the channel locked in. - /// Applied only for inbound channels (see ChannelHandshakeLimits::max_minimum_depth for the + /// Applied only for inbound channels (see [`ChannelHandshakeLimits::max_minimum_depth`] for the /// equivalent limit applied to outbound channels). /// - /// A lower-bound of 1 is applied, requiring all channels to have a confirmed commitment + /// A lower-bound of `1` is applied, requiring all channels to have a confirmed commitment /// transaction before operation. If you wish to accept channels with zero confirmations, see /// [`UserConfig::manually_accept_inbound_channels`] and /// [`ChannelManager::accept_inbound_channel_from_trusted_peer_0conf`]. /// - /// Default value: 6. + /// Default value: `6` /// /// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel /// [`ChannelManager::accept_inbound_channel_from_trusted_peer_0conf`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel_from_trusted_peer_0conf @@ -48,16 +48,16 @@ pub struct ChannelHandshakeConfig { /// case of an honest unilateral channel close, which implicitly decrease the economic value of /// our channel. /// - /// Default value: [`BREAKDOWN_TIMEOUT`], we enforce it as a minimum at channel opening so you - /// can tweak config to ask for more security, not less. + /// Default value: [`BREAKDOWN_TIMEOUT`] (We enforce it as a minimum at channel opening so you + /// can tweak config to ask for more security, not less.) pub our_to_self_delay: u16, /// Set to the smallest value HTLC we will accept to process. /// /// This value is sent to our counterparty on channel-open and we close the channel any time /// our counterparty misbehaves by sending us an HTLC with a value smaller than this. /// - /// Default value: 1. If the value is less than 1, it is ignored and set to 1, as is required - /// by the protocol. + /// Default value: `1` (If the value is less than `1`, it is ignored and set to `1`, as is + /// required by the protocol. pub our_htlc_minimum_msat: u64, /// Sets the percentage of the channel value we will cap the total value of outstanding inbound /// HTLCs to. @@ -66,7 +66,7 @@ pub struct ChannelHandshakeConfig { /// channel value in whole percentages. /// /// Note that: - /// * If configured to another value than the default value 10, any new channels created with + /// * If configured to another value than the default value `10`, any new channels created with /// the non default value will cause versions of LDK prior to 0.0.104 to refuse to read the /// `ChannelManager`. /// @@ -79,9 +79,11 @@ pub struct ChannelHandshakeConfig { /// See [`ChannelHandshakeConfig::our_to_self_delay`] and [`ChannelConfig::cltv_expiry_delta`] /// for more information. /// - /// Default value: 10. - /// Minimum value: 1, any values less than 1 will be treated as 1 instead. - /// Maximum value: 100, any values larger than 100 will be treated as 100 instead. + /// Default value: `10` + /// + /// Minimum value: `1` (Any values less will be treated as `1` instead.) + /// + /// Maximum value: `100` (Any values larger will be treated as `100` instead.) pub max_inbound_htlc_value_in_flight_percent_of_channel: u8, /// If set, we attempt to negotiate the `scid_privacy` (referred to as `scid_alias` in the /// BOLTs) option for outbound private channels. This provides better privacy by not including @@ -100,7 +102,7 @@ pub struct ChannelHandshakeConfig { /// [`ChannelHandshakeConfig::announced_channel`] and /// [`ChannelHandshakeLimits::force_announced_channel_preference`] for more. /// - /// Default value: false. This value is likely to change to true in the future. + /// Default value: `false` (This value is likely to change to `true` in the future.) /// /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager /// [`DecodeError::InvalidValue`]: crate::ln::msgs::DecodeError::InvalidValue @@ -113,7 +115,7 @@ pub struct ChannelHandshakeConfig { /// As the node which funds a channel picks this value this will only apply for new outbound /// channels unless [`ChannelHandshakeLimits::force_announced_channel_preference`] is set. /// - /// Default value: false. + /// Default value: `false` pub announced_channel: bool, /// When set, we commit to an upfront shutdown_pubkey at channel open. If our counterparty /// supports it, they will then enforce the mutual-close output to us matches what we provided @@ -125,7 +127,7 @@ pub struct ChannelHandshakeConfig { /// /// The upfront key committed is provided from [`SignerProvider::get_shutdown_scriptpubkey`]. /// - /// Default value: true. + /// Default value: `true` /// /// [`SignerProvider::get_shutdown_scriptpubkey`]: crate::sign::SignerProvider::get_shutdown_scriptpubkey pub commit_upfront_shutdown_pubkey: bool, @@ -146,11 +148,15 @@ pub struct ChannelHandshakeConfig { /// Note: Versions of LDK earlier than v0.0.104 will fail to read channels with any channel reserve /// other than the default value. /// - /// Default value: 1% of channel value, i.e., configured as 10,000 millionths. - /// Minimum value: If the calculated proportional value is less than 1000 sats, it will be treated - /// as 1000 sats instead, which is a safe implementation-specific lower bound. - /// Maximum value: 1,000,000, any values larger than 1 Million will be treated as 1 Million (or 100%) - /// instead, although channel negotiations will fail in that case. + /// Default value: `10_000` millionths (i.e., 1% of channel value) + /// + /// Minimum value: If the calculated proportional value is less than `1000` sats, it will be + /// treated as `1000` sats instead, which is a safe implementation-specific lower + /// bound. + /// + /// Maximum value: `1_000_000` (i.e., 100% of channel value. Any values larger than one million + /// will be treated as one million instead, although channel negotiations will + /// fail in that case.) pub their_channel_reserve_proportional_millionths: u32, /// If set, we attempt to negotiate the `anchors_zero_fee_htlc_tx`option for all future /// channels. This feature requires having a reserve of onchain funds readily available to bump @@ -174,7 +180,7 @@ pub struct ChannelHandshakeConfig { /// vulnerability after its deployment. For more context, see the [`SIGHASH_SINGLE + update_fee /// Considered Harmful`] mailing list post. /// - /// Default value: false. This value is likely to change to true in the future. + /// Default value: `false` (This value is likely to change to `true` in the future.) /// /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager /// [`ChannelManager::accept_inbound_channel`]: crate::ln::channelmanager::ChannelManager::accept_inbound_channel @@ -190,9 +196,10 @@ pub struct ChannelHandshakeConfig { /// Note: Versions of LDK earlier than v0.0.115 will fail to read channels with a configuration /// other than the default value. /// - /// Default value: 50 - /// Maximum value: 483, any values larger will be treated as 483. - /// This is the BOLT #2 spec limit on `max_accepted_htlcs`. + /// Default value: `50` + /// + /// Maximum value: `483` (Any values larger will be treated as `483`. This is the BOLT #2 spec + /// limit on `max_accepted_htlcs`.) pub our_max_accepted_htlcs: u16, } @@ -238,7 +245,7 @@ impl Readable for ChannelHandshakeConfig { /// /// These limits are only applied to our counterparty's limits, not our own. /// -/// Use 0/`::max_value()` as appropriate to skip checking. +/// Use `0` or `::max_value()` as appropriate to skip checking. /// /// Provides sane defaults for most configurations. /// @@ -250,40 +257,40 @@ pub struct ChannelHandshakeLimits { /// Minimum allowed satoshis when a channel is funded. This is supplied by the sender and so /// only applies to inbound channels. /// - /// Default value: 0. + /// Default value: `0` pub min_funding_satoshis: u64, /// Maximum allowed satoshis when a channel is funded. This is supplied by the sender and so /// only applies to inbound channels. /// - /// Default value: 2^24 - 1. + /// Default value: `2^24 - 1` pub max_funding_satoshis: u64, /// The remote node sets a limit on the minimum size of HTLCs we can send to them. This allows /// you to limit the maximum minimum-size they can require. /// - /// Default value: u64::max_value. + /// Default value: `u64::max_value` pub max_htlc_minimum_msat: u64, /// The remote node sets a limit on the maximum value of pending HTLCs to them at any given /// time to limit their funds exposure to HTLCs. This allows you to set a minimum such value. /// - /// Default value: 0. + /// Default value: `0` pub min_max_htlc_value_in_flight_msat: u64, /// The remote node will require we keep a certain amount in direct payment to ourselves at all /// time, ensuring that we are able to be punished if we broadcast an old state. This allows to /// you limit the amount which we will have to keep to ourselves (and cannot use for HTLCs). /// - /// Default value: u64::max_value. + /// Default value: `u64::max_value`. pub max_channel_reserve_satoshis: u64, /// The remote node sets a limit on the maximum number of pending HTLCs to them at any given /// time. This allows you to set a minimum such value. /// - /// Default value: 0. + /// Default value: `0` pub min_max_accepted_htlcs: u16, /// Before a channel is usable the funding transaction will need to be confirmed by at least a /// certain number of blocks, specified by the node which is not the funder (as the funder can /// assume they aren't going to double-spend themselves). /// This config allows you to set a limit on the maximum amount of time to wait. /// - /// Default value: 144, or roughly one day and only applies to outbound channels. + /// Default value: `144`, or roughly one day and only applies to outbound channels pub max_minimum_depth: u32, /// Whether we implicitly trust funding transactions generated by us for our own outbound /// channels to not be double-spent. @@ -296,12 +303,12 @@ pub struct ChannelHandshakeLimits { /// You may wish to un-set this if you allow the user to (or do in an automated fashion) /// double-spend the funding transaction to RBF with an alternative channel open. /// - /// This only applies if our counterparty set their confirmations-required value to 0, and we - /// always trust our own funding transaction at 1 confirmation irrespective of this value. + /// This only applies if our counterparty set their confirmations-required value to `0`, and we + /// always trust our own funding transaction at `1` confirmation irrespective of this value. /// Thus, this effectively acts as a `min_minimum_depth`, with the only possible values being - /// `true` (0) and `false` (1). + /// `true` (`0`) and `false` (`1`). /// - /// Default value: true + /// Default value: `true` pub trust_own_funding_0conf: bool, /// Set to force an incoming channel to match our announced channel preference in /// [`ChannelHandshakeConfig::announced_channel`]. @@ -310,14 +317,14 @@ pub struct ChannelHandshakeLimits { /// [`ChannelHandshakeConfig::announced_channel`] set to false, ensuring that no announced (aka public) /// channels will ever be opened. /// - /// Default value: true. + /// Default value: `true` pub force_announced_channel_preference: bool, /// Set to the amount of time we're willing to wait to claim money back to us. /// /// Not checking this value would be a security issue, as our peer would be able to set it to /// max relative lock-time (a year) and we would "lose" money as it would be locked for a long time. /// - /// Default value: 2016, which we also enforce as a maximum value so you can tweak config to + /// Default value: `2016`, which we also enforce as a maximum value so you can tweak config to /// reduce the loss of having useless locked funds (if your peer accepts) pub their_to_self_delay: u16 } @@ -417,7 +424,7 @@ pub struct ChannelConfig { /// This may be allowed to change at runtime in a later update, however doing so must result in /// update messages sent to notify all nodes of our updated relay fee. /// - /// Default value: 0. + /// Default value: `0` pub forwarding_fee_proportional_millionths: u32, /// Amount (in milli-satoshi) charged for payments forwarded outbound over the channel, in /// excess of [`forwarding_fee_proportional_millionths`]. @@ -428,7 +435,7 @@ pub struct ChannelConfig { /// as of July 2021. Adjusting it upwards or downwards may change whether nodes route through /// this node. /// - /// Default value: 1000. + /// Default value: `1000` /// /// [`forwarding_fee_proportional_millionths`]: ChannelConfig::forwarding_fee_proportional_millionths pub forwarding_fee_base_msat: u32, @@ -446,9 +453,10 @@ pub struct ChannelConfig { /// enough time to broadcast and confirm a transaction, possibly with time in between to RBF /// the spending transaction). /// - /// Default value: 72 (12 hours at an average of 6 blocks/hour). - /// Minimum value: [`MIN_CLTV_EXPIRY_DELTA`], any values less than this will be treated as - /// [`MIN_CLTV_EXPIRY_DELTA`] instead. + /// Default value: `72` (12 hours at an average of 6 blocks/hour) + /// + /// Minimum value: [`MIN_CLTV_EXPIRY_DELTA`] (Any values less than this will be treated as + /// [`MIN_CLTV_EXPIRY_DELTA`] instead.) /// /// [`MIN_CLTV_EXPIRY_DELTA`]: crate::ln::channelmanager::MIN_CLTV_EXPIRY_DELTA pub cltv_expiry_delta: u16, @@ -508,7 +516,7 @@ pub struct ChannelConfig { /// while we use [`ConfirmationTarget::OnChainSweep`] (which should be relatively high) and /// feerate disagreement force-closures should only occur when theirs is higher than ours. /// - /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of 10_000. + /// Default value: [`MaxDustHTLCExposure::FeeRateMultiplier`] with a multiplier of `10_000` /// /// [`ConfirmationTarget::OnChainSweep`]: crate::chain::chaininterface::ConfirmationTarget::OnChainSweep pub max_dust_htlc_exposure: MaxDustHTLCExposure, @@ -530,7 +538,7 @@ pub struct ChannelConfig { /// [`ChannelCloseMinimum`] fee estimate, but allow our counterparty to pay as much fee as they like. /// Thus, this value is ignored when we are not the funder. /// - /// Default value: 1000 satoshis. + /// Default value: `1000` /// /// [`NonAnchorChannelFee`]: crate::chain::chaininterface::ConfirmationTarget::NonAnchorChannelFee /// [`ChannelCloseMinimum`]: crate::chain::chaininterface::ConfirmationTarget::ChannelCloseMinimum @@ -558,7 +566,7 @@ pub struct ChannelConfig { /// Switching this config flag on may break compatibility with versions of LDK prior to 0.0.116. /// Unsetting this flag between restarts may lead to payment receive failures. /// - /// Default value: false. + /// Default value: `false` /// /// [intercept scids]: crate::ln::channelmanager::ChannelManager::get_intercept_scid /// [`forward_intercepted_htlc`]: crate::ln::channelmanager::ChannelManager::forward_intercepted_htlc @@ -781,8 +789,8 @@ impl crate::util::ser::Readable for LegacyChannelConfig { /// Top-level config which holds ChannelHandshakeLimits and ChannelConfig. /// -/// Default::default() provides sane defaults for most configurations -/// (but currently with 0 relay fees!) +/// `Default::default()` provides sane defaults for most configurations +/// (but currently with zero relay fees!) #[derive(Copy, Clone, Debug)] pub struct UserConfig { /// Channel handshake config that we propose to our counterparty. @@ -791,13 +799,13 @@ pub struct UserConfig { pub channel_handshake_limits: ChannelHandshakeLimits, /// Channel config which affects behavior during channel lifetime. pub channel_config: ChannelConfig, - /// If this is set to false, we will reject any HTLCs which were to be forwarded over private + /// If this is set to `false`, we will reject any HTLCs which were to be forwarded over private /// channels. This prevents us from taking on HTLC-forwarding risk when we intend to run as a /// node which is not online reliably. /// /// For nodes which are not online reliably, you should set all channels to *not* be announced /// (using [`ChannelHandshakeConfig::announced_channel`] and - /// [`ChannelHandshakeLimits::force_announced_channel_preference`]) and set this to false to + /// [`ChannelHandshakeLimits::force_announced_channel_preference`]) and set this to `false` to /// ensure you are not exposed to any forwarding risk. /// /// Note that because you cannot change a channel's announced state after creation, there is no @@ -806,53 +814,54 @@ pub struct UserConfig { /// all your channels and open new ones. For privacy, you should also change your node_id /// (swapping all private and public key material for new ones) at that time. /// - /// Default value: false. + /// Default value: `false` pub accept_forwards_to_priv_channels: bool, - /// If this is set to false, we do not accept inbound requests to open a new channel. - /// Default value: true. + /// If this is set to `false`, we do not accept inbound requests to open a new channel. + /// + /// Default value: `true` pub accept_inbound_channels: bool, - /// If this is set to true, the user needs to manually accept inbound requests to open a new + /// If this is set to `true`, the user needs to manually accept inbound requests to open a new /// channel. /// - /// When set to true, [`Event::OpenChannelRequest`] will be triggered once a request to open a + /// When set to `true`, [`Event::OpenChannelRequest`] will be triggered once a request to open a /// new inbound channel is received through a [`msgs::OpenChannel`] message. In that case, a /// [`msgs::AcceptChannel`] message will not be sent back to the counterparty node unless the /// user explicitly chooses to accept the request. /// - /// Default value: false. + /// Default value: `false` /// /// [`Event::OpenChannelRequest`]: crate::events::Event::OpenChannelRequest /// [`msgs::OpenChannel`]: crate::ln::msgs::OpenChannel /// [`msgs::AcceptChannel`]: crate::ln::msgs::AcceptChannel pub manually_accept_inbound_channels: bool, - /// If this is set to true, LDK will intercept HTLCs that are attempting to be forwarded over + /// If this is set to `true`, LDK will intercept HTLCs that are attempting to be forwarded over /// fake short channel ids generated via [`ChannelManager::get_intercept_scid`]. Upon HTLC /// intercept, LDK will generate an [`Event::HTLCIntercepted`] which MUST be handled by the user. /// - /// Setting this to true may break backwards compatibility with LDK versions < 0.0.113. + /// Setting this to `true` may break backwards compatibility with LDK versions < 0.0.113. /// - /// Default value: false. + /// Default value: `false` /// /// [`ChannelManager::get_intercept_scid`]: crate::ln::channelmanager::ChannelManager::get_intercept_scid /// [`Event::HTLCIntercepted`]: crate::events::Event::HTLCIntercepted pub accept_intercept_htlcs: bool, - /// If this is set to false, when receiving a keysend payment we'll fail it if it has multiple - /// parts. If this is set to true, we'll accept the payment. + /// If this is set to `false`, when receiving a keysend payment we'll fail it if it has multiple + /// parts. If this is set to `true`, we'll accept the payment. /// - /// Setting this to true will break backwards compatibility upon downgrading to an LDK - /// version < 0.0.116 while receiving an MPP keysend. If we have already received an MPP + /// Setting this to `true` will break backwards compatibility upon downgrading to an LDK + /// version prior to 0.0.116 while receiving an MPP keysend. If we have already received an MPP /// keysend, downgrading will cause us to fail to deserialize [`ChannelManager`]. /// - /// Default value: false. + /// Default value: `false` /// /// [`ChannelManager`]: crate::ln::channelmanager::ChannelManager pub accept_mpp_keysend: bool, - /// If this is set to true, the user needs to manually pay [`Bolt12Invoice`]s when received. + /// If this is set to `true`, the user needs to manually pay [`Bolt12Invoice`]s when received. /// - /// When set to true, [`Event::InvoiceReceived`] will be generated for each received + /// When set to `true`, [`Event::InvoiceReceived`] will be generated for each received /// [`Bolt12Invoice`] instead of being automatically paid after verification. /// - /// Default value: false. + /// Default value: `false` /// /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice /// [`Event::InvoiceReceived`]: crate::events::Event::InvoiceReceived From 65efd92c4a931b3b3622c84c6055bc015152fde0 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Tue, 21 May 2024 18:47:32 -0500 Subject: [PATCH 09/10] Expose send_payment_for_bolt12_invoice UserConfig::manually_handle_bolt12_invoices allows deferring payment of BOLT12 invoices by generating Event::InvoiceReceived. Expose ChannelManager::send_payment_for_bolt12_invoice to allow users to pay the Bolt12Invoice included in the event. While the event contains the PaymentId for reference, that parameter is now removed from the method in favor of extracting the PaymentId from the invoice's payer_metadata. --- lightning/src/events/mod.rs | 8 ++++++ lightning/src/ln/channelmanager.rs | 37 +++++++++++++++++++++++++--- lightning/src/ln/outbound_payment.rs | 4 +-- lightning/src/util/config.rs | 6 ++++- 4 files changed, 48 insertions(+), 7 deletions(-) diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index d6d2da936e4..6212cfec5f0 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -720,8 +720,16 @@ pub enum Event { /// Indicates a [`Bolt12Invoice`] in response to an [`InvoiceRequest`] or a [`Refund`] was /// received. /// + /// This event will only be generated if [`UserConfig::manually_handle_bolt12_invoices`] is set. + /// Use [`ChannelManager::send_payment_for_bolt12_invoice`] to pay the invoice or + /// [`ChannelManager::abandon_payment`] to abandon the associated payment. See those docs for + /// further details. + /// /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`Refund`]: crate::offers::refund::Refund + /// [`UserConfig::manually_handle_bolt12_invoices`]: crate::util::config::UserConfig::manually_handle_bolt12_invoices + /// [`ChannelManager::send_payment_for_bolt12_invoice`]: crate::ln::channelmanager::ChannelManager::send_payment_for_bolt12_invoice + /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment InvoiceReceived { /// The `payment_id` associated with payment for the invoice. payment_id: PaymentId, diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index fbff775d361..ba07cd606b8 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -58,7 +58,7 @@ use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING}; use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError}; #[cfg(test)] use crate::ln::outbound_payment; -use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration}; +use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration}; use crate::ln::wire::Encode; use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice}; use crate::offers::invoice_error::InvoiceError; @@ -105,7 +105,7 @@ use core::time::Duration; use core::ops::Deref; // Re-export this for use in the public API. -pub use crate::ln::outbound_payment::{PaymentSendFailure, ProbeSendFailure, Retry, RetryableSendFailure, RecipientOnionFields}; +pub use crate::ln::outbound_payment::{Bolt12PaymentError, PaymentSendFailure, ProbeSendFailure, Retry, RetryableSendFailure, RecipientOnionFields}; use crate::ln::script::ShutdownScript; // We hold various information about HTLC relay in the HTLC objects in Channel itself: @@ -3996,7 +3996,36 @@ where self.pending_outbound_payments.test_set_payment_metadata(payment_id, new_payment_metadata); } - pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> { + /// Pays the [`Bolt12Invoice`] associated with the `payment_id` encoded in its `payer_metadata`. + /// + /// The invoice's `payer_metadata` is used to authenticate that the invoice was indeed requested + /// before attempting a payment. [`Bolt12PaymentError::UnexpectedInvoice`] is returned if this + /// fails or if the encoded `payment_id` is not recognized. The latter may happen once the + /// payment is no longer tracked because the payment was attempted after: + /// - an invoice for the `payment_id` was already paid, + /// - one full [timer tick] has elapsed since initially requesting the invoice when paying an + /// offer, or + /// - the refund corresponding to the invoice has already expired. + /// + /// To retry the payment, request another invoice using a new `payment_id`. + /// + /// Attempting to pay the same invoice twice while the first payment is still pending will + /// result in a [`Bolt12PaymentError::DuplicateInvoice`]. + /// + /// Otherwise, either [`Event::PaymentSent`] or [`Event::PaymentFailed`] are used to indicate + /// whether or not the payment was successful. + /// + /// [timer tick]: Self::timer_tick_occurred + pub fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice) -> Result<(), Bolt12PaymentError> { + let secp_ctx = &self.secp_ctx; + let expanded_key = &self.inbound_payment_key; + match invoice.verify(expanded_key, secp_ctx) { + Ok(payment_id) => self.send_payment_for_verified_bolt12_invoice(invoice, payment_id), + Err(()) => Err(Bolt12PaymentError::UnexpectedInvoice), + } + } + + fn send_payment_for_verified_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> { let best_block_height = self.best_block.read().unwrap().height; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); self.pending_outbound_payments @@ -10287,7 +10316,7 @@ where self.pending_events.lock().unwrap().push_back((event, None)); return ResponseInstruction::NoResponse; } else { - self.send_payment_for_bolt12_invoice(&invoice, payment_id) + self.send_payment_for_verified_bolt12_invoice(&invoice, payment_id) .map_err(|e| { log_trace!(self.logger, "Failed paying invoice: {:?}", e); InvoiceError::from_string(format!("{:?}", e)) diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 2a9e52c83c7..3ae128cb3bb 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -501,9 +501,9 @@ pub enum PaymentSendFailure { }, } -/// An error when attempting to pay a BOLT 12 invoice. +/// An error when attempting to pay a [`Bolt12Invoice`]. #[derive(Clone, Debug, PartialEq, Eq)] -pub(super) enum Bolt12PaymentError { +pub enum Bolt12PaymentError { /// The invoice was not requested. UnexpectedInvoice, /// Payment for an invoice with the corresponding [`PaymentId`] was already initiated. diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index d2e038b833d..4e124c27fd9 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -859,12 +859,16 @@ pub struct UserConfig { /// If this is set to `true`, the user needs to manually pay [`Bolt12Invoice`]s when received. /// /// When set to `true`, [`Event::InvoiceReceived`] will be generated for each received - /// [`Bolt12Invoice`] instead of being automatically paid after verification. + /// [`Bolt12Invoice`] instead of being automatically paid after verification. Use + /// [`ChannelManager::send_payment_for_bolt12_invoice`] to pay the invoice or + /// [`ChannelManager::abandon_payment`] to abandon the associated payment. /// /// Default value: `false` /// /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice /// [`Event::InvoiceReceived`]: crate::events::Event::InvoiceReceived + /// [`ChannelManager::send_payment_for_bolt12_invoice`]: crate::ln::channelmanager::ChannelManager::send_payment_for_bolt12_invoice + /// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment pub manually_handle_bolt12_invoices: bool, } From 97c1d656ee3a84a276aeb956a41c40cbd2c68af9 Mon Sep 17 00:00:00 2001 From: Jeffrey Czyz Date: Wed, 22 May 2024 11:28:31 -0500 Subject: [PATCH 10/10] Add pays_bolt12_invoice_asynchronously test --- lightning/src/ln/offers_tests.rs | 87 +++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index c7fb5f8fd59..eca43afee8a 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -46,9 +46,10 @@ use core::time::Duration; use crate::blinded_path::{BlindedPath, IntroductionNode}; use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext}; use crate::events::{Event, MessageSendEventsProvider, PaymentPurpose}; -use crate::ln::channelmanager::{MAX_SHORT_LIVED_RELATIVE_EXPIRY, PaymentId, RecentPaymentDetails, Retry, self}; +use crate::ln::channelmanager::{Bolt12PaymentError, MAX_SHORT_LIVED_RELATIVE_EXPIRY, PaymentId, RecentPaymentDetails, Retry, self}; use crate::ln::functional_test_utils::*; use crate::ln::msgs::{ChannelMessageHandler, Init, NodeAnnouncement, OnionMessage, OnionMessageHandler, RoutingMessageHandler, SocketAddress, UnsignedGossipMessage, UnsignedNodeAnnouncement}; +use crate::ln::outbound_payment::IDEMPOTENCY_TIMEOUT_TICKS; use crate::offers::invoice::Bolt12Invoice; use crate::offers::invoice_error::InvoiceError; use crate::offers::invoice_request::{InvoiceRequest, InvoiceRequestFields}; @@ -865,6 +866,90 @@ fn pays_for_refund_without_blinded_paths() { expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id); } +/// Checks that a deferred invoice can be paid asynchronously from an Event::InvoiceReceived. +#[test] +fn pays_bolt12_invoice_asynchronously() { + let mut manually_pay_cfg = test_default_channel_config(); + manually_pay_cfg.manually_handle_bolt12_invoices = true; + + let chanmon_cfgs = create_chanmon_cfgs(2); + let node_cfgs = create_node_cfgs(2, &chanmon_cfgs); + let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, Some(manually_pay_cfg)]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + + create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 10_000_000, 1_000_000_000); + + let alice = &nodes[0]; + let alice_id = alice.node.get_our_node_id(); + let bob = &nodes[1]; + let bob_id = bob.node.get_our_node_id(); + + let offer = alice.node + .create_offer_builder(None).unwrap() + .amount_msats(10_000_000) + .build().unwrap(); + + let payment_id = PaymentId([1; 32]); + bob.node.pay_for_offer(&offer, None, None, None, payment_id, Retry::Attempts(0), None).unwrap(); + expect_recent_payment!(bob, RecentPaymentDetails::AwaitingInvoice, payment_id); + + let onion_message = bob.onion_messenger.next_onion_message_for_peer(alice_id).unwrap(); + alice.onion_messenger.handle_onion_message(&bob_id, &onion_message); + + let (invoice_request, _) = extract_invoice_request(alice, &onion_message); + let payment_context = PaymentContext::Bolt12Offer(Bolt12OfferContext { + offer_id: offer.id(), + invoice_request: InvoiceRequestFields { + payer_id: invoice_request.payer_id(), + quantity: None, + payer_note_truncated: None, + }, + }); + + let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap(); + bob.onion_messenger.handle_onion_message(&alice_id, &onion_message); + + let invoice = match get_event!(bob, Event::InvoiceReceived) { + Event::InvoiceReceived { payment_id: actual_payment_id, invoice, .. } => { + assert_eq!(actual_payment_id, payment_id); + invoice + }, + _ => panic!("No Event::InvoiceReceived"), + }; + assert_eq!(invoice.amount_msats(), 10_000_000); + assert_ne!(invoice.signing_pubkey(), alice_id); + assert!(!invoice.payment_paths().is_empty()); + for (_, path) in invoice.payment_paths() { + assert_eq!(path.introduction_node, IntroductionNode::NodeId(alice_id)); + } + + assert!(bob.node.send_payment_for_bolt12_invoice(&invoice).is_ok()); + assert_eq!( + bob.node.send_payment_for_bolt12_invoice(&invoice), + Err(Bolt12PaymentError::DuplicateInvoice), + ); + + route_bolt12_payment(bob, &[alice], &invoice); + expect_recent_payment!(bob, RecentPaymentDetails::Pending, payment_id); + + claim_bolt12_payment(bob, &[alice], payment_context); + expect_recent_payment!(bob, RecentPaymentDetails::Fulfilled, payment_id); + + assert_eq!( + bob.node.send_payment_for_bolt12_invoice(&invoice), + Err(Bolt12PaymentError::DuplicateInvoice), + ); + + for _ in 0..=IDEMPOTENCY_TIMEOUT_TICKS { + bob.node.timer_tick_occurred(); + } + + assert_eq!( + bob.node.send_payment_for_bolt12_invoice(&invoice), + Err(Bolt12PaymentError::UnexpectedInvoice), + ); +} + /// Fails creating an offer when a blinded path cannot be created without exposing the node's id. #[test] fn fails_creating_offer_without_blinded_paths() {