pass
elif cfg == "splicing":
pass
+ elif cfg == "async_payments":
+ pass
else:
print("Bad cfg tag: " + cfg)
assert False
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
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,
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,
&node_id_lookup,
&message_router,
&offers_msg_handler,
+ &async_payments_msg_handler,
&custom_msg_handler,
);
}
}
+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 {}
/// # 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>;
/// #
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>,
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)));
},
}
},
+ #[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
&'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
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()));
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"),
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"),
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"),
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;
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> {
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;
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>), ()> {
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::{
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;
--- /dev/null
+// 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),
+ }
+ }
+}
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};
Arc<test_utils::TestKeysInterface>
>>,
Arc<TestOffersMessageHandler>,
+ Arc<TestAsyncPaymentsMessageHandler>,
Arc<TestCustomMessageHandler>
>,
custom_message_handler: Arc<TestCustomMessageHandler>,
}
}
+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,
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 {
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;
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;
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.
/// # 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)]
///
/// [`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,
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>,
}
}
-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,
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
)
}
/// 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();
node_id_lookup,
message_router,
offers_handler,
+ async_payments_handler,
custom_handler,
intercept_messages_for_offline_peers,
pending_events: Mutex::new(PendingEvents {
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,
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 {
}
}
-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,
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) {
"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);
},
Arc<SimpleArcChannelManager<M, T, F, L>>,
Arc<DefaultMessageRouter<Arc<NetworkGraph<Arc<L>>>, Arc<L>, Arc<KeysManager>>>,
Arc<SimpleArcChannelManager<M, T, F, L>>,
+ IgnoringMessageHandler,
IgnoringMessageHandler
>;
&'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
>;
//! [blinded paths]: crate::blinded_path::BlindedPath
//! [`OnionMessenger`]: self::messenger::OnionMessenger
+pub mod async_payments;
pub mod messenger;
pub mod offers;
pub mod packet;
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};
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.
///
/// [`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),
}
/// 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,
}
}
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)),
}
}
OffersMessage::Invoice(message) => {
write!(f, "{:?}", message.as_tlv_stream())
}
+ #[cfg(async_payments)]
+ OffersMessage::StaticInvoice(message) => {
+ write!(f, "{:?}", message)
+ }
OffersMessage::InvoiceError(message) => {
write!(f, "{:?}", message)
}
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,
}
}
match &self {
OffersMessage::InvoiceRequest(_) => "Invoice Request",
OffersMessage::Invoice(_) => "Invoice",
+ #[cfg(async_payments)]
+ OffersMessage::StaticInvoice(_) => "Static Invoice",
OffersMessage::InvoiceError(_) => "Invoice Error",
}
}
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),
}
}
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};
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),
}
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(),
}
}
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)?),
}
}
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));
$($crate::_init_tlv_field_var!($tlvfield, $fieldty);)*
$crate::decode_tlv_stream!(r, {$(($type, $tlvfield, $fieldty)),*});
Ok(Self {
- $($field),*,
+ $($field,)*
$($tlvfield),*
})
}
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);
+ }
}