Skip to content

Commit 61153f1

Browse files
authored
Merge pull request #3245 from valentinewallace/2024-07-blinded-pay-path-refactor
Move `BlindedPayInfo` into `BlindedPaymentPath`
2 parents fb4403f + 4ef83a0 commit 61153f1

20 files changed

+330
-309
lines changed

fuzz/src/chanmon_consistency.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ use lightning::ln::msgs::{
5656
};
5757
use lightning::ln::script::ShutdownScript;
5858
use lightning::ln::types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
59-
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
59+
use lightning::offers::invoice::UnsignedBolt12Invoice;
6060
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
6161
use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
6262
use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RouteParameters, Router};
@@ -126,7 +126,7 @@ impl Router for FuzzRouter {
126126
fn create_blinded_payment_paths<T: secp256k1::Signing + secp256k1::Verification>(
127127
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
128128
_amount_msats: u64, _secp_ctx: &Secp256k1<T>,
129-
) -> Result<Vec<(BlindedPayInfo, BlindedPaymentPath)>, ()> {
129+
) -> Result<Vec<BlindedPaymentPath>, ()> {
130130
unreachable!()
131131
}
132132
}

fuzz/src/full_stack.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use lightning::ln::peer_handler::{
4949
};
5050
use lightning::ln::script::ShutdownScript;
5151
use lightning::ln::types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
52-
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
52+
use lightning::offers::invoice::UnsignedBolt12Invoice;
5353
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
5454
use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
5555
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
@@ -163,7 +163,7 @@ impl Router for FuzzRouter {
163163
fn create_blinded_payment_paths<T: secp256k1::Signing + secp256k1::Verification>(
164164
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
165165
_amount_msats: u64, _secp_ctx: &Secp256k1<T>,
166-
) -> Result<Vec<(BlindedPayInfo, BlindedPaymentPath)>, ()> {
166+
) -> Result<Vec<BlindedPaymentPath>, ()> {
167167
unreachable!()
168168
}
169169
}

fuzz/src/router.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,14 @@ use bitcoin::constants::ChainHash;
1212
use bitcoin::script::Builder;
1313
use bitcoin::transaction::TxOut;
1414

15-
use lightning::blinded_path::payment::BlindedPaymentPath;
15+
use lightning::blinded_path::payment::{BlindedPayInfo, BlindedPaymentPath};
1616
use lightning::blinded_path::BlindedHop;
1717
use lightning::chain::transaction::OutPoint;
1818
use lightning::ln::channel_state::{ChannelCounterparty, ChannelDetails, ChannelShutdownState};
1919
use lightning::ln::channelmanager;
2020
use lightning::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
2121
use lightning::ln::msgs;
2222
use lightning::ln::types::ChannelId;
23-
use lightning::offers::invoice::BlindedPayInfo;
2423
use lightning::routing::gossip::{NetworkGraph, RoutingFees};
2524
use lightning::routing::router::{
2625
find_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters,
@@ -381,7 +380,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
381380
let mut last_hops_unblinded = Vec::new();
382381
last_hops!(last_hops_unblinded);
383382
let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
384-
let last_hops: Vec<(BlindedPayInfo, BlindedPaymentPath)> = last_hops_unblinded
383+
let last_hops: Vec<BlindedPaymentPath> = last_hops_unblinded
385384
.into_iter()
386385
.map(|hint| {
387386
let hop = &hint.0[0];
@@ -401,9 +400,11 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
401400
encrypted_payload: Vec::new(),
402401
});
403402
}
404-
(
403+
BlindedPaymentPath::from_raw(
404+
hop.src_node_id,
405+
dummy_pk,
406+
blinded_hops,
405407
payinfo,
406-
BlindedPaymentPath::from_raw(hop.src_node_id, dummy_pk, blinded_hops),
407408
)
408409
})
409410
.collect();

