Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authenticate use of offer blinded paths #3139

Merged
merged 29 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
5278d31
Change Nonce visibility to pub
jkczyz Jun 19, 2024
0a5918e
Reorder imports
jkczyz Jun 19, 2024
d7aeaa0
Move Nonce to a separate offers sub-module
jkczyz Jun 19, 2024
219691f
Pass Nonce directly to OfferBuilder
jkczyz Jun 19, 2024
e156415
Add InvoiceRequest::verify_using_recipient_data
jkczyz Jun 20, 2024
c0cae08
Assert and document valid Metadata states
jkczyz Jul 11, 2024
c58a1bb
Clean up MessageContext docs
jkczyz Jul 9, 2024
7904e3c
Wrap docs at 100 characters
jkczyz Jul 9, 2024
f546aad
Expand OffersContext::OutboundPayment docs
jkczyz Jul 9, 2024
1ff8c8d
Fix grammar
jkczyz Jul 18, 2024
a5145e4
Fix OffersContext::Unknown docs
jkczyz Jul 18, 2024
6a54618
Add OffersContext::InvoiceRequest
jkczyz Jul 3, 2024
35b75fd
Authenticate InvoiceRequest using OfferContext
jkczyz Jul 2, 2024
bf42847
Elide metadata from Offer with derived keys
jkczyz Jun 20, 2024
9d46340
Rename InvoiceRequest::verify
jkczyz Jul 19, 2024
f537abd
Add docs to Metadata::without_keys
jkczyz Jul 18, 2024
c2a120e
Authenticate Bolt12Invoice using OfferContext
jkczyz Jul 2, 2024
559daeb
Don't send InvoiceError on failed authentication
jkczyz Jul 3, 2024
bdf3330
Add failure tests for offer message authentication
jkczyz Jul 3, 2024
fd596c3
Pass Nonce directly to InvoiceRequestBuilder
jkczyz Jul 12, 2024
114954c
Pass Nonce directly to RefundBuilder
jkczyz Jul 12, 2024
868fee7
Add Bolt12Invoice::verify_using_payer_data
jkczyz Jul 12, 2024
14634c6
Add nonce to OffersContext::OutboundPayment
jkczyz Jul 12, 2024
2c2f3fe
Authenticate Bolt12Invoice using BlindedPath data
jkczyz Jul 12, 2024
e6ee194
Include OffersContext in Event::InvoiceReceived
jkczyz Jul 15, 2024
4ed37d8
Correct docs
jkczyz Jul 19, 2024
df5d7ea
Elide nonce from payer metadata
jkczyz Jul 15, 2024
718bc47
Rename Bolt12Invoice::verify
jkczyz Jul 19, 2024
825bda0
Add pending changelog for BOLT12 authentication
jkczyz Jul 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 57 additions & 18 deletions lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::io;
use crate::io::Cursor;
use crate::ln::channelmanager::PaymentId;
use crate::ln::onion_utils;
use crate::offers::nonce::Nonce;
use crate::onion_message::packet::ControlTlvs;
use crate::sign::{NodeSigner, Recipient};
use crate::crypto::streams::ChaChaPolyReadAdapter;
Expand Down Expand Up @@ -85,37 +86,71 @@ impl Writeable for ReceiveTlvs {
}
}

/// Represents additional data included by the recipient in a [`BlindedPath`].
/// Additional data included by the recipient in a [`BlindedPath`].
///
/// This data is encrypted by the recipient and remains invisible to anyone else.
/// It is included in the [`BlindedPath`], making it accessible again to the recipient
/// whenever the [`BlindedPath`] is used.
/// The recipient can authenticate the message and utilize it for further processing
/// if needed.
/// This data is encrypted by the recipient and will be given to the corresponding message handler
/// when handling a message sent over the [`BlindedPath`]. The recipient can use this data to
/// authenticate the message or for further processing if needed.
#[derive(Clone, Debug)]
pub enum MessageContext {
/// Represents the data specific to [`OffersMessage`]
/// Context specific to an [`OffersMessage`].
///
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
Offers(OffersContext),
/// Represents custom data received in a Custom Onion Message.
/// Context specific to a [`CustomOnionMessageHandler::CustomMessage`].
///
/// [`CustomOnionMessageHandler::CustomMessage`]: crate::onion_message::messenger::CustomOnionMessageHandler::CustomMessage
Custom(Vec<u8>),
}

