Skip to content

Commit fd7500f

Browse files
committed
Add onion mailbox for async receivers
This introduces an in-memory mailbox to hold onion messages until the receiver comes online. This is required for async payment `held_htlc_available` messages. The mailbox is bounded by a maximum number of peers and a maximum number of messages per peer.
1 parent f3dea63 commit fd7500f

File tree

10 files changed

+272
-44
lines changed

10 files changed

+272
-44
lines changed

bindings/ldk_node.udl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ dictionary Config {
1313
u64 probing_liquidity_limit_multiplier;
1414
AnchorChannelsConfig? anchor_channels_config;
1515
RouteParametersConfig? route_parameters;
16-
boolean async_payment_services_enabled;
1716
};
1817

1918
dictionary AnchorChannelsConfig {
@@ -95,6 +94,7 @@ interface Builder {
9594
void set_announcement_addresses(sequence<SocketAddress> announcement_addresses);
9695
[Throws=BuildError]
9796
void set_node_alias(string node_alias);
97+
void set_async_payments_role(AsyncPaymentsRole? role);
9898
[Throws=BuildError]
9999
Node build();
100100
[Throws=BuildError]
@@ -720,6 +720,11 @@ enum Currency {
720720
"Signet",
721721
};
722722

723+
enum AsyncPaymentsRole {
724+
"Client",
725+
"Server",
726+
};
727+
723728
dictionary RouteHintHop {
724729
PublicKey src_node_id;
725730
u64 short_channel_id;

src/builder.rs

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77

88
use crate::chain::ChainSource;
99
use crate::config::{
10-
default_user_config, may_announce_channel, AnnounceError, BitcoindRestClientConfig, Config,
11-
ElectrumSyncConfig, EsploraSyncConfig, DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME,
12-
DEFAULT_LOG_LEVEL, WALLET_KEYS_SEED_LEN,
10+
default_user_config, may_announce_channel, AnnounceError, AsyncPaymentsRole,
11+
BitcoindRestClientConfig, Config, ElectrumSyncConfig, EsploraSyncConfig,
12+
DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL, WALLET_KEYS_SEED_LEN,
1313
};
1414

1515
use crate::connection::ConnectionManager;
@@ -27,6 +27,7 @@ use crate::liquidity::{
2727
};
2828
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
2929
use crate::message_handler::NodeCustomMessageHandler;
30+
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
3031
use crate::peer_store::PeerStore;
3132
use crate::runtime::Runtime;
3233
use crate::tx_broadcaster::TransactionBroadcaster;
@@ -241,6 +242,7 @@ pub struct NodeBuilder {
241242
liquidity_source_config: Option<LiquiditySourceConfig>,
242243
log_writer_config: Option<LogWriterConfig>,
243244
runtime_handle: Option<tokio::runtime::Handle>,
245+
async_payments_role: Option<AsyncPaymentsRole>,
244246
}
245247

246248
impl NodeBuilder {
@@ -266,6 +268,7 @@ impl NodeBuilder {
266268
liquidity_source_config,
267269
log_writer_config,
268270
runtime_handle,
271+
async_payments_role: None,
269272
}
270273
}
271274

@@ -544,6 +547,12 @@ impl NodeBuilder {
544547
Ok(self)
545548
}
546549

550+
/// Sets the role of the node in an asynchronous payments context.
551+
pub fn set_async_payments_role(&mut self, role: Option<AsyncPaymentsRole>) -> &mut Self {
552+
self.async_payments_role = role;
553+
self
554+
}
555+
547556
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
548557
/// previously configured.
549558
pub fn build(&self) -> Result<Node, BuildError> {
@@ -704,6 +713,7 @@ impl NodeBuilder {
704713
runtime,
705714
logger,
706715
Arc::new(vss_store),
716+
self.async_payments_role,
707717
)
708718
}
709719

@@ -736,6 +746,7 @@ impl NodeBuilder {
736746
runtime,
737747
logger,
738748
kv_store,
749+
self.async_payments_role,
739750
)
740751
}
741752
}
@@ -989,6 +1000,11 @@ impl ArcedNodeBuilder {
9891000
self.inner.write().unwrap().set_node_alias(node_alias).map(|_| ())
9901001
}
9911002

1003+
/// Sets the role of the node in an asynchronous payments context.
1004+
pub fn set_async_payments_role(&self, role: Option<AsyncPaymentsRole>) {
1005+
_ = self.inner.write().unwrap().set_async_payments_role(role)
1006+
}
1007+
9921008
/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
9931009
/// previously configured.
9941010
pub fn build(&self) -> Result<Arc<Node>, BuildError> {
@@ -1084,6 +1100,7 @@ fn build_with_store_internal(
10841100
gossip_source_config: Option<&GossipSourceConfig>,
10851101
liquidity_source_config: Option<&LiquiditySourceConfig>, seed_bytes: [u8; 64],
10861102
runtime: Arc<Runtime>, logger: Arc<Logger>, kv_store: Arc<DynStore>,
1103+
async_payments_role: Option<AsyncPaymentsRole>,
10871104
) -> Result<Node, BuildError> {
10881105
optionally_install_rustls_cryptoprovider();
10891106

@@ -1378,8 +1395,14 @@ fn build_with_store_internal(
13781395
100;
13791396
}
13801397

1381-
if config.async_payment_services_enabled {
1382-
user_config.accept_forwards_to_priv_channels = true;
1398+
if let Some(role) = async_payments_role {
1399+
match role {
1400+
AsyncPaymentsRole::Server => {
1401+
user_config.accept_forwards_to_priv_channels = true;
1402+
user_config.enable_htlc_hold = true;
1403+
},
1404+
AsyncPaymentsRole::Client => user_config.hold_outbound_htlcs_at_next_hop = true,
1405+
}
13831406
}
13841407

13851408
let message_router =
@@ -1452,17 +1475,32 @@ fn build_with_store_internal(
14521475
}
14531476

14541477
// Initialize the PeerManager
1455-
let onion_messenger: Arc<OnionMessenger> = Arc::new(OnionMessenger::new(
1456-
Arc::clone(&keys_manager),
1457-
Arc::clone(&keys_manager),
1458-
Arc::clone(&logger),
1459-
Arc::clone(&channel_manager),
1460-
message_router,
1461-
Arc::clone(&channel_manager),
1462-
Arc::clone(&channel_manager),
1463-
IgnoringMessageHandler {},
1464-
IgnoringMessageHandler {},
1465-
));
1478+
let onion_messenger: Arc<OnionMessenger> =
1479+
if let Some(AsyncPaymentsRole::Server) = async_payments_role {
1480+
Arc::new(OnionMessenger::new_with_offline_peer_interception(
1481+
Arc::clone(&keys_manager),
1482+
Arc::clone(&keys_manager),
1483+
Arc::clone(&logger),
1484+
Arc::clone(&channel_manager),
1485+
message_router,
1486+
Arc::clone(&channel_manager),
1487+
Arc::clone(&channel_manager),
1488+
IgnoringMessageHandler {},
1489+
IgnoringMessageHandler {},
1490+
))
1491+
} else {
1492+
Arc::new(OnionMessenger::new(
1493+
Arc::clone(&keys_manager),
1494+
Arc::clone(&keys_manager),
1495+
Arc::clone(&logger),
1496+
Arc::clone(&channel_manager),
1497+
message_router,
1498+
Arc::clone(&channel_manager),
1499+
Arc::clone(&channel_manager),
1500+
IgnoringMessageHandler {},
1501+
IgnoringMessageHandler {},
1502+
))
1503+
};
14661504
let ephemeral_bytes: [u8; 32] = keys_manager.get_secure_random_bytes();
14671505

14681506
// Initialize the GossipSource
@@ -1649,6 +1687,12 @@ fn build_with_store_internal(
16491687
},
16501688
};
16511689

1690+
let om_mailbox = if let Some(AsyncPaymentsRole::Server) = async_payments_role {
1691+
Some(Arc::new(OnionMessageMailbox::new()))
1692+
} else {
1693+
None
1694+
};
1695+
16521696
let (stop_sender, _) = tokio::sync::watch::channel(());
16531697
let (background_processor_stop_sender, _) = tokio::sync::watch::channel(());
16541698
let is_running = Arc::new(RwLock::new(false));
@@ -1681,6 +1725,8 @@ fn build_with_store_internal(
16811725
is_running,
16821726
is_listening,
16831727
node_metrics,
1728+
om_mailbox,
1729+
async_payments_role,
16841730
})
16851731
}
16861732

src/config.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,6 @@ pub struct Config {
179179
/// **Note:** If unset, default parameters will be used, and you will be able to override the
180180
/// parameters on a per-payment basis in the corresponding method calls.
181181
pub route_parameters: Option<RouteParametersConfig>,
182-
/// Whether to enable the static invoice service to support async payment reception for clients.
183-
pub async_payment_services_enabled: bool,
184182
}
185183

186184
impl Default for Config {
@@ -195,7 +193,6 @@ impl Default for Config {
195193
anchor_channels_config: Some(AnchorChannelsConfig::default()),
196194
route_parameters: None,
197195
node_alias: None,
198-
async_payment_services_enabled: false,
199196
}
200197
}
201198
}
@@ -537,6 +534,17 @@ impl From<MaxDustHTLCExposure> for LdkMaxDustHTLCExposure {
537534
}
538535
}
539536

