Skip to content

Commit 1d584a4

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.
1 parent 7a5145d commit 1d584a4

File tree

4 files changed

+295
-29
lines changed

4 files changed

+295
-29
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5555
use crate::ln::outbound_payment;
5656
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs};
5757
use crate::ln::wire::Encode;
58+
use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, InvoiceBuilder};
59+
use crate::offers::invoice_error::InvoiceError;
60+
use crate::offers::merkle::SignError;
61+
use crate::offers::parse::Bolt12SemanticError;
62+
use crate::onion_message::{OffersMessage, OffersMessageHandler};
5863
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
5964
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
6065
use crate::util::wakers::{Future, Notifier};
@@ -3404,6 +3409,17 @@ where
34043409
self.pending_outbound_payments.test_set_payment_metadata(payment_id, new_payment_metadata);
34053410
}
34063411

3412+
pub(super) fn send_payment_for_bolt12_invoice(&self, invoice: &Bolt12Invoice, payment_id: PaymentId) -> Result<(), RetryableSendFailure> {
3413+
let best_block_height = self.best_block.read().unwrap().height();
3414+
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
3415+
self.pending_outbound_payments
3416+
.send_payment_for_bolt12_invoice(
3417+
invoice, payment_id, &self.router, self.list_usable_channels(),
3418+
|| self.compute_inflight_htlcs(), &self.entropy_source, &self.node_signer,
3419+
best_block_height, &self.logger, &self.pending_events,
3420+
|args| self.send_payment_along_path(args)
3421+
)
3422+
}
34073423

34083424
/// Signals that no further retries for the given payment should occur. Useful if you have a
34093425
/// pending outbound payment with retries remaining, but wish to stop retrying the payment before
@@ -7734,6 +7750,121 @@ where
77347750
}
77357751
}
77367752

