Skip to content

Commit

Permalink
Merge pull request #3245 from valentinewallace/2024-07-blinded-pay-pa…
Browse files Browse the repository at this point in the history
…th-refactor

Move `BlindedPayInfo` into `BlindedPaymentPath`
  • Loading branch information
TheBlueMatt authored Aug 19, 2024
2 parents fb4403f + 4ef83a0 commit 61153f1
Show file tree
Hide file tree
Showing 20 changed files with 330 additions and 309 deletions.
4 changes: 2 additions & 2 deletions fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ use lightning::ln::msgs::{
};
use lightning::ln::script::ShutdownScript;
use lightning::ln::types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice::UnsignedBolt12Invoice;
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RouteParameters, Router};
Expand Down Expand Up @@ -126,7 +126,7 @@ impl Router for FuzzRouter {
fn create_blinded_payment_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
_amount_msats: u64, _secp_ctx: &Secp256k1<T>,
) -> Result<Vec<(BlindedPayInfo, BlindedPaymentPath)>, ()> {
) -> Result<Vec<BlindedPaymentPath>, ()> {
unreachable!()
}
}
Expand Down
4 changes: 2 additions & 2 deletions fuzz/src/full_stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ use lightning::ln::peer_handler::{
};
use lightning::ln::script::ShutdownScript;
use lightning::ln::types::{ChannelId, PaymentHash, PaymentPreimage, PaymentSecret};
use lightning::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use lightning::offers::invoice::UnsignedBolt12Invoice;
use lightning::offers::invoice_request::UnsignedInvoiceRequest;
use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
Expand Down Expand Up @@ -163,7 +163,7 @@ impl Router for FuzzRouter {
fn create_blinded_payment_paths<T: secp256k1::Signing + secp256k1::Verification>(
&self, _recipient: PublicKey, _first_hops: Vec<ChannelDetails>, _tlvs: ReceiveTlvs,
_amount_msats: u64, _secp_ctx: &Secp256k1<T>,
) -> Result<Vec<(BlindedPayInfo, BlindedPaymentPath)>, ()> {
) -> Result<Vec<BlindedPaymentPath>, ()> {
unreachable!()
}
}
Expand Down
11 changes: 6 additions & 5 deletions fuzz/src/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,14 @@ use bitcoin::constants::ChainHash;
use bitcoin::script::Builder;
use bitcoin::transaction::TxOut;

