Skip to content

Commit

Permalink
Merge pull request #2146 from valentinewallace/2023-03-blinded-pathfi…
Browse files Browse the repository at this point in the history
…nding-groundwork

Blinded pathfinding groundwork
  • Loading branch information
TheBlueMatt authored Apr 24, 2023
2 parents bc54441 + b131634 commit 607727f
Show file tree
Hide file tree
Showing 34 changed files with 1,322 additions and 967 deletions.
10 changes: 5 additions & 5 deletions fuzz/src/chanmon_consistency.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ use lightning::util::errors::APIError;
use lightning::util::logger::Logger;
use lightning::util::config::UserConfig;
use lightning::util::ser::{Readable, ReadableArgs, Writeable, Writer};
use lightning::routing::router::{InFlightHtlcs, Route, RouteHop, RouteParameters, Router};
use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RouteParameters, Router};

use crate::utils::test_logger::{self, Output};
use crate::utils::test_persister::TestPersister;
Expand Down Expand Up @@ -353,14 +353,14 @@ fn send_payment(source: &ChanMan, dest: &ChanMan, dest_chan_id: u64, amt: u64, p
payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes());
*payment_idx += 1;
if let Err(err) = source.send_payment_with_route(&Route {
paths: vec![vec![RouteHop {
paths: vec![Path { hops: vec![RouteHop {
pubkey: dest.get_our_node_id(),
node_features: dest.node_features(),
short_channel_id: dest_chan_id,
channel_features: dest.channel_features(),
fee_msat: amt,
cltv_expiry_delta: 200,
}]],
}], blinded_tail: None }],
payment_params: None,
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
check_payment_err(err);
Expand All @@ -375,7 +375,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
payment_id[0..8].copy_from_slice(&payment_idx.to_ne_bytes());
*payment_idx += 1;
if let Err(err) = source.send_payment_with_route(&Route {
paths: vec![vec![RouteHop {
paths: vec![Path { hops: vec![RouteHop {
pubkey: middle.get_our_node_id(),
node_features: middle.node_features(),
short_channel_id: middle_chan_id,
Expand All @@ -389,7 +389,7 @@ fn send_hop_payment(source: &ChanMan, middle: &ChanMan, middle_chan_id: u64, des
channel_features: dest.channel_features(),
fee_msat: amt,
cltv_expiry_delta: 200,
}]],
}], blinded_tail: None }],
payment_params: None,
}, payment_hash, RecipientOnionFields::secret_only(payment_secret), PaymentId(payment_id)) {
check_payment_err(err);
Expand Down
6 changes: 3 additions & 3 deletions fuzz/src/invoice_request_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
use bitcoin::secp256k1::{KeyPair, Parity, PublicKey, Secp256k1, SecretKey, self};
use crate::utils::test_logger;
use core::convert::{Infallible, TryFrom};
use lightning::blinded_path::BlindedPath;
use lightning::chain::keysinterface::EntropySource;
use lightning::ln::PaymentHash;
use lightning::ln::features::BlindedHopFeatures;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedInvoice};
use lightning::offers::invoice_request::InvoiceRequest;
use lightning::offers::parse::SemanticError;
use lightning::onion_message::BlindedPath;
use lightning::util::ser::Writeable;

#[inline]
Expand Down Expand Up @@ -74,8 +74,8 @@ fn build_response<'a, T: secp256k1::Signing + secp256k1::Verification>(
) -> Result<UnsignedInvoice<'a>, SemanticError> {
let entropy_source = Randomness {};
let paths = vec![
BlindedPath::new(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
BlindedPath::new(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
BlindedPath::new_for_message(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
];

let payinfo = vec![
Expand Down
6 changes: 3 additions & 3 deletions fuzz/src/refund_deser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
use bitcoin::secp256k1::{KeyPair, PublicKey, Secp256k1, SecretKey, self};
use crate::utils::test_logger;
use core::convert::{Infallible, TryFrom};
use lightning::blinded_path::BlindedPath;
use lightning::chain::keysinterface::EntropySource;
use lightning::ln::PaymentHash;
use lightning::ln::features::BlindedHopFeatures;
use lightning::offers::invoice::{BlindedPayInfo, UnsignedInvoice};
use lightning::offers::parse::SemanticError;
use lightning::offers::refund::Refund;
use lightning::onion_message::BlindedPath;
use lightning::util::ser::Writeable;

#[inline]
Expand Down Expand Up @@ -63,8 +63,8 @@ fn build_response<'a, T: secp256k1::Signing + secp256k1::Verification>(
) -> Result<UnsignedInvoice<'a>, SemanticError> {
let entropy_source = Randomness {};
let paths = vec![
BlindedPath::new(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
BlindedPath::new(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
BlindedPath::new_for_message(&[pubkey(43), pubkey(44), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
BlindedPath::new_for_message(&[pubkey(45), pubkey(46), pubkey(42)], &entropy_source, secp_ctx).unwrap(),
];

let payinfo = vec![
Expand Down
45 changes: 20 additions & 25 deletions lightning-background-processor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,26 +241,21 @@ fn update_scorer<'a, S: 'static + Deref<Target = SC> + Send + Sync, SC: 'a + Wri
let mut score = scorer.lock();
match event {
Event::PaymentPathFailed { ref path, short_channel_id: Some(scid), .. } => {
let path = path.iter().collect::<Vec<_>>();
score.payment_path_failed(&path, *scid);
score.payment_path_failed(path, *scid);
},
Event::PaymentPathFailed { ref path, payment_failed_permanently: true, .. } => {
// Reached if the destination explicitly failed it back. We treat this as a successful probe
// because the payment made it all the way to the destination with sufficient liquidity.
let path = path.iter().collect::<Vec<_>>();
score.probe_successful(&path);
score.probe_successful(path);
},
Event::PaymentPathSuccessful { path, .. } => {
let path = path.iter().collect::<Vec<_>>();
score.payment_path_successful(&path);
score.payment_path_successful(path);
},
Event::ProbeSuccessful { path, .. } => {
let path = path.iter().collect::<Vec<_>>();
score.probe_successful(&path);
score.probe_successful(path);
},
Event::ProbeFailed { path, short_channel_id: Some(scid), .. } => {
let path = path.iter().collect::<Vec<_>>();
score.probe_failed(&path, *scid);
score.probe_failed(path, *scid);
},
_ => {},
}
Expand Down Expand Up @@ -767,7 +762,7 @@ mod tests {
use lightning::ln::msgs::{ChannelMessageHandler, Init};
use lightning::ln::peer_handler::{PeerManager, MessageHandler, SocketDescriptor, IgnoringMessageHandler};
use lightning::routing::gossip::{NetworkGraph, NodeId, P2PGossipSync};
use lightning::routing::router::{DefaultRouter, RouteHop};
use lightning::routing::router::{DefaultRouter, Path, RouteHop};
use lightning::routing::scoring::{ChannelUsage, Score};
use lightning::util::config::UserConfig;
use lightning::util::ser::Writeable;
Expand Down Expand Up @@ -910,10 +905,10 @@ mod tests {

#[derive(Debug)]
enum TestResult {
PaymentFailure { path: Vec<RouteHop>, short_channel_id: u64 },
PaymentSuccess { path: Vec<RouteHop> },
ProbeFailure { path: Vec<RouteHop> },
ProbeSuccess { path: Vec<RouteHop> },
PaymentFailure { path: Path, short_channel_id: u64 },
PaymentSuccess { path: Path },
ProbeFailure { path: Path },
ProbeSuccess { path: Path },
}

impl TestScorer {
Expand All @@ -935,11 +930,11 @@ mod tests {
&self, _short_channel_id: u64, _source: &NodeId, _target: &NodeId, _usage: ChannelUsage
) -> u64 { unimplemented!(); }

fn payment_path_failed(&mut self, actual_path: &[&RouteHop], actual_short_channel_id: u64) {
fn payment_path_failed(&mut self, actual_path: &Path, actual_short_channel_id: u64) {
if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, short_channel_id } => {
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
assert_eq!(actual_path, &path);
assert_eq!(actual_short_channel_id, short_channel_id);
},
TestResult::PaymentSuccess { path } => {
Expand All @@ -955,14 +950,14 @@ mod tests {
}
}

fn payment_path_successful(&mut self, actual_path: &[&RouteHop]) {
fn payment_path_successful(&mut self, actual_path: &Path) {
if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, .. } => {
panic!("Unexpected payment path failure: {:?}", path)
},
TestResult::PaymentSuccess { path } => {
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
assert_eq!(actual_path, &path);
},
TestResult::ProbeFailure { path } => {
panic!("Unexpected probe failure: {:?}", path)
Expand All @@ -974,7 +969,7 @@ mod tests {
}
}

fn probe_failed(&mut self, actual_path: &[&RouteHop], _: u64) {
fn probe_failed(&mut self, actual_path: &Path, _: u64) {
if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, .. } => {
Expand All @@ -984,15 +979,15 @@ mod tests {
panic!("Unexpected payment path success: {:?}", path)
},
TestResult::ProbeFailure { path } => {
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
assert_eq!(actual_path, &path);
},
TestResult::ProbeSuccess { path } => {
panic!("Unexpected probe success: {:?}", path)
}
}
}
}
fn probe_successful(&mut self, actual_path: &[&RouteHop]) {
fn probe_successful(&mut self, actual_path: &Path) {
if let Some(expectations) = &mut self.event_expectations {
match expectations.pop_front().unwrap() {
TestResult::PaymentFailure { path, .. } => {
Expand All @@ -1005,7 +1000,7 @@ mod tests {
panic!("Unexpected probe failure: {:?}", path)
},
TestResult::ProbeSuccess { path } => {
assert_eq!(actual_path, &path.iter().collect::<Vec<_>>()[..]);
assert_eq!(actual_path, &path);
}
}
}
Expand Down Expand Up @@ -1533,14 +1528,14 @@ mod tests {
let node_1_privkey = SecretKey::from_slice(&[42; 32]).unwrap();
let node_1_id = PublicKey::from_secret_key(&secp_ctx, &node_1_privkey);

let path = vec![RouteHop {
let path = Path { hops: vec![RouteHop {
pubkey: node_1_id,
node_features: NodeFeatures::empty(),
short_channel_id: scored_scid,
channel_features: ChannelFeatures::empty(),
fee_msat: 0,
cltv_expiry_delta: MIN_CLTV_EXPIRY_DELTA as u32,
}];
}], blinded_tail: None };

$nodes[0].scorer.lock().unwrap().expect(TestResult::PaymentFailure { path: path.clone(), short_channel_id: scored_scid });
$nodes[0].node.push_pending_event(Event::PaymentPathFailed {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@

//! Creating blinded paths and related utilities live here.
pub(crate) mod utils;

use bitcoin::hashes::{Hash, HashEngine};
use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::{self, PublicKey, Scalar, Secp256k1, SecretKey};

use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient};
use super::packet::ControlTlvs;
use super::utils;
use crate::onion_message::ControlTlvs;
use crate::ln::msgs::DecodeError;
use crate::ln::onion_utils;
use crate::util::chacha20poly1305rfc::{ChaChaPolyReadAdapter, ChaChaPolyWriteAdapter};
Expand All @@ -26,18 +27,18 @@ use core::ops::Deref;
use crate::io::{self, Cursor};
use crate::prelude::*;

/// Onion messages can be sent and received to blinded paths, which serve to hide the identity of
/// the recipient.
#[derive(Clone, Debug, PartialEq)]
/// 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)]
pub struct BlindedPath {
/// To send to a blinded path, the sender first finds a route to the unblinded
/// `introduction_node_id`, which can unblind its [`encrypted_payload`] to find out the onion
/// message's next hop and forward it along.
/// message or payment's next hop and forward it along.
///
/// [`encrypted_payload`]: BlindedHop::encrypted_payload
pub(crate) introduction_node_id: PublicKey,
/// Used by the introduction node to decrypt its [`encrypted_payload`] to forward the onion
/// message.
/// message or payment.
///
/// [`encrypted_payload`]: BlindedHop::encrypted_payload
pub(crate) blinding_point: PublicKey,
Expand All @@ -47,7 +48,7 @@ pub struct BlindedPath {

/// Used to construct the blinded hops portion of a blinded path. These hops cannot be identified
/// by outside observers and thus can be used to hide the identity of the recipient.
#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct BlindedHop {
/// The blinded node id of this hop in a blinded path.
pub(crate) blinded_node_id: PublicKey,
Expand All @@ -58,12 +59,12 @@ pub struct BlindedHop {
}

impl BlindedPath {
/// Create a blinded path to be forwarded along `node_pks`. The last node pubkey in `node_pks`
/// will be the destination node.
/// Create a blinded path for an onion message, to be forwarded along `node_pks`. The last node
/// pubkey in `node_pks` will be the destination node.
///
/// Errors if less than two hops are provided or if `node_pk`(s) are invalid.
// TODO: make all payloads the same size with padding + add dummy hops
pub fn new<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>
pub fn new_for_message<ES: EntropySource, T: secp256k1::Signing + secp256k1::Verification>
(node_pks: &[PublicKey], entropy_source: &ES, secp_ctx: &Secp256k1<T>) -> Result<Self, ()>
{
if node_pks.len() < 2 { return Err(()) }
Expand All @@ -74,12 +75,13 @@ impl BlindedPath {
Ok(BlindedPath {
introduction_node_id,
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
blinded_hops: blinded_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?,
blinded_hops: blinded_message_hops(secp_ctx, node_pks, &blinding_secret).map_err(|_| ())?,
})
}

// Advance the blinded path by one hop, so make the second hop into the new introduction node.
pub(super) fn advance_by_one<NS: Deref, T: secp256k1::Signing + secp256k1::Verification>
// Advance the blinded onion message path by one hop, so make the second hop into the new
// introduction node.
pub(super) fn advance_message_path_by_one<NS: Deref, T: secp256k1::Signing + secp256k1::Verification>
(&mut self, node_signer: &NS, secp_ctx: &Secp256k1<T>) -> Result<(), ()>
where NS::Target: NodeSigner
{
Expand Down Expand Up @@ -114,8 +116,8 @@ impl BlindedPath {
}
}

/// Construct blinded hops for the given `unblinded_path`.
fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
/// Construct blinded onion message hops for the given `unblinded_path`.
fn blinded_message_hops<T: secp256k1::Signing + secp256k1::Verification>(
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], session_priv: &SecretKey
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
let mut blinded_hops = Vec::with_capacity(unblinded_path.len());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@ use bitcoin::hashes::sha256::Hash as Sha256;
use bitcoin::secp256k1::{self, PublicKey, Secp256k1, SecretKey, Scalar};
use bitcoin::secp256k1::ecdh::SharedSecret;

use super::BlindedPath;
use crate::ln::onion_utils;
use super::blinded_path::BlindedPath;
use super::messenger::Destination;
use crate::onion_message::Destination;

use crate::prelude::*;

// TODO: DRY with onion_utils::construct_onion_keys_callback
#[inline]
pub(super) fn construct_keys_callback<T: secp256k1::Signing + secp256k1::Verification,
pub(crate) fn construct_keys_callback<T: secp256k1::Signing + secp256k1::Verification,
FType: FnMut(PublicKey, SharedSecret, PublicKey, [u8; 32], Option<PublicKey>, Option<Vec<u8>>)>(
secp_ctx: &Secp256k1<T>, unblinded_path: &[PublicKey], destination: Option<Destination>,
session_priv: &SecretKey, mut callback: FType
Expand Down
Loading

0 comments on commit 607727f

Please sign in to comment.