Skip to content

Commit 7b1e091

Browse files
Support paying blinded paths.
1 parent 448b191 commit 7b1e091

File tree

3 files changed

+77
-23
lines changed

3 files changed

+77
-23
lines changed

lightning/src/ln/msgs.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1517,6 +1517,7 @@ pub trait OnionMessageHandler : OnionMessageProvider {
15171517
}
15181518

15191519
mod fuzzy_internal_msgs {
1520+
use bitcoin::secp256k1::PublicKey;
15201521
use crate::prelude::*;
15211522
use crate::ln::{PaymentPreimage, PaymentSecret};
15221523

@@ -1562,6 +1563,17 @@ mod fuzzy_internal_msgs {
15621563
amt_msat: u64,
15631564
outgoing_cltv_value: u32,
15641565
},
1566+
BlindedForward {
1567+
encrypted_tlvs: Vec<u8>,
1568+
intro_node_blinding_point: Option<PublicKey>,
1569+
},
1570+
BlindedReceive {
1571+
amt_msat: u64,
1572+
total_msat: u64,
1573+
outgoing_cltv_value: u32,
1574+
encrypted_tlvs: Vec<u8>,
1575+
intro_node_blinding_point: Option<PublicKey>, // Set if the introduction node of the blinded path is the final node
1576+
}
15651577
}
15661578

15671579
pub struct DecodedOnionErrorPacket {
@@ -2097,6 +2109,24 @@ impl Writeable for OutboundOnionPayload {
20972109
(16, payment_metadata.as_ref().map(|m| WithoutLength(m)), option)
20982110
}, custom_tlvs.iter());
20992111
},
2112+
Self::BlindedForward { encrypted_tlvs, intro_node_blinding_point } => {
2113+
_encode_varint_length_prefixed_tlv!(w, {
2114+
(10, *encrypted_tlvs, required_vec),
2115+
(12, intro_node_blinding_point, option)
2116+
});
2117+
},
2118+
Self::BlindedReceive {
2119+
amt_msat, total_msat, outgoing_cltv_value, encrypted_tlvs,
2120+
intro_node_blinding_point,
2121+
} => {
2122+
_encode_varint_length_prefixed_tlv!(w, {
2123+
(2, HighZeroBytesDroppedBigSize(*amt_msat), required),
2124+
(4, HighZeroBytesDroppedBigSize(*outgoing_cltv_value), required),
2125+
(10, *encrypted_tlvs, required_vec),
2126+
(12, intro_node_blinding_point, option),
2127+
(18, HighZeroBytesDroppedBigSize(*total_msat), required)
2128+
});
2129+
},
21002130
}
21012131
Ok(())
21022132
}

