Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
7c49f6d
Only adopt a funding payment's own transactions from wallet sync
jkczyz Aug 18, 2026
c3fae26
Fail funding payments lost to a confirmed conflict
jkczyz Sep 2, 2026
c49b223
f - Stop counting a funding round the wallet never saw as lost
jkczyz Oct 13, 2026
3205cf7
f - Say the counterparty has the round and the entry lists the funding
jkczyz Oct 13, 2026
18ea8a4
Add a channel transaction provenance store
tnull Sep 29, 2026
2f1d08a
f - Say which output a channel's funds sit in
jkczyz Oct 13, 2026
d3b01c6
Record channel transaction provenance from events
tnull Sep 29, 2026
7b7a9fd
f - Test the two answers to a failed facts write
jkczyz Oct 3, 2026
888a67e
f - Say what the sweeper tracks and how LDK derives a channel id
jkczyz Oct 13, 2026
95ca514
Classify transactions from recorded provenance
tnull Sep 29, 2026
4a88ecd
f - Test that a spent resolved output is named before a created fundi…
jkczyz Oct 3, 2026
fe96418
f - Say that recorded facts classify a transaction
jkczyz Oct 13, 2026
1b9d59b
Classify on-chain payments during wallet sync
tnull Sep 29, 2026
e99e7b1
f - Rename the sync-time naming of transactions to classifying them
jkczyz Oct 13, 2026
24fd0a0
Delete broadcast-time payment classification
tnull Sep 30, 2026
c245e57
f - Say that the payment kind lists the channels involved
jkczyz Oct 13, 2026
bcdb281
f - Call the renamed record_funding_payment in two tests
jkczyz Oct 13, 2026
0bfcbbe
Type the claims a channel pays straight to the wallet
jkczyz Oct 2, 2026
f81ba9a
f - Test the naming of a claim whose record graduated first
jkczyz Oct 3, 2026
f53a9a3
f - Rename the naming of a recorded transaction to classifying it
jkczyz Oct 13, 2026
08477aa
Gate on-chain RBF positively on a wallet-only tx
tnull Sep 29, 2026
acb58b4
f - Say a transaction is absent from the recorded facts
jkczyz Oct 13, 2026
1c242fa
Record a signed splice round before its signatures leave
jkczyz Sep 30, 2026
1a8b7a2
f - Rename the rounds LDK holds to the rounds LDK tracks
jkczyz Oct 13, 2026
6867b5b
Resolve funding payments when LDK discards a splice round
jkczyz Sep 30, 2026
7b0f06c
f - Say what tracks, lists or watches a round instead of "holds"
jkczyz Oct 13, 2026
57a2cf7
Prefer the record that owns a txid over one listing it as a conflict
jkczyz Sep 30, 2026
e7108d7
Have funding-record writers take a guard only the funding lock issues
jkczyz Sep 30, 2026
6b16e20
f - Wrap the signatures the tracked-round rename made too long
jkczyz Oct 13, 2026
9447b73
Put the wallet's payment stores behind an API that locks their writers
jkczyz Sep 30, 2026
6eba322
f - Wrap a test line the identifies-its-payment wording made too long
jkczyz Oct 13, 2026
841bb8d
Skip a replacement whose payment record is gone
tnull Sep 29, 2026
30ea913
f - Say that a round's facts identify its payment
jkczyz Oct 13, 2026
e1718ad
Drop recorded channel facts nothing needs anymore
tnull Sep 29, 2026
e480d32
f - Say what the pending-spender retention test exercises
jkczyz Oct 5, 2026
901f0fa
f - Say a channel is live instead of held, and lists instead of names
jkczyz Oct 13, 2026
2cf2538
Bound what the channel facts store may hold
tnull Sep 29, 2026
97dfc64
f - Say what the facts store contains instead of what it holds
jkczyz Oct 13, 2026
e723023
Record what the node's channel state holds when it starts
jkczyz Oct 3, 2026
93d8946
f - Make the two startup tests tell a no-op from an empty answer
jkczyz Oct 5, 2026
4ca97b6
f - Refuse an on-chain fee bump of a transaction that funds a channel
jkczyz Oct 13, 2026
c61c412
f - Rename held channel outputs to channel outputs
jkczyz Oct 13, 2026
e741529
f - Say which channel outputs the startup pass records
jkczyz Oct 13, 2026
5b9ce4e
Assign random PaymentIds to funding records
jkczyz Sep 30, 2026
ffd5df8
f - Say what LDK tracks and what the recorded facts identify
jkczyz Oct 13, 2026
ad6ba22
Record a splice's intent on its pending-payment entry
jkczyz Sep 30, 2026
368d596
f - Read a pending entry written before the splice intent was kept
jkczyz Oct 3, 2026
b74f2ec
f - Say what an entry carries instead of what it holds
jkczyz Oct 13, 2026
203b79e
Adopt the splice-time PaymentId when recording a splice
jkczyz Sep 30, 2026
bbcee21
f - Say that a replayed signing is a no-op and that LDK tracks the round
jkczyz Oct 13, 2026
2b2a907
Merge sync-created duplicates when recording a funding round
jkczyz Sep 30, 2026
1ca74db
f - Say a duplicate record was classified rather than named
jkczyz Oct 13, 2026
a40aeaf
Persist splice intents until the splice locks
jkczyz Oct 2, 2026
1075c80
f - Say what the wallet reserves and what LDK tracks for a splice
jkczyz Oct 13, 2026
5843d31
Abort a splice when funding signing fails
jkczyz Sep 30, 2026
2768728
f - Say the signed transaction can never be broadcast
jkczyz Oct 13, 2026
2d3b0d5
Add reason and splice parameters to splice failure events
jkczyz Sep 30, 2026
8bde4c7
Unlock lost splice inputs at startup
jkczyz Sep 30, 2026
23491fa
f - Say what the wallet reserves and LDK tracks at reconciliation
jkczyz Oct 13, 2026
4118001
Test splice failure surfacing and recovery
jkczyz Sep 30, 2026
6c31f40
f - Say that LDK tracks the splice in the failure tests
jkczyz Oct 13, 2026
0a9b2c0
Refuse to sign a round this node cannot measure
tnull Sep 30, 2026
5fd43ae
f - Admit the funding transaction this node builds past the cap
jkczyz Oct 13, 2026
4ef35d6
f - Say what the facts store contains and what a record describes
jkczyz Oct 13, 2026
d5c1897
Take back a splice intent its payment left behind
tnull Sep 29, 2026
b485ce2
Let the funding output alone identify a funding
tnull Sep 29, 2026
9d6658e
f - Say a funding output classifies and the payment lists channels
jkczyz Oct 13, 2026
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
57 changes: 52 additions & 5 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ use lightning_dns_resolver::OMDomainResolver;
use vss_client::headers::VssHeaderProvider;

