Skip to content

Commit 5d24087

Browse files
Introduce Dummy Hop support in Blinded Path Constructor
Adds a new constructor for blinded paths that allows specifying the number of dummy hops. This enables users to insert arbitrary hops before the real destination, enhancing privacy by making it harder to infer the sender–receiver distance or identify the final destination. Lays the groundwork for future use of dummy hops in blinded path construction. Co-authored-by: valentinewallace <[email protected]>
1 parent 08e50ff commit 5d24087

File tree

10 files changed

+265
-79
lines changed

10 files changed

+265
-79
lines changed

lightning-background-processor/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1085,7 +1085,7 @@ mod tests {
10851085
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
10861086
use lightning::routing::router::{CandidateRouteHop, DefaultRouter, Path, RouteHop};
10871087
use lightning::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp, ScoreUpdate};
1088-
use lightning::sign::{ChangeDestinationSource, InMemorySigner, KeysManager};
1088+
use lightning::sign::{ChangeDestinationSource, InMemorySigner, KeysManager, NodeSigner};
10891089
use lightning::types::features::{ChannelFeatures, NodeFeatures};
10901090
use lightning::types::payment::PaymentHash;
10911091
use lightning::util::config::UserConfig;
@@ -1556,6 +1556,7 @@ mod tests {
15561556
let msg_router = Arc::new(DefaultMessageRouter::new(
15571557
network_graph.clone(),
15581558
Arc::clone(&keys_manager),
1559+
keys_manager.get_inbound_payment_key(),
15591560
));
15601561
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin));
15611562
let kv_store =

lightning-dns-resolver/src/lib.rs

