Skip to content

Commit 1852715

Browse files
authored
Merge pull request #2578 from jkczyz/2023-09-offer-utilities
BOLT 12 Offers utilities
2 parents 0357caf + 905028b commit 1852715

File tree

16 files changed

+483
-252
lines changed

16 files changed

+483
-252
lines changed

fuzz/src/onion_message.rs

+5-15
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use lightning::offers::invoice_request::UnsignedInvoiceRequest;
1414
use lightning::util::test_channel_signer::TestChannelSigner;
1515
use lightning::util::logger::Logger;
1616
use lightning::util::ser::{Readable, Writeable, Writer};
17-
use lightning::onion_message::{CustomOnionMessageContents, CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessagePath, OnionMessenger};
17+
use lightning::onion_message::{CustomOnionMessageHandler, Destination, MessageRouter, OffersMessage, OffersMessageHandler, OnionMessageContents, OnionMessagePath, OnionMessenger, PendingOnionMessage};
1818

1919
use crate::utils::test_logger;
2020

@@ -84,7 +84,7 @@ struct TestCustomMessage {}
8484
const CUSTOM_MESSAGE_TYPE: u64 = 4242;
8585
const CUSTOM_MESSAGE_CONTENTS: [u8; 32] = [42; 32];
8686

87-
impl CustomOnionMessageContents for TestCustomMessage {
87+
impl OnionMessageContents for TestCustomMessage {
8888
fn tlv_type(&self) -> u64 {
8989
CUSTOM_MESSAGE_TYPE
9090
}
@@ -108,6 +108,9 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler {
108108
buffer.read_to_end(&mut buf)?;
109109
return Ok(Some(TestCustomMessage {}))
110110
}
111+
fn release_pending_custom_messages(&self) -> Vec<PendingOnionMessage<Self::CustomMessage>> {
112+
vec![]
113+
}
111114
}
112115

113116
pub struct VecWriter(pub Vec<u8>);
@@ -208,19 +211,6 @@ mod tests {
208211

209212
#[test]
210213
fn test_no_onion_message_breakage() {
211-
let one_hop_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e01ae0276020000000000000000000000000000000000000000000000000000000000000002020000000000000000000000000000000000000000000000000000000000000e0101022a0000000000000000000000000000014551231950b75fc4402da1732fc9bebf00109500000000000000000000000000000004106d000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005600000000000000000000000000000000000000000000000000000000000000";
212-
let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) };
213-
super::do_test(&::hex::decode(one_hop_om).unwrap(), &logger);
214-
{
215-
let log_entries = logger.lines.lock().unwrap();
216-
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
217-
"Received an onion message with path_id None and a reply_path".to_string())), Some(&1));
218-
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
219-
"Responding to onion message with path_id None".to_string())), Some(&1));
220-
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
221-
"Failed responding to onion message with path_id None: TooFewBlindedHops".to_string())), Some(&1));
222-
}
223-
224214
let two_unblinded_hops_om = "020000000000000000000000000000000000000000000000000000000000000e01055600020000000000000000000000000000000000000000000000000000000000000e0135043304210202020202020202020202020202020202020202020202020202020202020202026d000000000000000000000000000000eb0000000000000000000000000000000000000000000000000000000000000036041096000000000000000000000000000000fd1092202a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004800000000000000000000000000000000000000000000000000000000000000";
225215
let logger = TrackingLogger { lines: Mutex::new(HashMap::new()) };
226216
super::do_test(&::hex::decode(two_unblinded_hops_om).unwrap(), &logger);

lightning/src/blinded_path/mod.rs

+12-5
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,22 @@ pub struct BlindedHop {
5656
}
5757

