Skip to content

Commit ddcbbe7

Browse files
committed
OffersMessageHandler impl for ChannelManager
Define the BOLT 12 message flow in ChannelManager's OffersMessageHandler implementation. - An invoice_request message results in responding with an invoice message if it can be verified that the request is for a valid offer. - An invoice is paid if it can be verified to have originated from a sent invoice_request or a refund. - An invoice_error is sent in some failure cases. - Initial messages enqueued for sending are released to OnionMessenger
1 parent dbcde1c commit ddcbbe7

File tree

3 files changed

+150
-5
lines changed

3 files changed

+150
-5
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 135 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,15 @@ use crate::ln::onion_utils::HTLCFailReason;
5454
use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5555
#[cfg(test)]
5656
use crate::ln::outbound_payment;
57-
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs};
57+
use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs};
5858
use crate::ln::wire::Encode;
59+
use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder};
60+
use crate::offers::invoice_error::InvoiceError;
61+
use crate::offers::merkle::SignError;
5962
use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder};
6063
use crate::offers::parse::Bolt12SemanticError;
6164
use crate::offers::refund::RefundBuilder;
62-
use crate::onion_message::{Destination, OffersMessage, PendingOnionMessage};
65+
use crate::onion_message::{Destination, OffersMessage, OffersMessageHandler, PendingOnionMessage};
6366
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
6467
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
6568
use crate::util::wakers::{Future, Notifier};
@@ -3470,6 +3473,17 @@ where
34703473
self.pending_outbound_payments.test_set_payment_metadata(payment_id, new_payment_metadata);
34713474
}
34723475

3476+
pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), Bolt12PaymentError> {
3477+
let best_block_height = self.best_block.read().unwrap().height();
3478+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
3479+
self.pending_outbound_payments
3480+
.send_payment_for_bolt12_invoice(
3481+
invoice, payment_id, &self.router, self.list_usable_channels(),
3482+
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer,
3483+
best_block_height, &self.logger, &self.pending_events,
3484+
|args| self.send_payment_along_path(args)
3485+
)
3486+
}
34733487

34743488
/// Signals that no further attempts for the given payment should occur. Useful if you have a
34753489
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
@@ -8229,6 +8243,125 @@ where
82298243
}
82308244
}
82318245