7753+
impl<M: Deref, T: Deref, ES: Deref, NS: Deref, SP: Deref, F: Deref, R: Deref, L: Deref>
7754+
OffersMessageHandler for ChannelManager<M, T, ES, NS, SP, F, R, L>
7755+
where
7756+
M::Target: chain::Watch<<SP::Target as SignerProvider>::Signer>,
7757+
T::Target: BroadcasterInterface,
7758+
ES::Target: EntropySource,
7759+
NS::Target: NodeSigner,
7760+
SP::Target: SignerProvider,
7761+
F::Target: FeeEstimator,
7762+
R::Target: Router,
7763+
L::Target: Logger,
7764+
{
7765+
fn handle_message(&self, message: OffersMessage) -> Option<OffersMessage> {
7766+
let secp_ctx = &self.secp_ctx;
7767+
let expanded_key = &self.inbound_payment_key;
7768+
7769+
match message {
7770+
OffersMessage::InvoiceRequest(invoice_request) => {
7771+
let amount_msats = match InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(
7772+
&invoice_request
7773+
) {
7774+
Ok(amount_msats) => Some(amount_msats),
7775+
Err(error) => return Some(OffersMessage::InvoiceError(error.into())),
7776+
};
7777+
let invoice_request = match invoice_request.verify(expanded_key, secp_ctx) {
7778+
Ok(invoice_request) => invoice_request,
7779+
Err(()) => {
7780+
let error = Bolt12SemanticError::InvalidMetadata;
7781+
return Some(OffersMessage::InvoiceError(error.into()));
7782+
},
7783+
};
7784+
let relative_expiry = DEFAULT_RELATIVE_EXPIRY.as_secs() as u32;
7785+
7786+
match self.create_inbound_payment(amount_msats, relative_expiry, None) {
7787+
Ok((payment_hash, _payment_secret)) if invoice_request.keys.is_some() => {
7788+
// TODO: Include payment_secret in payment_paths.
7789+
let payment_paths = vec![];
7790+
#[cfg(not(feature = "no-std"))]
7791+
let builder = invoice_request.respond_using_derived_keys(
7792+
payment_paths, payment_hash
7793+
);
7794+
#[cfg(feature = "no-std")]
7795+
let created_at = Duration::from_secs(
7796+
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
7797+
);
7798+
#[cfg(feature = "no-std")]
7799+
let builder = invoice_request.respond_using_derived_keys_no_std(
7800+
payment_paths, payment_hash, created_at
7801+
);
7802+
match builder.and_then(|b| b.allow_mpp().build_and_sign(secp_ctx)) {
7803+
Ok(invoice) => Some(OffersMessage::Invoice(invoice)),
7804+
Err(error) => Some(OffersMessage::InvoiceError(error.into())),
7805+
}
7806+
},
7807+
Ok((payment_hash, _payment_secret)) => {
7808+
// TODO: Include payment_secret in payment_paths.
7809+
let payment_paths = vec![];
7810+
#[cfg(not(feature = "no-std"))]
7811+
let builder = invoice_request.respond_with(payment_paths, payment_hash);
7812+
#[cfg(feature = "no-std")]
7813+
let created_at = Duration::from_secs(
7814+
self.highest_seen_timestamp.load(Ordering::Acquire) as u64
7815+
);
7816+
#[cfg(feature = "no-std")]
7817+
let builder = invoice_request.respond_with_no_std(
7818+
payment_paths, payment_hash, created_at
7819+
);
7820+
let response = builder.and_then(|builder| builder.allow_mpp().build())
7821+
.map_err(|e| OffersMessage::InvoiceError(e.into()))
7822+
.and_then(|invoice|
7823+
match invoice.sign(|invoice| self.node_signer.sign_bolt12_invoice(invoice)) {
7824+
Ok(invoice) => Ok(OffersMessage::Invoice(invoice)),
7825+
Err(SignError::Signing(())) => Err(OffersMessage::InvoiceError(
7826+
InvoiceError::from_str("Failed signing invoice")
7827+
)),
7828+
Err(SignError::Verification(_)) => Err(OffersMessage::InvoiceError(
7829+
InvoiceError::from_str("Failed invoice signature verification")
7830+
)),
7831+
});
7832+
match response {
7833+
Ok(invoice) => Some(invoice),
7834+
Err(error) => Some(error),
7835+
}
7836+
},
7837+
Err(()) => {
7838+
Some(OffersMessage::InvoiceError(Bolt12SemanticError::InvalidAmount.into()))
7839+
},
7840+
}
7841+
},
7842+
OffersMessage::Invoice(invoice) => {
7843+
match invoice.verify(expanded_key, secp_ctx) {
7844+
Err(()) => {
7845+
Some(OffersMessage::InvoiceError(InvoiceError::from_str("Unrecognized invoice")))
7846+
},
7847+
Ok(_) if invoice.invoice_features().requires_unknown_bits() => {
7848+
Some(OffersMessage::InvoiceError(Bolt12SemanticError::UnknownRequiredFeatures.into()))
7849+
},
7850+
Ok(payment_id) => {
7851+
if let Err(e) = self.send_payment_for_bolt12_invoice(&invoice, payment_id) {
7852+
log_error!(self.logger, "Failed paying invoice: {:?}", e);
7853+
Some(OffersMessage::InvoiceError(InvoiceError::from_str(&format!("{:?}", e))))
7854+
} else {
7855+
None
7856+
}
7857+
},
7858+
}
7859+
},
7860+
OffersMessage::InvoiceError(invoice_error) => {
7861+
log_error!(self.logger, "Received invoice_error: {}", invoice_error);
7862+
None
7863+
},
7864+
}
7865+
}
7866+
}
7867+
77377868
/// Fetches the set of [`NodeFeatures`] flags which are provided by or required by
77387869
/// [`ChannelManager`].
77397870
pub(crate) fn provided_node_features(config: &UserConfig) -> NodeFeatures {

lightning/src/ln/outbound_payment.rs

Lines changed: 148 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::events::{self, PaymentFailureReason};
1818
use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret};
1919
use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, INVOICE_REQUEST_TIMEOUT_TICKS, PaymentId};
2020
use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason};
21+
use crate::offers::invoice::Bolt12Invoice;
2122
use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router};
2223
use crate::util::errors::APIError;
2324
use crate::util::logger::Logger;
@@ -356,6 +357,10 @@ pub enum RetryableSendFailure {
356357
/// [`Event::PaymentSent`]: crate::events::Event::PaymentSent
357358
/// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed
358359
DuplicatePayment,
360+
/// Indicates that a payment for the provided [`PaymentId`] was unexpected, either because the
361+
/// invoice was not explicitly requested (or for a known refund) or the invoice was not received
362+
/// in time (or was already paid).
363+
UnexpectedPayment,
359364
}
360365

361366
/// If a payment fails to send with [`ChannelManager::send_payment_with_route`], it can be in one
@@ -565,6 +570,8 @@ pub(super) struct SendAlongPathArgs<'a> {
565570
pub session_priv_bytes: [u8; 32],
566571
}
567572

