Skip to content

Commit 1b962d2

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 1b962d2

File tree

9 files changed

+115
-101
lines changed

9 files changed

+115
-101
lines changed

fuzz/src/bin/gen_target.sh

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ GEN_TEST refund_deser
2020
GEN_TEST router
2121
GEN_TEST zbase32
2222
GEN_TEST indexedmap
23+
GEN_TEST onion_hop_data
2324

2425
GEN_TEST msg_accept_channel msg_targets::
2526
GEN_TEST msg_announcement_signatures msg_targets::
@@ -51,7 +52,6 @@ GEN_TEST msg_update_add_htlc msg_targets::
5152
GEN_TEST msg_error_message msg_targets::
5253
GEN_TEST msg_channel_update msg_targets::
5354

54-
GEN_TEST msg_onion_hop_data msg_targets::
5555
GEN_TEST msg_ping msg_targets::
5656
GEN_TEST msg_pong msg_targets::
5757

fuzz/src/bin/msg_onion_hop_data_target.rs renamed to fuzz/src/bin/onion_hop_data_target.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616
compile_error!("Fuzz targets need cfg=fuzzing");
1717

1818
extern crate lightning_fuzz;
19-
use lightning_fuzz::msg_targets::msg_onion_hop_data::*;
19+
use lightning_fuzz::onion_hop_data::*;
2020

2121
#[cfg(feature = "afl")]
2222
#[macro_use] extern crate afl;
2323
#[cfg(feature = "afl")]
2424
fn main() {
2525
fuzz!(|data| {
26-
msg_onion_hop_data_run(data.as_ptr(), data.len());
26+
onion_hop_data_run(data.as_ptr(), data.len());
2727
});
2828
}
2929

@@ -33,7 +33,7 @@ fn main() {
3333
fn main() {
3434
loop {
3535
fuzz!(|data| {
36-
msg_onion_hop_data_run(data.as_ptr(), data.len());
36+
onion_hop_data_run(data.as_ptr(), data.len());
3737
});
3838
}
3939
}
@@ -42,7 +42,7 @@ fn main() {
4242
#[macro_use] extern crate libfuzzer_sys;
4343
#[cfg(feature = "libfuzzer_fuzz")]
4444
fuzz_target!(|data: &[u8]| {
45-
msg_onion_hop_data_run(data.as_ptr(), data.len());
45+
onion_hop_data_run(data.as_ptr(), data.len());
4646
});
4747

4848
#[cfg(feature = "stdin_fuzz")]
@@ -51,7 +51,7 @@ fn main() {
5151

5252
let mut data = Vec::with_capacity(8192);
5353
std::io::stdin().read_to_end(&mut data).unwrap();
54-
msg_onion_hop_data_run(data.as_ptr(), data.len());
54+
onion_hop_data_run(data.as_ptr(), data.len());
5555
}
5656

5757
#[test]
@@ -63,11 +63,11 @@ fn run_test_cases() {
6363
use std::sync::{atomic, Arc};
6464
{
6565
let data: Vec<u8> = vec![0];
66-
msg_onion_hop_data_run(data.as_ptr(), data.len());
66+
onion_hop_data_run(data.as_ptr(), data.len());
6767
}
6868
let mut threads = Vec::new();
6969
let threads_running = Arc::new(atomic::AtomicUsize::new(0));
70-
if let Ok(tests) = fs::read_dir("test_cases/msg_onion_hop_data") {
70+
if let Ok(tests) = fs::read_dir("test_cases/onion_hop_data") {
7171
for test in tests {
7272
let mut data: Vec<u8> = Vec::new();
7373
let path = test.unwrap().path();
@@ -82,7 +82,7 @@ fn run_test_cases() {
8282

8383
let panic_logger = string_logger.clone();
8484
let res = if ::std::panic::catch_unwind(move || {
85-
msg_onion_hop_data_test(&data, panic_logger);
85+
onion_hop_data_test(&data, panic_logger);
8686
}).is_err() {
8787
Some(string_logger.into_string())
8888
} else { None };

fuzz/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ pub mod process_network_graph;
2828
pub mod refund_deser;
2929
pub mod router;
3030
pub mod zbase32;
31+
pub mod onion_hop_data;
3132

3233
pub mod msg_targets;

fuzz/src/msg_targets/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ pub mod msg_channel_ready;
88
pub mod msg_funding_signed;
99
pub mod msg_gossip_timestamp_filter;
1010
pub mod msg_init;
11-
pub mod msg_onion_hop_data;
1211
pub mod msg_open_channel;
1312
pub mod msg_ping;
1413
pub mod msg_pong;

fuzz/src/msg_targets/msg_onion_hop_data.rs renamed to fuzz/src/onion_hop_data.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,19 @@
1010
// This file is auto-generated by gen_target.sh based on msg_target_template.txt
1111
// To modify it, modify msg_target_template.txt and run gen_target.sh instead.
1212

13-
use crate::msg_targets::utils::VecWriter;
1413
use crate::utils::test_logger;
1514

1615
#[inline]
17-
pub fn msg_onion_hop_data_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
18-
test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
16+
pub fn onion_hop_data_test<Out: test_logger::Output>(data: &[u8], _out: Out) {
17+
use lightning::util::ser::Readable;
18+
let mut r = ::std::io::Cursor::new(data);
19+
let _ = <lightning::ln::msgs::InboundPayload as Readable>::read(&mut r);
1920
}
2021

2122
#[no_mangle]
22-
pub extern "C" fn msg_onion_hop_data_run(data: *const u8, datalen: usize) {
23+
pub extern "C" fn onion_hop_data_run(data: *const u8, datalen: usize) {
24+
use lightning::util::ser::Readable;
2325
let data = unsafe { std::slice::from_raw_parts(data, datalen) };
24-
test_msg_simple!(lightning::ln::msgs::OnionHopData, data);
26+
let mut r = ::std::io::Cursor::new(data);
27+
let _ = <lightning::ln::msgs::InboundPayload as Readable>::read(&mut r);
2528
}

fuzz/targets.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ void refund_deser_run(const unsigned char* data, size_t data_len);
1313
void router_run(const unsigned char* data, size_t data_len);
1414
void zbase32_run(const unsigned char* data, size_t data_len);
1515
void indexedmap_run(const unsigned char* data, size_t data_len);
16+
void onion_hop_data_run(const unsigned char* data, size_t data_len);
1617
void msg_accept_channel_run(const unsigned char* data, size_t data_len);
1718
void msg_announcement_signatures_run(const unsigned char* data, size_t data_len);
1819
void msg_channel_reestablish_run(const unsigned char* data, size_t data_len);
@@ -40,7 +41,6 @@ void msg_gossip_timestamp_filter_run(const unsigned char* data, size_t data_len)
4041
void msg_update_add_htlc_run(const unsigned char* data, size_t data_len);
4142
void msg_error_message_run(const unsigned char* data, size_t data_len);
4243
void msg_channel_update_run(const unsigned char* data, size_t data_len);
43-
void msg_onion_hop_data_run(const unsigned char* data, size_t data_len);
4444
void msg_ping_run(const unsigned char* data, size_t data_len);
4545
void msg_pong_run(const unsigned char* data, size_t data_len);
4646
void msg_channel_details_run(const unsigned char* data, size_t data_len);

lightning/src/ln/channelmanager.rs

+41-46
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());

0 commit comments

Comments
 (0)