Skip to content

Commit 164c166

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 a11e66d commit 164c166

File tree

2 files changed

+233
-0
lines changed

2 files changed

+233
-0
lines changed

lightning/src/blinded_path/payment.rs

+72
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),
@@ -560,6 +591,47 @@ impl Readable for BlindedPaymentTlvs {
560591
}
561592
}
562593

594+
#[cfg(trampoline)]
595+
impl Readable for BlindedTrampolineTlvs {
596+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
597+
_init_and_read_tlv_stream!(r, {
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+
608+
if let Some(next_trampoline) = next_trampoline {
609+
if payment_secret.is_some() {
610+
return Err(DecodeError::InvalidValue);
611+
}
612+
Ok(BlindedTrampolineTlvs::Forward(TrampolineForwardTlvs {
613+
next_trampoline,
614+
payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?,
615+
payment_constraints: payment_constraints.0.unwrap(),
616+
next_blinding_override,
617+
features: features.unwrap_or_else(BlindedHopFeatures::empty),
618+
}))
619+
} else {
620+
if payment_relay.is_some() || features.is_some() {
621+
return Err(DecodeError::InvalidValue);
622+
}
623+
Ok(BlindedTrampolineTlvs::Receive(ReceiveTlvs {
624+
tlvs: UnauthenticatedReceiveTlvs {
625+
payment_secret: payment_secret.ok_or(DecodeError::InvalidValue)?,
626+
payment_constraints: payment_constraints.0.unwrap(),
627+
payment_context: payment_context.ok_or(DecodeError::InvalidValue)?,
628+
},
629+
authentication: authentication.ok_or(DecodeError::InvalidValue)?,
630+
}))
631+
}
632+
}
633+
}
634+
563635
/// Represents the padding round off size (in bytes) that
564636
/// is used to pad payment bilnded path's [`BlindedHop`]
565637
pub(crate) const PAYMENT_PADDING_ROUND_OFF: usize = 30;

lightning/src/ln/msgs.rs

+161
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};
@@ -2074,6 +2076,7 @@ mod fuzzy_internal_msgs {
20742076
}
20752077

