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
7 changes: 7 additions & 0 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1754,6 +1754,13 @@ fn build_with_store_internal(
Arc::clone(&pending_payment_store),
));

// Fill the address pool up front so LDK's sync `SignerProvider` callbacks can hand out
// pre-persisted addresses without waiting on wallet persistence.
runtime.block_on(wallet.initialize_address_pool()).map_err(|e| {
log_error!(logger, "Failed to initialize the wallet's address pool: {}", e);
BuildError::WalletSetupFailed
})?;

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

// Initialize the KeysManager
Expand Down
9 changes: 6 additions & 3 deletions src/chain/electrum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use lightning_transaction_sync::ElectrumSyncClient;

use super::WalletSyncStatus;
use crate::config::{
clamp_full_scan_stop_gap, Config, ElectrumSyncConfig, MAX_FULL_SCAN_STOP_GAP,
MIN_FULL_SCAN_STOP_GAP,
clamp_full_scan_stop_gap, Config, ElectrumSyncConfig, ADDRESS_POOL_SIZE,
MAX_FULL_SCAN_STOP_GAP, MIN_FULL_SCAN_STOP_GAP,
};
use crate::error::Error;
use crate::fee_estimator::{
Expand Down Expand Up @@ -598,7 +598,10 @@ impl ElectrumRuntimeClient {
bounded
);
}
bounded as usize
// Extend the gap by the address pool size: the pool keeps that many addresses standing
// revealed-but-unused, which a scan restoring the wallet from seed alone would otherwise
// count against the configured gap.
(bounded as usize).saturating_add(ADDRESS_POOL_SIZE as usize)
}

async fn get_incremental_sync_wallet_update(
Expand Down
7 changes: 5 additions & 2 deletions src/chain/esplora.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use lightning_transaction_sync::EsploraSyncClient;

use super::WalletSyncStatus;
use crate::config::{
clamp_full_scan_stop_gap, Config, EsploraSyncConfig, BDK_CLIENT_CONCURRENCY,
clamp_full_scan_stop_gap, Config, EsploraSyncConfig, ADDRESS_POOL_SIZE, BDK_CLIENT_CONCURRENCY,
MAX_FULL_SCAN_STOP_GAP, MIN_FULL_SCAN_STOP_GAP,
};
use crate::fee_estimator::{
Expand Down Expand Up @@ -256,7 +256,10 @@ impl EsploraChainSource {
bounded
);
}
bounded as usize
// Extend the gap by the address pool size: the pool keeps that many addresses standing
// revealed-but-unused, which a scan restoring the wallet from seed alone would otherwise
// count against the configured gap.
(bounded as usize).saturating_add(ADDRESS_POOL_SIZE as usize)
}

pub(super) async fn sync_lightning_wallet(
Expand Down
22 changes: 22 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ pub const MIN_FULL_SCAN_STOP_GAP: u32 = 1;
/// Values above 1000 are clamped to 1000 when a full scan runs.
pub const MAX_FULL_SCAN_STOP_GAP: u32 = 1000;

/// The number of addresses the node keeps revealed and persisted ahead of use, from which it
/// serves fresh-address requests and channel destination and shutdown scripts.
///
/// Pooled addresses are revealed-but-unused wallet scripts, and a wallet restored from seed
/// alone has no record of them. On-chain wallet full scans therefore extend the configured stop
/// gap (e.g. [`EsploraSyncConfig::full_scan_stop_gap`]) by this amount, so that the pool's
/// unused tail can never exhaust the gap on its own.
///
/// After a restore from seed, the pool refills from the keychain's first indices before the
/// initial full scan runs, so it can serve addresses a previous installation of the wallet
/// already handed out — possibly even used ones. The cost is address reuse, not fund
/// visibility: the reveals keep the scripts watched and the scan discovers any prior use.
pub const ADDRESS_POOL_SIZE: u32 = 16;

// The number of concurrent requests made against the API provider.
pub(crate) const BDK_CLIENT_CONCURRENCY: usize = 4;

Expand Down Expand Up @@ -550,6 +564,10 @@ pub struct EsploraSyncConfig {
/// ([`MAX_FULL_SCAN_STOP_GAP`]), inclusive. Values outside this range will be clamped to the
/// nearest bound and a warning will be logged when the full scan runs.
///
/// The scan extends this value by [`ADDRESS_POOL_SIZE`] to account for the addresses the
/// node keeps revealed-but-unused ahead of use, which would otherwise count against the gap
/// when restoring a wallet from seed.
///
/// **Note:** Large values can cause many Esplora requests, hit server rate limits,
/// take a long time to complete, or cause syncs to fail with
/// [`SyncTimeoutsConfig::onchain_wallet_sync_timeout_secs`].
Expand Down Expand Up @@ -601,6 +619,10 @@ pub struct ElectrumSyncConfig {
/// ([`MAX_FULL_SCAN_STOP_GAP`]), inclusive. Values outside this range will be clamped to the
/// nearest bound and a warning will be logged when the full scan runs.
///
/// The scan extends this value by [`ADDRESS_POOL_SIZE`] to account for the addresses the
/// node keeps revealed-but-unused ahead of use, which would otherwise count against the gap
/// when restoring a wallet from seed.
///
/// **Note:** Large values can cause many Electrum requests, hit server rate limits,
/// take a long time to complete, or cause syncs to fail with
/// [`SyncTimeoutsConfig::onchain_wallet_sync_timeout_secs`].
Expand Down
5 changes: 5 additions & 0 deletions src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ pub(crate) const BDK_WALLET_INDEXER_PRIMARY_NAMESPACE: &str = "bdk_wallet";
pub(crate) const BDK_WALLET_INDEXER_SECONDARY_NAMESPACE: &str = "";
pub(crate) const BDK_WALLET_INDEXER_KEY: &str = "indexer";

/// The derivation indices of the wallet's address pool will be persisted under this key.
pub(crate) const BDK_WALLET_ADDRESS_POOL_PRIMARY_NAMESPACE: &str = "bdk_wallet";
pub(crate) const BDK_WALLET_ADDRESS_POOL_SECONDARY_NAMESPACE: &str = "";
pub(crate) const BDK_WALLET_ADDRESS_POOL_KEY: &str = "address_pool";

/// [`StaticInvoice`]s will be persisted under this key.
///
/// [`StaticInvoice`]: lightning::offers::static_invoice::StaticInvoice
Expand Down
Loading
Loading