use crate::chain::ChainSource;
use crate::channel::SpliceTracker;
#[cfg(feature = "chain-bitcoind")]
use crate::config::BitcoindRestClientConfig;
use crate::config::{
default_user_config, may_announce_channel, AnnounceError, AsyncPaymentsRole, Config,
ElectrumSyncConfig, EsploraSyncConfig, HRNResolverConfig, TorConfig,
CHANNEL_TX_FACTS_CACHE_CAPACITY, CHANNEL_TX_FACTS_CACHE_WARMUP_COUNT,
DEFAULT_ESPLORA_SERVER_URL, DEFAULT_LOG_FILENAME, DEFAULT_LOG_LEVEL,
DEFAULT_MAX_PROBE_AMOUNT_MSAT, DEFAULT_MIN_PROBE_AMOUNT_MSAT, PAYMENT_CACHE_CAPACITY,
PAYMENT_CACHE_WARMUP_COUNT,
Expand All @@ -83,6 +85,8 @@ use crate::io::utils::{
use crate::io::vss_store::VssStoreBuilder;
use crate::io::{
self, CHANNEL_FORWARDING_STATS_PERSISTENCE_SECONDARY_NAMESPACE,
CHANNEL_TX_FACTS_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_TX_FACTS_PERSISTENCE_SECONDARY_NAMESPACE,
FORWARDED_PAYMENT_PERSISTENCE_PRIMARY_NAMESPACE, PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
Expand All @@ -104,11 +108,12 @@ use crate::probing::{
use crate::runtime::{Runtime, RuntimeSpawner};
use crate::tx_broadcaster::TransactionBroadcaster;
use crate::types::{
AsyncPersister, ChainMonitor, ChannelManager, DynStore, DynStoreRef, DynStoreWrapper,
GossipSync, Graph, KeysManager, MessageRouter, OnionMessenger, PaymentStore, PeerManager,
PendingPaymentStore,
AsyncPersister, ChainMonitor, ChannelManager, ChannelTxFactsStore, DynStore, DynStoreRef,
DynStoreWrapper, GossipSync, Graph, KeysManager, MessageRouter, OnionMessenger, PaymentStore,
PeerManager, PendingPaymentStore,
};
use crate::wallet::persist::{read_address_pool, KVStoreWalletPersister};
use crate::wallet::provenance::NodeChannelLiveness;
use crate::wallet::Wallet;
use crate::{Node, NodeMetrics, PersistedNodeMetrics};

Expand Down Expand Up @@ -1564,6 +1569,7 @@ fn build_with_store_internal(
channel_forwarding_stats_res,
node_metris_res,
pending_payment_store_res,
channel_tx_facts_store_res,
address_pool_res,
) = runtime.block_on(async move {
tokio::join!(
Expand All @@ -1587,6 +1593,13 @@ fn build_with_store_internal(
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
Arc::clone(&logger_ref),
),
read_n_objects(
&*kv_store_ref,
CHANNEL_TX_FACTS_PERSISTENCE_PRIMARY_NAMESPACE,
CHANNEL_TX_FACTS_PERSISTENCE_SECONDARY_NAMESPACE,
CHANNEL_TX_FACTS_CACHE_WARMUP_COUNT,
Arc::clone(&logger_ref),
),
read_address_pool(&*kv_store_ref, &*logger_ref),
)
});
Expand Down Expand Up @@ -1918,6 +1931,24 @@ fn build_with_store_internal(
},
};

let channel_tx_facts_store = match channel_tx_facts_store_res {
Ok(channel_tx_facts) => Arc::new(ChannelTxFactsStore::new(
// The read hands us the newest records first, while the cache treats the objects it
// is seeded with as increasingly recently used. Reverse them, so that the newest
// record is the last one to be evicted rather than the first.
channel_tx_facts.into_iter().rev().collect(),
KeepLeastRecentlyUsed::new(CHANNEL_TX_FACTS_CACHE_CAPACITY),
CHANNEL_TX_FACTS_PERSISTENCE_PRIMARY_NAMESPACE.to_string(),
CHANNEL_TX_FACTS_PERSISTENCE_SECONDARY_NAMESPACE.to_string(),
Arc::clone(&kv_store),
Arc::clone(&logger),
)),
Err(e) => {
log_error!(logger, "Failed to read channel transaction facts from store: {}", e);
return Err(BuildError::ReadFailed);
},
};

let persisted_pool_indices = match address_pool_res {
Ok(indices) => indices,
Err(e) => {
Expand All @@ -1938,6 +1969,7 @@ fn build_with_store_internal(
Arc::clone(&config),
Arc::clone(&logger),
Arc::clone(&pending_payment_store),
Arc::clone(&channel_tx_facts_store),
));

// Fill the address pool up front so LDK's sync `SignerProvider` callbacks can hand out
Expand All @@ -1947,8 +1979,6 @@ fn build_with_store_internal(
BuildError::WalletSetupFailed
})?;

tx_broadcaster.set_wallet(Arc::downgrade(&wallet));

// Initialize the KeysManager
let cur_time = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).map_err(|e| {
log_error!(logger, "Failed to get current time: {}", e);
Expand Down Expand Up @@ -2429,6 +2459,15 @@ fn build_with_store_internal(
},
};

// The wallet drops the facts it recorded for a channel once that channel is not live
// anymore, and records on start what a live channel's producers never reported. Both need the
// node's channel state, which exists from this point on.
wallet.set_channel_liveness(Arc::new(NodeChannelLiveness::new(
&channel_manager,
&chain_monitor,
&output_sweeper,
)));

let event_queue = match event_queue_res {
Ok(event_queue) => Arc::new(event_queue),
Err(e) => {
Expand Down Expand Up @@ -2512,6 +2551,13 @@ fn build_with_store_internal(
})
});

let splice_tracker = Arc::new(SpliceTracker::new(
Arc::clone(&channel_manager),
Arc::clone(&wallet),
Arc::clone(&pending_payment_store),
Arc::clone(&logger),
));

#[cfg(cycle_tests)]
let mut _leak_checker = crate::LeakChecker(Vec::new());
#[cfg(cycle_tests)]
Expand Down Expand Up @@ -2563,6 +2609,7 @@ fn build_with_store_internal(
payment_store,
forwarding_store,
forwarded_payment_aggregation_retention_secs,
splice_tracker,
lnurl_auth,
is_running,
node_metrics,
Expand Down
67 changes: 30 additions & 37 deletions src/chain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ use crate::config::ElectrumSyncConfig;
use crate::config::EsploraSyncConfig;
use crate::config::{BackgroundSyncConfig, Config, WALLET_SYNC_INTERVAL_MINIMUM_SECS};
use crate::fee_estimator::OnchainFeeEstimator;
use crate::logger::{log_debug, log_error, log_info, log_trace, LdkLogger, Logger};
use crate::logger::{log_debug, log_info, log_trace, LdkLogger, Logger};
use crate::runtime::Runtime;
use crate::tx_broadcaster::BroadcastPackage;
use crate::types::{Broadcaster, ChainMonitor, ChannelManager, DynStore, Sweeper, Wallet};
use crate::{Error, PersistedNodeMetrics};

Expand Down Expand Up @@ -562,52 +563,44 @@ impl ChainSource {
}
}

/// Hands the package to the configured chain source, parents before their child so a CPFP
/// package a chain source submits one transaction at a time is still accepted.
async fn broadcast(&self, package: BroadcastPackage) {
let package = package.into_sorted_transactions();
match &self.kind {
#[cfg(feature = "chain-esplora")]
ChainSourceKind::Esplora(esplora_chain_source) => {
esplora_chain_source.process_transaction_broadcast(package).await
},
#[cfg(feature = "chain-electrum")]
ChainSourceKind::Electrum(electrum_chain_source) => {
electrum_chain_source.process_transaction_broadcast(package).await
},
#[cfg(feature = "chain-bitcoind")]
ChainSourceKind::Bitcoind(bitcoind_chain_source) => {
bitcoind_chain_source.process_transaction_broadcast(package).await
},
}
}

pub(crate) async fn continuously_process_broadcast_queue(
&self, mut stop_tx_bcast_receiver: tokio::sync::watch::Receiver<()>,
) {
let mut receiver = self.tx_broadcaster.get_broadcast_queue().await;
loop {
let tx_bcast_logger = Arc::clone(&self.logger);
tokio::select! {
let package = tokio::select! {
// A stop request is polled first, so a queue that always has a package ready
// cannot starve it.
biased;
_ = stop_tx_bcast_receiver.changed() => {
log_debug!(
tx_bcast_logger,
self.logger,
"Stopping broadcasting transactions.",
);
return;
}
Some(next_package) = receiver.recv() => {
// Classify funding broadcasts into payment records before sending. If
// classification fails we skip the broadcast, since broadcasting a tx we
// failed to record would leave it on-chain without a payment.
let package = match self.tx_broadcaster.classify_package(next_package).await {
Ok(package) => package,
Err(e) => {
log_error!(
tx_bcast_logger,
"Skipping broadcast: failed to persist payment records: {:?}",
e,
);
continue;
},
};
let package = package.into_sorted_transactions();
match &self.kind {
#[cfg(feature = "chain-esplora")]
ChainSourceKind::Esplora(esplora_chain_source) => {
esplora_chain_source.process_transaction_broadcast(package).await
},
#[cfg(feature = "chain-electrum")]
ChainSourceKind::Electrum(electrum_chain_source) => {
electrum_chain_source.process_transaction_broadcast(package).await
},
#[cfg(feature = "chain-bitcoind")]
ChainSourceKind::Bitcoind(bitcoind_chain_source) => {
bitcoind_chain_source.process_transaction_broadcast(package).await
},
}
}
}
package = self.tx_broadcaster.next_package() => package,
};
self.broadcast(package).await;
}
}
}
Expand Down
Loading
Loading