From 2b5a6d791de418b3827c6cab09314e7a34b8a99c Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Mon, 3 Nov 2025 10:45:14 -0500 Subject: [PATCH 01/21] ci: fix audit workflow --- .github/workflows/audit.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/audit.yaml b/.github/workflows/audit.yaml index 26dae50c..c88f4f2a 100644 --- a/.github/workflows/audit.yaml +++ b/.github/workflows/audit.yaml @@ -5,6 +5,7 @@ name: Rust Dependency Audit # Helps identify known security vulnerabilities in the dependency tree. on: + workflow_dispatch: push: paths: - '**/Cargo.toml' @@ -28,5 +29,5 @@ jobs: - name: "Run audit" run: | - cargo install cargo-audit + cargo install cargo-audit --locked cargo-audit audit From f1d81ece19273340fcfd8b4526e18ecb546a5ac1 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Mon, 3 Nov 2025 14:49:44 -0500 Subject: [PATCH 02/21] docs: populate changelog for 2.2.0 release --- CHANGELOG.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e782402c..7a8416f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,54 @@ # Changelog + Changelog information can also be found in each release's git tag (which can be viewed with `git tag -ln100 "v*"`), as well as on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases) page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [v2.2.0] + +This is version 2.2.0 of the BDK language bindings! This release uses the following Rust dependencies: + + - bdk_wallet 2.2.0 + - bdk_electrum 0.23.2 + - bdk_esplora 0.22.1 + - bdk_kyoto 0.15.2 + - rust-bitcoin 0.32.7 + - uniffi 0.29.4 + +### Added + + - Implement `Display` for `Script` [#813] + - Expose `Wallet::create_single` constructor on Wallet type [#825] + - Set `lookahead` on `Wallet::load` [#829] + - Expose `Wallet::apply_evicted_txs` method [#832] + - Add `Wallet::create_from_two_path_descriptor` constructor [#847] + - `next_log` and `LogLevel` are removed from `kyoto` [#849] + - Updates Android native libraries to use 16KB page sizes [#865] + - Expose `TxBuilder::exclude_unconfirmed` method [#870] + - Expose `TxBuilder::exclude_below_confirmations` method [#870] + - Expose `Wallet::insert_txout` [#884] + - Expose `Wallet::nmark_used` [#882] + - Add `Wallet::load_single` [#897] + - Expose `DerivationPath::master` , `DerivationPath::is_master` , `DerivationPath::len` , `DerivationPath::is_empty` [#893] + +### Changed + + - Throw errors rather than panics on faulty Esplora sync/full_scan operations [#863] + +[#813]: https://github.com/bitcoindevkit/bdk-ffi/pull/813 +[#825]: https://github.com/bitcoindevkit/bdk-ffi/pull/825 +[#829]: https://github.com/bitcoindevkit/bdk-ffi/pull/829 +[#832]: https://github.com/bitcoindevkit/bdk-ffi/pull/832 +[#847]: https://github.com/bitcoindevkit/bdk-ffi/pull/847 +[#849]: https://github.com/bitcoindevkit/bdk-ffi/pull/849 +[#863]: https://github.com/bitcoindevkit/bdk-ffi/pull/863 +[#865]: https://github.com/bitcoindevkit/bdk-ffi/pull/865 +[#870]: https://github.com/bitcoindevkit/bdk-ffi/pull/870 +[#884]: https://github.com/bitcoindevkit/bdk-ffi/pull/884 +[#882]: https://github.com/bitcoindevkit/bdk-ffi/pull/882 +[#893]: https://github.com/bitcoindevkit/bdk-ffi/pull/893 +[#897]: https://github.com/bitcoindevkit/bdk-ffi/pull/897 + ## [v2.0.0] This release brings bdk-ffi to it's 2.0.0 version! @@ -514,6 +560,7 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding +[v2.0.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v2.0.0...v2.2.0 [v2.0.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.2.0...v2.0.0 [v1.2.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.1.0...v1.2.0 [v1.1.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-beta.7...v1.1.0 From deca51636746bf891ee6437d4dccb5846a154249 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Mon, 3 Nov 2025 14:52:51 -0500 Subject: [PATCH 03/21] docs: fix release template --- .github/ISSUE_TEMPLATE/release.md | 41 +++++++++++++++---------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/release.md b/.github/ISSUE_TEMPLATE/release.md index 31dd4754..b639e0fb 100644 --- a/.github/ISSUE_TEMPLATE/release.md +++ b/.github/ISSUE_TEMPLATE/release.md @@ -6,17 +6,17 @@ labels: "release" assignees: "" --- -# Part 1: Bump BDK Rust Version +## Part 1: Bump BDK Rust Version 1. - [ ] Open a PR with an update to `Cargo.toml` to the new bdk release candidate and ensure all CI workflows run correctly. Fix errors if necessary. 2. - [ ] Once the new bdk release is out, update the PR to replace the release candidate with the full release and merge. -# Part 2: Prepare Libraries for Release Branch +## Part 2: Prepare Libraries for Release Branch ### _Android_ -3. - [ ] Delete all previous build artifacts in order to build and test the library from scratch. -4. - [ ] Build the library and run the tests (note that you'll need an Android emulator running). Adjust them if necessary. +3. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root and `lib`) in the bdk-android directory to make sure you're building the library from scratch. +4. - [ ] Build the library and run the offline and live tests, and adjust them if necessary (note that you'll need an Android emulator running). ```shell # start an emulator prior to running the tests cd ./bdk-android/ @@ -28,8 +28,8 @@ just test ### _Swift_ -6. - [ ] Delete all previous build artifacts in order to build and test the library from scratch. -7. - [ ] Build the library and run the tests. Adjust them if necessary. +6. - [ ] Delete the `target` directory in bdk-ffi. +7. - [ ] Run all offline and live tests and adjust them if necessary. ```shell cd ./bdk-swift/ just clean @@ -40,22 +40,21 @@ just test ## Part 3: Release Workflow -9. - [ ] Update the Android and Swift libraries as per the _specific libraries' workflows_ sections above. Open a single PR on `master` for all of these changes called `Prepare language bindings libraries for 0.X release`. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/315). -10. - [ ] Create a new branch off of `master` called `release/`, e.g. `release/1.2` -11. - [ ] Update bdk-android version from `SNAPSHOT` version to release version -12. - [ ] Open a PR to that release branch that updates the Android library version in the step above. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/316). -13. - [ ] Get a review and ACK and merge the PR updating Android to its release version on the release branch. -14. - [ ] Create the tag for the release and make sure to add the changelog info to the tag (works better if you prepare the tag message on the side in a text editor). Push the tag to GitHub. +9. - [ ] Update the Android and Swift libraries as per the _Part 2_ section above. Open a single PR on `master` for all of these changes called `Prepare language bindings libraries for 0.X release`. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/315). +10. - [ ] Create a new branch off of `master` called `release/`, e.g. `release/1.2` +11. - [ ] Update bdk-android version from `SNAPSHOT` version to release version and open a PR to the release branch that updates the Android version. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/316). +12. - [ ] Get a review and ACK and merge this PR on the release branch. +13. - [ ] Create the tag for the release and make sure to add the changelog info to the tag (works better if you prepare the tag message on the side in a text editor). Push the tag to GitHub. ```shell git tag v0.6.0 --sign --edit git push upstream v0.6.0 ``` -15. - [ ] Trigger manual releases for both libraries (for Swift, go on the [bdk-swift](https://github.com/bitcoindevkit/bdk-swift) repository, and trigger the using `master`. Simply add the version number and tag name in the text fields when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`, but the tag will have it, i.e. `v0.26.0`). Note also that for the Android library, you should trigger the release workflow using the tag (not a branch). -16. - [ ] Make sure the released libraries work and contain the artifacts you would expect. -17. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file. PR that. -18. - [ ] Bump the version on master from `1.1.0-SNAPSHOT` to `1.2.0-SNAPSHOT` (Android) and `1.1.0-dev` to `1.2.0-alpha.0` (Rust). -19. - [ ] Apply changes to the release issue template if needed. -20. - [ ] Make release on GitHub (generate auto release notes between the previous tag and the new one). -21. - [ ] Trigger the workflow that builds the API docs for Android and PR the website to the bitcoindevkit.org repo. -22. - [ ] Post in the announcement channel. -23. - [ ] Tweet about the new releases! +14. - [ ] Trigger manual releases for both libraries (for Swift, go to the [bdk-swift](https://github.com/bitcoindevkit/bdk-swift) repository and trigger the workflow using `master`. Simply add the version number and tag name in the text fields when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`, but the tag will have it, i.e. `v0.26.0`). For Android, trigger the release workflow using the tag (not a branch). +15. - [ ] Make sure the released libraries work and contain the artifacts you would expect. +16. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file. PR that. +17. - [ ] Bump the version on master from `1.1.0-SNAPSHOT` to `1.2.0-SNAPSHOT` (Android) and `1.1.0-alpha.0` to `1.2.0-alpha.0` (Rust). +18. - [ ] Apply changes to the release issue template if needed. +19. - [ ] Make release on GitHub (generate auto release notes between the previous tag and the new one). +20. - [ ] Build API docs for Android locally and PR the website to the bitcoindevkit.org repo. +21. - [ ] Post in the announcement channel. +22. - [ ] Tweet about the new release! From 110703de1002a725c03b3f09c8857fb740830b83 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Mon, 3 Nov 2025 14:58:17 -0500 Subject: [PATCH 04/21] chore: bump development versions of libraries --- bdk-android/gradle.properties | 2 +- bdk-ffi/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bdk-android/gradle.properties b/bdk-android/gradle.properties index bde944e0..7076c4fe 100644 --- a/bdk-android/gradle.properties +++ b/bdk-android/gradle.properties @@ -2,6 +2,6 @@ org.gradle.jvmargs=-Xmx1536m android.useAndroidX=true android.enableJetifier=true kotlin.code.style=official -libraryVersion=2.1.0-SNAPSHOT +libraryVersion=2.3.0-SNAPSHOT org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index feafedc1..9068c913 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk-ffi" -version = "2.2.0-alpha.0" +version = "2.3.0-alpha.0" homepage = "https://bitcoindevkit.org" repository = "https://github.com/bitcoindevkit/bdk" edition = "2018" From c9f11de2b289d4bee9d5f880b18e0527b100b5ee Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Tue, 4 Nov 2025 17:47:51 +0000 Subject: [PATCH 05/21] fix(kyoto): Spawn dedicated thread for DNS In personal testing and also reported to me, DNS appears to be very, very slow with the current setup. This function uses a `new_current_thread` runtime executor, but what I did not realize is this might be the main application thread. Spawning a detached thread here, same as is done with the CBF node, should add an incremental speed up. The main thread can continue to do whatever work it is supposed to do without pausing the DNS queries. --- bdk-ffi/src/kyoto.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/bdk-ffi/src/kyoto.rs b/bdk-ffi/src/kyoto.rs index 04f66f16..3008df69 100644 --- a/bdk-ffi/src/kyoto.rs +++ b/bdk-ffi/src/kyoto.rs @@ -345,11 +345,14 @@ impl CbfClient { /// for compact block filter nodes from the seeder. For example `dns.myseeder.com` will be queried /// as `x849.dns.myseeder.com`. This has no guarantee to return any `IpAddr`. pub fn lookup_host(&self, hostname: String) -> Vec> { - let nodes = tokio::runtime::Builder::new_current_thread() - .enable_all() - .build() - .unwrap() - .block_on(lookup_host(hostname)); + let node_handle = std::thread::spawn(move || { + tokio::runtime::Builder::new_current_thread() + .enable_all() + .build() + .unwrap() + .block_on(lookup_host(hostname)) + }); + let nodes = node_handle.join().unwrap_or_default(); nodes .into_iter() .map(|ip| Arc::new(IpAddress { inner: ip })) From b86cafa2a005fe3e8db8c0b78652582e2a8794cb Mon Sep 17 00:00:00 2001 From: Matthew Date: Tue, 14 Oct 2025 09:30:31 -0500 Subject: [PATCH 06/21] chore bump uniffi 0.30.0 --- bdk-ffi/Cargo.lock | 87 ++++++++++++++++++++++++++++++++++------------ bdk-ffi/Cargo.toml | 6 ++-- 2 files changed, 67 insertions(+), 26 deletions(-) diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index 503195ba..8d12c200 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -645,6 +645,8 @@ checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown 0.16.0", + "serde", + "serde_core", ] [[package]] @@ -1047,6 +1049,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "shlex" version = "1.3.0" @@ -1195,13 +1206,43 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" dependencies = [ - "serde", + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "unicode-ident" version = "1.0.20" @@ -1219,9 +1260,9 @@ dependencies = [ [[package]] name = "uniffi" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6d968cb62160c11f2573e6be724ef8b1b18a277aededd17033f8a912d73e2b4" +checksum = "c866f627c3f04c3df068b68bb2d725492caaa539dd313e2a9d26bb85b1a32f4e" dependencies = [ "anyhow", "camino", @@ -1236,9 +1277,9 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6b39ef1acbe1467d5d210f274fae344cb6f8766339330cb4c9688752899bf6b" +checksum = "7c8ca600167641ebe7c8ba9254af40492dda3397c528cc3b2f511bd23e8541a5" dependencies = [ "anyhow", "askama", @@ -1263,9 +1304,9 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6683e6b665423cddeacd89a3f97312cf400b2fb245a26f197adaf65c45d505b2" +checksum = "3e55c05228f4858bb258f651d21d743fcc1fe5a2ec20d3c0f9daefddb105ee4d" dependencies = [ "anyhow", "camino", @@ -1274,9 +1315,9 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2d990b553d6b9a7ee9c3ae71134674739913d52350b56152b0e613595bb5a6f" +checksum = "7e7a5a038ebffe8f4cf91416b154ef3c2468b18e828b7009e01b1b99938089f9" dependencies = [ "anyhow", "bytes", @@ -1286,9 +1327,9 @@ dependencies = [ [[package]] name = "uniffi_internal_macros" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f4f224becf14885c10e6e400b95cc4d1985738140cb194ccc2044563f8a56b" +checksum = "e3c2a6f93e7b73726e2015696ece25ca0ac5a5f1cf8d6a7ab5214dd0a01d2edf" dependencies = [ "anyhow", "indexmap", @@ -1299,9 +1340,9 @@ dependencies = [ [[package]] name = "uniffi_macros" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b481d385af334871d70904e6a5f129be7cd38c18fcf8dd8fd1f646b426a56d58" +checksum = "64c6309fc36c7992afc03bc0c5b059c656bccbef3f2a4bc362980017f8936141" dependencies = [ "camino", "fs-err", @@ -1316,9 +1357,9 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10f817868a3b171bb7bf259e882138d104deafde65684689b4694c846d322491" +checksum = "0a138823392dba19b0aa494872689f97d0ee157de5852e2bec157ce6de9cdc22" dependencies = [ "anyhow", "siphasher", @@ -1328,9 +1369,9 @@ dependencies = [ [[package]] name = "uniffi_pipeline" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b147e133ad7824e32426b90bc41fda584363563f2ba747f590eca1fd6fd14e6" +checksum = "8c27c4b515d25f8e53cc918e238c39a79c3144a40eaf2e51c4a7958973422c29" dependencies = [ "anyhow", "heck", @@ -1341,9 +1382,9 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5e9befada8a7069066191beb8865cdb8287f66e9041fb0bbffc8dfc5bea6b7" +checksum = "a4adb08eb5589849231dc0626ba0f9a1297925fd751f0740fc630ae934dd9c5e" dependencies = [ "anyhow", "camino", @@ -1354,9 +1395,9 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.29.4" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caed654fb73da5abbc7a7e9c741532284532ba4762d6fe5071372df22a41730a" +checksum = "d0adacdd848aeed7af4f5af7d2f621d5e82531325d405e29463482becfdeafca" dependencies = [ "anyhow", "textwrap", diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index 9068c913..0033ef22 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -20,14 +20,14 @@ bdk_esplora = { version = "0.22.1", default-features = false, features = ["std", bdk_electrum = { version = "0.23.2", default-features = false, features = ["use-rustls-ring"] } bdk_kyoto = { version = "0.15.2" } -uniffi = { version = "=0.29.4", features = ["cli"]} +uniffi = { version = "=0.30.0", features = ["cli"]} thiserror = "2.0.17" [build-dependencies] -uniffi = { version = "=0.29.4", features = ["build"] } +uniffi = { version = "=0.30.0", features = ["build"] } [dev-dependencies] -uniffi = { version = "=0.29.4", features = ["bindgen-tests"] } +uniffi = { version = "=0.30.0", features = ["bindgen-tests"] } assert_matches = "1.5.0" [profile.release-smaller] From 94d9a3fc15b0ce60e9efb13f6f83e3e20222c024 Mon Sep 17 00:00:00 2001 From: Matthew Date: Tue, 14 Oct 2025 09:33:52 -0500 Subject: [PATCH 07/21] feat: export Ord for hash and id wrappers --- bdk-ffi/src/bitcoin.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 339c8cfd..35a21635 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -983,37 +983,37 @@ impl From for BdkTxOut { } /// A bitcoin Block hash -#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, uniffi::Object)] -#[uniffi::export(Display, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash, uniffi::Object)] +#[uniffi::export(Display, Eq, Hash, Ord)] pub struct BlockHash(pub(crate) BitcoinBlockHash); impl_hash_like!(BlockHash, BitcoinBlockHash); /// A bitcoin transaction identifier -#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, uniffi::Object)] -#[uniffi::export(Display, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash, uniffi::Object)] +#[uniffi::export(Display, Eq, Hash, Ord)] pub struct Txid(pub(crate) BitcoinTxid); impl_hash_like!(Txid, BitcoinTxid); /// A bitcoin transaction identifier, including witness data. /// For transactions with no SegWit inputs, the `txid` will be equivalent to `wtxid`. -#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, uniffi::Object)] -#[uniffi::export(Display, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash, uniffi::Object)] +#[uniffi::export(Display, Eq, Hash, Ord)] pub struct Wtxid(pub(crate) BitcoinWtxid); impl_hash_like!(Wtxid, BitcoinWtxid); /// A collision-proof unique identifier for a descriptor. -#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, uniffi::Object)] -#[uniffi::export(Display, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash, uniffi::Object)] +#[uniffi::export(Display, Eq, Hash, Ord)] pub struct DescriptorId(pub(crate) BitcoinSha256Hash); impl_hash_like!(DescriptorId, BitcoinSha256Hash); /// The merkle root of the merkle tree corresponding to a block's transactions. -#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, uniffi::Object)] -#[uniffi::export(Display, Eq, Hash)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, std::hash::Hash, uniffi::Object)] +#[uniffi::export(Display, Eq, Hash, Ord)] pub struct TxMerkleNode(pub(crate) BitcoinDoubleSha256Hash); impl_hash_like!(TxMerkleNode, BitcoinDoubleSha256Hash); From 9d1b0d4c8c55b553e35e7ffbda8a732d6cc1cffe Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Thu, 11 Sep 2025 14:32:44 -0400 Subject: [PATCH 08/21] docs: add api docs for FeeRate type --- bdk-ffi/src/bitcoin.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 35a21635..ceca0776 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -129,6 +129,7 @@ pub struct FeeRate(pub(crate) BdkFeeRate); #[uniffi::export] impl FeeRate { + // Constructs `FeeRate` from satoshis per virtual bytes. #[uniffi::constructor] pub fn from_sat_per_vb(sat_vb: u64) -> Result { let fee_rate: Option = BdkFeeRate::from_sat_per_vb(sat_vb); @@ -138,19 +139,23 @@ impl FeeRate { } } + // Constructs `FeeRate` from satoshis per 1000 weight units. #[uniffi::constructor] pub fn from_sat_per_kwu(sat_kwu: u64) -> Self { FeeRate(BdkFeeRate::from_sat_per_kwu(sat_kwu)) } + /// Converts to sat/vB rounding up. pub fn to_sat_per_vb_ceil(&self) -> u64 { self.0.to_sat_per_vb_ceil() } + /// Converts to sat/vB rounding down. pub fn to_sat_per_vb_floor(&self) -> u64 { self.0.to_sat_per_vb_floor() } + /// Returns raw fee rate. pub fn to_sat_per_kwu(&self) -> u64 { self.0.to_sat_per_kwu() } From 3f9326437e7eab602f0581814cb674001509aa3d Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Thu, 11 Sep 2025 15:05:40 -0400 Subject: [PATCH 09/21] feat: implement display trait for FeeRate type --- bdk-ffi/Cargo.lock | 2 +- bdk-ffi/src/bitcoin.rs | 35 +++++++++++++++++++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index 8d12c200..61641d88 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -119,7 +119,7 @@ dependencies = [ [[package]] name = "bdk-ffi" -version = "2.2.0-alpha.0" +version = "2.3.0-alpha.0" dependencies = [ "assert_matches", "bdk_electrum", diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index ceca0776..89c8c383 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -125,11 +125,12 @@ impl HashableOutPoint { /// This is an integer type representing fee rate in sat/kwu. It provides protection against mixing /// up the types as well as basic formatting features. #[derive(Clone, Debug, uniffi::Object)] +#[uniffi::export(Display)] pub struct FeeRate(pub(crate) BdkFeeRate); #[uniffi::export] impl FeeRate { - // Constructs `FeeRate` from satoshis per virtual bytes. + /// Constructs `FeeRate` from satoshis per virtual bytes. #[uniffi::constructor] pub fn from_sat_per_vb(sat_vb: u64) -> Result { let fee_rate: Option = BdkFeeRate::from_sat_per_vb(sat_vb); @@ -139,7 +140,7 @@ impl FeeRate { } } - // Constructs `FeeRate` from satoshis per 1000 weight units. + /// Constructs `FeeRate` from satoshis per 1000 weight units. #[uniffi::constructor] pub fn from_sat_per_kwu(sat_kwu: u64) -> Self { FeeRate(BdkFeeRate::from_sat_per_kwu(sat_kwu)) @@ -159,6 +160,36 @@ impl FeeRate { pub fn to_sat_per_kwu(&self) -> u64 { self.0.to_sat_per_kwu() } + + /// Calculates fee in satoshis by multiplying this fee rate by weight, in virtual bytes, returning `None` if overflow occurred. + /// + /// This is equivalent to converting vb to weight using Weight::from_vb and then calling Self::fee_wu(weight). + pub fn fee_vb(&self, vb: u64) -> Option> { + let rust_amount: BdkAmount = self.0.fee_vb(vb)?; + let amount: Amount = rust_amount.into(); + Some(Arc::new(amount)) + + // The whole code above should be replaceable by the following line: + // self.0.fee_vb(vb).map(Arc::new(Amount::from)) + // But in practice you get uniffi compilation errors on it. Not sure what is going on with it, + // but the code we use works just as well. + } + + /// Calculates fee by multiplying this fee rate by weight, in weight units, returning `None` if overflow occurred. + /// + /// This is equivalent to Self::checked_mul_by_weight(). + pub fn fee_wu(&self, wu: u64) -> Option> { + let weight: Weight = Weight::from_wu(wu); + let rust_amount: BdkAmount = self.0.fee_wu(weight)?; + let amount: Amount = rust_amount.into(); + Some(Arc::new(amount)) + } +} + +impl Display for FeeRate { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:#}", self.0) + } } impl_from_core_type!(BdkFeeRate, FeeRate); From 8c62796bdb3c448d8d2a52d7e6fc9e77e1c05a0a Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Thu, 11 Sep 2025 16:01:52 -0400 Subject: [PATCH 10/21] feat: expose FeeRate::fee_vb and FeeRate::fee_wu methods --- bdk-ffi/src/bitcoin.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 89c8c383..40335362 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -30,6 +30,7 @@ use bdk_wallet::bitcoin::Transaction as BdkTransaction; use bdk_wallet::bitcoin::TxIn as BdkTxIn; use bdk_wallet::bitcoin::TxOut as BdkTxOut; use bdk_wallet::bitcoin::Txid as BitcoinTxid; +use bdk_wallet::bitcoin::Weight; use bdk_wallet::bitcoin::Wtxid as BitcoinWtxid; use bdk_wallet::miniscript::psbt::PsbtExt; use bdk_wallet::serde_json; From 8149eb3aa7044a8fd96b52f5a9f90bfe1c62fe0f Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Thu, 13 Nov 2025 13:50:02 -0500 Subject: [PATCH 11/21] docs: small fixes to changelog --- CHANGELOG.md | 378 +++++++++++++++++++++++++++++---------------------- 1 file changed, 219 insertions(+), 159 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a8416f6..27d73a3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,34 +6,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [v2.2.0] -This is version 2.2.0 of the BDK language bindings! This release uses the following Rust dependencies: +This is version `2.2.0` of the BDK language bindings! This release uses the following Rust dependencies: - - bdk_wallet 2.2.0 - - bdk_electrum 0.23.2 - - bdk_esplora 0.22.1 - - bdk_kyoto 0.15.2 - - rust-bitcoin 0.32.7 - - uniffi 0.29.4 +- bdk_wallet `2.2.0` +- bdk_electrum `0.23.2` +- bdk_esplora `0.22.1` +- bdk_kyoto `0.15.2` +- rust-bitcoin `0.32.7` +- uniffi `0.29.4` ### Added - - Implement `Display` for `Script` [#813] - - Expose `Wallet::create_single` constructor on Wallet type [#825] - - Set `lookahead` on `Wallet::load` [#829] - - Expose `Wallet::apply_evicted_txs` method [#832] - - Add `Wallet::create_from_two_path_descriptor` constructor [#847] - - `next_log` and `LogLevel` are removed from `kyoto` [#849] - - Updates Android native libraries to use 16KB page sizes [#865] - - Expose `TxBuilder::exclude_unconfirmed` method [#870] - - Expose `TxBuilder::exclude_below_confirmations` method [#870] - - Expose `Wallet::insert_txout` [#884] - - Expose `Wallet::nmark_used` [#882] - - Add `Wallet::load_single` [#897] - - Expose `DerivationPath::master` , `DerivationPath::is_master` , `DerivationPath::len` , `DerivationPath::is_empty` [#893] +- Implement `Display` for `Script` [#813] +- Expose `Wallet::create_single` constructor on Wallet type [#825] +- Set `lookahead` on `Wallet::load` [#829] +- Expose `Wallet::apply_evicted_txs` method [#832] +- Add `Wallet::create_from_two_path_descriptor` constructor [#847] +- `next_log` and `LogLevel` are removed from `kyoto` [#849] +- Updates Android native libraries to use 16KB page sizes [#865] +- Expose `TxBuilder::exclude_unconfirmed` method [#870] +- Expose `TxBuilder::exclude_below_confirmations` method [#870] +- Expose `Wallet::insert_txout` [#884] +- Expose `Wallet::nmark_used` [#882] +- Add `Wallet::load_single` [#897] +- Expose `DerivationPath::master` , `DerivationPath::is_master` , `DerivationPath::len` , `DerivationPath::is_empty` [#893] ### Changed - - Throw errors rather than panics on faulty Esplora sync/full_scan operations [#863] +- Throw errors rather than panics on faulty Esplora sync/full_scan operations [#863] [#813]: https://github.com/bitcoindevkit/bdk-ffi/pull/813 [#825]: https://github.com/bitcoindevkit/bdk-ffi/pull/825 @@ -51,40 +51,44 @@ This is version 2.2.0 of the BDK language bindings! This release uses the follow ## [v2.0.0] -This release brings bdk-ffi to it's 2.0.0 version! +This release brings bdk-ffi to it's `2.0.0` version! The release uses the following Rust dependencies: - - bdk_wallet 2.0.0 - - bdk_electrum 0.23.0 - - bdk_esplora 0.22.0 - - bdk_kyoto 0.13.1 - - uniffi 0.29.1 - - rust-bitcoin 0.32.6 + +- bdk_wallet `2.0.0` +- bdk_electrum `0.23.0` +- bdk_esplora `0.22.0` +- bdk_kyoto `0.13.1` +- uniffi `0.29.1` +- rust-bitcoin `0.32.6` ### Added - - PSBT file operations: read and write PSBT files [#800] - - New `Psbt::from_unsigned_tx` constructor [#802] - - New `Psbt::spend_utxo` method [#798] - - Arbitrary persistence for wallet [#771] - - Wallet changeset primitives [#756] - - Display implementation for `Transaction` [#799] - - `Descriptor::max_weight_to_satisfy` method [#794] - - Expose `Wallet::public_descriptor` [#786] - - Expose `Wallet::tx_details` [#778] - - Expose `Wallet::latest_checkpoint` [#761] - - `TxGraphChangeSet::first_seen` and `last_evicted` fields [#782] - - `from_string` constructors for hash types [#784] - - `Transaction::wtxid` method [#773] - - Kyoto: average feerate and connect functionality [#797] + +- PSBT file operations: read and write PSBT files [#800] +- New `Psbt::from_unsigned_tx` constructor [#802] +- New `Psbt::spend_utxo` method [#798] +- Arbitrary persistence for wallet [#771] +- Wallet changeset primitives [#756] +- Display implementation for `Transaction` [#799] +- `Descriptor::max_weight_to_satisfy` method [#794] +- Expose `Wallet::public_descriptor` [#786] +- Expose `Wallet::tx_details` [#778] +- Expose `Wallet::latest_checkpoint` [#761] +- `TxGraphChangeSet::first_seen` and `last_evicted` fields [#782] +- `from_string` constructors for hash types [#784] +- `Transaction::wtxid` method [#773] +- Kyoto: average feerate and connect functionality [#797] ### Changed - - Use `Amount` type in `TxOut` instead of u64 [#781] - - Update to latest bdk_kyoto with API changes [#772] - - Add `lookahead` as optional argument to wallet methods [#770] + +- Use `Amount` type in `TxOut` instead of u64 [#781] +- Update to latest bdk_kyoto with API changes [#772] +- Add `lookahead` as optional argument to wallet methods [#770] ### Fixed - - Export public types from `Script` [#763] - - Change object to record for struct with fields [#738] + +- Export public types from `Script` [#763] +- Change object to record for struct with fields [#738] [#738]: https://github.com/bitcoindevkit/bdk-ffi/pull/738 [#756]: https://github.com/bitcoindevkit/bdk-ffi/pull/756 @@ -111,24 +115,27 @@ The release uses the following Rust dependencies: This release brings in a new experimental compact block filters client! The release uses the following Rust dependencies: - - bdk_wallet `1.2.0` - - bdk_electrum `0.21.0` - - bdk_esplora `0.20.1` - - bdk_kyoto `0.8.0` - - uniffi `0.29.1` - - rust-bitcoin `0.32.5` + +- bdk_wallet `1.2.0` +- bdk_electrum `0.21.0` +- bdk_esplora `0.20.1` +- bdk_kyoto `0.8.0` +- uniffi `0.29.1` +- rust-bitcoin `0.32.5` ### Added - - New CBF client (Kyoto) [#591], [#716] - - Add optional `proxy` parameter to Esplora client constructor [#711] - - Add optional `socks5` parameter to Electrum client constructor [#711] - - Add `ElectrumClient::ping` method [#689] - - New API docs for a lot of types - - Add `Wallet::apply_unconfirmed_txs` method [#704] - - Add `UnconfirmedTx` type [#704] + +- New CBF client (Kyoto) [#591], [#716] +- Add optional `proxy` parameter to Esplora client constructor [#711] +- Add optional `socks5` parameter to Electrum client constructor [#711] +- Add `ElectrumClient::ping` method [#689] +- New API docs for a lot of types +- Add `Wallet::apply_unconfirmed_txs` method [#704] +- Add `UnconfirmedTx` type [#704] ### Changed - - The `Amount::from_sat` constructor renamed its `sat` argument to `satoshi` [#708] + +- The `Amount::from_sat` constructor renamed its `sat` argument to `satoshi` [#708] [#591]: https://github.com/bitcoindevkit/bdk-ffi/pull/591 [#689]: https://github.com/bitcoindevkit/bdk-ffi/pull/689 @@ -142,23 +149,26 @@ The release uses the following Rust dependencies: This is our first stable release! This release uses the following Rust dependencies: - - bdk_wallet 1.1.0 - - bdk_electrum 0.21.0 - - bdk_esplora 0.20.1 - - uniffi 0.29.0 - - rust-bitcoin 0.32.5 - -#### Added - - Expose `ElectrumClient::block_headers_subscribe` method [#664] - - Expose `EsploraClient::get_block_hash` method [#665] - - Expose `EsploraClient::get_tx_status` method [#666] - - Expose `EsploraClient::get_tx_info` method [#666] - - Support for Testnet 4 [#674] - - Add `AddressData` and `WitnessProgram` types from rust bitcoin [#671] - - Expose `Address::to_address_data` method [#671] - -#### Changed - - More complete `LocalOutput` type [#667] + +- bdk_wallet `1.1.0` +- bdk_electrum `0.21.0` +- bdk_esplora `0.20.1` +- uniffi `0.29.0` +- rust-bitcoin `0.32.5` + +### Added + +- Expose `ElectrumClient::block_headers_subscribe` method [#664] +- Expose `EsploraClient::get_block_hash` method [#665] +- Expose `EsploraClient::get_tx_status` method [#666] +- Expose `EsploraClient::get_tx_info` method [#666] +- Support for Testnet 4 [#674] +- Add `AddressData` and `WitnessProgram` types from rust bitcoin [#671] +- Expose `Address::to_address_data` method [#671] + +### Changed + +- More complete `LocalOutput` type [#667] [#664]: https://github.com/bitcoindevkit/bdk-ffi/pull/664 [#665]: https://github.com/bitcoindevkit/bdk-ffi/pull/665 @@ -168,9 +178,10 @@ This release uses the following Rust dependencies: [#674]: https://github.com/bitcoindevkit/bdk-ffi/pull/674 ## [v1.0.0-beta.7] + This release updates the `bdk-ffi` libraries to the final `bdk_wallet` `1.0.0` and related libraries (Esplora, Electrum, etc). -#### Added +### Added - `ElectrumClient::server_features` [#641] - `ServerFeaturesRes` struct [#641] @@ -178,7 +189,7 @@ This release updates the `bdk-ffi` libraries to the final `bdk_wallet` `1.0.0` a - `EsploraClient::get_fee_estimates` [#648] - New optional argument sign_options on `Wallet::sign` and `Wallet::finalize_psbt` [#650] -#### Changed +### Changed - The full_scan and sync methods on the Electrum and Esplora clients now take a renamed `request` argument [#642] - ElectrumClient::broacast was renamed ElectrumClient::transaction_broadcast to mirror the Rust API [#642] @@ -189,9 +200,11 @@ This release updates the `bdk-ffi` libraries to the final `bdk_wallet` `1.0.0` a [#650]: https://github.com/bitcoindevkit/bdk-ffi/pull/650 ## [v1.0.0-beta.6] + This release updates the `bdk-ffi` libraries to the latest `bdk_wallet` `1.0.0-beta.6` and related libraries (Esplora, Electrum, etc). -#### Added +### Added + - `DescriptorPublicKey::is_multipath` [#625] - `DescriptorPublicKey::master_fingerprint` [#625] - `Descriptor::is_multipath` [#625] @@ -212,6 +225,7 @@ This release updates the `bdk-ffi` libraries to the latest `bdk_wallet` `1.0.0-b - `Wallet:: policies` [#629] #### Other + - Added documentation via docstrings [#601]: https://github.com/bitcoindevkit/bdk-ffi/pull/601 @@ -224,9 +238,11 @@ This release updates the `bdk-ffi` libraries to the latest `bdk_wallet` `1.0.0-b [#645]: https://github.com/bitcoindevkit/bdk-ffi/pull/645 ## [v1.0.0-beta.5] + This release updates the bdk-ffi libraries to the latest bdk_wallet 1.0.0-beta.5 and related libraries (Esplora, Electrum, etc.). -#### Added +### Added + `EsploraClient` - `get_tx` [#598] `Wallet` @@ -244,11 +260,11 @@ This release updates the bdk-ffi libraries to the latest bdk_wallet 1.0.0-beta.5 `TxBuilder` - `set_exact_sequence` [#600] -#### Changed +### Changed `Wallet` - corrected argument name in `reveal_next_address` [#599] -#### Removed +### Removed `TxBuilder` - `enable_rbf` [#600] @@ -260,33 +276,37 @@ This release updates the bdk-ffi libraries to the latest bdk_wallet 1.0.0-beta.5 [#604]: https://github.com/bitcoindevkit/bdk-ffi/pull/604 ## [v1.0.0-beta.2] + This release updates the bdk-ffi libraries to the latest bdk_wallet 1.0.0-beta.2 and related libraries (Esplora, Electrum, etc.), as well as uses the latest uniffi-rs library version 0.28.0. The releases now depend on [bitcoin-ffi] for the types that are exposed from the rust-bitcoin org. It also bumps the minimum supported Android API level to 24 (Android Nougat). -#### Added - - SQLite persistence through bdk_sqlite [#544] - - The `Address`, `DescriptorSecretKey`, `DescriptorPublicKey`, `Mnemonic`, and `Descriptor` types now have the `toString()` method implemented on them by default [#551] - - `Address.from_script()` [#554] - - New `FromScriptError` [#561] - - New type `ChangeSet` [#561] - - Wallet constructors do not take a persistence path anymore [#561] - - `Wallet.get_balance()` method renamed to `balance()` [#561] - - Add `add_global_xpubs()` method on `TxBuilder` [#574] - - Add `wallet.derivation_index` method on Wallet type [#579] - - Add `wallet.persist` method on Wallet type [#582] - - Add `Connection` type [#582] - -#### Changed - - `AddressError` is replaced by `AddressParseError` [#561] - - New variants in `CalculateFeeError` [#561] - - New variants in `CreateTxError` [#561] - - New variants in `ParseAmountError` [#561] - - New variants in `SignerError` [#561] - - New variants in `WalletCreationError` [#561] - - `Wallet.calculate_fee()` returns an `Amount` [#561] - - Renamed `Transaction.txid()` to `Transaction.compute_txid()` [#561] - -#### Removed - - flat file persistence [#544] +### Added + +- SQLite persistence through bdk_sqlite [#544] +- The `Address`, `DescriptorSecretKey`, `DescriptorPublicKey`, `Mnemonic`, and `Descriptor` types now have the `toString()` method implemented on them by default [#551] +- `Address.from_script()` [#554] +- New `FromScriptError` [#561] +- New type `ChangeSet` [#561] +- Wallet constructors do not take a persistence path anymore [#561] +- `Wallet.get_balance()` method renamed to `balance()` [#561] +- Add `add_global_xpubs()` method on `TxBuilder` [#574] +- Add `wallet.derivation_index` method on Wallet type [#579] +- Add `wallet.persist` method on Wallet type [#582] +- Add `Connection` type [#582] + +### Changed + +- `AddressError` is replaced by `AddressParseError` [#561] +- New variants in `CalculateFeeError` [#561] +- New variants in `CreateTxError` [#561] +- New variants in `ParseAmountError` [#561] +- New variants in `SignerError` [#561] +- New variants in `WalletCreationError` [#561] +- `Wallet.calculate_fee()` returns an `Amount` [#561] +- Renamed `Transaction.txid()` to `Transaction.compute_txid()` [#561] + +### Removed + +- flat file persistence [#544] [#544]: https://github.com/bitcoindevkit/bdk-ffi/pull/544 [#551]: https://github.com/bitcoindevkit/bdk-ffi/pull/551 @@ -298,16 +318,18 @@ This release updates the bdk-ffi libraries to the latest bdk_wallet 1.0.0-beta.2 [bitcoin-ffi]: https://github.com/bitcoindevkit/bitcoin-ffi ## [v1.0.0-alpha.11] + This release brings the latest alpha 11 release of the Rust bdk_wallet library, as well as the new Electrum client, the new memory wallet, and a whole lot of new types and APIs across the library. Also of note are the much simpler-to-use full_scan and sync workflows for syncing wallets. -Added: - - `Amount` type [#533] - - `TxIn` type [#536] - - `Transaction.input()` method [#536] - - `Transaction.output()` method [#536] - - `Transaction.lock_time()` method [#536] - - `Electrum` client [#535] - - Memory wallet [#528] +### Added + +- `Amount` type [#533] +- `TxIn` type [#536] +- `Transaction.input()` method [#536] +- `Transaction.output()` method [#536] +- `Transaction.lock_time()` method [#536] +- `Electrum` client [#535] +- Memory wallet [#528] [#528]: https://github.com/bitcoindevkit/bdk-ffi/pull/528 [#533]: https://github.com/bitcoindevkit/bdk-ffi/pull/533 @@ -315,9 +337,11 @@ Added: [#536]: https://github.com/bitcoindevkit/bdk-ffi/pull/536 ## [v1.0.0-alpha.7] + This release brings back into the 1.0 API a number of APIs from the 0.31 release, and adds the new flat file persistence feature, as well as more fine-grain errors. ## [v1.0.0-alpha.2a] + This release is the first alpha release of the 1.0 API for the bindings libraries. Here is what is now available: - Create and recover wallets using descriptors, including the four descriptor templates - Sync a wallet using a blocking Esplora client @@ -326,11 +350,13 @@ This release is the first alpha release of the 1.0 API for the bindings librarie - Broadcast transactions ## [v0.32.1] + This is a patch release, updating the bindings libraries to bdk version 0.30.2, fixing an issue with syncing very large wallets. See https://github.com/bitcoindevkit/bdk/releases/tag/v0.30.2 for details. ## [v0.31.0] + This release updates the bindings libraries to bdk version 0.29.0, updating rust-bitcoin to version 0.30.2. - APIs Changed: @@ -343,21 +369,24 @@ This release updates the bindings libraries to bdk version 0.29.0, updating rust [#443]: https://github.com/bitcoindevkit/bdk-ffi/pull/443 ## [v0.30.0] + This release has a new API and a few internal optimizations and refactorings. -- APIs Added - - Add BIP-86 descriptor templates [#388] +### Added +- Add BIP-86 descriptor templates [#388] [#388]: https://github.com/bitcoindevkit/bdk-ffi/pull/388 ## [v0.29.0] + This release has a number of new APIs, and adds support for Windows in bdk-jvm. +### Added + - Add support for Windows in bdk-jvm [#336] - Add support for older version of Linux distros in bdk-jvm [#345] -- APIs added - - Expose `is_mine()` method on the `Wallet` type [#355] - - Expose `to_bytes()` method on the `Script` type [#369] +- Expose `is_mine()` method on the `Wallet` type [#355] +- Expose `to_bytes()` method on the `Script` type [#369] [#336]: https://github.com/bitcoindevkit/bdk-ffi/pull/336 [#345]: https://github.com/bitcoindevkit/bdk-ffi/pull/345 @@ -365,17 +394,21 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [#369]: https://github.com/bitcoindevkit/bdk-ffi/pull/369 ## [v0.28.0] -- Update BDK to version 0.28.0 [#341] -- Drop support of pypy releases of Python libraries [#351] -- Drop support for Python 3.6 and 3.7 [#351] -- Drop support for very old Linux versions that do not support the manylinux_2_17_x86_64 platform tag [#351] -- APIs changed: + +### Added - Expose Address payload and network properties. [#325] - Add SignOptions to Wallet.sign() params. [#326] - address field on `AddressInfo` type is now of type `Address` [#333] - new PartiallySignedTransaction.json_serialize() function to get JSON serialized value of all PSBT fields. [#334] - Add from_script constructor to `Address` type [#337] +### Other + +- Update BDK to version 0.28.0 [#341] +- Drop support of pypy releases of Python libraries [#351] +- Drop support for Python 3.6 and 3.7 [#351] +- Drop support for very old Linux versions that do not support the manylinux_2_17_x86_64 platform tag [#351] + [#325]: https://github.com/bitcoindevkit/bdk-ffi/pull/325 [#326]: https://github.com/bitcoindevkit/bdk-ffi/pull/326 [#333]: https://github.com/bitcoindevkit/bdk-ffi/pull/333 @@ -385,14 +418,19 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [#351]: https://github.com/bitcoindevkit/bdk-ffi/pull/351 ## [v0.27.1] + - Update BDK to version 0.27.1 [#312] -- APIs changed - - `PartiallySignedTransaction.extract_tx()` returns a `Transaction` instead of the transaction bytes. [#296] - - `Blockchain.broadcast()` takes a `Transaction` instead of a `PartiallySignedTransaction`. [#296] -- APIs added - - New `Transaction` structure that can be created from or serialized to consensus encoded bytes. [#296] - - Add Wallet.get_internal_address() API [#304] - - Add `AddressIndex::Peek(index)` and `AddressIndex::Reset(index)` APIs [#305] + +### Added + +- New `Transaction` structure that can be created from or serialized to consensus encoded bytes. [#296] +- Add Wallet.get_internal_address() API [#304] +- Add `AddressIndex::Peek(index)` and `AddressIndex::Reset(index)` APIs [#305] + +### Changed + +- `PartiallySignedTransaction.extract_tx()` returns a `Transaction` instead of the transaction bytes. [#296] +- `Blockchain.broadcast()` takes a `Transaction` instead of a `PartiallySignedTransaction`. [#296] [#296]: https://github.com/bitcoindevkit/bdk-ffi/pull/296 [#304]: https://github.com/bitcoindevkit/bdk-ffi/pull/304 @@ -400,22 +438,26 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [#312]: https://github.com/bitcoindevkit/bdk-ffi/pull/312 ## [v0.26.0] + - Update BDK to version 0.26.0 [#288] -- APIs changed + +### Added +- Added RpcConfig, BlockchainConfig::Rpc, and Auth [#125] +- Added Descriptor type in [#260] with the following methods: + - Default constructor requires a descriptor in String format and a Network + - new_bip44 constructor returns a Descriptor with structure pkh(key/44'/{0,1}'/0'/{0,1}/*) + - new_bip44_public constructor returns a Descriptor with structure pkh(key/{0,1}/*) + - new_bip49 constructor returns a Descriptor with structure sh(wpkh(key/49'/{0,1}'/0'/{0,1}/*)) + - new_bip49_public constructor returns a Descriptor with structure sh(wpkh(key/{0,1}/*)) + - new_bip84 constructor returns a Descriptor with structure wpkh(key/84'/{0,1}'/0'/{0,1}/*) + - new_bip84_public constructor returns a Descriptor with structure wpkh(key/{0,1}/*) + - as_string returns the public version of the output descriptor + - as_string_private returns the private version of the output descriptor if available, otherwise return the public version + +### Changed - The descriptor and change_descriptor arguments on the wallet constructor now take a `Descriptor` instead of a `String`. [#260] - TxBuilder.drain_to() argument is now `Script` instead of address `String`. [#279] -- APIs added - - Added RpcConfig, BlockchainConfig::Rpc, and Auth [#125] - - Added Descriptor type in [#260] with the following methods: - - Default constructor requires a descriptor in String format and a Network - - new_bip44 constructor returns a Descriptor with structure pkh(key/44'/{0,1}'/0'/{0,1}/*) - - new_bip44_public constructor returns a Descriptor with structure pkh(key/{0,1}/*) - - new_bip49 constructor returns a Descriptor with structure sh(wpkh(key/49'/{0,1}'/0'/{0,1}/*)) - - new_bip49_public constructor returns a Descriptor with structure sh(wpkh(key/{0,1}/*)) - - new_bip84 constructor returns a Descriptor with structure wpkh(key/84'/{0,1}'/0'/{0,1}/*) - - new_bip84_public constructor returns a Descriptor with structure wpkh(key/{0,1}/*) - - as_string returns the public version of the output descriptor - - as_string_private returns the private version of the output descriptor if available, otherwise return the public version + [#125]: https://github.com/bitcoindevkit/bdk-ffi/pull/125 [#260]: https://github.com/bitcoindevkit/bdk-ffi/pull/260 @@ -423,30 +465,41 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [#288]: https://github.com/bitcoindevkit/bdk-ffi/pull/288 ## [v0.25.0] + - Update BDK to version 0.25.0 [#272] -- APIs Added: + +### Added + - from_string() constructors now available on DescriptorSecretKey and DescriptorPublicKey [#247] [#247]: https://github.com/bitcoindevkit/bdk-ffi/pull/247 [#272]: https://github.com/bitcoindevkit/bdk-ffi/pull/272 ## [v0.11.0] + - Update BDK to version 0.24.0 [#221] -- APIs changed - - The constructor on the DescriptorSecretKey type now takes a Mnemonic instead of a String. -- APIs added - - Added Mnemonic struct [#219] with following methods: - - new(word_count: WordCount) generates and returns Mnemonic with random entropy - - from_string(mnemonic: String) converts string Mnemonic to Mnemonic type with error - - from_entropy(entropy: Vec) generates and returns Mnemonic with given entropy - - as_string() view Mnemonic as string -- APIs removed - - generate_mnemonic(word_count: WordCount) + +### Added + +- Added Mnemonic struct [#219] with following methods: + - new(word_count: WordCount) generates and returns Mnemonic with random entropy + - from_string(mnemonic: String) converts string Mnemonic to Mnemonic type with error + - from_entropy(entropy: Vec) generates and returns Mnemonic with given entropy + - as_string() view Mnemonic as string + +### Changed + +- The constructor on the DescriptorSecretKey type now takes a Mnemonic instead of a String. + +### Removed + +- generate_mnemonic(word_count: WordCount) [#219]: https://github.com/bitcoindevkit/bdk-ffi/pull/219 [#221]: https://github.com/bitcoindevkit/bdk-ffi/pull/221 ## [v0.10.0] + - Update BDK to version 0.23.0 [#204] - Update uniffi-rs to latest version 0.21.0 [#216] - Breaking Changes @@ -468,6 +521,7 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [#216]: https://github.com/bitcoindevkit/bdk-ffi/pull/216 ## [v0.9.0] + - Breaking Changes - Rename `get_network()` method on `Wallet` interface to `network()` [#185] - Rename `get_transactions()` method on `Wallet` interface to `list_transactions()` [#185] @@ -503,6 +557,7 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [#193]: https://github.com/bitcoindevkit/bdk-ffi/pull/193 ## [v0.8.0] + - Update BDK to version 0.20.0 [#169] - APIs Added - `TxBuilder.add_data(data: Vec)` [#163] @@ -531,11 +586,13 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [#161]: https://github.com/bitcoindevkit/bdk-ffi/pull/161 ## [v0.6.0] + - Update BDK to version 0.18.0 - Add BumpFeeTxBuilder to bump the fee on an unconfirmed tx created by the Wallet - Change TxBuilder.build() to TxBuilder.finish() to align with bdk function name ## [v0.5.0] + - Fix Wallet.broadcast function, now returns a tx id as a hex string - Remove creating a new spending Transaction via the PartiallySignedBitcoinTransaction constructor - Add TxBuilder for creating new spending PartiallySignedBitcoinTransaction @@ -544,14 +601,17 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. - Update generate cli tool to generate all binding languages and rename to bdk-ffi-bindgen ## [v0.4.0] + - Add dual license MIT and Apache 2.0 - Add sqlite database support - Fix memory database configuration enum, remove junk field ## [v0.3.1] + - Remove hard coded sync progress value (was always returning 21.0) ## [v0.3.0] + - Move bdk-kotlin bindings and ios example to separate repos - Add bin to generate Python bindings - Add `PartiallySignedBitcoinTransaction::deserialize` function as named constructor to decode from a string per [BIP 0174] @@ -560,7 +620,7 @@ This release has a number of new APIs, and adds support for Windows in bdk-jvm. [BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding -[v2.0.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v2.0.0...v2.2.0 +[v2.2.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v2.0.0...v2.2.0 [v2.0.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.2.0...v2.0.0 [v1.2.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.1.0...v1.2.0 [v1.1.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-beta.7...v1.1.0 From 210947bc556e565e64e022fb3d589018e454eea4 Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Mon, 3 Nov 2025 19:03:06 -0500 Subject: [PATCH 12/21] chore: add development script and commands --- .github/workflows/android-api-docs.yaml | 2 +- .github/workflows/publish-android.yaml | 2 +- .github/workflows/test-android.yaml | 2 +- bdk-android/justfile | 7 ++++- .../scripts/dev/build-dev-macos-aarch64.sh | 28 +++++++++++++++++++ .../build-release-linux-x86_64.sh} | 0 .../build-release-macos-aarch64.sh} | 0 .../build-release-windows-x86_64.sh} | 0 bdk-ffi/justfile | 3 ++ 9 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 bdk-android/scripts/dev/build-dev-macos-aarch64.sh rename bdk-android/scripts/{build-linux-x86_64.sh => release/build-release-linux-x86_64.sh} (100%) rename bdk-android/scripts/{build-macos-aarch64.sh => release/build-release-macos-aarch64.sh} (100%) rename bdk-android/scripts/{build-windows-x86_64.sh => release/build-release-windows-x86_64.sh} (100%) diff --git a/.github/workflows/android-api-docs.yaml b/.github/workflows/android-api-docs.yaml index 7543f37b..40819f7d 100644 --- a/.github/workflows/android-api-docs.yaml +++ b/.github/workflows/android-api-docs.yaml @@ -27,7 +27,7 @@ jobs: - name: "Build Android API documentation" run: | cd ./bdk-android/ - bash ./scripts/build-linux-x86_64.sh + bash ./scripts/release/build-release-linux-x86_64.sh ./gradlew dokkaHtml - name: "Upload documentation website" diff --git a/.github/workflows/publish-android.yaml b/.github/workflows/publish-android.yaml index 9a640b5d..c968e43a 100644 --- a/.github/workflows/publish-android.yaml +++ b/.github/workflows/publish-android.yaml @@ -33,7 +33,7 @@ jobs: java-version: 17 - name: "Build bdk-android library" - run: bash ./scripts/build-linux-x86_64.sh + run: bash ./scripts/release/build-release-linux-x86_64.sh - name: "Publish to Maven Central" env: diff --git a/.github/workflows/test-android.yaml b/.github/workflows/test-android.yaml index 66151191..7ca81553 100644 --- a/.github/workflows/test-android.yaml +++ b/.github/workflows/test-android.yaml @@ -46,7 +46,7 @@ jobs: - name: "Build Android library" run: | cd bdk-android - bash ./scripts/build-linux-x86_64.sh + bash ./scripts/release/build-release-linux-x86_64.sh - name: "Enable KVM" run: | diff --git a/bdk-android/justfile b/bdk-android/justfile index f4162631..2baef941 100644 --- a/bdk-android/justfile +++ b/bdk-android/justfile @@ -21,7 +21,12 @@ publish-local: [group("Build")] [doc("Build the library for given ARCH.")] build ARCH="macos-aarch64": - bash ./scripts/build-{{ARCH}}.sh + bash ./scripts/release/build-release-{{ARCH}}.sh + +[group("Build")] +[doc("Build the library for a single architecture in development mode.")] +build-dev: + bash ./scripts/dev/build-dev-macos-aarch64.sh [group("Build")] [doc("List available architectures for the build command.")] diff --git a/bdk-android/scripts/dev/build-dev-macos-aarch64.sh b/bdk-android/scripts/dev/build-dev-macos-aarch64.sh new file mode 100644 index 00000000..c610bc0f --- /dev/null +++ b/bdk-android/scripts/dev/build-dev-macos-aarch64.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +if [ -z "$ANDROID_NDK_ROOT" ]; then + echo "Error: ANDROID_NDK_ROOT is not defined in your environment" + exit 1 +fi + +PATH="$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/darwin-x86_64/bin:$PATH" +CFLAGS="-D__ANDROID_MIN_SDK_VERSION__=24" +AR="llvm-ar" +LIB_NAME="libbdkffi.so" +COMPILATION_TARGET_ARM64_V8A="aarch64-linux-android" +RESOURCE_DIR_ARM64_V8A="arm64-v8a" + +# Move to the Rust library directory +cd ../bdk-ffi/ || exit +rustup target add $COMPILATION_TARGET_ARM64_V8A + +# Build the binaries +# The CC and CARGO_TARGET__LINUX_ANDROID_LINKER environment variables must be declared on the same line as the cargo build command +CC="aarch64-linux-android24-clang" CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="aarch64-linux-android24-clang" cargo build --target $COMPILATION_TARGET_ARM64_V8A + +# Copy the binaries to their respective resource directories +mkdir -p ../bdk-android/lib/src/main/jniLibs/$RESOURCE_DIR_ARM64_V8A/ +cp ./target/$COMPILATION_TARGET_ARM64_V8A/debug/$LIB_NAME ../bdk-android/lib/src/main/jniLibs/$RESOURCE_DIR_ARM64_V8A/ + +# Generate Kotlin bindings using uniffi-bindgen +cargo run --bin uniffi-bindgen generate --library ./target/$COMPILATION_TARGET_ARM64_V8A/debug/$LIB_NAME --language kotlin --out-dir ../bdk-android/lib/src/main/kotlin/ --no-format diff --git a/bdk-android/scripts/build-linux-x86_64.sh b/bdk-android/scripts/release/build-release-linux-x86_64.sh similarity index 100% rename from bdk-android/scripts/build-linux-x86_64.sh rename to bdk-android/scripts/release/build-release-linux-x86_64.sh diff --git a/bdk-android/scripts/build-macos-aarch64.sh b/bdk-android/scripts/release/build-release-macos-aarch64.sh similarity index 100% rename from bdk-android/scripts/build-macos-aarch64.sh rename to bdk-android/scripts/release/build-release-macos-aarch64.sh diff --git a/bdk-android/scripts/build-windows-x86_64.sh b/bdk-android/scripts/release/build-release-windows-x86_64.sh similarity index 100% rename from bdk-android/scripts/build-windows-x86_64.sh rename to bdk-android/scripts/release/build-release-windows-x86_64.sh diff --git a/bdk-ffi/justfile b/bdk-ffi/justfile index ea2bea39..62cbde6a 100644 --- a/bdk-ffi/justfile +++ b/bdk-ffi/justfile @@ -4,6 +4,9 @@ default: build: cargo build --profile release-smaller +build-dev: + cargo build + check: cargo fmt cargo clippy From 8bfc0670e0be6c7e86b4ae4ab1f1b495e5ee4619 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 19 Nov 2025 14:53:38 +0000 Subject: [PATCH 13/21] Bump `bdk_kyoto` patch release Adds a performance improvement and a bug fix. --- bdk-ffi/Cargo.lock | 4 ++-- bdk-ffi/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index 61641d88..2e0afc1e 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "bdk_kyoto" -version = "0.15.2" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d1148f84df0d8ad287677457f15b554f7cc300a05101b15e31d20af1710a996" +checksum = "8da8010d2587aba368afd0be83ad87563f465744eb124cda431905cf06f8bd1c" dependencies = [ "bdk_wallet", "bip157", diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index 0033ef22..7e1e7cc1 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -18,7 +18,7 @@ path = "uniffi-bindgen.rs" bdk_wallet = { version = "2.2.0", features = ["all-keys", "keys-bip39", "rusqlite"] } bdk_esplora = { version = "0.22.1", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] } bdk_electrum = { version = "0.23.2", default-features = false, features = ["use-rustls-ring"] } -bdk_kyoto = { version = "0.15.2" } +bdk_kyoto = { version = "0.15.3" } uniffi = { version = "=0.30.0", features = ["cli"]} thiserror = "2.0.17" From 17539655ba9460e63e1670acfc635ab75e1030ea Mon Sep 17 00:00:00 2001 From: itorod Date: Fri, 14 Nov 2025 12:37:01 +0000 Subject: [PATCH 14/21] feat: Add wallet event --- bdk-ffi/src/types.rs | 153 ++++++++++++++++++++++++++++++++++++++++++ bdk-ffi/src/wallet.rs | 20 +++++- 2 files changed, 172 insertions(+), 1 deletion(-) diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 1671c096..2dbd08ab 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -23,6 +23,7 @@ use bdk_wallet::descriptor::policy::{ Condition as BdkCondition, PkOrF as BdkPkOrF, Policy as BdkPolicy, Satisfaction as BdkSatisfaction, SatisfiableItem as BdkSatisfiableItem, }; +use bdk_wallet::event::WalletEvent as BdkWalletEvent; #[allow(deprecated)] use bdk_wallet::signer::{SignOptions as BdkSignOptions, TapLeavesOptions}; use bdk_wallet::AddressInfo as BdkAddressInfo; @@ -32,6 +33,7 @@ use bdk_wallet::Update as BdkUpdate; use std::collections::{BTreeMap, BTreeSet, HashMap}; use std::convert::TryFrom; +use std::fmt::Display; use std::sync::{Arc, Mutex}; use crate::{impl_from_core_type, impl_into_core_type}; @@ -498,6 +500,157 @@ impl From for SatisfiableItem { } } +/// Events representing changes to wallet transactions. +/// +/// Returned after calling +/// [`Wallet::apply_update_events`](crate::wallet::Wallet::apply_update_events). +#[derive(Debug, Clone, uniffi::Enum)] +#[uniffi::export(Display)] +#[non_exhaustive] +pub enum WalletEvent { + /// The latest chain tip known to the wallet changed. + ChainTipChanged { + /// Previous chain tip. + old_tip: BlockId, + /// New chain tip. + new_tip: BlockId, + }, + /// A transaction is now confirmed. + /// + /// If the transaction was previously unconfirmed `old_block_time` will be `None`. + /// + /// If a confirmed transaction is now re-confirmed in a new block `old_block_time` will contain + /// the block id and the time it was previously confirmed. This can happen after a chain + /// reorg. + TxConfirmed { + /// Transaction id. + txid: Arc, + /// Transaction. + tx: Arc, + /// Confirmation block time. + block_time: ConfirmationBlockTime, + /// Old confirmation block and time if previously confirmed in a different block. + old_block_time: Option, + }, + /// A transaction is now unconfirmed. + /// + /// If the transaction is first seen in the mempool `old_block_time` will be `None`. + /// + /// If a previously confirmed transaction is now seen in the mempool `old_block_time` will + /// contain the block id and the time it was previously confirmed. This can happen after a + /// chain reorg. + TxUnconfirmed { + /// Transaction id. + txid: Arc, + /// Transaction. + tx: Arc, + /// Old confirmation block and time, if previously confirmed. + old_block_time: Option, + }, + /// An unconfirmed transaction was replaced. + /// + /// This can happen after an RBF is broadcast or if a third party double spends an input of + /// a received payment transaction before it is confirmed. + /// + /// The conflicts field contains the txid and vin (in which it conflicts) of the conflicting + /// transactions. + TxReplaced { + /// Transaction id. + txid: Arc, + /// Transaction. + tx: Arc, + /// Conflicting transactions. + conflicts: Vec, + }, + /// Unconfirmed transaction dropped. + /// + /// The transaction was dropped from the local mempool. This is generally due to the fee rate + /// being too low. The transaction can still reappear in the mempool in the future resulting in + /// a [`WalletEvent::TxUnconfirmed`] event. + TxDropped { + /// Transaction id. + txid: Arc, + /// Transaction. + tx: Arc, + }, +} + +impl Display for WalletEvent { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Represent a conflict in a replacement transaction. +#[uniffi::export(Display)] +#[derive(Debug, Clone, uniffi::Record)] +pub struct Conflict { + /// The index of the conflicting input. + pub vin: u32, + /// Conflicting transaction id. + pub txid: Arc, +} + +impl Display for Conflict { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +impl From for WalletEvent { + fn from(event: BdkWalletEvent) -> Self { + match event { + BdkWalletEvent::ChainTipChanged { old_tip, new_tip } => WalletEvent::ChainTipChanged { + old_tip: BlockId::from(old_tip), + new_tip: BlockId::from(new_tip), + }, + BdkWalletEvent::TxConfirmed { + txid, + tx, + block_time, + old_block_time, + } => WalletEvent::TxConfirmed { + txid: Arc::new(Txid(txid)), + tx: Arc::new(tx.as_ref().clone().into()), + block_time: block_time.into(), + old_block_time: old_block_time.map(|v| v.into()), + }, + BdkWalletEvent::TxUnconfirmed { + txid, + tx, + old_block_time, + } => WalletEvent::TxUnconfirmed { + txid: Arc::new(Txid(txid)), + tx: Arc::new(tx.as_ref().clone().into()), + old_block_time: old_block_time.map(|v| v.into()), + }, + BdkWalletEvent::TxReplaced { + txid, + tx, + conflicts, + } => { + let conflict_list: Vec = conflicts + .into_iter() + .map(|(vin, conflict)| Conflict { + vin: vin as u32, + txid: Arc::new(Txid(conflict)), + }) + .collect(); + WalletEvent::TxReplaced { + txid: Arc::new(Txid(txid)), + tx: Arc::new(tx.as_ref().clone().into()), + conflicts: conflict_list, + } + } + BdkWalletEvent::TxDropped { txid, tx } => WalletEvent::TxDropped { + txid: Arc::new(Txid(txid)), + tx: Arc::new(tx.as_ref().clone().into()), + }, + _ => unreachable!("WalletEvent variant not covered in conversion"), + } + } +} + #[derive(Debug, Clone, uniffi::Enum)] pub enum PkOrF { Pubkey { value: String }, diff --git a/bdk-ffi/src/wallet.rs b/bdk-ffi/src/wallet.rs index d4d87d47..47da506c 100644 --- a/bdk-ffi/src/wallet.rs +++ b/bdk-ffi/src/wallet.rs @@ -8,7 +8,7 @@ use crate::store::{PersistenceType, Persister}; use crate::types::{ AddressInfo, Balance, BlockId, CanonicalTx, ChangeSet, EvictedTx, FullScanRequestBuilder, KeychainAndIndex, LocalOutput, Policy, SentAndReceivedValues, SignOptions, SyncRequestBuilder, - UnconfirmedTx, Update, + UnconfirmedTx, Update, WalletEvent, }; use bdk_wallet::bitcoin::Network; @@ -320,6 +320,24 @@ impl Wallet { .map_err(CannotConnectError::from) } + /// Applies an update to the wallet, stages the changes, and returns events. + /// + /// Usually you create an `update` by interacting with some blockchain data source and inserting + /// transactions related to your wallet into it. Staged changes are NOT persisted. + /// + /// After applying updates you should process the events in your app before persisting the + /// staged wallet changes. For an example of how to persist staged wallet changes see + /// [`Wallet::reveal_next_address`]. + pub fn apply_update_events( + &self, + update: Arc, + ) -> Result, CannotConnectError> { + match self.get_wallet().apply_update_events(update.0.clone()) { + Ok(events) => Ok(events.into_iter().map(|e| e.into()).collect()), + Err(e) => Err(CannotConnectError::from(e)), + } + } + /// Apply relevant unconfirmed transactions to the wallet. /// Transactions that are not relevant are filtered out. pub fn apply_unconfirmed_txs(&self, unconfirmed_txs: Vec) { From d6af757bc691034563588eba034c422db5f8290d Mon Sep 17 00:00:00 2001 From: itorod Date: Tue, 4 Nov 2025 19:48:16 +0000 Subject: [PATCH 15/21] feat: Expose psbt output --- bdk-ffi/src/bitcoin.rs | 223 +++++++++++++++++++++++++++++++++++ bdk-ffi/src/tests/bitcoin.rs | 83 +++++++++++++ 2 files changed, 306 insertions(+) diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 40335362..bd7fc486 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -19,7 +19,12 @@ use bdk_wallet::bitcoin::hashes::sha256::Hash as BitcoinSha256Hash; use bdk_wallet::bitcoin::hashes::sha256d::Hash as BitcoinDoubleSha256Hash; use bdk_wallet::bitcoin::io::Cursor; use bdk_wallet::bitcoin::psbt::Input as BdkInput; +use bdk_wallet::bitcoin::psbt::Output as BdkOutput; use bdk_wallet::bitcoin::secp256k1::Secp256k1; + +use bdk_wallet::bitcoin::taproot::LeafNode as BdkLeafNode; +use bdk_wallet::bitcoin::taproot::NodeInfo as BdkNodeInfo; +use bdk_wallet::bitcoin::taproot::TapTree as BdkTapTree; use bdk_wallet::bitcoin::Amount as BdkAmount; use bdk_wallet::bitcoin::BlockHash as BitcoinBlockHash; use bdk_wallet::bitcoin::FeeRate as BdkFeeRate; @@ -804,6 +809,218 @@ impl From<&BdkInput> for Input { } } +/// Store information about taproot leaf node. +#[derive(Debug, uniffi::Object)] +#[uniffi::export(Display)] +pub struct LeafNode(BdkLeafNode); + +#[uniffi::export] +impl LeafNode { + /// Returns the depth of this script leaf in the tap tree. + pub fn depth(&self) -> u8 { + self.0.depth() + } + + /// Computes a leaf hash for this ScriptLeaf if the leaf is known. + /// This TapLeafHash is useful while signing taproot script spends. + /// See LeafNode::node_hash for computing the TapNodeHash which returns the hidden node hash if the node is hidden. + pub fn leaf_hash(&self) -> Option { + self.0.leaf_hash().map(|h| h.to_string()) + } + + /// Computes the [`TapNodeHash`] for this [`ScriptLeaf`]. This returns the + /// leaf hash if the leaf is known and the hidden node hash if the leaf is + /// hidden. + /// See also, [`bdk_electrum::bdk_core::bitcoin::taproot::LeafNode::leaf_hash`]. + pub fn node_hash(&self) -> String { + self.0.node_hash().to_string() + } + + /// Returns reference to the leaf script if the leaf is known. + pub fn script(&self) -> Option> { + self.0.script().map(|s| Arc::new(Script(s.to_owned()))) + } + + /// Returns leaf version of the script if the leaf is known. + pub fn leaf_version(&self) -> Option { + self.0.leaf_version().map(|n| n.to_consensus()) + } + + /// Returns reference to the merkle proof (hashing partners) to get this + /// node in form of [`TaprootMerkleBranch`]. + pub fn merkle_branch(&self) -> Vec { + self.0 + .merkle_branch() + .to_vec() + .iter() + .map(|h| h.to_string()) + .collect() + } +} + +impl Display for LeafNode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Taproot Tree representing a complete binary tree without any hidden nodes. +/// +/// This is in contrast to NodeInfo, which allows hidden nodes. The implementations for Eq, PartialEq and Hash compare the merkle root of the tree +#[derive(Debug, uniffi::Object)] +#[uniffi::export(Display)] +pub struct TapTree(BdkTapTree); + +#[uniffi::export] +impl TapTree { + /// Returns the root TapNodeHash of this tree. + pub fn root_hash(&self) -> String { + self.0.root_hash().to_string() + } + + /// Gets the reference to inner NodeInfo of this tree root. + pub fn node_info(&self) -> Arc { + Arc::new(NodeInfo(self.0.node_info().clone())) + } +} + +impl Display for TapTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +/// Represents the node information in taproot tree. In contrast to TapTree, this is allowed to have hidden leaves as children. +/// +/// Helper type used in merkle tree construction allowing one to build sparse merkle trees. The node represents part of the tree that has information about all of its descendants. See how TaprootBuilder works for more details. +/// You can use TaprootSpendInfo::from_node_info to a get a TaprootSpendInfo from the merkle root NodeInfo. +#[derive(Debug, uniffi::Object)] +#[uniffi::export(Display)] +pub struct NodeInfo(BdkNodeInfo); + +#[uniffi::export] +impl NodeInfo { + /// Creates an iterator over all leaves (including hidden leaves) in the tree. + pub fn leaf_nodes(&self) -> Vec> { + self.0 + .leaf_nodes() + .map(|ln| Arc::new(LeafNode(ln.clone()))) + .collect() + } + + /// Returns the root TapNodeHash of this node info. + pub fn node_hash(&self) -> String { + self.0.node_hash().to_string() + } +} + +impl Display for NodeInfo { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +/// A key-value map for an output of the corresponding index in the unsigned +/// transaction. +#[derive(Debug, uniffi::Record)] +pub struct Output { + /// The redeem script for this output. + pub redeem_script: Option>, + /// The witness script for this output. + pub witness_script: Option>, + /// Map of public keys needed to spend this output to their corresponding + /// master key fingerprints and derivation paths. + pub bip32_derivation: HashMap, + /// Taproot Internal key. + pub tap_internal_key: Option, + /// Taproot Output tree (structured record). + pub tap_tree: Option>, + /// Map of tap root x only keys to origin info and leaf hashes contained in it. + pub tap_key_origins: HashMap, + /// Proprietary key-value pairs for this output. + pub proprietary: HashMap>, + /// Unknown key-value pairs for this output. + pub unknown: HashMap>, +} + +impl From<&BdkOutput> for Output { + fn from(output: &BdkOutput) -> Self { + Output { + redeem_script: output + .redeem_script + .as_ref() + .map(|s| Arc::new(Script(s.clone()))), + witness_script: output + .witness_script + .as_ref() + .map(|s| Arc::new(Script(s.clone()))), + bip32_derivation: output + .bip32_derivation + .iter() + .map(|(pk, (fingerprint, deriv_path))| { + ( + pk.to_string(), + KeySource { + fingerprint: fingerprint.to_string(), + path: Arc::new(deriv_path.clone().into()), + }, + ) + }) + .collect(), + tap_internal_key: output.tap_internal_key.as_ref().map(|k| k.to_string()), + tap_tree: output + .tap_tree + .as_ref() + .map(|t| Arc::new(TapTree(t.clone()))), + tap_key_origins: output + .tap_key_origins + .iter() + .map(|(k, v)| { + let key = k.to_string(); + let value = TapKeyOrigin { + tap_leaf_hashes: v.0.iter().map(|h| h.to_string()).collect(), + key_source: KeySource { + // Unnecessary spaces being added by fmt. We use #[rustfmt::skip] to avoid them for now. + #[rustfmt::skip] + fingerprint: v.1.0.to_string(), + #[rustfmt::skip] + path: Arc::new(v.1.1.clone().into()), + }, + }; + (key, value) + }) + .collect(), + proprietary: output + .proprietary + .iter() + .map(|(k, v)| { + ( + ProprietaryKey { + prefix: k.prefix.clone(), + subtype: k.subtype, + key: k.key.clone(), + }, + v.to_vec(), + ) + }) + .collect(), + unknown: output + .unknown + .iter() + .map(|(k, v)| { + ( + Key { + key: k.key.clone(), + type_value: k.type_value, + }, + v.to_vec(), + ) + }) + .collect(), + } + } +} + /// A Partially Signed Transaction. #[derive(uniffi::Object)] pub struct Psbt(pub(crate) Mutex); @@ -935,6 +1152,12 @@ impl Psbt { let psbt = self.0.lock().unwrap(); psbt.inputs.iter().map(|input| input.into()).collect() } + + /// The corresponding key-value map for each output in the unsigned transaction. + pub fn output(&self) -> Vec { + let psbt = self.0.lock().unwrap(); + psbt.outputs.iter().map(|o| o.into()).collect() + } } impl From for Psbt { diff --git a/bdk-ffi/src/tests/bitcoin.rs b/bdk-ffi/src/tests/bitcoin.rs index 53ee36d8..1a831853 100644 --- a/bdk-ffi/src/tests/bitcoin.rs +++ b/bdk-ffi/src/tests/bitcoin.rs @@ -524,6 +524,84 @@ fn test_psbt_input_proprietary() { ); } +#[test] +fn test_psbt_output_length() { + let psbt = sample_psbt(); + let psbt_outputs = psbt.output(); + println!("Psbt Output: {:?}", psbt_outputs); + + assert_eq!(psbt_outputs.len(), 2); +} + +#[test] +fn test_psbt_output_witness_script() { + let psbt = sample_psbt(); + println!("Psbt: {:?}", psbt.json_serialize()); + let psbt_outputs = psbt.output(); + assert!(!psbt_outputs.is_empty(), "Output should not be empty"); + println!("Psbt Output: {:?}", psbt_outputs); + let output = &psbt_outputs[0]; + let witness_script = output + .witness_script + .as_ref() + .expect("Witness script should be present"); + let byte_witness = witness_script.to_bytes(); + let witness_hex = DisplayHex::to_lower_hex_string(&byte_witness); + let expected_witness_hex = "522103b72bf1f4c738fb44fadd3333789626fa5f3efb0d695c90d126abea721ef6d417210326ee4ece63eabe2ec81eddb5400ae49af6bd7d26cfa536e4ed1217a15a4a5ed621027a51e6ce68730ec4130e702921c9d6473de8151ebc517d5a83c8df93f48aba8a53ae"; + assert_eq!( + witness_hex, expected_witness_hex, + "Witness script hex does not match the expected value" + ); +} + +#[test] +fn test_psbt_output_tap_tree() { + let psbt = sample_psbt_taproot(); + println!("Psbt: {:?}", psbt.json_serialize()); + let psbt_outputs = psbt.output(); + assert!(!psbt_outputs.is_empty(), "Output should not be empty"); + println!("Psbt Output: {:?}", psbt_outputs); + let output = &psbt_outputs[1]; + let tap_tree = output + .tap_tree + .as_ref() + .expect("Tap tree should be present"); + let tap_tree_root_hash = tap_tree.root_hash(); + + let expected_tap_tree_root_hash = + "a5cc5e9312d2a08787c6597d71ba00733d0b13357aac952ce4b9519c72ffc2c5"; + assert_eq!( + tap_tree_root_hash, expected_tap_tree_root_hash, + "Tap tree root hash does not match the expected value" + ); + + //Check script and version + let expected_script_hex = "200f7e1b4af070857b37c203c8759915b7cb97ef99f7d3d9c51eb516791cdb7145ac20a2574e343ae4bcee78c2b061e508e5e817bfa7d8ac5a07f20ac9b39e9933df20ba20400d4657d75ff6b396b59c496f61e25d4d2fe489c792a777682f32655387cbcaba529c"; + let expected_leaf_version = Some(192u8); // 0xc0 which is default for Taproot scripts. 192 in decimal + + // Get script and version from the first leaf node + let node_info = tap_tree.node_info(); + println!("Tap tree node infos: {:?}", node_info); + + let leaf_nodes = node_info.leaf_nodes(); + let leaf_node = &leaf_nodes[0]; + let script = leaf_node.script(); + + let script_byte = script.unwrap().to_bytes(); + let script_hex = DisplayHex::to_lower_hex_string(&script_byte); + + let leaf_version = leaf_node.leaf_version(); + + assert_eq!( + script_hex, expected_script_hex, + "Tap tree script hex does not match the expected value" + ); + assert_eq!( + leaf_version, expected_leaf_version, + "Tap tree leaf version does not match the expected value" + ); +} + fn sample_psbt() -> Psbt { Psbt::new("cHNidP8BAH0CAAAAAXHl8cCbj84lm1v42e54IGI6CQru/nBXwrPE3q2fiGO4AAAAAAD9////Ar4DAAAAAAAAIgAgYw/rnGd4Bifj8s7TaMgR2tal/lq+L1jVv2Sqd1mxMbJEEQAAAAAAABYAFNVpt8vHYUPZNSF6Hu07uP1YeHts4QsAAAABALUCAAAAAAEBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/////BAJ+CwD/////AkAlAAAAAAAAIgAgQyrnn86L9D3vDiH959KJbPudDHc/bp6nI9E5EBLQD1YAAAAAAAAAACZqJKohqe3i9hw/cdHe/T+pmd+jaVN1XGkGiXmZYrSL69g2l06M+QEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQErQCUAAAAAAAAiACBDKuefzov0Pe8OIf3n0ols+50Mdz9unqcj0TkQEtAPViICAy4V+d/Qff71zzPXxK4FWG5x+wL/Ku93y/LG5p+0rI2xSDBFAiEA9b0OdASAs0P2uhQinjN7QGP5jX/b32LcShBmny8U0RUCIBebxvCDbpchCjqLAhOMjydT80DAzokaalGzV7XVTsbiASICA1tMY+46EgxIHU18bgHnUvAAlAkMq5LfwkpOGZ97sDKRRzBEAiBpmlZwJocNEiKLxexEX0Par6UgG8a89AklTG3/z9AHlAIgQH/ybCvfKJzr2dq0+IyueDebm7FamKIJdzBYWMXRr/wBIgID+aCzK9nclwhbbN7KbIVGUQGLWZsjcaqWPxk9gFeG+FxIMEUCIQDRPBzb0i9vaUmxCcs1yz8uq4tq1mdDAYvvYn3isKEhFAIgfmeTLLzMo0mmQ23ooMnyx6iPceE8xV5CvARuJsd88tEBAQVpUiEDW0xj7joSDEgdTXxuAedS8ACUCQyrkt/CSk4Zn3uwMpEhAy4V+d/Qff71zzPXxK4FWG5x+wL/Ku93y/LG5p+0rI2xIQP5oLMr2dyXCFts3spshUZRAYtZmyNxqpY/GT2AV4b4XFOuIgYDLhX539B9/vXPM9fErgVYbnH7Av8q73fL8sbmn7SsjbEYCapBE1QAAIABAACAAAAAgAAAAAAAAAAAIgYDW0xj7joSDEgdTXxuAedS8ACUCQyrkt/CSk4Zn3uwMpEY2bvrelQAAIABAACAAAAAgAAAAAAAAAAAIgYD+aCzK9nclwhbbN7KbIVGUQGLWZsjcaqWPxk9gFeG+FwYAKVFVFQAAIABAACAAAAAgAAAAAAAAAAAAAEBaVIhA7cr8fTHOPtE+t0zM3iWJvpfPvsNaVyQ0Sar6nIe9tQXIQMm7k7OY+q+Lsge3bVACuSa9r19Js+lNuTtEhehWkpe1iECelHmzmhzDsQTDnApIcnWRz3oFR68UX1ag8jfk/SKuopTriICAnpR5s5ocw7EEw5wKSHJ1kc96BUevFF9WoPI35P0irqKGAClRVRUAACAAQAAgAAAAIABAAAAAAAAACICAybuTs5j6r4uyB7dtUAK5Jr2vX0mz6U25O0SF6FaSl7WGAmqQRNUAACAAQAAgAAAAIABAAAAAAAAACICA7cr8fTHOPtE+t0zM3iWJvpfPvsNaVyQ0Sar6nIe9tQXGNm763pUAACAAQAAgAAAAIABAAAAAAAAAAAA".to_string()) .unwrap() @@ -533,3 +611,8 @@ fn sample_psbt2() -> Psbt { Psbt::new("cHNidP8BAFMBAAAAATkUkZZWjQ4TAMqaOkez2dl2+5yBsfd38qS6x8fkjesmAQAAAAD/////AXL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHAAAAAE8BBIiyHgAAAAAAAAAAAIc9/4HAL1JWI/0f5RZ+rDpVoEnePTFLtC7iJ//tN9UIAzmjYBMwFZfa70H75ZOgLMUT0LVVJ+wt8QUOLo/0nIXCDN6tvu8AAACAAQAAABD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFAAEAjwEAAAAAAQGJo8ceq00g4Dcbu6TMaY+ilclGOvouOX+FM8y2L5Vn5QEAAAAXFgAUvhjRUqmwEgOdrz2n3k9TNJ7suYX/////AXL++E4sAAAAF6kUM5cluiHv1irHU6m80GfWx6ajnQWHASED0uFWdJQbrUqZY3LLh+GFbTZSYG2YVi/jnF6efkE/IQUAAAAAAQEgcv74TiwAAAAXqRQzlyW6Ie/WKsdTqbzQZ9bHpqOdBYciAgM5iA3JI5S3NV49BDn6KDwx3nWQgS6gEcQkXAZ0poXog0cwRAIgT2fir7dhQtRPrliiSV0zo0GdqibNDbjQTzRStjKJrA8CIBB2Kp+2fpTMXK2QJvbcmf9/Bw9CeNMPvH0Mhp3TjH/nAQEDBIMAAAABBAFRIgYDOYgNySOUtzVePQQ5+ig8Md51kIEuoBHEJFwGdKaF6IMM3q2+7wAAAIABAAAAAQgGAgIBAwEFFQoYn3yLGjhv/o7tkbODDHp7zR53jAIBAiELoShx/uIQ+4YZKR6uoZRYHL0lMeSyN1nSJfaAaSP2MiICAQIVDBXMSeGRy8Ug2RlEYApct3r2qjKRAgECIQ12pWrO2RXSUT3NhMLDeLLoqlzWMrW3HKLyrFsOOmSb2wIBAhD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFACICAzmIDckjlLc1Xj0EOfooPDHedZCBLqARxCRcBnSmheiDDN6tvu8AAACAAQAAABD8BXByZWZ4KnRlc3Rfa2V5AwUGBwMJAAEDAwQFAA==".to_string()) .unwrap() } + +fn sample_psbt_taproot() -> Psbt { + Psbt::new("cHNidP8BAH0CAAAAARblbcPN67JMY1pAsqbkYuqfh+OffiMD1PXBKuohxHUhAAAAAAD9////AkQRAAAAAAAAFgAU1Wm3y8dhQ9k1IXoe7Tu4/Vh4e2wVv/UFAAAAACJRILJL6QjSVc9B74yO2wV9qJ1D2HkxpgKV/LRX3dOOV+uMOAMAAAABASsA4fUFAAAAACJRIMSkYUKwqnaNBsaJxcZ1MKFYDd+ZEmqOaLTAGheYLSWeQRQZmDg8WRPva5p6l4cMrRyqdLSCYC74Gk1Mn1aimc9eDHAZu3+0gymYN/cLd5pvviwpc9YiW6HwxS7yCJ5umnS6QFa6MEJrll8dUVdGve8T2Q7nNfN27yTe0dWHAMEL4AvvpJddyZugvr1WuK5CfNdNvHUfuHsWalE8dXsM2XYvy4UiFcEZmDg8WRPva5p6l4cMrRyqdLSCYC74Gk1Mn1aimc9eDGkgGZg4PFkT72uaepeHDK0cqnS0gmAu+BpNTJ9WopnPXgysIGzdf1E91bpWIz3gwC+dFe5OS1a+SUQsP12wvvnaryY4uiCU7qCfqcnKJ7j6aL4hZr1iSn3Rrt04wcmnQwovyqPzWbpSnMAhFhmYODxZE+9rmnqXhwytHKp0tIJgLvgaTUyfVqKZz14MOQFwGbt/tIMpmDf3C3eab74sKXPWIluh8MUu8giebpp0ur6IapxWAACAAQAAgAAAAIAAAAAAAAAAACEWbN1/UT3VulYjPeDAL50V7k5LVr5JRCw/XbC++dqvJjg5AXAZu3+0gymYN/cLd5pvviwpc9YiW6HwxS7yCJ5umnS6WyNan1YAAIABAACAAAAAgAAAAAAAAAAAIRaU7qCfqcnKJ7j6aL4hZr1iSn3Rrt04wcmnQwovyqPzWTkBcBm7f7SDKZg39wt3mm++LClz1iJbofDFLvIInm6adLqsWpreVgAAgAEAAIAAAACAAAAAAAAAAAABFyAZmDg8WRPva5p6l4cMrRyqdLSCYC74Gk1Mn1aimc9eDAEYIHAZu3+0gymYN/cLd5pvviwpc9YiW6HwxS7yCJ5umnS6AAABBSAPfhtK8HCFezfCA8h1mRW3y5fvmffT2cUetRZ5HNtxRQEGawDAaCAPfhtK8HCFezfCA8h1mRW3y5fvmffT2cUetRZ5HNtxRawgoldONDrkvO54wrBh5Qjl6Be/p9isWgfyCsmznpkz3yC6IEANRlfXX/azlrWcSW9h4l1NL+SJx5Knd2gvMmVTh8vKulKcIQcPfhtK8HCFezfCA8h1mRW3y5fvmffT2cUetRZ5HNtxRTkBpcxekxLSoIeHxll9cboAcz0LEzV6rJUs5LlRnHL/wsW+iGqcVgAAgAEAAIAAAACAAQAAAAAAAAAhB0ANRlfXX/azlrWcSW9h4l1NL+SJx5Knd2gvMmVTh8vKOQGlzF6TEtKgh4fGWX1xugBzPQsTNXqslSzkuVGccv/Cxaxamt5WAACAAQAAgAAAAIABAAAAAAAAACEHoldONDrkvO54wrBh5Qjl6Be/p9isWgfyCsmznpkz3yA5AaXMXpMS0qCHh8ZZfXG6AHM9CxM1eqyVLOS5UZxy/8LFWyNan1YAAIABAACAAAAAgAEAAAAAAAAAAA==".to_string()) + .unwrap() +} From fa23c99c2857cac0c411c4c8799b4969486d07b5 Mon Sep 17 00:00:00 2001 From: Matthew Date: Mon, 24 Nov 2025 15:08:39 -0600 Subject: [PATCH 16/21] chore: add swift development script and commands --- bdk-swift/build-xcframework-dev.sh | 55 ++++++++++++++++++++++++++++++ bdk-swift/justfile | 5 +++ 2 files changed, 60 insertions(+) create mode 100755 bdk-swift/build-xcframework-dev.sh diff --git a/bdk-swift/build-xcframework-dev.sh b/bdk-swift/build-xcframework-dev.sh new file mode 100755 index 00000000..4513a893 --- /dev/null +++ b/bdk-swift/build-xcframework-dev.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +set -euo pipefail + +HEADERPATH="Sources/BitcoinDevKit/BitcoinDevKitFFI.h" +MODMAPPATH="Sources/BitcoinDevKit/BitcoinDevKitFFI.modulemap" +TARGETDIR="../bdk-ffi/target" +OUTDIR="." +NAME="bdkffi" +STATIC_LIB_NAME="lib${NAME}.a" +NEW_HEADER_DIR="../bdk-ffi/target/include" +PROFILE_DIR="debug" + +HOST_ARCH=$(uname -m) +if [ "$HOST_ARCH" = "arm64" ]; then + MAC_TARGET="aarch64-apple-darwin" + IOS_SIM_TARGET="aarch64-apple-ios-sim" +else + MAC_TARGET="x86_64-apple-darwin" + IOS_SIM_TARGET="x86_64-apple-ios" +fi +IOS_DEVICE_TARGET="aarch64-apple-ios" + +cd ../bdk-ffi/ || exit + +rustup component add rust-src +rustup target add "$MAC_TARGET" "$IOS_SIM_TARGET" "$IOS_DEVICE_TARGET" + +cargo build --package bdk-ffi --target "$MAC_TARGET" +cargo build --package bdk-ffi --target "$IOS_SIM_TARGET" +cargo build --package bdk-ffi --target "$IOS_DEVICE_TARGET" + +cargo run --bin uniffi-bindgen generate \ + --library "./target/$IOS_DEVICE_TARGET/$PROFILE_DIR/lib${NAME}.dylib" \ + --language swift \ + --out-dir ../bdk-swift/Sources/BitcoinDevKit \ + --no-format + +cd ../bdk-swift/ || exit + +mkdir -p "$NEW_HEADER_DIR" +mv "$HEADERPATH" "$NEW_HEADER_DIR" +mv "$MODMAPPATH" "$NEW_HEADER_DIR/module.modulemap" +echo >> "$NEW_HEADER_DIR/module.modulemap" + +rm -rf "${OUTDIR}/${NAME}.xcframework" + +xcodebuild -create-xcframework \ + -library "${TARGETDIR}/${MAC_TARGET}/${PROFILE_DIR}/${STATIC_LIB_NAME}" \ + -headers "${NEW_HEADER_DIR}" \ + -library "${TARGETDIR}/${IOS_DEVICE_TARGET}/${PROFILE_DIR}/${STATIC_LIB_NAME}" \ + -headers "${NEW_HEADER_DIR}" \ + -library "${TARGETDIR}/${IOS_SIM_TARGET}/${PROFILE_DIR}/${STATIC_LIB_NAME}" \ + -headers "${NEW_HEADER_DIR}" \ + -output "${OUTDIR}/${NAME}.xcframework" diff --git a/bdk-swift/justfile b/bdk-swift/justfile index 4c3414f2..d3a4d5a7 100644 --- a/bdk-swift/justfile +++ b/bdk-swift/justfile @@ -13,6 +13,11 @@ repo: build: bash ./build-xcframework.sh +[group("Build")] +[doc("Build the library in debug mode for faster local testing.")] +build-dev: + bash ./build-xcframework-dev.sh + [group("Build")] [doc("Remove all caches and previous build artifacts to start from scratch.")] clean: From 1ff5631c174edc86b1d2905ea7f29b16ede9f1b1 Mon Sep 17 00:00:00 2001 From: Itoro Ukpong Date: Mon, 1 Dec 2025 19:18:00 +0400 Subject: [PATCH 17/21] fix: remove unwanted software for more space in ci --- .github/workflows/test-android.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/test-android.yaml b/.github/workflows/test-android.yaml index 7ca81553..4caa7666 100644 --- a/.github/workflows/test-android.yaml +++ b/.github/workflows/test-android.yaml @@ -48,6 +48,15 @@ jobs: cd bdk-android bash ./scripts/release/build-release-linux-x86_64.sh + - name: "Free Disk Space (Ubuntu)" + uses: AdityaGarg8/remove-unwanted-software@v5 + with: + remove-dotnet: true + remove-haskell: true + remove-codeql: true + remove-docker-images: true + remove-large-packages: true + - name: "Enable KVM" run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules From 677a60e3667a55bd5613db9b6407f43116f3036f Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Tue, 2 Dec 2025 14:41:29 +0000 Subject: [PATCH 18/21] feat(kyoto): Add optional new wallet sync height For users recovering an old wallet, it is best to used the pre-defined points like segwit or taproot; however, new users that have definitely not used their descriptor may be caught off guard if they have to wait for their wallet to sync for more than a couple seconds. This change allows for a recovery from any hash and height, with the idea being the developer can provide a recent block via a service like mempool(dot)space --- bdk-ffi/src/kyoto.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/bdk-ffi/src/kyoto.rs b/bdk-ffi/src/kyoto.rs index 3008df69..2323e94d 100644 --- a/bdk-ffi/src/kyoto.rs +++ b/bdk-ffi/src/kyoto.rs @@ -27,6 +27,7 @@ use crate::bitcoin::BlockHash; use crate::bitcoin::Transaction; use crate::bitcoin::Wtxid; use crate::error::CbfError; +use crate::types::BlockId; use crate::types::Update; use crate::wallet::Wallet; use crate::FeeRate; @@ -185,7 +186,7 @@ impl CbfBuilder { trusted_peers.push(peer.clone().into()); } - let scan_type = match self.scan_type { + let scan_type = match self.scan_type.clone() { ScanType::Sync => bdk_kyoto::ScanType::Sync, ScanType::Recovery { used_script_index, @@ -212,6 +213,10 @@ impl CbfBuilder { used_script_index, checkpoint: HeaderCheckpoint::taproot_activation(), }, + RecoveryPoint::Other { birthday } => bdk_kyoto::ScanType::Recovery { + used_script_index, + checkpoint: HeaderCheckpoint::new(birthday.height, birthday.hash.0), + }, } } } @@ -457,7 +462,7 @@ impl From for Warning { /// Sync a wallet from the last known block hash or recover a wallet from a specified recovery /// point. -#[derive(Debug, Clone, Copy, Default, uniffi::Enum)] +#[derive(Debug, Clone, Default, uniffi::Enum)] pub enum ScanType { /// Sync an existing wallet from the last stored chain checkpoint. #[default] @@ -472,12 +477,15 @@ pub enum ScanType { }, } -#[derive(Debug, Clone, Copy, Default, uniffi::Enum)] +#[derive(Debug, Clone, Default, uniffi::Enum)] pub enum RecoveryPoint { GenesisBlock, #[default] SegwitActivation, TaprootActivation, + Other { + birthday: BlockId, + }, } /// A peer to connect to over the Bitcoin peer-to-peer network. From 79609eff664610a2bd997dc4e3b3198fb7462656 Mon Sep 17 00:00:00 2001 From: itorod Date: Wed, 10 Dec 2025 16:48:26 +0000 Subject: [PATCH 19/21] fix: Stop removal of large files in android CI - There is no control over what files the remove-large-packages property removes. Best to set this to false. --- .github/workflows/test-android.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-android.yaml b/.github/workflows/test-android.yaml index 4caa7666..dbe6db07 100644 --- a/.github/workflows/test-android.yaml +++ b/.github/workflows/test-android.yaml @@ -55,7 +55,7 @@ jobs: remove-haskell: true remove-codeql: true remove-docker-images: true - remove-large-packages: true + remove-large-packages: false - name: "Enable KVM" run: | @@ -70,4 +70,5 @@ jobs: target: google_apis arch: x86_64 profile: Nexus 6 + emulator-options: "-no-window -gpu swiftshader_indirect -no-snapshot" script: cd bdk-android && ./gradlew connectedAndroidTest --console=plain From f21ac743c2ef423cb4ce11ae3974e3c0436eaea5 Mon Sep 17 00:00:00 2001 From: Matthew Date: Tue, 25 Nov 2025 13:28:41 -0600 Subject: [PATCH 20/21] feat: expose `derived_address` helper on `Descriptor` --- bdk-ffi/src/descriptor.rs | 29 ++++++++++++++++++++++++++ bdk-ffi/src/tests/descriptor.rs | 36 +++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/bdk-ffi/src/descriptor.rs b/bdk-ffi/src/descriptor.rs index c07de859..e52769d7 100644 --- a/bdk-ffi/src/descriptor.rs +++ b/bdk-ffi/src/descriptor.rs @@ -1,3 +1,4 @@ +use crate::bitcoin::Address; use crate::bitcoin::DescriptorId; use crate::bitcoin::DescriptorType; use crate::error::DescriptorError; @@ -12,6 +13,7 @@ use bdk_wallet::chain::DescriptorExt; use bdk_wallet::descriptor::{ExtendedDescriptor, IntoWalletDescriptor}; use bdk_wallet::keys::DescriptorPublicKey as BdkDescriptorPublicKey; use bdk_wallet::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap}; +use bdk_wallet::miniscript::descriptor::ConversionError; use bdk_wallet::template::{ Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public, DescriptorTemplate, @@ -355,6 +357,33 @@ impl Descriptor { pub fn desc_type(&self) -> DescriptorType { self.extended_descriptor.desc_type() } + + pub fn derive_address( + &self, + index: u32, + network: Network, + ) -> Result, DescriptorError> { + if self.extended_descriptor.is_multipath() { + return Err(DescriptorError::MultiPath); + } + + let derived_descriptor = self + .extended_descriptor + .at_derivation_index(index) + .map_err(|error| match error { + ConversionError::HardenedChild => DescriptorError::HardenedDerivationXpub, + ConversionError::MultiKey => DescriptorError::MultiPath, + })?; + + let address = derived_descriptor + .address(network) + .map_err(|error| DescriptorError::Miniscript { + error_message: error.to_string(), + })? + .into(); + + Ok(Arc::new(address)) + } } impl Display for Descriptor { diff --git a/bdk-ffi/src/tests/descriptor.rs b/bdk-ffi/src/tests/descriptor.rs index fed07b89..c6dcb432 100644 --- a/bdk-ffi/src/tests/descriptor.rs +++ b/bdk-ffi/src/tests/descriptor.rs @@ -161,3 +161,39 @@ fn test_max_weight_to_satisfy() { // Verify the method works and returns a positive weight assert!(weight > 0, "Weight must be positive"); } + +#[test] +fn test_descriptor_derive_address() { + let descriptor = Descriptor::new_bip84( + &get_descriptor_secret_key(), + KeychainKind::External, + Network::Testnet, + ); + + let derived = descriptor + .derive_address(0, Network::Testnet) + .expect("derive address"); + + let expected_descriptor = descriptor + .extended_descriptor + .at_derivation_index(0) + .expect("derive descriptor"); + let expected_address = expected_descriptor + .address(Network::Testnet) + .expect("address from descriptor"); + + assert_eq!(derived.to_string(), expected_address.to_string()); +} + +#[test] +fn test_descriptor_derive_address_multipath_error() { + let descriptor = Descriptor::new( + "wpkh([9a6a2580/84'/1'/0']tpubDDnGNapGEY6AZAdQbfRJgMg9fvz8pUBrLwvyvUqEgcUfgzM6zc2eVK4vY9x9L5FJWdX8WumXuLEDV5zDZnTfbn87vLe9XceCFwTu9so9Kks/<0;1>/*)".to_string(), + Network::Testnet, + ) + .expect("multipath descriptor parses"); + + let error = descriptor.derive_address(0, Network::Testnet).unwrap_err(); + + assert_matches!(error, DescriptorError::MultiPath); +} From 08d700f695cb55641754b58649bb64c696d3af0d Mon Sep 17 00:00:00 2001 From: thunderbiscuit Date: Fri, 12 Dec 2025 09:50:14 -0500 Subject: [PATCH 21/21] chore: bump bdk_wallet dependency to 2.3.0 --- bdk-ffi/Cargo.lock | 4 ++-- bdk-ffi/Cargo.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index 2e0afc1e..e64302aa 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "bdk_wallet" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b172f2caa6311b8172cf99559cd7f7a61cb58834e35e4ca208b3299e7be8bec" +checksum = "b03f1e31ccc562f600981f747d2262b84428cbff52c9c9cdf14d15fb15bd2286" dependencies = [ "bdk_chain", "bip39", diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index 7e1e7cc1..b68e47f5 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -15,7 +15,7 @@ name = "uniffi-bindgen" path = "uniffi-bindgen.rs" [dependencies] -bdk_wallet = { version = "2.2.0", features = ["all-keys", "keys-bip39", "rusqlite"] } +bdk_wallet = { version = "2.3.0", features = ["all-keys", "keys-bip39", "rusqlite"] } bdk_esplora = { version = "0.22.1", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] } bdk_electrum = { version = "0.23.2", default-features = false, features = ["use-rustls-ring"] } bdk_kyoto = { version = "0.15.3" }