lightning/src/blinded_path/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::prelude::*;
2626
/// Onion messages and payments can be sent and received to blinded paths, which serve to hide the
2727
/// identity of the recipient.
2828
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
29-
struct BlindedPath {
29+
pub(crate) struct BlindedPath {
3030
/// To send to a blinded path, the sender first finds a route to the unblinded
3131
/// `introduction_node`, which can unblind its [`encrypted_payload`] to find out the onion
3232
/// message or payment's next hop and forward it along.

lightning/src/blinded_path/payment.rs

+83-39
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use crate::ln::channel_state::CounterpartyForwardingInfo;
2121
use crate::ln::features::BlindedHopFeatures;
2222
use crate::ln::msgs::DecodeError;
2323
use crate::ln::onion_utils;
24-
use crate::offers::invoice::BlindedPayInfo;
2524
use crate::offers::invoice_request::InvoiceRequestFields;
2625
use crate::offers::offer::OfferId;
2726
use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
@@ -34,29 +33,59 @@ use core::ops::Deref;
3433
#[allow(unused_imports)]
3534
use crate::prelude::*;
3635

37-
/// A blinded path to be used for sending or receiving a payment, hiding the identity of the
38-
/// recipient.
39-
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
40-
pub struct BlindedPaymentPath(pub(super) BlindedPath);
36+
/// Information needed to route a payment across a [`BlindedPaymentPath`].
37+
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
38+
pub struct BlindedPayInfo {
39+
/// Base fee charged (in millisatoshi) for the entire blinded path.
40+
pub fee_base_msat: u32,
4141

42-
impl Writeable for BlindedPaymentPath {
43-
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
44-
self.0.write(w)
45-
}
42+
/// Liquidity fee charged (in millionths of the amount transferred) for the entire blinded path
43+
/// (i.e., 10,000 is 1%).
44+
pub fee_proportional_millionths: u32,
45+
46+
/// Number of blocks subtracted from an incoming HTLC's `cltv_expiry` for the entire blinded
47+
/// path.
48+
pub cltv_expiry_delta: u16,
49+
50+
/// The minimum HTLC value (in millisatoshi) that is acceptable to all channel peers on the
51+
/// blinded path from the introduction node to the recipient, accounting for any fees, i.e., as
52+
/// seen by the recipient.
53+
pub htlc_minimum_msat: u64,
54+
55+
/// The maximum HTLC value (in millisatoshi) that is acceptable to all channel peers on the
56+
/// blinded path from the introduction node to the recipient, accounting for any fees, i.e., as
57+
/// seen by the recipient.
58+
pub htlc_maximum_msat: u64,
59+
60+
/// Features set in `encrypted_data_tlv` for the `encrypted_recipient_data` TLV record in an
61+
/// onion payload.
62+
pub features: BlindedHopFeatures,
4663
}
4764

48-
impl Readable for BlindedPaymentPath {
49-
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
50-
Ok(Self(BlindedPath::read(r)?))
51-
}
65+
impl_writeable!(BlindedPayInfo, {
66+
fee_base_msat,
67+
fee_proportional_millionths,
68+
cltv_expiry_delta,
69+
htlc_minimum_msat,
70+
htlc_maximum_msat,
71+
features
72+
});
73+
74+
/// A blinded path to be used for sending or receiving a payment, hiding the identity of the
75+
/// recipient.
76+
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
77+
pub struct BlindedPaymentPath {
78+
pub(super) inner_path: BlindedPath,
79+
/// The [`BlindedPayInfo`] used to pay this blinded path.
80+
pub payinfo: BlindedPayInfo,
5281
}
5382

5483
impl BlindedPaymentPath {
5584
/// Create a one-hop blinded path for a payment.
5685
pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
5786
payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs, min_final_cltv_expiry_delta: u16,
5887
entropy_source: ES, secp_ctx: &Secp256k1<T>
59-
) -> Result<(BlindedPayInfo, Self), ()> where ES::Target: EntropySource {
88+
) -> Result<Self, ()> where ES::Target: EntropySource {
6089
// This value is not considered in pathfinding for 1-hop blinded paths, because it's intended to
6190
// be in relation to a specific channel.
6291
let htlc_maximum_msat = u64::max_value();
@@ -77,7 +106,7 @@ impl BlindedPaymentPath {
77106
intermediate_nodes: &[ForwardNode], payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs,
78107
htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16, entropy_source: ES,
79108
secp_ctx: &Secp256k1<T>
80-
) -> Result<(BlindedPayInfo, Self), ()> where ES::Target: EntropySource {
109+
) -> Result<Self, ()> where ES::Target: EntropySource {
81110
let introduction_node = IntroductionNode::NodeId(
82111
intermediate_nodes.first().map_or(payee_node_id, |n| n.node_id)
83112
);
@@ -87,38 +116,41 @@ impl BlindedPaymentPath {
87116
let blinded_payinfo = compute_payinfo(
88117
intermediate_nodes, &payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
89118
)?;
90-
Ok((blinded_payinfo, Self(BlindedPath {
91-
introduction_node,
92-
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
93-
blinded_hops: blinded_hops(
94-
secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, &blinding_secret
95-
).map_err(|_| ())?,
96-
})))
119+
Ok(Self {
120+
inner_path: BlindedPath {
121+
introduction_node,
122+
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
123+
blinded_hops: blinded_hops(
124+
secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, &blinding_secret
125+
).map_err(|_| ())?,
126+
},
127+
payinfo: blinded_payinfo
128+
})
97129
}
98130

99131
/// Returns the introduction [`NodeId`] of the blinded path, if it is publicly reachable (i.e.,
100132
/// it is found in the network graph).
101133
pub fn public_introduction_node_id<'a>(
102134
&self, network_graph: &'a ReadOnlyNetworkGraph
103135
) -> Option<&'a NodeId> {
104-
self.0.public_introduction_node_id(network_graph)
136+
self.inner_path.public_introduction_node_id(network_graph)
105137
}
106138

107139
/// The [`IntroductionNode`] of the blinded path.
108140
pub fn introduction_node(&self) -> &IntroductionNode {
109-
&self.0.introduction_node
141+
&self.inner_path.introduction_node
110142
}
111143

112144
/// Used by the [`IntroductionNode`] to decrypt its [`encrypted_payload`] to forward the payment.
113145
///
114146
/// [`encrypted_payload`]: BlindedHop::encrypted_payload
115147
pub fn blinding_point(&self) -> PublicKey {
116-
self.0.blinding_point
148+
self.inner_path.blinding_point
117149
}
118150

119151
/// The [`BlindedHop`]s within the blinded path.
120152
pub fn blinded_hops(&self) -> &[BlindedHop] {
121-
&self.0.blinded_hops
153+
&self.inner_path.blinded_hops
122154
}
123155

124156
/// Advance the blinded onion payment path by one hop, making the second hop into the new
@@ -133,9 +165,9 @@ impl BlindedPaymentPath {
133165
NL::Target: NodeIdLookUp,
134166
T: secp256k1::Signing + secp256k1::Verification,
135167
{
136-
let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &self.0.blinding_point, None)?;
168+
let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &self.inner_path.blinding_point, None)?;
137169
let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
138-
let encrypted_control_tlvs = &self.0.blinded_hops.get(0).ok_or(())?.encrypted_payload;
170+
let encrypted_control_tlvs = &self.inner_path.blinded_hops.get(0).ok_or(())?.encrypted_payload;
139171
let mut s = Cursor::new(encrypted_control_tlvs);
140172
let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
141173
match ChaChaPolyReadAdapter::read(&mut reader, rho) {
@@ -147,31 +179,43 @@ impl BlindedPaymentPath {
147179
None => return Err(()),
148180
};
149181
let mut new_blinding_point = onion_utils::next_hop_pubkey(
150-
secp_ctx, self.0.blinding_point, control_tlvs_ss.as_ref()
182+
secp_ctx, self.inner_path.blinding_point, control_tlvs_ss.as_ref()
151183
).map_err(|_| ())?;
152-
mem::swap(&mut self.0.blinding_point, &mut new_blinding_point);
153-
self.0.introduction_node = IntroductionNode::NodeId(next_node_id);
154-
self.0.blinded_hops.remove(0);
184+
mem::swap(&mut self.inner_path.blinding_point, &mut new_blinding_point);
185+
self.inner_path.introduction_node = IntroductionNode::NodeId(next_node_id);
186+
self.inner_path.blinded_hops.remove(0);
155187
Ok(())
156188
},
157189
_ => Err(())
158190
}
159191
}
160192

