Skip to content

Commit 607cb7d

Browse files
committed
Parse Trampoline onion payloads
We will be using the same logic for decoding onion payloads for outer and for Trampoline onions. To accommodate the Trampoline payloads, we parse a next node ID rather than an SCID.
1 parent 7fe1d9a commit 607cb7d

File tree

2 files changed

+237
-2
lines changed

2 files changed

+237
-2
lines changed

lightning/src/blinded_path/payment.rs

+74
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,26 @@ pub struct ForwardTlvs {
297297
pub next_blinding_override: Option<PublicKey>,
298298
}
299299

300+
/// Data to construct a [`BlindedHop`] for forwarding a Trampoline payment.
301+
#[cfg(trampoline)]
302+
#[derive(Clone, Debug)]
303+
pub struct TrampolineForwardTlvs {
304+
/// The node id to which the trampoline node must find a route.
305+
pub next_trampoline: PublicKey,
306+
/// Payment parameters for relaying over [`Self::next_trampoline`].
307+
pub payment_relay: PaymentRelay,
308+
/// Payment constraints for relaying over [`Self::next_trampoline`].
309+
pub payment_constraints: PaymentConstraints,
310+
/// Supported and required features when relaying a payment onion containing this object's
311+
/// corresponding [`BlindedHop::encrypted_payload`].
312+
///
313+
/// [`BlindedHop::encrypted_payload`]: crate::blinded_path::BlindedHop::encrypted_payload
314+
pub features: BlindedHopFeatures,
315+
/// Set if this [`BlindedPaymentPath`] is concatenated to another, to indicate the
316+
/// [`BlindedPaymentPath::blinding_point`] of the appended blinded path.
317+
pub next_blinding_override: Option<PublicKey>,
318+
}
319+
300320
/// Data to construct a [`BlindedHop`] for receiving a payment. This payload is custom to LDK and
301321
/// may not be valid if received by another lightning implementation.
302322
///
@@ -348,6 +368,17 @@ pub(crate) enum BlindedPaymentTlvs {
348368
Receive(ReceiveTlvs),
349369
}
350370

371+
/// Data to construct a [`BlindedHop`] for sending a Trampoline payment over.
372+
///
373+
/// [`BlindedHop`]: crate::blinded_path::BlindedHop
374+
#[cfg(trampoline)]
375+
pub(crate) enum BlindedTrampolineTlvs {
376+
/// This blinded payment data is for a forwarding node.
377+
Forward(TrampolineForwardTlvs),
378+
/// This blinded payment data is for the receiving node.
379+
Receive(ReceiveTlvs),
380+
}
381+
351382
// Used to include forward and receive TLVs in the same iterator for encoding.
352383
enum BlindedPaymentTlvsRef<'a> {
353384
Forward(&'a ForwardTlvs),
@@ -559,6 +590,49 @@ impl Readable for BlindedPaymentTlvs {
559590
}
560591
}
561592

593+
#[cfg(trampoline)]
594+
impl Readable for BlindedTrampolineTlvs {
595+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
596+
_init_and_read_tlv_stream!(r, {
597+
(1, _padding, option),
598+
(8, next_blinding_override, option),
599+
(10, payment_relay, option),
600+
(12, payment_constraints, required),
601+
(14, next_trampoline, option),
602+
(14, features, (option, encoding: (BlindedHopFeatures, WithoutLength))),
603+
(65536, payment_secret, option),
604+
(65537, payment_context, option),
605+
(65539, authentication, option),
606+
});
607+
let _padding: Option<utils::Padding> = _padding;
608+
609+
if let Some(next_trampoline) = next_trampoline {
610+
if payment_secret.is_some() {
611+
return Err(DecodeError::InvalidValue);
612+
}
613+
Ok(BlindedTrampolineTlvs::Forward(TrampolineForwardTlvs {
614+
next_trampoline,
615+
payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?,
616+
payment_constraints: payment_constraints.0.unwrap(),
617+
next_blinding_override,
618+
features: features.unwrap_or_else(BlindedHopFeatures::empty),
619+
}))
620+
} else {
621+
if payment_relay.is_some() || features.is_some() {
622+
return Err(DecodeError::InvalidValue);
623+
}
624+
Ok(BlindedTrampolineTlvs::Receive(ReceiveTlvs {
625+
tlvs: UnauthenticatedReceiveTlvs {
626+
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
627+
payment_constraints: payment_constraints.0.unwrap(),
628+
payment_context: payment_context.ok_or(DecodeError::InvalidValue)?,
629+
},
630+
authentication: authentication.ok_or(DecodeError::InvalidValue)?,
631+
}))
632+
}
633+
}
634+
}
635+
562636
/// Construct blinded payment hops for the given `intermediate_nodes` and payee info.
563637
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
564638
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[PaymentForwardNode], payee_node_id: PublicKey,

lightning/src/ln/msgs.rs

+163-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ use bitcoin::script::ScriptBuf;
3232
use bitcoin::hash_types::Txid;
3333

