]> git.bitcoin.ninja Git - rust-lightning/commitdiff
Merge pull request #3125 from valentinewallace/2024-06-async-payments-prefactor
authorvalentinewallace <valentinewallace@users.noreply.github.com>
Mon, 24 Jun 2024 16:06:17 +0000 (12:06 -0400)
committerGitHub <noreply@github.com>
Mon, 24 Jun 2024 16:06:17 +0000 (12:06 -0400)
Async payments message encoding and prefactor

18 files changed:
ci/check-cfg-flags.py
ci/ci-tests.sh
fuzz/src/onion_message.rs
lightning-background-processor/src/lib.rs
lightning/src/ln/channelmanager.rs
lightning/src/ln/functional_test_utils.rs
lightning/src/ln/offers_tests.rs
lightning/src/ln/peer_handler.rs
lightning/src/offers/mod.rs
lightning/src/offers/offer.rs
lightning/src/offers/static_invoice.rs
lightning/src/onion_message/async_payments.rs [new file with mode: 0644]
lightning/src/onion_message/functional_tests.rs
lightning/src/onion_message/messenger.rs
lightning/src/onion_message/mod.rs
lightning/src/onion_message/offers.rs
lightning/src/onion_message/packet.rs
lightning/src/util/ser_macros.rs

index c33e8aa3a15015fb7e7c470def921d633cb2ceb9..d73bf50a1f6cf4a483d7e1b2d237062b4c3f1e3e 100755 (executable)
@@ -104,6 +104,8 @@ def check_cfg_tag(cfg):
         pass
     elif cfg == "splicing":
         pass
+    elif cfg == "async_payments":
+        pass
     else:
         print("Bad cfg tag: " + cfg)
         assert False