573+
const BOLT_12_INVOICE_RETRY_STRATEGY: Retry = Retry::Attempts(3);
574+
568575
pub(super) struct OutboundPayments {
569576
pub(super) pending_outbound_payments: Mutex<HashMap<PaymentId, PendingOutboundPayment>>,
570577
pub(super) retry_lock: Mutex<()>,
@@ -598,6 +605,84 @@ impl OutboundPayments {
598605
best_block_height, logger, pending_events, &send_payment_along_path)
599606
}
600607

608+
pub(super) fn send_payment_for_bolt12_invoice<R: Deref, ES: Deref, NS: Deref, IH, SP, L: Deref>(
609+
&self, invoice: &Bolt12Invoice, payment_id: PaymentId, router: &R,
610+
first_hops: Vec<ChannelDetails>, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS,
611+
best_block_height: u32, logger: &L,
612+
pending_events: &Mutex<VecDeque<(events::Event, Option<EventCompletionAction>)>>,
613+
send_payment_along_path: SP,
614+
) -> Result<(), RetryableSendFailure>
615+
where
616+
R::Target: Router,
617+
ES::Target: EntropySource,
618+
NS::Target: NodeSigner,
619+
L::Target: Logger,
620+
IH: Fn() -> InFlightHtlcs,
621+
SP: Fn(SendAlongPathArgs) -> Result<(), APIError>,
622+
{
623+
let payment_hash = invoice.payment_hash();
624+
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
625+
hash_map::Entry::Occupied(entry) if entry.get().is_awaiting_invoice() => {
626+
*entry.into_mut() = PendingOutboundPayment::InvoiceReceived { payment_hash };
627+
},
628+
hash_map::Entry::Occupied(_) => return Err(RetryableSendFailure::DuplicatePayment),
629+
hash_map::Entry::Vacant(_) => return Err(RetryableSendFailure::UnexpectedPayment),
630+
};
631+
632+
#[cfg(feature = "std")] {
633+
if invoice.is_expired() {
634+
log_error!(logger, "Invoice expired, abandoning payment {}", &payment_id);
635+
self.abandon_payment(payment_id, PaymentFailureReason::PaymentExpired, pending_events);
636+
return Err(RetryableSendFailure::PaymentExpired);
637+
}
638+
}
639+
640+
let recipient_onion = RecipientOnionFields {
641+
payment_secret: None,
642+
payment_metadata: None,
643+
custom_tlvs: vec![],
644+
};
645+
let route_params = RouteParameters {
646+
payment_params: PaymentParameters::from_bolt12_invoice(&invoice),
647+
final_value_msat: invoice.amount_msats(),
648+
};
649+
650+
let route = match router.find_route_with_id(
651+
&node_signer.get_node_id(Recipient::Node).unwrap(), &route_params,
652+
Some(&first_hops.iter().collect::<Vec<_>>()), inflight_htlcs(), payment_hash, payment_id
653+
) {
654+
Ok(route) => route,
655+
Err(e) => {
656+
log_error!(
657+
logger, "Failed to find a route when sending, abandoning payment {}: {:#?}",
658+
&payment_id, e
659+
);
660+
self.abandon_payment(payment_id, PaymentFailureReason::RouteNotFound, pending_events);
661+
return Err(RetryableSendFailure::RouteNotFound);
662+
}
663+
};
664+
665+
let onion_session_privs = self.add_new_pending_bolt12_payment(
666+
recipient_onion.clone(), payment_id, None, &route, Some(BOLT_12_INVOICE_RETRY_STRATEGY),
667+
Some(route_params.payment_params.clone()), entropy_source, best_block_height
668+
)?;
669+
let pay_result = self.pay_route_internal(
670+
&route, payment_hash, recipient_onion, None, payment_id, None, onion_session_privs,
671+
node_signer, best_block_height, &send_payment_along_path
672+
);
673+
log_info!(logger, "Result sending payment with id {}: {:?}", &payment_id, pay_result);
674+
675+
if let Err(e) = pay_result {
676+
self.handle_pay_route_err(
677+
e, payment_id, payment_hash, route, route_params, router, first_hops,
678+
&inflight_htlcs, entropy_source, node_signer, best_block_height, logger,
679+
pending_events, &send_payment_along_path
680+
);
681+
}
682+
683+
Ok(())
684+
}
685+
601686
pub(super) fn send_payment_with_route<ES: Deref, NS: Deref, F>(
602687
&self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
603688
payment_id: PaymentId, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
@@ -1051,40 +1136,78 @@ impl OutboundPayments {
10511136
keysend_preimage: Option<PaymentPreimage>, route: &Route, retry_strategy: Option<Retry>,
10521137
payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32
10531138
) -> Result<Vec<[u8; 32]>, PaymentSendFailure> where ES::Target: EntropySource {
1054-
let mut onion_session_privs = Vec::with_capacity(route.paths.len());
1055-
for _ in 0..route.paths.len() {
1056-
onion_session_privs.push(entropy_source.get_secure_random_bytes());
1057-
}
1058-
10591139
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
10601140
match pending_outbounds.entry(payment_id) {
10611141
hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment),
10621142
hash_map::Entry::Vacant(entry) => {
1063-
let payment = entry.insert(PendingOutboundPayment::Retryable {
1064-
retry_strategy,
1065-
attempts: PaymentAttempts::new(),
1066-
payment_params,
1067-
session_privs: HashSet::new(),
1068-
pending_amt_msat: 0,
1069-
pending_fee_msat: Some(0),
1070-
payment_hash,
1071-
payment_secret: recipient_onion.payment_secret,
1072-
payment_metadata: recipient_onion.payment_metadata,
1073-
keysend_preimage,
1074-
custom_tlvs: recipient_onion.custom_tlvs,
1075-
starting_block_height: best_block_height,
1076-
total_msat: route.get_total_amount(),
1077-
});
1078-
1079-
for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.iter()) {
1080-
assert!(payment.insert(*session_priv_bytes, path));
1081-
}
1082-
1143+
let (payment, onion_session_privs) = self.create_pending_payment(
1144+
payment_hash, recipient_onion, keysend_preimage, route, retry_strategy,
1145+
payment_params, entropy_source, best_block_height
1146+
);
1147+
entry.insert(payment);
10831148
Ok(onion_session_privs)
10841149
},
10851150
}
10861151
}
10871152

