Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
17d3ebb
Assign random PaymentIds to funding records
jkczyz Sep 30, 2026
d038d83
f - Assign random PaymentIds to funding records
jkczyz Oct 16, 2026
8ecc450
f - Assign random PaymentIds to funding records
jkczyz Oct 18, 2026
967325d
Record a splice's intent on its pending-payment entry
jkczyz Sep 30, 2026
7ad7e9b
f - Record a splice's intent on its pending-payment entry
jkczyz Oct 18, 2026
36a7f00
Adopt the splice-time PaymentId when recording a splice
jkczyz Sep 30, 2026
84ec635
f - Adopt the splice-time PaymentId when recording a splice
jkczyz Oct 18, 2026
579dc52
Merge sync-created duplicates when recording a funding round
jkczyz Sep 30, 2026
cdde509
f - Merge sync-created duplicates when recording a funding round
jkczyz Oct 16, 2026
d054fca
f - Merge sync-created duplicates when recording a funding round
jkczyz Oct 18, 2026
a686784
f - Merge sync-created duplicates when recording a funding round
jkczyz Oct 18, 2026
375dff3
Persist splice intents until the splice locks
jkczyz Oct 2, 2026
244f8f0
Abort a splice when funding signing fails
jkczyz Sep 30, 2026
16bb0e8
Add reason and splice parameters to splice failure events
jkczyz Sep 30, 2026
9aa40b2
Unlock lost splice inputs at startup
jkczyz Sep 30, 2026
49f66d8
Test splice failure surfacing and recovery
jkczyz Sep 30, 2026
9f48c20
Refuse to sign a round this node cannot measure
tnull Sep 30, 2026
2857678
Take back a splice intent its payment left behind
tnull Sep 29, 2026
9f474a0
Let the funding output alone identify a funding
tnull Sep 29, 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
9 changes: 9 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ 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::{
Expand Down Expand Up @@ -2550,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 @@ -2601,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
1,669 changes: 1,669 additions & 0 deletions src/channel/mod.rs

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,10 @@ pub(crate) const CHANNEL_TX_FACTS_MAX_RECORD_BYTES: usize = 128 * 1024;
// Records are dropped only once the channels they belong to have resolved, so between prunes a
// counterparty opening and closing channels, or replacing a negotiated funding again and again,
// drives the store's growth. Past this many records the store is full and the transactions
// it would have described go unclassified, which is bounded loss of detail rather than unbounded
// storage.
// it would have described go unclassified, which is bounded loss of detail rather than
// unbounded storage. An interactive funding round this node is about to sign is admitted beside
// them: how many of those there are is this node's own decision, and refusing one would cost a
// figure that is never corrected rather than a detail.
pub(crate) const CHANNEL_TX_FACTS_MAX_RECORDS: usize = 100_000;

// The number of pages of channel transaction provenance records one chain tip change examines.
Expand Down
50 changes: 50 additions & 0 deletions src/data_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,19 @@ where
Ok(Some(new_object))
}

/// Like [`Self::mutate`], but allows the transformation to await fallible reads.
///
/// The mutation lock remains held while `f` runs. This is useful when the new state must be
/// decided from an async read of another store without letting a concurrent writer invalidate
/// that decision. Callers must keep cross-store lock ordering consistent to avoid deadlocks.
pub(crate) async fn mutate_async<F, Fut>(&self, id: &SO::Id, f: F) -> Result<Option<SO>, Error>
where
F: FnOnce(Option<SO>) -> Fut,
Fut: Future<Output = Result<Option<SO>, Error>>,
{
self.mutate_with(id, f).await
}

/// Returns whether an object is stored under `id`.
pub(crate) async fn contains_key(&self, id: &SO::Id) -> Result<bool, Error> {
let _guard = self.mutation_lock.read().await;
Expand Down Expand Up @@ -1246,6 +1259,43 @@ mod tests {
assert_eq!(Some(expected), data_store.get(&id).await.unwrap());
}

#[tokio::test]
async fn mutate_async_awaits_fallible_reads() {
let store: Arc<DynStore> = Arc::new(DynStoreWrapper(InMemoryStore::new()));
let logger = Arc::new(TestLogger::new());
let id = TestObjectId { id: [42u8; 4] };
let other_id = TestObjectId { id: [43u8; 4] };
let existing_object = TestObject::new(id, [23u8; 3]);
let other_object = TestObject::new(other_id, [24u8; 3]);
let data_store: DataStore<TestObject, Arc<TestLogger>> = DataStore::new(
vec![existing_object],
KeepAllEntries,
TEST_PRIMARY_NAMESPACE.to_string(),
TEST_SECONDARY_NAMESPACE.to_string(),
Arc::clone(&store),
Arc::clone(&logger),
);
let other_store: DataStore<TestObject, Arc<TestLogger>> = DataStore::new(
vec![other_object],
KeepAllEntries,
"other_datastore_test_primary".to_string(),
"other_datastore_test_secondary".to_string(),
store,
logger,
);

let result = data_store
.mutate_async(&id, |existing| async move {
let mut updated = existing.unwrap();
updated.data = other_store.get(&other_id).await?.unwrap().data;
Ok(Some(updated))
})
.await;
let expected = TestObject::new(id, [24u8; 3]);
assert_eq!(Ok(Some(expected)), result);
assert_eq!(Some(expected), data_store.get(&id).await.unwrap());
}

#[tokio::test]
async fn mutate_runs_the_closure_without_the_cache_lock() {
let store: Arc<DynStore> = Arc::new(DynStoreWrapper(InMemoryStore::new()));
Expand Down
Loading
Loading