537+
#[derive(Debug, Clone, Copy)]
538+
/// The role of the node in an asynchronous payments context.
539+
pub enum AsyncPaymentsRole {
540+
/// Node acts a client in an async payments context. This means that if possible, it will instruct its peers to hold
541+
/// htlcs for it, so that it can go offline.
542+
Client,
543+
/// Node acts as a server in an async payments context. This means that it will hold async payments htlcs and onion
544+
/// messages for its peers.
545+
Server,
546+
}
547+
540548
#[cfg(test)]
541549
mod tests {
542550
use std::str::FromStr;

src/event.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
77

8-
use crate::types::{CustomTlvRecord, DynStore, PaymentStore, Sweeper, Wallet};
8+
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
9+
use crate::types::{CustomTlvRecord, DynStore, OnionMessenger, PaymentStore, Sweeper, Wallet};
910
use crate::{
1011
hex_utils, BumpTransactionEventHandler, ChannelManager, Error, Graph, PeerInfo, PeerStore,
1112
UserChannelId,
@@ -459,6 +460,8 @@ where
459460
logger: L,
460461
config: Arc<Config>,
461462
static_invoice_store: Option<StaticInvoiceStore>,
463+
onion_messenger: Arc<OnionMessenger>,
464+
om_mailbox: Option<Arc<OnionMessageMailbox>>,
462465
}
463466

464467
impl<L: Deref + Clone + Sync + Send + 'static> EventHandler<L>
@@ -472,7 +475,8 @@ where
472475
output_sweeper: Arc<Sweeper>, network_graph: Arc<Graph>,
473476
liquidity_source: Option<Arc<LiquiditySource<Arc<Logger>>>>,
474477
payment_store: Arc<PaymentStore>, peer_store: Arc<PeerStore<L>>,
475-
static_invoice_store: Option<StaticInvoiceStore>, runtime: Arc<Runtime>, logger: L,
478+
static_invoice_store: Option<StaticInvoiceStore>, onion_messenger: Arc<OnionMessenger>,
479+
om_mailbox: Option<Arc<OnionMessageMailbox>>, runtime: Arc<Runtime>, logger: L,
476480
config: Arc<Config>,
477481
) -> Self {
478482
Self {
@@ -490,6 +494,8 @@ where
490494
runtime,
491495
config,
492496
static_invoice_store,
497+
onion_messenger,
498+
om_mailbox,
493499
}
494500
}
495501