index 0dc654d8bedca04688e5cf5e8afcd248b4cc3587..d0ba7c7fe9e581292fc36816582e1884dedc7607 100755 (executable)
@@ -179,3 +179,5 @@ RUSTFLAGS="--cfg=async_signing" cargo test --verbose --color always -p lightning
 RUSTFLAGS="--cfg=dual_funding" cargo test --verbose --color always -p lightning
 [ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
 RUSTFLAGS="--cfg=splicing" cargo test --verbose --color always -p lightning
+[ "$CI_MINIMIZE_DISK_USAGE" != "" ] && cargo clean
+RUSTFLAGS="--cfg=async_payments" cargo test --verbose --color always -p lightning
index a8a290fefb3379a176ff781e8b80c8df1036804f..ba76815af59cb6b88226ca45c5fede1e38b624f7 100644 (file)
@@ -12,6 +12,9 @@ use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
 use lightning::ln::script::ShutdownScript;
 use lightning::offers::invoice::UnsignedBolt12Invoice;
 use lightning::offers::invoice_request::UnsignedInvoiceRequest;
+use lightning::onion_message::async_payments::{
+       AsyncPaymentsMessage, AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc,
+};
 use lightning::onion_message::messenger::{
        CustomOnionMessageHandler, Destination, MessageRouter, OnionMessagePath, OnionMessenger,
        PendingOnionMessage, Responder, ResponseInstruction,
@@ -39,6 +42,7 @@ pub fn do_test<L: Logger>(data: &[u8], logger: &L) {
                let node_id_lookup = EmptyNodeIdLookUp {};
                let message_router = TestMessageRouter {};
                let offers_msg_handler = TestOffersMessageHandler {};
+               let async_payments_msg_handler = TestAsyncPaymentsMessageHandler {};
                let custom_msg_handler = TestCustomMessageHandler {};
                let onion_messenger = OnionMessenger::new(
                        &keys_manager,
@@ -47,6 +51,7 @@ pub fn do_test<L: Logger>(data: &[u8], logger: &L) {
                        &node_id_lookup,
                        &message_router,
                        &offers_msg_handler,
+                       &async_payments_msg_handler,
                        &custom_msg_handler,
                );
 
@@ -105,6 +110,17 @@ impl OffersMessageHandler for TestOffersMessageHandler {
        }
 }
 
+struct TestAsyncPaymentsMessageHandler {}
+
+impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler {
+       fn held_htlc_available(
+               &self, _message: HeldHtlcAvailable, _responder: Option<Responder>,
+       ) -> ResponseInstruction<ReleaseHeldHtlc> {
+               ResponseInstruction::NoResponse
+       }
+       fn release_held_htlc(&self, _message: ReleaseHeldHtlc) {}
+}
+
 #[derive(Debug)]
 struct TestCustomMessage {}
 
index 4719d7e5c1aba8a7dd15c1bdf47f1447f872d39a..aae64e981bb54271d0279c26db666bdee77f4d10 100644 (file)
@@ -568,7 +568,7 @@ use core::task;
 /// # type NetworkGraph = lightning::routing::gossip::NetworkGraph<Arc<Logger>>;
 /// # type P2PGossipSync<UL> = lightning::routing::gossip::P2PGossipSync<Arc<NetworkGraph>, Arc<UL>, Arc<Logger>>;
 /// # type ChannelManager<B, F, FE> = lightning::ln::channelmanager::SimpleArcChannelManager<ChainMonitor<B, F, FE>, B, FE, Logger>;
-/// # type OnionMessenger<B, F, FE> = lightning::onion_message::messenger::OnionMessenger<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<Logger>, Arc<ChannelManager<B, F, FE>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<NetworkGraph>, Arc<Logger>, Arc<lightning::sign::KeysManager>>>, Arc<ChannelManager<B, F, FE>>, lightning::ln::peer_handler::IgnoringMessageHandler>;
+/// # type OnionMessenger<B, F, FE> = lightning::onion_message::messenger::OnionMessenger<Arc<lightning::sign::KeysManager>, Arc<lightning::sign::KeysManager>, Arc<Logger>, Arc<ChannelManager<B, F, FE>>, Arc<lightning::onion_message::messenger::DefaultMessageRouter<Arc<NetworkGraph>, Arc<Logger>, Arc<lightning::sign::KeysManager>>>, Arc<ChannelManager<B, F, FE>>, lightning::ln::peer_handler::IgnoringMessageHandler, lightning::ln::peer_handler::IgnoringMessageHandler>;
 /// # type Scorer = RwLock<lightning::routing::scoring::ProbabilisticScorer<Arc<NetworkGraph>, Arc<Logger>>>;
 /// # type PeerManager<B, F, FE, UL> = lightning::ln::peer_handler::SimpleArcPeerManager<SocketDescriptor, ChainMonitor<B, F, FE>, B, FE, Arc<UL>, Logger>;
 /// #
@@ -996,7 +996,7 @@ mod tests {
        type PGS = Arc<P2PGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestChainSource>, Arc<test_utils::TestLogger>>>;
        type RGS = Arc<RapidGossipSync<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>>>;
 
-       type OM = OnionMessenger<Arc<KeysManager>, Arc<KeysManager>, Arc<test_utils::TestLogger>, Arc<ChannelManager>, Arc<DefaultMessageRouter<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>, Arc<KeysManager>>>, IgnoringMessageHandler, IgnoringMessageHandler>;
+       type OM = OnionMessenger<Arc<KeysManager>, Arc<KeysManager>, Arc<test_utils::TestLogger>, Arc<ChannelManager>, Arc<DefaultMessageRouter<Arc<NetworkGraph<Arc<test_utils::TestLogger>>>, Arc<test_utils::TestLogger>, Arc<KeysManager>>>, IgnoringMessageHandler, IgnoringMessageHandler, IgnoringMessageHandler>;
 
        struct Node {
                node: Arc<ChannelManager>,
@@ -1291,7 +1291,7 @@ mod tests {
                        let best_block = BestBlock::from_network(network);
                        let params = ChainParameters { network, best_block };
                        let manager = Arc::new(ChannelManager::new(fee_estimator.clone(), chain_monitor.clone(), tx_broadcaster.clone(), router.clone(), logger.clone(), keys_manager.clone(), keys_manager.clone(), keys_manager.clone(), UserConfig::default(), params, genesis_block.header.time));
-                       let messenger = Arc::new(OnionMessenger::new(keys_manager.clone(), keys_manager.clone(), logger.clone(), manager.clone(), msg_router.clone(), IgnoringMessageHandler {}, IgnoringMessageHandler {}));
+                       let messenger = Arc::new(OnionMessenger::new(keys_manager.clone(), keys_manager.clone(), logger.clone(), manager.clone(), msg_router.clone(), IgnoringMessageHandler {}, IgnoringMessageHandler {}, IgnoringMessageHandler {}));
                        let wallet = Arc::new(TestWallet {});
                        let sweeper = Arc::new(OutputSweeper::new(best_block, Arc::clone(&tx_broadcaster), Arc::clone(&fee_estimator),
                                None::<Arc<dyn Filter + Sync + Send>>, Arc::clone(&keys_manager), wallet, Arc::clone(&kv_store), Arc::clone(&logger)));
index c2dcf31f2aea5fc5818888f2de0c98a772b3b133..6ba7396ebfe04262904640c6b041d6969e95195f 100644 (file)
@@ -10377,6 +10377,17 @@ where
                                        },
                                }
                        },
+                       #[cfg(async_payments)]
+                       OffersMessage::StaticInvoice(_invoice) => {
+                               match responder {
+                                       Some(responder) => {
+                                               responder.respond(OffersMessage::InvoiceError(
+                                                               InvoiceError::from_string("Static invoices not yet supported".to_string())
+                                               ))
+                                       },
+                                       None => return ResponseInstruction::NoResponse,
+                               }
+                       },
                        OffersMessage::InvoiceError(invoice_error) => {
                                log_trace!(self.logger, "Received invoice_error: {}", invoice_error);
                                ResponseInstruction::NoResponse
index 8c08d37f5f6193e2d1fc932a90e547f9e5593d90..00168fdfb0ca0252c3c54117ad6a05533cbdd794 100644 (file)
@@ -423,6 +423,7 @@ type TestOnionMessenger<'chan_man, 'node_cfg, 'chan_mon_cfg> = OnionMessenger<
        &'node_cfg test_utils::TestMessageRouter<'chan_mon_cfg>,
        &'chan_man TestChannelManager<'node_cfg, 'chan_mon_cfg>,
        IgnoringMessageHandler,
+       IgnoringMessageHandler,
 >;
 
 /// For use with [`OnionMessenger`] otherwise `test_restored_packages_retry` will fail. This is
@@ -3258,7 +3259,7 @@ pub fn create_network<'a, 'b: 'a, 'c: 'b>(node_count: usize, cfgs: &'b Vec<NodeC
                let dedicated_entropy = DedicatedEntropy(RandomBytes::new([i as u8; 32]));
                let onion_messenger = OnionMessenger::new(
                        dedicated_entropy, cfgs[i].keys_manager, cfgs[i].logger, &chan_mgrs[i],
-                       &cfgs[i].message_router, &chan_mgrs[i], IgnoringMessageHandler {},
+                       &cfgs[i].message_router, &chan_mgrs[i], IgnoringMessageHandler {}, IgnoringMessageHandler {},
                );
                let gossip_sync = P2PGossipSync::new(cfgs[i].network_graph.as_ref(), None, cfgs[i].logger);
                let wallet_source = Arc::new(test_utils::TestWalletSource::new(SecretKey::from_slice(&[i as u8 + 1; 32]).unwrap()));
index 52375f723c75ec216241468456a43f5f63a4c69f..405ab87be3f11fb25c99a13960d854e6253f2cbe 100644 (file)
@@ -192,8 +192,12 @@ fn extract_invoice_request<'a, 'b, 'c>(
                        ParsedOnionMessageContents::Offers(offers_message) => match offers_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),
                        },
+                       #[cfg(async_payments)]
+                       ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
                        ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
                },
                Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
@@ -207,8 +211,12 @@ fn extract_invoice<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, message: &OnionMessage)
                        ParsedOnionMessageContents::Offers(offers_message) => match offers_message {
                                OffersMessage::InvoiceRequest(invoice_request) => panic!("Unexpected invoice_request: {:?}", invoice_request),
                                OffersMessage::Invoice(invoice) => invoice,
+                               #[cfg(async_payments)]
+                               OffersMessage::StaticInvoice(invoice) => panic!("Unexpected static invoice: {:?}", invoice),
                                OffersMessage::InvoiceError(error) => panic!("Unexpected invoice_error: {:?}", error),
                        },
