Skip to content

Commit 46794b9

Browse files
Receive payment onions as new InboundPayload instead of OnionHopData
To support route blinding, we want to split OnionHopData into two separate structs, one for inbound onions and one for outbound onions. This is because blinded payloads change the fields present in the onion hop data struct based on whether we're sending vs receiving (outbound onions include encrypted blobs, inbound onions can decrypt those blobs and contain the decrypted fields themselves). In upcoming commits, we'll add variants for blinded payloads to the new InboundPayload enum.
1 parent 21890a3 commit 46794b9

File tree

4 files changed

+98
-87
lines changed

4 files changed

+98
-87
lines changed

fuzz/src/msg_targets/msg_onion_hop_data.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ use crate::utils::test_logger;
1515

1616
#[inline]
1717
pub fn msg_onion_hop_data_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
18-
test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
18+
// TODO
1919
}
2020

2121
#[no_mangle]
2222
pub extern "C" fn msg_onion_hop_data_run(data: *const u8, datalen: usize) {
2323
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
24-
test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
24+
// TODO
2525
}

lightning/src/ln/channelmanager.rs

Lines changed: 41 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -2617,7 +2617,7 @@ where
26172617
}
26182618

26192619
fn construct_fwd_pending_htlc_info(
2620-
&self, msg: &msgs::UpdateAddHTLC, hop_data: msgs::OnionHopData, hop_hmac: [u8; 32],
2620+
&self, msg: &msgs::UpdateAddHTLC, hop_data: msgs::InboundPayload, hop_hmac: [u8; 32],
26212621
new_packet_bytes: [u8; onion_utils::ONION_DATA_LEN], shared_secret: [u8; 32],
26222622
next_packet_pubkey_opt: Option<Result<PublicKey, secp256k1::Error>>
26232623
) -> Result<PendingHTLCInfo, InboundOnionErr> {
@@ -2626,42 +2626,44 @@ where
26262626
version: 0,
26272627
public_key: next_packet_pubkey_opt.unwrap_or(Err(secp256k1::Error::InvalidPublicKey)),
26282628
hop_data: new_packet_bytes,
2629-
hmac: hop_hmac.clone(),
2629+
hmac: hop_hmac,
26302630
};
26312631

2632-
let short_channel_id = match hop_data.format {
2633-
msgs::OnionHopDataFormat::NonFinalNode { short_channel_id } => short_channel_id,
2634-
msgs::OnionHopDataFormat::FinalNode { .. } => {
2632+
let (short_channel_id, amt_to_forward, outgoing_cltv_value) = match hop_data {
2633+
msgs::InboundPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } =>
2634+
(short_channel_id, amt_to_forward, outgoing_cltv_value),
2635+
msgs::InboundPayload::Receive { .. } =>
26352636
return Err(InboundOnionErr {
26362637
msg: "Final Node OnionHopData provided for us as an intermediary node",
26372638
err_code: 0x4000 | 22,
26382639
err_data: Vec::new(),
2639-
})
2640-
},
2640+
}),
26412641
};
26422642

26432643
Ok(PendingHTLCInfo {
26442644
routing: PendingHTLCRouting::Forward {
26452645
onion_packet: outgoing_packet,
26462646
short_channel_id,
26472647
},
2648-
payment_hash: msg.payment_hash.clone(),
2648+
payment_hash: msg.payment_hash,
26492649
incoming_shared_secret: shared_secret,
26502650
incoming_amt_msat: Some(msg.amount_msat),
2651-
outgoing_amt_msat: hop_data.amt_to_forward,
2652-
outgoing_cltv_value: hop_data.outgoing_cltv_value,
2651+
outgoing_amt_msat: amt_to_forward,
2652+
outgoing_cltv_value,
26532653
skimmed_fee_msat: None,
26542654
})
26552655
}
26562656

