Skip to content

Commit 83b55d7

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 92d2c75 commit 83b55d7

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
@@ -1127,7 +1127,7 @@ mod tests {
11271127
use lightning::routing::gossip::{NetworkGraph, P2PGossipSync};
11281128
use lightning::routing::router::{CandidateRouteHop, DefaultRouter, Path, RouteHop};
11291129
use lightning::routing::scoring::{ChannelUsage, LockableScore, ScoreLookUp, ScoreUpdate};
1130-
use lightning::sign::{ChangeDestinationSource, InMemorySigner, KeysManager};
1130+
use lightning::sign::{ChangeDestinationSource, InMemorySigner, KeysManager, NodeSigner};
11311131
use lightning::types::features::{ChannelFeatures, NodeFeatures};
11321132
use lightning::types::payment::PaymentHash;
11331133
use lightning::util::config::UserConfig;
@@ -1603,6 +1603,7 @@ mod tests {
16031603
let msg_router = Arc::new(DefaultMessageRouter::new(
16041604
network_graph.clone(),
16051605
Arc::clone(&keys_manager),
1606+
keys_manager.get_inbound_payment_key(),
16061607
));
16071608
let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Bitcoin));
16081609
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;
@@ -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_inbound_payment_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

+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
@@ -27,7 +27,7 @@ use crate::onion_message::messenger::OnionMessenger;
2727
use crate::ln::onion_utils::LocalHTLCFailureReason;
2828
use crate::routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate};
2929
use crate::routing::router::{self, PaymentParameters, Route, RouteParameters};
30-
use crate::sign::{EntropySource, RandomBytes};
30+
use crate::sign::{EntropySource, NodeSigner, RandomBytes};
3131
use crate::util::config::{MaxDustHTLCExposure, UserConfig};
3232
use crate::util::logger::Logger;
3333
use crate::util::scid_utils;
@@ -723,7 +723,7 @@ impl<'a, 'b, 'c> Drop for Node<'a, 'b, 'c> {
723723
signer_provider: self.keys_manager,
724724
fee_estimator: &test_utils::TestFeeEstimator::new(253),
725725
router: &test_utils::TestRouter::new(Arc::clone(&network_graph), &self.logger, &scorer),
726-
message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager),
726+
message_router: &test_utils::TestMessageRouter::new(network_graph, self.keys_manager, self.keys_manager.get_inbound_payment_key()),
727727
chain_monitor: self.chain_monitor,
728728
tx_broadcaster: &broadcaster,
729729
logger: &self.logger,
@@ -3347,7 +3347,7 @@ pub fn create_node_cfgs_with_persisters<'a>(node_count: usize, chanmon_cfgs: &'a
33473347
tx_broadcaster: &chanmon_cfgs[i].tx_broadcaster,
33483348
fee_estimator: &chanmon_cfgs[i].fee_estimator,
33493349
router: test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[i].logger, &chanmon_cfgs[i].scorer),
3350-
message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager),
3350+
message_router: test_utils::TestMessageRouter::new(network_graph.clone(), &chanmon_cfgs[i].keys_manager, chanmon_cfgs[i].keys_manager.get_inbound_payment_key()),
33513351
chain_monitor,
33523352
keys_manager: &chanmon_cfgs[i].keys_manager,
33533353
node_seed: seed,

lightning/src/ln/functional_tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ 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;
2020
use crate::ln::onion_utils::LocalHTLCFailureReason;
21-
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider};
21+
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, NodeSigner, OutputSpender, SignerProvider};
2222
use crate::events::bump_transaction::WalletSource;
2323
use crate::events::{Event, FundingInfo, PathFailure, PaymentPurpose, ClosureReason, HTLCHandlingFailureType, PaymentFailureReason};
2424
use crate::ln::types::ChannelId;
@@ -5992,7 +5992,7 @@ pub fn test_key_derivation_params() {
59925992
let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &chanmon_cfgs[0].logger));
59935993
let scorer = RwLock::new(test_utils::TestScorer::new());
59945994
let router = test_utils::TestRouter::new(network_graph.clone(), &chanmon_cfgs[0].logger, &scorer);
5995-
let message_router = test_utils::TestMessageRouter::new(network_graph.clone(), &keys_manager);
5995+
let message_router = test_utils::TestMessageRouter::new(network_graph.clone(), &keys_manager, keys_manager.get_inbound_payment_key());
59965996
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)) };
59975997
let mut node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
59985998
node_cfgs.remove(0);

0 commit comments

Comments
 (0)