Skip to content

Commit 25aea5a

Browse files
committed
Introduce test for Padding
Add test to verify blinded message and payment path padding.
1 parent ea208f9 commit 25aea5a

File tree

3 files changed

+99
-1
lines changed

3 files changed

+99
-1
lines changed

lightning/src/blinded_path/utils.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,4 +234,19 @@ impl<T:Writeable> Writeable for WithPadding<T> {
234234

235235
self.tlvs.write(writer)
236236
}
237-
}
237+
}
238+
239+
#[cfg(test)]
240+
/// Checks if all the packets in the blinded path are properly padded.
241+
pub fn is_padded(hops: &[BlindedHop]) -> bool {
242+
let first_hop = hops.first().expect("BlindedPath must have at least one hop");
243+
let first_payload_size = first_hop.encrypted_payload.len();
244+
245+
// The unencrypted payload data is padded before getting encrypted.
246+
// Assuming the first payload is padded properly, get the extra data length.
247+
let extra_length = first_payload_size % PADDING_ROUND_OFF;
248+
hops.iter().all(|hop| {
249+
// Check that every packet is padded to the round off length subtracting the extra length.
250+
(hop.encrypted_payload.len() - extra_length) % PADDING_ROUND_OFF == 0
251+
})
252+
}

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use bitcoin::secp256k1::ecdh::SharedSecret;
1313
use bitcoin::secp256k1::ecdsa::{RecoverableSignature, Signature};
1414
use crate::blinded_path;
1515
use crate::blinded_path::payment::{BlindedPaymentPath, PaymentForwardNode, ForwardTlvs, PaymentConstraints, PaymentContext, PaymentRelay, ReceiveTlvs};
16+
use crate::blinded_path::utils::is_padded;
1617
use crate::events::{Event, HTLCDestination, MessageSendEvent, MessageSendEventsProvider, PaymentFailureReason};
1718
use crate::ln::types::{ChannelId, PaymentHash, PaymentSecret};
1819
use crate::ln::channelmanager;
@@ -1345,6 +1346,42 @@ fn custom_tlvs_to_blinded_path() {
13451346
);
13461347
}
13471348

1349+
#[test]
1350+
fn blinded_payment_path_padding() {
1351+
// Make sure that for a blinded payment path, all encrypted payloads are padded to equal lengths.
1352+
let chanmon_cfgs = create_chanmon_cfgs(5);
1353+
let node_cfgs = create_node_cfgs(5, &chanmon_cfgs);
1354+
let node_chanmgrs = create_node_chanmgrs(5, &node_cfgs, &[None, None, None, None, None]);
1355+
let mut nodes = create_network(5, &node_cfgs, &node_chanmgrs);
1356+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
1357+
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
1358+
let chan_upd_2_3 = create_announced_chan_between_nodes_with_value(&nodes, 2, 3, 1_000_000, 0).0.contents;
1359+
let chan_upd_3_4 = create_announced_chan_between_nodes_with_value(&nodes, 3, 4, 1_000_000, 0).0.contents;
1360+
1361+
// Get all our nodes onto the same height so payments don't fail for CLTV violations.
1362+
connect_blocks(&nodes[0], nodes[4].best_block_info().1 - nodes[0].best_block_info().1);
1363+
connect_blocks(&nodes[1], nodes[4].best_block_info().1 - nodes[1].best_block_info().1);
1364+
connect_blocks(&nodes[2], nodes[4].best_block_info().1 - nodes[2].best_block_info().1);
1365+
assert_eq!(nodes[4].best_block_info().1, nodes[3].best_block_info().1);
1366+
1367+
let amt_msat = 5000;
1368+
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[4], Some(amt_msat), None);
1369+
1370+
let blinded_path = blinded_payment_path(payment_secret, 1, 1_0000_0000,
1371+
nodes.iter().skip(2).map(|n| n.node.get_our_node_id()).collect(), &[&chan_upd_2_3, &chan_upd_3_4],
1372+
&chanmon_cfgs[4].keys_manager
1373+
);
1374+
1375+
assert!(is_padded(&blinded_path.blinded_hops()));
1376+
1377+
let route_params = RouteParameters::from_payment_params_and_value(PaymentParameters::blinded(vec![blinded_path]), amt_msat);
1378+
1379+
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
1380+
check_added_monitors(&nodes[0], 1);
1381+
pass_along_route(&nodes[0], &[&[&nodes[1], &nodes[2], &nodes[3], &nodes[4]]], amt_msat, payment_hash, payment_secret);
1382+
claim_payment(&nodes[0], &[&nodes[1], &nodes[2], &nodes[3], &nodes[4]], payment_preimage);
1383+
}
1384+
13481385
fn secret_from_hex(hex: &str) -> SecretKey {
13491386
SecretKey::from_slice(&<Vec<u8>>::from_hex(hex).unwrap()).unwrap()
13501387
}

