Skip to content

Introduce MessageContext and use it to allow abandon failed payments #3085

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
use bitcoin::hashes::Hash as TraitImport;
use bitcoin::WPubkeyHash;

use lightning::blinded_path::message::MessageContext;
use lightning::blinded_path::payment::ReceiveTlvs;
use lightning::blinded_path::BlindedPath;
use lightning::chain;
Expand Down Expand Up @@ -138,7 +139,8 @@ impl MessageRouter for FuzzRouter {
}

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

use lightning::blinded_path::message::MessageContext;
use lightning::blinded_path::payment::ReceiveTlvs;
use lightning::blinded_path::BlindedPath;
use lightning::chain;
Expand Down Expand Up @@ -175,7 +176,8 @@ impl MessageRouter for FuzzRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedPath>, ()> {
unreachable!()
}
Expand Down
22 changes: 17 additions & 5 deletions fuzz/src/invoice_request_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use crate::utils::test_logger;
use bitcoin::secp256k1::{self, Keypair, Parity, PublicKey, Secp256k1, SecretKey};
use core::convert::TryFrom;
use lightning::blinded_path::message::ForwardNode;
use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext};
use lightning::blinded_path::BlindedPath;
use lightning::ln::features::BlindedHopFeatures;
use lightning::ln::PaymentHash;
Expand Down Expand Up @@ -87,10 +87,22 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
],
];
let paths = vec![
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[0],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[1],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
];

let payinfo = vec![
Expand Down
24 changes: 16 additions & 8 deletions fuzz/src/onion_message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use bitcoin::secp256k1::ecdsa::RecoverableSignature;
use bitcoin::secp256k1::schnorr;
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};

use lightning::blinded_path::message::{MessageContext, OffersContext};
use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
use lightning::ln::features::InitFeatures;
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
Expand Down Expand Up @@ -94,7 +95,8 @@ impl MessageRouter for TestMessageRouter {
}

fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
_secp_ctx: &Secp256k1<T>,
) -> Result<Vec<BlindedPath>, ()> {
unreachable!()
}
Expand All @@ -104,7 +106,7 @@ struct TestOffersMessageHandler {}

impl OffersMessageHandler for TestOffersMessageHandler {
fn handle_message(
&self, _message: OffersMessage, _responder: Option<Responder>,
&self, _message: OffersMessage, _context: OffersContext, _responder: Option<Responder>,
) -> ResponseInstruction<OffersMessage> {
ResponseInstruction::NoResponse
}
Expand Down Expand Up @@ -152,7 +154,8 @@ struct TestCustomMessageHandler {}
impl CustomOnionMessageHandler for TestCustomMessageHandler {
type CustomMessage = TestCustomMessage;
fn handle_custom_message(
&self, message: Self::CustomMessage, responder: Option<Responder>,
&self, message: Self::CustomMessage, _context: Option<Vec<u8>>,
responder: Option<Responder>,
) -> ResponseInstruction<Self::CustomMessage> {
match responder {
Some(responder) => responder.respond(message),
Expand Down Expand Up @@ -342,13 +345,18 @@ mod tests {
super::do_test(&<Vec<u8>>::from_hex(one_hop_om).unwrap(), &logger);
{
let log_entries = logger.lines.lock().unwrap();
assert_eq!(
log_entries.get(&(
"lightning::onion_message::messenger".to_string(),
"Received an onion message with a reply_path: Custom(TestCustomMessage)"
.to_string()
)),
Some(&1)
);
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
"Received an onion message with path_id None and a reply_path: Custom(TestCustomMessage)"
.to_string())), Some(&1));
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
"Constructing onion message when responding with Custom Message to an onion message with path_id None: TestCustomMessage".to_string())), Some(&1));
"Constructing onion message when responding with Custom Message to an onion message: TestCustomMessage".to_string())), Some(&1));
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
"Buffered onion message when responding with Custom Message to an onion message with path_id None".to_string())), Some(&1));
"Buffered onion message when responding with Custom Message to an onion message".to_string())), Some(&1));
}

let two_unblinded_hops_om = "\
Expand Down
22 changes: 17 additions & 5 deletions fuzz/src/refund_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
use crate::utils::test_logger;
use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1, SecretKey};
use core::convert::TryFrom;
use lightning::blinded_path::message::ForwardNode;
use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext};
use lightning::blinded_path::BlindedPath;
use lightning::ln::features::BlindedHopFeatures;
use lightning::ln::PaymentHash;
Expand Down Expand Up @@ -76,10 +76,22 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
],
];
let paths = vec![
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[0],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
BlindedPath::new_for_message(
&intermediate_nodes[1],
pubkey(42),
MessageContext::Offers(OffersContext::Unknown {}),
&entropy_source,
secp_ctx,
)
.unwrap(),
];

