Skip to content

Commit b59626b

Browse files
valentinewallacea-mpch
authored andcommitted
Test trampoline fwd payload encoded as receive
This re-adds test coverage for a case that was removed in the previous commit.
1 parent c6a115d commit b59626b

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed

lightning/src/ln/blinded_payment_tests.rs

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1983,6 +1983,194 @@ fn test_trampoline_inbound_payment_decoding() {
19831983
};
19841984
}
19851985

1986+
#[test]
1987+
fn test_trampoline_forward_payload_encoded_as_receive() {
1988+
// Test that we'll fail backwards as expected when receiving a well-formed blinded forward
1989+
// trampoline onion payload with no next hop present.
1990+
const TOTAL_NODE_COUNT: usize = 3;
1991+
let secp_ctx = Secp256k1::new();
1992+
1993+
let chanmon_cfgs = create_chanmon_cfgs(TOTAL_NODE_COUNT);
1994+
let node_cfgs = create_node_cfgs(TOTAL_NODE_COUNT, &chanmon_cfgs);
1995+
let node_chanmgrs = create_node_chanmgrs(TOTAL_NODE_COUNT, &node_cfgs, &vec![None; TOTAL_NODE_COUNT]);
1996+
let mut nodes = create_network(TOTAL_NODE_COUNT, &node_cfgs, &node_chanmgrs);
1997+
1998+
let (_, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 0);
1999+
let (_, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 1_000_000, 0);
2000+
2001+
for i in 0..TOTAL_NODE_COUNT { // connect all nodes' blocks
2002+
connect_blocks(&nodes[i], (TOTAL_NODE_COUNT as u32) * CHAN_CONFIRM_DEPTH + 1 - nodes[i].best_block_info().1);
2003+
}
2004+
2005+
let alice_node_id = nodes[0].node().get_our_node_id();
2006+
let bob_node_id = nodes[1].node().get_our_node_id();
2007+
let carol_node_id = nodes[2].node().get_our_node_id();
2008+
2009+
let alice_bob_scid = nodes[0].node().list_channels().iter().find(|c| c.channel_id == chan_id_alice_bob).unwrap().short_channel_id.unwrap();
2010+
let bob_carol_scid = nodes[1].node().list_channels().iter().find(|c| c.channel_id == chan_id_bob_carol).unwrap().short_channel_id.unwrap();
2011+
2012+
let amt_msat = 1000;
2013+
let (payment_preimage, payment_hash, _) = get_payment_preimage_hash(&nodes[2], Some(amt_msat), None);
2014+
2015+
// We need the session priv to construct an invalid onion packet later.
2016+
let override_random_bytes = [3; 32];
2017+
*nodes[0].keys_manager.override_random_bytes.lock().unwrap() = Some(override_random_bytes);
2018+
2019+
let outer_session_priv = SecretKey::from_slice(&override_random_bytes).unwrap();
2020+
let trampoline_session_priv = onion_utils::compute_trampoline_session_priv(&outer_session_priv);
2021+
2022+
// Create a blinded hop for the recipient that is encoded as a trampoline forward.
2023+
let carol_blinding_point = PublicKey::from_secret_key(&secp_ctx, &trampoline_session_priv);
2024+
let carol_blinded_hops = {
2025+
let payee_tlvs = blinded_path::payment::TrampolineForwardTlvs {
2026+
next_trampoline: alice_node_id,
2027+
payment_constraints: PaymentConstraints {
2028+
max_cltv_expiry: u32::max_value(),
2029+
htlc_minimum_msat: amt_msat,
2030+
},
2031+
features: BlindedHopFeatures::empty(),
2032+
payment_relay: PaymentRelay {
2033+
cltv_expiry_delta: 0,
2034+
fee_proportional_millionths: 0,
2035+
fee_base_msat: 0,
2036+
},
2037+
next_blinding_override: None,
2038+
};
2039+
2040+
let carol_unblinded_tlvs = payee_tlvs.encode();
2041+
let path = [((carol_node_id, None), WithoutLength(&carol_unblinded_tlvs))];
2042+
blinded_path::utils::construct_blinded_hops(
2043+
&secp_ctx, path.into_iter(), &trampoline_session_priv,
2044+
)
2045+
};
2046+
2047+
let route = Route {
2048+
paths: vec![Path {
2049+
hops: vec![
2050+
// Bob
2051+
RouteHop {
2052+
pubkey: bob_node_id,
2053+
node_features: NodeFeatures::empty(),
2054+
short_channel_id: alice_bob_scid,
2055+
channel_features: ChannelFeatures::empty(),
2056+
fee_msat: 1000,
2057+
cltv_expiry_delta: 48,
2058+
maybe_announced_channel: false,
2059+
},
2060+
2061+
// Carol
2062+
RouteHop {
2063+
pubkey: carol_node_id,
2064+
node_features: NodeFeatures::empty(),
2065+
short_channel_id: bob_carol_scid,
2066+
channel_features: ChannelFeatures::empty(),
2067+
fee_msat: 0,
2068+
cltv_expiry_delta: 48,
2069+
maybe_announced_channel: false,
2070+
}
2071+
],
2072+
blinded_tail: Some(BlindedTail {
2073+
trampoline_hops: vec![
2074+
// Carol
2075+
TrampolineHop {
2076+
pubkey: carol_node_id,
2077+
node_features: Features::empty(),
2078+
fee_msat: amt_msat,
2079+
cltv_expiry_delta: 24,
2080+
},
2081+
],
2082+
hops: carol_blinded_hops,
2083+
blinding_point: carol_blinding_point,
2084+
excess_final_cltv_expiry_delta: 39,
2085+
final_value_msat: amt_msat,
2086+
})
2087+
}],
2088+
route_params: None,
2089+
};
2090+
2091+
nodes[0].node.send_payment_with_route(route.clone(), payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0)).unwrap();
2092+
check_added_monitors!(&nodes[0], 1);
2093+
2094+
let replacement_onion = {
2095+
// create a substitute onion where the last Trampoline hop is a forward
2096+
let recipient_onion_fields = RecipientOnionFields::spontaneous_empty();
2097+
2098+
let mut blinded_tail = route.paths[0].blinded_tail.clone().unwrap();
2099+
2100+
// append some dummy blinded hop so the intro hop looks like a forward
2101+
blinded_tail.hops.push(BlindedHop {
2102+
blinded_node_id: alice_node_id,
2103+
encrypted_payload: vec![],
2104+
});
2105+
2106+
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();
2107+
2108+
// pop the last dummy hop
2109+
trampoline_payloads.pop();
2110+
2111+
let trampoline_onion_keys = onion_utils::construct_trampoline_onion_keys(&secp_ctx, &route.paths[0].blinded_tail.as_ref().unwrap(), &trampoline_session_priv);
2112+
let trampoline_packet = onion_utils::construct_trampoline_onion_packet(
2113+
trampoline_payloads,
2114+
trampoline_onion_keys,
2115+
override_random_bytes,
2116+
&payment_hash,
2117+
None,
2118+
).unwrap();
2119+
2120+
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();
2121+
let outer_onion_keys = onion_utils::construct_onion_keys(&secp_ctx, &route.clone().paths[0], &outer_session_priv);
2122+
let outer_packet = onion_utils::construct_onion_packet(
2123+
outer_payloads,
2124+
outer_onion_keys,
2125+
override_random_bytes,
2126+
&payment_hash,
2127+
).unwrap();
2128+
2129+
outer_packet
2130+
};
2131+
2132+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
2133+
assert_eq!(events.len(), 1);
2134+
let mut first_message_event = remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events);
2135+
let mut update_message = match first_message_event {
2136+
MessageSendEvent::UpdateHTLCs { ref mut updates, .. } => {
2137+
assert_eq!(updates.update_add_htlcs.len(), 1);
2138+
updates.update_add_htlcs.get_mut(0)
2139+
},
2140+
_ => panic!()
2141+
};
2142+
update_message.map(|msg| {
2143+
msg.onion_routing_packet = replacement_onion.clone();
2144+
});
2145+
2146+
let route: &[&Node] = &[&nodes[1], &nodes[2]];
2147+
let args = PassAlongPathArgs::new(&nodes[0], route, amt_msat, payment_hash, first_message_event)
2148+
.with_payment_preimage(payment_preimage)
2149+
.without_claimable_event()
2150+
.expect_failure(HTLCHandlingFailureType::InvalidOnion);
2151+
do_pass_along_path(args);
2152+
2153+
{
2154+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
2155+
nodes[1].node.handle_update_fail_htlc(
2156+
nodes[2].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
2157+
);
2158+
do_commitment_signed_dance(&nodes[1], &nodes[2], &unblinded_node_updates.commitment_signed, true, false);
2159+
}
2160+
{
2161+
let unblinded_node_updates = get_htlc_update_msgs!(nodes[1], nodes[0].node.get_our_node_id());
2162+
nodes[0].node.handle_update_fail_htlc(
2163+
nodes[1].node.get_our_node_id(), &unblinded_node_updates.update_fail_htlcs[0]
2164+
);
2165+
do_commitment_signed_dance(&nodes[0], &nodes[1], &unblinded_node_updates.commitment_signed, false, false);
2166+
}
2167+
{
2168+
let payment_failed_conditions = PaymentFailedConditions::new()
2169+
.expected_htlc_error_data(LocalHTLCFailureReason::InvalidOnionPayload, &[0; 0]);
2170+
expect_payment_failed_conditions(&nodes[0], payment_hash, true, payment_failed_conditions);
2171+
}
2172+
}
2173+
19862174
fn do_test_trampoline_single_hop_receive(success: bool) {
19872175
const TOTAL_NODE_COUNT: usize = 3;
19882176
let secp_ctx = Secp256k1::new();

0 commit comments

Comments
 (0)