use lightning::blinded_path::payment::BlindedPaymentPath;
use lightning::blinded_path::payment::{BlindedPayInfo, BlindedPaymentPath};
use lightning::blinded_path::BlindedHop;
use lightning::chain::transaction::OutPoint;
use lightning::ln::channel_state::{ChannelCounterparty, ChannelDetails, ChannelShutdownState};
use lightning::ln::channelmanager;
use lightning::ln::features::{BlindedHopFeatures, Bolt12InvoiceFeatures};
use lightning::ln::msgs;
use lightning::ln::types::ChannelId;
use lightning::offers::invoice::BlindedPayInfo;
use lightning::routing::gossip::{NetworkGraph, RoutingFees};
use lightning::routing::router::{
find_route, PaymentParameters, RouteHint, RouteHintHop, RouteParameters,
Expand Down Expand Up @@ -381,7 +380,7 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
let mut last_hops_unblinded = Vec::new();
last_hops!(last_hops_unblinded);
let dummy_pk = PublicKey::from_slice(&[2; 33]).unwrap();
let last_hops: Vec<(BlindedPayInfo, BlindedPaymentPath)> = last_hops_unblinded
let last_hops: Vec<BlindedPaymentPath> = last_hops_unblinded
.into_iter()
.map(|hint| {
let hop = &hint.0[0];
Expand All @@ -401,9 +400,11 @@ pub fn do_test<Out: test_logger::Output>(data: &[u8], out: Out) {
encrypted_payload: Vec::new(),
});
}
(
BlindedPaymentPath::from_raw(
hop.src_node_id,
dummy_pk,
blinded_hops,
payinfo,
BlindedPaymentPath::from_raw(hop.src_node_id, dummy_pk, blinded_hops),
)
})
.collect();
Expand Down
2 changes: 1 addition & 1 deletion lightning/src/blinded_path/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::prelude::*;
/// Onion messages and payments can be sent and received to blinded paths, which serve to hide the
/// identity of the recipient.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct BlindedPath {
pub(crate) struct BlindedPath {
/// To send to a blinded path, the sender first finds a route to the unblinded
/// `introduction_node`, which can unblind its [`encrypted_payload`] to find out the onion
/// message or payment's next hop and forward it along.
Expand Down
122 changes: 83 additions & 39 deletions lightning/src/blinded_path/payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ use crate::ln::channel_state::CounterpartyForwardingInfo;
use crate::ln::features::BlindedHopFeatures;
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils;
use crate::offers::invoice::BlindedPayInfo;
use crate::offers::invoice_request::InvoiceRequestFields;
use crate::offers::offer::OfferId;
use crate::routing::gossip::{NodeId, ReadOnlyNetworkGraph};
Expand All @@ -34,29 +33,59 @@ use core::ops::Deref;
#[allow(unused_imports)]
use crate::prelude::*;

/// A blinded path to be used for sending or receiving a payment, hiding the identity of the
/// recipient.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct BlindedPaymentPath(pub(super) BlindedPath);
/// Information needed to route a payment across a [`BlindedPaymentPath`].
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct BlindedPayInfo {
/// Base fee charged (in millisatoshi) for the entire blinded path.
pub fee_base_msat: u32,

impl Writeable for BlindedPaymentPath {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
self.0.write(w)
}
/// Liquidity fee charged (in millionths of the amount transferred) for the entire blinded path
/// (i.e., 10,000 is 1%).
pub fee_proportional_millionths: u32,

/// Number of blocks subtracted from an incoming HTLC's `cltv_expiry` for the entire blinded
/// path.
pub cltv_expiry_delta: u16,

/// The minimum HTLC value (in millisatoshi) that is acceptable to all channel peers on the
/// blinded path from the introduction node to the recipient, accounting for any fees, i.e., as
/// seen by the recipient.
pub htlc_minimum_msat: u64,

/// The maximum HTLC value (in millisatoshi) that is acceptable to all channel peers on the
/// blinded path from the introduction node to the recipient, accounting for any fees, i.e., as
/// seen by the recipient.
pub htlc_maximum_msat: u64,

/// Features set in `encrypted_data_tlv` for the `encrypted_recipient_data` TLV record in an
/// onion payload.
pub features: BlindedHopFeatures,
}

impl Readable for BlindedPaymentPath {
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
Ok(Self(BlindedPath::read(r)?))
}
impl_writeable!(BlindedPayInfo, {
fee_base_msat,
fee_proportional_millionths,
cltv_expiry_delta,
htlc_minimum_msat,
htlc_maximum_msat,
features
});

/// A blinded path to be used for sending or receiving a payment, hiding the identity of the
/// recipient.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct BlindedPaymentPath {
pub(super) inner_path: BlindedPath,
/// The [`BlindedPayInfo`] used to pay this blinded path.
pub payinfo: BlindedPayInfo,
}

impl BlindedPaymentPath {
/// Create a one-hop blinded path for a payment.
pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs, min_final_cltv_expiry_delta: u16,
entropy_source: ES, secp_ctx: &Secp256k1<T>
) -> Result<(BlindedPayInfo, Self), ()> where ES::Target: EntropySource {
) -> Result<Self, ()> where ES::Target: EntropySource {
// This value is not considered in pathfinding for 1-hop blinded paths, because it's intended to
// be in relation to a specific channel.
let htlc_maximum_msat = u64::max_value();
Expand All @@ -77,7 +106,7 @@ impl BlindedPaymentPath {
intermediate_nodes: &[ForwardNode], payee_node_id: PublicKey, payee_tlvs: ReceiveTlvs,
htlc_maximum_msat: u64, min_final_cltv_expiry_delta: u16, entropy_source: ES,
secp_ctx: &Secp256k1<T>
) -> Result<(BlindedPayInfo, Self), ()> where ES::Target: EntropySource {
) -> Result<Self, ()> where ES::Target: EntropySource {
let introduction_node = IntroductionNode::NodeId(
intermediate_nodes.first().map_or(payee_node_id, |n| n.node_id)
);
Expand All @@ -87,38 +116,41 @@ impl BlindedPaymentPath {
let blinded_payinfo = compute_payinfo(
intermediate_nodes, &payee_tlvs, htlc_maximum_msat, min_final_cltv_expiry_delta
)?;
Ok((blinded_payinfo, Self(BlindedPath {
introduction_node,
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
blinded_hops: blinded_hops(
secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, &blinding_secret
).map_err(|_| ())?,
})))
Ok(Self {
inner_path: BlindedPath {
introduction_node,
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
blinded_hops: blinded_hops(
secp_ctx, intermediate_nodes, payee_node_id, payee_tlvs, &blinding_secret
).map_err(|_| ())?,
},
payinfo: blinded_payinfo
})
}

/// Returns the introduction [`NodeId`] of the blinded path, if it is publicly reachable (i.e.,
/// it is found in the network graph).
pub fn public_introduction_node_id<'a>(
&self, network_graph: &'a ReadOnlyNetworkGraph
) -> Option<&'a NodeId> {
self.0.public_introduction_node_id(network_graph)
self.inner_path.public_introduction_node_id(network_graph)
}