let payinfo = vec![
Expand Down
57 changes: 52 additions & 5 deletions lightning/src/blinded_path/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NextMessage
use crate::blinded_path::utils;
use crate::io;
use crate::io::Cursor;
use crate::ln::channelmanager::PaymentId;
use crate::ln::onion_utils;
use crate::onion_message::packet::ControlTlvs;
use crate::sign::{NodeSigner, Recipient};
Expand Down Expand Up @@ -52,10 +53,10 @@ pub(crate) struct ForwardTlvs {

/// Similar to [`ForwardTlvs`], but these TLVs are for the final node.
pub(crate) struct ReceiveTlvs {
/// If `path_id` is `Some`, it is used to identify the blinded path that this onion message is
/// 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(crate) path_id: Option<[u8; 32]>,
pub context: Option<MessageContext>
}

impl Writeable for ForwardTlvs {
Expand All @@ -78,16 +79,62 @@ impl Writeable for ReceiveTlvs {
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
// TODO: write padding
encode_tlv_stream!(writer, {
(6, self.path_id, option),
(65537, self.context, option),
});
Ok(())
}
}

/// Represents additional data included by the recipient in a [`BlindedPath`].
///
/// This data is encrypted by the recipient and remains invisible to anyone else.
/// It is included in the [`BlindedPath`], making it accessible again to the recipient
/// whenever the [`BlindedPath`] is used.
/// The recipient can authenticate the message and utilize it for further processing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
/// The recipient can authenticate the message and utilize it for further processing
/// The recipient can use this data to authenticate the message or for further processing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll update this in #3139 since I'm touching this.

/// if needed.
#[derive(Clone, Debug)]
pub enum MessageContext {
/// Represents the data specific to [`OffersMessage`]
///
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
Offers(OffersContext),
/// Represents custom data received in a Custom Onion Message.
Custom(Vec<u8>),
}

/// Contains the data specific to [`OffersMessage`]
///
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
#[derive(Clone, Debug)]
pub enum OffersContext {
/// Represents an unknown BOLT12 payment context.
/// This variant is used when a message is sent without
/// using a [`BlindedPath`] or over one created prior to
/// LDK version 0.0.124.
Unknown {},
/// Represents an outbound BOLT12 payment context.
OutboundPayment {
/// Payment ID of the outbound BOLT12 payment.
payment_id: PaymentId
},
}

impl_writeable_tlv_based_enum!(MessageContext, ;
(0, Offers),
(1, Custom),
);

impl_writeable_tlv_based_enum!(OffersContext,
(0, Unknown) => {},
(1, OutboundPayment) => {
(0, payment_id, required),
},
;);

/// 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: &[ForwardNode], recipient_node_id: PublicKey,
session_priv: &SecretKey
context: MessageContext, session_priv: &SecretKey
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
let pks = intermediate_nodes.iter().map(|node| &node.node_id)
.chain(core::iter::once(&recipient_node_id));
Expand All @@ -99,7 +146,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
None => NextMessageHop::NodeId(*pubkey),
})
.map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None }))
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs{ context: Some(context) })));

utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv)
}
Expand Down
10 changes: 6 additions & 4 deletions lightning/src/blinded_path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod message;
pub(crate) mod utils;

use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
use message::MessageContext;
use core::ops::Deref;

use crate::ln::msgs::DecodeError;
Expand Down Expand Up @@ -123,9 +124,9 @@ pub struct BlindedHop {
impl BlindedPath {
/// Create a one-hop blinded path for a message.
pub fn one_hop_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
recipient_node_id: PublicKey, entropy_source: ES, secp_ctx: &Secp256k1<T>
recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>
) -> Result<Self, ()> where ES::Target: EntropySource {
Self::new_for_message(&[], recipient_node_id, entropy_source, secp_ctx)
Self::new_for_message(&[], recipient_node_id, context, entropy_source, secp_ctx)
}

/// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
Expand All @@ -135,7 +136,7 @@ impl BlindedPath {
// TODO: make all payloads the same size with padding + add dummy hops
pub fn new_for_message<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
intermediate_nodes: &[message::ForwardNode], recipient_node_id: PublicKey,
entropy_source: ES, secp_ctx: &Secp256k1<T>
context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>
) -> Result<Self, ()> where ES::Target: EntropySource {
let introduction_node = IntroductionNode::NodeId(
intermediate_nodes.first().map_or(recipient_node_id, |n| n.node_id)
Expand All @@ -147,7 +148,8 @@ impl BlindedPath {
introduction_node,
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
blinded_hops: message::blinded_hops(
secp_ctx, intermediate_nodes, recipient_node_id, &blinding_secret,
secp_ctx, intermediate_nodes, recipient_node_id,
context, &blinding_secret,
).map_err(|_| ())?,
})
}
Expand Down
Loading
Loading