Skip to content

Commit 070f7e0

Browse files
Support receiving to 1-hop blinded payment paths.
1 parent 154841b commit 070f7e0

File tree

4 files changed

+92
-19
lines changed

4 files changed

+92
-19
lines changed

lightning/src/blinded_path/payment.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,21 @@ impl Writeable for ReceiveTlvs {
119119
}
120120
}
121121

122+
// This will be removed once we support forwarding blinded HTLCs, because we'll always read a
123+
// `BlindedPaymentTlvs` instead.
124+
impl Readable for ReceiveTlvs {
125+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
126+
_init_and_read_tlv_stream!(r, {
127+
(12, payment_constraints, required),
128+
(65536, payment_secret, required),
129+
});
130+
Ok(Self {
131+
payment_secret: payment_secret.0.unwrap(),
132+
payment_constraints: payment_constraints.0.unwrap()
133+
})
134+
}
135+
}
136+
122137
impl<'a> Writeable for BlindedPaymentTlvsRef<'a> {
123138
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
124139
// TODO: write padding

lightning/src/ln/channelmanager.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2738,7 +2738,7 @@ where
27382738
let (short_channel_id, amt_to_forward, outgoing_cltv_value) = match hop_data {
27392739
msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } =>
27402740
(short_channel_id, amt_to_forward, outgoing_cltv_value),
2741-
msgs::InboundOnionPayload::Receive { .. } =>
2741+
msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } =>
27422742
return Err(InboundOnionErr {
27432743
msg: "Final Node OnionHopData provided for us as an intermediary node",
27442744
err_code: 0x4000 | 22,
@@ -2770,12 +2770,19 @@ where
27702770
payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata, ..
27712771
} =>
27722772
(payment_data, keysend_preimage, custom_tlvs, amt_msat, outgoing_cltv_value, payment_metadata),
2773-
_ =>
2773+
msgs::InboundOnionPayload::BlindedReceive {
2774+
amt_msat, total_msat, outgoing_cltv_value, payment_secret, ..
2775+
} => {
2776+
let payment_data = msgs::FinalOnionHopData { payment_secret, total_msat };
2777+
(Some(payment_data), None, Vec::new(), amt_msat, outgoing_cltv_value, None)
2778+
}
2779+
msgs::InboundOnionPayload::Forward { .. } => {
27742780
return Err(InboundOnionErr {
27752781
err_code: 0x4000|22,
27762782
err_data: Vec::new(),
27772783
msg: "Got non final data with an HMAC of 0",
2778-
}),
2784+
})
2785+
},
27792786
};
27802787
// final_incorrect_cltv_expiry
27812788
if outgoing_cltv_value > cltv_expiry {
@@ -2940,7 +2947,9 @@ where
29402947
// We'll do receive checks in [`Self::construct_pending_htlc_info`] so we have access to the
29412948
// inbound channel's state.
29422949
onion_utils::Hop::Receive { .. } => return Ok((next_hop, shared_secret, None)),
2943-
onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionPayload::Receive { .. }, .. } => {
2950+
onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionPayload::Receive { .. }, .. } |
2951+
onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionPayload::BlindedReceive { .. }, .. } =>
2952+
{
29442953
return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0; 0]);
29452954
}
29462955
};

lightning/src/ln/msgs.rs

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -31,24 +31,26 @@ use bitcoin::{secp256k1, Witness};
3131
use bitcoin::blockdata::script::Script;
3232
use bitcoin::hash_types::{Txid, BlockHash};
3333

34+
use crate::blinded_path::payment::ReceiveTlvs;
3435
use crate::ln::{ChannelId, PaymentPreimage, PaymentHash, PaymentSecret};
3536
use crate::ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
3637
use crate::ln::onion_utils;
3738
use crate::onion_message;
38-
use crate::sign::NodeSigner;
39+
use crate::sign::{NodeSigner, Recipient};
3940

4041
use crate::prelude::*;
4142
use core::convert::TryFrom;
4243
use core::fmt;
4344
use core::fmt::Debug;
4445
use core::ops::Deref;
4546
use core::str::FromStr;
46-
use crate::io::{self, Read};
47+
use crate::io::{self, Cursor, Read};
4748
use crate::io_extras::read_to_end;
4849

