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
1 change: 1 addition & 0 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ interface Builder {
void set_node_alias(string node_alias);
[Throws=BuildError]
void set_async_payments_role(AsyncPaymentsRole? role);
void set_wallet_recovery_mode();
[Throws=BuildError]
Node build(NodeEntropy node_entropy);
[Throws=BuildError]
Expand Down
57 changes: 42 additions & 15 deletions src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ pub struct NodeBuilder {
async_payments_role: Option<AsyncPaymentsRole>,
runtime_handle: Option<tokio::runtime::Handle>,
pathfinding_scores_sync_config: Option<PathfindingScoresSyncConfig>,
recovery_mode: bool,
}

impl NodeBuilder {
Expand All @@ -258,6 +259,7 @@ impl NodeBuilder {
let log_writer_config = None;
let runtime_handle = None;
let pathfinding_scores_sync_config = None;
let recovery_mode = false;
Self {
config,
chain_data_source_config,
Expand All @@ -267,6 +269,7 @@ impl NodeBuilder {
runtime_handle,
async_payments_role: None,
pathfinding_scores_sync_config,
recovery_mode,
}
}

Expand Down Expand Up @@ -541,6 +544,16 @@ impl NodeBuilder {
Ok(self)
}

/// Configures the [`Node`] to resync chain data from genesis on first startup, recovering any
/// historical wallet funds.
///
/// This should only be set on first startup when importing an older wallet from a previously
/// used [`NodeEntropy`].
pub fn set_wallet_recovery_mode(&mut self) -> &mut Self {
self.recovery_mode = true;
self
}

/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
/// previously configured.
pub fn build(&self, node_entropy: NodeEntropy) -> Result<Node, BuildError> {
Expand Down Expand Up @@ -676,6 +689,7 @@ impl NodeBuilder {
self.liquidity_source_config.as_ref(),
self.pathfinding_scores_sync_config.as_ref(),
self.async_payments_role,
self.recovery_mode,
seed_bytes,
runtime,
logger,
Expand Down Expand Up @@ -916,6 +930,15 @@ impl ArcedNodeBuilder {
self.inner.write().unwrap().set_async_payments_role(role).map(|_| ())
}

/// Configures the [`Node`] to resync chain data from genesis on first startup, recovering any
/// historical wallet funds.
///
/// This should only be set on first startup when importing an older wallet from a previously
/// used [`NodeEntropy`].
pub fn set_wallet_recovery_mode(&self) {
self.inner.write().unwrap().set_wallet_recovery_mode();
}

/// Builds a [`Node`] instance with a [`SqliteStore`] backend and according to the options
/// previously configured.
pub fn build(&self, node_entropy: Arc<NodeEntropy>) -> Result<Arc<Node>, BuildError> {
Expand Down Expand Up @@ -1030,8 +1053,8 @@ fn build_with_store_internal(
gossip_source_config: Option<&GossipSourceConfig>,
liquidity_source_config: Option<&LiquiditySourceConfig>,
pathfinding_scores_sync_config: Option<&PathfindingScoresSyncConfig>,
async_payments_role: Option<AsyncPaymentsRole>, seed_bytes: [u8; 64], runtime: Arc<Runtime>,
logger: Arc<Logger>, kv_store: Arc<DynStore>,
async_payments_role: Option<AsyncPaymentsRole>, recovery_mode: bool, seed_bytes: [u8; 64],
runtime: Arc<Runtime>, logger: Arc<Logger>, kv_store: Arc<DynStore>,
) -> Result<Node, BuildError> {
optionally_install_rustls_cryptoprovider();

Expand Down Expand Up @@ -1225,19 +1248,23 @@ fn build_with_store_internal(
BuildError::WalletSetupFailed
})?;

if let Some(best_block) = chain_tip_opt {
// Insert the first checkpoint if we have it, to avoid resyncing from genesis.
// TODO: Use a proper wallet birthday once BDK supports it.
let mut latest_checkpoint = wallet.latest_checkpoint();
let block_id =
bdk_chain::BlockId { height: best_block.height, hash: best_block.block_hash };
latest_checkpoint = latest_checkpoint.insert(block_id);
let update =
bdk_wallet::Update { chain: Some(latest_checkpoint), ..Default::default() };
wallet.apply_update(update).map_err(|e| {
log_error!(logger, "Failed to apply checkpoint during wallet setup: {}", e);
BuildError::WalletSetupFailed
})?;
if !recovery_mode {
if let Some(best_block) = chain_tip_opt {
// Insert the first checkpoint if we have it, to avoid resyncing from genesis.
// TODO: Use a proper wallet birthday once BDK supports it.
let mut latest_checkpoint = wallet.latest_checkpoint();
let block_id = bdk_chain::BlockId {
height: best_block.height,
hash: best_block.block_hash,
};
latest_checkpoint = latest_checkpoint.insert(block_id);
let update =
bdk_wallet::Update { chain: Some(latest_checkpoint), ..Default::default() };
wallet.apply_update(update).map_err(|e| {
log_error!(logger, "Failed to apply checkpoint during wallet setup: {}", e);
BuildError::WalletSetupFailed
})?;
}
}
wallet
},
Expand Down
9 changes: 9 additions & 0 deletions src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ impl Wallet {
pub(crate) fn apply_mempool_txs(
&self, unconfirmed_txs: Vec<(Transaction, u64)>, evicted_txids: Vec<(Txid, u64)>,
) -> Result<(), Error> {
if unconfirmed_txs.is_empty() {
return Ok(());
}

let mut locked_wallet = self.inner.lock().unwrap();
locked_wallet.apply_unconfirmed_txs(unconfirmed_txs);
locked_wallet.apply_evicted_txs(evicted_txids);
Expand All @@ -149,6 +153,11 @@ impl Wallet {
Error::PersistenceFailed
})?;

self.update_payment_store(&mut *locked_wallet).map_err(|e| {
log_error!(self.logger, "Failed to update payment store: {}", e);
Error::PersistenceFailed
})?;

Ok(())
}

Expand Down
55 changes: 46 additions & 9 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,31 @@ pub(crate) fn setup_bitcoind_and_electrsd() -> (BitcoinD, ElectrsD) {
(bitcoind, electrsd)
}

pub(crate) fn random_chain_source<'a>(
bitcoind: &'a BitcoinD, electrsd: &'a ElectrsD,
) -> TestChainSource<'a> {
let r = rand::random_range(0..3);
match r {
0 => {
println!("Randomly setting up Esplora chain syncing...");
TestChainSource::Esplora(electrsd)
},
1 => {
println!("Randomly setting up Electrum chain syncing...");
TestChainSource::Electrum(electrsd)
},
2 => {
println!("Randomly setting up Bitcoind RPC chain syncing...");
TestChainSource::BitcoindRpcSync(bitcoind)
},
3 => {
println!("Randomly setting up Bitcoind REST chain syncing...");
TestChainSource::BitcoindRestSync(bitcoind)
},
_ => unreachable!(),
}
}

pub(crate) fn random_storage_path() -> PathBuf {
let mut temp_path = std::env::temp_dir();
let mut rng = rng();
Expand Down Expand Up @@ -292,6 +317,8 @@ pub(crate) struct TestConfig {
pub log_writer: TestLogWriter,
pub store_type: TestStoreType,
pub node_entropy: NodeEntropy,
pub async_payments_role: Option<AsyncPaymentsRole>,
pub recovery_mode: bool,
}

impl Default for TestConfig {
Expand All @@ -302,7 +329,16 @@ impl Default for TestConfig {

let mnemonic = generate_entropy_mnemonic(None);
let node_entropy = NodeEntropy::from_bip39_mnemonic(mnemonic, None);
TestConfig { node_config, log_writer, store_type, node_entropy }
let async_payments_role = None;
let recovery_mode = false;
TestConfig {
node_config,
log_writer,
store_type,
node_entropy,
async_payments_role,
recovery_mode,
}
}
}

Expand Down Expand Up @@ -359,13 +395,6 @@ pub(crate) fn setup_two_nodes_with_store(
}

pub(crate) fn setup_node(chain_source: &TestChainSource, config: TestConfig) -> TestNode {
setup_node_for_async_payments(chain_source, config, None)
}

pub(crate) fn setup_node_for_async_payments(
chain_source: &TestChainSource, config: TestConfig,
async_payments_role: Option<AsyncPaymentsRole>,
) -> TestNode {
setup_builder!(builder, config.node_config);
match chain_source {
TestChainSource::Esplora(electrsd) => {
Expand Down Expand Up @@ -417,7 +446,11 @@ pub(crate) fn setup_node_for_async_payments(
},
}

builder.set_async_payments_role(async_payments_role).unwrap();
builder.set_async_payments_role(config.async_payments_role).unwrap();

if config.recovery_mode {
builder.set_wallet_recovery_mode();
}

let node = match config.store_type {
TestStoreType::TestSyncStore => {
Expand All @@ -427,6 +460,10 @@ pub(crate) fn setup_node_for_async_payments(
TestStoreType::Sqlite => builder.build(config.node_entropy.into()).unwrap(),
};

if config.recovery_mode {
builder.set_wallet_recovery_mode();
}

node.start().unwrap();
assert!(node.status().is_running);
assert!(node.status().latest_fee_rate_cache_update_timestamp.is_some());
Expand Down
Loading
Loading