Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,18 +86,18 @@ uniffi-default = [
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }

bdk_chain = { version = "0.23.3", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.2", default-features = false, features = ["async-https-rustls", "tokio"], optional = true }
Expand DownExpand Up@@ -132,14 +132,14 @@ postgres-native-tls = { version = "0.5", default-features = false, features = ["
vss-client = { package = "vss-client-ng", version = "0.6", optional = true }
prost = { version = "0.11.6", default-features = false, optional = true}
#bitcoin-payment-instructions = { version = "0.6" }
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "1d6ffaa8962391ddc84aeef98bd7439e55ccae9d", optional = true }
bitcoin-payment-instructions = { git = "https://github.com/benthecarman/bitcoin-payment-instructions", rev = "224b07fcc45a687bd56d8a9bded1e837d3d1bc37", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"] }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"] }
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
proptest = "1.0.0"
regex = "1.5.6"
Expand Down
25 changes: 25 additions & 0 deletions bindings/ldk_node.udl
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,8 @@ typedef dictionary ElectrumSyncConfig;

typedef dictionary TorConfig;

typedef enum ForwardedPaymentTrackingMode;

typedef interface NodeEntropy;

typedef interface ProbingConfig;
Expand DownExpand Up@@ -68,6 +70,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
Liquidity liquidity();
ForwardingAnalytics forwarding_analytics();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand DownExpand Up@@ -139,6 +142,8 @@ interface FeeRate {

typedef interface Liquidity;

typedef interface ForwardingAnalytics;

[Error]
enum NodeError {
"AlreadyRunning",
Expand DownExpand Up@@ -177,6 +182,8 @@ enum NodeError {
"InvalidOfferId",
"InvalidNodeId",
"InvalidPaymentId",
"InvalidForwardedPaymentId",
"InvalidChannelPairForwardingStatsId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand DownExpand Up@@ -358,6 +365,12 @@ typedef string OfferId;
[Custom]
typedef string PaymentId;

[Custom]
typedef string ForwardedPaymentId;

[Custom]
typedef string ChannelPairForwardingStatsId;

[Custom]
typedef string PaymentHash;

Expand DownExpand Up@@ -395,3 +408,15 @@ typedef enum Event;
typedef interface HRNResolverConfig;

typedef dictionary HumanReadableNamesConfig;

typedef dictionary ForwardedPaymentDetails;

typedef dictionary ChannelForwardingStats;

typedef dictionary ChannelPairForwardingStats;

typedef dictionary ForwardedPaymentDetailsPage;

typedef dictionary ChannelForwardingStatsPage;

typedef dictionary ChannelPairForwardingStatsPage;
82 changes: 61 additions & 21 deletions src/builder.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,9 @@ use crate::io::utils::{
#[cfg(feature = "storage-vss")]
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
Expand All@@ -89,6 +91,7 @@ use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
use crate::payment::forwarding_store::ForwardingStore;
#[cfg(feature = "unified-payments")]
use crate::payment::HRNResolver;
use crate::peer_store::PeerStore;
Expand DownExpand Up@@ -1524,26 +1527,37 @@ fn build_with_store_internal(

let kv_store_ref = Arc::clone(&kv_store);
let logger_ref = Arc::clone(&logger);
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref)
)
});
let (
payment_store_res,
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_all_objects(
&*kv_store_ref,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});

// Initialize the status fields.
let node_metrics = match node_metris_res {
Expand DownExpand Up@@ -1576,6 +1590,14 @@ fn build_with_store_internal(
},
};

let channel_forwarding_stats = match channel_forwarding_stats_res {
Ok(stats) => stats,
Err(e) => {
log_error!(logger, "Failed to read channel forwarding stats from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let (chain_source, chain_tip_opt) = match chain_data_source_config {
#[cfg(feature = "chain-esplora")]
Some(ChainDataSourceConfig::Esplora { server_url, headers, sync_config }) => {
Expand DownExpand Up@@ -1902,6 +1924,12 @@ fn build_with_store_internal(
Arc::clone(&wallet),
Arc::clone(&logger),
));
let forwarding_store = Arc::new(ForwardingStore::new(
channel_forwarding_stats,
config.forwarded_payment_tracking_mode,
Arc::clone(&kv_store),
Arc::clone(&logger),
));

let peer_storage_key = keys_manager.get_peer_storage_key();
let monitor_reader = Arc::new(AsyncPersister::new(
Expand DownExpand Up@@ -2457,6 +2485,16 @@ fn build_with_store_internal(
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
}

// How long detail records are kept before being folded into channel-pair buckets. `Stats` keeps
// none of its own, and only drains records a previous `Detailed` configuration left behind.
let forwarded_payment_aggregation_retention_secs = match config.forwarded_payment_tracking_mode
{
crate::config::ForwardedPaymentTrackingMode::Detailed => {
crate::payment::forwarding_store::FORWARDED_PAYMENT_AGGREGATION_BUCKET_SIZE_SECS
},
crate::config::ForwardedPaymentTrackingMode::Stats => 0,
};

Ok(Node {
runtime,
stop_sender,
Expand DownExpand Up@@ -2484,6 +2522,8 @@ fn build_with_store_internal(
scorer,
peer_store,
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
lnurl_auth,
is_running,
node_metrics,
Expand Down
32 changes: 30 additions & 2 deletions src/config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -169,6 +169,30 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
// thereafter until every configured LSP has been discovered.
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);

/// The mode used for tracking forwarded payments.
///
/// In either mode, a forward is tracked only when it has exactly one incoming HTLC and one outgoing
/// HTLC, and LDK reports both the outbound amount and total fee.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ForwardedPaymentTrackingMode {
/// Track eligible new forwarded payments only as per-channel aggregate statistics.
///
/// Any detailed records left by a previous configuration are aggregated and removed after their
/// current one-hour bucket closes.
Stats,
/// Store eligible individual forwarded payments for the current and previous one-hour buckets.
///
/// Payments from older buckets are aggregated into channel-pair statistics and removed.
Detailed,
}

impl Default for ForwardedPaymentTrackingMode {
fn default() -> Self {
Self::Stats
}
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// Represents the configuration of an [`Node`] instance.
Expand All@@ -192,9 +216,10 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_
doc = "| `hrn_config` | HumanReadableNamesConfig::default() |"
)]
/// | `manually_handle_unknown_bolt11_payments` | false |
/// | `forwarded_payment_tracking_mode` | Stats |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`RouteParametersConfig`], and
/// [`ForwardedPaymentTrackingMode`] for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand DownExpand Up@@ -268,6 +293,8 @@ pub struct Config {
///
/// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable
pub manually_handle_unknown_bolt11_payments: bool,
/// The mode used for tracking forwarded payments.
pub forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode,
}

impl Default for Config {
Expand All@@ -286,6 +313,7 @@ impl Default for Config {
#[cfg(feature = "unified-payments")]
hrn_config: HumanReadableNamesConfig::default(),
manually_handle_unknown_bolt11_payments: false,
forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode::default(),
}
}
}
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add storage for forwarded payments by benthecarman · Pull Request #772 · lightningdevkit/ldk-node · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,18 +86,18 @@ uniffi-default = [
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }

bdk_chain = { version = "0.23.3", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.2", default-features = false, features = ["async-https-rustls", "tokio"], optional = true }
Expand DownExpand Up@@ -132,14 +132,14 @@ postgres-native-tls = { version = "0.5", default-features = false, features = ["
vss-client = { package = "vss-client-ng", version = "0.6", optional = true }
prost = { version = "0.11.6", default-features = false, optional = true}
#bitcoin-payment-instructions = { version = "0.6" }
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "1d6ffaa8962391ddc84aeef98bd7439e55ccae9d", optional = true }
bitcoin-payment-instructions = { git = "https://github.com/benthecarman/bitcoin-payment-instructions", rev = "224b07fcc45a687bd56d8a9bded1e837d3d1bc37", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"] }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"] }
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
proptest = "1.0.0"
regex = "1.5.6"
Expand Down
25 changes: 25 additions & 0 deletions bindings/ldk_node.udl
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,8 @@ typedef dictionary ElectrumSyncConfig;

typedef dictionary TorConfig;

typedef enum ForwardedPaymentTrackingMode;

typedef interface NodeEntropy;

typedef interface ProbingConfig;
Expand DownExpand Up@@ -68,6 +70,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
Liquidity liquidity();
ForwardingAnalytics forwarding_analytics();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand DownExpand Up@@ -139,6 +142,8 @@ interface FeeRate {

typedef interface Liquidity;

typedef interface ForwardingAnalytics;

[Error]
enum NodeError {
"AlreadyRunning",
Expand DownExpand Up@@ -177,6 +182,8 @@ enum NodeError {
"InvalidOfferId",
"InvalidNodeId",
"InvalidPaymentId",
"InvalidForwardedPaymentId",
"InvalidChannelPairForwardingStatsId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand DownExpand Up@@ -358,6 +365,12 @@ typedef string OfferId;
[Custom]
typedef string PaymentId;

[Custom]
typedef string ForwardedPaymentId;

[Custom]
typedef string ChannelPairForwardingStatsId;

[Custom]
typedef string PaymentHash;

Expand DownExpand Up@@ -395,3 +408,15 @@ typedef enum Event;
typedef interface HRNResolverConfig;

typedef dictionary HumanReadableNamesConfig;

typedef dictionary ForwardedPaymentDetails;

typedef dictionary ChannelForwardingStats;

typedef dictionary ChannelPairForwardingStats;

typedef dictionary ForwardedPaymentDetailsPage;

typedef dictionary ChannelForwardingStatsPage;

typedef dictionary ChannelPairForwardingStatsPage;
82 changes: 61 additions & 21 deletions src/builder.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,9 @@ use crate::io::utils::{
#[cfg(feature = "storage-vss")]
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
Expand All@@ -89,6 +91,7 @@ use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
use crate::payment::forwarding_store::ForwardingStore;
#[cfg(feature = "unified-payments")]
use crate::payment::HRNResolver;
use crate::peer_store::PeerStore;
Expand DownExpand Up@@ -1524,26 +1527,37 @@ fn build_with_store_internal(

let kv_store_ref = Arc::clone(&kv_store);
let logger_ref = Arc::clone(&logger);
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref)
)
});
let (
payment_store_res,
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_all_objects(
&*kv_store_ref,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});

// Initialize the status fields.
let node_metrics = match node_metris_res {
Expand DownExpand Up@@ -1576,6 +1590,14 @@ fn build_with_store_internal(
},
};

let channel_forwarding_stats = match channel_forwarding_stats_res {
Ok(stats) => stats,
Err(e) => {
log_error!(logger, "Failed to read channel forwarding stats from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let (chain_source, chain_tip_opt) = match chain_data_source_config {
#[cfg(feature = "chain-esplora")]
Some(ChainDataSourceConfig::Esplora { server_url, headers, sync_config }) => {
Expand DownExpand Up@@ -1902,6 +1924,12 @@ fn build_with_store_internal(
Arc::clone(&wallet),
Arc::clone(&logger),
));
let forwarding_store = Arc::new(ForwardingStore::new(
channel_forwarding_stats,
config.forwarded_payment_tracking_mode,
Arc::clone(&kv_store),
Arc::clone(&logger),
));

let peer_storage_key = keys_manager.get_peer_storage_key();
let monitor_reader = Arc::new(AsyncPersister::new(
Expand DownExpand Up@@ -2457,6 +2485,16 @@ fn build_with_store_internal(
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
}

// How long detail records are kept before being folded into channel-pair buckets. `Stats` keeps
// none of its own, and only drains records a previous `Detailed` configuration left behind.
let forwarded_payment_aggregation_retention_secs = match config.forwarded_payment_tracking_mode
{
crate::config::ForwardedPaymentTrackingMode::Detailed => {
crate::payment::forwarding_store::FORWARDED_PAYMENT_AGGREGATION_BUCKET_SIZE_SECS
},
crate::config::ForwardedPaymentTrackingMode::Stats => 0,
};

Ok(Node {
runtime,
stop_sender,
Expand DownExpand Up@@ -2484,6 +2522,8 @@ fn build_with_store_internal(
scorer,
peer_store,
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
lnurl_auth,
is_running,
node_metrics,
Expand Down
32 changes: 30 additions & 2 deletions src/config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -169,6 +169,30 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
// thereafter until every configured LSP has been discovered.
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);

/// The mode used for tracking forwarded payments.
///
/// In either mode, a forward is tracked only when it has exactly one incoming HTLC and one outgoing
/// HTLC, and LDK reports both the outbound amount and total fee.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ForwardedPaymentTrackingMode {
/// Track eligible new forwarded payments only as per-channel aggregate statistics.
///
/// Any detailed records left by a previous configuration are aggregated and removed after their
/// current one-hour bucket closes.
Stats,
/// Store eligible individual forwarded payments for the current and previous one-hour buckets.
///
/// Payments from older buckets are aggregated into channel-pair statistics and removed.
Detailed,
}

impl Default for ForwardedPaymentTrackingMode {
fn default() -> Self {
Self::Stats
}
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// Represents the configuration of an [`Node`] instance.
Expand All@@ -192,9 +216,10 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_
doc = "| `hrn_config` | HumanReadableNamesConfig::default() |"
)]
/// | `manually_handle_unknown_bolt11_payments` | false |
/// | `forwarded_payment_tracking_mode` | Stats |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`RouteParametersConfig`], and
/// [`ForwardedPaymentTrackingMode`] for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand DownExpand Up@@ -268,6 +293,8 @@ pub struct Config {
///
/// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable
pub manually_handle_unknown_bolt11_payments: bool,
/// The mode used for tracking forwarded payments.
pub forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode,
}

impl Default for Config {
Expand All@@ -286,6 +313,7 @@ impl Default for Config {
#[cfg(feature = "unified-payments")]
hrn_config: HumanReadableNamesConfig::default(),
manually_handle_unknown_bolt11_payments: false,
forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode::default(),
}
}
}
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add storage for forwarded payments by benthecarman · Pull Request #772 · lightningdevkit/ldk-node · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,18 +86,18 @@ uniffi-default = [
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }

bdk_chain = { version = "0.23.3", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.2", default-features = false, features = ["async-https-rustls", "tokio"], optional = true }
Expand DownExpand Up@@ -132,14 +132,14 @@ postgres-native-tls = { version = "0.5", default-features = false, features = ["
vss-client = { package = "vss-client-ng", version = "0.6", optional = true }
prost = { version = "0.11.6", default-features = false, optional = true}
#bitcoin-payment-instructions = { version = "0.6" }
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "1d6ffaa8962391ddc84aeef98bd7439e55ccae9d", optional = true }
bitcoin-payment-instructions = { git = "https://github.com/benthecarman/bitcoin-payment-instructions", rev = "224b07fcc45a687bd56d8a9bded1e837d3d1bc37", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"] }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"] }
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
proptest = "1.0.0"
regex = "1.5.6"
Expand Down
25 changes: 25 additions & 0 deletions bindings/ldk_node.udl
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,8 @@ typedef dictionary ElectrumSyncConfig;

typedef dictionary TorConfig;

typedef enum ForwardedPaymentTrackingMode;

typedef interface NodeEntropy;

typedef interface ProbingConfig;
Expand DownExpand Up@@ -68,6 +70,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
Liquidity liquidity();
ForwardingAnalytics forwarding_analytics();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand DownExpand Up@@ -139,6 +142,8 @@ interface FeeRate {

typedef interface Liquidity;

typedef interface ForwardingAnalytics;

[Error]
enum NodeError {
"AlreadyRunning",
Expand DownExpand Up@@ -177,6 +182,8 @@ enum NodeError {
"InvalidOfferId",
"InvalidNodeId",
"InvalidPaymentId",
"InvalidForwardedPaymentId",
"InvalidChannelPairForwardingStatsId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand DownExpand Up@@ -358,6 +365,12 @@ typedef string OfferId;
[Custom]
typedef string PaymentId;

[Custom]
typedef string ForwardedPaymentId;

[Custom]
typedef string ChannelPairForwardingStatsId;

[Custom]
typedef string PaymentHash;

Expand DownExpand Up@@ -395,3 +408,15 @@ typedef enum Event;
typedef interface HRNResolverConfig;

typedef dictionary HumanReadableNamesConfig;

typedef dictionary ForwardedPaymentDetails;

typedef dictionary ChannelForwardingStats;

typedef dictionary ChannelPairForwardingStats;

typedef dictionary ForwardedPaymentDetailsPage;

typedef dictionary ChannelForwardingStatsPage;

typedef dictionary ChannelPairForwardingStatsPage;
82 changes: 61 additions & 21 deletions src/builder.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,9 @@ use crate::io::utils::{
#[cfg(feature = "storage-vss")]
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
Expand All@@ -89,6 +91,7 @@ use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
use crate::payment::forwarding_store::ForwardingStore;
#[cfg(feature = "unified-payments")]
use crate::payment::HRNResolver;
use crate::peer_store::PeerStore;
Expand DownExpand Up@@ -1524,26 +1527,37 @@ fn build_with_store_internal(

let kv_store_ref = Arc::clone(&kv_store);
let logger_ref = Arc::clone(&logger);
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref)
)
});
let (
payment_store_res,
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_all_objects(
&*kv_store_ref,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});

// Initialize the status fields.
let node_metrics = match node_metris_res {
Expand DownExpand Up@@ -1576,6 +1590,14 @@ fn build_with_store_internal(
},
};

let channel_forwarding_stats = match channel_forwarding_stats_res {
Ok(stats) => stats,
Err(e) => {
log_error!(logger, "Failed to read channel forwarding stats from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let (chain_source, chain_tip_opt) = match chain_data_source_config {
#[cfg(feature = "chain-esplora")]
Some(ChainDataSourceConfig::Esplora { server_url, headers, sync_config }) => {
Expand DownExpand Up@@ -1902,6 +1924,12 @@ fn build_with_store_internal(
Arc::clone(&wallet),
Arc::clone(&logger),
));
let forwarding_store = Arc::new(ForwardingStore::new(
channel_forwarding_stats,
config.forwarded_payment_tracking_mode,
Arc::clone(&kv_store),
Arc::clone(&logger),
));

let peer_storage_key = keys_manager.get_peer_storage_key();
let monitor_reader = Arc::new(AsyncPersister::new(
Expand DownExpand Up@@ -2457,6 +2485,16 @@ fn build_with_store_internal(
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
}

// How long detail records are kept before being folded into channel-pair buckets. `Stats` keeps
// none of its own, and only drains records a previous `Detailed` configuration left behind.
let forwarded_payment_aggregation_retention_secs = match config.forwarded_payment_tracking_mode
{
crate::config::ForwardedPaymentTrackingMode::Detailed => {
crate::payment::forwarding_store::FORWARDED_PAYMENT_AGGREGATION_BUCKET_SIZE_SECS
},
crate::config::ForwardedPaymentTrackingMode::Stats => 0,
};

Ok(Node {
runtime,
stop_sender,
Expand DownExpand Up@@ -2484,6 +2522,8 @@ fn build_with_store_internal(
scorer,
peer_store,
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
lnurl_auth,
is_running,
node_metrics,
Expand Down
32 changes: 30 additions & 2 deletions src/config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -169,6 +169,30 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
// thereafter until every configured LSP has been discovered.
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);

/// The mode used for tracking forwarded payments.
///
/// In either mode, a forward is tracked only when it has exactly one incoming HTLC and one outgoing
/// HTLC, and LDK reports both the outbound amount and total fee.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ForwardedPaymentTrackingMode {
/// Track eligible new forwarded payments only as per-channel aggregate statistics.
///
/// Any detailed records left by a previous configuration are aggregated and removed after their
/// current one-hour bucket closes.
Stats,
/// Store eligible individual forwarded payments for the current and previous one-hour buckets.
///
/// Payments from older buckets are aggregated into channel-pair statistics and removed.
Detailed,
}

impl Default for ForwardedPaymentTrackingMode {
fn default() -> Self {
Self::Stats
}
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// Represents the configuration of an [`Node`] instance.
Expand All@@ -192,9 +216,10 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_
doc = "| `hrn_config` | HumanReadableNamesConfig::default() |"
)]
/// | `manually_handle_unknown_bolt11_payments` | false |
/// | `forwarded_payment_tracking_mode` | Stats |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`RouteParametersConfig`], and
/// [`ForwardedPaymentTrackingMode`] for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand DownExpand Up@@ -268,6 +293,8 @@ pub struct Config {
///
/// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable
pub manually_handle_unknown_bolt11_payments: bool,
/// The mode used for tracking forwarded payments.
pub forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode,
}

impl Default for Config {
Expand All@@ -286,6 +313,7 @@ impl Default for Config {
#[cfg(feature = "unified-payments")]
hrn_config: HumanReadableNamesConfig::default(),
manually_handle_unknown_bolt11_payments: false,
forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode::default(),
}
}
}
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add storage for forwarded payments by benthecarman · Pull Request #772 · lightningdevkit/ldk-node · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,18 +86,18 @@ uniffi-default = [
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }

bdk_chain = { version = "0.23.3", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.2", default-features = false, features = ["async-https-rustls", "tokio"], optional = true }
Expand DownExpand Up@@ -132,14 +132,14 @@ postgres-native-tls = { version = "0.5", default-features = false, features = ["
vss-client = { package = "vss-client-ng", version = "0.6", optional = true }
prost = { version = "0.11.6", default-features = false, optional = true}
#bitcoin-payment-instructions = { version = "0.6" }
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "1d6ffaa8962391ddc84aeef98bd7439e55ccae9d", optional = true }
bitcoin-payment-instructions = { git = "https://github.com/benthecarman/bitcoin-payment-instructions", rev = "224b07fcc45a687bd56d8a9bded1e837d3d1bc37", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"] }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"] }
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
proptest = "1.0.0"
regex = "1.5.6"
Expand Down
25 changes: 25 additions & 0 deletions bindings/ldk_node.udl
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,8 @@ typedef dictionary ElectrumSyncConfig;

typedef dictionary TorConfig;

typedef enum ForwardedPaymentTrackingMode;

typedef interface NodeEntropy;

typedef interface ProbingConfig;
Expand DownExpand Up@@ -68,6 +70,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
Liquidity liquidity();
ForwardingAnalytics forwarding_analytics();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand DownExpand Up@@ -139,6 +142,8 @@ interface FeeRate {

typedef interface Liquidity;

typedef interface ForwardingAnalytics;

[Error]
enum NodeError {
"AlreadyRunning",
Expand DownExpand Up@@ -177,6 +182,8 @@ enum NodeError {
"InvalidOfferId",
"InvalidNodeId",
"InvalidPaymentId",
"InvalidForwardedPaymentId",
"InvalidChannelPairForwardingStatsId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand DownExpand Up@@ -358,6 +365,12 @@ typedef string OfferId;
[Custom]
typedef string PaymentId;

[Custom]
typedef string ForwardedPaymentId;

[Custom]
typedef string ChannelPairForwardingStatsId;

[Custom]
typedef string PaymentHash;

Expand DownExpand Up@@ -395,3 +408,15 @@ typedef enum Event;
typedef interface HRNResolverConfig;

typedef dictionary HumanReadableNamesConfig;

typedef dictionary ForwardedPaymentDetails;

typedef dictionary ChannelForwardingStats;

typedef dictionary ChannelPairForwardingStats;

typedef dictionary ForwardedPaymentDetailsPage;

typedef dictionary ChannelForwardingStatsPage;

typedef dictionary ChannelPairForwardingStatsPage;
82 changes: 61 additions & 21 deletions src/builder.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,9 @@ use crate::io::utils::{
#[cfg(feature = "storage-vss")]
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
Expand All@@ -89,6 +91,7 @@ use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
use crate::payment::forwarding_store::ForwardingStore;
#[cfg(feature = "unified-payments")]
use crate::payment::HRNResolver;
use crate::peer_store::PeerStore;
Expand DownExpand Up@@ -1524,26 +1527,37 @@ fn build_with_store_internal(

let kv_store_ref = Arc::clone(&kv_store);
let logger_ref = Arc::clone(&logger);
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref)
)
});
let (
payment_store_res,
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_all_objects(
&*kv_store_ref,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});

// Initialize the status fields.
let node_metrics = match node_metris_res {
Expand DownExpand Up@@ -1576,6 +1590,14 @@ fn build_with_store_internal(
},
};

let channel_forwarding_stats = match channel_forwarding_stats_res {
Ok(stats) => stats,
Err(e) => {
log_error!(logger, "Failed to read channel forwarding stats from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let (chain_source, chain_tip_opt) = match chain_data_source_config {
#[cfg(feature = "chain-esplora")]
Some(ChainDataSourceConfig::Esplora { server_url, headers, sync_config }) => {
Expand DownExpand Up@@ -1902,6 +1924,12 @@ fn build_with_store_internal(
Arc::clone(&wallet),
Arc::clone(&logger),
));
let forwarding_store = Arc::new(ForwardingStore::new(
channel_forwarding_stats,
config.forwarded_payment_tracking_mode,
Arc::clone(&kv_store),
Arc::clone(&logger),
));

let peer_storage_key = keys_manager.get_peer_storage_key();
let monitor_reader = Arc::new(AsyncPersister::new(
Expand DownExpand Up@@ -2457,6 +2485,16 @@ fn build_with_store_internal(
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
}

// How long detail records are kept before being folded into channel-pair buckets. `Stats` keeps
// none of its own, and only drains records a previous `Detailed` configuration left behind.
let forwarded_payment_aggregation_retention_secs = match config.forwarded_payment_tracking_mode
{
crate::config::ForwardedPaymentTrackingMode::Detailed => {
crate::payment::forwarding_store::FORWARDED_PAYMENT_AGGREGATION_BUCKET_SIZE_SECS
},
crate::config::ForwardedPaymentTrackingMode::Stats => 0,
};

Ok(Node {
runtime,
stop_sender,
Expand DownExpand Up@@ -2484,6 +2522,8 @@ fn build_with_store_internal(
scorer,
peer_store,
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
lnurl_auth,
is_running,
node_metrics,
Expand Down
32 changes: 30 additions & 2 deletions src/config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -169,6 +169,30 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
// thereafter until every configured LSP has been discovered.
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);

/// The mode used for tracking forwarded payments.
///
/// In either mode, a forward is tracked only when it has exactly one incoming HTLC and one outgoing
/// HTLC, and LDK reports both the outbound amount and total fee.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ForwardedPaymentTrackingMode {
/// Track eligible new forwarded payments only as per-channel aggregate statistics.
///
/// Any detailed records left by a previous configuration are aggregated and removed after their
/// current one-hour bucket closes.
Stats,
/// Store eligible individual forwarded payments for the current and previous one-hour buckets.
///
/// Payments from older buckets are aggregated into channel-pair statistics and removed.
Detailed,
}

impl Default for ForwardedPaymentTrackingMode {
fn default() -> Self {
Self::Stats
}
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// Represents the configuration of an [`Node`] instance.
Expand All@@ -192,9 +216,10 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_
doc = "| `hrn_config` | HumanReadableNamesConfig::default() |"
)]
/// | `manually_handle_unknown_bolt11_payments` | false |
/// | `forwarded_payment_tracking_mode` | Stats |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`RouteParametersConfig`], and
/// [`ForwardedPaymentTrackingMode`] for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand DownExpand Up@@ -268,6 +293,8 @@ pub struct Config {
///
/// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable
pub manually_handle_unknown_bolt11_payments: bool,
/// The mode used for tracking forwarded payments.
pub forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode,
}

impl Default for Config {
Expand All@@ -286,6 +313,7 @@ impl Default for Config {
#[cfg(feature = "unified-payments")]
hrn_config: HumanReadableNamesConfig::default(),
manually_handle_unknown_bolt11_payments: false,
forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode::default(),
}
}
}
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add storage for forwarded payments by benthecarman · Pull Request #772 · lightningdevkit/ldk-node · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,18 +86,18 @@ uniffi-default = [
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }

bdk_chain = { version = "0.23.3", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.2", default-features = false, features = ["async-https-rustls", "tokio"], optional = true }
Expand DownExpand Up@@ -132,14 +132,14 @@ postgres-native-tls = { version = "0.5", default-features = false, features = ["
vss-client = { package = "vss-client-ng", version = "0.6", optional = true }
prost = { version = "0.11.6", default-features = false, optional = true}
#bitcoin-payment-instructions = { version = "0.6" }
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "1d6ffaa8962391ddc84aeef98bd7439e55ccae9d", optional = true }
bitcoin-payment-instructions = { git = "https://github.com/benthecarman/bitcoin-payment-instructions", rev = "224b07fcc45a687bd56d8a9bded1e837d3d1bc37", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"] }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"] }
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
proptest = "1.0.0"
regex = "1.5.6"
Expand Down
25 changes: 25 additions & 0 deletions bindings/ldk_node.udl
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,8 @@ typedef dictionary ElectrumSyncConfig;

typedef dictionary TorConfig;

typedef enum ForwardedPaymentTrackingMode;

typedef interface NodeEntropy;

typedef interface ProbingConfig;
Expand DownExpand Up@@ -68,6 +70,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
Liquidity liquidity();
ForwardingAnalytics forwarding_analytics();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand DownExpand Up@@ -139,6 +142,8 @@ interface FeeRate {

typedef interface Liquidity;

typedef interface ForwardingAnalytics;

[Error]
enum NodeError {
"AlreadyRunning",
Expand DownExpand Up@@ -177,6 +182,8 @@ enum NodeError {
"InvalidOfferId",
"InvalidNodeId",
"InvalidPaymentId",
"InvalidForwardedPaymentId",
"InvalidChannelPairForwardingStatsId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand DownExpand Up@@ -358,6 +365,12 @@ typedef string OfferId;
[Custom]
typedef string PaymentId;

[Custom]
typedef string ForwardedPaymentId;

[Custom]
typedef string ChannelPairForwardingStatsId;

[Custom]
typedef string PaymentHash;

Expand DownExpand Up@@ -395,3 +408,15 @@ typedef enum Event;
typedef interface HRNResolverConfig;

typedef dictionary HumanReadableNamesConfig;

typedef dictionary ForwardedPaymentDetails;

typedef dictionary ChannelForwardingStats;

typedef dictionary ChannelPairForwardingStats;

typedef dictionary ForwardedPaymentDetailsPage;

typedef dictionary ChannelForwardingStatsPage;

typedef dictionary ChannelPairForwardingStatsPage;
82 changes: 61 additions & 21 deletions src/builder.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,9 @@ use crate::io::utils::{
#[cfg(feature = "storage-vss")]
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
Expand All@@ -89,6 +91,7 @@ use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
use crate::payment::forwarding_store::ForwardingStore;
#[cfg(feature = "unified-payments")]
use crate::payment::HRNResolver;
use crate::peer_store::PeerStore;
Expand DownExpand Up@@ -1524,26 +1527,37 @@ fn build_with_store_internal(

let kv_store_ref = Arc::clone(&kv_store);
let logger_ref = Arc::clone(&logger);
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref)
)
});
let (
payment_store_res,
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_all_objects(
&*kv_store_ref,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});

// Initialize the status fields.
let node_metrics = match node_metris_res {
Expand DownExpand Up@@ -1576,6 +1590,14 @@ fn build_with_store_internal(
},
};

let channel_forwarding_stats = match channel_forwarding_stats_res {
Ok(stats) => stats,
Err(e) => {
log_error!(logger, "Failed to read channel forwarding stats from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let (chain_source, chain_tip_opt) = match chain_data_source_config {
#[cfg(feature = "chain-esplora")]
Some(ChainDataSourceConfig::Esplora { server_url, headers, sync_config }) => {
Expand DownExpand Up@@ -1902,6 +1924,12 @@ fn build_with_store_internal(
Arc::clone(&wallet),
Arc::clone(&logger),
));
let forwarding_store = Arc::new(ForwardingStore::new(
channel_forwarding_stats,
config.forwarded_payment_tracking_mode,
Arc::clone(&kv_store),
Arc::clone(&logger),
));

let peer_storage_key = keys_manager.get_peer_storage_key();
let monitor_reader = Arc::new(AsyncPersister::new(
Expand DownExpand Up@@ -2457,6 +2485,16 @@ fn build_with_store_internal(
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
}

// How long detail records are kept before being folded into channel-pair buckets. `Stats` keeps
// none of its own, and only drains records a previous `Detailed` configuration left behind.
let forwarded_payment_aggregation_retention_secs = match config.forwarded_payment_tracking_mode
{
crate::config::ForwardedPaymentTrackingMode::Detailed => {
crate::payment::forwarding_store::FORWARDED_PAYMENT_AGGREGATION_BUCKET_SIZE_SECS
},
crate::config::ForwardedPaymentTrackingMode::Stats => 0,
};

Ok(Node {
runtime,
stop_sender,
Expand DownExpand Up@@ -2484,6 +2522,8 @@ fn build_with_store_internal(
scorer,
peer_store,
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
lnurl_auth,
is_running,
node_metrics,
Expand Down
32 changes: 30 additions & 2 deletions src/config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -169,6 +169,30 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
// thereafter until every configured LSP has been discovered.
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);

/// The mode used for tracking forwarded payments.
///
/// In either mode, a forward is tracked only when it has exactly one incoming HTLC and one outgoing
/// HTLC, and LDK reports both the outbound amount and total fee.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ForwardedPaymentTrackingMode {
/// Track eligible new forwarded payments only as per-channel aggregate statistics.
///
/// Any detailed records left by a previous configuration are aggregated and removed after their
/// current one-hour bucket closes.
Stats,
/// Store eligible individual forwarded payments for the current and previous one-hour buckets.
///
/// Payments from older buckets are aggregated into channel-pair statistics and removed.
Detailed,
}

impl Default for ForwardedPaymentTrackingMode {
fn default() -> Self {
Self::Stats
}
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// Represents the configuration of an [`Node`] instance.
Expand All@@ -192,9 +216,10 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_
doc = "| `hrn_config` | HumanReadableNamesConfig::default() |"
)]
/// | `manually_handle_unknown_bolt11_payments` | false |
/// | `forwarded_payment_tracking_mode` | Stats |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`RouteParametersConfig`], and
/// [`ForwardedPaymentTrackingMode`] for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand DownExpand Up@@ -268,6 +293,8 @@ pub struct Config {
///
/// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable
pub manually_handle_unknown_bolt11_payments: bool,
/// The mode used for tracking forwarded payments.
pub forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode,
}

impl Default for Config {
Expand All@@ -286,6 +313,7 @@ impl Default for Config {
#[cfg(feature = "unified-payments")]
hrn_config: HumanReadableNamesConfig::default(),
manually_handle_unknown_bolt11_payments: false,
forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode::default(),
}
}
}
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add storage for forwarded payments by benthecarman · Pull Request #772 · lightningdevkit/ldk-node · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,18 +86,18 @@ uniffi-default = [
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }

bdk_chain = { version = "0.23.3", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.2", default-features = false, features = ["async-https-rustls", "tokio"], optional = true }
Expand DownExpand Up@@ -132,14 +132,14 @@ postgres-native-tls = { version = "0.5", default-features = false, features = ["
vss-client = { package = "vss-client-ng", version = "0.6", optional = true }
prost = { version = "0.11.6", default-features = false, optional = true}
#bitcoin-payment-instructions = { version = "0.6" }
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "1d6ffaa8962391ddc84aeef98bd7439e55ccae9d", optional = true }
bitcoin-payment-instructions = { git = "https://github.com/benthecarman/bitcoin-payment-instructions", rev = "224b07fcc45a687bd56d8a9bded1e837d3d1bc37", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"] }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"] }
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
proptest = "1.0.0"
regex = "1.5.6"
Expand Down
25 changes: 25 additions & 0 deletions bindings/ldk_node.udl
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,8 @@ typedef dictionary ElectrumSyncConfig;

typedef dictionary TorConfig;

typedef enum ForwardedPaymentTrackingMode;

typedef interface NodeEntropy;

typedef interface ProbingConfig;
Expand DownExpand Up@@ -68,6 +70,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
Liquidity liquidity();
ForwardingAnalytics forwarding_analytics();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand DownExpand Up@@ -139,6 +142,8 @@ interface FeeRate {

typedef interface Liquidity;

typedef interface ForwardingAnalytics;

[Error]
enum NodeError {
"AlreadyRunning",
Expand DownExpand Up@@ -177,6 +182,8 @@ enum NodeError {
"InvalidOfferId",
"InvalidNodeId",
"InvalidPaymentId",
"InvalidForwardedPaymentId",
"InvalidChannelPairForwardingStatsId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand DownExpand Up@@ -358,6 +365,12 @@ typedef string OfferId;
[Custom]
typedef string PaymentId;

[Custom]
typedef string ForwardedPaymentId;

[Custom]
typedef string ChannelPairForwardingStatsId;

[Custom]
typedef string PaymentHash;

Expand DownExpand Up@@ -395,3 +408,15 @@ typedef enum Event;
typedef interface HRNResolverConfig;

typedef dictionary HumanReadableNamesConfig;

typedef dictionary ForwardedPaymentDetails;

typedef dictionary ChannelForwardingStats;

typedef dictionary ChannelPairForwardingStats;

typedef dictionary ForwardedPaymentDetailsPage;

typedef dictionary ChannelForwardingStatsPage;

typedef dictionary ChannelPairForwardingStatsPage;
82 changes: 61 additions & 21 deletions src/builder.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,9 @@ use crate::io::utils::{
#[cfg(feature = "storage-vss")]
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
Expand All@@ -89,6 +91,7 @@ use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
use crate::payment::forwarding_store::ForwardingStore;
#[cfg(feature = "unified-payments")]
use crate::payment::HRNResolver;
use crate::peer_store::PeerStore;
Expand DownExpand Up@@ -1524,26 +1527,37 @@ fn build_with_store_internal(

let kv_store_ref = Arc::clone(&kv_store);
let logger_ref = Arc::clone(&logger);
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref)
)
});
let (
payment_store_res,
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_all_objects(
&*kv_store_ref,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});

// Initialize the status fields.
let node_metrics = match node_metris_res {
Expand DownExpand Up@@ -1576,6 +1590,14 @@ fn build_with_store_internal(
},
};

let channel_forwarding_stats = match channel_forwarding_stats_res {
Ok(stats) => stats,
Err(e) => {
log_error!(logger, "Failed to read channel forwarding stats from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let (chain_source, chain_tip_opt) = match chain_data_source_config {
#[cfg(feature = "chain-esplora")]
Some(ChainDataSourceConfig::Esplora { server_url, headers, sync_config }) => {
Expand DownExpand Up@@ -1902,6 +1924,12 @@ fn build_with_store_internal(
Arc::clone(&wallet),
Arc::clone(&logger),
));
let forwarding_store = Arc::new(ForwardingStore::new(
channel_forwarding_stats,
config.forwarded_payment_tracking_mode,
Arc::clone(&kv_store),
Arc::clone(&logger),
));

let peer_storage_key = keys_manager.get_peer_storage_key();
let monitor_reader = Arc::new(AsyncPersister::new(
Expand DownExpand Up@@ -2457,6 +2485,16 @@ fn build_with_store_internal(
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
}

// How long detail records are kept before being folded into channel-pair buckets. `Stats` keeps
// none of its own, and only drains records a previous `Detailed` configuration left behind.
let forwarded_payment_aggregation_retention_secs = match config.forwarded_payment_tracking_mode
{
crate::config::ForwardedPaymentTrackingMode::Detailed => {
crate::payment::forwarding_store::FORWARDED_PAYMENT_AGGREGATION_BUCKET_SIZE_SECS
},
crate::config::ForwardedPaymentTrackingMode::Stats => 0,
};

Ok(Node {
runtime,
stop_sender,
Expand DownExpand Up@@ -2484,6 +2522,8 @@ fn build_with_store_internal(
scorer,
peer_store,
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
lnurl_auth,
is_running,
node_metrics,
Expand Down
32 changes: 30 additions & 2 deletions src/config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -169,6 +169,30 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
// thereafter until every configured LSP has been discovered.
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);

/// The mode used for tracking forwarded payments.
///
/// In either mode, a forward is tracked only when it has exactly one incoming HTLC and one outgoing
/// HTLC, and LDK reports both the outbound amount and total fee.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ForwardedPaymentTrackingMode {
/// Track eligible new forwarded payments only as per-channel aggregate statistics.
///
/// Any detailed records left by a previous configuration are aggregated and removed after their
/// current one-hour bucket closes.
Stats,
/// Store eligible individual forwarded payments for the current and previous one-hour buckets.
///
/// Payments from older buckets are aggregated into channel-pair statistics and removed.
Detailed,
}

impl Default for ForwardedPaymentTrackingMode {
fn default() -> Self {
Self::Stats
}
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// Represents the configuration of an [`Node`] instance.
Expand All@@ -192,9 +216,10 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_
doc = "| `hrn_config` | HumanReadableNamesConfig::default() |"
)]
/// | `manually_handle_unknown_bolt11_payments` | false |
/// | `forwarded_payment_tracking_mode` | Stats |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`RouteParametersConfig`], and
/// [`ForwardedPaymentTrackingMode`] for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand DownExpand Up@@ -268,6 +293,8 @@ pub struct Config {
///
/// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable
pub manually_handle_unknown_bolt11_payments: bool,
/// The mode used for tracking forwarded payments.
pub forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode,
}

impl Default for Config {
Expand All@@ -286,6 +313,7 @@ impl Default for Config {
#[cfg(feature = "unified-payments")]
hrn_config: HumanReadableNamesConfig::default(),
manually_handle_unknown_bolt11_payments: false,
forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode::default(),
}
}
}
Expand Down
Loading
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add storage for forwarded payments by benthecarman · Pull Request #772 · lightningdevkit/ldk-node · GitHub
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions Cargo.toml
Original file line numberDiff line numberDiff line change
Expand Up@@ -86,18 +86,18 @@ uniffi-default = [
#lightning-macros = { version = "0.2.0" }
#lightning-dns-resolver = { version = "0.3.0" }

lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", optional = true }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-types = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-invoice = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-net-tokio = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"], optional = true }
lightning-background-processor = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-rapid-gossip-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-block-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["rest-client", "rpc-client", "tokio"], optional = true }
lightning-transaction-sync = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }
lightning-liquidity = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std"] }
lightning-macros = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a" }
lightning-dns-resolver = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", optional = true }

bdk_chain = { version = "0.23.3", default-features = false, features = ["std"] }
bdk_esplora = { version = "0.22.2", default-features = false, features = ["async-https-rustls", "tokio"], optional = true }
Expand DownExpand Up@@ -132,14 +132,14 @@ postgres-native-tls = { version = "0.5", default-features = false, features = ["
vss-client = { package = "vss-client-ng", version = "0.6", optional = true }
prost = { version = "0.11.6", default-features = false, optional = true}
#bitcoin-payment-instructions = { version = "0.6" }
bitcoin-payment-instructions = { git = "https://github.com/tnull/bitcoin-payment-instructions", rev = "1d6ffaa8962391ddc84aeef98bd7439e55ccae9d", optional = true }
bitcoin-payment-instructions = { git = "https://github.com/benthecarman/bitcoin-payment-instructions", rev = "224b07fcc45a687bd56d8a9bded1e837d3d1bc37", optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["winbase"] }

[dev-dependencies]
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "9174965af9437196c527a9aa0df36bbcf050c8bb", features = ["tokio"] }
lightning = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["std", "_test_utils"] }
lightning-persister = { git = "https://github.com/lightningdevkit/rust-lightning", rev = "22cdec713c9c2f077d4e9f51c8f5cd615300085a", features = ["tokio"] }
rand = { version = "0.9.2", default-features = false, features = ["std", "thread_rng", "os_rng"] }
proptest = "1.0.0"
regex = "1.5.6"
Expand Down
25 changes: 25 additions & 0 deletions bindings/ldk_node.udl
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,8 @@ typedef dictionary ElectrumSyncConfig;

typedef dictionary TorConfig;

typedef enum ForwardedPaymentTrackingMode;

typedef interface NodeEntropy;

typedef interface ProbingConfig;
Expand DownExpand Up@@ -68,6 +70,7 @@ interface Node {
SpontaneousPayment spontaneous_payment();
OnchainPayment onchain_payment();
Liquidity liquidity();
ForwardingAnalytics forwarding_analytics();
[Throws=NodeError]
void lnurl_auth(string lnurl);
[Throws=NodeError]
Expand DownExpand Up@@ -139,6 +142,8 @@ interface FeeRate {

typedef interface Liquidity;

typedef interface ForwardingAnalytics;

[Error]
enum NodeError {
"AlreadyRunning",
Expand DownExpand Up@@ -177,6 +182,8 @@ enum NodeError {
"InvalidOfferId",
"InvalidNodeId",
"InvalidPaymentId",
"InvalidForwardedPaymentId",
"InvalidChannelPairForwardingStatsId",
"InvalidPaymentHash",
"InvalidPaymentPreimage",
"InvalidPaymentSecret",
Expand DownExpand Up@@ -358,6 +365,12 @@ typedef string OfferId;
[Custom]
typedef string PaymentId;

[Custom]
typedef string ForwardedPaymentId;

[Custom]
typedef string ChannelPairForwardingStatsId;

[Custom]
typedef string PaymentHash;

Expand DownExpand Up@@ -395,3 +408,15 @@ typedef enum Event;
typedef interface HRNResolverConfig;

typedef dictionary HumanReadableNamesConfig;

typedef dictionary ForwardedPaymentDetails;

typedef dictionary ChannelForwardingStats;

typedef dictionary ChannelPairForwardingStats;

typedef dictionary ForwardedPaymentDetailsPage;

typedef dictionary ChannelForwardingStatsPage;

typedef dictionary ChannelPairForwardingStatsPage;
82 changes: 61 additions & 21 deletions src/builder.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -80,7 +80,9 @@ use crate::io::utils::{
#[cfg(feature = "storage-vss")]
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
};
Expand All@@ -89,6 +91,7 @@ use crate::lnurl_auth::LnurlAuth;
use crate::logger::{log_error, LdkLogger, LogLevel, LogWriter, Logger};
use crate::message_handler::NodeCustomMessageHandler;
use crate::payment::asynchronous::om_mailbox::OnionMessageMailbox;
use crate::payment::forwarding_store::ForwardingStore;
#[cfg(feature = "unified-payments")]
use crate::payment::HRNResolver;
use crate::peer_store::PeerStore;
Expand DownExpand Up@@ -1524,26 +1527,37 @@ fn build_with_store_internal(

let kv_store_ref = Arc::clone(&kv_store);
let logger_ref = Arc::clone(&logger);
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref)
)
});
let (
payment_store_res,
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
read_n_objects(
&*kv_store_ref,
PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PAYMENT_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_all_objects(
&*kv_store_ref,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_node_metrics(&*kv_store_ref, Arc::clone(&logger_ref)),
read_all_objects(
&*kv_store_ref,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});

// Initialize the status fields.
let node_metrics = match node_metris_res {
Expand DownExpand Up@@ -1576,6 +1590,14 @@ fn build_with_store_internal(
},
};

let channel_forwarding_stats = match channel_forwarding_stats_res {
Ok(stats) => stats,
Err(e) => {
log_error!(logger, "Failed to read channel forwarding stats from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let (chain_source, chain_tip_opt) = match chain_data_source_config {
#[cfg(feature = "chain-esplora")]
Some(ChainDataSourceConfig::Esplora { server_url, headers, sync_config }) => {
Expand DownExpand Up@@ -1902,6 +1924,12 @@ fn build_with_store_internal(
Arc::clone(&wallet),
Arc::clone(&logger),
));
let forwarding_store = Arc::new(ForwardingStore::new(
channel_forwarding_stats,
config.forwarded_payment_tracking_mode,
Arc::clone(&kv_store),
Arc::clone(&logger),
));

let peer_storage_key = keys_manager.get_peer_storage_key();
let monitor_reader = Arc::new(AsyncPersister::new(
Expand DownExpand Up@@ -2457,6 +2485,16 @@ fn build_with_store_internal(
_leak_checker.0.push(Arc::downgrade(&wallet) as Weak<dyn Any + Send + Sync>);
}

// How long detail records are kept before being folded into channel-pair buckets. `Stats` keeps
// none of its own, and only drains records a previous `Detailed` configuration left behind.
let forwarded_payment_aggregation_retention_secs = match config.forwarded_payment_tracking_mode
{
crate::config::ForwardedPaymentTrackingMode::Detailed => {
crate::payment::forwarding_store::FORWARDED_PAYMENT_AGGREGATION_BUCKET_SIZE_SECS
},
crate::config::ForwardedPaymentTrackingMode::Stats => 0,
};

Ok(Node {
runtime,
stop_sender,
Expand DownExpand Up@@ -2484,6 +2522,8 @@ fn build_with_store_internal(
scorer,
peer_store,
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
lnurl_auth,
is_running,
node_metrics,
Expand Down
32 changes: 30 additions & 2 deletions src/config.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -169,6 +169,30 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_INITIAL_DELAY: Duration = Duration::f
// thereafter until every configured LSP has been discovered.
pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_secs(60 * 60);

/// The mode used for tracking forwarded payments.
///
/// In either mode, a forward is tracked only when it has exactly one incoming HTLC and one outgoing
/// HTLC, and LDK reports both the outbound amount and total fee.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
pub enum ForwardedPaymentTrackingMode {
/// Track eligible new forwarded payments only as per-channel aggregate statistics.
///
/// Any detailed records left by a previous configuration are aggregated and removed after their
/// current one-hour bucket closes.
Stats,
/// Store eligible individual forwarded payments for the current and previous one-hour buckets.
///
/// Payments from older buckets are aggregated into channel-pair statistics and removed.
Detailed,
}

impl Default for ForwardedPaymentTrackingMode {
fn default() -> Self {
Self::Stats
}
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
/// Represents the configuration of an [`Node`] instance.
Expand All@@ -192,9 +216,10 @@ pub(crate) const LIQUIDITY_DISCOVERY_RETRY_MAX_DELAY: Duration = Duration::from_
doc = "| `hrn_config` | HumanReadableNamesConfig::default() |"
)]
/// | `manually_handle_unknown_bolt11_payments` | false |
/// | `forwarded_payment_tracking_mode` | Stats |
///
/// See [`AnchorChannelsConfig`] and [`RouteParametersConfig`] for more information regarding their
/// respective default values.
/// See [`AnchorChannelsConfig`], [`RouteParametersConfig`], and
/// [`ForwardedPaymentTrackingMode`] for more information regarding their respective default values.
///
/// [`Node`]: crate::Node
pub struct Config {
Expand DownExpand Up@@ -268,6 +293,8 @@ pub struct Config {
///
/// [`Event::PaymentClaimable`]: crate::Event::PaymentClaimable
pub manually_handle_unknown_bolt11_payments: bool,
/// The mode used for tracking forwarded payments.
pub forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode,
}

impl Default for Config {
Expand All@@ -286,6 +313,7 @@ impl Default for Config {
#[cfg(feature = "unified-payments")]
hrn_config: HumanReadableNamesConfig::default(),
manually_handle_unknown_bolt11_payments: false,
forwarded_payment_tracking_mode: ForwardedPaymentTrackingMode::default(),
}
}
}
Expand Down
Loading
Loading