26572657
fn construct_recv_pending_htlc_info(
2658-
&self, hop_data: msgs::OnionHopData, shared_secret: [u8; 32], payment_hash: PaymentHash,
2658+
&self, hop_data: msgs::InboundPayload, shared_secret: [u8; 32], payment_hash: PaymentHash,
26592659
amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
26602660
counterparty_skimmed_fee_msat: Option<u64>,
26612661
) -> Result<PendingHTLCInfo, InboundOnionErr> {
2662-
let (payment_data, keysend_preimage, payment_metadata) = match hop_data.format {
2663-
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage, payment_metadata } =>
2664-
(payment_data, keysend_preimage, payment_metadata),
2662+
let (payment_data, keysend_preimage, onion_amt_msat, outgoing_cltv_value, payment_metadata) = match hop_data {
2663+
msgs::InboundPayload::Receive {
2664+
payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata, ..
2665+
} =>
2666+
(payment_data, keysend_preimage, amt_msat, outgoing_cltv_value, payment_metadata),
26652667
_ =>
26662668
return Err(InboundOnionErr {
26672669
err_code: 0x4000|22,
@@ -2670,7 +2672,7 @@ where
26702672
}),
26712673
};
26722674
// final_incorrect_cltv_expiry
2673-
if hop_data.outgoing_cltv_value > cltv_expiry {
2675+
if outgoing_cltv_value > cltv_expiry {
26742676
return Err(InboundOnionErr {
26752677
msg: "Upstream node set CLTV to less than the CLTV set by the sender",
26762678
err_code: 18,
@@ -2685,7 +2687,7 @@ where
26852687
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
26862688
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
26872689
let current_height: u32 = self.best_block.read().unwrap().height();
2688-
if (hop_data.outgoing_cltv_value as u64) <= current_height as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
2690+
if (outgoing_cltv_value as u64) <= current_height as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
26892691
let mut err_data = Vec::with_capacity(12);
26902692
err_data.extend_from_slice(&amt_msat.to_be_bytes());
26912693
err_data.extend_from_slice(&current_height.to_be_bytes());
@@ -2694,8 +2696,8 @@ where
26942696
msg: "The final CLTV expiry is too soon to handle",
26952697
});
26962698
}
2697-
if (!allow_underpay && hop_data.amt_to_forward > amt_msat) ||
2698-
(allow_underpay && hop_data.amt_to_forward >
2699+
if (!allow_underpay && onion_amt_msat > amt_msat) ||
2700+
(allow_underpay && onion_amt_msat >
26992701
amt_msat.saturating_add(counterparty_skimmed_fee_msat.unwrap_or(0)))
27002702
{
27012703
return Err(InboundOnionErr {
@@ -2731,13 +2733,13 @@ where
27312733
payment_data,
27322734
payment_preimage,
27332735
payment_metadata,
2734-
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
2736+
incoming_cltv_expiry: outgoing_cltv_value,
27352737
}
27362738
} else if let Some(data) = payment_data {
27372739
routing = PendingHTLCRouting::Receive {
27382740
payment_data: data,
27392741
payment_metadata,
2740-
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
2742+
incoming_cltv_expiry: outgoing_cltv_value,
27412743
phantom_shared_secret,
27422744
}
27432745
} else {
@@ -2752,8 +2754,8 @@ where
27522754
payment_hash,
27532755
incoming_shared_secret: shared_secret,
27542756
incoming_amt_msat: Some(amt_msat),
2755-
outgoing_amt_msat: hop_data.amt_to_forward,
2756-
outgoing_cltv_value: hop_data.outgoing_cltv_value,
2757+
outgoing_amt_msat: onion_amt_msat,
2758+
outgoing_cltv_value,
27572759
skimmed_fee_msat: counterparty_skimmed_fee_msat,
27582760
})
27592761
}
@@ -2817,9 +2819,8 @@ where
28172819
};
28182820
let (outgoing_scid, outgoing_amt_msat, outgoing_cltv_value, next_packet_pk_opt) = match next_hop {
28192821
onion_utils::Hop::Forward {
2820-
next_hop_data: msgs::OnionHopData {
2821-
format: msgs::OnionHopDataFormat::NonFinalNode { short_channel_id }, amt_to_forward,
2822-
outgoing_cltv_value,
2822+
next_hop_data: msgs::InboundPayload::Forward {
2823+
short_channel_id, amt_to_forward, outgoing_cltv_value
28232824
}, ..
28242825
} => {
28252826
let next_pk = onion_utils::next_hop_packet_pubkey(&self.secp_ctx,
@@ -2829,9 +2830,7 @@ where
28292830
// We'll do receive checks in [`Self::construct_pending_htlc_info`] so we have access to the
28302831
// inbound channel's state.
28312832
onion_utils::Hop::Receive { .. } => return Ok((next_hop, shared_secret, None)),
2832-
onion_utils::Hop::Forward {
2833-
next_hop_data: msgs::OnionHopData { format: msgs::OnionHopDataFormat::FinalNode { .. }, .. }, ..
2834-
} => {
2833+
onion_utils::Hop::Forward { next_hop_data: msgs::InboundPayload::Receive { .. }, .. } => {
28352834
return_err!("Final Node OnionHopData provided for us as an intermediary node", 0x4000 | 22, &[0; 0]);
28362835
}
28372836
};
@@ -10021,16 +10020,14 @@ mod tests {
1002110020
let node = create_network(1, &node_cfg, &node_chanmgr);
1002210021
let sender_intended_amt_msat = 100;
1002310022
let extra_fee_msat = 10;
10024-
let hop_data = msgs::OnionHopData {
10025-
amt_to_forward: 100,
10023+
let hop_data = msgs::InboundPayload::Receive {
10024+
amt_msat: 100,
1002610025
outgoing_cltv_value: 42,
10027-
format: msgs::OnionHopDataFormat::FinalNode {
10028-
keysend_preimage: None,
10029-
payment_metadata: None,
10030-
payment_data: Some(msgs::FinalOnionHopData {
10031-
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
10032-
}),
10033-
}
10026+
payment_metadata: None,
10027+
keysend_preimage: None,
10028+
payment_data: Some(msgs::FinalOnionHopData {
10029+
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
10030+
}),
1003410031
};
1003510032
// Check that if the amount we received + the penultimate hop extra fee is less than the sender
1003610033
// intended amount, we fail the payment.
@@ -10042,16 +10039,14 @@ mod tests {
1004210039
} else { panic!(); }
1004310040

1004410041
// If amt_received + extra_fee is equal to the sender intended amount, we're fine.
10045-
let hop_data = msgs::OnionHopData { // This is the same hop_data as above, OnionHopData doesn't implement Clone
10046-
amt_to_forward: 100,
10042+
let hop_data = msgs::InboundPayload::Receive { // This is the same payload as above, InboundPayload doesn't implement Clone
10043+
amt_msat: 100,
1004710044
outgoing_cltv_value: 42,
10048-
format: msgs::OnionHopDataFormat::FinalNode {
10049-
keysend_preimage: None,
10050-
payment_metadata: None,
10051-
payment_data: Some(msgs::FinalOnionHopData {
10052-
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
10053-
}),
10054-
}
10045+
payment_metadata: None,
10046+
keysend_preimage: None,
10047+
payment_data: Some(msgs::FinalOnionHopData {
10048+
payment_secret: PaymentSecret([0; 32]), total_msat: sender_intended_amt_msat,
10049+
}),
1005510050
};
1005610051
assert!(node[0].node.construct_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
1005710052
sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat)).is_ok());

lightning/src/ln/msgs.rs

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,11 +1419,26 @@ mod fuzzy_internal_msgs {
14191419
// These types aren't intended to be pub, but are exposed for direct fuzzing (as we deserialize
14201420
// them from untrusted input):
14211421
#[derive(Clone)]
1422-
pub(crate) struct FinalOnionHopData {
1423-
pub(crate) payment_secret: PaymentSecret,
1422+
pub struct FinalOnionHopData {
1423+
pub payment_secret: PaymentSecret,
14241424
/// The total value, in msat, of the payment as received by the ultimate recipient.
14251425
/// Message serialization may panic if this value is more than 21 million Bitcoin.
1426-
pub(crate) total_msat: u64,
1426+
pub total_msat: u64,
1427+
}
1428+
1429+
pub enum InboundPayload {
1430+
Forward {
1431+
short_channel_id: u64,
1432+
amt_to_forward: u64,
1433+
outgoing_cltv_value: u32,
1434+
},
1435+
Receive {
1436+
payment_data: Option<FinalOnionHopData>,
1437+
payment_metadata: Option<Vec<u8>>,
1438+
keysend_preimage: Option<PaymentPreimage>,
1439+
amt_msat: u64,
1440+
outgoing_cltv_value: u32,
1441+
},
14271442
}
14281443

14291444
pub(crate) enum OnionHopDataFormat {
@@ -1975,7 +1990,7 @@ impl Writeable for OnionHopData {
19751990
}
19761991
}
19771992

1978-
impl Readable for OnionHopData {
1993+
impl Readable for InboundPayload {
19791994
fn read<R: Read>(r: &mut R) -> Result<Self, DecodeError> {
19801995
let mut amt = HighZeroBytesDroppedBigSize(0u64);
19811996
let mut cltv_value = HighZeroBytesDroppedBigSize(0u32);
@@ -1993,39 +2008,35 @@ impl Readable for OnionHopData {
19932008
(5482373484, keysend_preimage, option)
19942009
});
19952010

1996-
let format = if let Some(short_channel_id) = short_id {
1997-
if payment_data.is_some() { return Err(DecodeError::InvalidValue); }
2011+
if amt.0 > MAX_VALUE_MSAT { return Err(DecodeError::InvalidValue) }
2012+
if let Some(short_channel_id) = short_id {
2013+
if payment_data.is_some() { return Err(DecodeError::InvalidValue) }
19982014
if payment_metadata.is_some() { return Err(DecodeError::InvalidValue); }
1999-
OnionHopDataFormat::NonFinalNode {
2015+
Ok(Self::Forward {
20002016
short_channel_id,
2001-
}
2017+
amt_to_forward: amt.0,
2018+
outgoing_cltv_value: cltv_value.0,
2019+
})
20022020
} else {
20032021
if let Some(data) = &payment_data {
20042022
if data.total_msat > MAX_VALUE_MSAT {
20052023
return Err(DecodeError::InvalidValue);
20062024
}
20072025
}
2008-
OnionHopDataFormat::FinalNode {
2026+
Ok(Self::Receive {
20092027
payment_data,
20102028
payment_metadata: payment_metadata.map(|w| w.0),
20112029
keysend_preimage,
2012-
}
2013-
};
2014-
2015-
if amt.0 > MAX_VALUE_MSAT {
2016-
return Err(DecodeError::InvalidValue);
2030+
amt_msat: amt.0,
2031+
outgoing_cltv_value: cltv_value.0,
2032+
})
20172033
}
2018-
Ok(OnionHopData {
2019-
format,
2020-
amt_to_forward: amt.0,
2021-
outgoing_cltv_value: cltv_value.0,
2022-
})
20232034
}
20242035
}
20252036

20262037
// ReadableArgs because we need onion_utils::decode_next_hop to accommodate payment packets and
20272038
// onion message packets.
2028-
impl ReadableArgs<()> for OnionHopData {
2039+
impl ReadableArgs<()> for InboundPayload {
20292040
fn read<R: Read>(r: &mut R, _arg: ()) -> Result<Self, DecodeError> {
20302041
<Self as Readable>::read(r)
20312042
}
@@ -3526,7 +3537,7 @@ mod tests {
35263537

35273538
#[test]
35283539
fn encoding_nonfinal_onion_hop_data() {
3529-
let mut msg = msgs::OnionHopData {
3540+
let msg = msgs::OnionHopData {
35303541
format: OnionHopDataFormat::NonFinalNode {
35313542
short_channel_id: 0xdeadbeef1bad1dea,
35323543
},
@@ -3536,17 +3547,18 @@ mod tests {
35363547
let encoded_value = msg.encode();
35373548
let target_value = hex::decode("1a02080badf00d010203040404ffffffff0608deadbeef1bad1dea").unwrap();
35383549
assert_eq!(encoded_value, target_value);
3539-
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3540-
if let OnionHopDataFormat::NonFinalNode { short_channel_id } = msg.format {
3550+
3551+
let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3552+
if let msgs::InboundPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } = inbound_msg {
35413553
assert_eq!(short_channel_id, 0xdeadbeef1bad1dea);
3554+
assert_eq!(amt_to_forward, 0x0badf00d01020304);
3555+
assert_eq!(outgoing_cltv_value, 0xffffffff);
35423556
} else { panic!(); }
3543-
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
3544-
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
35453557
}
35463558

35473559
#[test]
35483560
fn encoding_final_onion_hop_data() {
3549-
let mut msg = msgs::OnionHopData {
3561+
let msg = msgs::OnionHopData {
35503562
format: OnionHopDataFormat::FinalNode {
35513563
payment_data: None,
35523564
payment_metadata: None,
@@ -3558,16 +3570,18 @@ mod tests {
35583570
let encoded_value = msg.encode();
35593571
let target_value = hex::decode("1002080badf00d010203040404ffffffff").unwrap();
35603572
assert_eq!(encoded_value, target_value);
3561-
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3562-
if let OnionHopDataFormat::FinalNode { payment_data: None, .. } = msg.format { } else { panic!(); }
3563-
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
3564-
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
3573+
3574+
let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3575+
if let msgs::InboundPayload::Receive { payment_data: None, amt_msat, outgoing_cltv_value, .. } = inbound_msg {
3576+
assert_eq!(amt_msat, 0x0badf00d01020304);
3577+
assert_eq!(outgoing_cltv_value, 0xffffffff);
3578+
} else { panic!(); }
35653579
}
35663580

35673581
#[test]
35683582
fn encoding_final_onion_hop_data_with_secret() {
35693583
let expected_payment_secret = PaymentSecret([0x42u8; 32]);
3570-
let mut msg = msgs::OnionHopData {
3584+
let msg = msgs::OnionHopData {
35713585
format: OnionHopDataFormat::FinalNode {
35723586
payment_data: Some(FinalOnionHopData {
35733587
payment_secret: expected_payment_secret,
@@ -3582,19 +3596,21 @@ mod tests {
35823596
let encoded_value = msg.encode();
35833597
let target_value = hex::decode("3602080badf00d010203040404ffffffff082442424242424242424242424242424242424242424242424242424242424242421badca1f").unwrap();
35843598
assert_eq!(encoded_value, target_value);
3585-
msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3586-
if let OnionHopDataFormat::FinalNode {
3599+
3600+
let inbound_msg = Readable::read(&mut Cursor::new(&target_value[..])).unwrap();
3601+
if let msgs::InboundPayload::Receive {
35873602
payment_data: Some(FinalOnionHopData {
35883603
payment_secret,
35893604
total_msat: 0x1badca1f
35903605
}),
3606+
amt_msat, outgoing_cltv_value,
35913607
payment_metadata: None,
35923608
keysend_preimage: None,
3593-
} = msg.format {
3609+
} = inbound_msg {
35943610
assert_eq!(payment_secret, expected_payment_secret);
3611+
assert_eq!(amt_msat, 0x0badf00d01020304);
3612+
assert_eq!(outgoing_cltv_value, 0xffffffff);
35953613
} else { panic!(); }
3596-
assert_eq!(msg.amt_to_forward, 0x0badf00d01020304);
3597-
assert_eq!(msg.outgoing_cltv_value, 0xffffffff);
35983614
}
35993615

36003616
#[test]
@@ -3744,7 +3760,7 @@ mod tests {
37443760
// payload length to be encoded over multiple bytes rather than a single u8.
37453761
let big_payload = encode_big_payload().unwrap();
37463762
let mut rd = Cursor::new(&big_payload[..]);
3747-
<msgs::OnionHopData as Readable>::read(&mut rd).unwrap();
3763+
<msgs::InboundPayload as Readable>::read(&mut rd).unwrap();
37483764
}
37493765
// see above test, needs to be a separate method for use of the serialization macros.
37503766
fn encode_big_payload() -> Result<Vec<u8>, io::Error> {

0 commit comments

Comments
 (0)