5858
impl BlindedPath {
59+
/// Create a one-hop blinded path for a message.
60+
pub fn one_hop_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
61+
recipient_node_id: PublicKey, entropy_source: &ES, secp_ctx: &Secp256k1<T>
62+
) -> Result<Self, ()> {
63+
Self::new_for_message(&[recipient_node_id], entropy_source, secp_ctx)
64+
}
65+
5966
/// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
6067
/// pubkey in `node_pks` will be the destination node.
6168
///
62-
/// Errors if less than two hops are provided or if `node_pk`(s) are invalid.
69+
/// Errors if no hops are provided or if `node_pk`(s) are invalid.
6370
// TODO: make all payloads the same size with padding + add dummy hops
64-
pub fn new_for_message<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>
65-
(node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>) -> Result<Self, ()>
66-
{
67-
if node_pks.len() < 2 { return Err(()) }
71+
pub fn new_for_message<ES: EntropySource + ?Sized, T: secp256k1::Signing + secp256k1::Verification>(
72+
node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>
73+
) -> Result<Self, ()> {
74+
if node_pks.is_empty() { return Err(()) }
6875
let blinding_secret_bytes = entropy_source.get_secure_random_bytes();
6976
let blinding_secret = SecretKey::from_slice(&blinding_secret_bytes[..]).expect("RNG is busted");
7077
let introduction_node_id = node_pks[0];

lightning/src/events/mod.rs

-6
Original file line numberDiff line numberDiff line change
@@ -1846,12 +1846,6 @@ pub trait MessageSendEventsProvider {
18461846
fn get_and_clear_pending_msg_events(&self) -> Vec<MessageSendEvent>;
18471847
}
18481848

1849-
/// A trait indicating an object may generate onion messages to send
1850-
pub trait OnionMessageProvider {
1851-
/// Gets the next pending onion message for the peer with the given node id.
1852-
fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<msgs::OnionMessage>;
1853-
}
1854-
18551849
/// A trait indicating an object may generate events.
18561850
///
18571851
/// Events are processed by passing an [`EventHandler`] to [`process_pending_events`].

lightning/src/ln/channelmanager.rs

+93-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use bitcoin::secp256k1::{SecretKey,PublicKey};
3030
use bitcoin::secp256k1::Secp256k1;
3131
use bitcoin::{LockTime, secp256k1, Sequence};
3232

33+
use crate::blinded_path::BlindedPath;
3334
use crate::chain;
3435
use crate::chain::{Confirm, ChannelMonitorUpdateStatus, Watch, BestBlock};
3536
use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
@@ -55,6 +56,9 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
5556
use crate::ln::outbound_payment;
5657
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs};
5758
use crate::ln::wire::Encode;
59+
use crate::offers::offer::{DerivedMetadata, OfferBuilder};
60+
use crate::offers::parse::Bolt12SemanticError;
61+
use crate::offers::refund::RefundBuilder;
5862
use crate::sign::{EntropySource, KeysManager, NodeSigner, Recipient, SignerProvider, WriteableEcdsaChannelSigner};
5963
use crate::util::config::{UserConfig, ChannelConfig, ChannelConfigUpdate};
6064
use crate::util::wakers::{Future, Notifier};
@@ -4791,6 +4795,10 @@ where
47914795
/// with the current [`ChannelConfig`].
47924796
/// * Removing peers which have disconnected but and no longer have any channels.
47934797
/// * Force-closing and removing channels which have not completed establishment in a timely manner.
4798+
/// * Forgetting about stale outbound payments, either those that have already been fulfilled
4799+
/// or those awaiting an invoice that hasn't been delivered in the necessary amount of time.
4800+
/// The latter is determined using the system clock in `std` and the block time minus two
4801+
/// hours in `no-std`.
47944802
///
47954803
/// Note that this may cause reentrancy through [`chain::Watch::update_channel`] calls or feerate
47964804
/// estimate fetches.
@@ -5019,7 +5027,18 @@ where
50195027
self.finish_close_channel(shutdown_res);
50205028
}
50215029

5022-
self.pending_outbound_payments.remove_stale_payments(&self.pending_events);
5030+
#[cfg(feature = "std")]
5031+
let duration_since_epoch = std::time::SystemTime::now()
5032+
.duration_since(std::time::SystemTime::UNIX_EPOCH)
5033+
.expect("SystemTime::now() should come after SystemTime::UNIX_EPOCH");
5034+
#[cfg(not(feature = "std"))]
5035+
let duration_since_epoch = Duration::from_secs(
5036+
self.highest_seen_timestamp.load(Ordering::Acquire).saturating_sub(7200) as u64
5037+
);
5038+
5039+
self.pending_outbound_payments.remove_stale_payments(
5040+
duration_since_epoch, &self.pending_events
5041+
);
50235042

50245043
// Technically we don't need to do this here, but if we have holding cell entries in a
50255044
// channel that need freeing, it's better to do that here and block a background task
@@ -7108,6 +7127,71 @@ where
71087127
}
71097128
}
71107129