/// The [`IntroductionNode`] of the blinded path.
pub fn introduction_node(&self) -> &IntroductionNode {
&self.0.introduction_node
&self.inner_path.introduction_node
}

/// Used by the [`IntroductionNode`] to decrypt its [`encrypted_payload`] to forward the payment.
///
/// [`encrypted_payload`]: BlindedHop::encrypted_payload
pub fn blinding_point(&self) -> PublicKey {
self.0.blinding_point
self.inner_path.blinding_point
}

/// The [`BlindedHop`]s within the blinded path.
pub fn blinded_hops(&self) -> &[BlindedHop] {
&self.0.blinded_hops
&self.inner_path.blinded_hops
}

/// Advance the blinded onion payment path by one hop, making the second hop into the new
Expand All @@ -133,9 +165,9 @@ impl BlindedPaymentPath {
NL::Target: NodeIdLookUp,
T: secp256k1::Signing + secp256k1::Verification,
{
let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &self.0.blinding_point, None)?;
let control_tlvs_ss = node_signer.ecdh(Recipient::Node, &self.inner_path.blinding_point, None)?;
let rho = onion_utils::gen_rho_from_shared_secret(&control_tlvs_ss.secret_bytes());
let encrypted_control_tlvs = &self.0.blinded_hops.get(0).ok_or(())?.encrypted_payload;
let encrypted_control_tlvs = &self.inner_path.blinded_hops.get(0).ok_or(())?.encrypted_payload;
let mut s = Cursor::new(encrypted_control_tlvs);
let mut reader = FixedLengthReader::new(&mut s, encrypted_control_tlvs.len() as u64);
match ChaChaPolyReadAdapter::read(&mut reader, rho) {
Expand All @@ -147,31 +179,43 @@ impl BlindedPaymentPath {
None => return Err(()),
};
let mut new_blinding_point = onion_utils::next_hop_pubkey(
secp_ctx, self.0.blinding_point, control_tlvs_ss.as_ref()
secp_ctx, self.inner_path.blinding_point, control_tlvs_ss.as_ref()
).map_err(|_| ())?;
mem::swap(&mut self.0.blinding_point, &mut new_blinding_point);
self.0.introduction_node = IntroductionNode::NodeId(next_node_id);
self.0.blinded_hops.remove(0);
mem::swap(&mut self.inner_path.blinding_point, &mut new_blinding_point);
self.inner_path.introduction_node = IntroductionNode::NodeId(next_node_id);
self.inner_path.blinded_hops.remove(0);
Ok(())
},
_ => Err(())
}
}

pub(crate) fn inner_blinded_path(&self) -> &BlindedPath {
&self.inner_path
}

pub(crate) fn from_parts(inner_path: BlindedPath, payinfo: BlindedPayInfo) -> Self {
Self { inner_path, payinfo }
}

#[cfg(any(test, fuzzing))]
pub fn from_raw(
introduction_node_id: PublicKey, blinding_point: PublicKey, blinded_hops: Vec<BlindedHop>
introduction_node_id: PublicKey, blinding_point: PublicKey, blinded_hops: Vec<BlindedHop>,
payinfo: BlindedPayInfo
) -> Self {
Self(BlindedPath {
introduction_node: IntroductionNode::NodeId(introduction_node_id),
blinding_point,
blinded_hops,
})
Self {
inner_path: BlindedPath {
introduction_node: IntroductionNode::NodeId(introduction_node_id),
blinding_point,
blinded_hops,
},
payinfo
}
}

#[cfg(test)]
pub fn clear_blinded_hops(&mut self) {
self.0.blinded_hops.clear()
self.inner_path.blinded_hops.clear()
}
}

