Skip to content

Commit dfd0be7

Browse files
committed
f: ensure MPP rejection
1 parent ba45b1f commit dfd0be7

File tree

1 file changed

+256
-0
lines changed

1 file changed

+256
-0
lines changed

lightning/src/ln/blinded_payment_tests.rs

+256
Original file line numberDiff line numberDiff line change
@@ -3150,3 +3150,259 @@ fn test_blinded_trampoline_forward() {
31503150
do_test_blinded_trampoline_forward(Some(TrampolineForwardFailureScenario::InvalidInterTrampolineOnion));
31513151
do_test_blinded_trampoline_forward(Some(TrampolineForwardFailureScenario::InvalidRecipientOnion));
31523152
}
3153+
3154+
#[test]
3155+
fn test_trampoline_mpp_rejection() {
3156+
// Simulate a payment of A (0) -> B (1) -> C(Trampoline) (2) -> D(Trampoline(receive)) (3)
3157+
// MPP segment A: trampoline hops C -> T0 (4) -> D
3158+
// MPP segment B: trampoline hops C -> T1 (5) -> D
3159+
const TOTAL_NODE_COUNT: usize = 6;
3160+
let secp_ctx = Secp256k1::new();
3161+
3162+
let chanmon_cfgs = create_chanmon_cfgs(TOTAL_NODE_COUNT);
3163+
let node_cfgs = create_node_cfgs(TOTAL_NODE_COUNT, &chanmon_cfgs);
3164+
let node_chanmgrs = create_node_chanmgrs(TOTAL_NODE_COUNT, &node_cfgs, &vec![None; TOTAL_NODE_COUNT]);
3165+
let mut nodes = create_network(TOTAL_NODE_COUNT, &node_cfgs, &node_chanmgrs);
3166+
3167+
let large_channel_size = 21_000;
3168+
let small_channel_size = 15_000;
3169+
let (_, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, large_channel_size, 0);
3170+
let (_, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, large_channel_size, 0);
3171+
3172+
// inter-Trampoline-path A
3173+
let (_, _, _, _) = create_announced_chan_between_nodes_with_value(&nodes, 2, 4, small_channel_size, 0);
3174+
let (_, _, _, _) = create_announced_chan_between_nodes_with_value(&nodes, 4, 3, small_channel_size, 0);
3175+
3176+
// inter-Trampoline-path B
3177+
let (_, _, _, _) = create_announced_chan_between_nodes_with_value(&nodes, 2, 5, small_channel_size, 0);
3178+
let (_, _, _, _) = create_announced_chan_between_nodes_with_value(&nodes, 5, 3, small_channel_size, 0);
3179+
3180+
for i in 0..TOTAL_NODE_COUNT { // connect all nodes' blocks
3181+
connect_blocks(&nodes[i], (TOTAL_NODE_COUNT as u32) * CHAN_CONFIRM_DEPTH + 1 - nodes[i].best_block_info().1);
3182+
}
3183+
3184+
let alice_node_id = nodes[0].node().get_our_node_id();
3185+
let bob_node_id = nodes[1].node().get_our_node_id();
3186+
let carol_node_id = nodes[2].node().get_our_node_id();
3187+
let dave_node_id = nodes[3].node().get_our_node_id();
3188+
3189+
let alice_bob_scid = nodes[0].node().list_channels().iter().find(|c| c.channel_id == chan_id_alice_bob).unwrap().short_channel_id.unwrap();
3190+
let bob_carol_scid = nodes[1].node().list_channels().iter().find(|c| c.channel_id == chan_id_bob_carol).unwrap().short_channel_id.unwrap();
3191+
3192+
let amt_msat = 2_000_000; // send 2k satoshis over channels allowing 20k satoshis
3193+
let (payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash(&nodes[3], Some(amt_msat), None);
3194+
3195+
let route = Route {
3196+
paths: vec![Path {
3197+
hops: vec![
3198+
// Bob
3199+
RouteHop {
3200+
pubkey: bob_node_id,
3201+
node_features: NodeFeatures::empty(),
3202+
short_channel_id: alice_bob_scid,
3203+
channel_features: ChannelFeatures::empty(),
3204+
fee_msat: 1000, // forwarding fee to Carol
3205+
cltv_expiry_delta: 48,
3206+
maybe_announced_channel: false,
3207+
},
3208+
3209+
// Carol
3210+
RouteHop {
3211+
pubkey: carol_node_id,
3212+
node_features: NodeFeatures::empty(),
3213+
short_channel_id: bob_carol_scid,
3214+
channel_features: ChannelFeatures::empty(),
3215+
fee_msat: 3000, // fee for the usage of the entire blinded path, including Trampoline
3216+
cltv_expiry_delta: 48,
3217+
maybe_announced_channel: false,
3218+
}
3219+
],
3220+
blinded_tail: Some(BlindedTail {
3221+
trampoline_hops: vec![
3222+
// Carol
3223+
TrampolineHop {
3224+
pubkey: carol_node_id,
3225+
node_features: Features::empty(),
3226+
fee_msat: amt_msat,
3227+
cltv_expiry_delta: 176, // let her cook
3228+
},
3229+
3230+
// Dave (recipient)
3231+
TrampolineHop {
3232+
pubkey: dave_node_id,
3233+
node_features: Features::empty(),
3234+
fee_msat: 0, // no need to charge a fee as the recipient
3235+
cltv_expiry_delta: 24,
3236+
},
3237+
],
3238+
hops: vec![
3239+
// Dave's blinded node id
3240+
BlindedHop {
3241+
blinded_node_id: pubkey_from_hex("0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be"),
3242+
encrypted_payload: bytes_from_hex("0ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a"),
3243+
}
3244+
],
3245+
blinding_point: alice_node_id,
3246+
excess_final_cltv_expiry_delta: 39,
3247+
final_value_msat: amt_msat,
3248+
})
3249+
}],
3250+
route_params: None,
3251+
};
3252+
3253+
nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap();
3254+
3255+
let replacement_onion = {
3256+
// create a substitute onion where the last Trampoline hop is an unblinded receive, which we
3257+
// (deliberately) do not support out of the box, therefore necessitating this workaround
3258+
let trampoline_secret_key = secret_from_hex("0134928f7b7ca6769080d70f16be84c812c741f545b49a34db47ce338a205799");
3259+
let prng_seed = secret_from_hex("fe02b4b9054302a3ddf4e1e9f7c411d644aebbd295218ab009dca94435f775a9");
3260+
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
3261+
3262+
let blinded_tail = route.paths[0].blinded_tail.clone().unwrap();
3263+
let (mut trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils::build_trampoline_onion_payloads(&blinded_tail, amt_msat, &recipient_onion_fields, 32, &None).unwrap();
3264+
3265+
// pop the last dummy hop
3266+
trampoline_payloads.pop();
3267+
3268+
trampoline_payloads.push(msgs::OutboundTrampolinePayload::Receive {
3269+
payment_data: Some(msgs::FinalOnionHopData {
3270+
payment_secret,
3271+
total_msat: amt_msat,
3272+
}),
3273+
sender_intended_htlc_amt_msat: amt_msat,
3274+
cltv_expiry_height: 96,
3275+
});
3276+
3277+
let trampoline_onion_keys = onion_utils::construct_trampoline_onion_keys(&secp_ctx, &route.paths[0].blinded_tail.as_ref().unwrap(), &trampoline_secret_key);
3278+
let trampoline_packet = onion_utils::construct_trampoline_onion_packet(
3279+
trampoline_payloads,
3280+
trampoline_onion_keys,
3281+
prng_seed.secret_bytes(),
3282+
&payment_hash,
3283+
None,
3284+
).unwrap();
3285+
3286+
let outer_session_priv = secret_from_hex("e52c20461ed7acd46c4e7b591a37610519179482887bd73bf3b94617f8f03677");
3287+
3288+
let (outer_payloads, _, _) = onion_utils::build_onion_payloads(&route.paths[0], outer_total_msat, &recipient_onion_fields, outer_starting_htlc_offset, &None, None, Some(trampoline_packet)).unwrap();
3289+
let outer_onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.clone().paths[0], &outer_session_priv);
3290+
let outer_packet = onion_utils::construct_onion_packet(
3291+
outer_payloads,
3292+
outer_onion_keys,
3293+
prng_seed.secret_bytes(),
3294+
&payment_hash,
3295+
).unwrap();
3296+
3297+
outer_packet
3298+
};
3299+
3300+
check_added_monitors!(&nodes[0], 1);
3301+
3302+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
3303+
assert_eq!(events.len(), 1);
3304+
let mut first_message_event = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
3305+
let mut update_message = match first_message_event {
3306+
MessageSendEvent::UpdateHTLCs { ref mut updates, .. } => {
3307+
assert_eq!(updates.update_add_htlcs.len(), 1);
3308+
updates.update_add_htlcs.get_mut(0)
3309+
},
3310+
_ => panic!()
3311+
};
3312+
update_message.map(|msg| {
3313+
msg.onion_routing_packet = replacement_onion.clone();
3314+
});
3315+
3316+
let unforked_route: &[&Node] = &[&nodes[1], &nodes[2]];
3317+
pass_along_path(&nodes[0], unforked_route, amt_msat, payment_hash.clone(),
3318+
None, first_message_event.clone(), false, Some(payment_preimage));
3319+
3320+
check_added_monitors!(&nodes[2], 2);
3321+
let mut events = nodes[2].node.get_and_clear_pending_msg_events();
3322+
assert_eq!(events.len(), 2);
3323+
3324+
let intermediate_message_event_a = remove_first_msg_event_to_node(&nodes[4].node.get_our_node_id(), &mut events);
3325+
let intermediate_message_event_b = remove_first_msg_event_to_node(&nodes[5].node.get_our_node_id(), &mut events);
3326+
3327+
let route_via_t0: &[&Node] = &[&nodes[4], &nodes[3]];
3328+
let route_via_t1: &[&Node] = &[&nodes[5], &nodes[3]];
3329+
let args_a = PassAlongPathArgs::new(&nodes[2], route_via_t0, amt_msat, payment_hash, intermediate_message_event_a.clone())
3330+
.expect_failure(HTLCDestination::FailedPayment { payment_hash });
3331+
let args_b = PassAlongPathArgs::new(&nodes[2], route_via_t1, amt_msat, payment_hash, intermediate_message_event_b.clone())
3332+
.expect_failure(HTLCDestination::FailedPayment { payment_hash });
3333+
3334+
{
3335+
do_pass_along_path(args_a);
3336+
{
3337+
let downstream_id = 3;
3338+
let upstream_id = 4;
3339+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[downstream_id], nodes[upstream_id].node.get_our_node_id());
3340+
nodes[upstream_id].node.handle_update_fail_htlc(
3341+
nodes[downstream_id].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
3342+
);
3343+
do_commitment_signed_dance(&nodes[upstream_id], &nodes[downstream_id], &unblinded_node_updates.commitment_signed, true, false);
3344+
}
3345+
{
3346+
let downstream_id = 4;
3347+
let upstream_id = 2;
3348+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[downstream_id], nodes[upstream_id].node.get_our_node_id());
3349+
nodes[upstream_id].node.handle_update_fail_htlc(
3350+
nodes[downstream_id].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
3351+
);
3352+
do_commitment_signed_dance(&nodes[upstream_id], &nodes[downstream_id], &unblinded_node_updates.commitment_signed, true, false);
3353+
}
3354+
{
3355+
let downstream_id = 2;
3356+
let upstream_id = 1;
3357+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[downstream_id], nodes[upstream_id].node.get_our_node_id());
3358+
nodes[upstream_id].node.handle_update_fail_htlc(
3359+
nodes[downstream_id].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
3360+
);
3361+
do_commitment_signed_dance(&nodes[upstream_id], &nodes[downstream_id], &unblinded_node_updates.commitment_signed, true, false);
3362+
}
3363+
{
3364+
let downstream_id = 1;
3365+
let upstream_id = 0;
3366+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[downstream_id], nodes[upstream_id].node.get_our_node_id());
3367+
nodes[upstream_id].node.handle_update_fail_htlc(
3368+
nodes[downstream_id].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
3369+
);
3370+
do_commitment_signed_dance(&nodes[upstream_id], &nodes[downstream_id], &unblinded_node_updates.commitment_signed, false, false);
3371+
}
3372+
{
3373+
let payment_failed_conditions = PaymentFailedConditions::new()
3374+
.expected_htlc_error_data(0x2000 | 25, &[0; 0]);
3375+
expect_payment_failed_conditions(&nodes[0], payment_hash, false, payment_failed_conditions);
3376+
}
3377+
}
3378+
3379+
{
3380+
do_pass_along_path(args_b);
3381+
{
3382+
let downstream_id = 3;
3383+
let upstream_id = 5;
3384+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[downstream_id], nodes[upstream_id].node.get_our_node_id());
3385+
nodes[upstream_id].node.handle_update_fail_htlc(
3386+
nodes[downstream_id].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
3387+
);
3388+
do_commitment_signed_dance(&nodes[upstream_id], &nodes[downstream_id], &unblinded_node_updates.commitment_signed, true, false);
3389+
}
3390+
{
3391+
let downstream_id = 5;
3392+
let upstream_id = 2;
3393+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[downstream_id], nodes[upstream_id].node.get_our_node_id());
3394+
nodes[upstream_id].node.handle_update_fail_htlc(
3395+
nodes[downstream_id].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
3396+
);
3397+
do_commitment_signed_dance(&nodes[upstream_id], &nodes[downstream_id], &unblinded_node_updates.commitment_signed, false, false);
3398+
}
3399+
{
3400+
let events = nodes[2].node.get_and_clear_pending_events();
3401+
assert_eq!(events.len(), 2);
3402+
match events[0] {
3403+
Event::HTLCHandlingFailed { .. } => {}
3404+
_ => panic!("unexpected event")
3405+
};
3406+
}
3407+
}
3408+
}

0 commit comments

Comments
 (0)