1153+
pub(super) fn add_new_pending_bolt12_payment<ES: Deref>(
1154+
&self, recipient_onion: RecipientOnionFields, payment_id: PaymentId,
1155+
keysend_preimage: Option<PaymentPreimage>, route: &Route, retry_strategy: Option<Retry>,
1156+
payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32
1157+
) -> Result<Vec<[u8; 32]>, RetryableSendFailure> where ES::Target: EntropySource {
1158+
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();
1159+
match pending_outbounds.entry(payment_id) {
1160+
hash_map::Entry::Occupied(entry) => match entry.get() {
1161+
PendingOutboundPayment::InvoiceReceived { payment_hash } => {
1162+
let (payment, onion_session_privs) = self.create_pending_payment(
1163+
*payment_hash, recipient_onion, keysend_preimage, route, retry_strategy,
1164+
payment_params, entropy_source, best_block_height
1165+
);
1166+
*entry.into_mut() = payment;
1167+
Ok(onion_session_privs)
1168+
},
1169+
_ => Err(RetryableSendFailure::DuplicatePayment),
1170+
},
1171+
hash_map::Entry::Vacant(_) => Err(RetryableSendFailure::UnexpectedPayment),
1172+
}
1173+
}
1174+
1175+
fn create_pending_payment<ES: Deref>(
1176+
&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
1177+
keysend_preimage: Option<PaymentPreimage>, route: &Route, retry_strategy: Option<Retry>,
1178+
payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32
1179+
) -> (PendingOutboundPayment, Vec<[u8; 32]>)
1180+
where
1181+
ES::Target: EntropySource,
1182+
{
1183+
let mut onion_session_privs = Vec::with_capacity(route.paths.len());
1184+
for _ in 0..route.paths.len() {
1185+
onion_session_privs.push(entropy_source.get_secure_random_bytes());
1186+
}
1187+
1188+
let mut payment = PendingOutboundPayment::Retryable {
1189+
retry_strategy,
1190+
attempts: PaymentAttempts::new(),
1191+
payment_params,
1192+
session_privs: HashSet::new(),
1193+
pending_amt_msat: 0,
1194+
pending_fee_msat: Some(0),
1195+
payment_hash,
1196+
payment_secret: recipient_onion.payment_secret,
1197+
payment_metadata: recipient_onion.payment_metadata,
1198+
keysend_preimage,
1199+
custom_tlvs: recipient_onion.custom_tlvs,
1200+
starting_block_height: best_block_height,
1201+
total_msat: route.get_total_amount(),
1202+
};
1203+
1204+
for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.iter()) {
1205+
assert!(payment.insert(*session_priv_bytes, path));
1206+
}
1207+
1208+
(payment, onion_session_privs)
1209+
}
1210+
10881211
#[allow(unused)]
10891212
pub(super) fn add_new_awaiting_invoice(&self, payment_id: PaymentId) -> Result<(), ()> {
10901213
let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap();

0 commit comments

Comments
 (0)