+                       #[cfg(async_payments)]
+                       ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
                        ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
                },
                Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
@@ -224,8 +232,12 @@ fn extract_invoice_error<'a, 'b, 'c>(
                        ParsedOnionMessageContents::Offers(offers_message) => match offers_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,
                        },
+                       #[cfg(async_payments)]
+                       ParsedOnionMessageContents::AsyncPayments(message) => panic!("Unexpected async payments message: {:?}", message),
                        ParsedOnionMessageContents::Custom(message) => panic!("Unexpected custom message: {:?}", message),
                },
                Ok(PeeledOnion::Forward(_, _)) => panic!("Unexpected onion message forward"),
index 448dd213dad553ba8143e15b9d32cbea2fb1464c..9a026d709cffa95a2dc66120ee80f0a6c6a89b29 100644 (file)
@@ -28,6 +28,7 @@ use crate::util::ser::{VecWriter, Writeable, Writer};
 use crate::ln::peer_channel_encryptor::{PeerChannelEncryptor, NextNoiseStep, MessageBuf, MSG_BUF_ALLOC_SIZE};
 use crate::ln::wire;
 use crate::ln::wire::{Encode, Type};
+use crate::onion_message::async_payments::{AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc};
 use crate::onion_message::messenger::{CustomOnionMessageHandler, PendingOnionMessage, Responder, ResponseInstruction};
 use crate::onion_message::offers::{OffersMessage, OffersMessageHandler};
 use crate::onion_message::packet::OnionMessageContents;
@@ -148,6 +149,14 @@ impl OffersMessageHandler for IgnoringMessageHandler {
                ResponseInstruction::NoResponse
        }
 }
