UkoeHB / monero

Monero: the secure, private, untraceable cryptocurrency
https://getmonero.org
Other
7 stars 4 forks source link

Async wallet scanner #23

Closed j-berman closed 4 months ago

j-berman commented 10 months ago

Ready for review.

I've observed the following speed-ups benchmarking versus wallet2:

Setup % faster than wallet2
Remote node, clearnet 50-60% faster
Remote node, tor 40-45% faster
Local node 25-35% faster

Benchmark results can vary widely based on setup (client/node internet speeds and machines). Source for the scanner used to benchmark.

How it works

Assume the user's wallet must start scanning blocks from height 5000.

  1. The scanner begins by launching 10 RPC requests in parallel to fetch chunks of blocks as follows:
request 0: { start_height: 5000, max_block_count: 20 }
request 1: { start_height: 5020, max_block_count: 20 }
...
request 9: { start_height: 5180, max_block_count: 20 }
  1. As soon as any single request completes, the wallet immediately parses that chunk.
    • This is all in parallel. For example, as soon as request 7 responds, the wallet immediately begins parsing that chunk in parallel to any other chunks it's already parsing.
  2. If a chunk does not include a total of max_block_count blocks, and the chunk is not the tip of the chain, this means there was a "gap" in the chunk request. The scanner launches another parallel RPC request to fill in the gap.
    • This gap can occur because the server will not return a chunk of blocks greater than 100mb or 20,000 txs via the /getblocks.bin RPC endpoint.
    • The gap is from (req.start_height + res.blocks.size()) to (req.start_height + req.max_block_count).
  3. As soon as the scanner finishes parsing the chunk, it immediately submits another parallel RPC request.
  4. In parallel, the scanner identifies a user's received (and spent) enotes in each chunk.
    • For example, as soon as request 7 responds and the wallet parses it, the wallet scans that chunk in parallel to any other chunks it's already scanning.
  5. Once a single chunk is fully scanned locally, the scanner launches a parallel task to fetch and scan the next chunk.
  6. Once the scanner reaches the tip of the chain (the terminal chunk), the scanner terminates.

Some technical highlights

Major TODO's for this PR

Other TODO's for this PR

Misc.

There are a few things from this PR I can PR to the Monero repo today in bite-sized PR's:

UkoeHB commented 10 months ago

Nice to see this coming along :)

j-berman commented 6 months ago

Probably worth holding off on a deeper review until it's ready, almost there :)

j-berman commented 5 months ago

Ready for review :)

Noting there are 2 significant aspects of this scanner that will need to be dealt with. I think it'll be easier to handle in follow-up PR's.

1. The subaddress lookahead.

2. Pool handling.

j-berman commented 5 months ago

To reviewers: I suggest going commit-by-commit and working up to the final async scanner.

10/13 of the commits are theoretically independent and can be PR'd separately.

If the design generally looks solid, I can divide it up into smaller PR's to ease review. Additionally we can PR the daemon RPC changes to monerod at any time, so that daemons would be prepared for this scanner well in advance of releasing the Seraphis lib in a functional wallet to end-users.

UkoeHB commented 5 months ago

I think it would make sense to re-structure the Seraphis lib to simply do a single "scanning pass" (scan chain then pool).

I intentionally added a follow-up pass because there is a race condition between chain scanning and pool scanning that can cause mined blocks to contain txs invisible to a wallet even after it claims to have scanned the pool.

j-berman commented 5 months ago

Opened a separate issue to discuss since I don't think it needs to affect this PR: https://github.com/UkoeHB/monero/issues/41

UkoeHB commented 4 months ago

Once this is merged, please open as many PRs to monero core as possible to reduce the file overlap between this branch and core.

j-berman commented 2 months ago

Submitted PR's for the simple modifications to the core monero repo for the async scanner. If daemons run those changes, they'll be fully compatible with the async scanner in this PR.