Skip to content

Commit 6035c83

Browse files
authored
Merge pull request #3085 from shaavan/issue2837
Introduce RecipientData and use it to allow abandon failed payments
2 parents 1a71725 + 42c096c commit 6035c83

17 files changed

+311
-144
lines changed

fuzz/src/chanmon_consistency.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
3333
use bitcoin::hashes::Hash as TraitImport;
3434
use bitcoin::WPubkeyHash;
3535

36+
use lightning::blinded_path::message::MessageContext;
3637
use lightning::blinded_path::payment::ReceiveTlvs;
3738
use lightning::blinded_path::BlindedPath;
3839
use lightning::chain;
@@ -138,7 +139,8 @@ impl MessageRouter for FuzzRouter {
138139
}
139140

140141
fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
141-
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
142+
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
143+
_secp_ctx: &Secp256k1<T>,
142144
) -> Result<Vec<BlindedPath>, ()> {
143145
unreachable!()
144146
}

fuzz/src/full_stack.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ use bitcoin::hashes::sha256d::Hash as Sha256dHash;
3030
use bitcoin::hashes::Hash as _;
3131
use bitcoin::WPubkeyHash;
3232

33+
use lightning::blinded_path::message::MessageContext;
3334
use lightning::blinded_path::payment::ReceiveTlvs;
3435
use lightning::blinded_path::BlindedPath;
3536
use lightning::chain;
@@ -175,7 +176,8 @@ impl MessageRouter for FuzzRouter {
175176
}
176177

177178
fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
178-
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
179+
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
180+
_secp_ctx: &Secp256k1<T>,
179181
) -> Result<Vec<BlindedPath>, ()> {
180182
unreachable!()
181183
}

fuzz/src/invoice_request_deser.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use crate::utils::test_logger;
1111
use bitcoin::secp256k1::{self, Keypair, Parity, PublicKey, Secp256k1, SecretKey};
1212
use core::convert::TryFrom;
13-
use lightning::blinded_path::message::ForwardNode;
13+
use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext};
1414
use lightning::blinded_path::BlindedPath;
1515
use lightning::ln::features::BlindedHopFeatures;
1616
use lightning::ln::PaymentHash;
@@ -87,10 +87,22 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
8787
],
8888
];
8989
let paths = vec![
90-
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx)
91-
.unwrap(),
92-
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx)
93-
.unwrap(),
90+
BlindedPath::new_for_message(
91+
&intermediate_nodes[0],
92+
pubkey(42),
93+
MessageContext::Offers(OffersContext::Unknown {}),
94+
&entropy_source,
95+
secp_ctx,
96+
)
97+
.unwrap(),
98+
BlindedPath::new_for_message(
99+
&intermediate_nodes[1],
100+
pubkey(42),
101+
MessageContext::Offers(OffersContext::Unknown {}),
102+
&entropy_source,
103+
secp_ctx,
104+
)
105+
.unwrap(),
94106
];
95107

96108
let payinfo = vec![

fuzz/src/onion_message.rs

+16-8
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use bitcoin::secp256k1::ecdsa::RecoverableSignature;
66
use bitcoin::secp256k1::schnorr;
77
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};
88

9+
use lightning::blinded_path::message::{MessageContext, OffersContext};
910
use lightning::blinded_path::{BlindedPath, EmptyNodeIdLookUp};
1011
use lightning::ln::features::InitFeatures;
1112
use lightning::ln::msgs::{self, DecodeError, OnionMessageHandler};
@@ -94,7 +95,8 @@ impl MessageRouter for TestMessageRouter {
9495
}
9596

9697
fn create_blinded_paths<T: secp256k1::Signing + secp256k1::Verification>(
97-
&self, _recipient: PublicKey, _peers: Vec<PublicKey>, _secp_ctx: &Secp256k1<T>,
98+
&self, _recipient: PublicKey, _context: MessageContext, _peers: Vec<PublicKey>,
99+
_secp_ctx: &Secp256k1<T>,
98100
) -> Result<Vec<BlindedPath>, ()> {
99101
unreachable!()
100102
}
@@ -104,7 +106,7 @@ struct TestOffersMessageHandler {}
104106