@@ -1491,11 +1497,33 @@ where
14911497

14921498
self.bump_tx_event_handler.handle_event(&bte).await;
14931499
},
1494-
LdkEvent::OnionMessageIntercepted { .. } => {
1495-
debug_assert!(false, "We currently don't support onion message interception, so this event should never be emitted.");
1500+
LdkEvent::OnionMessageIntercepted { peer_node_id, message } => {
1501+
if let Some(om_mailbox) = self.om_mailbox.as_ref() {
1502+
om_mailbox.onion_message_intercepted(peer_node_id, message);
1503+
} else {
1504+
log_trace!(
1505+
self.logger,
1506+
"Onion message intercepted, but no onion message mailbox available"
1507+
);
1508+
}
14961509
},
1497-
LdkEvent::OnionMessagePeerConnected { .. } => {
1498-
debug_assert!(false, "We currently don't support onion message interception, so this event should never be emitted.");
1510+
LdkEvent::OnionMessagePeerConnected { peer_node_id } => {
1511+
if let Some(om_mailbox) = self.om_mailbox.as_ref() {
1512+
let messages = om_mailbox.onion_message_peer_connected(peer_node_id);
1513+
1514+
for message in messages {
1515+
if let Err(e) =
1516+
self.onion_messenger.forward_onion_message(message, &peer_node_id)
1517+
{
1518+
log_trace!(
1519+
self.logger,
1520+
"Failed to forward onion message to peer {}: {:?}",
1521+
peer_node_id,
1522+
e
1523+
);
1524+
}
1525+
}
1526+
}
14991527
},
15001528

15011529
LdkEvent::PersistStaticInvoice {

src/lib.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ pub use builder::BuildError;
126126
pub use builder::NodeBuilder as Builder;
127127

128128
use chain::ChainSource;
129+
use config::AsyncPaymentsRole;
129130
use config::{
130131
default_user_config, may_announce_channel, ChannelConfig, Config, NODE_ANN_BCAST_INTERVAL,
131132
PEER_RECONNECTION_INTERVAL, RGS_SYNC_INTERVAL,
@@ -136,6 +137,7 @@ use gossip::GossipSource;
136137
use graph::NetworkGraph;
137138
use io::utils::write_node_metrics;
138139
use liquidity::{LSPS1Liquidity, LiquiditySource};
140+
use payment::asynchronous::om_mailbox::OnionMessageMailbox;
139141
use payment::asynchronous::static_invoice_store::StaticInvoiceStore;
140142
use payment::{
141143
Bolt11Payment, Bolt12Payment, OnchainPayment, PaymentDetails, SpontaneousPayment,
@@ -205,6 +207,8 @@ pub struct Node {
205207
is_running: Arc<RwLock<bool>>,
206208
is_listening: Arc<AtomicBool>,
207209
node_metrics: Arc<RwLock<NodeMetrics>>,
210+
om_mailbox: Option<Arc<OnionMessageMailbox>>,
211+
async_payments_role: Option<AsyncPaymentsRole>,
208212
}
209213

210214
impl Node {
@@ -499,7 +503,8 @@ impl Node {
499503
Arc::clone(&self.logger),
500504
));
501505

502-
let static_invoice_store = if self.config.async_payment_services_enabled {
506+
let static_invoice_store = if let Some(AsyncPaymentsRole::Server) = self.async_payments_role
507+
{
503508
Some(StaticInvoiceStore::new(Arc::clone(&self.kv_store)))
504509
} else {
505510
None
@@ -517,6 +522,8 @@ impl Node {
517522
Arc::clone(&self.payment_store),
518523
Arc::clone(&self.peer_store),
519524
static_invoice_store,
525+
Arc::clone(&self.onion_messenger),
526+
self.om_mailbox.clone(),
520527
Arc::clone(&self.runtime),
521528
Arc::clone(&self.logger),
522529
Arc::clone(&self.config),
@@ -826,9 +833,9 @@ impl Node {
826833
Bolt12Payment::new(
827834
Arc::clone(&self.channel_manager),
828835
Arc::clone(&self.payment_store),
829-
Arc::clone(&self.config),
830836
Arc::clone(&self.is_running),
831837
Arc::clone(&self.logger),
838+
self.async_payments_role,
832839
)
833840
}
834841

@@ -840,9 +847,9 @@ impl Node {
840847
Arc::new(Bolt12Payment::new(
841848
Arc::clone(&self.channel_manager),
842849
Arc::clone(&self.payment_store),
843-
Arc::clone(&self.config),
844850
Arc::clone(&self.is_running),
845851
Arc::clone(&self.logger),
852+
self.async_payments_role,
846853
))
847854
}
848855

src/payment/asynchronous/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
77

8+
pub(crate) mod om_mailbox;
89
mod rate_limiter;
910
pub(crate) mod static_invoice_store;

0 commit comments

Comments
 (0)