193+
pub(crate) fn inner_blinded_path(&self) -> &BlindedPath {
194+
&self.inner_path
195+
}
196+
197+
pub(crate) fn from_parts(inner_path: BlindedPath, payinfo: BlindedPayInfo) -> Self {
198+
Self { inner_path, payinfo }
199+
}
200+
161201
#[cfg(any(test, fuzzing))]
162202
pub fn from_raw(
163-
introduction_node_id: PublicKey, blinding_point: PublicKey, blinded_hops: Vec<BlindedHop>
203+
introduction_node_id: PublicKey, blinding_point: PublicKey, blinded_hops: Vec<BlindedHop>,
204+
payinfo: BlindedPayInfo
164205
) -> Self {
165-
Self(BlindedPath {
166-
introduction_node: IntroductionNode::NodeId(introduction_node_id),
167-
blinding_point,
168-
blinded_hops,
169-
})
206+
Self {
207+
inner_path: BlindedPath {
208+
introduction_node: IntroductionNode::NodeId(introduction_node_id),
209+
blinding_point,
210+
blinded_hops,
211+
},
212+
payinfo
213+
}
170214
}
171215

172216
#[cfg(test)]
173217
pub fn clear_blinded_hops(&mut self) {
174-
self.0.blinded_hops.clear()
218+
self.inner_path.blinded_hops.clear()
175219
}
176220
}
177221