Expand Down
13 changes: 7 additions & 6 deletions lightning/src/ln/blinded_payment_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::ln::onion_payment;
use crate::ln::onion_utils;
use crate::ln::onion_utils::INVALID_ONION_BLINDING;
use crate::ln::outbound_payment::{Retry, IDEMPOTENCY_TIMEOUT_TICKS};
use crate::offers::invoice::{BlindedPayInfo, UnsignedBolt12Invoice};
use crate::offers::invoice::UnsignedBolt12Invoice;
use crate::offers::invoice_request::UnsignedInvoiceRequest;
use crate::prelude::*;
use crate::routing::router::{BlindedTail, Path, Payee, PaymentParameters, RouteHop, RouteParameters};
Expand All @@ -39,7 +39,7 @@ fn blinded_payment_path(
payment_secret: PaymentSecret, intro_node_min_htlc: u64, intro_node_max_htlc: u64,
node_ids: Vec<PublicKey>, channel_upds: &[&msgs::UnsignedChannelUpdate],
keys_manager: &test_utils::TestKeysInterface
) -> (BlindedPayInfo, BlindedPaymentPath) {
) -> BlindedPaymentPath {
let mut intermediate_nodes = Vec::new();
let mut intro_node_min_htlc_opt = Some(intro_node_min_htlc);
let mut intro_node_max_htlc_opt = Some(intro_node_max_htlc);
Expand Down Expand Up @@ -839,11 +839,12 @@ fn do_multi_hop_receiver_fail(check: ReceiveCheckFail) {
nodes.iter().skip(1).map(|n| n.node.get_our_node_id()).collect(), &[&high_htlc_minimum_upd],
&chanmon_cfgs[2].keys_manager);
if let Payee::Blinded { route_hints, .. } = high_htlc_min_params.payment_params.payee {
route_hints[0].1.clone()
route_hints[0].clone()
} else { panic!() }
};
if let Payee::Blinded { ref mut route_hints, .. } = route_params.payment_params.payee {
route_hints[0].1 = high_htlc_min_bp;
route_hints[0] = high_htlc_min_bp;
route_hints[0].payinfo.htlc_minimum_msat = amt_msat;
} else { panic!() }
find_route(&nodes[0], &route_params).unwrap()
} else {
Expand Down Expand Up @@ -1121,7 +1122,7 @@ fn min_htlc() {
nodes[2].node.get_our_node_id(), nodes[3].node.get_our_node_id()],
&[&chan_1_2.0.contents, &chan_2_3.0.contents], &chanmon_cfgs[3].keys_manager);
assert_eq!(min_htlc_msat,
route_params.payment_params.payee.blinded_route_hints()[0].0.htlc_minimum_msat);
route_params.payment_params.payee.blinded_route_hints()[0].payinfo.htlc_minimum_msat);

nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params.clone(), Retry::Attempts(0)).unwrap();
check_added_monitors(&nodes[0], 1);
Expand All @@ -1133,7 +1134,7 @@ fn min_htlc() {
nodes[0].node.timer_tick_occurred();
}
if let Payee::Blinded { ref mut route_hints, .. } = route_params.payment_params.payee {
route_hints[0].0.htlc_minimum_msat -= 1;
route_hints[0].payinfo.htlc_minimum_msat -= 1;
} else { panic!() }
route_params.final_value_msat -= 1;
nodes[0].node.send_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), PaymentId(payment_hash.0), route_params, Retry::Attempts(0)).unwrap();
Expand Down
4 changes: 2 additions & 2 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ use crate::ln::msgs::{ChannelMessageHandler, DecodeError, LightningError};
use crate::ln::outbound_payment;
use crate::ln::outbound_payment::{OutboundPayments, PaymentAttempts, PendingOutboundPayment, SendAlongPathArgs, StaleExpiration};
use crate::ln::wire::Encode;
use crate::offers::invoice::{BlindedPayInfo, Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
use crate::offers::invoice::{Bolt12Invoice, DEFAULT_RELATIVE_EXPIRY, DerivedSigningPubkey, ExplicitSigningPubkey, InvoiceBuilder, UnsignedBolt12Invoice};
use crate::offers::invoice_error::InvoiceError;
use crate::offers::invoice_request::{DerivedPayerId, InvoiceRequestBuilder};
use crate::offers::nonce::Nonce;
Expand Down Expand Up @@ -9373,7 +9373,7 @@ where
/// [`Router::create_blinded_payment_paths`].
fn create_blinded_payment_paths(
&self, amount_msats: u64, payment_secret: PaymentSecret, payment_context: PaymentContext
) -> Result<Vec<(BlindedPayInfo, BlindedPaymentPath)>, ()> {
) -> Result<Vec<BlindedPaymentPath>, ()> {
let secp_ctx = &self.secp_ctx;

let first_hops = self.list_usable_channels();
Expand Down
Loading

0 comments on commit 61153f1

Please sign in to comment.