Skip to content

Commit 124b762

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 3548d56 commit 124b762

File tree

10 files changed

+269
-79
lines changed

10 files changed

+269
-79
lines changed

lightning-background-processor/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1174,7 +1174,7 @@ mod tests {
11741174
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
11751175
use lightning::routing::router::{CandidateRouteHop, DefaultRouter, Path, RouteHop};
11761176
use lightning::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp, ScoreUpdate};
1177-
use lightning::sign::{ChangeDestinationSourceSync, InMemorySigner, KeysManager};
1177+
use lightning::sign::{ChangeDestinationSourceSync, InMemorySigner, KeysManager, NodeSigner};
11781178
use lightning::types::features::{ChannelFeatures, NodeFeatures};
11791179
use lightning::types::payment::PaymentHash;
11801180
use lightning::util::config::UserConfig;
@@ -1650,6 +1650,7 @@ mod tests {
16501650
let msg_router = Arc::new(DefaultMessageRouter::new(
16511651
network_graph.clone(),
16521652
Arc::clone(&keys_manager),
1653+
keys_manager.get_expanded_key(),
16531654
));
16541655
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin));
16551656
let kv_store =

lightning-dns-resolver/src/lib.rs

Lines changed: 16 additions & 3 deletions
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_expanded_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_expanded_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

Lines changed: 6 additions & 3 deletions
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;
@@ -418,8 +418,11 @@ pub(crate) fn create_liquidity_node(
418418
scorer.clone(),
419419
Default::default(),
420420
));
421-
let msg_router =
422-
Arc::new(DefaultMessageRouter::new(Arc::clone(&network_graph), Arc::clone(&keys_manager)));
421+
let msg_router = Arc::new(DefaultMessageRouter::new(
422+
Arc::clone(&network_graph),
423+
Arc::clone(&keys_manager),
424+
keys_manager.get_expanded_key(),
425+
));
423426
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin));
424427
let kv_store =
425428
Arc::new(FilesystemStore::new(format!("{}_persister_{}", &persist_dir, i).into()));

lightning/src/blinded_path/message.rs

Lines changed: 51 additions & 9 deletions
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(|_| ())?,
@@ -563,13 +594,18 @@ impl_writeable_tlv_based!(DNSResolverContext, {
563594
pub(crate) const MESSAGE_PADDING_ROUND_OFF: usize = 100;
564595

565596
/// Construct blinded onion message hops for the given `intermediate_nodes` and `recipient_node_id`.
566-
pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
567-
secp_ctx: &Secp256k1<T>, intermediate_nodes: &[MessageForwardNode],
568-
recipient_node_id: PublicKey, context: MessageContext, session_priv: &SecretKey,
569-
) -> Result<Vec<BlindedHop>, secp256k1::Error> {
597+
pub(super) fn blinded_hops<ES: Deref, T: secp256k1::Signing + secp256k1::Verification>(
598+
secp_ctx: &Secp256k1<T>, entropy_source: ES, expanded_key: inbound_payment::ExpandedKey,
599+
intermediate_nodes: &[MessageForwardNode], recipient_node_id: PublicKey,
600+
context: MessageContext, dummy_hops_count: u8, session_priv: &SecretKey,
601+
) -> Result<Vec<BlindedHop>, secp256k1::Error>
602+
where
603+
ES::Target: EntropySource,
604+
{
570605
let pks = intermediate_nodes
571606
.iter()
572607
.map(|node| node.node_id)
608+
.chain((0..dummy_hops_count).map(|_| recipient_node_id))
573609
.chain(core::iter::once(recipient_node_id));
574610
let is_compact = intermediate_nodes.iter().any(|node| node.short_channel_id.is_some());
575611

@@ -584,6 +620,12 @@ pub(super) fn blinded_hops<T: secp256k1::Signing + secp256k1::Verification>(
584620
.map(|next_hop| {
585621
ControlTlvs::Forward(ForwardTlvs { next_hop, next_blinding_override: None })
586622
})
623+
.chain((0..dummy_hops_count).map(|_| {
624+
let dummy_tlv = UnauthenticatedDummyTlv {};
625+
let nonce = Nonce::from_entropy_source(&*entropy_source);
626+
let hmac = dummy_tlv.hmac_data(nonce, &expanded_key);
627+
ControlTlvs::Dummy(DummyTlv { dummy_tlv, authentication: (hmac, nonce) })
628+
}))
587629
.chain(core::iter::once(ControlTlvs::Receive(ReceiveTlvs { context: Some(context) })));
588630

589631
if is_compact {

lightning/src/ln/functional_test_utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ use crate::onion_message::messenger::OnionMessenger;
2929
use crate::ln::onion_utils::LocalHTLCFailureReason;
3030
use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
3131
use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
32-
use crate::sign::{EntropySource, RandomBytes};
32+
use crate::sign::{EntropySource, NodeSigner, RandomBytes};
3333
use crate::util::config::{MaxDustHTLCExposure, UserConfig};
3434
use crate::util::logger::Logger;
3535
use crate::util::scid_utils;
@@ -725,7 +725,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
725725
signer_provider: self.keys_manager,
726726
fee_estimator: &test_utils::TestFeeEstimator::new(253),
727727
router: &test_utils::TestRouter::new(Arc::clone(&network_graph), &self.logger, &scorer),
728-
message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager),
728+
message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager, self.keys_manager.get_expanded_key()),
729729
chain_monitor: self.chain_monitor,
730730
tx_broadcaster: &broadcaster,
731731
logger: &self.logger,
@@ -3353,7 +3353,7 @@ pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a
33533353
tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster,
33543354
fee_estimator: &chanmon_cfgs[i].fee_estimator,
33553355
router: test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[i].logger, &chanmon_cfgs[i].scorer),
3356-
message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager),
3356+
message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager, chanmon_cfgs[i].keys_manager.get_expanded_key()),
33573357
chain_monitor,
33583358
keys_manager: &chanmon_cfgs[i].keys_manager,
33593359
node_seed: seed,

lightning/src/ln/functional_tests.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ use crate::routing::gossip::{NetworkGraph, NetworkUpdate};
5050
use crate::routing::router::{
5151
get_route, Path, PaymentParameters, Route, RouteHop, RouteParameters,
5252
};
53-
use crate::sign::{EntropySource, OutputSpender, SignerProvider};
53+
use crate::sign::{EntropySource, NodeSigner, OutputSpender, SignerProvider};
5454
use crate::types::features::{ChannelFeatures, ChannelTypeFeatures, NodeFeatures};
5555
use crate::types::payment::{PaymentHash, PaymentSecret};
5656
use crate::util::config::{
@@ -5112,7 +5112,11 @@ pub fn test_key_derivation_params() {
51125112
let scorer = RwLock::new(test_utils::TestScorer::new());
51135113
let router =
51145114
test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[0].logger, &scorer);
5115-
let message_router = test_utils::TestMessageRouter::new(network_graph.clone(), &keys_manager);
5115+
let message_router = test_utils::TestMessageRouter::new(
5116+
network_graph.clone(),
5117+
&keys_manager,
5118+
keys_manager.get_expanded_key(),
5119+
);
51165120
let node = NodeCfg {
51175121
chain_source: &chanmon_cfgs[0].chain_source,
51185122
logger: &chanmon_cfgs[0].logger,

0 commit comments

Comments
 (0)