+16-3
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,14 @@ mod test {
232232
secp_ctx: &Secp256k1<T>,
233233
) -> Result<Vec<BlindedMessagePath>, ()> {
234234
let keys = KeysManager::new(&[0; 32], 42, 43);
235-
Ok(vec![BlindedMessagePath::one_hop(recipient, context, &keys, secp_ctx).unwrap()])
235+
Ok(vec![BlindedMessagePath::one_hop(
236+
recipient,
237+
context,
238+
keys.get_inbound_payment_key(),
239+
&keys,
240+
secp_ctx,
241+
)
242+
.unwrap()])
236243
}
237244
}
238245
impl Deref for DirectlyConnectedRouter {
@@ -334,8 +341,14 @@ mod test {
334341
let (msg, context) =
335342
payer.resolver.resolve_name(payment_id, name.clone(), &*payer_keys).unwrap();
336343
let query_context = MessageContext::DNSResolver(context);
337-
let reply_path =
338-
BlindedMessagePath::one_hop(payer_id, query_context, &*payer_keys, &secp_ctx).unwrap();
344+
let reply_path = BlindedMessagePath::one_hop(
345+
payer_id,
346+
query_context,
347+
payer_keys.get_inbound_payment_key(),
348+
&*payer_keys,
349+
&secp_ctx,
350+
)
351+
.unwrap();
339352
payer.pending_messages.lock().unwrap().push((
340353
DNSResolverMessage::DNSSECQuery(msg),
341354
MessageSendInstructions::WithSpecifiedReplyPath {

lightning-liquidity/tests/common/mod.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
#![allow(unused_macros)]
66

77
use lightning::chain::Filter;
8-
use lightning::sign::EntropySource;
8+
use lightning::sign::{EntropySource, NodeSigner};
99

1010
use bitcoin::blockdata::constants::{genesis_block, ChainHash};
1111
use bitcoin::blockdata::transaction::Transaction;
@@ -419,8 +419,11 @@ pub(crate) fn create_liquidity_node(
419419
scorer.clone(),
420420
Default::default(),
421421
));
422-
let msg_router =
423-
Arc::new(DefaultMessageRouter::new(Arc::clone(&network_graph), Arc::clone(&keys_manager)));
422+
let msg_router = Arc::new(DefaultMessageRouter::new(
423+
Arc::clone(&network_graph),
424+
Arc::clone(&keys_manager),
425+
keys_manager.get_inbound_payment_key(),
426+
));
424427
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin));
425428
let kv_store =
426429
Arc::new(FilesystemStore::new(format!("{}_persister_{}", &persist_dir, i).into()));

lightning/src/blinded_path/message.rs

+51-9
Original file line numberDiff line numberDiff line change
@@ -56,23 +56,51 @@ impl Readable for BlindedMessagePath {
5656
impl BlindedMessagePath {
5757
/// Create a one-hop blinded path for a message.
5858
pub fn one_hop<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
59-
recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES,
60-
secp_ctx: &Secp256k1<T>,
59+
recipient_node_id: PublicKey, context: MessageContext,
60+
expanded_key: inbound_payment::ExpandedKey, entropy_source: ES, secp_ctx: &Secp256k1<T>,
6161
) -> Result<Self, ()>
6262
where
6363
ES::Target: EntropySource,
6464
{
65-
Self::new(&[], recipient_node_id, context, entropy_source, secp_ctx)
65+
Self::new(&[], recipient_node_id, context, entropy_source, expanded_key, secp_ctx)
6666
}
6767

6868
/// Create a path for an onion message, to be forwarded along `node_pks`. The last node
6969
/// pubkey in `node_pks` will be the destination node.
7070
///
7171
/// Errors if no hops are provided or if `node_pk`(s) are invalid.
72-
// TODO: make all payloads the same size with padding + add dummy hops
7372
pub fn new<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
7473
intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
75-
context: MessageContext, entropy_source: ES, secp_ctx: &Secp256k1<T>,
74+
context: MessageContext, entropy_source: ES, expanded_key: inbound_payment::ExpandedKey,
75+
secp_ctx: &Secp256k1<T>,
76+
) -> Result<Self, ()>
77+
where
78+
ES::Target: EntropySource,
79+
{
80+
BlindedMessagePath::new_with_dummy_hops(
81+
intermediate_nodes,
82+
0,
83+
recipient_node_id,
84+
context,
85+
entropy_source,
86+
expanded_key,
87+
secp_ctx,
88+
)
89+
}
90+
91+
/// Create a path for an onion message, to be forwarded along `node_pks`.
92+
///
93+
/// Additionally allows appending a number of dummy hops before the final hop,
94+
/// increasing the total path length and enhancing privacy by obscuring the true
95+
/// distance between sender and recipient.
96+
///
97+
/// The last node pubkey in `node_pks` will be the destination node.
98+
///
99+
/// Errors if no hops are provided or if `node_pk`(s) are invalid.
100+
pub fn new_with_dummy_hops<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
101+
intermediate_nodes: &[MessageForwardNode], dummy_hops_count: u8,
102+
recipient_node_id: PublicKey, context: MessageContext, entropy_source: ES,
103+
expanded_key: inbound_payment::ExpandedKey, secp_ctx: &Secp256k1<T>,
76104
) -> Result<Self, ()>
77105
where
78106
ES::Target: EntropySource,
@@ -89,9 +117,12 @@ impl BlindedMessagePath {
89117
blinding_point: PublicKey::from_secret_key(secp_ctx, &blinding_secret),
90118
blinded_hops: blinded_hops(
91119
secp_ctx,
120+
entropy_source,
121+
expanded_key,
92122
intermediate_nodes,
93123
recipient_node_id,
94124
context,
125+
dummy_hops_count,
95126
&blinding_secret,
96127
)
97128
.map_err(|_| ())?,
@@ -545,13 +576,18 @@ impl_writeable_tlv_based!(DNSResolverContext, {
545576
pub(crate) const MESSAGE_PADDING_ROUND_OFF: usize = 100;
546577

547578
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
548-
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
549-
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[MessageForwardNode],
550-
recipient_node_id: PublicKey, context: MessageContext, session_priv: &SecretKey,
551-
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
579+
pub(super) fn blinded_hops<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
580+
secp_ctx: &Secp256k1<T>, entropy_source: ES, expanded_key: inbound_payment::ExpandedKey,
581+
intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
582+
context: MessageContext, dummy_hops_count: u8, session_priv: &SecretKey,
583+
) -> Result<Vec<BlindedHop>, secp256k1::Error>
584+
where
585+
ES::Target: EntropySource,
586+
{
552587
let pks = intermediate_nodes
553588
.iter()
554589
.map(|node| node.node_id)
590+
.chain((0..dummy_hops_count).map(|_| recipient_node_id))
555591
.chain(core::iter::once(recipient_node_id));
556592
let is_compact = intermediate_nodes.iter().any(|node| node.short_channel_id.is_some());
557593

@@ -566,6 +602,12 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
566602
.map(|next_hop| {
567603
ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None })
568604
})
605+
.chain((0..dummy_hops_count).map(|_| {
606+
let dummy_tlvs = UnauthenticatedDummyTlvs {};
607+
let nonce = Nonce::from_entropy_source(&*entropy_source);
608+
let hmac = dummy_tlvs.hmac_data(nonce, &expanded_key);
609+
ControlTlvs::Dummy(DummyTlvs { dummy_tlvs, authentication: (hmac, nonce) })
610+
}))
569611
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: Some(context) })));
570612