3434
use crate::blinded_path::payment::{BlindedPaymentTlvs, ForwardTlvs, ReceiveTlvs, UnauthenticatedReceiveTlvs};
35+
#[cfg(trampoline)]
36+
use crate::blinded_path::payment::{BlindedTrampolineTlvs, TrampolineForwardTlvs};
3537
use crate::ln::channelmanager::Verification;
3638
use crate::ln::types::ChannelId;
3739
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
@@ -1812,10 +1814,11 @@ mod fuzzy_internal_msgs {
18121814
}
18131815

18141816
#[cfg(trampoline)]
1817+
#[cfg_attr(trampoline, allow(unused))]
18151818
pub struct InboundTrampolineEntrypointPayload {
18161819
pub amt_to_forward: u64,
18171820
pub outgoing_cltv_value: u32,
1818-
pub multipath_trampoline_data: Option<FinalOnionHopData>,
1821+
pub multipath_trampoline_data: FinalOnionHopData,
18191822
pub trampoline_packet: TrampolineOnionPacket,
18201823
/// The blinding point this hop needs to decrypt its Trampoline onion.
18211824
/// This is used for Trampoline hops that are not the blinded path intro hop.
@@ -1854,12 +1857,42 @@ mod fuzzy_internal_msgs {
18541857
pub enum InboundOnionPayload {
18551858
Forward(InboundOnionForwardPayload),
18561859
#[cfg(trampoline)]
1860+
#[cfg_attr(trampoline, allow(unused))]
18571861
TrampolineEntrypoint(InboundTrampolineEntrypointPayload),
18581862
Receive(InboundOnionReceivePayload),
18591863
BlindedForward(InboundOnionBlindedForwardPayload),
18601864
BlindedReceive(InboundOnionBlindedReceivePayload),
18611865
}
18621866

1867+
#[cfg(trampoline)]
1868+
#[cfg_attr(trampoline, allow(unused))]
1869+
pub struct InboundTrampolineForwardPayload {
1870+
pub next_trampoline: PublicKey,
1871+
/// The value, in msat, of the payment after this hop's fee is deducted.
1872+
pub amt_to_forward: u64,
1873+
pub outgoing_cltv_value: u32,
1874+
}
1875+
1876+
#[cfg(trampoline)]
1877+
#[cfg_attr(trampoline, allow(unused))]
1878+
pub struct InboundTrampolineBlindedForwardPayload {
1879+
pub next_trampoline: PublicKey,
1880+
pub payment_relay: PaymentRelay,
1881+
pub payment_constraints: PaymentConstraints,
1882+
pub features: BlindedHopFeatures,
1883+
pub intro_node_blinding_point: Option<PublicKey>,
1884+
pub next_blinding_override: Option<PublicKey>,
1885+
}
1886+
1887+
#[cfg(trampoline)]
1888+
#[cfg_attr(trampoline, allow(unused))]
1889+
pub enum InboundTrampolinePayload {
1890+
Forward(InboundTrampolineForwardPayload),
1891+
BlindedForward(InboundTrampolineBlindedForwardPayload),
1892+
Receive(InboundOnionReceivePayload),
1893+
BlindedReceive(InboundOnionBlindedReceivePayload),
1894+
}
1895+
18631896
pub(crate) enum OutboundOnionPayload<'a> {
18641897
Forward {
18651898
short_channel_id: u64,
@@ -3008,7 +3041,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundOnionPayload wh
30083041
return Ok(Self::TrampolineEntrypoint(InboundTrampolineEntrypointPayload {
30093042
amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?,
30103043
outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
3011-
multipath_trampoline_data: payment_data,
3044+
multipath_trampoline_data: payment_data.ok_or(DecodeError::InvalidValue)?,
30123045
trampoline_packet: trampoline_onion_packet,
30133046
current_path_key: intro_node_blinding_point,
30143047
}))
@@ -3097,6 +3130,134 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundOnionPayload wh
30973130
}
30983131
}
30993132

3133+
#[cfg(trampoline)]
3134+
impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundTrampolinePayload where NS::Target: NodeSigner {
3135+
fn read<R: Read>(r: &mut R, args: (Option<PublicKey>, NS)) -> Result<Self, DecodeError> {
3136+
let (update_add_blinding_point, node_signer) = args;
3137+
3138+
let mut amt = None;
3139+
let mut cltv_value = None;
3140+
let mut payment_data: Option<FinalOnionHopData> = None;
3141+
let mut encrypted_tlvs_opt: Option<WithoutLength<Vec<u8>>> = None;
3142+
let mut intro_node_blinding_point = None;
3143+
let mut next_trampoline: Option<PublicKey> = None;
3144+
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
3145+
let mut total_msat = None;
3146+
let mut keysend_preimage: Option<PaymentPreimage> = None;
3147+
let mut invoice_request: Option<InvoiceRequest> = None;
3148+
let mut custom_tlvs = Vec::new();
3149+
3150+
let tlv_len = BigSize::read(r)?;
3151+
let mut rd = FixedLengthReader::new(r, tlv_len.0);
3152+
decode_tlv_stream_with_custom_tlv_decode!(&mut rd, {
3153+
(2, amt, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
3154+
(4, cltv_value, (option, encoding: (u32, HighZeroBytesDroppedBigSize))),
3155+
(8, payment_data, option),
3156+
(10, encrypted_tlvs_opt, option),
3157+
(12, intro_node_blinding_point, option),
3158+
(14, next_trampoline, option),
3159+
(16, payment_metadata, option),
3160+
(18, total_msat, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
3161+
(77_777, invoice_request, option),
3162+
// See https://github.com/lightning/blips/blob/master/blip-0003.md
3163+
(5482373484, keysend_preimage, option)
3164+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
3165+
if msg_type < 1 << 16 { return Ok(false) }
3166+
let mut value = Vec::new();
3167+
msg_reader.read_to_limit(&mut value, u64::MAX)?;
3168+
custom_tlvs.push((msg_type, value));
3169+
Ok(true)
3170+
});
3171+
3172+
if amt.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
3173+
if intro_node_blinding_point.is_some() && update_add_blinding_point.is_some() {
3174+
return Err(DecodeError::InvalidValue)
3175+
}
3176+
3177+
if let Some(blinding_point) = intro_node_blinding_point.or(update_add_blinding_point) {
3178+
if next_trampoline.is_some() || payment_data.is_some() || payment_metadata.is_some() {
3179+
return Err(DecodeError::InvalidValue)
3180+
}
3181+
let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0;
3182+
let enc_tlvs_ss = node_signer.ecdh(Recipient::Node, &blinding_point, None)
3183+
.map_err(|_| DecodeError::InvalidValue)?;
3184+
let rho = onion_utils::gen_rho_from_shared_secret(&enc_tlvs_ss.secret_bytes());
3185+
let mut s = Cursor::new(&enc_tlvs);
3186+
let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64);
3187+
match ChaChaPolyReadAdapter::read(&mut reader, rho)? {
3188+
ChaChaPolyReadAdapter { readable: BlindedTrampolineTlvs::Forward(TrampolineForwardTlvs {
3189+
next_trampoline, payment_relay, payment_constraints, features, next_blinding_override
3190+
})} => {
3191+
if amt.is_some() || cltv_value.is_some() || total_msat.is_some() ||
3192+
keysend_preimage.is_some() || invoice_request.is_some()
3193+
{
3194+
return Err(DecodeError::InvalidValue)
3195+
}
3196+
Ok(Self::BlindedForward(InboundTrampolineBlindedForwardPayload {
3197+
next_trampoline,
3198+
payment_relay,
3199+
payment_constraints,
3200+
features,
3201+
intro_node_blinding_point,
3202+
next_blinding_override,
3203+
}))
3204+
},
3205+
ChaChaPolyReadAdapter { readable: BlindedTrampolineTlvs::Receive(receive_tlvs) } => {
3206+
let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs;
3207+
let expanded_key = node_signer.get_inbound_payment_key();
3208+
if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() {
3209+
return Err(DecodeError::InvalidValue);
3210+
}
3211+
3212+
let UnauthenticatedReceiveTlvs {
3213+
payment_secret, payment_constraints, payment_context,
3214+
} = tlvs;
3215+
if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
3216+
Ok(Self::BlindedReceive(InboundOnionBlindedReceivePayload {
3217+
sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
3218+
total_msat: total_msat.ok_or(DecodeError::InvalidValue)?,
3219+
cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?,
3220+
payment_secret,
3221+
payment_constraints,
3222+
payment_context,
3223+
intro_node_blinding_point,
3224+
keysend_preimage,
3225+
invoice_request,
3226+
custom_tlvs,
3227+
}))
3228+
},
3229+
}
3230+
} else if let Some(next_trampoline) = next_trampoline {
3231+
if payment_data.is_some() || payment_metadata.is_some() || encrypted_tlvs_opt.is_some() ||
3232+
total_msat.is_some() || invoice_request.is_some()
3233+
{ return Err(DecodeError::InvalidValue) }
3234+
Ok(Self::Forward(InboundTrampolineForwardPayload {
3235+
next_trampoline,
3236+
amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?,
3237+
outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
3238+
}))
3239+
} else {
3240+
if encrypted_tlvs_opt.is_some() || total_msat.is_some() || invoice_request.is_some() {
3241+
return Err(DecodeError::InvalidValue)
3242+
}
3243+
if let Some(data) = &payment_data {
3244+
if data.total_msat > MAX_VALUE_MSAT {
3245+
return Err(DecodeError::InvalidValue);
3246+
}
3247+
}
3248+
Ok(Self::Receive(InboundOnionReceivePayload {
3249+
payment_data,
3250+
payment_metadata: payment_metadata.map(|w| w.0),
3251+
keysend_preimage,
3252+
sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
3253+
cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?,
3254+
custom_tlvs,
3255+
}))
3256+
}
3257+
}
3258+
}
3259+
3260+
31003261
impl Writeable for Ping {
31013262
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
31023263
self.ponglen.write(w)?;

0 commit comments

Comments
 (0)