Support next_blinding_override in blinded payment paths.

This allow us to forward blinded payments where the blinded path that we are
forwarding within was concatenated to another blinded path that starts at the
next hop.

Also allows constructing blinded paths using this override.
This commit is contained in:
Valentine Wallace 2024-07-22 15:22:54 -04:00
parent 8fe3a56d4b
commit f68b1249a9
No known key found for this signature in database
GPG key ID: FD3E106A2CE099B4
8 changed files with 40 additions and 10 deletions

View file

@ -113,6 +113,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
htlc_minimum_msat: 100,
},
features: BlindedHopFeatures::empty(),
next_blinding_override: None,
},
node_id: pubkey(43),
htlc_maximum_msat: 1_000_000_000_000,

View file

@ -91,6 +91,7 @@ fn build_response<T: secp256k1::Signing + secp256k1::Verification>(
htlc_minimum_msat: 100,
},
features: BlindedHopFeatures::empty(),
next_blinding_override: None,
},
node_id: pubkey(43),
htlc_maximum_msat: 1_000_000_000_000,

View file

@ -201,6 +201,9 @@ pub struct ForwardTlvs {
///
/// [`BlindedHop::encrypted_payload`]: crate::blinded_path::BlindedHop::encrypted_payload
pub features: BlindedHopFeatures,
/// Set if this [`BlindedPaymentPath`] is concatenated to another, to indicate the
/// [`BlindedPaymentPath::blinding_point`] of the appended blinded path.
pub next_blinding_override: Option<PublicKey>,
}
/// Data to construct a [`BlindedHop`] for receiving a payment. This payload is custom to LDK and
@ -379,6 +382,7 @@ impl Readable for BlindedPaymentTlvs {
_init_and_read_tlv_stream!(r, {
(1, _padding, option),
(2, scid, option),
(8, next_blinding_override, option),
(10, payment_relay, option),
(12, payment_constraints, required),
(14, features, option),
@ -395,6 +399,7 @@ impl Readable for BlindedPaymentTlvs {
short_channel_id,
payment_relay: payment_relay.ok_or(DecodeError::InvalidValue)?,
payment_constraints: payment_constraints.0.unwrap(),
next_blinding_override,
features: features.unwrap_or_else(BlindedHopFeatures::empty),
}))
} else {
@ -602,6 +607,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 100,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: u64::max_value(),
@ -618,6 +624,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 1_000,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: u64::max_value(),
@ -675,6 +682,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 1,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: u64::max_value()
@ -691,6 +699,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 2_000,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: u64::max_value()
@ -726,6 +735,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 5_000,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: u64::max_value()
@ -742,6 +752,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 2_000,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: u64::max_value()
@ -781,6 +792,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 1,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: 5_000,
@ -797,6 +809,7 @@ mod tests {
max_cltv_expiry: 0,
htlc_minimum_msat: 1,
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: 10_000

View file

@ -49,6 +49,7 @@ fn blinded_payment_path(
htlc_minimum_msat: intro_node_min_htlc_opt.take()
.unwrap_or_else(|| channel_upds[idx - 1].htlc_minimum_msat),
},
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
htlc_maximum_msat: intro_node_max_htlc_opt.take()

View file

@ -226,6 +226,10 @@ pub struct BlindedForward {
/// If needed, this determines how this HTLC should be failed backwards, based on whether we are
/// the introduction node.
pub failure: BlindedFailure,
/// Overrides the next hop's [`msgs::UpdateAddHTLC::blinding_point`]. Set if this HTLC is being
/// forwarded within a [`BlindedPaymentPath`] that was concatenated to another blinded path that
/// starts at the next hop.
pub next_blinding_override: Option<PublicKey>,
}
impl PendingHTLCRouting {
@ -5313,12 +5317,14 @@ where
blinded_failure: blinded.map(|b| b.failure),
});
let next_blinding_point = blinded.and_then(|b| {
let encrypted_tlvs_ss = self.node_signer.ecdh(
Recipient::Node, &b.inbound_blinding_point, None
).unwrap().secret_bytes();
onion_utils::next_hop_pubkey(
&self.secp_ctx, b.inbound_blinding_point, &encrypted_tlvs_ss
).ok()
b.next_blinding_override.or_else(|| {
let encrypted_tlvs_ss = self.node_signer.ecdh(
Recipient::Node, &b.inbound_blinding_point, None
).unwrap().secret_bytes();
onion_utils::next_hop_pubkey(
&self.secp_ctx, b.inbound_blinding_point, &encrypted_tlvs_ss
).ok()
})
});
// Forward the HTLC over the most appropriate channel with the corresponding peer,
@ -11034,6 +11040,7 @@ impl_writeable_tlv_based!(PhantomRouteHints, {
impl_writeable_tlv_based!(BlindedForward, {
(0, inbound_blinding_point, required),
(1, failure, (default_value, BlindedFailure::FromIntroductionNode)),
(3, next_blinding_override, option),
});
impl_writeable_tlv_based_enum!(PendingHTLCRouting,

View file

@ -1764,6 +1764,7 @@ mod fuzzy_internal_msgs {
payment_constraints: PaymentConstraints,
features: BlindedHopFeatures,
intro_node_blinding_point: Option<PublicKey>,
next_blinding_override: Option<PublicKey>,
},
BlindedReceive {
sender_intended_htlc_amt_msat: u64,
@ -2808,7 +2809,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload w
let mut reader = FixedLengthReader::new(&mut s, enc_tlvs.len() as u64);
match ChaChaPolyReadAdapter::read(&mut reader, rho)? {
ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Forward(ForwardTlvs {
short_channel_id, payment_relay, payment_constraints, features
short_channel_id, payment_relay, payment_constraints, features, next_blinding_override
})} => {
if amt.is_some() || cltv_value.is_some() || total_msat.is_some() ||
keysend_preimage.is_some()
@ -2821,6 +2822,7 @@ impl<NS: Deref> ReadableArgs<(Option<PublicKey>, &NS)> for InboundOnionPayload w
payment_constraints,
features,
intro_node_blinding_point,
next_blinding_override,
})
},
ChaChaPolyReadAdapter { readable: BlindedPaymentTlvs::Receive(ReceiveTlvs {

View file

@ -75,12 +75,14 @@ pub(super) fn create_fwd_pending_htlc_info(
};
let (
short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point
short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point,
next_blinding_override
) = match hop_data {
msgs::InboundOnionPayload::Forward { short_channel_id, amt_to_forward, outgoing_cltv_value } =>
(short_channel_id, amt_to_forward, outgoing_cltv_value, None),
(short_channel_id, amt_to_forward, outgoing_cltv_value, None, None),
msgs::InboundOnionPayload::BlindedForward {
short_channel_id, payment_relay, payment_constraints, intro_node_blinding_point, features,
next_blinding_override,
} => {
let (amt_to_forward, outgoing_cltv_value) = check_blinded_forward(
msg.amount_msat, msg.cltv_expiry, &payment_relay, &payment_constraints, &features
@ -93,7 +95,8 @@ pub(super) fn create_fwd_pending_htlc_info(
err_data: vec![0; 32],
}
})?;
(short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point)
(short_channel_id, amt_to_forward, outgoing_cltv_value, intro_node_blinding_point,
next_blinding_override)
},
msgs::InboundOnionPayload::Receive { .. } | msgs::InboundOnionPayload::BlindedReceive { .. } =>
return Err(InboundHTLCErr {
@ -110,6 +113,7 @@ pub(super) fn create_fwd_pending_htlc_info(
blinded: intro_node_blinding_point.or(msg.blinding_point)
.map(|bp| BlindedForward {
inbound_blinding_point: bp,
next_blinding_override,
failure: intro_node_blinding_point
.map(|_| BlindedFailure::FromIntroductionNode)
.unwrap_or(BlindedFailure::FromBlindedNode),

View file

@ -151,6 +151,7 @@ impl<G: Deref<Target = NetworkGraph<L>>, L: Deref, ES: Deref, S: Deref, SP: Size
short_channel_id,
payment_relay,
payment_constraints,
next_blinding_override: None,
features: BlindedHopFeatures::empty(),
},
node_id: details.counterparty.node_id,