7130+
/// Creates an [`OfferBuilder`] such that the [`Offer`] it builds is recognized by the
7131+
/// [`ChannelManager`] when handling [`InvoiceRequest`] messages for the offer. The offer will
7132+
/// not have an expiration unless otherwise set on the builder.
7133+
///
7134+
/// Uses a one-hop [`BlindedPath`] for the offer with [`ChannelManager::get_our_node_id`] as the
7135+
/// introduction node and a derived signing pubkey for recipient privacy. As such, currently,
7136+
/// the node must be announced. Otherwise, there is no way to find a path to the introduction
7137+
/// node in order to send the [`InvoiceRequest`].
7138+
///
7139+
/// [`Offer`]: crate::offers::offer::Offer
7140+
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest
7141+
pub fn create_offer_builder(
7142+
&self, description: String
7143+
) -> OfferBuilder<DerivedMetadata, secp256k1::All> {
7144+
let node_id = self.get_our_node_id();
7145+
let expanded_key = &self.inbound_payment_key;
7146+
let entropy = &*self.entropy_source;
7147+
let secp_ctx = &self.secp_ctx;
7148+
let path = self.create_one_hop_blinded_path();
7149+
7150+
OfferBuilder::deriving_signing_pubkey(description, node_id, expanded_key, entropy, secp_ctx)
7151+
.chain_hash(self.chain_hash)
7152+
.path(path)
7153+
}
7154+
7155+
/// Creates a [`RefundBuilder`] such that the [`Refund`] it builds is recognized by the
7156+
/// [`ChannelManager`] when handling [`Bolt12Invoice`] messages for the refund. The builder will
7157+
/// have the provided expiration set. Any changes to the expiration on the returned builder will
7158+
/// not be honored by [`ChannelManager`].
7159+
///
7160+
/// The provided `payment_id` is used to ensure that only one invoice is paid for the refund.
7161+
///
7162+
/// Uses a one-hop [`BlindedPath`] for the refund with [`ChannelManager::get_our_node_id`] as
7163+
/// the introduction node and a derived payer id for sender privacy. As such, currently, the
7164+
/// node must be announced. Otherwise, there is no way to find a path to the introduction node
7165+
/// in order to send the [`Bolt12Invoice`].
7166+
///
7167+
/// [`Refund`]: crate::offers::refund::Refund
7168+
/// [`Bolt12Invoice`]: crate::offers::invoice::Bolt12Invoice
7169+
pub fn create_refund_builder(
7170+
&self, description: String, amount_msats: u64, absolute_expiry: Duration,
7171+
payment_id: PaymentId, retry_strategy: Retry, max_total_routing_fee_msat: Option<u64>
7172+
) -> Result<RefundBuilder<secp256k1::All>, Bolt12SemanticError> {
7173+
let node_id = self.get_our_node_id();
7174+
let expanded_key = &self.inbound_payment_key;
7175+
let entropy = &*self.entropy_source;
7176+
let secp_ctx = &self.secp_ctx;
7177+
let path = self.create_one_hop_blinded_path();
7178+
7179+
let builder = RefundBuilder::deriving_payer_id(
7180+
description, node_id, expanded_key, entropy, secp_ctx, amount_msats, payment_id
7181+
)?
7182+
.chain_hash(self.chain_hash)
7183+
.absolute_expiry(absolute_expiry)
7184+
.path(path);
7185+
7186+
self.pending_outbound_payments
7187+
.add_new_awaiting_invoice(
7188+
payment_id, absolute_expiry, retry_strategy, max_total_routing_fee_msat,
7189+
)
7190+
.map_err(|_| Bolt12SemanticError::DuplicatePaymentId)?;
7191+
7192+
Ok(builder)
7193+
}
7194+
71117195
/// Gets a payment secret and payment hash for use in an invoice given to a third party wishing
71127196
/// to pay us.
71137197
///
@@ -7208,6 +7292,14 @@ where
72087292
inbound_payment::get_payment_preimage(payment_hash, payment_secret, &self.inbound_payment_key)
72097293
}
72107294

7295+
/// Creates a one-hop blinded path with [`ChannelManager::get_our_node_id`] as the introduction
7296+
/// node.
7297+
fn create_one_hop_blinded_path(&self) -> BlindedPath {
7298+
let entropy_source = self.entropy_source.deref();
7299+
let secp_ctx = &self.secp_ctx;
7300+
BlindedPath::one_hop_for_message(self.get_our_node_id(), entropy_source, secp_ctx).unwrap()
7301+
}
7302+
72117303
/// Gets a fake short channel id for use in receiving [phantom node payments]. These fake scids
72127304
/// are used when constructing the phantom invoice's route hints.
72137305
///

lightning/src/ln/msgs.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use core::str::FromStr;
4949
use crate::io::{self, Cursor, Read};
5050
use crate::io_extras::read_to_end;
5151

52-
use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
52+
use crate::events::MessageSendEventsProvider;
5353
use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
5454
use crate::util::logger;
5555
use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
@@ -1497,17 +1497,22 @@ pub trait RoutingMessageHandler : MessageSendEventsProvider {
14971497
fn provided_init_features(&self, their_node_id: &PublicKey) -> InitFeatures;
14981498
}
14991499

1500-
/// A trait to describe an object that can receive onion messages.
1501-
pub trait OnionMessageHandler : OnionMessageProvider {
1500+
/// A handler for received [`OnionMessage`]s and for providing generated ones to send.
1501+
pub trait OnionMessageHandler {
15021502
/// Handle an incoming `onion_message` message from the given peer.
15031503
fn handle_onion_message(&self, peer_node_id: &PublicKey, msg: &OnionMessage);
1504+
1505+
/// Returns the next pending onion message for the peer with the given node id.
1506+
fn next_onion_message_for_peer(&self, peer_node_id: PublicKey) -> Option<OnionMessage>;
1507+
15041508
/// Called when a connection is established with a peer. Can be used to track which peers
15051509
/// advertise onion message support and are online.
15061510
///
15071511
/// May return an `Err(())` if the features the peer supports are not sufficient to communicate
15081512
/// with us. Implementors should be somewhat conservative about doing so, however, as other
15091513
/// message handlers may still wish to communicate with this peer.
15101514
fn peer_connected(&self, their_node_id: &PublicKey, init: &Init, inbound: bool) -> Result<(), ()>;
1515+
15111516
/// Indicates a connection to the peer failed/an existing connection was lost. Allows handlers to
15121517
/// drop and refuse to forward onion messages to this peer.
15131518
fn peer_disconnected(&self, their_node_id: &PublicKey);

0 commit comments

Comments
 (0)