8246+
impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
8247+
OffersMessageHandler for ChannelManager<M, T, ES, NS, SP, F, R, L>
8248+
where
8249+
M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
8250+
T::Target: BroadcasterInterface,
8251+
ES::Target: EntropySource,
8252+
NS::Target: NodeSigner,
8253+
SP::Target: SignerProvider,
8254+
F::Target: FeeEstimator,
8255+
R::Target: Router,
8256+
L::Target: Logger,
8257+
{
8258+
fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage> {
8259+
let secp_ctx = &self.secp_ctx;
8260+
let expanded_key = &self.inbound_payment_key;
8261+
8262+
match message {
8263+
OffersMessage::InvoiceRequest(invoice_request) => {
8264+
let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
8265+
&invoice_request
8266+
) {
8267+
Ok(amount_msats) => Some(amount_msats),
8268+
Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
8269+
};
8270+
let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
8271+
Ok(invoice_request) => invoice_request,
8272+
Err(()) => {
8273+
let error = Bolt12SemanticError::InvalidMetadata;
8274+
return Some(OffersMessage::InvoiceError(error.into()));
8275+
},
8276+
};
8277+
let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
8278+
8279+
match self.create_inbound_payment(amount_msats, relative_expiry, None) {
8280+
Ok((payment_hash, _payment_secret)) if invoice_request.keys.is_some() => {
8281+
// TODO: Include payment_secret in payment_paths.
8282+
let payment_paths = vec![];
8283+
#[cfg(not(feature = "no-std"))]
8284+
let builder = invoice_request.respond_using_derived_keys(
8285+
payment_paths, payment_hash
8286+
);
8287+
#[cfg(feature = "no-std")]
8288+
let created_at = Duration::from_secs(
8289+
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
8290+
);
8291+
#[cfg(feature = "no-std")]
8292+
let builder = invoice_request.respond_using_derived_keys_no_std(
8293+
payment_paths, payment_hash, created_at
8294+
);
8295+
match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
8296+
Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
8297+
Err(error) => Some(OffersMessage::InvoiceError(error.into())),
8298+
}
8299+
},
8300+
Ok((payment_hash, _payment_secret)) => {
8301+
// TODO: Include payment_secret in payment_paths.
8302+
let payment_paths = vec![];
8303+
#[cfg(not(feature = "no-std"))]
8304+
let builder = invoice_request.respond_with(payment_paths, payment_hash);
8305+
#[cfg(feature = "no-std")]
8306+
let created_at = Duration::from_secs(
8307+
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
8308+
);
8309+
#[cfg(feature = "no-std")]
8310+
let builder = invoice_request.respond_with_no_std(
8311+
payment_paths, payment_hash, created_at
8312+
);
8313+
let response = builder.and_then(|builder| builder.allow_mpp().build())
8314+
.map_err(|e| OffersMessage::InvoiceError(e.into()))
8315+
.and_then(|invoice|
8316+
match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
8317+
Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
8318+
Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
8319+
InvoiceError::from_str("Failed signing invoice")
8320+
)),
8321+
Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
8322+
InvoiceError::from_str("Failed invoice signature verification")
8323+
)),
8324+
});
8325+
match response {
8326+
Ok(invoice) => Some(invoice),
8327+
Err(error) => Some(error),
8328+
}
8329+
},
8330+
Err(()) => {
8331+
Some(OffersMessage::InvoiceError(Bolt12SemanticError::InvalidAmount.into()))
8332+
},
8333+
}
8334+
},
8335+
OffersMessage::Invoice(invoice) => {
8336+
match invoice.verify(expanded_key, secp_ctx) {
8337+
Err(()) => {
8338+
Some(OffersMessage::InvoiceError(InvoiceError::from_str("Unrecognized invoice")))
8339+
},
8340+
Ok(_) if invoice.invoice_features().requires_unknown_bits_from(&self.bolt12_invoice_features()) => {
8341+
Some(OffersMessage::InvoiceError(Bolt12SemanticError::UnknownRequiredFeatures.into()))
8342+
},
8343+
Ok(payment_id) => {
8344+
if let Err(e) = self.send_payment_for_bolt12_invoice(&invoice, payment_id) {
8345+
log_trace!(self.logger, "Failed paying invoice: {:?}", e);
8346+
Some(OffersMessage::InvoiceError(InvoiceError::from_str(&format!("{:?}", e))))
8347+
} else {
8348+
None
8349+
}
8350+
},
8351+
}
8352+
},
8353+
OffersMessage::InvoiceError(invoice_error) => {
8354+
log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
8355+
None
8356+
},
8357+
}
8358+
}
8359+
8360+
fn release_pending_messages(&self) -> Vec<PendingOnionMessage<OffersMessage>> {
8361+
core::mem::take(&mut self.pending_offers_messages.lock().unwrap())
8362+
}
8363+
}
8364+
82328365
/// Fetches the set of [`NodeFeatures`] flags that are provided by or required by
82338366
/// [`ChannelManager`].
82348367
pub(crate) fn provided_node_features(config: &UserConfig) -> NodeFeatures {

lightning/src/offers/invoice.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl<'a> InvoiceBuilder<'a, ExplicitSigningPubkey> {
174174
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
175175
created_at: Duration, payment_hash: PaymentHash
176176
) -> Result<Self, Bolt12SemanticError> {
177-
let amount_msats = Self::check_amount_msats(invoice_request)?;
177+
let amount_msats = Self::amount_msats(invoice_request)?;
178178
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
179179
let contents = InvoiceContents::ForOffer {
180180
invoice_request: invoice_request.contents.clone(),
@@ -207,7 +207,7 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
207207
invoice_request: &'a InvoiceRequest, payment_paths: Vec<(BlindedPayInfo, BlindedPath)>,
208208
created_at: Duration, payment_hash: PaymentHash, keys: KeyPair
209209
) -> Result<Self, Bolt12SemanticError> {
210-
let amount_msats = Self::check_amount_msats(invoice_request)?;
210+
let amount_msats = Self::amount_msats(invoice_request)?;
211211
let signing_pubkey = invoice_request.contents.inner.offer.signing_pubkey();
212212
let contents = InvoiceContents::ForOffer {
213213
invoice_request: invoice_request.contents.clone(),
@@ -237,7 +237,9 @@ impl<'a> InvoiceBuilder<'a, DerivedSigningPubkey> {
237237
}
238238

239239
impl<'a, S: SigningPubkeyStrategy> InvoiceBuilder<'a, S> {
240-
fn check_amount_msats(invoice_request: &InvoiceRequest) -> Result<u64, Bolt12SemanticError> {
240+
pub(crate) fn amount_msats(
241+
invoice_request: &InvoiceRequest
242+
) -> Result<u64, Bolt12SemanticError> {
241243
match invoice_request.amount_msats() {
242244
Some(amount_msats) => Ok(amount_msats),
243245
None => match invoice_request.contents.inner.offer.amount() {

lightning/src/offers/invoice_error.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,16 @@ pub struct ErroneousField {
4848
pub suggested_value: Option<Vec<u8>>,
4949
}
5050

51+
impl InvoiceError {
52+
/// Creates an [`InvoiceError`] with the given message.
53+
pub fn from_str(s: &str) -> Self {
54+
Self {
55+
erroneous_field: None,
56+
message: UntrustedString(s.to_string()),
57+
}
58+
}
59+
}
60+
5161
impl core::fmt::Display for InvoiceError {
5262
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
5363
self.message.fmt(f)

0 commit comments

Comments
 (0)