lightning/src/ln/onion_utils.rs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::ln::channelmanager::{HTLCSource, RecipientOnionFields};
1212
use crate::ln::msgs;
1313
use crate::ln::wire::Encode;
1414
use crate::routing::gossip::NetworkUpdate;
15-
use crate::routing::router::{Path, RouteHop};
15+
use crate::routing::router::{BlindedTail, Path, RouteHop};
1616
use crate::util::chacha20::{ChaCha20, ChaChaReader};
1717
use crate::util::errors::{self, APIError};
1818
use crate::util::ser::{Readable, ReadableArgs, Writeable, Writer, LengthCalculatingWriter};
@@ -169,35 +169,61 @@ pub(super) fn build_onion_payloads(path: &Path, total_msat: u64, mut recipient_o
169169
let mut cur_value_msat = 0u64;
170170
let mut cur_cltv = starting_htlc_offset;
171171
let mut last_short_channel_id = 0;
172-
let mut res: Vec<msgs::OutboundOnionPayload> = Vec::with_capacity(path.hops.len());
172+
let mut res: Vec<msgs::OutboundOnionPayload> = Vec::with_capacity(
173+
path.hops.len() + path.blinded_tail.as_ref().map_or(0, |t| t.hops.len())
174+
);
173175

174176
for (idx, hop) in path.hops.iter().rev().enumerate() {
175177
// First hop gets special values so that it can check, on receipt, that everything is
176178
// exactly as it should be (and the next hop isn't trying to probe to find out if we're
177179
// the intended recipient).
178180
let value_msat = if cur_value_msat == 0 { hop.fee_msat } else { cur_value_msat };
179181
let cltv = if cur_cltv == starting_htlc_offset { hop.cltv_expiry_delta + starting_htlc_offset } else { cur_cltv };
180-
res.insert(0, if idx == 0 {
181-
msgs::OutboundOnionPayload::Receive {
182-
payment_data: if let Some(secret) = recipient_onion.payment_secret.take() {
183-
Some(msgs::FinalOnionHopData {
184-
payment_secret: secret,
185-
total_msat,
186-
})
187-
} else { None },
188-
payment_metadata: recipient_onion.payment_metadata.take(),
189-
keysend_preimage: *keysend_preimage,
190-
custom_tlvs: recipient_onion.custom_tlvs.clone(),
191-
amt_msat: value_msat,
192-
outgoing_cltv_value: cltv,
182+
if idx == 0 {
183+
if let Some(BlindedTail {
184+
blinding_point, hops, final_value_msat, excess_final_cltv_expiry_delta, ..
185+
}) = &path.blinded_tail {
186+
let mut blinding_point = Some(*blinding_point);
187+
for (i, blinded_hop) in hops.iter().enumerate() {
188+
if i == hops.len() - 1 {
189+
cur_value_msat += final_value_msat;
190+
cur_cltv += excess_final_cltv_expiry_delta;
191+
res.push(msgs::OutboundOnionPayload::BlindedReceive {
192+
amt_msat: *final_value_msat,
193+
total_msat,
194+
outgoing_cltv_value: cltv,
195+
encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
196+
intro_node_blinding_point: blinding_point.take(),
197+
});
198+
} else {
199+
res.push(msgs::OutboundOnionPayload::BlindedForward {
200+
encrypted_tlvs: blinded_hop.encrypted_payload.clone(),
201+
intro_node_blinding_point: blinding_point.take(),
202+
});
203+
}
204+
}
205+
} else {
206+
res.push(msgs::OutboundOnionPayload::Receive {
207+
payment_data: if let Some(secret) = recipient_onion.payment_secret.take() {
208+
Some(msgs::FinalOnionHopData {
209+
payment_secret: secret,
210+
total_msat,
211+
})
212+
} else { None },
213+
payment_metadata: recipient_onion.payment_metadata.take(),
214+
keysend_preimage: *keysend_preimage,
215+
custom_tlvs: recipient_onion.custom_tlvs.clone(),
216+
amt_msat: value_msat,
217+
outgoing_cltv_value: cltv,
218+
});
193219
}
194220
} else {
195-
msgs::OutboundOnionPayload::Forward {
221+
res.insert(0, msgs::OutboundOnionPayload::Forward {
196222
short_channel_id: last_short_channel_id,
197223
amt_to_forward: value_msat,
198224
outgoing_cltv_value: cltv,
199-
}
200-
});
225+
});
226+
}
201227
cur_value_msat += hop.fee_msat;
202228
if cur_value_msat >= 21000000 * 100000000 * 1000 {
203229
return Err(APIError::InvalidRoute{err: "Channel fees overflowed?".to_owned()});

lightning/src/ln/outbound_payment.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,9 @@ impl OutboundPayments {
12341234
if route.paths.len() < 1 {
12351235
return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over".to_owned()}));
12361236
}
1237-
if recipient_onion.payment_secret.is_none() && route.paths.len() > 1 {
1237+
if recipient_onion.payment_secret.is_none() && route.paths.len() > 1
1238+
&& !route.paths.iter().any(|p| p.blinded_tail.is_some())
1239+
{
12381240
return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError{err: "Payment secret is required for multi-path payments".to_owned()}));
12391241
}
12401242
let mut total_value = 0;
@@ -1245,10 +1247,6 @@ impl OutboundPayments {
12451247
path_errs.push(Err(APIError::InvalidRoute{err: "Path didn't go anywhere/had bogus size".to_owned()}));
12461248
continue 'path_check;
12471249
}
1248-
if path.blinded_tail.is_some() {
1249-
path_errs.push(Err(APIError::InvalidRoute{err: "Sending to blinded paths isn't supported yet".to_owned()}));
1250-
continue 'path_check;
1251-
}
12521250
let dest_hop_idx = if path.blinded_tail.is_some() && path.blinded_tail.as_ref().unwrap().hops.len() > 1 {
12531251
usize::max_value() } else { path.hops.len() - 1 };
12541252
for (idx, hop) in path.hops.iter().enumerate() {

0 commit comments

Comments
 (0)