Skip to content

Electrum: optimize merkle proof validation with batching #1908

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

Keerthi421
Copy link

This PR optimizes the Electrum client's performance by improving Merkle proof validation, addressing the significant performance regression in BDK 1.1.0 where full sync time increased from 4s to 26s.

Key improvements:

  • Implemented batch processing for Merkle proof validations
  • Added Merkle proof caching to prevent redundant network calls
  • Optimized header handling with pre-fetching and reuse
  • Modified core functions to use batch operations instead of individual calls

Notes to the reviewers

The optimization approach focuses on three main areas:

  1. Reducing network round trips through batched Merkle proof requests
  2. Minimizing redundant operations with a new Merkle proof cache
  3. Improving header handling efficiency with pre-fetching

The batch size is set to 100 as a balance between performance and memory usage. This value can be adjusted based on testing results.

Changelog notice

Added

  • New Merkle proof cache to prevent redundant network calls
  • Batch processing for Merkle proof validations
  • Performance tests to verify sync time improvements
    Solves issue Electrum client Performance issues #1891

@ValuedMammal
Copy link
Contributor

Concept ACK

In 3ef0c83:

I don't think rustup-init.exe should be included.

@@ -22,6 +25,8 @@ pub struct BdkElectrumClient<E> {
tx_cache: Mutex<HashMap<Txid, Arc<Transaction>>>,
/// The header cache
block_header_cache: Mutex<HashMap<u32, Header>>,
/// The Merkle proof cache
merkle_cache: Mutex<HashMap<(Txid, u32), GetMerkleRes>>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say a tx is confirmed in block of height 100 and hash A. We then cache it's merkle proof. Then a reorg happens and the tx is now in block of hash B (but still height 100).

With the current implementation, we will never fetch the merkle proof for block B because we already have a merkle proof cached for a txid at the same height as block B.

Is there a way to cache merkle proofs against (txid, blockhash)?

I apologize in advance for how bad the Electrum API is designed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evanlinjin I will work on it

Copy link
Author

@Keerthi421 Keerthi421 Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@evanlinjin , @notmandatory Please Review the changes made

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Keerthi421 thank you for your attempt but you have not addressed the problem.

See if a transaction with txid get's confirmed in a block of height:hash 100:A and then we do a sync. We will have an entry: (txid, 100) : merkle_res_for_txid_in_A.

If there is a reorg, and the block at height 100 is now A', the entry will stay as it is and we will never fetch an anchor for "txid in A'". This will mean that this transaction will appear to never confirm on the wallet (until we restart and clear the cache).

@Keerthi421
Copy link
Author

@notmandatory please review the changes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Needs Review
Development

Successfully merging this pull request may close these issues.

4 participants