Skip to content

Commit 9f2e6ad

Browse files
channelmanager: DRY PendingHTLCInfo creation for receives
Will be used to facilitate decoding multiple onion layers for phantom payment receive
1 parent a830a1f commit 9f2e6ad

File tree

1 file changed

+111
-61
lines changed

1 file changed

+111
-61
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 111 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,12 @@ pub(super) enum HTLCFailReason {
518518
}
519519
}
520520

521+
struct ReceiveError {
522+
err_code: u16,
523+
err_data: Vec<u8>,
524+
msg: &'static str,
525+
}
526+
521527
/// Return value for claim_funds_from_hop
522528
enum ClaimFundsFromHop {
523529
PrevHopForceClosed,
@@ -2043,6 +2049,102 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
20432049
}
20442050
}
20452051

2052+
fn construct_recv_pending_htlc_info(&self, hop_data: msgs::OnionHopData, shared_secret: [u8; 32],
2053+
payment_hash: PaymentHash, amt_msat: u64, cltv_expiry: u32) -> Result<PendingHTLCInfo, ReceiveError>
2054+
{
2055+
// final_incorrect_cltv_expiry
2056+
if hop_data.outgoing_cltv_value != cltv_expiry {
2057+
return Err(ReceiveError {
2058+
msg: "Upstream node set CLTV to the wrong value",
2059+
err_code: 18,
2060+
err_data: byte_utils::be32_to_array(cltv_expiry).to_vec()
2061+
})
2062+
}
2063+
// final_expiry_too_soon
2064+
// We have to have some headroom to broadcast on chain if we have the preimage, so make sure
2065+
// we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
2066+
// Also, ensure that, in the case of an unknown preimage for the received payment hash, our
2067+
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
2068+
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
2069+
if (hop_data.outgoing_cltv_value as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
2070+
return Err(ReceiveError {
2071+
err_code: 17,
2072+
err_data: Vec::new(),
2073+
msg: "The final CLTV expiry is too soon to handle",
2074+
});
2075+
}
2076+
if hop_data.amt_to_forward > amt_msat {
2077+
return Err(ReceiveError {
2078+
err_code: 19,
2079+
err_data: byte_utils::be64_to_array(amt_msat).to_vec(),
2080+
msg: "Upstream node sent less than we were supposed to receive in payment",
2081+
});
2082+
}
2083+
2084+
let routing = match hop_data.format {
2085+
msgs::OnionHopDataFormat::Legacy { .. } => {
2086+
return Err(ReceiveError {
2087+
err_code: 0x4000|0x2000|3,
2088+
err_data: Vec::new(),
2089+
msg: "We require payment_secrets",
2090+
});
2091+
},
2092+
msgs::OnionHopDataFormat::NonFinalNode { .. } => {
2093+
return Err(ReceiveError {
2094+
err_code: 0x4000|22,
2095+
err_data: Vec::new(),
2096+
msg: "Got non final data with an HMAC of 0",
2097+
});
2098+
},
2099+
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
2100+
if payment_data.is_some() && keysend_preimage.is_some() {
2101+
return Err(ReceiveError {
2102+
err_code: 0x4000|22,
2103+
err_data: Vec::new(),
2104+
msg: "We don't support MPP keysend payments",
2105+
});
2106+
} else if let Some(data) = payment_data {
2107+
PendingHTLCRouting::Receive {
2108+
payment_data: data,
2109+
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
2110+
}
2111+
} else if let Some(payment_preimage) = keysend_preimage {
2112+
// We need to check that the sender knows the keysend preimage before processing this
2113+
// payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
2114+
// could discover the final destination of X, by probing the adjacent nodes on the route
2115+
// with a keysend payment of identical payment hash to X and observing the processing
2116+
// time discrepancies due to a hash collision with X.
2117+
let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
2118+
if hashed_preimage != payment_hash {
2119+
return Err(ReceiveError {
2120+
err_code: 0x4000|22,
2121+
err_data: Vec::new(),
2122+
msg: "Payment preimage didn't match payment hash",
2123+
});
2124+
}
2125+
2126+
PendingHTLCRouting::ReceiveKeysend {
2127+
payment_preimage,
2128+
incoming_cltv_expiry: hop_data.outgoing_cltv_value,
2129+
}
2130+
} else {
2131+
return Err(ReceiveError {
2132+
err_code: 0x4000|0x2000|3,
2133+
err_data: Vec::new(),
2134+
msg: "We require payment_secrets",
2135+
});
2136+
}
2137+
},
2138+
};
2139+
Ok(PendingHTLCInfo {
2140+
routing,
2141+
payment_hash,
2142+
incoming_shared_secret: shared_secret,
2143+
amt_to_forward: amt_msat,
2144+
outgoing_cltv_value: hop_data.outgoing_cltv_value,
2145+
})
2146+
}
2147+
20462148
fn decode_update_add_htlc_onion(&self, msg: &msgs::UpdateAddHTLC) -> (PendingHTLCStatus, MutexGuard<ChannelHolder<Signer>>) {
20472149
macro_rules! return_malformed_err {
20482150
($msg: expr, $err_code: expr) => {
@@ -2108,68 +2210,16 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
21082210
let pending_forward_info = match next_hop {
21092211
onion_utils::Hop::Receive(next_hop_data) => {
21102212
// OUR PAYMENT!
2111-
// final_expiry_too_soon
2112-
// We have to have some headroom to broadcast on chain if we have the preimage, so make sure
2113-
// we have at least HTLC_FAIL_BACK_BUFFER blocks to go.
2114-
// Also, ensure that, in the case of an unknown preimage for the received payment hash, our
2115-
// payment logic has enough time to fail the HTLC backward before our onchain logic triggers a
2116-
// channel closure (see HTLC_FAIL_BACK_BUFFER rationale).
2117-
if (msg.cltv_expiry as u64) <= self.best_block.read().unwrap().height() as u64 + HTLC_FAIL_BACK_BUFFER as u64 + 1 {
2118-
return_err!("The final CLTV expiry is too soon to handle", 17, &[0;0]);
2119-
}
2120-
// final_incorrect_htlc_amount
2121-
if next_hop_data.amt_to_forward > msg.amount_msat {
2122-
return_err!("Upstream node sent less than we were supposed to receive in payment", 19, &byte_utils::be64_to_array(msg.amount_msat));
2123-
}
2124-
// final_incorrect_cltv_expiry
2125-
if next_hop_data.outgoing_cltv_value != msg.cltv_expiry {
2126-
return_err!("Upstream node set CLTV to the wrong value", 18, &byte_utils::be32_to_array(msg.cltv_expiry));
2127-
}
2128-
2129-
let routing = match next_hop_data.format {
2130-
msgs::OnionHopDataFormat::Legacy { .. } => return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]),
2131-
msgs::OnionHopDataFormat::NonFinalNode { .. } => return_err!("Got non final data with an HMAC of 0", 0x4000 | 22, &[0;0]),
2132-
msgs::OnionHopDataFormat::FinalNode { payment_data, keysend_preimage } => {
2133-
if payment_data.is_some() && keysend_preimage.is_some() {
2134-
return_err!("We don't support MPP keysend payments", 0x4000|22, &[0;0]);
2135-
} else if let Some(data) = payment_data {
2136-
PendingHTLCRouting::Receive {
2137-
payment_data: data,
2138-
incoming_cltv_expiry: msg.cltv_expiry,
2139-
}
2140-
} else if let Some(payment_preimage) = keysend_preimage {
2141-
// We need to check that the sender knows the keysend preimage before processing this
2142-
// payment further. Otherwise, an intermediary routing hop forwarding non-keysend-HTLC X
2143-
// could discover the final destination of X, by probing the adjacent nodes on the route
2144-
// with a keysend payment of identical payment hash to X and observing the processing
2145-
// time discrepancies due to a hash collision with X.
2146-
let hashed_preimage = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());
2147-
if hashed_preimage != msg.payment_hash {
2148-
return_err!("Payment preimage didn't match payment hash", 0x4000|22, &[0;0]);
2149-
}
2150-
2151-
PendingHTLCRouting::ReceiveKeysend {
2152-
payment_preimage,
2153-
incoming_cltv_expiry: msg.cltv_expiry,
2154-
}
2155-
} else {
2156-
return_err!("We require payment_secrets", 0x4000|0x2000|3, &[0;0]);
2157-
}
2213+
match self.construct_recv_pending_htlc_info(next_hop_data, shared_secret, msg.payment_hash, msg.amount_msat, msg.cltv_expiry) {
2214+
Ok(info) => {
2215+
// Note that we could obviously respond immediately with an update_fulfill_htlc
2216+
// message, however that would leak that we are the recipient of this payment, so
2217+
// instead we stay symmetric with the forwarding case, only responding (after a
2218+
// delay) once they've send us a commitment_signed!
2219+
PendingHTLCStatus::Forward(info)
21582220
},
2159-
};
2160-
2161-
// Note that we could obviously respond immediately with an update_fulfill_htlc
2162-
// message, however that would leak that we are the recipient of this payment, so
2163-
// instead we stay symmetric with the forwarding case, only responding (after a
2164-
// delay) once they've send us a commitment_signed!
2165-
2166-
PendingHTLCStatus::Forward(PendingHTLCInfo {
2167-
routing,
2168-
payment_hash: msg.payment_hash.clone(),
2169-
incoming_shared_secret: shared_secret,
2170-
amt_to_forward: next_hop_data.amt_to_forward,
2171-
outgoing_cltv_value: next_hop_data.outgoing_cltv_value,
2172-
})
2221+
Err(ReceiveError { err_code, err_data, msg }) => return_err!(msg, err_code, &err_data)
2222+
}
21732223
},
21742224
onion_utils::Hop::Forward { next_hop_data, next_hop_hmac, new_packet_bytes } => {
21752225
let mut new_pubkey = msg.onion_routing_packet.public_key.unwrap();

0 commit comments

Comments
 (0)