+impl AsyncPaymentsMessageHandler for IgnoringMessageHandler {
+       fn held_htlc_available(
+               &self, _message: HeldHtlcAvailable, _responder: Option<Responder>,
+       ) -> ResponseInstruction<ReleaseHeldHtlc> {
+               ResponseInstruction::NoResponse
+       }
+       fn release_held_htlc(&self, _message: ReleaseHeldHtlc) {}
+}
 impl CustomOnionMessageHandler for IgnoringMessageHandler {
        type CustomMessage = Infallible;
        fn handle_custom_message(&self, _message: Self::CustomMessage, _responder: Option<Responder>) -> ResponseInstruction<Self::CustomMessage> {
index b77eec1611906789182c4a1da8cf6694b24ef55b..e5e894e2a128fe395a51ca71fb6e38fa1125aaff 100644 (file)
@@ -24,7 +24,7 @@ pub mod parse;
 mod payer;
 pub mod refund;
 pub(crate) mod signer;
-#[allow(unused)]
-pub(crate) mod static_invoice;
+#[cfg(async_payments)]
+pub mod static_invoice;
 #[cfg(test)]
 pub(crate) mod test_utils;
index dd58c75cec27e600e803122d72d92036665b0976..253de8652bbdb8bbe70792988fa676a9b52bcbc6 100644 (file)
@@ -665,6 +665,7 @@ impl Offer {
                self.contents.expects_quantity()
        }
 
+       #[cfg(async_payments)]
        pub(super) fn verify<T: secp256k1::Signing>(
                &self, key: &ExpandedKey, secp_ctx: &Secp256k1<T>
        ) -> Result<(OfferId, Option<Keypair>), ()> {
index d0846b29af6f93e031bcbc6e517b777ae4d236df..69f4073e678ec0c7b4ae5254d9059e950eadfc32 100644 (file)
@@ -15,8 +15,8 @@ use crate::ln::features::{Bolt12InvoiceFeatures, OfferFeatures};
 use crate::ln::inbound_payment::ExpandedKey;
 use crate::ln::msgs::DecodeError;
 use crate::offers::invoice::{
-       check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, BlindedPathIter,
-       BlindedPayInfo, BlindedPayInfoIter, FallbackAddress, InvoiceTlvStream, InvoiceTlvStreamRef,
+       check_invoice_signing_pubkey, construct_payment_paths, filter_fallbacks, BlindedPayInfo,
+       FallbackAddress, InvoiceTlvStream, InvoiceTlvStreamRef,
 };
 use crate::offers::invoice_macros::{invoice_accessors_common, invoice_builder_methods_common};
 use crate::offers::merkle::{
@@ -26,9 +26,7 @@ use crate::offers::offer::{
        Amount, Offer, OfferContents, OfferTlvStream, OfferTlvStreamRef, Quantity,
 };
 use crate::offers::parse::{Bolt12ParseError, Bolt12SemanticError, ParsedMessage};
-use crate::util::ser::{
-       HighZeroBytesDroppedBigSize, Iterable, SeekReadable, WithoutLength, Writeable, Writer,
-};
+use crate::util::ser::{Iterable, SeekReadable, WithoutLength, Writeable, Writer};
 use crate::util::string::PrintableString;
 use bitcoin::address::Address;
 use bitcoin::blockdata::constants::ChainHash;
diff --git a/lightning/src/onion_message/async_payments.rs b/lightning/src/onion_message/async_payments.rs
new file mode 100644 (file)
index 0000000..f5953c6
--- /dev/null
@@ -0,0 +1,152 @@
+// This file is Copyright its original authors, visible in version control
+// history.
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
+//! Message handling for async payments.
+
+use crate::io;
+use crate::ln::msgs::DecodeError;
+use crate::onion_message::messenger::PendingOnionMessage;
+use crate::onion_message::messenger::{Responder, ResponseInstruction};
+use crate::onion_message::packet::OnionMessageContents;
+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 HELD_HTLC_AVAILABLE_TLV_TYPE: u64 = 72;
+const RELEASE_HELD_HTLC_TLV_TYPE: u64 = 74;
+
+/// A handler for an [`OnionMessage`] containing an async payments message as its payload.
+///
+/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
+pub trait AsyncPaymentsMessageHandler {
+       /// Handle a [`HeldHtlcAvailable`] message. A [`ReleaseHeldHtlc`] should be returned to release
+       /// the held funds.
+       fn held_htlc_available(
+               &self, message: HeldHtlcAvailable, responder: Option<Responder>,
+       ) -> ResponseInstruction<ReleaseHeldHtlc>;
+
+       /// Handle a [`ReleaseHeldHtlc`] message. If authentication of the message succeeds, an HTLC
+       /// should be released to the corresponding payee.
+       fn release_held_htlc(&self, message: ReleaseHeldHtlc);
+
+       /// Release any [`AsyncPaymentsMessage`]s that need to be sent.
+       ///
+       /// Typically, this is used for messages initiating an async payment flow rather than in response
+       /// to another message.
+       #[cfg(not(c_bindings))]
+       fn release_pending_messages(&self) -> Vec<PendingOnionMessage<AsyncPaymentsMessage>> {
+               vec![]
+       }
+
+       /// Release any [`AsyncPaymentsMessage`]s that need to be sent.
+       ///
+       /// Typically, this is used for messages initiating a payment flow rather than in response to
+       /// another message.
+       #[cfg(c_bindings)]
+       fn release_pending_messages(
+               &self,
+       ) -> Vec<(
+               AsyncPaymentsMessage,
+               crate::onion_message::messenger::Destination,
+               Option<crate::blinded_path::BlindedPath>,
+       )> {
+               vec![]
+       }
+}
+
+/// Possible async payment messages sent and received via an [`OnionMessage`].
+///
+/// [`OnionMessage`]: crate::ln::msgs::OnionMessage
+#[derive(Clone, Debug)]
+pub enum AsyncPaymentsMessage {
+       /// An HTLC is being held upstream for the often-offline recipient, to be released via
+       /// [`ReleaseHeldHtlc`].
+       HeldHtlcAvailable(HeldHtlcAvailable),
+
+       /// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
+       ReleaseHeldHtlc(ReleaseHeldHtlc),
+}
+
+/// An HTLC destined for the recipient of this message is being held upstream. The reply path
+/// accompanying this onion message should be used to send a [`ReleaseHeldHtlc`] response, which
+/// will cause the upstream HTLC to be released.
+#[derive(Clone, Debug)]
+pub struct HeldHtlcAvailable {
+       /// The secret that will be used by the recipient of this message to release the held HTLC.
+       pub payment_release_secret: [u8; 32],
+}
+
+/// Releases the HTLC corresponding to an inbound [`HeldHtlcAvailable`] message.
+#[derive(Clone, Debug)]
+pub struct ReleaseHeldHtlc {
+       /// Used to release the HTLC held upstream if it matches the corresponding
+       /// [`HeldHtlcAvailable::payment_release_secret`].
+       pub payment_release_secret: [u8; 32],
+}
+
+impl OnionMessageContents for ReleaseHeldHtlc {
+       fn tlv_type(&self) -> u64 {
+               RELEASE_HELD_HTLC_TLV_TYPE
+       }
+       fn msg_type(&self) -> &'static str {
+               "Release Held HTLC"
+       }
+}
+
+impl_writeable_tlv_based!(HeldHtlcAvailable, {
+       (0, payment_release_secret, required),
+});
+
+impl_writeable_tlv_based!(ReleaseHeldHtlc, {
+       (0, payment_release_secret, required),
+});
+
+impl AsyncPaymentsMessage {
+       /// Returns whether `tlv_type` corresponds to a TLV record for async payment messages.
+       pub fn is_known_type(tlv_type: u64) -> bool {
+               match tlv_type {
+                       HELD_HTLC_AVAILABLE_TLV_TYPE | RELEASE_HELD_HTLC_TLV_TYPE => true,
+                       _ => false,
+               }
+       }
+}
+
+impl OnionMessageContents for AsyncPaymentsMessage {
+       fn tlv_type(&self) -> u64 {
+               match self {
+                       Self::HeldHtlcAvailable(_) => HELD_HTLC_AVAILABLE_TLV_TYPE,
+                       Self::ReleaseHeldHtlc(msg) => msg.tlv_type(),
+               }
+       }
+       fn msg_type(&self) -> &'static str {
+               match &self {
+                       Self::HeldHtlcAvailable(_) => "Held HTLC Available",
+                       Self::ReleaseHeldHtlc(msg) => msg.msg_type(),
+               }
+       }
+}
+
+impl Writeable for AsyncPaymentsMessage {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               match self {
+                       Self::HeldHtlcAvailable(message) => message.write(w),
+                       Self::ReleaseHeldHtlc(message) => message.write(w),
+               }
+       }
+}
+
+impl ReadableArgs<u64> for AsyncPaymentsMessage {
+       fn read<R: io::Read>(r: &mut R, tlv_type: u64) -> Result<Self, DecodeError> {
+               match tlv_type {
+                       HELD_HTLC_AVAILABLE_TLV_TYPE => Ok(Self::HeldHtlcAvailable(Readable::read(r)?)),
+                       RELEASE_HELD_HTLC_TLV_TYPE => Ok(Self::ReleaseHeldHtlc(Readable::read(r)?)),
+                       _ => Err(DecodeError::InvalidValue),
+               }
+       }
+}
index ed601c047c28a1edfbd1bb930a74a3c8ccf33441..40b6177921fb144a66924707fab1101a2a0dced0 100644 (file)
@@ -19,6 +19,7 @@ use crate::routing::test_utils::{add_channel, add_or_update_node};
 use crate::sign::{NodeSigner, Recipient};
 use crate::util::ser::{FixedLengthReader, LengthReadable, Writeable, Writer};
 use crate::util::test_utils;
+use super::async_payments::{AsyncPaymentsMessageHandler, HeldHtlcAvailable, ReleaseHeldHtlc};
 use super::messenger::{CustomOnionMessageHandler, DefaultMessageRouter, Destination, OnionMessagePath, OnionMessenger, PendingOnionMessage, Responder, ResponseInstruction, SendError, SendSuccess};
 use super::offers::{OffersMessage, OffersMessageHandler};
 use super::packet::{OnionMessageContents, Packet};
@@ -50,6 +51,7 @@ struct MessengerNode {
                        Arc<test_utils::TestKeysInterface>
                >>,
                Arc<TestOffersMessageHandler>,
+               Arc<TestAsyncPaymentsMessageHandler>,
                Arc<TestCustomMessageHandler>
        >,
        custom_message_handler: Arc<TestCustomMessageHandler>,
@@ -79,6 +81,17 @@ impl OffersMessageHandler for TestOffersMessageHandler {
        }
 }
 
+struct TestAsyncPaymentsMessageHandler {}
+
+impl AsyncPaymentsMessageHandler for TestAsyncPaymentsMessageHandler {
+       fn held_htlc_available(
+               &self, _message: HeldHtlcAvailable, _responder: Option<Responder>,
+       ) -> ResponseInstruction<ReleaseHeldHtlc> {
+               ResponseInstruction::NoResponse
+       }
+       fn release_held_htlc(&self, _message: ReleaseHeldHtlc) {}
+}
+
 #[derive(Clone, Debug, PartialEq)]
 enum TestCustomMessage {
        Ping,
@@ -249,18 +262,19 @@ fn create_nodes_using_cfgs(cfgs: Vec<MessengerCfg>) -> Vec<MessengerNode> {
                        DefaultMessageRouter::new(network_graph.clone(), entropy_source.clone())
                );
                let offers_message_handler = Arc::new(TestOffersMessageHandler {});
+               let async_payments_message_handler = Arc::new(TestAsyncPaymentsMessageHandler {});
                let custom_message_handler = Arc::new(TestCustomMessageHandler::new());
                let messenger = if cfg.intercept_offline_peer_oms {
                        OnionMessenger::new_with_offline_peer_interception(
                                entropy_source.clone(), node_signer.clone(), logger.clone(),
                                node_id_lookup, message_router, offers_message_handler,
-                               custom_message_handler.clone()
+                               async_payments_message_handler, custom_message_handler.clone()
                        )
                } else {
                        OnionMessenger::new(
                                entropy_source.clone(), node_signer.clone(), logger.clone(),
                                node_id_lookup, message_router, offers_message_handler,
-                               custom_message_handler.clone()
+                               async_payments_message_handler, custom_message_handler.clone()
                        )
                };
                nodes.push(MessengerNode {
index 859c3f2b5b9c88fd6e7c42c23698d1402dcbee61..85600190db3df5cd0015ea4b1c809104f36a2292 100644 (file)
@@ -24,6 +24,9 @@ use crate::ln::features::{InitFeatures, NodeFeatures};
 use crate::ln::msgs::{self, OnionMessage, OnionMessageHandler, SocketAddress};
 use crate::ln::onion_utils;
 use crate::routing::gossip::{NetworkGraph, NodeId, ReadOnlyNetworkGraph};
+use super::async_payments::AsyncPaymentsMessageHandler;
+#[cfg(async_payments)]
+use super::async_payments::AsyncPaymentsMessage;
 use super::packet::OnionMessageContents;
 use super::packet::ParsedOnionMessageContents;
 use super::offers::OffersMessageHandler;
@@ -76,22 +79,27 @@ pub trait AOnionMessenger {
        type OffersMessageHandler: OffersMessageHandler + ?Sized;
        /// A type that may be dereferenced to [`Self::OffersMessageHandler`]
        type OMH: Deref<Target = Self::OffersMessageHandler>;
+       /// A type implementing [`AsyncPaymentsMessageHandler`]
+       type AsyncPaymentsMessageHandler: AsyncPaymentsMessageHandler + ?Sized;
+       /// A type that may be dereferenced to [`Self::AsyncPaymentsMessageHandler`]
+       type APH: Deref<Target = Self::AsyncPaymentsMessageHandler>;
        /// A type implementing [`CustomOnionMessageHandler`]
        type CustomOnionMessageHandler: CustomOnionMessageHandler + ?Sized;
        /// A type that may be dereferenced to [`Self::CustomOnionMessageHandler`]
        type CMH: Deref<Target = Self::CustomOnionMessageHandler>;
        /// Returns a reference to the actual [`OnionMessenger`] object.
-       fn get_om(&self) -> &OnionMessenger<Self::ES, Self::NS, Self::L, Self::NL, Self::MR, Self::OMH, Self::CMH>;
+       fn get_om(&self) -> &OnionMessenger<Self::ES, Self::NS, Self::L, Self::NL, Self::MR, Self::OMH, Self::APH, Self::CMH>;
 }
 
-impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref> AOnionMessenger
-for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH> where
+impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, APH: Deref, CMH: Deref> AOnionMessenger
+for OnionMessenger<ES, NS, L, NL, MR, OMH, APH, CMH> where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
        NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
+       APH:: Target: AsyncPaymentsMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
 {
        type EntropySource = ES::Target;
@@ -106,9 +114,11 @@ for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH> where
        type MR = MR;
        type OffersMessageHandler = OMH::Target;
        type OMH = OMH;
+       type AsyncPaymentsMessageHandler = APH::Target;
+       type APH = APH;
        type CustomOnionMessageHandler = CMH::Target;
        type CMH = CMH;
-       fn get_om(&self) -> &OnionMessenger<ES, NS, L, NL, MR, OMH, CMH> { self }
+       fn get_om(&self) -> &OnionMessenger<ES, NS, L, NL, MR, OMH, APH, CMH> { self }
 }
 
 /// A sender, receiver and forwarder of [`OnionMessage`]s.
@@ -180,11 +190,12 @@ for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH> where
 /// # let message_router = Arc::new(FakeMessageRouter {});
 /// # let custom_message_handler = IgnoringMessageHandler {};
 /// # let offers_message_handler = IgnoringMessageHandler {};
+/// # let async_payments_message_handler = IgnoringMessageHandler {};
 /// // Create the onion messenger. This must use the same `keys_manager` as is passed to your
 /// // ChannelManager.
 /// let onion_messenger = OnionMessenger::new(
 ///     &keys_manager, &keys_manager, logger, &node_id_lookup, message_router,
-///     &offers_message_handler, &custom_message_handler
+///     &offers_message_handler, &async_payments_message_handler, &custom_message_handler
 /// );
 
 /// # #[derive(Debug)]
@@ -225,14 +236,16 @@ for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH> where
 ///
 /// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
 /// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
-pub struct OnionMessenger<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref>
-where
+pub struct OnionMessenger<
+       ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, APH: Deref, CMH: Deref
+> where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
        L::Target: Logger,
        NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
+       APH::Target: AsyncPaymentsMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
 {
        entropy_source: ES,
@@ -243,6 +256,8 @@ where
        node_id_lookup: NL,
        message_router: MR,
        offers_handler: OMH,
+       #[allow(unused)]
+       async_payments_handler: APH,
        custom_handler: CMH,
        intercept_messages_for_offline_peers: bool,
        pending_events: Mutex<PendingEvents>,
@@ -993,8 +1008,8 @@ where
        }
 }
 
-impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref>
-OnionMessenger<ES, NS, L, NL, MR, OMH, CMH>
+impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, APH: Deref, CMH: Deref>
+OnionMessenger<ES, NS, L, NL, MR, OMH, APH, CMH>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
@@ -1002,17 +1017,18 @@ where
        NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
+       APH::Target: AsyncPaymentsMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
 {
        /// Constructs a new `OnionMessenger` to send, forward, and delegate received onion messages to
        /// their respective handlers.
        pub fn new(
                entropy_source: ES, node_signer: NS, logger: L, node_id_lookup: NL, message_router: MR,
-               offers_handler: OMH, custom_handler: CMH
+               offers_handler: OMH, async_payments_handler: APH, custom_handler: CMH
        ) -> Self {
                Self::new_inner(
                        entropy_source, node_signer, logger, node_id_lookup, message_router,
-                       offers_handler, custom_handler, false
+                       offers_handler, async_payments_handler, custom_handler, false
                )
        }
 
@@ -1039,17 +1055,17 @@ where
        /// peers.
        pub fn new_with_offline_peer_interception(
                entropy_source: ES, node_signer: NS, logger: L, node_id_lookup: NL,
-               message_router: MR, offers_handler: OMH, custom_handler: CMH
+               message_router: MR, offers_handler: OMH, async_payments_handler: APH, custom_handler: CMH
        ) -> Self {
                Self::new_inner(
                        entropy_source, node_signer, logger, node_id_lookup, message_router,
-                       offers_handler, custom_handler, true
+                       offers_handler, async_payments_handler, custom_handler, true
                )
        }
 
        fn new_inner(
                entropy_source: ES, node_signer: NS, logger: L, node_id_lookup: NL,
-               message_router: MR, offers_handler: OMH, custom_handler: CMH,
+               message_router: MR, offers_handler: OMH, async_payments_handler: APH, custom_handler: CMH,
                intercept_messages_for_offline_peers: bool
        ) -> Self {
                let mut secp_ctx = Secp256k1::new();
@@ -1063,6 +1079,7 @@ where
                        node_id_lookup,
                        message_router,
                        offers_handler,
+                       async_payments_handler,
                        custom_handler,
                        intercept_messages_for_offline_peers,
                        pending_events: Mutex::new(PendingEvents {
@@ -1367,8 +1384,8 @@ fn outbound_buffer_full(peer_node_id: &PublicKey, buffer: &HashMap<PublicKey, On
        false
 }
 
-impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref> EventsProvider
-for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH>
+impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, APH: Deref, CMH: Deref> EventsProvider
+for OnionMessenger<ES, NS, L, NL, MR, OMH, APH, CMH>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
@@ -1376,6 +1393,7 @@ where
        NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
+       APH::Target: AsyncPaymentsMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
 {
        fn process_pending_events<H: Deref>(&self, handler: H) where H::Target: EventHandler {
@@ -1407,8 +1425,8 @@ where
        }
 }
 
-impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, CMH: Deref> OnionMessageHandler
-for OnionMessenger<ES, NS, L, NL, MR, OMH, CMH>
+impl<ES: Deref, NS: Deref, L: Deref, NL: Deref, MR: Deref, OMH: Deref, APH: Deref, CMH: Deref> OnionMessageHandler
+for OnionMessenger<ES, NS, L, NL, MR, OMH, APH, CMH>
 where
        ES::Target: EntropySource,
        NS::Target: NodeSigner,
@@ -1416,6 +1434,7 @@ where
        NL::Target: NodeIdLookUp,
        MR::Target: MessageRouter,
        OMH::Target: OffersMessageHandler,
+       APH::Target: AsyncPaymentsMessageHandler,
        CMH::Target: CustomOnionMessageHandler,
 {
        fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage) {
@@ -1427,18 +1446,26 @@ where
                                        "Received an onion message with path_id {:02x?} and {} reply_path: {:?}",
                                        path_id, if reply_path.is_some() { "a" } else { "no" }, message);
 
+                               let responder = reply_path.map(
+                                       |reply_path| Responder::new(reply_path, path_id)
+                               );
                                match message {
                                        ParsedOnionMessageContents::Offers(msg) => {
-                                               let responder = reply_path.map(
-                                                       |reply_path| Responder::new(reply_path, path_id)
-                                               );
                                                let response_instructions = self.offers_handler.handle_message(msg, responder);
                                                let _ = self.handle_onion_message_response(response_instructions);
                                        },
-                                       ParsedOnionMessageContents::Custom(msg) => {
-                                               let responder = reply_path.map(
-                                                       |reply_path| Responder::new(reply_path, path_id)
+                                       #[cfg(async_payments)]
+                                       ParsedOnionMessageContents::AsyncPayments(AsyncPaymentsMessage::HeldHtlcAvailable(msg)) => {
+                                               let response_instructions = self.async_payments_handler.held_htlc_available(
+                                                       msg, responder
                                                );
+                                               let _ = self.handle_onion_message_response(response_instructions);
+                                       },
+                                       #[cfg(async_payments)]
+                                       ParsedOnionMessageContents::AsyncPayments(AsyncPaymentsMessage::ReleaseHeldHtlc(msg)) => {
+                                               self.async_payments_handler.release_held_htlc(msg);
+                                       },
+                                       ParsedOnionMessageContents::Custom(msg) => {
                                                let response_instructions = self.custom_handler.handle_custom_message(msg, responder);
                                                let _ = self.handle_onion_message_response(response_instructions);
                                        },
@@ -1606,6 +1633,7 @@ pub type SimpleArcOnionMessenger<M, T, F, L> = OnionMessenger<
        Arc<SimpleArcChannelManager<M, T, F, L>>,
        Arc<DefaultMessageRouter<Arc<NetworkGraph<Arc<L>>>, Arc<L>, Arc<KeysManager>>>,
        Arc<SimpleArcChannelManager<M, T, F, L>>,
+       IgnoringMessageHandler,
        IgnoringMessageHandler
 >;
 
@@ -1626,6 +1654,7 @@ pub type SimpleRefOnionMessenger<
        &'i SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>,
        &'j DefaultMessageRouter<&'g NetworkGraph<&'b L>, &'b L, &'a KeysManager>,
        &'i SimpleRefChannelManager<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, M, T, F, L>,
+       IgnoringMessageHandler,
        IgnoringMessageHandler
 >;
 
index 05a8b7d6fbd033922fb140ea97d326287aec8757..1693c5a9911727651588ab7ca65f78ee264884ef 100644 (file)
@@ -21,6 +21,7 @@
 //! [blinded paths]: crate::blinded_path::BlindedPath
 //! [`OnionMessenger`]: self::messenger::OnionMessenger
 
+pub mod async_payments;
 pub mod messenger;
 pub mod offers;
 pub mod packet;
index 42c6914157e74f345ed0d2ad354834262653646a..397f4b8a72b7a827b8bb0543f78a076cd9eb2210 100644 (file)
@@ -16,6 +16,8 @@ use crate::offers::invoice_error::InvoiceError;
 use crate::offers::invoice_request::InvoiceRequest;
 use crate::offers::invoice::Bolt12Invoice;
 use crate::offers::parse::Bolt12ParseError;
+#[cfg(async_payments)]
+use crate::offers::static_invoice::StaticInvoice;
 use crate::onion_message::packet::OnionMessageContents;
 use crate::util::logger::Logger;
 use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer};
@@ -29,6 +31,8 @@ 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)]
+const STATIC_INVOICE_TLV_TYPE: u64 = 70;
 
 /// A handler for an [`OnionMessage`] containing a BOLT 12 Offers message as its payload.
 ///
@@ -72,6 +76,10 @@ pub enum OffersMessage {
        /// [`Refund`]: crate::offers::refund::Refund
        Invoice(Bolt12Invoice),
 
+       #[cfg(async_payments)]
+       /// A [`StaticInvoice`] sent in response to an [`InvoiceRequest`].
+       StaticInvoice(StaticInvoice),
+
        /// An error from handling an [`OffersMessage`].
        InvoiceError(InvoiceError),
 }
@@ -80,7 +88,11 @@ 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,
+                       INVOICE_REQUEST_TLV_TYPE
+                       | INVOICE_TLV_TYPE
+                       | INVOICE_ERROR_TLV_TYPE => true,
+                       #[cfg(async_payments)]
+                       STATIC_INVOICE_TLV_TYPE => true,
                        _ => false,
                }
        }
@@ -89,6 +101,8 @@ 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)),
                }
        }
@@ -103,6 +117,10 @@ impl fmt::Debug for OffersMessage {
                        OffersMessage::Invoice(message) => {
                                write!(f, "{:?}", message.as_tlv_stream())
                        }
+                       #[cfg(async_payments)]
+                       OffersMessage::StaticInvoice(message) => {
+                               write!(f, "{:?}", message)
+                       }
                        OffersMessage::InvoiceError(message) => {
                                write!(f, "{:?}", message)
                        }
@@ -115,6 +133,8 @@ 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,
                }
        }
