Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 50 additions & 58 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4466,38 +4466,24 @@ where
}
match decoded_hop {
onion_utils::Hop::Receive { .. } | onion_utils::Hop::BlindedReceive { .. } => {
let inbound_onion_payload = match decoded_hop {
onion_utils::Hop::Receive { hop_data, .. } => msgs::InboundOnionPayload::Receive(hop_data),
onion_utils::Hop::BlindedReceive { hop_data, .. } => msgs::InboundOnionPayload::BlindedReceive(hop_data),
_ => unreachable!()
};

// OUR PAYMENT!
let current_height: u32 = self.best_block.read().unwrap().height;
match create_recv_pending_htlc_info(inbound_onion_payload, shared_secret, msg.payment_hash,
match create_recv_pending_htlc_info(decoded_hop, shared_secret, msg.payment_hash,
msg.amount_msat, msg.cltv_expiry, None, allow_underpay, msg.skimmed_fee_msat,
current_height)
{
Ok(info) => {
// Note that we could obviously respond immediately with an update_fulfill_htlc
// message, however that would leak that we are the recipient of this payment, so
// instead we stay symmetric with the forwarding case, only responding (after a
// delay) once they've send us a commitment_signed!
// delay) once they've sent us a commitment_signed!
PendingHTLCStatus::Forward(info)
},
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
}
},
onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes, .. } => {
match create_fwd_pending_htlc_info(msg, msgs::InboundOnionPayload::Forward(next_hop_data), next_hop_hmac,
new_packet_bytes, shared_secret, next_packet_pubkey_opt) {
Ok(info) => PendingHTLCStatus::Forward(info),
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
}
},
onion_utils::Hop::BlindedForward { next_hop_data, next_hop_hmac, new_packet_bytes, .. } => {
match create_fwd_pending_htlc_info(msg, msgs::InboundOnionPayload::BlindedForward(next_hop_data), next_hop_hmac,
new_packet_bytes, shared_secret, next_packet_pubkey_opt) {
onion_utils::Hop::Forward { .. } | onion_utils::Hop::BlindedForward { .. } => {
match create_fwd_pending_htlc_info(msg, decoded_hop, shared_secret, next_packet_pubkey_opt) {
Ok(info) => PendingHTLCStatus::Forward(info),
Err(InboundHTLCErr { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
}
Expand Down Expand Up @@ -5912,14 +5898,9 @@ where
failed_payment!(err_msg, err_code, Vec::new(), Some(phantom_shared_secret));
},
};
let (inbound_onion_payload, shared_secret) = match next_hop {
onion_utils::Hop::Receive { hop_data, shared_secret } => (msgs::InboundOnionPayload::Receive(hop_data), shared_secret),
onion_utils::Hop::BlindedReceive { hop_data, shared_secret } => (msgs::InboundOnionPayload::BlindedReceive(hop_data), shared_secret),
_ => panic!()
};
let phantom_shared_secret = shared_secret.secret_bytes();
let phantom_shared_secret = next_hop.shared_secret().secret_bytes();
let current_height: u32 = self.best_block.read().unwrap().height;
match create_recv_pending_htlc_info(inbound_onion_payload,
match create_recv_pending_htlc_info(next_hop,
incoming_shared_secret, payment_hash, outgoing_amt_msat,
outgoing_cltv_value, Some(phantom_shared_secret), false, None,
current_height)
Expand Down Expand Up @@ -14847,13 +14828,15 @@ where
mod tests {
use bitcoin::hashes::Hash;
use bitcoin::secp256k1::{PublicKey, Secp256k1, SecretKey};
use bitcoin::secp256k1::ecdh::SharedSecret;
use core::sync::atomic::Ordering;
use crate::events::{Event, HTLCDestination, ClosureReason};
use crate::ln::types::ChannelId;
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::ln::channelmanager::{create_recv_pending_htlc_info, inbound_payment, ChannelConfigOverrides, HTLCForwardInfo, InterceptId, PaymentId, RecipientOnionFields};
use crate::ln::functional_test_utils::*;
use crate::ln::msgs::{self, BaseMessageHandler, ChannelMessageHandler, AcceptChannel, ErrorAction, MessageSendEvent};
use crate::ln::onion_utils;
use crate::ln::outbound_payment::Retry;
use crate::prelude::*;
use crate::routing::router::{PaymentParameters, RouteParameters, find_route};
Expand Down Expand Up @@ -15944,17 +15927,20 @@ mod tests {
let node = create_network(1, &node_cfg, &node_chanmgr);
let sender_intended_amt_msat = 100;
let extra_fee_msat = 10;
let hop_data = msgs::InboundOnionPayload::Receive(msgs::InboundOnionReceivePayload {
sender_intended_htlc_amt_msat: 100,
cltv_expiry_height: 42,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
payment_secret: PaymentSecret([0; 32]),
total_msat: sender_intended_amt_msat,
}),
custom_tlvs: Vec::new(),
});
let hop_data = onion_utils::Hop::Receive {
hop_data: msgs::InboundOnionReceivePayload {
sender_intended_htlc_amt_msat: 100,
cltv_expiry_height: 42,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
payment_secret: PaymentSecret([0; 32]),
total_msat: sender_intended_amt_msat,
}),
custom_tlvs: Vec::new(),
},
shared_secret: SharedSecret::from_bytes([0; 32]),
};
// Check that if the amount we received + the penultimate hop extra fee is less than the sender
// intended amount, we fail the payment.
let current_height: u32 = node[0].node.best_block.read().unwrap().height;
Expand All @@ -15967,17 +15953,20 @@ mod tests {
} else { panic!(); }

// If amt_received + extra_fee is equal to the sender intended amount, we're fine.
let hop_data = msgs::InboundOnionPayload::Receive(msgs::InboundOnionReceivePayload { // This is the same payload as above, InboundOnionPayload doesn't implement Clone
sender_intended_htlc_amt_msat: 100,
cltv_expiry_height: 42,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
payment_secret: PaymentSecret([0; 32]),
total_msat: sender_intended_amt_msat,
}),
custom_tlvs: Vec::new(),
});
let hop_data = onion_utils::Hop::Receive {
hop_data: msgs::InboundOnionReceivePayload { // This is the same payload as above, InboundOnionPayload doesn't implement Clone
sender_intended_htlc_amt_msat: 100,
cltv_expiry_height: 42,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
payment_secret: PaymentSecret([0; 32]),
total_msat: sender_intended_amt_msat,
}),
custom_tlvs: Vec::new(),
},
shared_secret: SharedSecret::from_bytes([0; 32]),
};
let current_height: u32 = node[0].node.best_block.read().unwrap().height;
assert!(create_recv_pending_htlc_info(hop_data, [0; 32], PaymentHash([0; 32]),
sender_intended_amt_msat - extra_fee_msat, 42, None, true, Some(extra_fee_msat),
Expand All @@ -15992,17 +15981,20 @@ mod tests {
let node = create_network(1, &node_cfg, &node_chanmgr);

let current_height: u32 = node[0].node.best_block.read().unwrap().height;
let result = create_recv_pending_htlc_info(msgs::InboundOnionPayload::Receive(msgs::InboundOnionReceivePayload {
sender_intended_htlc_amt_msat: 100,
cltv_expiry_height: TEST_FINAL_CLTV,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
payment_secret: PaymentSecret([0; 32]),
total_msat: 100,
}),
custom_tlvs: Vec::new(),
}), [0; 32], PaymentHash([0; 32]), 100, TEST_FINAL_CLTV + 1, None, true, None, current_height);
let result = create_recv_pending_htlc_info(onion_utils::Hop::Receive {
hop_data: msgs::InboundOnionReceivePayload {
sender_intended_htlc_amt_msat: 100,
cltv_expiry_height: TEST_FINAL_CLTV,
payment_metadata: None,
keysend_preimage: None,
payment_data: Some(msgs::FinalOnionHopData {
payment_secret: PaymentSecret([0; 32]),
total_msat: 100,
}),
custom_tlvs: Vec::new(),
},
shared_secret: SharedSecret::from_bytes([0; 32]),
}, [0; 32], PaymentHash([0; 32]), 100, TEST_FINAL_CLTV + 1, None, true, None, current_height);

// Should not return an error as this condition:
// https://github.com/lightning/bolts/blob/4dcc377209509b13cf89a4b91fde7d478f5b46d8/04-onion-routing.md?plain=1#L334
Expand Down
80 changes: 34 additions & 46 deletions lightning/src/ln/onion_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use crate::ln::channelmanager::{BlindedFailure, BlindedForward, CLTV_FAR_FAR_AWA
use crate::types::features::BlindedHopFeatures;
use crate::ln::msgs;
use crate::ln::onion_utils;
use crate::ln::onion_utils::{Hop, HTLCFailReason, INVALID_ONION_BLINDING};
use crate::ln::onion_utils::{HTLCFailReason, INVALID_ONION_BLINDING};
use crate::sign::{NodeSigner, Recipient};
use crate::util::logger::Logger;

Expand Down Expand Up @@ -61,28 +61,23 @@ fn check_blinded_forward(
}

pub(super) fn create_fwd_pending_htlc_info(
msg: &msgs::UpdateAddHTLC, hop_data: msgs::InboundOnionPayload, hop_hmac: [u8; 32],
new_packet_bytes: [u8; onion_utils::ONION_DATA_LEN], shared_secret: [u8; 32],
msg: &msgs::UpdateAddHTLC, hop_data: onion_utils::Hop, shared_secret: [u8; 32],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in a followup we drop the shared_secret argument given that Hop::Forward already has it?

next_packet_pubkey_opt: Option<Result<PublicKey, secp256k1::Error>>
) -> Result<PendingHTLCInfo, InboundHTLCErr> {
debug_assert!(next_packet_pubkey_opt.is_some());
let outgoing_packet = msgs::OnionPacket {
version: 0,
public_key: next_packet_pubkey_opt.unwrap_or(Err(secp256k1::Error::InvalidPublicKey)),
hop_data: new_packet_bytes,
hmac: hop_hmac,
};

let (
short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point,
next_blinding_override
next_blinding_override, new_packet_bytes, next_hop_hmac
) = match hop_data {
msgs::InboundOnionPayload::Forward(msgs::InboundOnionForwardPayload { short_channel_id, amt_to_forward, outgoing_cltv_value }) =>
(short_channel_id, amt_to_forward, outgoing_cltv_value, None, None),
msgs::InboundOnionPayload::BlindedForward(msgs::InboundOnionBlindedForwardPayload {
onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionForwardPayload {
short_channel_id, amt_to_forward, outgoing_cltv_value
}, new_packet_bytes, next_hop_hmac, .. } =>
(short_channel_id, amt_to_forward, outgoing_cltv_value, None, None, new_packet_bytes, next_hop_hmac),
onion_utils::Hop::BlindedForward { next_hop_data: msgs::InboundOnionBlindedForwardPayload {
short_channel_id, payment_relay, payment_constraints, intro_node_blinding_point, features,
next_blinding_override,
}) => {
}, new_packet_bytes, next_hop_hmac, .. } => {
let (amt_to_forward, outgoing_cltv_value) = check_blinded_forward(
msg.amount_msat, msg.cltv_expiry, &payment_relay, &payment_constraints, &features
).map_err(|()| {
Expand All @@ -95,16 +90,23 @@ pub(super) fn create_fwd_pending_htlc_info(
}
})?;
(short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point,
next_blinding_override)
next_blinding_override, new_packet_bytes, next_hop_hmac)
},
msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } =>
onion_utils::Hop::Receive { .. } | onion_utils::Hop::BlindedReceive { .. } =>
return Err(InboundHTLCErr {
msg: "Final Node OnionHopData provided for us as an intermediary node",
err_code: 0x4000 | 22,
err_data: Vec::new(),
}),
};

let outgoing_packet = msgs::OnionPacket {
version: 0,
public_key: next_packet_pubkey_opt.unwrap_or(Err(secp256k1::Error::InvalidPublicKey)),
hop_data: new_packet_bytes,
hmac: next_hop_hmac,
};

Ok(PendingHTLCInfo {
routing: PendingHTLCRouting::Forward {
onion_packet: outgoing_packet,
Expand All @@ -129,7 +131,7 @@ pub(super) fn create_fwd_pending_htlc_info(
}

pub(super) fn create_recv_pending_htlc_info(
hop_data: msgs::InboundOnionPayload, shared_secret: [u8; 32], payment_hash: PaymentHash,
hop_data: onion_utils::Hop, shared_secret: [u8; 32], payment_hash: PaymentHash,
amt_msat: u64, cltv_expiry: u32, phantom_shared_secret: Option<[u8; 32]>, allow_underpay: bool,
counterparty_skimmed_fee_msat: Option<u64>, current_height: u32
) -> Result<PendingHTLCInfo, InboundHTLCErr> {
Expand All @@ -138,17 +140,17 @@ pub(super) fn create_recv_pending_htlc_info(
payment_metadata, payment_context, requires_blinded_error, has_recipient_created_payment_secret,
invoice_request
) = match hop_data {
msgs::InboundOnionPayload::Receive(msgs::InboundOnionReceivePayload {
onion_utils::Hop::Receive { hop_data: msgs::InboundOnionReceivePayload {
payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat,
cltv_expiry_height, payment_metadata, ..
}) =>
}, .. } =>
(payment_data, keysend_preimage, custom_tlvs, sender_intended_htlc_amt_msat,
cltv_expiry_height, payment_metadata, None, false, keysend_preimage.is_none(), None),
msgs::InboundOnionPayload::BlindedReceive(msgs::InboundOnionBlindedReceivePayload {
onion_utils::Hop::BlindedReceive { hop_data: msgs::InboundOnionBlindedReceivePayload {
sender_intended_htlc_amt_msat, total_msat, cltv_expiry_height, payment_secret,
intro_node_blinding_point, payment_constraints, payment_context, keysend_preimage,
custom_tlvs, invoice_request
}) => {
}, .. } => {
check_blinded_payment_constraints(
sender_intended_htlc_amt_msat, cltv_expiry, &payment_constraints
)
Expand All @@ -164,20 +166,20 @@ pub(super) fn create_recv_pending_htlc_info(
sender_intended_htlc_amt_msat, cltv_expiry_height, None, Some(payment_context),
intro_node_blinding_point.is_none(), true, invoice_request)
}
msgs::InboundOnionPayload::Forward { .. } => {
onion_utils::Hop::Forward { .. } => {
return Err(InboundHTLCErr {
err_code: 0x4000|22,
err_data: Vec::new(),
msg: "Got non final data with an HMAC of 0",
})
},
msgs::InboundOnionPayload::BlindedForward { .. } => {
onion_utils::Hop::BlindedForward { .. } => {
return Err(InboundHTLCErr {
err_code: INVALID_ONION_BLINDING,
err_data: vec![0; 32],
msg: "Got blinded non final data with an HMAC of 0",
})
}
},
};
// final_incorrect_cltv_expiry
if onion_cltv_expiry > cltv_expiry {
Expand Down Expand Up @@ -295,14 +297,8 @@ where
InboundHTLCErr { msg, err_code, err_data }
})?;
Ok(match hop {
onion_utils::Hop::Forward { shared_secret, next_hop_hmac, new_packet_bytes, .. } |
onion_utils::Hop::BlindedForward { shared_secret, next_hop_hmac, new_packet_bytes, .. } => {
let inbound_onion_payload = match hop {
onion_utils::Hop::Forward { next_hop_data, .. } => msgs::InboundOnionPayload::Forward(next_hop_data),
onion_utils::Hop::BlindedForward { next_hop_data, .. } => msgs::InboundOnionPayload::BlindedForward(next_hop_data),
_ => unreachable!()
};

onion_utils::Hop::Forward { shared_secret, .. } |
onion_utils::Hop::BlindedForward { shared_secret, .. } => {
let NextPacketDetails {
next_packet_pubkey, outgoing_amt_msat: _, outgoing_scid: _, outgoing_cltv_value
} = match next_packet_details_opt {
Expand All @@ -327,20 +323,12 @@ where

// TODO: If this is potentially a phantom payment we should decode the phantom payment
// onion here and check it.
create_fwd_pending_htlc_info(
msg, inbound_onion_payload, next_hop_hmac, new_packet_bytes, shared_secret.secret_bytes(),
Some(next_packet_pubkey),
)?
},
onion_utils::Hop::Receive { hop_data, shared_secret } => {
create_recv_pending_htlc_info(
msgs::InboundOnionPayload::Receive(hop_data), shared_secret.secret_bytes(), msg.payment_hash, msg.amount_msat, msg.cltv_expiry,
None, allow_skimmed_fees, msg.skimmed_fee_msat, cur_height,
)?
create_fwd_pending_htlc_info(msg, hop, shared_secret.secret_bytes(), Some(next_packet_pubkey))?
},
onion_utils::Hop::BlindedReceive { hop_data, shared_secret } => {
_ => {
let shared_secret = hop.shared_secret().secret_bytes();
create_recv_pending_htlc_info(
msgs::InboundOnionPayload::BlindedReceive(hop_data), shared_secret.secret_bytes(), msg.payment_hash, msg.amount_msat, msg.cltv_expiry,
hop, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry,
None, allow_skimmed_fees, msg.skimmed_fee_msat, cur_height,
)?
}
Expand Down Expand Up @@ -422,15 +410,15 @@ where
};

let next_packet_details = match next_hop {
Hop::Forward { next_hop_data: msgs::InboundOnionForwardPayload { short_channel_id, amt_to_forward, outgoing_cltv_value }, shared_secret, .. } => {
onion_utils::Hop::Forward { next_hop_data: msgs::InboundOnionForwardPayload { short_channel_id, amt_to_forward, outgoing_cltv_value }, shared_secret, .. } => {
let next_packet_pubkey = onion_utils::next_hop_pubkey(secp_ctx,
msg.onion_routing_packet.public_key.unwrap(), &shared_secret.secret_bytes());
Some(NextPacketDetails {
next_packet_pubkey, outgoing_scid: short_channel_id,
outgoing_amt_msat: amt_to_forward, outgoing_cltv_value
})
}
Hop::BlindedForward { next_hop_data: msgs::InboundOnionBlindedForwardPayload { short_channel_id, ref payment_relay, ref payment_constraints, ref features, .. }, shared_secret, .. } => {
onion_utils::Hop::BlindedForward { next_hop_data: msgs::InboundOnionBlindedForwardPayload { short_channel_id, ref payment_relay, ref payment_constraints, ref features, .. }, shared_secret, .. } => {
let (amt_to_forward, outgoing_cltv_value) = match check_blinded_forward(
msg.amount_msat, msg.cltv_expiry, &payment_relay, &payment_constraints, &features
) {
Expand Down