Skip to content
This repository has been archived by the owner on Aug 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request lightningdevkit#3125 from valentinewallace/2024-06…
Browse files Browse the repository at this point in the history
…-async-payments-prefactor

Async payments message encoding and prefactor
  • Loading branch information
valentinewallace authored Jun 24, 2024
2 parents 07d991c + 5c7af8c commit 88e1b56
Show file tree
Hide file tree
Showing 18 changed files with 342 additions and 39 deletions.
2 changes: 2 additions & 0 deletions ci/check-cfg-flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions ci/ci-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
16 changes: 16 additions & 0 deletions fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
);

Expand Down Expand Up @@ -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 {}

Expand Down
6 changes: 3 additions & 3 deletions lightning-background-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>;
/// #
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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)));
Expand Down
11 changes: 11 additions & 0 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lightning/src/ln/functional_test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()));
Expand Down
12 changes: 12 additions & 0 deletions lightning/src/ln/offers_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
Expand All @@ -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"),
Expand All @@ -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"),
Expand Down
9 changes: 9 additions & 0 deletions lightning/src/ln/peer_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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> {
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/offers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
1 change: 1 addition & 0 deletions lightning/src/offers/offer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>), ()> {
Expand Down
8 changes: 3 additions & 5 deletions lightning/src/offers/static_invoice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -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;
Expand Down
152 changes: 152 additions & 0 deletions lightning/src/onion_message/async_payments.rs
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
Loading

0 comments on commit 88e1b56

Please sign in to comment.