Skip to content
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

Add compact block filter client via Kyoto #591

Merged
merged 1 commit into from
Mar 6, 2025

Conversation

rustaceanrob
Copy link
Collaborator

@rustaceanrob rustaceanrob commented Sep 17, 2024

Description

Adds a compact block filter backend via Kyoto.

Notes to the reviewers

This description has been updated to the latest forced push as of Jan 29

Architectural notes:

The language semantics of Rust make it difficult to access the same type in two different place within a project. Due to this the bdk_kyoto "client" is separated into smaller component types that handle logs, warnings, and wallet updates. Because the target languages for the bindings are garbage collected/reference counted, I think it is an advantage to unite all of these parts into a single Client type. When a user builds a new node, they receive a LightNode and Client.

The LightNode must be run to do anything, and a user would do so with LightNode.run().

The Client is responsible for everything else:

  • Getting log messages with next_log
  • Getting warnings
  • Getting wallet Update
  • Asking for the minimum fee rate to broadcast a transaction
  • Adding new scripts
  • Broadcasting transactions

Taking into account previous conversations, this was conveyed as a more intuitive approach than using callbacks to handle events.

Some additions in particular:

  • The client can add all revealed scripts with add_revealed_scripts that uses a &Wallet. This would be called after a user creates a transaction or reveals a receiving address
  • There is a simple is_running function to check on the node
  • Developers must pick between three different ScanType. This is to prevent dangerous guess-work when building the client. When creating a new wallet, all that should occur is a quick sync to add the tip of the blockchain and addition of this block hash to the wallet database. This is accomplished with ScanType::New. Recoveries are handled with ScanType::Recovery where the user can select a particular height to scan from. Lastly, syncing from the last known block hash by the wallet is accomplished with ScanType::Sync, which is the default.

Changelog notice

  • Add a compact block filter chain source

Checklists

All Submissions:

  • I've signed all my commits
  • I followed the contribution guidelines
  • I ran cargo fmt and cargo clippy before committing

New Features:

  • I've added tests for the new feature
  • I've added docs for the new feature

Bugfixes:

  • This pull request breaks the existing API
  • I've added tests to reproduce the issue which are now passing
  • I'm linking the issue being fixed by this PR

@thunderbiscuit
Copy link
Member

Just for fun:

Binary size when including Kyoto, building the aarch64 binary on macOS using the release-smaller profile (libbdkffi.dylib): 9.7MB
Binary size without Kyoto, building the aarch64 binary on macOS using the release-smaller profile (libbdkffi.dylib): 8.6MB

Not bad at all for what is likely to be one of the most popular clients one day.

@rustaceanrob
Copy link
Collaborator Author

Awesome thank you for checking that out. Props to rust-bitcoin and tokio for keeping things tidy.

@rustaceanrob
Copy link
Collaborator Author

Objects cannot currently be used in enum variant data

You can imagine my disappointment with this error message after hacking on this PR again. I'm trying to express the new Event<K> from bdk_kyoto but it returns objects like Update (FullScanResult) as a variant. We can return more primitive types as variants, but then we can't express the Update. It doesn't make sense to express an event as anything else than an enum, as only one variant is used at a time to express the event.

It's been so long but maybe that is what I had in mind with the callback interface in the first place lol. Open to new ideas, but until we can return objects from enum variants I think we may have to stick with callbacks...

@rustaceanrob rustaceanrob force-pushed the kyoto-bindings branch 4 times, most recently from f57905a to 4609fe1 Compare November 14, 2024 18:23
@rustaceanrob
Copy link
Collaborator Author

I consider this ready for review now. Some updates, I added live tests for android, JVM, swift, and python. Users now have to build a Peer with an IpAddress if they have a company or personal node they want to connect to. NodeMessageHandler is renamed to NodeEventHandler, which reflects a bit more what is happening. Some open questions I have:

Do we really need a separate live test in android when the JVM one exists? It's basically the exact same code and it would likely just increase the maintenance burden for no real benefit IMO.

Should I add docstrings? I think so, but I wanted to confirm

@rustaceanrob rustaceanrob marked this pull request as ready for review November 14, 2024 18:59
@thunderbiscuit
Copy link
Member

To answer your questions @rustaceanrob:

  1. No I don't think we need a live test on Android if we have one on JVM (moreso because the Android emulators don't currently work in the CI!)
  2. Yes let's add docstrings.

Awesome! I'll try to review this again next week.