4950
use crate::events::{MessageSendEventsProvider, OnionMessageProvider};
51+
use crate::util::chacha20poly1305rfc::ChaChaPolyReadAdapter;
5052
use crate::util::logger;
51-
use crate::util::ser::{LengthReadable, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
53+
use crate::util::ser::{LengthReadable, LengthReadableArgs, Readable, ReadableArgs, Writeable, Writer, WithoutLength, FixedLengthReader, HighZeroBytesDroppedBigSize, Hostname, TransactionU16LenLimited, BigSize};
5254
use crate::util::base32;
5355

5456
use crate::routing::gossip::{NodeAlias, NodeId};
@@ -1520,6 +1522,7 @@ pub trait OnionMessageHandler : OnionMessageProvider {
15201522

15211523
mod fuzzy_internal_msgs {
15221524
use bitcoin::secp256k1::PublicKey;
1525+
use crate::blinded_path::payment::PaymentConstraints;
15231526
use crate::prelude::*;
15241527
use crate::ln::{PaymentPreimage, PaymentSecret};
15251528

@@ -1548,6 +1551,14 @@ mod fuzzy_internal_msgs {
15481551
amt_msat: u64,
15491552
outgoing_cltv_value: u32,
15501553
},
1554+
BlindedReceive {
1555+
amt_msat: u64,
1556+
total_msat: u64,
1557+
outgoing_cltv_value: u32,
1558+
payment_secret: PaymentSecret,
1559+
payment_constraints: PaymentConstraints,
1560+
intro_node_blinding_point: PublicKey,
1561+
}
15511562
}
15521563

15531564
pub(crate) enum OutboundOnionPayload {
@@ -2136,22 +2147,28 @@ impl Writeable for OutboundOnionPayload {
21362147

21372148
impl<NS: Deref> ReadableArgs<&NS> for InboundOnionPayload where NS::Target: NodeSigner {
21382149
fn read<R: Read>(r: &mut R, node_signer: &NS) -> Result<Self, DecodeError> {
2139-
let mut amt = HighZeroBytesDroppedBigSize(0u64);
2140-
let mut cltv_value = HighZeroBytesDroppedBigSize(0u32);
2150+
let mut amt = None;
2151+
let mut cltv_value = None;
21412152
let mut short_id: Option<u64> = None;
21422153
let mut payment_data: Option<FinalOnionHopData> = None;
2154+
let mut encrypted_tlvs_opt: Option<WithoutLength<Vec<u8>>> = None;
2155+
let mut intro_node_blinding_point = None;
21432156
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
2157+
let mut total_msat = None;
21442158
let mut keysend_preimage: Option<PaymentPreimage> = None;
21452159
let mut custom_tlvs = Vec::new();
21462160

21472161
let tlv_len = BigSize::read(r)?;
21482162
let rd = FixedLengthReader::new(r, tlv_len.0);
21492163
decode_tlv_stream_with_custom_tlv_decode!(rd, {
2150-
(2, amt, required),
2151-
(4, cltv_value, required),
2164+
(2, amt, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
2165+
(4, cltv_value, (option, encoding: (u32, HighZeroBytesDroppedBigSize))),
21522166
(6, short_id, option),
21532167
(8, payment_data, option),
2168+
(10, encrypted_tlvs_opt, option),
2169+
(12, intro_node_blinding_point, option),
21542170
(16, payment_metadata, option),
2171+
(18, total_msat, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
21552172
// See https://github.com/lightning/blips/blob/master/blip-0003.md
21562173
(5482373484, keysend_preimage, option)
21572174
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
@@ -2162,16 +2179,44 @@ impl<NS: Deref> ReadableArgs<&NS> for InboundOnionPayload where NS::Target: Node
21622179
Ok(true)
21632180
});
21642181

2165-
if amt.0 > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
2166-
if let Some(short_channel_id) = short_id {
2167-
if payment_data.is_some() { return Err(DecodeError::InvalidValue) }
2168-
if payment_metadata.is_some() { return Err(DecodeError::InvalidValue); }
2182+
if amt.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
2183+
2184+
if let Some(blinding_point) = intro_node_blinding_point {
2185+
if short_id.is_some() || payment_data.is_some() || payment_metadata.is_some() {
2186+
return Err(DecodeError::InvalidValue)
2187+
}
2188+
let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0;
2189+
let enc_tlvs_ss = node_signer.ecdh(Recipient::Node, &blinding_point, None)
2190+
.map_err(|_| DecodeError::InvalidValue)?;
2191+
let rho = onion_utils::gen_rho_from_shared_secret(&enc_tlvs_ss.secret_bytes());
2192+
let mut s = Cursor::new(&enc_tlvs);
2193+
let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64);
2194+
match ChaChaPolyReadAdapter::read(&mut reader, rho)? {
2195+
ChaChaPolyReadAdapter { readable: ReceiveTlvs { payment_secret, payment_constraints }} => {
2196+
if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
2197+
Ok(Self::BlindedReceive {
2198+
amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
2199+
total_msat: total_msat.ok_or(DecodeError::InvalidValue)?,
2200+
outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
2201+
payment_secret,
2202+
payment_constraints,
2203+
intro_node_blinding_point: blinding_point,
2204+
})
2205+
},
2206+
}
2207+
} else if let Some(short_channel_id) = short_id {
2208+
if payment_data.is_some() || payment_metadata.is_some() || encrypted_tlvs_opt.is_some() ||
2209+
total_msat.is_some()
2210+
{ return Err(DecodeError::InvalidValue) }
21692211
Ok(Self::Forward {
21702212
short_channel_id,
2171-
amt_to_forward: amt.0,
2172-
outgoing_cltv_value: cltv_value.0,
2213+
amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?,
2214+
outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
21732215
})
21742216
} else {
2217+
if encrypted_tlvs_opt.is_some() || total_msat.is_some() {
2218+
return Err(DecodeError::InvalidValue)
2219+
}
21752220
if let Some(data) = &payment_data {
21762221
if data.total_msat > MAX_VALUE_MSAT {
21772222
return Err(DecodeError::InvalidValue);
@@ -2181,8 +2226,8 @@ impl<NS: Deref> ReadableArgs<&NS> for InboundOnionPayload where NS::Target: Node
21812226
payment_data,
21822227
payment_metadata: payment_metadata.map(|w| w.0),
21832228
keysend_preimage,
2184-
amt_msat: amt.0,
2185-
outgoing_cltv_value: cltv_value.0,
2229+
amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
2230+
outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
21862231
custom_tlvs,
21872232
})
21882233
}

pending_changelog/1-hop-bps.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## Backwards Compatibility
2+
3+
* Creating a blinded path to receive a payment over and then downgrading to a version of LDK prior
4+
to 0.0.117 may result in failure to receive the payment (#2413).

0 commit comments

Comments
 (0)