lightning/src/onion_message/functional_tests.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
1212
use crate::blinded_path::EmptyNodeIdLookUp;
1313
use crate::blinded_path::message::{AsyncPaymentsContext, BlindedMessagePath, DNSResolverContext, MessageForwardNode, MessageContext, OffersContext};
14+
use crate::blinded_path::utils::is_padded;
1415
use crate::events::{Event, EventsProvider};
1516
use crate::ln::features::{ChannelFeatures, InitFeatures};
1617
use crate::ln::msgs::{self, DecodeError, OnionMessageHandler};
@@ -562,6 +563,51 @@ fn too_big_packet_error() {
562563
assert_eq!(err, SendError::TooBigPacket);
563564
}
564565

566+
#[test]
567+
fn test_blinded_path_padding_for_full_length_path() {
568+
// Check that for a full blinded path, all encrypted payload are padded to rounded-off length.
569+
let nodes = create_nodes(4);
570+
let test_msg = TestCustomMessage::Pong;
571+
572+
let secp_ctx = Secp256k1::new();
573+
let intermediate_nodes = [
574+
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: None },
575+
MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: None },
576+
];
577+
// Update the context to create a larger final receive TLVs, ensuring that
578+
// the hop sizes vary before padding.
579+
let context = MessageContext::Custom(vec![0u8; 42]);
580+
let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, &*nodes[3].entropy_source, &secp_ctx).unwrap();
581+
582+
assert!(is_padded(&blinded_path.blinded_hops()));
583+
584+
let destination = Destination::BlindedPath(blinded_path);
585+
let instructions = MessageSendInstructions::WithoutReplyPath { destination };
586+
587+
nodes[0].messenger.send_onion_message(test_msg, instructions).unwrap();
588+
nodes[3].custom_message_handler.expect_message(TestCustomMessage::Pong);
589+
pass_along_path(&nodes);
590+
}
591+
592+
#[test]
593+
fn test_blinded_path_no_padding_for_compact_path() {
594+
// Check that for a compact blinded path, no padding is applied.
595+
let nodes = create_nodes(4);
596+
let secp_ctx = Secp256k1::new();
597+
598+
// Include some short_channel_id, so that MessageRouter uses this to create compact blinded paths.
599+
let intermediate_nodes = [
600+
MessageForwardNode { node_id: nodes[1].node_id, short_channel_id: Some(24) },
601+
MessageForwardNode { node_id: nodes[2].node_id, short_channel_id: Some(25) },
602+
];
603+
// Update the context to create a larger final receive TLVs, ensuring that
604+
// the hop sizes vary before padding.
605+
let context = MessageContext::Custom(vec![0u8; 42]);
606+
let blinded_path = BlindedMessagePath::new(&intermediate_nodes, nodes[3].node_id, context, &*nodes[3].entropy_source, &secp_ctx).unwrap();
607+
608+
assert!(!is_padded(&blinded_path.blinded_hops()));
609+
}
610+
565611
#[test]
566612
fn we_are_intro_node() {
567613
// If we are sending straight to a blinded path and we are the introduction node, we need to

0 commit comments

Comments
 (0)