Skip to content
6 changes: 3 additions & 3 deletions fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash as TraitImport;
use bitcoin::WPubkeyHash;

use lightning::blinded_path::message::{BlindedMessagePath, MessageContext};
use lightning::blinded_path::message::{self, BlindedMessagePath, MessageContext};
use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs};
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
Expand Down Expand Up @@ -138,8 +138,8 @@ impl MessageRouter for FuzzRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _recipient_tlvs: message::ReceiveTlvs,
_peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
unreachable!()
}
Expand Down
6 changes: 3 additions & 3 deletions fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use bitcoin::hashes::Hash as _;
use bitcoin::hex::FromHex;
use bitcoin::WPubkeyHash;

use lightning::blinded_path::message::{BlindedMessagePath, MessageContext};
use lightning::blinded_path::message::{self, BlindedMessagePath, MessageContext};
use lightning::blinded_path::payment::{BlindedPaymentPath, ReceiveTlvs};
use lightning::chain;
use lightning::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator};
Expand Down Expand Up @@ -171,8 +171,8 @@ impl MessageRouter for FuzzRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _recipient_tlvs: message::ReceiveTlvs,
_peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
unreachable!()
}
Expand Down
1 change: 1 addition & 0 deletions fuzz/src/invoice_request_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
htlc_minimum_msat: 1,
},
payment_context,
custom_data: None,
};
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
let intermediate_nodes = [PaymentForwardNode {
Expand Down
8 changes: 4 additions & 4 deletions fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use bitcoin::secp256k1::schnorr;
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};

use lightning::blinded_path::message::{
AsyncPaymentsContext, BlindedMessagePath, MessageContext, OffersContext,
AsyncPaymentsContext, BlindedMessagePath, MessageContext, OffersContext, ReceiveTlvs,
};
use lightning::blinded_path::EmptyNodeIdLookUp;
use lightning::ln::inbound_payment::ExpandedKey;
Expand Down Expand Up @@ -100,7 +100,7 @@ impl MessageRouter for TestMessageRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
&self, _recipient: PublicKey, _recipient_tlvs: ReceiveTlvs, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
unreachable!()
Expand All @@ -112,7 +112,7 @@ struct TestOffersMessageHandler {}
impl OffersMessageHandler for TestOffersMessageHandler {
fn handle_message(
&self, _message: OffersMessage, _context: Option<OffersContext>,
_responder: Option<Responder>,
_custom_data: Option<Vec<u8>>, _responder: Option<Responder>,
) -> Option<(OffersMessage, ResponseInstruction)> {
None
}
Expand Down Expand Up @@ -161,7 +161,7 @@ impl CustomOnionMessageHandler for TestCustomMessageHandler {
type CustomMessage = TestCustomMessage;
fn handle_custom_message(
&self, message: Self::CustomMessage, _context: Option<Vec<u8>>,
responder: Option<Responder>,
_custom_data: Option<Vec<u8>>, responder: Option<Responder>,
) -> Option<(Self::CustomMessage, ResponseInstruction)> {
match responder {
Some(responder) => Some((message, responder.respond())),
Expand Down
1 change: 1 addition & 0 deletions fuzz/src/refund_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
htlc_minimum_msat: 1,
},
payment_context,
custom_data: None,
};
let payee_tlvs = payee_tlvs.authenticate(nonce, &expanded_key);
let intermediate_nodes = [PaymentForwardNode {
Expand Down
24 changes: 16 additions & 8 deletions lightning-dns-resolver/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,11 @@ impl<PH: Deref> DNSResolverMessageHandler for OMDomainResolver<PH>
where
PH::Target: DNSResolverMessageHandler,
{
fn handle_dnssec_proof(&self, proof: DNSSECProof, context: DNSResolverContext) {
fn handle_dnssec_proof(
&self, proof: DNSSECProof, context: DNSResolverContext, custom_data: Option<Vec<u8>>,
) {
if let Some(proof_handler) = &self.proof_handler {
proof_handler.handle_dnssec_proof(proof, context);
proof_handler.handle_dnssec_proof(proof, context, custom_data);
}
}

Expand Down Expand Up @@ -159,7 +161,7 @@ mod test {
use bitcoin::secp256k1::{self, PublicKey, Secp256k1};
use bitcoin::Block;

use lightning::blinded_path::message::{BlindedMessagePath, MessageContext};
use lightning::blinded_path::message::{BlindedMessagePath, MessageContext, ReceiveTlvs};
use lightning::blinded_path::NodeIdLookUp;
use lightning::events::{Event, PaymentPurpose};
use lightning::ln::channelmanager::{PaymentId, Retry};
Expand Down Expand Up @@ -225,11 +227,13 @@ mod test {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, recipient: PublicKey, context: MessageContext, _peers: Vec<PublicKey>,
&self, recipient: PublicKey, recipient_tlvs: ReceiveTlvs, _peers: Vec<PublicKey>,
secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedMessagePath>, ()> {
let keys = KeysManager::new(&[0; 32], 42, 43);
Ok(vec![BlindedMessagePath::one_hop(recipient, context, &keys, secp_ctx).unwrap()])
Ok(vec![
BlindedMessagePath::one_hop(recipient, recipient_tlvs, &keys, secp_ctx).unwrap()
])
}
}
impl Deref for DirectlyConnectedRouter {
Expand All @@ -251,8 +255,11 @@ mod test {
panic!();
}

fn handle_dnssec_proof(&self, msg: DNSSECProof, context: DNSResolverContext) {
let mut proof = self.resolver.handle_dnssec_proof_for_uri(msg, context).unwrap();
fn handle_dnssec_proof(
&self, msg: DNSSECProof, context: DNSResolverContext, custom_data: Option<Vec<u8>>,
) {
let mut proof =
self.resolver.handle_dnssec_proof_for_uri(msg, context, custom_data).unwrap();
assert_eq!(proof.0.len(), 1);
let payment = proof.0.pop().unwrap();
let mut result = Some((payment.0, payment.1, proof.1));
Expand Down Expand Up @@ -330,7 +337,8 @@ mod test {

let (msg, context) =
payer.resolver.resolve_name(payment_id, name.clone(), &*payer_keys).unwrap();
let query_context = MessageContext::DNSResolver(context);
let query_context =
ReceiveTlvs { context: Some(MessageContext::DNSResolver(context)), custom_data: None };
let reply_path =
BlindedMessagePath::one_hop(payer_id, query_context, &*payer_keys, &secp_ctx).unwrap();
payer.pending_messages.lock().unwrap().push((
Expand Down
33 changes: 25 additions & 8 deletions lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,13 +55,13 @@ impl Readable for BlindedMessagePath {
impl BlindedMessagePath {
/// Create a one-hop blinded path for a message.
pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES,
recipient_node_id: PublicKey, recipient_tlvs: ReceiveTlvs, entropy_source: ES,
secp_ctx: &Secp256k1<T>,
) -> Result<Self, ()>
where
ES::Target: EntropySource,
{
Self::new(&[], recipient_node_id, context, entropy_source, secp_ctx)
Self::new(&[], recipient_node_id, recipient_tlvs, entropy_source, secp_ctx)
}

/// Create a path for an onion message, to be forwarded along `node_pks`. The last node
Expand All @@ -71,7 +71,7 @@ impl BlindedMessagePath {
// TODO: make all payloads the same size with padding + add dummy hops
pub fn new<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>,
recipient_tlvs: ReceiveTlvs, entropy_source: ES, secp_ctx: &Secp256k1<T>,
) -> Result<Self, ()>
where
ES::Target: EntropySource,
Expand All @@ -90,7 +90,7 @@ impl BlindedMessagePath {
secp_ctx,
intermediate_nodes,
recipient_node_id,
context,
recipient_tlvs,
&blinding_secret,
)
.map_err(|_| ())?,
Expand Down Expand Up @@ -251,12 +251,28 @@ pub(crate) struct ForwardTlvs {
pub(crate) next_blinding_override: Option<PublicKey>,
}

/// Similar to [`ForwardTlvs`], but these TLVs are for the final node.
pub(crate) struct ReceiveTlvs {
/// TLVs to encode in the final onion message packet's hop data. These TLVs are specific to the
/// recipient node and provide information necessary for final processing of the message.
/// When provided in a blinded route, they are encoded into [`BlindedHop::encrypted_payload`].
#[derive(Clone)]
pub struct ReceiveTlvs {
/// If `context` is `Some`, it is used to identify the blinded path that this onion message is
/// sending to. This is useful for receivers to check that said blinded path is being used in
/// the right context.
pub context: Option<MessageContext>,

/// Custom data set by the user. If `custom_data` is `Some`, it will be provided to the message
/// recipient when the blinded path is used.
///
/// This field allows encoding custom data intended to be provided back when the blinded path is used.
///
/// ## Note on Forward Compatibility:
/// Users can encode any kind of data into the `Vec<u8>` bytes here. However, they should ensure
/// that the data is structured in a forward-compatible manner. This is especially important as
/// `ReceiveTlvs` created in one version of the software may still appear in messages received
/// shortly after a software upgrade. Proper forward compatibility helps prevent data loss or
/// misinterpretation in future versions.
pub custom_data: Option<Vec<u8>>,
}

impl Writeable for ForwardTlvs {
Expand All @@ -280,6 +296,7 @@ impl Writeable for ReceiveTlvs {
// TODO: write padding
encode_tlv_stream!(writer, {
(65537, self.context, option),
(65539, self.custom_data, option)
});
Ok(())
}
Expand Down Expand Up @@ -498,7 +515,7 @@ impl_writeable_tlv_based!(DNSResolverContext, {
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[MessageForwardNode],
recipient_node_id: PublicKey, context: MessageContext, session_priv: &SecretKey,
recipient_node_id: PublicKey, recipient_tlvs: ReceiveTlvs, session_priv: &SecretKey,
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
let pks = intermediate_nodes
.iter()
Expand All @@ -515,7 +532,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
.map(|next_hop| {
ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None })
})
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: Some(context) })));
.chain(core::iter::once(ControlTlvs::Receive(recipient_tlvs)));

let path = pks.zip(tlvs);

Expand Down
18 changes: 18 additions & 0 deletions lightning/src/blinded_path/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,15 @@ pub struct UnauthenticatedReceiveTlvs {
pub payment_constraints: PaymentConstraints,
/// Context for the receiver of this payment.
pub payment_context: PaymentContext,
/// Custom data set by the user. And is returned back when the blinded path is used.
///
/// ## Note on Forward Compatibility:
/// Users can encode any kind of data into the `Vec<u8>` bytes here. However, they should ensure
/// that the data is structured in a forward-compatible manner. This is especially important as
/// `ReceiveTlvs` created in one version of the software may still appear in payments received
/// shortly after a software upgrade. Proper forward compatibility helps prevent data loss or
/// misinterpretation in future versions.
pub custom_data: Option<Vec<u8>>,
}

impl UnauthenticatedReceiveTlvs {
Expand Down Expand Up @@ -490,6 +499,7 @@ impl Writeable for ReceiveTlvs {
(65536, self.tlvs.payment_secret, required),
(65537, self.tlvs.payment_context, required),
(65539, self.authentication, required),
(65541, self.tlvs.custom_data, required)
});
Ok(())
}
Expand All @@ -501,6 +511,7 @@ impl Writeable for UnauthenticatedReceiveTlvs {
(12, self.payment_constraints, required),
(65536, self.payment_secret, required),
(65537, self.payment_context, required),
(65541, self.custom_data, (default_value, Vec::new())),
});
Ok(())
}
Expand Down Expand Up @@ -529,6 +540,7 @@ impl Readable for BlindedPaymentTlvs {
(65536, payment_secret, option),
(65537, payment_context, option),
(65539, authentication, option),
(65541, custom_data, option)
});
let _padding: Option<utils::Padding> = _padding;

Expand All @@ -552,6 +564,7 @@ impl Readable for BlindedPaymentTlvs {
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
payment_constraints: payment_constraints.0.unwrap(),
payment_context: payment_context.ok_or(DecodeError::InvalidValue)?,
custom_data: custom_data.ok_or(DecodeError::InvalidValue)?,
},
authentication: authentication.ok_or(DecodeError::InvalidValue)?,
}))
Expand Down Expand Up @@ -794,6 +807,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]),
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
custom_data: None,
};
let htlc_maximum_msat = 100_000;
let blinded_payinfo =
Expand All @@ -812,6 +826,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]),
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
custom_data: None,
};
let blinded_payinfo =
super::compute_payinfo(&[], &recv_tlvs, 4242, TEST_FINAL_CLTV as u16).unwrap();
Expand Down Expand Up @@ -869,6 +884,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]),
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 3 },
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
custom_data: None,
};
let htlc_maximum_msat = 100_000;
let blinded_payinfo = super::compute_payinfo(
Expand Down Expand Up @@ -928,6 +944,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]),
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
custom_data: None,
};
let htlc_minimum_msat = 3798;
assert!(super::compute_payinfo(
Expand Down Expand Up @@ -997,6 +1014,7 @@ mod tests {
payment_secret: PaymentSecret([0; 32]),
payment_constraints: PaymentConstraints { max_cltv_expiry: 0, htlc_minimum_msat: 1 },
payment_context: PaymentContext::Bolt12Refund(Bolt12RefundContext {}),
custom_data: None,
};

let blinded_payinfo = super::compute_payinfo(
Expand Down
12 changes: 6 additions & 6 deletions lightning/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,15 +705,15 @@ pub enum Event {
/// [`ChannelManager::fail_htlc_backwards`] or [`ChannelManager::fail_htlc_backwards_with_reason`]
/// to free up resources for this HTLC and avoid network congestion.
///
/// If [`Event::PaymentClaimable::onion_fields`] is `Some`, and includes custom TLVs with even type
/// If [`Event::PaymentClaimable::onion_fields`] is `Some`, and includes sender custom TLVs with even type
/// numbers, you should use [`ChannelManager::fail_htlc_backwards_with_reason`] with
/// [`FailureCode::InvalidOnionPayload`] if you fail to understand and handle the contents, or
/// [`ChannelManager::claim_funds_with_known_custom_tlvs`] upon successful handling.
/// If you don't intend to check for custom TLVs, you can simply use
/// [`ChannelManager::claim_funds`], which will automatically fail back even custom TLVs.
/// [`ChannelManager::claim_funds_with_known_sender_custom_tlvs`] upon successful handling.
/// If you don't intend to check for sender custom TLVs, you can simply use
/// [`ChannelManager::claim_funds`], which will automatically fail back even sender custom TLVs.
///
/// If you fail to call [`ChannelManager::claim_funds`],
/// [`ChannelManager::claim_funds_with_known_custom_tlvs`],
/// [`ChannelManager::claim_funds_with_known_sender_custom_tlvs`],
/// [`ChannelManager::fail_htlc_backwards`], or
/// [`ChannelManager::fail_htlc_backwards_with_reason`] within the HTLC's timeout, the HTLC will
/// be automatically failed.
Expand All @@ -732,7 +732,7 @@ pub enum Event {
/// returning `Err(ReplayEvent ())`) and will be persisted across restarts.
///
/// [`ChannelManager::claim_funds`]: crate::ln::channelmanager::ChannelManager::claim_funds
/// [`ChannelManager::claim_funds_with_known_custom_tlvs`]: crate::ln::channelmanager::ChannelManager::claim_funds_with_known_custom_tlvs
/// [`ChannelManager::claim_funds_with_known_sender_custom_tlvs`]: crate::ln::channelmanager::ChannelManager::claim_funds_with_known_sender_custom_tlvs
/// [`FailureCode::InvalidOnionPayload`]: crate::ln::channelmanager::FailureCode::InvalidOnionPayload
/// [`ChannelManager::fail_htlc_backwards`]: crate::ln::channelmanager::ChannelManager::fail_htlc_backwards
/// [`ChannelManager::fail_htlc_backwards_with_reason`]: crate::ln::channelmanager::ChannelManager::fail_htlc_backwards_with_reason
Expand Down
Loading
Loading