@rustaceanrob rustaceanrob force-pushed the kyoto-bindings branch 7 times, most recently from 8861ae8 to 31653bd Compare November 16, 2024 20:47
Copy link
Member

@thunderbiscuit thunderbiscuit left a comment

Choose a reason for hiding this comment

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

Just writing down some stuff as I build and test.

@thunderbiscuit
Copy link
Member

thunderbiscuit commented Dec 3, 2024

I got it working on the Android example wallet! So far so good. I'll take a closer look at the code before giving the final ACK.

My first little API-related comment is that the NodeEventHandler doesn't have an unique event for a new block being mined but that's a significant event from the point of view of the user, particularly because they loose mempool information when using Kyoto (if blocks pass and the transaction they expect doesn't get mined they need to look into it).

At the moment this is what I do to pull the new blocks out of the events:

override fun dialog(dialog: String) {
    if (dialog.contains("peer height")) {
        val height = dialog.split("peer height: ")[1].split(" ")[0]
        triggerSnackbar("New block mined: $height")
    }
}

Which is basically a custom parsing of the string just to get what I need. I wonder if a newBlock() method on the interface would be cleaner.

Just to showcase my use of it, here is the simple version of the idea, where a snackbar shows when new blocks are mined:

new-blocks.mp4

@rustaceanrob
Copy link
Collaborator Author

rustaceanrob commented Dec 3, 2024

The best known peer height is found when connecting to a peer, but our height may not yet match theirs yet. The Synced event is emitted every time we sync to their height, which includes when they advertise a new block to us. In effect, I believe the Synced event is more accurate as to what height the client is synced to. However, I did add a recent Progress event that does contain the best known height of remote peers, so the user has an idea of how many filters and filter headers must be downloaded until synced. I will add that in the next kyoto and bdk_kyoto releases and update this PR along with some other improvements.

For reference, I believe you can achieve the same effect in terms of the client height by using NodeEventHandler::synced and updating the toast bar

@thunderbiscuit
Copy link
Member

Oh sweet yes I had not looked at all the methods carefully enough. It was easy to update the app to use that instead. 👍

@rustaceanrob rustaceanrob force-pushed the kyoto-bindings branch 2 times, most recently from 5ca066c to d1b98fd Compare January 30, 2025 19:30
@thunderbiscuit
Copy link
Member

Small note: the Android library is missing a dependency on the kotlinx-coroutine-core library.

implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")

@rustaceanrob rustaceanrob force-pushed the kyoto-bindings branch 2 times, most recently from 3cb4250 to 54fc561 Compare February 5, 2025 18:24
@rustaceanrob
Copy link
Collaborator Author

rustaceanrob commented Feb 5, 2025

Added to bdk-android/lib/build-gradle.kts. Not sure if that is correct

@thunderbiscuit
Copy link
Member

thunderbiscuit commented Feb 28, 2025

With the cut of the release/1.1 branch, I don't think we'll be merging anything else that'll make it in before the big release. If we end up needing to add anything to the release, we'll merge those commits right on the release branch and cherry-pick on master if needed.

This means master is free to keep going at its own pace and I think this PR is ready to merge (again it will not make it into the 1.1 but rather in 1.2).

No real rush, but also I feel like @rustaceanrob has been really patient and rebasing every few days... I have this working well in my variant/kyoto app (which needs a big rebase on the esplora variant which has added a bunch of cleanups and features, but works well nonetheless!).

@reez what do you say? Have you had a chance to test it out a bit?
@rustaceanrob is this a version good to merge? Or are you expecting newer/better features in the next few days/weeks and would rather polish those up first?

@rustaceanrob
Copy link
Collaborator Author

rustaceanrob commented Feb 28, 2025

I can rebase this soon and I think it's ready to go. I would prefer to do follow-up patches for anything additional

edit: Rebased

Thanks to @thunderbiscuit for getting the work started initially.
Copy link
Member

@thunderbiscuit thunderbiscuit left a comment

Choose a reason for hiding this comment

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

ACK d154117.

@reez
Copy link
Collaborator

reez commented Mar 6, 2025

ACK d154117.

@thunderbiscuit thunderbiscuit merged commit d154117 into bitcoindevkit:master Mar 6, 2025
25 checks passed
@rustaceanrob rustaceanrob deleted the kyoto-bindings branch March 6, 2025 20:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add compact block filters blockchain support
3 participants