Skip to content

Commit

Permalink
Merge pull request lightningdevkit#3085 from shaavan/issue2837
Browse files Browse the repository at this point in the history
Introduce RecipientData and use it to allow abandon failed payments
  • Loading branch information
TheBlueMatt authored Jul 9, 2024
2 parents 1a71725 + 42c096c commit 6035c83
Show file tree
Hide file tree
Showing 17 changed files with 311 additions and 144 deletions.
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
/// 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

0 comments on commit 6035c83

Please sign in to comment.