@@ -122,6 +142,8 @@ impl OnionMessageContents for OffersMessage {
                match &self {
                        OffersMessage::InvoiceRequest(_) => "Invoice Request",
                        OffersMessage::Invoice(_) => "Invoice",
+                       #[cfg(async_payments)]
+                       OffersMessage::StaticInvoice(_) => "Static Invoice",
                        OffersMessage::InvoiceError(_) => "Invoice Error",
                }
        }
@@ -132,6 +154,8 @@ 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),
                }
        }
index 75ec3cd90ab83c811b18b1283eab668ca7ddcb1d..fca7dd6a91ac6a1eda39e5519822c1c9101df78c 100644 (file)
@@ -17,6 +17,8 @@ use crate::blinded_path::message::{ForwardTlvs, ReceiveTlvs};
 use crate::blinded_path::utils::Padding;
 use crate::ln::msgs::DecodeError;
 use crate::ln::onion_utils;
+#[cfg(async_payments)]
+use super::async_payments::AsyncPaymentsMessage;
 use super::messenger::CustomOnionMessageHandler;
 use super::offers::OffersMessage;
 use crate::crypto::streams::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
@@ -128,6 +130,9 @@ pub(super) enum Payload<T: OnionMessageContents> {
 pub enum ParsedOnionMessageContents<T: OnionMessageContents> {
        /// A message related to BOLT 12 Offers.
        Offers(OffersMessage),
+       /// A message related to async payments.
+       #[cfg(async_payments)]
+       AsyncPayments(AsyncPaymentsMessage),
        /// A custom onion message specified by the user.
        Custom(T),
 }
@@ -139,12 +144,16 @@ impl<T: OnionMessageContents> 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::Custom(ref msg) => msg.tlv_type(),
                }
        }
        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::Custom(ref msg) => msg.msg_type(),
                }
        }
