diff --git a/Cargo.toml b/Cargo.toml index 3891b11a2b4..340e1f2444f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,6 +66,5 @@ check-cfg = [ "cfg(taproot)", "cfg(require_route_graph_test)", "cfg(splicing)", - "cfg(async_payments)", "cfg(simple_close)", ] diff --git a/fuzz/src/full_stack.rs b/fuzz/src/full_stack.rs index eb9d51d487d..ae9e385d5b0 100644 --- a/fuzz/src/full_stack.rs +++ b/fuzz/src/full_stack.rs @@ -1171,6 +1171,9 @@ fn two_peer_forwarding_seed() -> Vec { ext_from_hex("030120", &mut test); // init message (type 16) with static_remotekey required, no anchors/taproot, and other bits optional and mac ext_from_hex("0010 00021aaa 0008aaa210aa2a0a9aaa 01000000000000000000000000000000", &mut test); + // One feerate request on peer connection due to a list_channels call when seeing if the async + // receive offer cache needs updating + ext_from_hex("00fd", &mut test); // create outbound channel to peer 1 for 50k sat ext_from_hex( diff --git a/lightning/src/blinded_path/message.rs b/lightning/src/blinded_path/message.rs index 7db5dc00b05..142fe99fd86 100644 --- a/lightning/src/blinded_path/message.rs +++ b/lightning/src/blinded_path/message.rs @@ -506,10 +506,9 @@ pub enum AsyncPaymentsContext { /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest offer_id: OfferId, - /// The time as duration since the Unix epoch at which this path expires and messages sent over - /// it should be ignored. If we receive confirmation of an invoice over this path after its - /// expiry, it may be outdated and a new invoice update should be sent instead. - path_absolute_expiry: core::time::Duration, + /// The time as duration since the Unix epoch at which the invoice corresponding to this path + /// was created. Useful to know when an invoice needs replacement. + invoice_created_at: core::time::Duration, }, /// Context contained within the reply [`BlindedMessagePath`] we put in outbound /// [`HeldHtlcAvailable`] messages, provided back to us in corresponding [`ReleaseHeldHtlc`] @@ -577,7 +576,7 @@ impl_writeable_tlv_based_enum!(AsyncPaymentsContext, }, (3, StaticInvoicePersisted) => { (0, offer_id, required), - (2, path_absolute_expiry, required), + (2, invoice_created_at, required), }, (4, OfferPathsRequest) => { (0, recipient_id, required), diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs index 2b712073568..70a6ba271da 100644 --- a/lightning/src/events/mod.rs +++ b/lightning/src/events/mod.rs @@ -1628,7 +1628,6 @@ pub enum Event { /// /// [`ChannelManager::blinded_paths_for_async_recipient`]: crate::ln::channelmanager::ChannelManager::blinded_paths_for_async_recipient /// [`ChannelManager::set_paths_to_static_invoice_server`]: crate::ln::channelmanager::ChannelManager::set_paths_to_static_invoice_server - #[cfg(async_payments)] PersistStaticInvoice { /// The invoice that should be persisted and later provided to payers when handling a future /// [`Event::StaticInvoiceRequested`]. @@ -1645,6 +1644,8 @@ pub enum Event { /// /// When an [`Event::StaticInvoiceRequested`] comes in for the invoice, this id will be surfaced /// and can be used alongside the `invoice_id` to retrieve the invoice from the database. + /// + ///[`ChannelManager::blinded_paths_for_async_recipient`]: crate::ln::channelmanager::ChannelManager::blinded_paths_for_async_recipient recipient_id: Vec, /// A random identifier for the invoice. When an [`Event::StaticInvoiceRequested`] comes in for /// the invoice, this id will be surfaced and can be used alongside the `recipient_id` to @@ -1676,7 +1677,6 @@ pub enum Event { /// [`ChannelManager::set_paths_to_static_invoice_server`]: crate::ln::channelmanager::ChannelManager::set_paths_to_static_invoice_server /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest /// [`ChannelManager::send_static_invoice`]: crate::ln::channelmanager::ChannelManager::send_static_invoice - #[cfg(async_payments)] StaticInvoiceRequested { /// An identifier for the recipient previously surfaced in /// [`Event::PersistStaticInvoice::recipient_id`]. Useful when paired with the `invoice_id` to @@ -2123,13 +2123,11 @@ impl Writeable for Event { (8, former_temporary_channel_id, required), }); }, - #[cfg(async_payments)] &Event::PersistStaticInvoice { .. } => { 45u8.write(writer)?; // No need to write these events because we can just restart the static invoice negotiation // on startup. }, - #[cfg(async_payments)] &Event::StaticInvoiceRequested { .. } => { 47u8.write(writer)?; // Never write StaticInvoiceRequested events as buffered onion messages aren't serialized. @@ -2711,10 +2709,8 @@ impl MaybeReadable for Event { })) }, // Note that we do not write a length-prefixed TLV for PersistStaticInvoice events. - #[cfg(async_payments)] 45u8 => Ok(None), // Note that we do not write a length-prefixed TLV for StaticInvoiceRequested events. - #[cfg(async_payments)] 47u8 => Ok(None), // 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 diff --git a/lightning/src/ln/async_payments_tests.rs b/lightning/src/ln/async_payments_tests.rs index 7ee48225f78..d868eeeda80 100644 --- a/lightning/src/ln/async_payments_tests.rs +++ b/lightning/src/ln/async_payments_tests.rs @@ -17,6 +17,7 @@ use crate::events::{ use crate::ln::blinded_payment_tests::{fail_blinded_htlc_backwards, get_blinded_route_parameters}; use crate::ln::channelmanager::{PaymentId, RecipientOnionFields}; use crate::ln::functional_test_utils::*; +use crate::ln::inbound_payment; use crate::ln::msgs; use crate::ln::msgs::{ BaseMessageHandler, ChannelMessageHandler, MessageSendEvent, OnionMessageHandler, @@ -27,7 +28,7 @@ use crate::ln::outbound_payment::{ PendingOutboundPayment, Retry, TEST_ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY, }; use crate::offers::async_receive_offer_cache::{ - TEST_MAX_CACHED_OFFERS_TARGET, TEST_MAX_UPDATE_ATTEMPTS, + TEST_INVOICE_REFRESH_THRESHOLD, TEST_MAX_CACHED_OFFERS_TARGET, TEST_MAX_UPDATE_ATTEMPTS, TEST_MIN_OFFER_PATHS_RELATIVE_EXPIRY_SECS, TEST_OFFER_REFRESH_THRESHOLD, }; use crate::offers::flow::{ @@ -36,8 +37,11 @@ use crate::offers::flow::{ }; use crate::offers::invoice_request::InvoiceRequest; use crate::offers::nonce::Nonce; -use crate::offers::offer::Offer; -use crate::offers::static_invoice::StaticInvoice; +use crate::offers::offer::{Amount, Offer}; +use crate::offers::static_invoice::{ + StaticInvoice, StaticInvoiceBuilder, + DEFAULT_RELATIVE_EXPIRY as STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY, +}; use crate::onion_message::async_payments::{AsyncPaymentsMessage, AsyncPaymentsMessageHandler}; use crate::onion_message::messenger::{ Destination, MessageRouter, MessageSendInstructions, PeeledOnion, @@ -240,10 +244,50 @@ fn pass_async_payments_oms( (held_htlc_available_om_1_2, release_held_htlc) } +fn create_static_invoice_builder<'a>( + recipient: &Node, offer: &'a Offer, offer_nonce: Nonce, relative_expiry: Option, +) -> StaticInvoiceBuilder<'a> { + let entropy = recipient.keys_manager; + let amount_msat = offer.amount().and_then(|amount| match amount { + Amount::Bitcoin { amount_msats } => Some(amount_msats), + Amount::Currency { .. } => None, + }); + + let relative_expiry = relative_expiry.unwrap_or(STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY); + let relative_expiry_secs: u32 = relative_expiry.as_secs().try_into().unwrap_or(u32::MAX); + + let created_at = recipient.node.duration_since_epoch(); + let payment_secret = inbound_payment::create_for_spontaneous_payment( + &recipient.keys_manager.get_inbound_payment_key(), + amount_msat, + relative_expiry_secs, + created_at.as_secs(), + None, + ) + .unwrap(); + + recipient + .node + .flow + .create_static_invoice_builder( + &recipient.router, + entropy, + offer, + offer_nonce, + payment_secret, + relative_expiry_secs, + recipient.node.list_usable_channels(), + recipient.node.test_get_peers_for_blinded_path(), + ) + .unwrap() +} + fn create_static_invoice( always_online_counterparty: &Node, recipient: &Node, relative_expiry: Option, secp_ctx: &Secp256k1, ) -> (Offer, StaticInvoice) { + let entropy_source = recipient.keys_manager; + let blinded_paths_to_always_online_node = always_online_counterparty .message_router .create_blinded_paths( @@ -256,15 +300,14 @@ fn create_static_invoice( .unwrap(); let (offer_builder, offer_nonce) = recipient .node - .create_async_receive_offer_builder(blinded_paths_to_always_online_node) + .flow + .create_async_receive_offer_builder(entropy_source, blinded_paths_to_always_online_node) .unwrap(); let offer = offer_builder.build().unwrap(); - let static_invoice = recipient - .node - .create_static_invoice_builder(&offer, offer_nonce, relative_expiry) - .unwrap() - .build_and_sign(&secp_ctx) - .unwrap(); + let static_invoice = + create_static_invoice_builder(recipient, &offer, offer_nonce, relative_expiry) + .build_and_sign(&secp_ctx) + .unwrap(); (offer, static_invoice) } @@ -287,6 +330,38 @@ fn extract_payment_preimage(event: &Event) -> PaymentPreimage { } } +fn expect_offer_paths_requests(recipient: &Node, next_hop_nodes: &[&Node]) { + // We want to check that the async recipient has enqueued at least one `OfferPathsRequest` and no + // other message types. Check this by iterating through all their outbound onion messages, peeling + // multiple times if the messages are forwarded through other nodes. + let per_msg_recipient_msgs = recipient.onion_messenger.release_pending_msgs(); + let mut pk_to_msg = Vec::new(); + for (pk, msgs) in per_msg_recipient_msgs { + for msg in msgs { + pk_to_msg.push((pk, msg)); + } + } + let mut num_offer_paths_reqs: u8 = 0; + while let Some((pk, msg)) = pk_to_msg.pop() { + let node = next_hop_nodes.iter().find(|node| node.node.get_our_node_id() == pk).unwrap(); + let peeled_msg = node.onion_messenger.peel_onion_message(&msg).unwrap(); + match peeled_msg { + PeeledOnion::AsyncPayments(AsyncPaymentsMessage::OfferPathsRequest(_), _, _) => { + num_offer_paths_reqs += 1; + }, + PeeledOnion::Forward(next_hop, msg) => { + let next_pk = match next_hop { + crate::blinded_path::message::NextMessageHop::NodeId(pk) => pk, + _ => panic!(), + }; + pk_to_msg.push((next_pk, msg)); + }, + _ => panic!("Unexpected message"), + } + } + assert!(num_offer_paths_reqs > 0); +} + fn advance_time_by(duration: Duration, node: &Node) { let target_time = (node.node.duration_since_epoch() + duration).as_secs() as u32; let block = create_dummy_block(node.best_block_hash(), target_time, Vec::new()); @@ -377,6 +452,7 @@ fn static_invoice_unknown_required_features() { let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); + let entropy_source = nodes[2].keys_manager; create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); @@ -393,16 +469,15 @@ fn static_invoice_unknown_required_features() { .unwrap(); let (offer_builder, nonce) = nodes[2] .node - .create_async_receive_offer_builder(blinded_paths_to_always_online_node) + .flow + .create_async_receive_offer_builder(entropy_source, blinded_paths_to_always_online_node) .unwrap(); let offer = offer_builder.build().unwrap(); - let static_invoice_unknown_req_features = nodes[2] - .node - .create_static_invoice_builder(&offer, nonce, None) - .unwrap() - .features_unchecked(Bolt12InvoiceFeatures::unknown()) - .build_and_sign(&secp_ctx) - .unwrap(); + let static_invoice_unknown_req_features = + create_static_invoice_builder(&nodes[2], &offer, nonce, None) + .features_unchecked(Bolt12InvoiceFeatures::unknown()) + .build_and_sign(&secp_ctx) + .unwrap(); // Initiate payment to the offer corresponding to the manually-constructed invoice that has // unknown required features. @@ -469,6 +544,7 @@ fn ignore_unexpected_static_invoice() { let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); // Initiate payment to the sender's intended offer. let valid_static_invoice = @@ -566,6 +642,7 @@ fn async_receive_flow_success() { let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); let invoice_flow_res = pass_static_invoice_server_messages(&nodes[1], &nodes[2], recipient_id.clone()); @@ -628,6 +705,7 @@ fn expired_static_invoice_fail() { let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); let static_invoice = pass_static_invoice_server_messages(&nodes[1], &nodes[2], recipient_id.clone()).invoice; @@ -703,6 +781,7 @@ fn timeout_unreleased_payment() { let inv_server_paths = server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); let static_invoice = pass_static_invoice_server_messages(server, recipient, recipient_id.clone()).invoice; @@ -788,6 +867,7 @@ fn async_receive_mpp() { let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[3].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[3], &[&nodes[0], &nodes[1], &nodes[2]]); let static_invoice = pass_static_invoice_server_messages(&nodes[1], &nodes[3], recipient_id.clone()).invoice; @@ -881,6 +961,7 @@ fn amount_doesnt_match_invreq() { let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[3].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[3], &[&nodes[0], &nodes[1], &nodes[2]]); let static_invoice = pass_static_invoice_server_messages(&nodes[1], &nodes[3], recipient_id.clone()).invoice; @@ -1073,6 +1154,7 @@ fn invalid_async_receive_with_retry( create_node_chanmgrs(3, &node_cfgs, &[None, Some(allow_priv_chan_fwds_cfg), None]); let nodes = create_network(3, &node_cfgs, &node_chanmgrs); + let entropy_source = nodes[2].keys_manager; create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); create_unannounced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0); @@ -1080,6 +1162,7 @@ fn invalid_async_receive_with_retry( let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); // Set the random bytes so we can predict the offer nonce. let hardcoded_random_bytes = [42; 32]; @@ -1102,7 +1185,8 @@ fn invalid_async_receive_with_retry( .unwrap(); let (offer_builder, offer_nonce) = nodes[2] .node - .create_async_receive_offer_builder(blinded_paths_to_always_online_node) + .flow + .create_async_receive_offer_builder(entropy_source, blinded_paths_to_always_online_node) .unwrap(); let offer = offer_builder.build().unwrap(); let amt_msat = 5000; @@ -1112,12 +1196,10 @@ fn invalid_async_receive_with_retry( // use the same nodes to avoid complicating the test with a bunch of extra nodes. let mut static_invoice_paths = Vec::new(); for _ in 0..3 { - let static_inv_for_path = nodes[2] - .node - .create_static_invoice_builder(&offer, offer_nonce, None) - .unwrap() - .build_and_sign(&secp_ctx) - .unwrap(); + let static_inv_for_path = + create_static_invoice_builder(&nodes[2], &offer, offer_nonce, None) + .build_and_sign(&secp_ctx) + .unwrap(); static_invoice_paths.push(static_inv_for_path.payment_paths()[0].clone()); } nodes[2].router.expect_blinded_payment_paths(static_invoice_paths); @@ -1208,6 +1290,7 @@ fn expired_static_invoice_message_path() { let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); let static_invoice = pass_static_invoice_server_messages(&nodes[1], &nodes[2], recipient_id.clone()).invoice; @@ -1272,6 +1355,7 @@ fn expired_static_invoice_payment_path() { let inv_server_paths = nodes[1].node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); nodes[2].node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); // Make sure all nodes are at the same block height in preparation for CLTV timeout things. let node_max_height = @@ -1470,54 +1554,6 @@ fn ignore_expired_offer_paths_message() { .is_none()); } -#[cfg_attr(feature = "std", ignore)] -#[test] -fn ignore_expired_invoice_persisted_message() { - // If the recipient receives a static_invoice_persisted message over an expired reply path, it - // should be ignored. - 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, None]); - let nodes = create_network(2, &node_cfgs, &node_chanmgrs); - create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); - let server = &nodes[0]; - let recipient = &nodes[1]; - - let recipient_id = vec![42; 32]; - let inv_server_paths = - server.node.blinded_paths_for_async_recipient(recipient_id, None).unwrap(); - recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); - - // Exchange messages until we can extract the final static_invoice_persisted OM. - recipient.node.timer_tick_occurred(); - let serve_static_invoice = invoice_flow_up_to_send_serve_static_invoice(server, recipient).1; - server - .onion_messenger - .handle_onion_message(recipient.node.get_our_node_id(), &serve_static_invoice); - let mut events = server.node.get_and_clear_pending_events(); - assert_eq!(events.len(), 1); - let ack_path = match events.pop().unwrap() { - Event::PersistStaticInvoice { invoice_persisted_path, .. } => invoice_persisted_path, - _ => panic!(), - }; - - server.node.static_invoice_persisted(ack_path); - let invoice_persisted = server - .onion_messenger - .next_onion_message_for_peer(recipient.node.get_our_node_id()) - .unwrap(); - assert!(matches!( - recipient.onion_messenger.peel_onion_message(&invoice_persisted).unwrap(), - PeeledOnion::AsyncPayments(AsyncPaymentsMessage::StaticInvoicePersisted(_), _, _) - )); - - advance_time_by(TEST_TEMP_REPLY_PATH_RELATIVE_EXPIRY + Duration::from_secs(1), recipient); - recipient - .onion_messenger - .handle_onion_message(server.node.get_our_node_id(), &invoice_persisted); - assert!(recipient.node.get_async_receive_offer().is_err()); -} - #[test] fn limit_offer_paths_requests() { // Limit the number of offer_paths_requests sent to the server if they aren't responding. @@ -1533,10 +1569,12 @@ fn limit_offer_paths_requests() { let inv_server_paths = server.node.blinded_paths_for_async_recipient(recipient_id, None).unwrap(); recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[1], &[&nodes[0]]); // Up to TEST_MAX_UPDATE_ATTEMPTS offer_paths_requests are allowed to be sent out before the async // recipient should give up. - for _ in 0..TEST_MAX_UPDATE_ATTEMPTS { + // Subtract 1 because we sent the first request when invoice server paths were set above. + for _ in 0..TEST_MAX_UPDATE_ATTEMPTS - 1 { recipient.node.test_check_refresh_async_receive_offers(); let offer_paths_req = recipient .onion_messenger @@ -1677,11 +1715,53 @@ fn offer_cache_round_trip_ser() { assert_eq!(cached_offers_pre_ser, cached_offers_post_ser); } +#[test] +fn refresh_static_invoices_for_pending_offers() { + // Check that an invoice for an offer that is pending persistence with the server will be updated + // every timer tick. + 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, None, None]); + let nodes = create_network(2, &node_cfgs, &node_chanmgrs); + create_unannounced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0); + let server = &nodes[0]; + let recipient = &nodes[1]; + + let recipient_id = vec![42; 32]; + let inv_server_paths = + server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); + recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[1], &[&nodes[0]]); + + // Set up the recipient to have one offer pending with the static invoice server. + invoice_flow_up_to_send_serve_static_invoice(server, recipient); + + // Every timer tick, we'll send a fresh invoice to the server. + for _ in 0..10 { + recipient.node.timer_tick_occurred(); + let pending_oms = recipient.onion_messenger.release_pending_msgs(); + pending_oms + .get(&server.node.get_our_node_id()) + .unwrap() + .iter() + .find(|msg| match server.onion_messenger.peel_onion_message(&msg).unwrap() { + PeeledOnion::AsyncPayments(AsyncPaymentsMessage::ServeStaticInvoice(_), _, _) => { + true + }, + PeeledOnion::AsyncPayments(AsyncPaymentsMessage::OfferPathsRequest(_), _, _) => { + false + }, + _ => panic!("Unexpected message"), + }) + .unwrap(); + } +} + #[cfg_attr(feature = "std", ignore)] #[test] -fn refresh_static_invoices() { - // Check that an invoice for a particular offer stored with the server will be updated once per - // timer tick. +fn refresh_static_invoices_for_used_offers() { + // Check that an invoice for a used offer stored with the server will be updated every + // INVOICE_REFRESH_THRESHOLD. let chanmon_cfgs = create_chanmon_cfgs(3); let node_cfgs = create_node_cfgs(3, &chanmon_cfgs); @@ -1701,29 +1781,31 @@ fn refresh_static_invoices() { let inv_server_paths = server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); // Set up the recipient to have one offer and an invoice with the static invoice server. let flow_res = pass_static_invoice_server_messages(server, recipient, recipient_id.clone()); let original_invoice = flow_res.invoice; - // Mark the offer as used so we'll update the invoice on timer tick. + // Mark the offer as used so we'll update the invoice after INVOICE_REFRESH_THRESHOLD. let _offer = recipient.node.get_async_receive_offer().unwrap(); // Force the server and recipient to send OMs directly to each other for testing simplicity. server.message_router.peers_override.lock().unwrap().push(recipient.node.get_our_node_id()); recipient.message_router.peers_override.lock().unwrap().push(server.node.get_our_node_id()); - assert!(recipient - .onion_messenger - .next_onion_message_for_peer(server.node.get_our_node_id()) - .is_none()); + // Prior to INVOICE_REFRESH_THRESHOLD, we won't refresh the invoice. + advance_time_by(TEST_INVOICE_REFRESH_THRESHOLD, recipient); + recipient.node.timer_tick_occurred(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1]]); - // Check that we'll refresh the invoice on the next timer tick. + // After INVOICE_REFRESH_THRESHOLD, we will refresh the invoice. + advance_time_by(Duration::from_secs(1), recipient); recipient.node.timer_tick_occurred(); let pending_oms = recipient.onion_messenger.release_pending_msgs(); let serve_static_invoice_om = pending_oms .get(&server.node.get_our_node_id()) .unwrap() - .into_iter() + .iter() .find(|msg| match server.onion_messenger.peel_onion_message(&msg).unwrap() { PeeledOnion::AsyncPayments(AsyncPaymentsMessage::ServeStaticInvoice(_), _, _) => true, PeeledOnion::AsyncPayments(AsyncPaymentsMessage::OfferPathsRequest(_), _, _) => false, @@ -2093,6 +2175,7 @@ fn invoice_server_is_not_channel_peer() { let inv_server_paths = invoice_server.node.blinded_paths_for_async_recipient(recipient_id.clone(), None).unwrap(); recipient.node.set_paths_to_static_invoice_server(inv_server_paths).unwrap(); + expect_offer_paths_requests(&nodes[2], &[&nodes[0], &nodes[1], &nodes[3]]); let invoice = pass_static_invoice_server_messages(invoice_server, recipient, recipient_id.clone()) .invoice; diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 8fa34d7c349..00e05aa9e9e 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -34,8 +34,9 @@ use bitcoin::{secp256k1, Sequence}; #[cfg(splicing)] use bitcoin::{ScriptBuf, TxIn, Weight}; -use crate::blinded_path::message::MessageForwardNode; -use crate::blinded_path::message::{AsyncPaymentsContext, OffersContext}; +use crate::blinded_path::message::{ + AsyncPaymentsContext, BlindedMessagePath, MessageForwardNode, OffersContext, +}; use crate::blinded_path::payment::{ AsyncBolt12OfferContext, Bolt12OfferContext, PaymentContext, UnauthenticatedReceiveTlvs, }; @@ -102,6 +103,7 @@ use crate::offers::offer::Offer; use crate::offers::parse::Bolt12SemanticError; use crate::offers::refund::Refund; use crate::offers::signer; +use crate::offers::static_invoice::StaticInvoice; use crate::onion_message::async_payments::{ AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, OfferPaths, OfferPathsRequest, ReleaseHeldHtlc, ServeStaticInvoice, StaticInvoicePersisted, @@ -130,21 +132,12 @@ use crate::util::logger::{Level, Logger, WithContext}; use crate::util::scid_utils::fake_scid; use crate::util::ser::{ BigSize, FixedLengthReader, LengthReadable, MaybeReadable, Readable, ReadableArgs, VecWriter, - Writeable, Writer, + WithoutLength, Writeable, Writer, }; use crate::util::wakers::{Future, Notifier}; -#[cfg(all(test, async_payments))] +#[cfg(test)] use crate::blinded_path::payment::BlindedPaymentPath; -#[cfg(async_payments)] -use { - crate::blinded_path::message::BlindedMessagePath, - crate::offers::offer::Amount, - crate::offers::static_invoice::{ - StaticInvoice, StaticInvoiceBuilder, - DEFAULT_RELATIVE_EXPIRY as STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY, - }, -}; #[cfg(feature = "dnssec")] use { @@ -5099,7 +5092,7 @@ where ) } - #[cfg(all(test, async_payments))] + #[cfg(test)] pub(crate) fn test_modify_pending_payment(&self, payment_id: &PaymentId, mut callback: Fn) where Fn: FnMut(&mut PendingOutboundPayment), @@ -5229,7 +5222,6 @@ where ) } - #[cfg(async_payments)] fn check_refresh_async_receive_offer_cache(&self, timer_tick_occurred: bool) { let peers = self.get_peers_for_blinded_path(); let channels = self.list_usable_channels(); @@ -5253,27 +5245,24 @@ where } } - #[cfg(all(test, async_payments))] + #[cfg(test)] pub(crate) fn test_check_refresh_async_receive_offers(&self) { self.check_refresh_async_receive_offer_cache(false); } /// Should be called after handling an [`Event::PersistStaticInvoice`], where the `Responder` /// comes from [`Event::PersistStaticInvoice::invoice_persisted_path`]. - #[cfg(async_payments)] pub fn static_invoice_persisted(&self, invoice_persisted_path: Responder) { self.flow.static_invoice_persisted(invoice_persisted_path); } /// Forwards a [`StaticInvoice`] in response to an [`Event::StaticInvoiceRequested`]. - #[cfg(async_payments)] pub fn send_static_invoice( &self, invoice: StaticInvoice, responder: Responder, ) -> Result<(), Bolt12SemanticError> { self.flow.enqueue_static_invoice(invoice, responder) } - #[cfg(async_payments)] fn initiate_async_payment( &self, invoice: &StaticInvoice, payment_id: PaymentId, ) -> Result<(), Bolt12PaymentError> { @@ -5323,7 +5312,6 @@ where res } - #[cfg(async_payments)] fn send_payment_for_static_invoice( &self, payment_id: PaymentId, ) -> Result<(), Bolt12PaymentError> { @@ -7676,7 +7664,6 @@ where self.pending_outbound_payments .remove_stale_payments(duration_since_epoch, &self.pending_events); - #[cfg(async_payments)] self.check_refresh_async_receive_offer_cache(true); // Technically we don't need to do this here, but if we have holding cell entries in a @@ -11889,7 +11876,6 @@ where /// interactively building a [`StaticInvoice`] with the static invoice server. /// /// Useful for posting offers to receive payments later, such as posting an offer on a website. - #[cfg(async_payments)] pub fn get_async_receive_offer(&self) -> Result { let (offer, needs_persist) = self.flow.get_async_receive_offer()?; if needs_persist { @@ -11900,79 +11886,17 @@ where Ok(offer) } - /// Create an offer for receiving async payments as an often-offline recipient. - /// - /// Instead of using this method, it is preferable to call - /// [`Self::set_paths_to_static_invoice_server`] and retrieve the automatically built offer via - /// [`Self::get_async_receive_offer`]. - /// - /// If you want to build the [`StaticInvoice`] manually using this method instead, you MUST: - /// 1. Provide at least 1 [`BlindedMessagePath`] terminating at an always-online node that will - /// serve the [`StaticInvoice`] created from this offer on our behalf. - /// 2. Use [`Self::create_static_invoice_builder`] to create a [`StaticInvoice`] from this - /// [`Offer`] plus the returned [`Nonce`], and provide the static invoice to the - /// aforementioned always-online node. - #[cfg(async_payments)] - pub fn create_async_receive_offer_builder( - &self, message_paths_to_always_online_node: Vec, - ) -> Result<(OfferBuilder, Nonce), Bolt12SemanticError> { - let entropy = &*self.entropy_source; - self.flow.create_async_receive_offer_builder(entropy, message_paths_to_always_online_node) - } - - /// Creates a [`StaticInvoiceBuilder`] from the corresponding [`Offer`] and [`Nonce`] that were - /// created via [`Self::create_async_receive_offer_builder`]. If `relative_expiry` is unset, the - /// invoice's expiry will default to [`STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY`]. - /// - /// Instead of using this method to manually build the invoice, it is preferable to set - /// [`Self::set_paths_to_static_invoice_server`] and retrieve the automatically built offer via - /// [`Self::get_async_receive_offer`]. - #[cfg(async_payments)] - pub fn create_static_invoice_builder<'a>( - &self, offer: &'a Offer, offer_nonce: Nonce, relative_expiry: Option, - ) -> Result, Bolt12SemanticError> { - let entropy = &*self.entropy_source; - let amount_msat = offer.amount().and_then(|amount| match amount { - Amount::Bitcoin { amount_msats } => Some(amount_msats), - Amount::Currency { .. } => None, - }); - - let relative_expiry = relative_expiry.unwrap_or(STATIC_INVOICE_DEFAULT_RELATIVE_EXPIRY); - let relative_expiry_secs: u32 = relative_expiry.as_secs().try_into().unwrap_or(u32::MAX); - - let created_at = self.duration_since_epoch(); - let payment_secret = inbound_payment::create_for_spontaneous_payment( - &self.inbound_payment_key, - amount_msat, - relative_expiry_secs, - created_at.as_secs(), - None, - ) - .map_err(|()| Bolt12SemanticError::InvalidAmount)?; - - self.flow.create_static_invoice_builder( - &self.router, - entropy, - offer, - offer_nonce, - payment_secret, - relative_expiry_secs, - self.list_usable_channels(), - self.get_peers_for_blinded_path(), - ) - } - /// Sets the [`BlindedMessagePath`]s that we will use as an async recipient to interactively build /// [`Offer`]s with a static invoice server, so the server can serve [`StaticInvoice`]s to payers /// on our behalf when we're offline. /// /// This method only needs to be called once when the server first takes on the recipient as a /// client, or when the paths change, e.g. if the paths are set to expire at a particular time. - #[cfg(async_payments)] pub fn set_paths_to_static_invoice_server( &self, paths_to_static_invoice_server: Vec, ) -> Result<(), ()> { - self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server)?; + let peers = self.get_peers_for_blinded_path(); + self.flow.set_paths_to_static_invoice_server(paths_to_static_invoice_server, peers)?; let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); Ok(()) @@ -12373,7 +12297,6 @@ where /// The provided `recipient_id` must uniquely identify the recipient, and will be surfaced later /// when the recipient provides us with a static invoice to persist and serve to payers on their /// behalf. - #[cfg(async_payments)] pub fn blinded_paths_for_async_recipient( &self, recipient_id: Vec, relative_expiry: Option, ) -> Result, ()> { @@ -12381,7 +12304,6 @@ where self.flow.blinded_paths_for_async_recipient(recipient_id, relative_expiry, peers) } - #[cfg(any(test, async_payments))] pub(super) fn duration_since_epoch(&self) -> Duration { #[cfg(not(feature = "std"))] let now = Duration::from_secs(self.highest_seen_timestamp.load(Ordering::Acquire) as u64); @@ -12413,7 +12335,12 @@ where .collect::>() } - #[cfg(all(test, async_payments))] + #[cfg(test)] + pub(super) fn test_get_peers_for_blinded_path(&self) -> Vec { + self.get_peers_for_blinded_path() + } + + #[cfg(test)] /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to /// [`Router::create_blinded_payment_paths`]. pub(super) fn test_create_blinded_payment_paths( @@ -12915,9 +12842,8 @@ where // While we usually refresh the AsyncReceiveOfferCache on a timer, we also want to start // interactively building offers as soon as we can after startup. We can't start building offers - // until we have some peer connection(s) to send onion messages over, so as a minor optimization + // until we have some peer connection(s) to receive onion messages over, so as a minor optimization // refresh the cache when a peer connects. - #[cfg(async_payments)] self.check_refresh_async_receive_offer_cache(false); res } @@ -14189,7 +14115,6 @@ where log_trace!($logger, "Failed paying invoice: {:?}", e); InvoiceError::from_string(format!("{:?}", e)) }, - #[cfg(async_payments)] Err(Bolt12PaymentError::BlindedPathCreationFailed) => { let err_msg = "Failed to create a blinded path back to ourselves"; log_trace!($logger, "{}", err_msg); @@ -14222,7 +14147,6 @@ where Ok(InvreqResponseInstructions::SendStaticInvoice { recipient_id: _recipient_id, invoice_id: _invoice_id }) => { - #[cfg(async_payments)] self.pending_events.lock().unwrap().push_back((Event::StaticInvoiceRequested { recipient_id: _recipient_id, invoice_id: _invoice_id, reply_path: responder }, None)); @@ -14287,7 +14211,6 @@ where let res = self.send_payment_for_verified_bolt12_invoice(&invoice, payment_id); handle_pay_invoice_res!(res, invoice, logger); }, - #[cfg(async_payments)] OffersMessage::StaticInvoice(invoice) => { let payment_id = match context { Some(OffersContext::OutboundPayment { payment_id, .. }) => payment_id, @@ -14350,95 +14273,77 @@ where &self, _message: OfferPathsRequest, _context: AsyncPaymentsContext, _responder: Option, ) -> Option<(OfferPaths, ResponseInstruction)> { - #[cfg(async_payments)] - { - let peers = self.get_peers_for_blinded_path(); - let entropy = &*self.entropy_source; - let (message, reply_path_context) = - match self.flow.handle_offer_paths_request(_context, peers, entropy) { - Some(msg) => msg, - None => return None, - }; - _responder.map(|resp| (message, resp.respond_with_reply_path(reply_path_context))) - } - - #[cfg(not(async_payments))] - None + let peers = self.get_peers_for_blinded_path(); + let entropy = &*self.entropy_source; + let (message, reply_path_context) = + match self.flow.handle_offer_paths_request(_context, peers, entropy) { + Some(msg) => msg, + None => return None, + }; + _responder.map(|resp| (message, resp.respond_with_reply_path(reply_path_context))) } fn handle_offer_paths( &self, _message: OfferPaths, _context: AsyncPaymentsContext, _responder: Option, ) -> Option<(ServeStaticInvoice, ResponseInstruction)> { - #[cfg(async_payments)] - { - let responder = match _responder { - Some(responder) => responder, - None => return None, - }; - let (serve_static_invoice, reply_context) = match self.flow.handle_offer_paths( - _message, - _context, - responder.clone(), - self.get_peers_for_blinded_path(), - self.list_usable_channels(), - &*self.entropy_source, - &*self.router, - ) { - Some((msg, ctx)) => (msg, ctx), - None => return None, - }; - - // We cached a new pending offer, so persist the cache. - let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); + let responder = match _responder { + Some(responder) => responder, + None => return None, + }; + let (serve_static_invoice, reply_context) = match self.flow.handle_offer_paths( + _message, + _context, + responder.clone(), + self.get_peers_for_blinded_path(), + self.list_usable_channels(), + &*self.entropy_source, + &*self.router, + ) { + Some((msg, ctx)) => (msg, ctx), + None => return None, + }; - let response_instructions = responder.respond_with_reply_path(reply_context); - return Some((serve_static_invoice, response_instructions)); - } + // We cached a new pending offer, so persist the cache. + let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); - #[cfg(not(async_payments))] - return None; + let response_instructions = responder.respond_with_reply_path(reply_context); + return Some((serve_static_invoice, response_instructions)); } fn handle_serve_static_invoice( &self, _message: ServeStaticInvoice, _context: AsyncPaymentsContext, _responder: Option, ) { - #[cfg(async_payments)] - { - let responder = match _responder { - Some(resp) => resp, - None => return, - }; + let responder = match _responder { + Some(resp) => resp, + None => return, + }; - let (recipient_id, invoice_id) = - match self.flow.verify_serve_static_invoice_message(&_message, _context) { - Ok((recipient_id, inv_id)) => (recipient_id, inv_id), - Err(()) => return, - }; + let (recipient_id, invoice_id) = + match self.flow.verify_serve_static_invoice_message(&_message, _context) { + Ok((recipient_id, inv_id)) => (recipient_id, inv_id), + Err(()) => return, + }; - let mut pending_events = self.pending_events.lock().unwrap(); - pending_events.push_back(( - Event::PersistStaticInvoice { - invoice: _message.invoice, - invoice_slot: _message.invoice_slot, - recipient_id, - invoice_id, - invoice_persisted_path: responder, - }, - None, - )); - } + let mut pending_events = self.pending_events.lock().unwrap(); + pending_events.push_back(( + Event::PersistStaticInvoice { + invoice: _message.invoice, + invoice_slot: _message.invoice_slot, + recipient_id, + invoice_id, + invoice_persisted_path: responder, + }, + None, + )); } fn handle_static_invoice_persisted( &self, _message: StaticInvoicePersisted, _context: AsyncPaymentsContext, ) { - #[cfg(async_payments)] - { - let should_persist = self.flow.handle_static_invoice_persisted(_context); - if should_persist { - let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); - } + let should_persist = self.flow.handle_static_invoice_persisted(_context); + if should_persist { + let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self); } } @@ -14446,31 +14351,23 @@ where &self, _message: HeldHtlcAvailable, _context: AsyncPaymentsContext, _responder: Option, ) -> Option<(ReleaseHeldHtlc, ResponseInstruction)> { - #[cfg(async_payments)] - { - self.flow.verify_inbound_async_payment_context(_context).ok()?; - return _responder.map(|responder| (ReleaseHeldHtlc {}, responder.respond())); - } - #[cfg(not(async_payments))] - return None; + self.flow.verify_inbound_async_payment_context(_context).ok()?; + return _responder.map(|responder| (ReleaseHeldHtlc {}, responder.respond())); } fn handle_release_held_htlc(&self, _message: ReleaseHeldHtlc, _context: AsyncPaymentsContext) { - #[cfg(async_payments)] - { - let payment_id = match _context { - AsyncPaymentsContext::OutboundPayment { payment_id } => payment_id, - _ => return, - }; + let payment_id = match _context { + AsyncPaymentsContext::OutboundPayment { payment_id } => payment_id, + _ => return, + }; - if let Err(e) = self.send_payment_for_static_invoice(payment_id) { - log_trace!( - self.logger, - "Failed to release held HTLC with payment id {}: {:?}", - payment_id, - e - ); - } + if let Err(e) = self.send_payment_for_static_invoice(payment_id) { + log_trace!( + self.logger, + "Failed to release held HTLC with payment id {}: {:?}", + payment_id, + e + ); } } @@ -15310,7 +15207,7 @@ where (15, self.inbound_payment_id_secret, required), (17, in_flight_monitor_updates, option), (19, peer_storage_dir, optional_vec), - (21, self.flow.writeable_async_receive_offer_cache(), required), + (21, WithoutLength(&self.flow.writeable_async_receive_offer_cache()), required), }); Ok(()) diff --git a/lightning/src/ln/inbound_payment.rs b/lightning/src/ln/inbound_payment.rs index a7d45b896a9..f7b2a4a2b57 100644 --- a/lightning/src/ln/inbound_payment.rs +++ b/lightning/src/ln/inbound_payment.rs @@ -213,7 +213,6 @@ pub fn create_from_hash( Ok(construct_payment_secret(&iv_bytes, &metadata_bytes, &keys.metadata_key)) } -#[cfg(async_payments)] pub(crate) fn create_for_spontaneous_payment( keys: &ExpandedKey, min_value_msat: Option, invoice_expiry_delta_secs: u32, current_time: u64, min_final_cltv_expiry_delta: Option, diff --git a/lightning/src/ln/mod.rs b/lightning/src/ln/mod.rs index a513582cb64..3ca3b765dc2 100644 --- a/lightning/src/ln/mod.rs +++ b/lightning/src/ln/mod.rs @@ -60,7 +60,7 @@ pub use onion_utils::process_onion_failure; #[cfg(fuzzing)] pub use onion_utils::AttributionData; -#[cfg(all(test, async_payments))] +#[cfg(test)] #[allow(unused_mut)] mod async_payments_tests; #[cfg(test)] diff --git a/lightning/src/ln/offers_tests.rs b/lightning/src/ln/offers_tests.rs index ba9858be197..6c56ecc4270 100644 --- a/lightning/src/ln/offers_tests.rs +++ b/lightning/src/ln/offers_tests.rs @@ -227,7 +227,6 @@ pub(super) fn extract_invoice_request<'a, 'b, 'c>( Ok(PeeledOnion::Offers(message, _, reply_path)) => match message { OffersMessage::InvoiceRequest(invoice_request) => (invoice_request, reply_path.unwrap()), OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice), - #[cfg(async_payments)] OffersMessage::StaticInvoice(invoice) => panic!("Unexpected static invoice: {:?}", invoice), OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error), }, @@ -242,7 +241,6 @@ fn extract_invoice<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage) Ok(PeeledOnion::Offers(message, _, reply_path)) => match message { OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request), OffersMessage::Invoice(invoice) => (invoice, reply_path.unwrap()), - #[cfg(async_payments)] OffersMessage::StaticInvoice(invoice) => panic!("Unexpected static invoice: {:?}", invoice), OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error), }, @@ -259,7 +257,6 @@ fn extract_invoice_error<'a, 'b, 'c>( Ok(PeeledOnion::Offers(message, _, _)) => match message { OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request), OffersMessage::Invoice(invoice) => panic!("Unexpected invoice: {:?}", invoice), - #[cfg(async_payments)] OffersMessage::StaticInvoice(invoice) => panic!("Unexpected invoice: {:?}", invoice), OffersMessage::InvoiceError(error) => error, }, @@ -1235,7 +1232,7 @@ fn pays_bolt12_invoice_asynchronously() { let onion_message = alice.onion_messenger.next_onion_message_for_peer(bob_id).unwrap(); bob.onion_messenger.handle_onion_message(alice_id, &onion_message); - // Re-process the same onion message to ensure idempotency — + // Re-process the same onion message to ensure idempotency — // we should not generate a duplicate `InvoiceReceived` event. bob.onion_messenger.handle_onion_message(alice_id, &onion_message); diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index ffc3ee4ae19..476964db889 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -20,7 +20,7 @@ use crate::ln::channel_state::ChannelDetails; use crate::ln::channelmanager::{EventCompletionAction, HTLCSource, PaymentId}; use crate::ln::onion_utils; use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason}; -use crate::offers::invoice::Bolt12Invoice; +use crate::offers::invoice::{Bolt12Invoice, DerivedSigningPubkey, InvoiceBuilder}; use crate::offers::invoice_request::InvoiceRequest; use crate::offers::nonce::Nonce; use crate::offers::static_invoice::StaticInvoice; @@ -37,9 +37,6 @@ use crate::util::ser::ReadableArgs; #[cfg(feature = "std")] use crate::util::time::Instant; -#[cfg(async_payments)] -use crate::offers::invoice::{DerivedSigningPubkey, InvoiceBuilder}; - use core::fmt::{self, Display, Formatter}; use core::ops::Deref; use core::sync::atomic::{AtomicBool, Ordering}; @@ -54,12 +51,11 @@ use crate::sync::Mutex; /// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7; -#[cfg(async_payments)] /// The default relative expiration to wait for a pending outbound HTLC to a often-offline /// payee to fulfill. const ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY: Duration = Duration::from_secs(60 * 60 * 24 * 7); -#[cfg(all(async_payments, test))] +#[cfg(test)] pub(crate) const TEST_ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY: Duration = ASYNC_PAYMENT_TIMEOUT_RELATIVE_EXPIRY; @@ -637,7 +633,6 @@ pub enum Bolt12PaymentError { UnknownRequiredFeatures, /// The invoice was valid for the corresponding [`PaymentId`], but sending the payment failed. SendingFailed(RetryableSendFailure), - #[cfg(async_payments)] /// Failed to create a blinded path back to ourselves. /// /// We attempted to initiate payment to a [`StaticInvoice`] but failed to create a reply path for @@ -1111,7 +1106,6 @@ impl OutboundPayments { Ok(()) } - #[cfg(async_payments)] #[rustfmt::skip] pub(super) fn static_invoice_received( &self, invoice: &StaticInvoice, payment_id: PaymentId, features: Bolt12InvoiceFeatures, @@ -1201,7 +1195,6 @@ impl OutboundPayments { }; } - #[cfg(async_payments)] #[rustfmt::skip] pub(super) fn send_payment_for_static_invoice< R: Deref, ES: Deref, NS: Deref, NL: Deref, IH, SP, L: Deref diff --git a/lightning/src/offers/async_receive_offer_cache.rs b/lightning/src/offers/async_receive_offer_cache.rs index 3a7857ffd0f..1b1078dbb23 100644 --- a/lightning/src/offers/async_receive_offer_cache.rs +++ b/lightning/src/offers/async_receive_offer_cache.rs @@ -11,7 +11,7 @@ //! server as an async recipient. The static invoice server will serve the resulting invoices to //! payers on our behalf when we're offline. -use crate::blinded_path::message::BlindedMessagePath; +use crate::blinded_path::message::{AsyncPaymentsContext, BlindedMessagePath}; use crate::io; use crate::io::Read; use crate::ln::msgs::DecodeError; @@ -22,24 +22,24 @@ use crate::prelude::*; use crate::util::ser::{Readable, Writeable, Writer}; use core::time::Duration; -#[cfg(async_payments)] -use crate::blinded_path::message::AsyncPaymentsContext; - /// The status of this offer in the cache. #[derive(Clone, PartialEq)] enum OfferStatus { /// This offer has been returned to the user from the cache, so it needs to be stored until it /// expires and its invoice needs to be kept updated. - Used, + Used { + /// The creation time of the invoice that was last confirmed as persisted by the server. Useful + /// to know when the invoice needs refreshing. + invoice_created_at: Duration, + }, /// This offer has not yet been returned to the user, and is safe to replace to ensure we always /// have a maximally fresh offer. We always want to have at least 1 offer in this state, /// preferably a few so we can respond to user requests for new offers without returning the same /// one multiple times. Returning a new offer each time is better for privacy. Ready { - /// If this offer's invoice has been persisted for some time, it's safe to replace to ensure we - /// always have the freshest possible offer available when the user goes to pull an offer from - /// the cache. - invoice_confirmed_persisted_at: Duration, + /// The creation time of the invoice that was last confirmed as persisted by the server. Useful + /// to know when the invoice needs refreshing. + invoice_created_at: Duration, }, /// This offer's invoice is not yet confirmed as persisted by the static invoice server, so it is /// not yet ready to receive payments. @@ -49,6 +49,9 @@ enum OfferStatus { #[derive(Clone)] struct AsyncReceiveOffer { offer: Offer, + /// The time as duration since the Unix epoch at which this offer was created. Useful when + /// refreshing unused offers. + created_at: Duration, /// Whether this offer is used, ready for use, or pending invoice persistence with the static /// invoice server. status: OfferStatus, @@ -62,9 +65,11 @@ struct AsyncReceiveOffer { } impl_writeable_tlv_based_enum!(OfferStatus, - (0, Used) => {}, + (0, Used) => { + (0, invoice_created_at, required), + }, (1, Ready) => { - (0, invoice_confirmed_persisted_at, required), + (0, invoice_created_at, required), }, (2, Pending) => {}, ); @@ -74,6 +79,7 @@ impl_writeable_tlv_based!(AsyncReceiveOffer, { (2, offer_nonce, required), (4, status, required), (6, update_static_invoice_path, required), + (8, created_at, required), }); /// If we are an often-offline recipient, we'll want to interactively build offers and static @@ -143,8 +149,8 @@ impl AsyncReceiveOfferCache { } } - pub(super) fn paths_to_static_invoice_server(&self) -> Vec { - self.paths_to_static_invoice_server.clone() + pub(super) fn paths_to_static_invoice_server(&self) -> &[BlindedMessagePath] { + &self.paths_to_static_invoice_server[..] } /// Sets the [`BlindedMessagePath`]s that we will use as an async recipient to interactively build @@ -152,8 +158,7 @@ impl AsyncReceiveOfferCache { /// on our behalf when we're offline. /// /// [`StaticInvoice`]: crate::offers::static_invoice::StaticInvoice - #[cfg(async_payments)] - pub fn set_paths_to_static_invoice_server( + pub(crate) fn set_paths_to_static_invoice_server( &mut self, paths_to_static_invoice_server: Vec, ) -> Result<(), ()> { if paths_to_static_invoice_server.is_empty() { @@ -172,66 +177,66 @@ impl AsyncReceiveOfferCache { // The target number of offers we want to have cached at any given time, to mitigate too much // reuse of the same offer while also limiting the amount of space our offers take up on the // server's end. -#[cfg(async_payments)] const MAX_CACHED_OFFERS_TARGET: usize = 10; // The max number of times we'll attempt to request offer paths per timer tick. -#[cfg(async_payments)] const MAX_UPDATE_ATTEMPTS: u8 = 3; -// If we have an offer that is replaceable and its invoice was confirmed as persisted more than 2 -// hours ago, we can go ahead and refresh it because we always want to have the freshest offer -// possible when a user goes to retrieve a cached offer. +// If we have an offer that is replaceable and is more than 2 hours old, we can go ahead and refresh +// it because we always want to have the freshest offer possible when a user goes to retrieve a +// cached offer. // // We avoid replacing unused offers too quickly -- this prevents the case where we send multiple // invoices from different offers competing for the same slot to the server, messages are received // delayed or out-of-order, and we end up providing an offer to the user that the server just // deleted and replaced. -#[cfg(async_payments)] const OFFER_REFRESH_THRESHOLD: Duration = Duration::from_secs(2 * 60 * 60); +/// Invoices stored with the static invoice server may become stale due to outdated channel and fee +/// info, so they should be updated regularly. +const INVOICE_REFRESH_THRESHOLD: Duration = Duration::from_secs(2 * 60 * 60); + // Require offer paths that we receive to last at least 3 months. -#[cfg(async_payments)] const MIN_OFFER_PATHS_RELATIVE_EXPIRY_SECS: u64 = 3 * 30 * 24 * 60 * 60; -#[cfg(all(test, async_payments))] +#[cfg(test)] pub(crate) const TEST_MAX_CACHED_OFFERS_TARGET: usize = MAX_CACHED_OFFERS_TARGET; -#[cfg(all(test, async_payments))] +#[cfg(test)] pub(crate) const TEST_MAX_UPDATE_ATTEMPTS: u8 = MAX_UPDATE_ATTEMPTS; -#[cfg(all(test, async_payments))] +#[cfg(test)] pub(crate) const TEST_OFFER_REFRESH_THRESHOLD: Duration = OFFER_REFRESH_THRESHOLD; -#[cfg(all(test, async_payments))] +#[cfg(test)] +pub(crate) const TEST_INVOICE_REFRESH_THRESHOLD: Duration = INVOICE_REFRESH_THRESHOLD; +#[cfg(test)] pub(crate) const TEST_MIN_OFFER_PATHS_RELATIVE_EXPIRY_SECS: u64 = MIN_OFFER_PATHS_RELATIVE_EXPIRY_SECS; -#[cfg(async_payments)] impl AsyncReceiveOfferCache { /// Retrieve a cached [`Offer`] for receiving async payments as an often-offline recipient, as /// well as returning a bool indicating whether the cache needs to be re-persisted. /// // We need to re-persist the cache if a fresh offer was just marked as used to ensure we continue // to keep this offer's invoice updated and don't replace it with the server. - pub fn get_async_receive_offer( + pub(crate) fn get_async_receive_offer( &mut self, duration_since_epoch: Duration, ) -> Result<(Offer, bool), ()> { self.prune_expired_offers(duration_since_epoch, false); - // Find the freshest unused offer, where "freshness" is based on when the invoice was confirmed - // persisted by the server. See `OfferStatus::Ready`. + // Find the freshest unused offer. See `OfferStatus::Ready`. let newest_unused_offer_opt = self - .unused_offers() - .max_by(|(_, _, persisted_at_a), (_, _, persisted_at_b)| { - persisted_at_a.cmp(&persisted_at_b) - }) - .map(|(idx, offer, _)| (idx, offer.offer.clone())); - if let Some((idx, newest_ready_offer)) = newest_unused_offer_opt { - self.offers[idx].as_mut().map(|offer| offer.status = OfferStatus::Used); + .unused_ready_offers() + .max_by(|(_, offer_a, _), (_, offer_b, _)| offer_a.created_at.cmp(&offer_b.created_at)) + .map(|(idx, offer, invoice_created_at)| (idx, offer.offer.clone(), invoice_created_at)); + if let Some((idx, newest_ready_offer, invoice_created_at)) = newest_unused_offer_opt { + self.offers[idx] + .as_mut() + .map(|offer| offer.status = OfferStatus::Used { invoice_created_at }); return Ok((newest_ready_offer, true)); } // If no unused offers are available, return the used offer with the latest absolute expiry self.offers_with_idx() - .filter(|(_, offer)| matches!(offer.status, OfferStatus::Used)) + .filter(|(_, offer)| matches!(offer.status, OfferStatus::Used { .. })) .max_by(|a, b| { let abs_expiry_a = a.1.offer.absolute_expiry().unwrap_or(Duration::MAX); let abs_expiry_b = b.1.offer.absolute_expiry().unwrap_or(Duration::MAX); @@ -316,6 +321,7 @@ impl AsyncReceiveOfferCache { Some(offer_opt) => { *offer_opt = Some(AsyncReceiveOffer { offer, + created_at: duration_since_epoch, offer_nonce, status: OfferStatus::Pending, update_static_invoice_path, @@ -343,9 +349,9 @@ impl AsyncReceiveOfferCache { } // If all of our offers are already used or pending, then none are available to be replaced - let no_replaceable_offers = self - .offers_with_idx() - .all(|(_, offer)| matches!(offer.status, OfferStatus::Used | OfferStatus::Pending)); + let no_replaceable_offers = self.offers_with_idx().all(|(_, offer)| { + matches!(offer.status, OfferStatus::Used { .. } | OfferStatus::Pending) + }); if no_replaceable_offers { return None; } @@ -355,7 +361,7 @@ impl AsyncReceiveOfferCache { let num_payable_offers = self .offers_with_idx() .filter(|(_, offer)| { - matches!(offer.status, OfferStatus::Used | OfferStatus::Ready { .. }) + matches!(offer.status, OfferStatus::Used { .. } | OfferStatus::Ready { .. }) }) .count(); if num_payable_offers <= 1 { @@ -365,14 +371,10 @@ impl AsyncReceiveOfferCache { // Filter for unused offers where longer than OFFER_REFRESH_THRESHOLD time has passed since they // were last updated, so they are stale enough to warrant replacement. let awhile_ago = duration_since_epoch.saturating_sub(OFFER_REFRESH_THRESHOLD); - self.unused_offers() - .filter(|(_, _, invoice_confirmed_persisted_at)| { - *invoice_confirmed_persisted_at < awhile_ago - }) + self.unused_ready_offers() + .filter(|(_, offer, _)| offer.created_at < awhile_ago) // Get the stalest offer and return its index - .min_by(|(_, _, persisted_at_a), (_, _, persisted_at_b)| { - persisted_at_a.cmp(&persisted_at_b) - }) + .min_by(|(_, offer_a, _), (_, offer_b, _)| offer_a.created_at.cmp(&offer_b.created_at)) .map(|(idx, _, _)| idx) } @@ -387,13 +389,11 @@ impl AsyncReceiveOfferCache { }) } - /// Returns an iterator over (offer_idx, offer, invoice_confirmed_persisted_at) - /// where all returned offers are [`OfferStatus::Ready`] - fn unused_offers(&self) -> impl Iterator { + /// Returns an iterator over (offer_idx, offer, invoice_created_at) where all returned offers are + /// [`OfferStatus::Ready`] + fn unused_ready_offers(&self) -> impl Iterator { self.offers_with_idx().filter_map(|(idx, offer)| match offer.status { - OfferStatus::Ready { invoice_confirmed_persisted_at } => { - Some((idx, offer, invoice_confirmed_persisted_at)) - }, + OfferStatus::Ready { invoice_created_at } => Some((idx, offer, invoice_created_at)), _ => None, }) } @@ -413,13 +413,21 @@ impl AsyncReceiveOfferCache { /// Returns an iterator over the list of cached offers where we need to send an updated invoice to /// the static invoice server. pub(super) fn offers_needing_invoice_refresh( - &self, + &self, duration_since_epoch: Duration, ) -> impl Iterator { // For any offers which are either in use or pending confirmation by the server, we should send // them a fresh invoice on each timer tick. - self.offers_with_idx().filter_map(|(idx, offer)| { - let needs_invoice_update = - offer.status == OfferStatus::Used || offer.status == OfferStatus::Pending; + self.offers_with_idx().filter_map(move |(idx, offer)| { + let needs_invoice_update = match offer.status { + OfferStatus::Used { invoice_created_at } => { + invoice_created_at.saturating_add(INVOICE_REFRESH_THRESHOLD) + < duration_since_epoch + }, + OfferStatus::Pending => true, + // Don't bother updating `Ready` offers' invoices on a timer because the offers themselves + // are regularly rotated anyway. + OfferStatus::Ready { .. } => false, + }; if needs_invoice_update { let offer_slot = idx.try_into().unwrap_or(u16::MAX); Some(( @@ -442,15 +450,10 @@ impl AsyncReceiveOfferCache { /// is needed. /// /// [`StaticInvoicePersisted`]: crate::onion_message::async_payments::StaticInvoicePersisted - pub(super) fn static_invoice_persisted( - &mut self, context: AsyncPaymentsContext, duration_since_epoch: Duration, - ) -> bool { - let offer_id = match context { - AsyncPaymentsContext::StaticInvoicePersisted { path_absolute_expiry, offer_id } => { - if duration_since_epoch > path_absolute_expiry { - return false; - } - offer_id + pub(super) fn static_invoice_persisted(&mut self, context: AsyncPaymentsContext) -> bool { + let (invoice_created_at, offer_id) = match context { + AsyncPaymentsContext::StaticInvoicePersisted { invoice_created_at, offer_id } => { + (invoice_created_at, offer_id) }, _ => return false, }; @@ -458,14 +461,14 @@ impl AsyncReceiveOfferCache { let mut offers = self.offers.iter_mut(); let offer_entry = offers.find(|o| o.as_ref().map_or(false, |o| o.offer.id() == offer_id)); if let Some(Some(ref mut offer)) = offer_entry { - if offer.status == OfferStatus::Used { - // We succeeded in updating the invoice for a used offer, no re-persistence of the cache - // needed - return false; + match offer.status { + OfferStatus::Used { invoice_created_at: ref mut inv_created_at } + | OfferStatus::Ready { invoice_created_at: ref mut inv_created_at } => { + *inv_created_at = core::cmp::min(invoice_created_at, *inv_created_at); + }, + OfferStatus::Pending => offer.status = OfferStatus::Ready { invoice_created_at }, } - offer.status = - OfferStatus::Ready { invoice_confirmed_persisted_at: duration_since_epoch }; return true; } @@ -477,7 +480,7 @@ impl AsyncReceiveOfferCache { self.offers_with_idx() .filter_map(|(_, offer)| { if matches!(offer.status, OfferStatus::Ready { .. }) - || matches!(offer.status, OfferStatus::Used) + || matches!(offer.status, OfferStatus::Used { .. }) { Some(offer.offer.clone()) } else { diff --git a/lightning/src/offers/flow.rs b/lightning/src/offers/flow.rs index cd3f95e0841..91c506eb4a9 100644 --- a/lightning/src/offers/flow.rs +++ b/lightning/src/offers/flow.rs @@ -19,11 +19,11 @@ use bitcoin::constants::ChainHash; use bitcoin::secp256k1::{self, PublicKey, Secp256k1}; use crate::blinded_path::message::{ - BlindedMessagePath, MessageContext, MessageForwardNode, OffersContext, + AsyncPaymentsContext, BlindedMessagePath, MessageContext, MessageForwardNode, OffersContext, }; use crate::blinded_path::payment::{ - BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext, PaymentConstraints, - PaymentContext, UnauthenticatedReceiveTlvs, + AsyncBolt12OfferContext, BlindedPaymentPath, Bolt12OfferContext, Bolt12RefundContext, + PaymentConstraints, PaymentContext, UnauthenticatedReceiveTlvs, }; use crate::chain::channelmonitor::LATENCY_GRACE_PERIOD_BLOCKS; @@ -44,32 +44,26 @@ use crate::offers::invoice_request::{ InvoiceRequest, InvoiceRequestBuilder, VerifiedInvoiceRequest, }; use crate::offers::nonce::Nonce; -use crate::offers::offer::{DerivedMetadata, Offer, OfferBuilder}; +use crate::offers::offer::{Amount, DerivedMetadata, Offer, OfferBuilder}; use crate::offers::parse::Bolt12SemanticError; use crate::offers::refund::{Refund, RefundBuilder}; -use crate::onion_message::async_payments::AsyncPaymentsMessage; -use crate::onion_message::messenger::{Destination, MessageRouter, MessageSendInstructions}; +use crate::onion_message::async_payments::{ + AsyncPaymentsMessage, HeldHtlcAvailable, OfferPaths, OfferPathsRequest, ServeStaticInvoice, + StaticInvoicePersisted, +}; +use crate::onion_message::messenger::{ + Destination, MessageRouter, MessageSendInstructions, Responder, +}; use crate::onion_message::offers::OffersMessage; use crate::onion_message::packet::OnionMessageContents; use crate::routing::router::Router; use crate::sign::{EntropySource, NodeSigner, ReceiveAuthKey}; + +use crate::offers::static_invoice::{StaticInvoice, StaticInvoiceBuilder}; use crate::sync::{Mutex, RwLock}; use crate::types::payment::{PaymentHash, PaymentSecret}; use crate::util::ser::Writeable; -#[cfg(async_payments)] -use { - crate::blinded_path::message::AsyncPaymentsContext, - crate::blinded_path::payment::AsyncBolt12OfferContext, - crate::offers::offer::Amount, - crate::offers::static_invoice::{StaticInvoice, StaticInvoiceBuilder}, - crate::onion_message::async_payments::{ - HeldHtlcAvailable, OfferPaths, OfferPathsRequest, ServeStaticInvoice, - StaticInvoicePersisted, - }, - crate::onion_message::messenger::Responder, -}; - #[cfg(feature = "dnssec")] use { crate::blinded_path::message::DNSResolverContext, @@ -104,9 +98,6 @@ where pending_async_payments_messages: Mutex>, async_receive_offer_cache: Mutex, - /// Blinded paths used to request offer paths from the static invoice server, if we are an async - /// recipient. - paths_to_static_invoice_server: Mutex>, #[cfg(feature = "dnssec")] pub(crate) hrn_resolver: OMNameResolver, @@ -146,19 +137,17 @@ where pending_dns_onion_messages: Mutex::new(Vec::new()), async_receive_offer_cache: Mutex::new(AsyncReceiveOfferCache::new()), - paths_to_static_invoice_server: Mutex::new(Vec::new()), } } /// If we are an async recipient, on startup we'll interactively build offers and static invoices /// with an always-online node that will serve static invoices on our behalf. Once the offer is /// built and the static invoice is confirmed as persisted by the server, the underlying - /// [`AsyncReceiveOfferCache`] should be persisted so we remember the offers we've built. - pub(crate) fn with_async_payments_offers_cache( + /// [`AsyncReceiveOfferCache`] should be persisted using + /// [`Self::writeable_async_receive_offer_cache`] so we remember the offers we've built. + pub fn with_async_payments_offers_cache( mut self, async_receive_offer_cache: AsyncReceiveOfferCache, ) -> Self { - self.paths_to_static_invoice_server = - Mutex::new(async_receive_offer_cache.paths_to_static_invoice_server()); self.async_receive_offer_cache = Mutex::new(async_receive_offer_cache); self } @@ -167,21 +156,24 @@ where /// [`Offer`]s with a static invoice server, so the server can serve [`StaticInvoice`]s to payers /// on our behalf when we're offline. /// + /// This method will also send out messages initiating async offer creation to the static invoice + /// server, if any peers are connected. + /// /// This method only needs to be called once when the server first takes on the recipient as a /// client, or when the paths change, e.g. if the paths are set to expire at a particular time. - #[cfg(async_payments)] - pub(crate) fn set_paths_to_static_invoice_server( + pub fn set_paths_to_static_invoice_server( &self, paths_to_static_invoice_server: Vec, + peers: Vec, ) -> Result<(), ()> { - // Store the paths in the async receive cache so they are persisted with the cache, but also - // store them in-memory in the `OffersMessageFlow` so the flow has access to them when building - // onion messages to send to the static invoice server, without introducing undesirable lock - // dependencies with the cache. - *self.paths_to_static_invoice_server.lock().unwrap() = - paths_to_static_invoice_server.clone(); - let mut cache = self.async_receive_offer_cache.lock().unwrap(); - cache.set_paths_to_static_invoice_server(paths_to_static_invoice_server) + cache.set_paths_to_static_invoice_server(paths_to_static_invoice_server.clone())?; + core::mem::drop(cache); + + // We'll only fail here if no peers are connected yet for us to create reply paths to outbound + // offer_paths_requests, so ignore the error. + let _ = self.check_refresh_async_offers(peers, false); + + Ok(()) } /// Gets the node_id held by this [`OffersMessageFlow`]` @@ -193,7 +185,6 @@ where self.receive_auth_key } - #[cfg(async_payments)] fn duration_since_epoch(&self) -> Duration { #[cfg(not(feature = "std"))] let now = Duration::from_secs(self.highest_seen_timestamp.load(Ordering::Acquire) as u64); @@ -243,7 +234,6 @@ where /// The maximum size of a received [`StaticInvoice`] before we'll fail verification in /// [`OffersMessageFlow::verify_serve_static_invoice_message]. -#[cfg(async_payments)] pub const MAX_STATIC_INVOICE_SIZE_BYTES: usize = 5 * 1024; /// Defines the maximum number of [`OffersMessage`] including different reply paths to be sent @@ -253,22 +243,20 @@ pub const MAX_STATIC_INVOICE_SIZE_BYTES: usize = 5 * 1024; /// even if multiple invoices are received. const OFFERS_MESSAGE_REQUEST_LIMIT: usize = 10; -#[cfg(all(async_payments, test))] +#[cfg(test)] pub(crate) const TEST_OFFERS_MESSAGE_REQUEST_LIMIT: usize = OFFERS_MESSAGE_REQUEST_LIMIT; /// The default relative expiry for reply paths where a quick response is expected and the reply /// path is single-use. -#[cfg(async_payments)] const TEMP_REPLY_PATH_RELATIVE_EXPIRY: Duration = Duration::from_secs(2 * 60 * 60); -#[cfg(all(async_payments, test))] +#[cfg(test)] pub(crate) const TEST_TEMP_REPLY_PATH_RELATIVE_EXPIRY: Duration = TEMP_REPLY_PATH_RELATIVE_EXPIRY; // Default to async receive offers and the paths used to update them lasting one year. -#[cfg(async_payments)] const DEFAULT_ASYNC_RECEIVE_OFFER_EXPIRY: Duration = Duration::from_secs(365 * 24 * 60 * 60); -#[cfg(all(async_payments, test))] +#[cfg(test)] pub(crate) const TEST_DEFAULT_ASYNC_RECEIVE_OFFER_EXPIRY: Duration = DEFAULT_ASYNC_RECEIVE_OFFER_EXPIRY; @@ -285,8 +273,7 @@ where /// [`Self::set_paths_to_static_invoice_server`]. /// /// Errors if blinded path creation fails or the provided `recipient_id` is larger than 1KiB. - #[cfg(async_payments)] - pub(crate) fn blinded_paths_for_async_recipient( + pub fn blinded_paths_for_async_recipient( &self, recipient_id: Vec, relative_expiry: Option, peers: Vec, ) -> Result, ()> { @@ -361,7 +348,7 @@ where ) } - #[cfg(all(test, async_payments))] + #[cfg(test)] /// Creates multi-hop blinded payment paths for the given `amount_msats` by delegating to /// [`Router::create_blinded_payment_paths`]. pub(crate) fn test_create_blinded_payment_paths( @@ -446,7 +433,6 @@ where let nonce = match context { None if invoice_request.metadata().is_some() => None, Some(OffersContext::InvoiceRequest { nonce }) => Some(nonce), - #[cfg(async_payments)] Some(OffersContext::StaticInvoiceRequested { recipient_id, invoice_id, @@ -508,7 +494,6 @@ where /// /// Returns `Err(())` if: /// - The inbound payment context has expired. - #[cfg(async_payments)] pub fn verify_inbound_async_payment_context( &self, context: AsyncPaymentsContext, ) -> Result<(), ()> { @@ -624,7 +609,6 @@ where /// 2. Use [`Self::create_static_invoice_builder`] to create a [`StaticInvoice`] from this /// [`Offer`] plus the returned [`Nonce`], and provide the static invoice to the /// aforementioned always-online node. - #[cfg(async_payments)] pub fn create_async_receive_offer_builder( &self, entropy_source: ES, message_paths_to_always_online_node: Vec, ) -> Result<(OfferBuilder, Nonce), Bolt12SemanticError> @@ -793,7 +777,6 @@ where /// Creates a [`StaticInvoiceBuilder`] from the corresponding [`Offer`] and [`Nonce`] that were /// created via [`Self::create_async_receive_offer_builder`]. - #[cfg(async_payments)] pub fn create_static_invoice_builder<'a, ES: Deref, R: Deref>( &self, router: &R, entropy_source: ES, offer: &'a Offer, offer_nonce: Nonce, payment_secret: PaymentSecret, relative_expiry_secs: u32, @@ -1113,9 +1096,10 @@ where Ok(()) } - /// Forwards a [`StaticInvoice`] over the provided `responder`. - #[cfg(async_payments)] - pub(crate) fn enqueue_static_invoice( + /// Forwards a [`StaticInvoice`] over the provided [`Responder`] in response to an + /// [`InvoiceRequest`] that we as a static invoice server received on behalf of an often-offline + /// recipient. + pub fn enqueue_static_invoice( &self, invoice: StaticInvoice, responder: Responder, ) -> Result<(), Bolt12SemanticError> { let duration_since_epoch = self.duration_since_epoch(); @@ -1143,7 +1127,6 @@ where /// /// [`ReleaseHeldHtlc`]: crate::onion_message::async_payments::ReleaseHeldHtlc /// [`supports_onion_messages`]: crate::types::features::Features::supports_onion_messages - #[cfg(async_payments)] pub fn enqueue_held_htlc_available( &self, invoice: &StaticInvoice, payment_id: PaymentId, peers: Vec, ) -> Result<(), Bolt12SemanticError> { @@ -1226,13 +1209,15 @@ where /// Retrieve an [`Offer`] for receiving async payments as an often-offline recipient. Will only /// return an offer if [`Self::set_paths_to_static_invoice_server`] was called and we succeeded in /// interactively building a [`StaticInvoice`] with the static invoice server. - #[cfg(async_payments)] - pub(crate) fn get_async_receive_offer(&self) -> Result<(Offer, bool), ()> { + /// + /// Returns the requested offer as well as a bool indicating whether the cache needs to be + /// persisted using [`Self::writeable_async_receive_offer_cache`]. + pub fn get_async_receive_offer(&self) -> Result<(Offer, bool), ()> { let mut cache = self.async_receive_offer_cache.lock().unwrap(); cache.get_async_receive_offer(self.duration_since_epoch()) } - #[cfg(all(test, async_payments))] + #[cfg(test)] pub(crate) fn test_get_async_receive_offers(&self) -> Vec { self.async_receive_offer_cache.lock().unwrap().test_get_payable_offers() } @@ -1248,8 +1233,7 @@ where /// the cache can self-regulate the number of messages sent out. /// /// Errors if we failed to create blinded reply paths when sending an [`OfferPathsRequest`] message. - #[cfg(async_payments)] - pub(crate) fn check_refresh_async_receive_offer_cache( + pub fn check_refresh_async_receive_offer_cache( &self, peers: Vec, usable_channels: Vec, entropy: ES, router: R, timer_tick_occurred: bool, ) -> Result<(), ()> @@ -1258,74 +1242,78 @@ where R::Target: Router, { // Terminate early if this node does not intend to receive async payments. - if self.paths_to_static_invoice_server.lock().unwrap().is_empty() { - return Ok(()); + { + let cache = self.async_receive_offer_cache.lock().unwrap(); + if cache.paths_to_static_invoice_server().is_empty() { + return Ok(()); + } + } + + self.check_refresh_async_offers(peers.clone(), timer_tick_occurred)?; + + if timer_tick_occurred { + self.check_refresh_static_invoices(peers, usable_channels, entropy, router); } + Ok(()) + } + + fn check_refresh_async_offers( + &self, peers: Vec, timer_tick_occurred: bool, + ) -> Result<(), ()> { let duration_since_epoch = self.duration_since_epoch(); + let mut cache = self.async_receive_offer_cache.lock().unwrap(); // Update the cache to remove expired offers, and check to see whether we need new offers to be // interactively built with the static invoice server. - let needs_new_offers = self - .async_receive_offer_cache - .lock() - .unwrap() - .prune_expired_offers(duration_since_epoch, timer_tick_occurred); + let needs_new_offers = + cache.prune_expired_offers(duration_since_epoch, timer_tick_occurred); + if !needs_new_offers { + return Ok(()); + } // If we need new offers, send out offer paths request messages to the static invoice server. - if needs_new_offers { - let context = MessageContext::AsyncPayments(AsyncPaymentsContext::OfferPaths { - path_absolute_expiry: duration_since_epoch - .saturating_add(TEMP_REPLY_PATH_RELATIVE_EXPIRY), - }); - let reply_paths = match self.create_blinded_paths(peers.clone(), context) { - Ok(paths) => paths, - Err(()) => { - return Err(()); - }, - }; - - // We can't fail past this point, so indicate to the cache that we've requested new offers. - self.async_receive_offer_cache.lock().unwrap().new_offers_requested(); + let context = MessageContext::AsyncPayments(AsyncPaymentsContext::OfferPaths { + path_absolute_expiry: duration_since_epoch + .saturating_add(TEMP_REPLY_PATH_RELATIVE_EXPIRY), + }); + let reply_paths = match self.create_blinded_paths(peers, context) { + Ok(paths) => paths, + Err(()) => { + return Err(()); + }, + }; - let mut pending_async_payments_messages = - self.pending_async_payments_messages.lock().unwrap(); - let message = AsyncPaymentsMessage::OfferPathsRequest(OfferPathsRequest {}); - enqueue_onion_message_with_reply_paths( - message, - &self.paths_to_static_invoice_server.lock().unwrap()[..], - reply_paths, - &mut pending_async_payments_messages, - ); - } + // We can't fail past this point, so indicate to the cache that we've requested new offers. + cache.new_offers_requested(); - if timer_tick_occurred { - self.check_refresh_static_invoices( - peers, - usable_channels, - duration_since_epoch, - entropy, - router, - ); - } + let mut pending_async_payments_messages = + self.pending_async_payments_messages.lock().unwrap(); + let message = AsyncPaymentsMessage::OfferPathsRequest(OfferPathsRequest {}); + enqueue_onion_message_with_reply_paths( + message, + cache.paths_to_static_invoice_server(), + reply_paths, + &mut pending_async_payments_messages, + ); Ok(()) } /// Enqueue onion messages that will used to request invoice refresh from the static invoice /// server, based on the offers provided by the cache. - #[cfg(async_payments)] fn check_refresh_static_invoices( - &self, peers: Vec, usable_channels: Vec, - duration_since_epoch: Duration, entropy: ES, router: R, + &self, peers: Vec, usable_channels: Vec, entropy: ES, + router: R, ) where ES::Target: EntropySource, R::Target: Router, { let mut serve_static_invoice_msgs = Vec::new(); { + let duration_since_epoch = self.duration_since_epoch(); let cache = self.async_receive_offer_cache.lock().unwrap(); - for offer_and_metadata in cache.offers_needing_invoice_refresh() { + for offer_and_metadata in cache.offers_needing_invoice_refresh(duration_since_epoch) { let (offer, offer_nonce, slot_number, update_static_invoice_path) = offer_and_metadata; @@ -1342,10 +1330,8 @@ where }; let reply_path_context = { - let path_absolute_expiry = - duration_since_epoch.saturating_add(TEMP_REPLY_PATH_RELATIVE_EXPIRY); MessageContext::AsyncPayments(AsyncPaymentsContext::StaticInvoicePersisted { - path_absolute_expiry, + invoice_created_at: invoice.created_at(), offer_id: offer.id(), }) }; @@ -1384,8 +1370,7 @@ where /// Handles an incoming [`OfferPathsRequest`] onion message from an often-offline recipient who /// wants us (the static invoice server) to serve [`StaticInvoice`]s to payers on their behalf. /// Sends out [`OfferPaths`] onion messages in response. - #[cfg(async_payments)] - pub(crate) fn handle_offer_paths_request( + pub fn handle_offer_paths_request( &self, context: AsyncPaymentsContext, peers: Vec, entropy_source: ES, ) -> Option<(OfferPaths, MessageContext)> where @@ -1447,8 +1432,7 @@ where /// /// Returns `None` if we have enough offers cached already, verification of `message` fails, or we /// fail to create blinded paths. - #[cfg(async_payments)] - pub(crate) fn handle_offer_paths( + pub fn handle_offer_paths( &self, message: OfferPaths, context: AsyncPaymentsContext, responder: Responder, peers: Vec, usable_channels: Vec, entropy: ES, router: R, @@ -1523,11 +1507,9 @@ where }; let reply_path_context = { - let path_absolute_expiry = - duration_since_epoch.saturating_add(TEMP_REPLY_PATH_RELATIVE_EXPIRY); MessageContext::AsyncPayments(AsyncPaymentsContext::StaticInvoicePersisted { offer_id, - path_absolute_expiry, + invoice_created_at: invoice.created_at(), }) }; @@ -1538,7 +1520,6 @@ where /// Creates a [`StaticInvoice`] and a blinded path for the server to forward invoice requests from /// payers to our node. - #[cfg(async_payments)] fn create_static_invoice_for_server( &self, offer: &Offer, offer_nonce: Nonce, peers: Vec, usable_channels: Vec, entropy: ES, router: R, @@ -1599,10 +1580,9 @@ where /// the static invoice from the database. /// /// Errors if the [`ServeStaticInvoice::invoice`] is expired or larger than - /// [`MAX_STATIC_INVOICE_SIZE_BYTES`], or if blinded path verification fails. + /// [`MAX_STATIC_INVOICE_SIZE_BYTES`]. /// /// [`ServeStaticInvoice::invoice`]: crate::onion_message::async_payments::ServeStaticInvoice::invoice - #[cfg(async_payments)] pub fn verify_serve_static_invoice_message( &self, message: &ServeStaticInvoice, context: AsyncPaymentsContext, ) -> Result<(Vec, u128), ()> { @@ -1632,7 +1612,6 @@ where /// to payers on behalf of an often-offline recipient. This method must be called after persisting /// a [`StaticInvoice`] to confirm to the recipient that their corresponding [`Offer`] is ready to /// receive async payments. - #[cfg(async_payments)] pub fn static_invoice_persisted(&self, responder: Responder) { let mut pending_async_payments_messages = self.pending_async_payments_messages.lock().unwrap(); @@ -1641,17 +1620,17 @@ where } /// Handles an incoming [`StaticInvoicePersisted`] onion message from the static invoice server. - /// Returns a bool indicating whether the async receive offer cache needs to be re-persisted. + /// Returns a bool indicating whether the async receive offer cache needs to be re-persisted using + /// [`Self::writeable_async_receive_offer_cache`]. /// /// [`StaticInvoicePersisted`]: crate::onion_message::async_payments::StaticInvoicePersisted - #[cfg(async_payments)] - pub(crate) fn handle_static_invoice_persisted(&self, context: AsyncPaymentsContext) -> bool { + pub fn handle_static_invoice_persisted(&self, context: AsyncPaymentsContext) -> bool { let mut cache = self.async_receive_offer_cache.lock().unwrap(); - cache.static_invoice_persisted(context, self.duration_since_epoch()) + cache.static_invoice_persisted(context) } - /// Get the `AsyncReceiveOfferCache` for persistence. - pub(crate) fn writeable_async_receive_offer_cache(&self) -> impl Writeable + '_ { - &self.async_receive_offer_cache + /// Get the encoded [`AsyncReceiveOfferCache`] for persistence. + pub fn writeable_async_receive_offer_cache(&self) -> Vec { + self.async_receive_offer_cache.encode() } } diff --git a/lightning/src/offers/mod.rs b/lightning/src/offers/mod.rs index b603deecd60..5b5cf6cdc78 100644 --- a/lightning/src/offers/mod.rs +++ b/lightning/src/offers/mod.rs @@ -16,7 +16,7 @@ pub mod offer; pub mod flow; -pub(crate) mod async_receive_offer_cache; +pub mod async_receive_offer_cache; pub mod invoice; pub mod invoice_error; mod invoice_macros; diff --git a/lightning/src/onion_message/async_payments.rs b/lightning/src/onion_message/async_payments.rs index 1f13e68f3d8..52badd71394 100644 --- a/lightning/src/onion_message/async_payments.rs +++ b/lightning/src/onion_message/async_payments.rs @@ -19,10 +19,10 @@ use crate::prelude::*; use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer}; // TLV record types for the `onionmsg_tlv` TLV stream as defined in BOLT 4. -const OFFER_PATHS_REQ_TLV_TYPE: u64 = 65538; -const OFFER_PATHS_TLV_TYPE: u64 = 65540; -const SERVE_INVOICE_TLV_TYPE: u64 = 65542; -const INVOICE_PERSISTED_TLV_TYPE: u64 = 65544; +const OFFER_PATHS_REQ_TLV_TYPE: u64 = 75540; +const OFFER_PATHS_TLV_TYPE: u64 = 75542; +const SERVE_INVOICE_TLV_TYPE: u64 = 75544; +const INVOICE_PERSISTED_TLV_TYPE: u64 = 75546; const HELD_HTLC_AVAILABLE_TLV_TYPE: u64 = 72; const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74; diff --git a/lightning/src/onion_message/messenger.rs b/lightning/src/onion_message/messenger.rs index 38c8cd304d9..553977dfe18 100644 --- a/lightning/src/onion_message/messenger.rs +++ b/lightning/src/onion_message/messenger.rs @@ -15,9 +15,7 @@ use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::{Hash, HashEngine}; use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey}; -#[cfg(async_payments)] -use super::async_payments::AsyncPaymentsMessage; -use super::async_payments::AsyncPaymentsMessageHandler; +use super::async_payments::{AsyncPaymentsMessage, AsyncPaymentsMessageHandler}; use super::dns_resolution::{DNSResolverMessage, DNSResolverMessageHandler}; use super::offers::{OffersMessage, OffersMessageHandler}; use super::packet::OnionMessageContents; @@ -26,11 +24,9 @@ use super::packet::{ ForwardControlTlvs, Packet, Payload, ReceiveControlTlvs, BIG_PACKET_HOP_DATA_LEN, SMALL_PACKET_HOP_DATA_LEN, }; -#[cfg(async_payments)] -use crate::blinded_path::message::AsyncPaymentsContext; use crate::blinded_path::message::{ - BlindedMessagePath, DNSResolverContext, ForwardTlvs, MessageContext, MessageForwardNode, - NextMessageHop, OffersContext, ReceiveTlvs, + AsyncPaymentsContext, BlindedMessagePath, DNSResolverContext, ForwardTlvs, MessageContext, + MessageForwardNode, NextMessageHop, OffersContext, ReceiveTlvs, }; use crate::blinded_path::utils; use crate::blinded_path::{IntroductionNode, NodeIdLookUp}; @@ -440,7 +436,6 @@ impl Responder { } /// Converts a [`Responder`] into its inner [`BlindedMessagePath`]. - #[cfg(async_payments)] pub(crate) fn into_blinded_path(self) -> BlindedMessagePath { self.reply_path } @@ -977,7 +972,6 @@ pub enum PeeledOnion { /// Received offers onion message, with decrypted contents, context, and reply path Offers(OffersMessage, Option, Option), /// Received async payments onion message, with decrypted contents, context, and reply path - #[cfg(async_payments)] AsyncPayments(AsyncPaymentsMessage, AsyncPaymentsContext, Option), /// Received DNS resolver onion message, with decrypted contents, context, and reply path DNSResolver(DNSResolverMessage, Option, Option), @@ -1181,7 +1175,6 @@ where (ParsedOnionMessageContents::Offers(msg), None) => { Ok(PeeledOnion::Offers(msg, None, reply_path)) }, - #[cfg(async_payments)] ( ParsedOnionMessageContents::AsyncPayments(msg), Some(MessageContext::AsyncPayments(ctx)), @@ -1678,15 +1671,12 @@ where ); } - #[cfg(async_payments)] - { - for (message, instructions) in self.async_payments_handler.release_pending_messages() { - let _ = self.send_onion_message_internal( - message, - instructions, - format_args!("when sending AsyncPaymentsMessage"), - ); - } + for (message, instructions) in self.async_payments_handler.release_pending_messages() { + let _ = self.send_onion_message_internal( + message, + instructions, + format_args!("when sending AsyncPaymentsMessage"), + ); } // Enqueue any initiating `DNSResolverMessage`s to send. @@ -2098,7 +2088,6 @@ where let _ = self.handle_onion_message_response(msg, instructions); } }, - #[cfg(async_payments)] Ok(PeeledOnion::AsyncPayments(message, context, reply_path)) => { log_receive!(message, reply_path.is_some()); let responder = reply_path.map(Responder::new); diff --git a/lightning/src/onion_message/offers.rs b/lightning/src/onion_message/offers.rs index c4c7774a112..06988d4db8f 100644 --- a/lightning/src/onion_message/offers.rs +++ b/lightning/src/onion_message/offers.rs @@ -16,7 +16,6 @@ use crate::offers::invoice::Bolt12Invoice; use crate::offers::invoice_error::InvoiceError; use crate::offers::invoice_request::InvoiceRequest; use crate::offers::parse::Bolt12ParseError; -#[cfg(async_payments)] use crate::offers::static_invoice::StaticInvoice; use crate::onion_message::messenger::{MessageSendInstructions, Responder, ResponseInstruction}; use crate::onion_message::packet::OnionMessageContents; @@ -30,7 +29,7 @@ use crate::prelude::*; const INVOICE_REQUEST_TLV_TYPE: u64 = 64; const INVOICE_TLV_TYPE: u64 = 66; const INVOICE_ERROR_TLV_TYPE: u64 = 68; -#[cfg(async_payments)] +// Spec'd in https://github.com/lightning/bolts/pull/1149. const STATIC_INVOICE_TLV_TYPE: u64 = 70; /// A handler for an [`OnionMessage`] containing a BOLT 12 Offers message as its payload. @@ -79,7 +78,6 @@ pub enum OffersMessage { /// [`Refund`]: crate::offers::refund::Refund Invoice(Bolt12Invoice), - #[cfg(async_payments)] /// A [`StaticInvoice`] sent in response to an [`InvoiceRequest`]. StaticInvoice(StaticInvoice), @@ -91,9 +89,10 @@ impl OffersMessage { /// Returns whether `tlv_type` corresponds to a TLV record for Offers. pub fn is_known_type(tlv_type: u64) -> bool { match tlv_type { - INVOICE_REQUEST_TLV_TYPE | INVOICE_TLV_TYPE | INVOICE_ERROR_TLV_TYPE => true, - #[cfg(async_payments)] - STATIC_INVOICE_TLV_TYPE => true, + INVOICE_REQUEST_TLV_TYPE + | INVOICE_TLV_TYPE + | INVOICE_ERROR_TLV_TYPE + | STATIC_INVOICE_TLV_TYPE => true, _ => false, } } @@ -102,7 +101,6 @@ impl OffersMessage { match tlv_type { INVOICE_REQUEST_TLV_TYPE => Ok(Self::InvoiceRequest(InvoiceRequest::try_from(bytes)?)), INVOICE_TLV_TYPE => Ok(Self::Invoice(Bolt12Invoice::try_from(bytes)?)), - #[cfg(async_payments)] STATIC_INVOICE_TLV_TYPE => Ok(Self::StaticInvoice(StaticInvoice::try_from(bytes)?)), _ => Err(Bolt12ParseError::Decode(DecodeError::InvalidValue)), } @@ -112,7 +110,6 @@ impl OffersMessage { match &self { OffersMessage::InvoiceRequest(_) => "Invoice Request", OffersMessage::Invoice(_) => "Invoice", - #[cfg(async_payments)] OffersMessage::StaticInvoice(_) => "Static Invoice", OffersMessage::InvoiceError(_) => "Invoice Error", } @@ -128,7 +125,6 @@ impl fmt::Debug for OffersMessage { OffersMessage::Invoice(message) => { write!(f, "{:?}", message.as_tlv_stream()) }, - #[cfg(async_payments)] OffersMessage::StaticInvoice(message) => { write!(f, "{:?}", message) }, @@ -144,7 +140,6 @@ impl OnionMessageContents for OffersMessage { match self { OffersMessage::InvoiceRequest(_) => INVOICE_REQUEST_TLV_TYPE, OffersMessage::Invoice(_) => INVOICE_TLV_TYPE, - #[cfg(async_payments)] OffersMessage::StaticInvoice(_) => STATIC_INVOICE_TLV_TYPE, OffersMessage::InvoiceError(_) => INVOICE_ERROR_TLV_TYPE, } @@ -164,7 +159,6 @@ impl Writeable for OffersMessage { match self { OffersMessage::InvoiceRequest(message) => message.write(w), OffersMessage::Invoice(message) => message.write(w), - #[cfg(async_payments)] OffersMessage::StaticInvoice(message) => message.write(w), OffersMessage::InvoiceError(message) => message.write(w), } diff --git a/lightning/src/onion_message/packet.rs b/lightning/src/onion_message/packet.rs index 301473fba6a..ee41ee98572 100644 --- a/lightning/src/onion_message/packet.rs +++ b/lightning/src/onion_message/packet.rs @@ -12,7 +12,6 @@ use bitcoin::secp256k1::ecdh::SharedSecret; use bitcoin::secp256k1::PublicKey; -#[cfg(async_payments)] use super::async_payments::AsyncPaymentsMessage; use super::dns_resolution::DNSResolverMessage; use super::messenger::CustomOnionMessageHandler; @@ -131,7 +130,6 @@ pub enum ParsedOnionMessageContents { /// A message related to BOLT 12 Offers. Offers(OffersMessage), /// A message related to async payments. - #[cfg(async_payments)] AsyncPayments(AsyncPaymentsMessage), /// A message requesting or providing a DNSSEC proof DNSResolver(DNSResolverMessage), @@ -146,7 +144,6 @@ impl OnionMessageContents for ParsedOnionMessageContent fn tlv_type(&self) -> u64 { match self { &ParsedOnionMessageContents::Offers(ref msg) => msg.tlv_type(), - #[cfg(async_payments)] &ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.tlv_type(), &ParsedOnionMessageContents::DNSResolver(ref msg) => msg.tlv_type(), &ParsedOnionMessageContents::Custom(ref msg) => msg.tlv_type(), @@ -156,7 +153,6 @@ impl OnionMessageContents for ParsedOnionMessageContent fn msg_type(&self) -> String { match self { ParsedOnionMessageContents::Offers(ref msg) => msg.msg_type(), - #[cfg(async_payments)] ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.msg_type(), ParsedOnionMessageContents::DNSResolver(ref msg) => msg.msg_type(), ParsedOnionMessageContents::Custom(ref msg) => msg.msg_type(), @@ -166,7 +162,6 @@ impl OnionMessageContents for ParsedOnionMessageContent fn msg_type(&self) -> &'static str { match self { ParsedOnionMessageContents::Offers(ref msg) => msg.msg_type(), - #[cfg(async_payments)] ParsedOnionMessageContents::AsyncPayments(ref msg) => msg.msg_type(), ParsedOnionMessageContents::DNSResolver(ref msg) => msg.msg_type(), ParsedOnionMessageContents::Custom(ref msg) => msg.msg_type(), @@ -178,7 +173,6 @@ impl Writeable for ParsedOnionMessageContents { fn write(&self, w: &mut W) -> Result<(), io::Error> { match self { ParsedOnionMessageContents::Offers(msg) => msg.write(w), - #[cfg(async_payments)] ParsedOnionMessageContents::AsyncPayments(msg) => msg.write(w), ParsedOnionMessageContents::DNSResolver(msg) => msg.write(w), ParsedOnionMessageContents::Custom(msg) => msg.write(w), @@ -293,7 +287,6 @@ impl message = Some(ParsedOnionMessageContents::Offers(msg)); Ok(true) }, - #[cfg(async_payments)] tlv_type if AsyncPaymentsMessage::is_known_type(tlv_type) => { let msg = AsyncPaymentsMessage::read(msg_reader, tlv_type)?; message = Some(ParsedOnionMessageContents::AsyncPayments(msg)); diff --git a/lightning/src/routing/router.rs b/lightning/src/routing/router.rs index d56d5747e09..77f2e204b68 100644 --- a/lightning/src/routing/router.rs +++ b/lightning/src/routing/router.rs @@ -23,7 +23,6 @@ use crate::ln::channelmanager::{PaymentId, RecipientOnionFields, MIN_FINAL_CLTV_ use crate::ln::msgs::{DecodeError, MAX_VALUE_MSAT}; use crate::ln::onion_utils; use crate::offers::invoice::Bolt12Invoice; -#[cfg(async_payments)] use crate::offers::static_invoice::StaticInvoice; use crate::routing::gossip::{ DirectedChannelInfo, EffectiveCapacity, NetworkGraph, NodeId, ReadOnlyNetworkGraph, @@ -1010,7 +1009,6 @@ impl PaymentParameters { /// Creates parameters for paying to a blinded payee from the provided invoice. Sets /// [`Payee::Blinded::route_hints`], [`Payee::Blinded::features`], and /// [`PaymentParameters::expiry_time`]. - #[cfg(async_payments)] #[rustfmt::skip] pub fn from_static_invoice(invoice: &StaticInvoice) -> Self { Self::blinded(invoice.payment_paths().to_vec())