lightning/src/ln/blinded_payment_tests.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::ln::onion_payment;
2525
use crate::ln::onion_utils;
2626
use crate::ln::onion_utils::INVALID_ONION_BLINDING;
2727
use crate::ln::outbound_payment::{Retry, IDEMPOTENCY_TIMEOUT_TICKS};
28-
use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
28+
use crate::offers::invoice::UnsignedBolt12Invoice;
2929
use crate::offers::invoice_request::UnsignedInvoiceRequest;
3030
use crate::prelude::*;
3131
use crate::routing::router::{BlindedTail, Path, Payee, PaymentParameters, RouteHop, RouteParameters};
@@ -39,7 +39,7 @@ fn blinded_payment_path(
3939
payment_secret: PaymentSecret, intro_node_min_htlc: u64, intro_node_max_htlc: u64,
4040
node_ids: Vec<PublicKey>, channel_upds: &[&msgs::UnsignedChannelUpdate],
4141
keys_manager: &test_utils::TestKeysInterface
42-
) -> (BlindedPayInfo, BlindedPaymentPath) {
42+
) -> BlindedPaymentPath {
4343
let mut intermediate_nodes = Vec::new();
4444
let mut intro_node_min_htlc_opt = Some(intro_node_min_htlc);
4545
let mut intro_node_max_htlc_opt = Some(intro_node_max_htlc);
@@ -839,11 +839,12 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
839839
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&high_htlc_minimum_upd],
840840
&chanmon_cfgs[2].keys_manager);
841841
if let Payee::Blinded { route_hints, .. } = high_htlc_min_params.payment_params.payee {
842-
route_hints[0].1.clone()
842+
route_hints[0].clone()
843843
} else { panic!() }
844844
};
845845
if let Payee::Blinded { ref mut route_hints, .. } = route_params.payment_params.payee {
846-
route_hints[0].1 = high_htlc_min_bp;
846+
route_hints[0] = high_htlc_min_bp;
847+
route_hints[0].payinfo.htlc_minimum_msat = amt_msat;
847848
} else { panic!() }
848849
find_route(&nodes[0], &route_params).unwrap()
849850
} else {
@@ -1121,7 +1122,7 @@ fn min_htlc() {
11211122
nodes[2].node.get_our_node_id(), nodes[3].node.get_our_node_id()],
11221123
&[&chan_1_2.0.contents, &chan_2_3.0.contents], &chanmon_cfgs[3].keys_manager);
11231124
assert_eq!(min_htlc_msat,
1124-
route_params.payment_params.payee.blinded_route_hints()[0].0.htlc_minimum_msat);
1125+
route_params.payment_params.payee.blinded_route_hints()[0].payinfo.htlc_minimum_msat);
11251126

11261127
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap();
11271128
check_added_monitors(&nodes[0], 1);
@@ -1133,7 +1134,7 @@ fn min_htlc() {
11331134
nodes[0].node.timer_tick_occurred();
11341135
}
11351136
if let Payee::Blinded { ref mut route_hints, .. } = route_params.payment_params.payee {
1136-
route_hints[0].0.htlc_minimum_msat -= 1;
1137+
route_hints[0].payinfo.htlc_minimum_msat -= 1;
11371138
} else { panic!() }
11381139
route_params.final_value_msat -= 1;
11391140
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();

lightning/src/ln/channelmanager.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
6262
use crate::ln::outbound_payment;
6363
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
6464
use crate::ln::wire::Encode;
65-
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
65+
use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
6666
use crate::offers::invoice_error::InvoiceError;
6767
use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
6868
use crate::offers::nonce::Nonce;
@@ -9373,7 +9373,7 @@ where
93739373
/// [`Router::create_blinded_payment_paths`].
93749374
fn create_blinded_payment_paths(
93759375
&self, amount_msats: u64, payment_secret: PaymentSecret, payment_context: PaymentContext
9376-
) -> Result<Vec<(BlindedPayInfo, BlindedPaymentPath)>, ()> {
9376+
) -> Result<Vec<BlindedPaymentPath>, ()> {
93779377
let secp_ctx = &self.secp_ctx;
93789378

93799379
let first_hops = self.list_usable_channels();

0 commit comments

Comments
 (0)