@@ -154,6 +163,8 @@ impl<T: OnionMessageContents> Writeable for ParsedOnionMessageContents<T> {
        fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
                match self {
                        ParsedOnionMessageContents::Offers(msg) => Ok(msg.write(w)?),
+                       #[cfg(async_payments)]
+                       ParsedOnionMessageContents::AsyncPayments(msg) => Ok(msg.write(w)?),
                        ParsedOnionMessageContents::Custom(msg) => Ok(msg.write(w)?),
                }
        }
@@ -255,6 +266,12 @@ for Payload<ParsedOnionMessageContents<<H as CustomOnionMessageHandler>::CustomM
                                        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));
+                                       Ok(true)
+                               },
                                _ => match handler.read_custom_message(msg_type, msg_reader)? {
                                        Some(msg) => {
                                                message = Some(ParsedOnionMessageContents::Custom(msg));
index 740b7c12561ce8466d1a2e30f56faacc797961ac..255fd3ebc327899c98bc599a4a3213834179bb79 100644 (file)
@@ -633,7 +633,7 @@ macro_rules! impl_writeable_msg {
                                $($crate::_init_tlv_field_var!($tlvfield, $fieldty);)*
                                $crate::decode_tlv_stream!(r, {$(($type, $tlvfield, $fieldty)),*});
                                Ok(Self {
-                                       $($field),*,
+                                       $($field,)*
                                        $($tlvfield),*
                                })
                        }
@@ -1531,4 +1531,18 @@ mod tests {
        fn simple_test_tlv_write() {
                do_simple_test_tlv_write().unwrap();
        }
+
+       #[derive(Debug, Eq, PartialEq)]
+       struct EmptyMsg {}
+       impl_writeable_msg!(EmptyMsg, {}, {});
+
+       #[test]
+       fn impl_writeable_msg_empty() {
+               let msg = EmptyMsg {};
+               let mut encoded_msg = msg.encode();
+               assert!(encoded_msg.is_empty());
+               let mut encoded_msg_stream = Cursor::new(&mut encoded_msg);
+               let decoded_msg: EmptyMsg = Readable::read(&mut encoded_msg_stream).unwrap();
+               assert_eq!(msg, decoded_msg);
+       }
 }