105107
impl OffersMessageHandler for TestOffersMessageHandler {
106108
fn handle_message(
107-
&self, _message: OffersMessage, _responder: Option<Responder>,
109+
&self, _message: OffersMessage, _context: OffersContext, _responder: Option<Responder>,
108110
) -> ResponseInstruction<OffersMessage> {
109111
ResponseInstruction::NoResponse
110112
}
@@ -152,7 +154,8 @@ struct TestCustomMessageHandler {}
152154
impl CustomOnionMessageHandler for TestCustomMessageHandler {
153155
type CustomMessage = TestCustomMessage;
154156
fn handle_custom_message(
155-
&self, message: Self::CustomMessage, responder: Option<Responder>,
157+
&self, message: Self::CustomMessage, _context: Option<Vec<u8>>,
158+
responder: Option<Responder>,
156159
) -> ResponseInstruction<Self::CustomMessage> {
157160
match responder {
158161
Some(responder) => responder.respond(message),
@@ -342,13 +345,18 @@ mod tests {
342345
super::do_test(&<Vec<u8>>::from_hex(one_hop_om).unwrap(), &logger);
343346
{
344347
let log_entries = logger.lines.lock().unwrap();
348+
assert_eq!(
349+
log_entries.get(&(
350+
"lightning::onion_message::messenger".to_string(),
351+
"Received an onion message with a reply_path: Custom(TestCustomMessage)"
352+
.to_string()
353+
)),
354+
Some(&1)
355+
);
345356
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
346-
"Received an onion message with path_id None and a reply_path: Custom(TestCustomMessage)"
347-
.to_string())), Some(&1));
348-
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
349-
"Constructing onion message when responding with Custom Message to an onion message with path_id None: TestCustomMessage".to_string())), Some(&1));
357+
"Constructing onion message when responding with Custom Message to an onion message: TestCustomMessage".to_string())), Some(&1));
350358
assert_eq!(log_entries.get(&("lightning::onion_message::messenger".to_string(),
351-
"Buffered onion message when responding with Custom Message to an onion message with path_id None".to_string())), Some(&1));
359+
"Buffered onion message when responding with Custom Message to an onion message".to_string())), Some(&1));
352360
}
353361

354362
let two_unblinded_hops_om = "\

fuzz/src/refund_deser.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
use crate::utils::test_logger;
1111
use bitcoin::secp256k1::{self, Keypair, PublicKey, Secp256k1, SecretKey};
1212
use core::convert::TryFrom;
13-
use lightning::blinded_path::message::ForwardNode;
13+
use lightning::blinded_path::message::{ForwardNode, MessageContext, OffersContext};
1414
use lightning::blinded_path::BlindedPath;
1515
use lightning::ln::features::BlindedHopFeatures;
1616
use lightning::ln::PaymentHash;
@@ -76,10 +76,22 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
7676
],
7777
];
7878
let paths = vec![
79-
BlindedPath::new_for_message(&intermediate_nodes[0], pubkey(42), &entropy_source, secp_ctx)
80-
.unwrap(),
81-
BlindedPath::new_for_message(&intermediate_nodes[1], pubkey(42), &entropy_source, secp_ctx)
82-
.unwrap(),
79+
BlindedPath::new_for_message(
80+
&intermediate_nodes[0],
81+
pubkey(42),
82+
MessageContext::Offers(OffersContext::Unknown {}),
83+
&entropy_source,
84+
secp_ctx,
85+
)
86+
.unwrap(),
87+
BlindedPath::new_for_message(
88+
&intermediate_nodes[1],
89+
pubkey(42),
90+
MessageContext::Offers(OffersContext::Unknown {}),
91+
&entropy_source,
92+
secp_ctx,
93+
)
94+
.unwrap(),
8395
];
8496

