Skip to content

Commit d7ec0cf

Browse files
authored
Merge pull request #1011 from jkczyz/2026-07-signer-blockon-deadlock
Derive shutdown scripts without blocking on wallet persistence
2 parents 5e3ffd5 + d8f1b9d commit d7ec0cf

6 files changed

Lines changed: 1497 additions & 22 deletions

File tree

src/builder.rs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ use crate::types::{
8686
GossipSync, Graph, HRNResolver, KeysManager, MessageRouter, OnionMessenger, PaymentStore,
8787
PeerManager, PendingPaymentStore,
8888
};
89-
use crate::wallet::persist::KVStoreWalletPersister;
89+
use crate::wallet::persist::{read_address_pool, KVStoreWalletPersister};
9090
use crate::wallet::Wallet;
9191
use crate::{Node, NodeMetrics, PersistedNodeMetrics};
9292

@@ -1455,8 +1455,8 @@ fn build_with_store_internal(
14551455

14561456
let kv_store_ref = Arc::clone(&kv_store);
14571457
let logger_ref = Arc::clone(&logger);
1458-
let (payment_store_res, node_metris_res, pending_payment_store_res) =
1459-
runtime.block_on(async move {
1458+
let (payment_store_res, node_metris_res, pending_payment_store_res, address_pool_res) = runtime
1459+
.block_on(async move {
14601460
tokio::join!(
14611461
read_all_objects(
14621462
&*kv_store_ref,
@@ -1470,7 +1470,8 @@ fn build_with_store_internal(
14701470
PENDING_PAYMENT_INFO_PERSISTENCE_PRIMARY_NAMESPACE,
14711471
PENDING_PAYMENT_INFO_PERSISTENCE_SECONDARY_NAMESPACE,
14721472
Arc::clone(&logger_ref),
1473-
)
1473+
),
1474+
read_address_pool(&*kv_store_ref, &*logger_ref)
14741475
)
14751476
});
14761477

@@ -1757,9 +1758,18 @@ fn build_with_store_internal(
17571758
},
17581759
};
17591760

1761+
let persisted_pool_indices = match address_pool_res {
1762+
Ok(indices) => indices,
1763+
Err(e) => {
1764+
log_error!(logger, "Failed to read address pool data from store: {}", e);
1765+
return Err(BuildError::ReadFailed);
1766+
},
1767+
};
1768+
17601769
let wallet = Arc::new(Wallet::new(
17611770
bdk_wallet,
17621771
wallet_persister,
1772+
persisted_pool_indices,
17631773
Arc::clone(&tx_broadcaster),
17641774
Arc::clone(&fee_estimator),
17651775
Arc::clone(&chain_source),
@@ -1770,6 +1780,13 @@ fn build_with_store_internal(
17701780
Arc::clone(&pending_payment_store),
17711781
));
17721782

1783+
// Fill the address pool up front so LDK's sync `SignerProvider` callbacks can hand out
1784+
// pre-persisted addresses without waiting on wallet persistence.
1785+
runtime.block_on(wallet.refill_address_pool()).map_err(|e| {
1786+
log_error!(logger, "Failed to fill the wallet's address pool: {}", e);
1787+
BuildError::WalletSetupFailed
1788+
})?;
1789+
17731790
tx_broadcaster.set_wallet(Arc::downgrade(&wallet));
17741791

17751792
// Initialize the KeysManager

src/config.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@ pub const MIN_FULL_SCAN_STOP_GAP: u32 = 1;
7979
/// Values above 1000 are clamped to 1000 when a full scan runs.
8080
pub const MAX_FULL_SCAN_STOP_GAP: u32 = 1000;
8181

82+
/// The number of addresses the node keeps revealed and persisted ahead of use, from which it
83+
/// serves fresh-address requests and channel destination and shutdown scripts.
84+
///
85+
/// Pooled addresses are revealed-but-unused wallet scripts. Addresses are handed out oldest
86+
/// first, which keeps the pool beyond the handed-out addresses, where it cannot hide funds
87+
/// from a full scan's stop gap (e.g. [`EsploraSyncConfig::full_scan_stop_gap`]). A handout
88+
/// that fails while a concurrent one proceeds can briefly leave a pooled address below a
89+
/// handed-out one; such inversions are bounded by the pool size and consumed by the next
90+
/// handouts.
91+
///
92+
/// After a restore from seed, the pool refills from the keychain's first indices before the
93+
/// initial full scan runs, so it can serve addresses a previous installation of the wallet
94+
/// already handed out — possibly even used ones. The cost is address reuse, not fund
95+
/// visibility: the reveals keep the scripts watched and the scan discovers any prior use.
96+
pub const ADDRESS_POOL_SIZE: u32 = 16;
97+
8298
// The number of concurrent requests made against the API provider.
8399
pub(crate) const BDK_CLIENT_CONCURRENCY: usize = 4;
84100

src/io/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,11 @@ pub(crate) const BDK_WALLET_INDEXER_PRIMARY_NAMESPACE: &str = "bdk_wallet";
8080
pub(crate) const BDK_WALLET_INDEXER_SECONDARY_NAMESPACE: &str = "";
8181
pub(crate) const BDK_WALLET_INDEXER_KEY: &str = "indexer";
8282

83+
/// The derivation indices of the wallet's address pool will be persisted under this key.
84+
pub(crate) const BDK_WALLET_ADDRESS_POOL_PRIMARY_NAMESPACE: &str = "bdk_wallet";
85+
pub(crate) const BDK_WALLET_ADDRESS_POOL_SECONDARY_NAMESPACE: &str = "";
86+
pub(crate) const BDK_WALLET_ADDRESS_POOL_KEY: &str = "address_pool";
87+
8388
/// [`StaticInvoice`]s will be persisted under this key.
8489
///
8590
/// [`StaticInvoice`]: lightning::offers::static_invoice::StaticInvoice

0 commit comments

Comments
 (0)