20762078
#[cfg(trampoline)]
2079+
#[cfg_attr(trampoline, allow(unused))]
20772080
pub struct InboundTrampolineEntrypointPayload {
20782081
pub amt_to_forward: u64,
20792082
pub outgoing_cltv_value: u32,
@@ -2116,12 +2119,42 @@ mod fuzzy_internal_msgs {
21162119
pub enum InboundOnionPayload {
21172120
Forward(InboundOnionForwardPayload),
21182121
#[cfg(trampoline)]
2122+
#[cfg_attr(trampoline, allow(unused))]
21192123
TrampolineEntrypoint(InboundTrampolineEntrypointPayload),
21202124
Receive(InboundOnionReceivePayload),
21212125
BlindedForward(InboundOnionBlindedForwardPayload),
21222126
BlindedReceive(InboundOnionBlindedReceivePayload),
21232127
}
21242128

2129+
#[cfg(trampoline)]
2130+
#[cfg_attr(trampoline, allow(unused))]
2131+
pub struct InboundTrampolineForwardPayload {
2132+
pub next_trampoline: PublicKey,
2133+
/// The value, in msat, of the payment after this hop's fee is deducted.
2134+
pub amt_to_forward: u64,
2135+
pub outgoing_cltv_value: u32,
2136+
}
2137+
2138+
#[cfg(trampoline)]
2139+
#[cfg_attr(trampoline, allow(unused))]
2140+
pub struct InboundTrampolineBlindedForwardPayload {
2141+
pub next_trampoline: PublicKey,
2142+
pub payment_relay: PaymentRelay,
2143+
pub payment_constraints: PaymentConstraints,
2144+
pub features: BlindedHopFeatures,
2145+
pub intro_node_blinding_point: Option<PublicKey>,
2146+
pub next_blinding_override: Option<PublicKey>,
2147+
}
2148+
2149+
#[cfg(trampoline)]
2150+
#[cfg_attr(trampoline, allow(unused))]
2151+
pub enum InboundTrampolinePayload {
2152+
Forward(InboundTrampolineForwardPayload),
2153+
BlindedForward(InboundTrampolineBlindedForwardPayload),
2154+
Receive(InboundOnionReceivePayload),
2155+
BlindedReceive(InboundOnionBlindedReceivePayload),
2156+
}
2157+
21252158
pub(crate) enum OutboundOnionPayload<'a> {
21262159
Forward {
21272160
short_channel_id: u64,
@@ -3359,6 +3392,134 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundOnionPayload wh
33593392
}
33603393
}
33613394

3395+
#[cfg(trampoline)]
3396+
impl<NS: Deref> ReadableArgs<(Option<PublicKey>, NS)> for InboundTrampolinePayload where NS::Target: NodeSigner {
3397+
fn read<R: Read>(r: &mut R, args: (Option<PublicKey>, NS)) -> Result<Self, DecodeError> {
3398+
let (update_add_blinding_point, node_signer) = args;
3399+
3400+
let mut amt = None;
3401+
let mut cltv_value = None;
3402+
let mut payment_data: Option<FinalOnionHopData> = None;
3403+
let mut encrypted_tlvs_opt: Option<WithoutLength<Vec<u8>>> = None;
3404+
let mut intro_node_blinding_point = None;
3405+
let mut next_trampoline: Option<PublicKey> = None;
3406+
let mut payment_metadata: Option<WithoutLength<Vec<u8>>> = None;
3407+
let mut total_msat = None;
3408+
let mut keysend_preimage: Option<PaymentPreimage> = None;
3409+
let mut invoice_request: Option<InvoiceRequest> = None;
3410+
let mut custom_tlvs = Vec::new();
3411+
3412+
let tlv_len = BigSize::read(r)?;
3413+
let mut rd = FixedLengthReader::new(r, tlv_len.0);
3414+
decode_tlv_stream_with_custom_tlv_decode!(&mut rd, {
3415+
(2, amt, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
3416+
(4, cltv_value, (option, encoding: (u32, HighZeroBytesDroppedBigSize))),
3417+
(8, payment_data, option),
3418+
(10, encrypted_tlvs_opt, option),
3419+
(12, intro_node_blinding_point, option),
3420+
(14, next_trampoline, option),
3421+
(16, payment_metadata, option),
3422+
(18, total_msat, (option, encoding: (u64, HighZeroBytesDroppedBigSize))),
3423+
(77_777, invoice_request, option),
3424+
// See https://github.com/lightning/blips/blob/master/blip-0003.md
3425+
(5482373484, keysend_preimage, option)
3426+
}, |msg_type: u64, msg_reader: &mut FixedLengthReader<_>| -> Result<bool, DecodeError> {
3427+
if msg_type < 1 << 16 { return Ok(false) }
3428+
let mut value = Vec::new();
3429+
msg_reader.read_to_limit(&mut value, u64::MAX)?;
3430+
custom_tlvs.push((msg_type, value));
3431+
Ok(true)
3432+
});
3433+
3434+
if amt.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
3435+
if intro_node_blinding_point.is_some() && update_add_blinding_point.is_some() {
3436+
return Err(DecodeError::InvalidValue)
3437+
}
3438+
3439+
if let Some(blinding_point) = intro_node_blinding_point.or(update_add_blinding_point) {
3440+
if next_trampoline.is_some() || payment_data.is_some() || payment_metadata.is_some() {
3441+
return Err(DecodeError::InvalidValue)
3442+
}
3443+
let enc_tlvs = encrypted_tlvs_opt.ok_or(DecodeError::InvalidValue)?.0;
3444+
let enc_tlvs_ss = node_signer.ecdh(Recipient::Node, &blinding_point, None)
3445+
.map_err(|_| DecodeError::InvalidValue)?;
3446+
let rho = onion_utils::gen_rho_from_shared_secret(&enc_tlvs_ss.secret_bytes());
3447+
let mut s = Cursor::new(&enc_tlvs);
3448+
let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64);
3449+
match ChaChaPolyReadAdapter::read(&mut reader, rho)? {
3450+
ChaChaPolyReadAdapter { readable: BlindedTrampolineTlvs::Forward(TrampolineForwardTlvs {
3451+
next_trampoline, payment_relay, payment_constraints, features, next_blinding_override
3452+
})} => {
3453+
if amt.is_some() || cltv_value.is_some() || total_msat.is_some() ||
3454+
keysend_preimage.is_some() || invoice_request.is_some()
3455+
{
3456+
return Err(DecodeError::InvalidValue)
3457+
}
3458+
Ok(Self::BlindedForward(InboundTrampolineBlindedForwardPayload {
3459+
next_trampoline,
3460+
payment_relay,
3461+
payment_constraints,
3462+
features,
3463+
intro_node_blinding_point,
3464+
next_blinding_override,
3465+
}))
3466+
},
3467+
ChaChaPolyReadAdapter { readable: BlindedTrampolineTlvs::Receive(receive_tlvs) } => {
3468+
let ReceiveTlvs { tlvs, authentication: (hmac, nonce) } = receive_tlvs;
3469+
let expanded_key = node_signer.get_inbound_payment_key();
3470+
if tlvs.verify_for_offer_payment(hmac, nonce, &expanded_key).is_err() {
3471+
return Err(DecodeError::InvalidValue);
3472+
}
3473+
3474+
let UnauthenticatedReceiveTlvs {
3475+
payment_secret, payment_constraints, payment_context,
3476+
} = tlvs;
3477+
if total_msat.unwrap_or(0) > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
3478+
Ok(Self::BlindedReceive(InboundOnionBlindedReceivePayload {
3479+
sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
3480+
total_msat: total_msat.ok_or(DecodeError::InvalidValue)?,
3481+
cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?,
3482+
payment_secret,
3483+
payment_constraints,
3484+
payment_context,
3485+
intro_node_blinding_point,
3486+
keysend_preimage,
3487+
invoice_request,
3488+
custom_tlvs,
3489+
}))
3490+
},
3491+
}
3492+
} else if let Some(next_trampoline) = next_trampoline {
3493+
if payment_data.is_some() || payment_metadata.is_some() || encrypted_tlvs_opt.is_some() ||
3494+
total_msat.is_some() || invoice_request.is_some()
3495+
{ return Err(DecodeError::InvalidValue) }
3496+
Ok(Self::Forward(InboundTrampolineForwardPayload {
3497+
next_trampoline,
3498+
amt_to_forward: amt.ok_or(DecodeError::InvalidValue)?,
3499+
outgoing_cltv_value: cltv_value.ok_or(DecodeError::InvalidValue)?,
3500+
}))
3501+
} else {
3502+
if encrypted_tlvs_opt.is_some() || total_msat.is_some() || invoice_request.is_some() {
3503+
return Err(DecodeError::InvalidValue)
3504+
}
3505+
if let Some(data) = &payment_data {
3506+
if data.total_msat > MAX_VALUE_MSAT {
3507+
return Err(DecodeError::InvalidValue);
3508+
}
3509+
}
3510+
Ok(Self::Receive(InboundOnionReceivePayload {
3511+
payment_data,
3512+
payment_metadata: payment_metadata.map(|w| w.0),
3513+
keysend_preimage,
3514+
sender_intended_htlc_amt_msat: amt.ok_or(DecodeError::InvalidValue)?,
3515+
cltv_expiry_height: cltv_value.ok_or(DecodeError::InvalidValue)?,
3516+
custom_tlvs,
3517+
}))
3518+
}
3519+
}
3520+
}
3521+
3522+
33623523
impl Writeable for Ping {
33633524
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
33643525
self.ponglen.write(w)?;

0 commit comments

Comments
 (0)