571613
if is_compact {

lightning/src/ln/functional_test_utils.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::ln::peer_handler::IgnoringMessageHandler;
2626
use crate::onion_message::messenger::OnionMessenger;
2727
use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
2828
use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
29-
use crate::sign::{EntropySource, RandomBytes};
29+
use crate::sign::{EntropySource, NodeSigner, RandomBytes};
3030
use crate::util::config::{MaxDustHTLCExposure, UserConfig};
3131
use crate::util::logger::Logger;
3232
use crate::util::scid_utils;
@@ -722,7 +722,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
722722
signer_provider: self.keys_manager,
723723
fee_estimator: &test_utils::TestFeeEstimator::new(253),
724724
router: &test_utils::TestRouter::new(Arc::clone(&network_graph), &self.logger, &scorer),
725-
message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager),
725+
message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager, self.keys_manager.get_inbound_payment_key()),
726726
chain_monitor: self.chain_monitor,
727727
tx_broadcaster: &broadcaster,
728728
logger: &self.logger,
@@ -3310,7 +3310,7 @@ pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a
33103310
tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster,
33113311
fee_estimator: &chanmon_cfgs[i].fee_estimator,
33123312
router: test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[i].logger, &chanmon_cfgs[i].scorer),
3313-
message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager),
3313+
message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager, chanmon_cfgs[i].keys_manager.get_inbound_payment_key()),
33143314
chain_monitor,
33153315
keys_manager: &chanmon_cfgs[i].keys_manager,
33163316
node_seed: seed,

lightning/src/ln/functional_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use crate::chain::chaininterface::LowerBoundedFeeEstimator;
1717
use crate::chain::channelmonitor;
1818
use crate::chain::channelmonitor::{Balance, ChannelMonitorUpdateStep, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY, COUNTERPARTY_CLAIMABLE_WITHIN_BLOCKS_PINNABLE};
1919
use crate::chain::transaction::OutPoint;
20-
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider};
20+
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, NodeSigner, OutputSpender, SignerProvider};
2121
use crate::events::bump_transaction::WalletSource;
2222
use crate::events::{Event, FundingInfo, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
2323
use crate::ln::types::ChannelId;
@@ -5987,7 +5987,7 @@ pub fn test_key_derivation_params() {
59875987
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[0].logger));
59885988
let scorer = RwLock::new(test_utils::TestScorer::new());
59895989
let router = test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[0].logger, &scorer);
5990-
let message_router = test_utils::TestMessageRouter::new(network_graph.clone(), &keys_manager);
5990+
let message_router = test_utils::TestMessageRouter::new(network_graph.clone(), &keys_manager, keys_manager.get_inbound_payment_key());
59915991
let node = NodeCfg { chain_source: &chanmon_cfgs[0].chain_source, logger: &chanmon_cfgs[0].logger, tx_broadcaster: &chanmon_cfgs[0].tx_broadcaster, fee_estimator: &chanmon_cfgs[0].fee_estimator, router, message_router, chain_monitor, keys_manager: &keys_manager, network_graph, node_seed: seed, override_init_features: alloc::rc::Rc::new(core::cell::RefCell::new(None)) };
59925992
let mut node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
59935993
node_cfgs.remove(0);

0 commit comments

Comments
 (0)