/// Contains the data specific to [`OffersMessage`]
/// Contains data specific to an [`OffersMessage`].
///
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum OffersContext {
/// Represents an unknown BOLT12 payment context.
/// This variant is used when a message is sent without
jkczyz marked this conversation as resolved.
Show resolved Hide resolved
/// using a [`BlindedPath`] or over one created prior to
/// LDK version 0.0.124.
/// Represents an unknown BOLT12 message context.
///
/// This variant is used when a message is sent without using a [`BlindedPath`] or over one
/// created prior to LDK version 0.0.124.
Unknown {},
/// Represents an outbound BOLT12 payment context.
/// Context used by a [`BlindedPath`] within an [`Offer`].
///
/// This variant is intended to be received when handling an [`InvoiceRequest`].
///
/// [`Offer`]: crate::offers::offer::Offer
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
InvoiceRequest {
/// A nonce used for authenticating that an [`InvoiceRequest`] is for a valid [`Offer`] and
/// for deriving the offer's signing keys.
///
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Offer`]: crate::offers::offer::Offer
nonce: Nonce,
},
/// Context used by a [`BlindedPath`] within a [`Refund`] or as a reply path for an
/// [`InvoiceRequest`].
///
/// This variant is intended to be received when handling a [`Bolt12Invoice`] or an
/// [`InvoiceError`].
///
/// [`Refund`]: crate::offers::refund::Refund
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
/// [`InvoiceError`]: crate::offers::invoice_error::InvoiceError
OutboundPayment {
/// Payment ID of the outbound BOLT12 payment.
payment_id: PaymentId
/// Payment ID used when creating a [`Refund`] or [`InvoiceRequest`].
///
/// [`Refund`]: crate::offers::refund::Refund
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
payment_id: PaymentId,

/// A nonce used for authenticating that a [`Bolt12Invoice`] is for a valid [`Refund`] or
/// [`InvoiceRequest`] and for deriving their signing keys.
///
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
/// [`Refund`]: crate::offers::refund::Refund
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
nonce: Nonce,
},
}

Expand All @@ -126,8 +161,12 @@ impl_writeable_tlv_based_enum!(MessageContext,

impl_writeable_tlv_based_enum!(OffersContext,
(0, Unknown) => {},
(1, OutboundPayment) => {
(1, InvoiceRequest) => {
(0, nonce, required),
},
(2, OutboundPayment) => {
(0, payment_id, required),
(1, nonce, required),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be 2. Will push a fixup after the next round of reviews.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OffersContext hasn't shipped in a release yet, right? It shouldn't matter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you mean don't worry about using odd numbers until we release? Wasn't sure if we had a convention around that.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't, it doesn't matter much so we often just do odds.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverted

},
);

Expand Down
14 changes: 11 additions & 3 deletions lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod bump_transaction;

pub use bump_transaction::BumpTransactionEvent;

use crate::blinded_path::message::OffersContext;
use crate::blinded_path::payment::{Bolt12OfferContext, Bolt12RefundContext, PaymentContext, PaymentContextRef};
use crate::chain::transaction;
use crate::ln::channelmanager::{InterceptId, PaymentId, RecipientOnionFields};
Expand Down Expand Up @@ -806,6 +807,10 @@ pub enum Event {
payment_id: PaymentId,
/// The invoice to pay.
invoice: Bolt12Invoice,
/// The context of the [`BlindedPath`] used to send the invoice.
///
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
context: OffersContext,
/// A responder for replying with an [`InvoiceError`] if needed.
///
/// `None` if the invoice wasn't sent with a reply path.
Expand Down Expand Up @@ -1648,12 +1653,13 @@ impl Writeable for Event {
(0, peer_node_id, required),
});
},
&Event::InvoiceReceived { ref payment_id, ref invoice, ref responder } => {
&Event::InvoiceReceived { ref payment_id, ref invoice, ref context, ref responder } => {
41u8.write(writer)?;
write_tlv_fields!(writer, {
(0, payment_id, required),
(2, invoice, required),
(4, responder, option),
(4, context, required),
(6, responder, option),
});
},
&Event::FundingTxBroadcastSafe { ref channel_id, ref user_channel_id, ref funding_txo, ref counterparty_node_id, ref former_temporary_channel_id} => {
Expand Down Expand Up @@ -2107,11 +2113,13 @@ impl MaybeReadable for Event {
_init_and_read_len_prefixed_tlv_fields!(reader, {
(0, payment_id, required),
(2, invoice, required),
(4, responder, option),
(4, context, required),
(6, responder, option),
});
Ok(Some(Event::InvoiceReceived {
payment_id: payment_id.0.unwrap(),
invoice: invoice.0.unwrap(),
context: context.0.unwrap(),
responder,
}))
};
Expand Down
125 changes: 84 additions & 41 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ use crate::ln::wire::Encode;
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
use crate::offers::invoice_error::InvoiceError;
use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
use crate::offers::nonce::Nonce;
use crate::offers::offer::{Offer, OfferBuilder};
use crate::offers::parse::Bolt12SemanticError;
use crate::offers::refund::{Refund, RefundBuilder};
Expand Down Expand Up @@ -2254,7 +2255,10 @@ where
event_persist_notifier: Notifier,
needs_persist_flag: AtomicBool,

#[cfg(not(any(test, feature = "_test_utils")))]
pending_offers_messages: Mutex<Vec<PendingOnionMessage<OffersMessage>>>,
#[cfg(any(test, feature = "_test_utils"))]
pub(crate) pending_offers_messages: Mutex<Vec<PendingOnionMessage<OffersMessage>>>,

/// Tracks the message events that are to be broadcasted when we are connected to some peer.
pending_broadcast_messages: Mutex<Vec<MessageSendEvent>>,
Expand Down Expand Up @@ -4199,15 +4203,35 @@ where
/// 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) {
pub fn send_payment_for_bolt12_invoice(
&self, invoice: &Bolt12Invoice, context: &OffersContext,
) -> Result<(), Bolt12PaymentError> {
match self.verify_bolt12_invoice(invoice, context) {
Ok(payment_id) => self.send_payment_for_verified_bolt12_invoice(invoice, payment_id),
Err(()) => Err(Bolt12PaymentError::UnexpectedInvoice),
}
}

fn verify_bolt12_invoice(
&self, invoice: &Bolt12Invoice, context: &OffersContext,
) -> Result<PaymentId, ()> {
let secp_ctx = &self.secp_ctx;
let expanded_key = &self.inbound_payment_key;

match context {
OffersContext::Unknown {} if invoice.is_for_refund_without_paths() => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we planning to also stop using OffersContext::Unknown {} when creating a reply path for an invoice we're sending to a Refund? I thought that change would be in #3192 but don't see it atm. Trying to figure out if this code is going to go away given it only applies to refunds and we seem ok with breaking refunds in this release.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we planning to also stop using OffersContext::Unknown {} when creating a reply path for an invoice we're sending to a Refund? I thought that change would be in #3192 but don't see it atm.

Yeah, looks like I need to update that to use OffersContext::InboundPayment. Thanks for catching that!

Trying to figure out if this code is going to go away given it only applies to refunds and we seem ok with breaking refunds in this release.

This code here will stay the same since the context is from the refund's blinded path -- or OffersContext::Unknown if the refund doesn't have any blinded paths. The case that you are thinking of -- the reply path used when creating an invoice for a refund -- would be seen when handling an InvoiceError.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay. To me it reads like using an Option would capture the state better than having the Unknown variant. To me OffersContext is context we provide back to ourselves in our blinded paths, and Unknown sounds like we got an unknown context in our blinded path, vs no context at all because there was no blinded path.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think we wanted to avoid having an Option throughout the creation code. But we could remove OffersContext::Unknown once we no longer create any paths with it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if we're avoiding an Option, won't ::Unknown continue to be used when receiving a message to an offer/refund without paths? i.e. here https://github.com/lightningdevkit/rust-lightning/blob/main/lightning/src/onion_message/messenger.rs#L1516

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, I'm in agreement that once we remove the last use of OffersContext::Unknown on the creation side, we can update the handler to use an Option.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah okay, thanks for that clarification.

invoice.verify_using_metadata(expanded_key, secp_ctx)
},
OffersContext::OutboundPayment { payment_id, nonce } => {
invoice
.verify_using_payer_data(*payment_id, *nonce, expanded_key, secp_ctx)
.then(|| *payment_id)
.ok_or(())
},
_ => Err(()),
}
}

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);
Expand Down Expand Up @@ -8784,13 +8808,12 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
let entropy = &*$self.entropy_source;
let secp_ctx = &$self.secp_ctx;

let path = $self.create_blinded_paths_using_absolute_expiry(OffersContext::Unknown {}, absolute_expiry)
let nonce = Nonce::from_entropy_source(entropy);
let context = OffersContext::InvoiceRequest { nonce };
let path = $self.create_blinded_paths_using_absolute_expiry(context, absolute_expiry)
.and_then(|paths| paths.into_iter().next().ok_or(()))
.map_err(|_| Bolt12SemanticError::MissingPaths)?;

let builder = OfferBuilder::deriving_signing_pubkey(
node_id, expanded_key, entropy, secp_ctx
)
let builder = OfferBuilder::deriving_signing_pubkey(node_id, expanded_key, nonce, secp_ctx)
.chain_hash($self.chain_hash)
.path(path);

Expand Down Expand Up @@ -8858,13 +8881,14 @@ macro_rules! create_refund_builder { ($self: ident, $builder: ty) => {
let entropy = &*$self.entropy_source;
let secp_ctx = &$self.secp_ctx;

let context = OffersContext::OutboundPayment { payment_id };
let nonce = Nonce::from_entropy_source(entropy);
let context = OffersContext::OutboundPayment { payment_id, nonce };
let path = $self.create_blinded_paths_using_absolute_expiry(context, Some(absolute_expiry))
.and_then(|paths| paths.into_iter().next().ok_or(()))
.map_err(|_| Bolt12SemanticError::MissingPaths)?;

let builder = RefundBuilder::deriving_payer_id(
node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
node_id, expanded_key, nonce, secp_ctx, amount_msats, payment_id
)?
.chain_hash($self.chain_hash)
.absolute_expiry(absolute_expiry)
Expand Down Expand Up @@ -8973,8 +8997,9 @@ where
let entropy = &*self.entropy_source;
let secp_ctx = &self.secp_ctx;

let nonce = Nonce::from_entropy_source(entropy);
let builder: InvoiceRequestBuilder<DerivedPayerId, secp256k1::All> = offer
.request_invoice_deriving_payer_id(expanded_key, entropy, secp_ctx, payment_id)?
.request_invoice_deriving_payer_id(expanded_key, nonce, secp_ctx, payment_id)?
.into();
let builder = builder.chain_hash(self.chain_hash)?;

Expand All @@ -8992,8 +9017,9 @@ where
};
let invoice_request = builder.build_and_sign()?;

let context = OffersContext::OutboundPayment { payment_id };
let reply_paths = self.create_blinded_paths(context).map_err(|_| Bolt12SemanticError::MissingPaths)?;
let context = OffersContext::OutboundPayment { payment_id, nonce };
let reply_paths = self.create_blinded_paths(context)
.map_err(|_| Bolt12SemanticError::MissingPaths)?;

let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);

Expand Down Expand Up @@ -10692,7 +10718,7 @@ where

let abandon_if_payment = |context| {
match context {
OffersContext::OutboundPayment { payment_id } => self.abandon_payment(payment_id),
OffersContext::OutboundPayment { payment_id, .. } => self.abandon_payment(payment_id),
_ => {},
}
};
Expand All @@ -10703,19 +10729,32 @@ where
Some(responder) => responder,
None => return ResponseInstruction::NoResponse,
};

let nonce = match context {
OffersContext::Unknown {} if invoice_request.metadata().is_some() => None,
OffersContext::InvoiceRequest { nonce } => Some(nonce),
_ => return ResponseInstruction::NoResponse,
};

let invoice_request = match nonce {
valentinewallace marked this conversation as resolved.
Show resolved Hide resolved
Some(nonce) => match invoice_request.verify_using_recipient_data(
nonce, expanded_key, secp_ctx,
) {
Ok(invoice_request) => invoice_request,
Err(()) => return ResponseInstruction::NoResponse,
},
None => match invoice_request.verify_using_metadata(expanded_key, secp_ctx) {
Ok(invoice_request) => invoice_request,
Err(()) => return ResponseInstruction::NoResponse,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I don't think there's anything wrong with ignoring the BOLTs here - as long as we handle "unsolicited invoice_request that is for an offer we didnt built" and "invoice_request that we received over the wrong path" the same we're fine. Not sure it matters much if we think this case isn't going to be a common one that we need to worry about making sure we get good errors back, but if we think it'd be helpful we should just do it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, this will only be triggered for older offers. When we add support for producing offers without blinded paths (i.e. with a public node id), it will also be hit. So at that point someone could de-anonymize the older offer by sending an invoice request for it to the node id given in the newer offer.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm? Not sure I understand the scenario you're describing - are you saying if we issue two offers, one with a blinded path and one that publishes our node, someone can use the one with the blinded path and the one that publishes our node somehow to get different behavior?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, no, I was saying they could determine that they came from the same issuer. Though, that is actually a different problem than what you are asking about and one we probably can't solve.

Sorry, think I was confused about your comment. Were you saying we should continue to send an InvoiceError with Bolt12SemanticError::InvalidMetadata for both verification paths?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so? Like, if we think it doesn't matter if we pass back an error then no need, but if we think we may hit those cases and we should pass back an error then we should ignore the BOLTs here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, probably not useful to send an InvoiceError. The only time those should ever fail is if we are given an invoice request for an invalid offer or an invalid path id used. Older offers should still work.

},
};

let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
&invoice_request
&invoice_request.inner
) {
Ok(amount_msats) => amount_msats,
Err(error) => return responder.respond(OffersMessage::InvoiceError(error.into())),
};
let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
Ok(invoice_request) => invoice_request,
Err(()) => {
let error = Bolt12SemanticError::InvalidMetadata;
return responder.respond(OffersMessage::InvoiceError(error.into()));
},
};

let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
let (payment_hash, payment_secret) = match self.create_inbound_payment(
Expand Down Expand Up @@ -10788,24 +10827,28 @@ where
}
},
OffersMessage::Invoice(invoice) => {
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_verified_bolt12_invoice(&invoice, payment_id)
.map_err(|e| {
log_trace!(self.logger, "Failed paying invoice: {:?}", e);
InvoiceError::from_string(format!("{:?}", e))
})
}
},
Err(()) => Err(InvoiceError::from_string("Unrecognized invoice".to_owned())),
let payment_id = match self.verify_bolt12_invoice(&invoice, &context) {
Ok(payment_id) => payment_id,
Err(()) => return ResponseInstruction::NoResponse,
};

let result = {
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, context, responder,
};
self.pending_events.lock().unwrap().push_back((event, None));
return ResponseInstruction::NoResponse;
} else {
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))
})
}
};

match result {
Expand Down
Loading
Loading