8597
let payinfo = vec![

lightning/src/blinded_path/message.rs

+52-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use crate::blinded_path::{BlindedHop, BlindedPath, IntroductionNode, NextMessage
2020
use crate::blinded_path::utils;
2121
use crate::io;
2222
use crate::io::Cursor;
23+
use crate::ln::channelmanager::PaymentId;
2324
use crate::ln::onion_utils;
2425
use crate::onion_message::packet::ControlTlvs;
2526
use crate::sign::{NodeSigner, Recipient};
@@ -52,10 +53,10 @@ pub(crate) struct ForwardTlvs {
5253

5354
/// Similar to [`ForwardTlvs`], but these TLVs are for the final node.
5455
pub(crate) struct ReceiveTlvs {
55-
/// If `path_id` is `Some`, it is used to identify the blinded path that this onion message is
56+
/// If `context` is `Some`, it is used to identify the blinded path that this onion message is
5657
/// sending to. This is useful for receivers to check that said blinded path is being used in
5758
/// the right context.
58-
pub(crate) path_id: Option<[u8; 32]>,
59+
pub context: Option<MessageContext>
5960
}
6061

6162
impl Writeable for ForwardTlvs {
@@ -78,16 +79,62 @@ impl Writeable for ReceiveTlvs {
7879
fn write<W: Writer>(&self, writer: &mut W) -> Result<(), io::Error> {
7980
// TODO: write padding
8081
encode_tlv_stream!(writer, {
81-
(6, self.path_id, option),
82+
(65537, self.context, option),
8283
});
8384
Ok(())
8485
}
8586
}
8687

88+
/// Represents additional data included by the recipient in a [`BlindedPath`].
89+
///
90+
/// This data is encrypted by the recipient and remains invisible to anyone else.
91+
/// It is included in the [`BlindedPath`], making it accessible again to the recipient
92+
/// whenever the [`BlindedPath`] is used.
93+
/// The recipient can authenticate the message and utilize it for further processing
94+
/// if needed.
95+
#[derive(Clone, Debug)]
96+
pub enum MessageContext {
97+
/// Represents the data specific to [`OffersMessage`]
98+
///
99+
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
100+
Offers(OffersContext),
101+
/// Represents custom data received in a Custom Onion Message.
102+
Custom(Vec<u8>),
103+
}
104+
105+
/// Contains the data specific to [`OffersMessage`]
106+
///
107+
/// [`OffersMessage`]: crate::onion_message::offers::OffersMessage
108+
#[derive(Clone, Debug)]
109+
pub enum OffersContext {
110+
/// Represents an unknown BOLT12 payment context.
111+
/// This variant is used when a message is sent without
112+
/// using a [`BlindedPath`] or over one created prior to
113+
/// LDK version 0.0.124.
114+
Unknown {},
115+
/// Represents an outbound BOLT12 payment context.
116+
OutboundPayment {
117+
/// Payment ID of the outbound BOLT12 payment.
118+
payment_id: PaymentId
119+
},
120+
}
121+
122+
impl_writeable_tlv_based_enum!(MessageContext, ;
123+
(0, Offers),
124+
(1, Custom),
125+
);
126+
127+
impl_writeable_tlv_based_enum!(OffersContext,
128+
(0, Unknown) => {},
129+
(1, OutboundPayment) => {
130+
(0, payment_id, required),
131+
},
132+
;);
133+
87134
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
88135
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
89136
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[ForwardNode], recipient_node_id: PublicKey,
90-
session_priv: &SecretKey
137+
context: MessageContext, session_priv: &SecretKey
91138
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
92139
let pks = intermediate_nodes.iter().map(|node| &node.node_id)
93140
.chain(core::iter::once(&recipient_node_id));
@@ -99,7 +146,7 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
99146
None => NextMessageHop::NodeId(*pubkey),
100147
})
101148
.map(|next_hop| ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None }))
102-
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { path_id: None })));
149+
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs{ context: Some(context) })));
103150

104151
utils::construct_blinded_hops(secp_ctx, pks, tlvs, session_priv)
105152
}

lightning/src/blinded_path/mod.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ pub mod message;
1414
pub(crate) mod utils;
1515

1616
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey};
17+
use message::MessageContext;
1718
use core::ops::Deref;
1819

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

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

0 commit comments

Comments
 (0)