Compare commits

..

2 Commits

Author SHA1 Message Date
thunderbiscuit
189a9c08aa chore: add developer information for jvm, android, and python libraries 2024-03-27 09:30:59 -04:00
thunderbiscuit
3d3dbef90b chore: update libraries to official alpha 7 release versions 2024-03-26 14:45:40 -04:00
61 changed files with 856 additions and 1276 deletions

View File

@@ -6,90 +6,84 @@ labels: 'release'
assignees: '' assignees: ''
--- ---
# Part 1: Bump BDK Rust Version ## Create a new minor release
## Bumping 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. 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. 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 ### Specific Libraries' Workflows
#### _Android_
### _Android_
3. - [ ] Update the API docs to reflect the changes in the API 3. - [ ] Update the API docs to reflect the changes in the API
4. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root, `lib`, and `plugins`) in the bdk-android directory to make sure you're building the library from scratch. 4. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch.
5. - [ ] Build the library and run the offline and live tests, and adjust them if necessary (note that you'll need an Android emulator running). 5. - [ ] Build the library and run the tests, and adjust if necessary.
1. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root, `lib`, and `plugins`) in bdk-android directory to make sure you're building the library from scratch.
2. - [ ] 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 ```shell
# start an emulator prior to running the tests # start an emulator prior to running the tests
cd ./bdk-android/ cd ./bdk-android/
just clean ./gradlew buildAndroidLib
just build ./gradlew connectedAndroidTest
just test
``` ```
6. - [ ] Update the readme if necessary 6. - [ ] Update the readme if necessary
1. - [ ] Update the readme if necessary
### _JVM_ #### _JVM_
7. - [ ] Update the API docs to reflect the changes in the API 7. - [ ] Update the API docs to reflect the changes in the API
8. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root, `lib`, and `plugins`) in bdk-jvm directory to make sure you're building the library from scratch. 8. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch
9. - [ ] Build the library and run the tests, and adjust if necessary 9. - [ ] Build the library and run the tests, and adjust if necessary
2. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root, `lib`, and `plugins`) in bdk-android directory to make sure you're building the library from scratch.
3. - [ ] Build the library and run the tests, and adjust if necessary
```shell ```shell
cd ./bdk-jvm/ cd ./bdk-jvm/
just clean ./gradlew buildJvmLib
just build ./gradlew test
just test
``` ```
10. - [ ] Update the readme if necessary 10. - [ ] Update the readme if necessary
1. - [ ] Update the readme if necessary
### _Swift_ #### _Swift_
11. - [ ] Run the tests and adjust if necessary
11. - [ ] Delete the `target` directory in bdk-ffi 1. - [ ] Run the tests and adjust if necessary
12. - [ ] Run the tests and adjust if necessary
```shell ```shell
./bdk-swift/build-local-swift.sh
cd ./bdk-swift/ cd ./bdk-swift/
just clean swift test
just build
just test
``` ```
13. - [ ] Update the readme if necessary 12. - [ ] Update the readme if necessary
1. - [ ] Update the readme if necessary
### _Python_ #### _Python_
13. - [ ] Delete the `dist`, `build`, and `bdkpython.egg-info` and rust `target` directories to make sure you are building the library from scratch without any caches
14. - [ ] Delete the `dist`, `build`, and `bdkpython.egg-info` and rust `target` directories to make sure you are building the library from scratch without any caches 14. - [ ] Build the library
15. - [ ] Build the library
```shell ```shell
cd ./bdk-python/ cd ./bdk-python/
just clean
pip3 install --requirement requirements.txt pip3 install --requirement requirements.txt
bash ./scripts/generate-macos-arm64.sh # run the script for your particular platform bash ./scripts/generate-macos-arm64.sh # run the script for your particular platform
python3 setup.py --verbose bdist_wheel python3 setup.py --verbose bdist_wheel
``` ```
16. - [ ] Run the tests and adjust if necessary 1. - [ ] Run the tests and adjust if necessary
```shell ```shell
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
python -m unittest --verbose python -m unittest --verbose
``` ```
17. - [ ] Update the readme and `setup.py` if necessary 1. - [ ] Update the readme and `setup.py` if necessary
18. - [ ] Update the Android, JVM, Python, and Swift libraries as per the _Specific Libraries' Workflows_ 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).
## Part 3: Release Workflow ### Release Workflow
17. - [ ] Update the Android, JVM, Python, and Swift libraries as per the _Specific Libraries' Workflows_ 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).
19. - [ ] Create a new branch off of `master` called `release/<feature version>`, e.g. `release/0.31` 18. - [ ] Create a new branch off of `master` called `release/version`
20. - [ ] Update bdk-android version from `SNAPSHOT` version to release version 19. - [ ] Update bdk-android version from `SNAPSHOT` version to release version
21. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version 20. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
22. - [ ] Update bdk-python version from `.dev` version to release version 21. - [ ] Update bdk-python version from `.dev` version to release version
23. - [ ] Open a PR to that release branch that updates the Android, JVM, and Python libraries' versions in the three steps above. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/316). 22. - [ ] Open a PR to that release branch that updates the Android, JVM, and Python libraries' versions in step 19, 20, and 21. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/316).
24. - [ ] Get a review and ACK and merge the PR updating all the languages to their release versions 23. - [ ] Get a review and ACK and merge the PR updating all the languages to their release versions
25. - [ ] 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. 24. - [ ] 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 ```shell
git tag v0.6.0 --sign --edit git tag v0.6.0 --sign --edit
git push upstream v0.6.0 git push upstream v0.6.0
``` ```
26. - [ ] Trigger manual releases for all 4 libraries (for Swift, go on the [bdk-swift](https://github.com/bitcoindevkit/bdk-swift) trigger the release on `master` and 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`). 25. - [ ] Trigger manual releases for all 4 libraries (for Swift, trigger the release on `master` and simply add the version number in the text field when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`)
27. - [ ] Make sure the released libraries work and contain the artifacts you would expect 26. - [ ] Make sure the released libraries work and contain the artifacts you would expect
28. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file 27. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file
29. - [ ] Bump the versions on master from `0.9.0-SNAPSHOT` to `0.10.0-SNAPSHOT`, `0.6.0.dev0` to `0.7.0.dev0` 28. - [ ] Bump the versions on master from `0.9.0-SNAPSHOT` to `0.10.0-SNAPSHOT`, `0.6.0.dev0` to `0.7.0.dev0`
30. - [ ] Apply changes to the minor_release and patch_release issue templates if they need any 29. - [ ] Apply changes to the minor_release and patch_release issue templates if they need any
31. - [ ] Open a PR on master with the changes in steps 29, 30, and 31. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/317). Get a review and merge the PR. 30. - [ ] Open a PR on master with the changes in steps 29, 30, and 31. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/317). Get a review and merge the PR.
32. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one) 31. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one)
33. - [ ] Post in the announcement channel 32. - [ ] Post in the announcement channel
34. - [ ] Tweet about the library 33. - [ ] Tweet about the library

View File

@@ -17,7 +17,7 @@ jobs:
strategy: strategy:
matrix: matrix:
rust: rust:
- version: 1.77.1 - version: 1.73.0
clippy: true clippy: true
steps: steps:
- name: "Checkout" - name: "Checkout"

View File

@@ -27,8 +27,8 @@ jobs:
distribution: temurin distribution: temurin
java-version: 11 java-version: 11
- name: "Set default Rust version to 1.77.1" - name: "Set default Rust version to 1.73.0"
run: rustup default 1.77.1 run: rustup default 1.73.0
- name: "Build bdk-jvm library" - name: "Build bdk-jvm library"
run: | run: |

View File

@@ -23,10 +23,10 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 11
- name: "Set default Rust version to 1.77.1" - name: "Set default Rust version to 1.73.0"
run: rustup default 1.77.1 run: rustup default 1.73.0
- name: "Install Rust Android targets" - name: "Install Rust Android targets"
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
@@ -36,7 +36,7 @@ jobs:
cd bdk-android cd bdk-android
./gradlew buildAndroidLib ./gradlew buildAndroidLib
- name: "Publish to Maven Central" - name: "Publish to Maven Local and Maven Central"
env: env:
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }} ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }} ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}

View File

@@ -22,10 +22,10 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 11
- name: "Set default Rust version to 1.77.1" - name: "Set default Rust version to 1.73.0"
run: rustup default 1.77.1 run: rustup default 1.73.0
- name: "Install aarch64 Rust target" - name: "Install aarch64 Rust target"
run: rustup target add aarch64-apple-darwin run: rustup target add aarch64-apple-darwin
@@ -52,10 +52,10 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 11
- name: "Set default Rust version to 1.77.1" - name: "Set default Rust version to 1.73.0"
run: rustup default 1.77.1 run: rustup default 1.73.0
- name: "Install x86_64-pc-windows-msvc Rust target" - name: "Install x86_64-pc-windows-msvc Rust target"
run: rustup target add x86_64-pc-windows-msvc run: rustup target add x86_64-pc-windows-msvc
@@ -92,10 +92,10 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 11
- name: "Set default Rust version to 1.77.1" - name: "Set default Rust version to 1.73.0"
run: rustup default 1.77.1 run: rustup default 1.73.0
- name: "Build bdk-jvm library" - name: "Build bdk-jvm library"
run: | run: |

View File

@@ -24,8 +24,6 @@ jobs:
- cp38-cp38 - cp38-cp38
- cp39-cp39 - cp39-cp39
- cp310-cp310 - cp310-cp310
- cp311-cp311
- cp312-cp312
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -61,8 +59,6 @@ jobs:
- "3.8" - "3.8"
- "3.9" - "3.9"
- "3.10" - "3.10"
- "3.11"
- "3.12"
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -100,8 +96,6 @@ jobs:
- "3.8" - "3.8"
- "3.9" - "3.9"
- "3.10" - "3.10"
- "3.11"
- "3.12"
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -138,8 +132,6 @@ jobs:
- "3.8" - "3.8"
- "3.9" - "3.9"
- "3.10" - "3.10"
- "3.11"
- "3.12"
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@@ -35,10 +35,10 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 11
- name: "Set default Rust version to 1.77.1" - name: "Set default Rust version to 1.73.0"
run: rustup default 1.77.1 run: rustup default 1.73.0
- name: "Install Rust Android targets" - name: "Install Rust Android targets"
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi

View File

@@ -30,10 +30,10 @@ jobs:
uses: actions/setup-java@v3 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: 17 java-version: 11
- name: "Set default Rust version to 1.77.1" - name: "Set default Rust version to 1.73.0"
run: rustup default 1.77.1 run: rustup default 1.73.0
- name: "Run JVM tests" - name: "Run JVM tests"
run: | run: |

View File

@@ -33,8 +33,6 @@ jobs:
- cp38-cp38 - cp38-cp38
- cp39-cp39 - cp39-cp39
- cp310-cp310 - cp310-cp310
- cp311-cp311
- cp312-cp312
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -76,8 +74,6 @@ jobs:
- "3.8" - "3.8"
- "3.9" - "3.9"
- "3.10" - "3.10"
- "3.11"
- "3.12"
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -121,8 +117,6 @@ jobs:
- "3.8" - "3.8"
- "3.9" - "3.9"
- "3.10" - "3.10"
- "3.11"
- "3.12"
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3
@@ -164,8 +158,6 @@ jobs:
- "3.8" - "3.8"
- "3.9" - "3.9"
- "3.10" - "3.10"
- "3.11"
- "3.12"
steps: steps:
- name: "Checkout" - name: "Checkout"
uses: actions/checkout@v3 uses: actions/checkout@v3

View File

@@ -19,8 +19,7 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- name: "Build Swift package" - name: "Build Swift package"
working-directory: bdk-swift run: bash ./bdk-swift/build-local-swift.sh
run: bash ./build-local-swift.sh
- name: "Run Swift tests" - name: "Run Swift tests"
working-directory: bdk-swift working-directory: bdk-swift

View File

@@ -3,9 +3,6 @@ Changelog information can also be found in each release's git tag (which can be
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). 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).
## [1.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.
## [1.0.0-alpha.2a] ## [1.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: 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 - Create and recover wallets using descriptors, including the four descriptor templates
@@ -245,8 +242,6 @@ Changelog
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding [BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
[1.0.0-alpha.7]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.2a...v1.0.0-alpha.7
[1.0.0-alpha.2a]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.31.0...v1.0.0-alpha.2a
[v0.31.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.30.0...v0.31.0 [v0.31.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.30.0...v0.31.0
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0 [v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
[v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0 [v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0

View File

@@ -26,21 +26,22 @@ The below directories (a separate repository in the case of bdk-swift) include i
| Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | | | Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | |
| Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | | | Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | |
## Building and Testing the Libraries
If you are familiar with the build tools for the specific languages you wish to build the libraries for, you can use their normal build/test workflows. We also include some [just](https://just.systems/) files to simplify the work across different languages. If you have the just tool installed on your system, you can simply call the commands defined in the `justfile`s, for example:
```sh
cd bdk-android
just build
just offlinetests
just publishlocal
```
## Minimum Supported Rust Version (MSRV) ## Minimum Supported Rust Version (MSRV)
This library should compile with any combination of features with Rust 1.77.1.
This library should compile with any combination of features with Rust 1.73.0.
## Contributing ## Contributing
To add new structs and functions, see the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/) and the [uniffi-examples](https://thunderbiscuit.github.io/uniffi-examples/) repository.
### Adding new structs and functions
See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
#### For pass by value objects
1. Create new rust struct with only fields that are supported UniFFI types
2. Update mapping `bdk.udl` file with new `dictionary`
#### For pass by reference values
1. Create wrapper rust struct/impl with only fields that are `Sync + Send`
2. Update mapping `bdk.udl` file with new `interface`
## Goals ## Goals
1. Language bindings should feel idiomatic in target languages/platforms 1. Language bindings should feel idiomatic in target languages/platforms

View File

@@ -13,6 +13,33 @@ dependencies {
} }
``` ```
You may then import and use the `org.bitcoindevkit` library in your Kotlin code like so. Note that this example is for the `0.30.0` release. For examples of the 1.0 API in the alpha releases, take a look at the tests [here](https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit).
```kotlin
import org.bitcoindevkit.*
// ...
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
val esploraClient: EsploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val wallet: Wallet = Wallet(
descriptor = externalDescriptor,
changeDescriptor = internalDescriptor,
persistenceBackendPath = "./bdkwallet.db",
network = Network.TESTNET
)
val update = esploraClient.fullScan(
wallet = wallet,
stopGap = 10uL,
parallelRequests = 1uL
)
wallet.applyUpdate(update)
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
```
### Snapshot releases ### Snapshot releases
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block: To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
```kotlin ```kotlin
@@ -31,17 +58,17 @@ dependencies {
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet) * [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
### How to build ### How to build
_Note that Kotlin version `1.9.23` or later is required to build the library._ _Note that Kotlin version `1.6.10` or later is required to build the library._
1. Clone this repository. 1. Clone this repository.
```shell ```shell
git clone https://github.com/bitcoindevkit/bdk-ffi git clone https://github.com/bitcoindevkit/bdk-ffi
``` ```
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions. 2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
3. Install Rust (note that we are currently building using Rust 1.77.1): 3. Install Rust (note that we are currently building using Rust 1.73.0):
```shell ```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default 1.77.1 rustup default 1.73.0
``` ```
4. Install required targets 4. Install required targets
```sh ```sh
@@ -68,7 +95,7 @@ export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
## How to publish to your local Maven repo ## How to publish to your local Maven repo
```shell ```shell
cd bdk-android cd bdk-android
./gradlew publishToMavenLocal -P localBuild ./gradlew publishToMavenLocal --exclude-task signMavenPublication
``` ```
Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so: Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so:
@@ -77,7 +104,7 @@ signing.gnupg.keyName=<YOUR_GNUPG_ID>
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE> signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
``` ```
and use the `publishToMavenLocal` task without the `localBuild` flag: and use the `publishToMavenLocal` task without excluding the signing task:
```shell ```shell
./gradlew publishToMavenLocal ./gradlew publishToMavenLocal
``` ```

View File

@@ -1,10 +1,14 @@
buildscript {
repositories {
google()
}
dependencies {
classpath("com.android.tools.build:gradle:7.1.2")
}
}
plugins { plugins {
id("com.android.library").version("8.3.1").apply(false) id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
id("org.jetbrains.kotlin.android").version("1.9.23").apply(false)
id("org.gradle.maven-publish")
id("org.gradle.signing")
id("org.bitcoindevkit.plugins.generate-android-bindings").apply(false)
id("io.github.gradle-nexus.publish-plugin").version("1.1.0").apply(true)
} }
// library version is defined in gradle.properties // library version is defined in gradle.properties

View File

@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
kotlin.code.style=official kotlin.code.style=official
libraryVersion=1.0.0-alpha.9 libraryVersion=1.0.0-alpha.7

Binary file not shown.

View File

@@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

41
bdk-android/gradlew vendored
View File

@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,11 +80,13 @@ do
esac esac
done done
# This is normally unused APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -131,29 +133,22 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
if ! command -v java >/dev/null 2>&1 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@@ -198,15 +193,11 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Collect all arguments for the java command;
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# Collect all arguments for the java command: # * put everything else in single quotes, so that it's not re-expanded.
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -214,12 +205,6 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \ org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

View File

@@ -14,7 +14,7 @@
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -25,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@@ -41,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View File

@@ -1,17 +0,0 @@
test:
./gradlew connectedAndroidTest
onetest TEST:
./gradlew test --tests {{TEST}}
build:
./gradlew buildAndroidLib
publishlocal:
./gradlew publishToMavenLocal -P localBuild
clean:
rm -rf ../bdk-ffi/target/
rm -rf ./build/
rm -rf ./lib/build/
rm -rf ./plugins/build/

View File

@@ -5,21 +5,25 @@ val libraryVersion: String by project
plugins { plugins {
id("com.android.library") id("com.android.library")
id("org.jetbrains.kotlin.android") id("org.jetbrains.kotlin.android") version "1.6.10"
id("org.gradle.maven-publish") id("maven-publish")
id("org.gradle.signing") id("signing")
// Custom plugin to generate the native libs and bindings file // Custom plugin to generate the native libs and bindings file
id("org.bitcoindevkit.plugins.generate-android-bindings") id("org.bitcoindevkit.plugins.generate-android-bindings")
} }
repositories {
mavenCentral()
google()
}
android { android {
namespace = "org.bitcoindevkit" compileSdk = 33
compileSdk = 34
defaultConfig { defaultConfig {
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 33
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro") consumerProguardFiles("consumer-rules.pro")
} }
@@ -39,20 +43,6 @@ android {
} }
} }
kotlin {
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "17"
}
}
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
dependencies { dependencies {
implementation("net.java.dev.jna:jna:5.14.0@aar") implementation("net.java.dev.jna:jna:5.14.0@aar")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
@@ -106,22 +96,9 @@ afterEvaluate {
} }
} }
} }
// This is required because we must ensure the moveNativeAndroidLibs task is executed after
// the mergeReleaseJniLibFolders (hard requirement introduced by our upgrade to Gradle 8.7)
tasks.named("mergeReleaseJniLibFolders") {
dependsOn(":lib:moveNativeAndroidLibs")
}
tasks.named("mergeDebugJniLibFolders") {
dependsOn(":lib:moveNativeAndroidLibs")
}
} }
signing { signing {
if (project.hasProperty("localBuild")) {
isRequired = false
}
val signingKeyId: String? by project val signingKeyId: String? by project
val signingKey: String? by project val signingKey: String? by project
val signingPassword: String? by project val signingPassword: String? by project
@@ -129,7 +106,8 @@ signing {
sign(publishing.publications) sign(publishing.publications)
} }
// This task dependency ensures that we build the bindings binaries before running the tests // This task dependency ensures that we build the bindings
// binaries before running the tests
tasks.withType<KotlinCompile> { tasks.withType<KotlinCompile> {
dependsOn("buildAndroidLib") dependsOn("buildAndroidLib")
} }

View File

@@ -33,9 +33,9 @@ class LiveTxBuilderTest {
assert(wallet.getBalance().total > 0uL) assert(wallet.getBalance().total > 0uL)
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET) val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: Psbt = TxBuilder() val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL) .addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(FeeRate.fromSatPerVb(2uL)) .feeRate(FeeRate.fromSatPerVb(2.0f))
.finish(wallet) .finish(wallet)
println(psbt.serialize()) println(psbt.serialize())
@@ -61,9 +61,9 @@ class LiveTxBuilderTest {
ScriptAmount(recipient2.scriptPubkey(), 4200uL), ScriptAmount(recipient2.scriptPubkey(), 4200uL),
) )
val psbt: Psbt = TxBuilder() val psbt: PartiallySignedTransaction = TxBuilder()
.setRecipients(allRecipients) .setRecipients(allRecipients)
.feeRate(FeeRate.fromSatPerVb(4uL)) .feeRate(FeeRate.fromSatPerVb(4.0f))
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN) .changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
.enableRbf() .enableRbf()
.finish(wallet) .finish(wallet)

View File

@@ -37,8 +37,8 @@ class LiveWalletTest {
println("Transactions count: ${wallet.transactions().count()}") println("Transactions count: ${wallet.transactions().count()}")
val transactions = wallet.transactions().take(3) val transactions = wallet.transactions().take(3)
for (tx in transactions) { for (tx in transactions) {
val sentAndReceived = wallet.sentAndReceived(tx.transaction) val sentAndReceived = wallet.sentAndReceived(tx)
println("Transaction: ${tx.transaction.txid()}") println("Transaction: ${tx.txid()}")
println("Sent ${sentAndReceived.sent}") println("Sent ${sentAndReceived.sent}")
println("Received ${sentAndReceived.received}") println("Received ${sentAndReceived.received}")
} }
@@ -61,9 +61,9 @@ class LiveWalletTest {
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET) val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: Psbt = TxBuilder() val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL) .addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(FeeRate.fromSatPerVb(4uL)) .feeRate(FeeRate.fromSatPerVb(4.0f))
.finish(wallet) .finish(wallet)
println(psbt.serialize()) println(psbt.serialize())
@@ -79,7 +79,7 @@ class LiveWalletTest {
println("Tx fee is: ${txFee}") println("Tx fee is: ${txFee}")
val feeRate: FeeRate = wallet.calculateFeeRate(tx) val feeRate: FeeRate = wallet.calculateFeeRate(tx)
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB") println("Tx fee rate is: ${feeRate.asSatPerVb()} sat/vB")
esploraClient.broadcast(tx) esploraClient.broadcast(tx)
} }

View File

@@ -1,3 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.bitcoindevkit">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
</manifest> </manifest>

View File

@@ -113,8 +113,6 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
val moveNativeAndroidLibs by tasks.register<Copy>("moveNativeAndroidLibs") { val moveNativeAndroidLibs by tasks.register<Copy>("moveNativeAndroidLibs") {
dependsOn(buildAndroidAarch64Binary) dependsOn(buildAndroidAarch64Binary)
dependsOn(buildAndroidArmv7Binary)
dependsOn(buildAndroidX86_64Binary)
into("${project.projectDir}/../lib/src/main/jniLibs/") into("${project.projectDir}/../lib/src/main/jniLibs/")

View File

@@ -2,17 +2,3 @@ rootProject.name = "bdk-android"
include(":lib") include(":lib")
includeBuild("plugins") includeBuild("plugins")
pluginManagement {
repositories {
gradlePluginPortal()
google()
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
google()
}
}

613
bdk-ffi/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "bdk-ffi" name = "bdk-ffi"
version = "1.0.0-alpha.9" version = "1.0.0-alpha.7"
homepage = "https://bitcoindevkit.org" homepage = "https://bitcoindevkit.org"
repository = "https://github.com/bitcoindevkit/bdk" repository = "https://github.com/bitcoindevkit/bdk"
edition = "2018" edition = "2018"
@@ -18,14 +18,12 @@ path = "uniffi-bindgen.rs"
default = ["uniffi/cli"] default = ["uniffi/cli"]
[dependencies] [dependencies]
bdk = { version = "1.0.0-alpha.9", features = ["all-keys", "keys-bip39"] } bdk = { version = "1.0.0-alpha.7", features = ["all-keys", "keys-bip39"] }
bdk_esplora = { version = "0.11.0", default-features = false, features = ["std", "blocking"] } bdk_esplora = { version = "0.9.0", default-features = false, features = ["std", "blocking"] }
esplora-client = { version = "0.7.0", default-features = false, features = ["blocking-https-rustls"] } # bdk_esplora = { version = "0.9.0", default-features = false, features = ["std", "blocking", "async-https-rustls"] }
# bdk_esplora = { version = "0.10.0", default-features = false, features = ["std", "blocking", "async-https-rustls"] } bdk_file_store = { version = "0.7.0" }
bdk_file_store = { version = "0.9.0" }
uniffi = { version = "=0.26.1" } uniffi = { version = "=0.26.1" }
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
thiserror = "1.0.58" thiserror = "1.0.58"
[build-dependencies] [build-dependencies]

View File

@@ -1,5 +0,0 @@
build:
cargo build
test:
cargo test --lib

View File

@@ -27,89 +27,19 @@ interface WalletCreationError {
LoadedNetworkDoesNotMatch(Network expected, Network? got); LoadedNetworkDoesNotMatch(Network expected, Network? got);
}; };
[Error]
interface PersistenceError {
Write(string e);
};
[Error] [Error]
interface EsploraError { interface EsploraError {
Minreq(string error_message); Ureq(string error_message);
HttpResponse(u16 status, string error_message); UreqTransport(string error_message);
Http(u16 status_code);
Io(string error_message);
NoHeader();
Parsing(string error_message); Parsing(string error_message);
StatusCode(string error_message);
BitcoinEncoding(string error_message); BitcoinEncoding(string error_message);
HexToArray(string error_message); Hex(string error_message);
HexToBytes(string error_message);
TransactionNotFound(); TransactionNotFound();
HeaderHeightNotFound(u32 height); HeaderHeightNotFound(u32 height);
HeaderHashNotFound(); HeaderHashNotFound();
InvalidHttpHeaderName(string name);
InvalidHttpHeaderValue(string value);
};
[Error]
enum FeeRateError {
"ArithmeticOverflow"
};
[Error]
interface AddressError {
Base58();
Bech32();
WitnessVersion(string error_message);
WitnessProgram(string error_message);
UncompressedPubkey();
ExcessiveScriptSize();
UnrecognizedScript();
NetworkValidation(Network required, Network found, string address);
OtherAddressErr();
};
[Error]
interface TransactionError {
Io();
OversizedVectorAllocation();
InvalidChecksum(string expected, string actual);
NonMinimalVarInt();
ParseFailed();
UnsupportedSegwitFlag(u8 flag);
OtherTransactionErr();
};
[Error]
interface PsbtParseError {
PsbtEncoding(string e);
Base64Encoding(string e);
};
[Error]
interface DescriptorError {
InvalidHdKeyPath();
InvalidDescriptorChecksum();
HardenedDerivationXpub();
MultiPath();
Key(string e);
Policy(string e);
InvalidDescriptorCharacter(string char);
Bip32(string e);
Base58(string e);
Pk(string e);
Miniscript(string e);
Hex(string e);
};
[Error]
interface TxidParseError {
InvalidTxid(string txid);
};
[Error]
interface ExtractTxError {
AbsurdFeeRate(u64 fee_rate);
MissingInputValue();
SendingTooMuch();
OtherExtractTxErr();
}; };
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@@ -160,33 +90,20 @@ dictionary TxOut {
Script script_pubkey; Script script_pubkey;
}; };
[Enum]
interface ChainPosition {
Confirmed(u32 height, u64 timestamp);
Unconfirmed(u64 timestamp);
};
dictionary CanonicalTx {
Transaction transaction;
ChainPosition chain_position;
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// bdk crate - wallet module // bdk crate - wallet module
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
interface FeeRate { interface FeeRate {
[Name=from_sat_per_vb, Throws=FeeRateError] [Name=from_sat_per_vb]
constructor(u64 sat_per_vb); constructor(f32 sat_per_vb);
[Name=from_sat_per_kwu] [Name=from_sat_per_kwu]
constructor(u64 sat_per_kwu); constructor(f32 sat_per_kwu);
u64 to_sat_per_vb_ceil(); f32 as_sat_per_vb();
u64 to_sat_per_vb_floor(); f32 sat_per_kwu();
u64 to_sat_per_kwu();
}; };
enum ChangeSpendPolicy { enum ChangeSpendPolicy {
@@ -201,7 +118,7 @@ interface Wallet {
AddressInfo get_address(AddressIndex address_index); AddressInfo get_address(AddressIndex address_index);
[Throws=PersistenceError] [Throws=Alpha3Error]
AddressInfo try_get_internal_address(AddressIndex address_index); AddressInfo try_get_internal_address(AddressIndex address_index);
Network network(); Network network();
@@ -214,24 +131,17 @@ interface Wallet {
boolean is_mine([ByRef] Script script); boolean is_mine([ByRef] Script script);
[Throws=Alpha3Error] [Throws=Alpha3Error]
boolean sign(Psbt psbt); boolean sign(PartiallySignedTransaction psbt);
SentAndReceivedValues sent_and_received([ByRef] Transaction tx); SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
sequence<CanonicalTx> transactions(); sequence<Transaction> transactions();
[Throws=TxidParseError]
CanonicalTx? get_tx(string txid);
[Throws=CalculateFeeError] [Throws=CalculateFeeError]
u64 calculate_fee([ByRef] Transaction tx); u64 calculate_fee([ByRef] Transaction tx);
[Throws=CalculateFeeError] [Throws=CalculateFeeError]
FeeRate calculate_fee_rate([ByRef] Transaction tx); FeeRate calculate_fee_rate([ByRef] Transaction tx);
sequence<LocalOutput> list_unspent();
sequence<LocalOutput> list_output();
}; };
interface Update {}; interface Update {};
@@ -270,11 +180,11 @@ interface TxBuilder {
TxBuilder enable_rbf_with_sequence(u32 nsequence); TxBuilder enable_rbf_with_sequence(u32 nsequence);
[Throws=Alpha3Error] [Throws=Alpha3Error]
Psbt finish([ByRef] Wallet wallet); PartiallySignedTransaction finish([ByRef] Wallet wallet);
}; };
interface BumpFeeTxBuilder { interface BumpFeeTxBuilder {
constructor(string txid, FeeRate fee_rate); constructor(string txid, f32 fee_rate);
BumpFeeTxBuilder allow_shrinking(Script script_pubkey); BumpFeeTxBuilder allow_shrinking(Script script_pubkey);
@@ -283,7 +193,7 @@ interface BumpFeeTxBuilder {
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence); BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
[Throws=Alpha3Error] [Throws=Alpha3Error]
Psbt finish([ByRef] Wallet wallet); PartiallySignedTransaction finish([ByRef] Wallet wallet);
}; };
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@@ -340,7 +250,7 @@ interface DescriptorPublicKey {
}; };
interface Descriptor { interface Descriptor {
[Throws=DescriptorError] [Throws=Alpha3Error]
constructor(string descriptor, Network network); constructor(string descriptor, Network network);
[Name=new_bip44] [Name=new_bip44]
@@ -382,7 +292,7 @@ interface EsploraClient {
[Throws=EsploraError] [Throws=EsploraError]
Update full_scan(Wallet wallet, u64 stop_gap, u64 parallel_requests); Update full_scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);
[Throws=EsploraError] [Throws=Alpha3Error]
void broadcast([ByRef] Transaction transaction); void broadcast([ByRef] Transaction transaction);
}; };
@@ -427,7 +337,7 @@ enum WordCount {
}; };
interface Address { interface Address {
[Throws=AddressError] [Throws=Alpha3Error]
constructor(string address, Network network); constructor(string address, Network network);
Network network(); Network network();
@@ -442,33 +352,30 @@ interface Address {
}; };
interface Transaction { interface Transaction {
[Throws=TransactionError] [Throws=Alpha3Error]
constructor(sequence<u8> transaction_bytes); constructor(sequence<u8> transaction_bytes);
string txid(); string txid();
u64 total_size(); u64 size();
u64 vsize(); u64 vsize();
boolean is_coinbase(); boolean is_coin_base();
boolean is_explicitly_rbf(); boolean is_explicitly_rbf();
boolean is_lock_time_enabled(); boolean is_lock_time_enabled();
i32 version(); i32 version();
sequence<u8> serialize();
}; };
interface Psbt { interface PartiallySignedTransaction {
[Throws=PsbtParseError] [Throws=Alpha3Error]
constructor(string psbt_base64); constructor(string psbt_base64);
string serialize(); string serialize();
[Throws=ExtractTxError]
Transaction extract_tx(); Transaction extract_tx();
}; };

View File

@@ -1,18 +1,15 @@
use crate::error::{AddressError, PsbtParseError, TransactionError};
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked}; use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf; use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut; use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut;
use bdk::bitcoin::consensus::encode::serialize;
use bdk::bitcoin::consensus::Decodable; use bdk::bitcoin::consensus::Decodable;
use bdk::bitcoin::psbt::ExtractTxError; use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::bitcoin::Address as BdkAddress; use bdk::bitcoin::Address as BdkAddress;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
use bdk::bitcoin::OutPoint as BdkOutPoint; use bdk::bitcoin::OutPoint as BdkOutPoint;
use bdk::bitcoin::Psbt as BdkPsbt;
use bdk::bitcoin::Transaction as BdkTransaction; use bdk::bitcoin::Transaction as BdkTransaction;
use bdk::bitcoin::Txid; use bdk::bitcoin::Txid;
use crate::error::Alpha3Error;
use std::io::Cursor; use std::io::Cursor;
use std::str::FromStr; use std::str::FromStr;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@@ -43,9 +40,14 @@ pub struct Address {
} }
impl Address { impl Address {
pub fn new(address: String, network: Network) -> Result<Self, AddressError> { pub fn new(address: String, network: Network) -> Result<Self, Alpha3Error> {
let parsed_address = address.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()?; let parsed_address = address
let network_checked_address = parsed_address.require_network(network)?; .parse::<bdk::bitcoin::Address<NetworkUnchecked>>()
.map_err(|_| Alpha3Error::Generic)?;
let network_checked_address = parsed_address
.require_network(network)
.map_err(|_| Alpha3Error::Generic)?;
Ok(Address { Ok(Address {
inner: network_checked_address, inner: network_checked_address,
@@ -75,7 +77,7 @@ impl Address {
// } // }
pub fn network(&self) -> Network { pub fn network(&self) -> Network {
*self.inner.network() self.inner.network
} }
pub fn script_pubkey(&self) -> Arc<Script> { pub fn script_pubkey(&self) -> Arc<Script> {
@@ -118,9 +120,10 @@ pub struct Transaction {
} }
impl Transaction { impl Transaction {
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, TransactionError> { pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, Alpha3Error> {
let mut decoder = Cursor::new(transaction_bytes); let mut decoder = Cursor::new(transaction_bytes);
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?; let tx: BdkTransaction =
BdkTransaction::consensus_decode(&mut decoder).map_err(|_| Alpha3Error::Generic)?;
Ok(Transaction { inner: tx }) Ok(Transaction { inner: tx })
} }
@@ -132,16 +135,20 @@ impl Transaction {
// self.inner.weight() as u64 // self.inner.weight() as u64
// } // }
pub fn total_size(&self) -> u64 { pub fn size(&self) -> u64 {
self.inner.total_size() as u64 self.inner.size() as u64
} }
pub fn vsize(&self) -> u64 { pub fn vsize(&self) -> u64 {
self.inner.vsize() as u64 self.inner.vsize() as u64
} }
pub fn is_coinbase(&self) -> bool { // fn serialize(&self) -> Vec<u8> {
self.inner.is_coinbase() // self.inner.serialize()
// }
pub fn is_coin_base(&self) -> bool {
self.inner.is_coin_base()
} }
pub fn is_explicitly_rbf(&self) -> bool { pub fn is_explicitly_rbf(&self) -> bool {
@@ -153,11 +160,7 @@ impl Transaction {
} }
pub fn version(&self) -> i32 { pub fn version(&self) -> i32 {
self.inner.version.0 self.inner.version
}
pub fn serialize(&self) -> Vec<u8> {
serialize(&self.inner)
} }
// fn lock_time(&self) -> u32 { // fn lock_time(&self) -> u32 {
@@ -191,14 +194,17 @@ impl From<&Transaction> for BdkTransaction {
} }
} }
pub struct Psbt { pub struct PartiallySignedTransaction {
pub(crate) inner: Mutex<BdkPsbt>, pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
} }
impl Psbt { impl PartiallySignedTransaction {
pub(crate) fn new(psbt_base64: String) -> Result<Self, PsbtParseError> { pub(crate) fn new(psbt_base64: String) -> Result<Self, Alpha3Error> {
let psbt: BdkPsbt = BdkPsbt::from_str(&psbt_base64)?; let psbt: BdkPartiallySignedTransaction =
Ok(Psbt { BdkPartiallySignedTransaction::from_str(&psbt_base64)
.map_err(|_| Alpha3Error::Generic)?;
Ok(PartiallySignedTransaction {
inner: Mutex::new(psbt), inner: Mutex::new(psbt),
}) })
} }
@@ -214,10 +220,9 @@ impl Psbt {
// txid.to_hex() // txid.to_hex()
// } // }
pub(crate) fn extract_tx(&self) -> Result<Arc<Transaction>, ExtractTxError> { pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
let tx: BdkTransaction = self.inner.lock().unwrap().clone().extract_tx()?; let tx = self.inner.lock().unwrap().clone().extract_tx();
let transaction: Transaction = tx.into(); Arc::new(tx.into())
Ok(Arc::new(transaction))
} }
// /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174. // /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
@@ -257,9 +262,9 @@ impl Psbt {
// } // }
} }
impl From<BdkPsbt> for Psbt { impl From<BdkPartiallySignedTransaction> for PartiallySignedTransaction {
fn from(psbt: BdkPsbt) -> Self { fn from(psbt: BdkPartiallySignedTransaction) -> Self {
Psbt { PartiallySignedTransaction {
inner: Mutex::new(psbt), inner: Mutex::new(psbt),
} }
} }
@@ -298,7 +303,7 @@ pub struct TxOut {
impl From<&BdkTxOut> for TxOut { impl From<&BdkTxOut> for TxOut {
fn from(tx_out: &BdkTxOut) -> Self { fn from(tx_out: &BdkTxOut) -> Self {
TxOut { TxOut {
value: tx_out.value.to_sat(), value: tx_out.value,
script_pubkey: Arc::new(Script(tx_out.script_pubkey.clone())), script_pubkey: Arc::new(Script(tx_out.script_pubkey.clone())),
} }
} }

View File

@@ -1,4 +1,4 @@
use crate::error::DescriptorError; use crate::error::Alpha3Error;
use crate::keys::DescriptorPublicKey; use crate::keys::DescriptorPublicKey;
use crate::keys::DescriptorSecretKey; use crate::keys::DescriptorSecretKey;
@@ -23,7 +23,7 @@ pub struct Descriptor {
} }
impl Descriptor { impl Descriptor {
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, DescriptorError> { pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, Alpha3Error> {
let secp = Secp256k1::new(); let secp = Secp256k1::new();
let (extended_descriptor, key_map) = descriptor.into_wallet_descriptor(&secp, network)?; let (extended_descriptor, key_map) = descriptor.into_wallet_descriptor(&secp, network)?;
Ok(Self { Ok(Self {
@@ -276,6 +276,8 @@ mod test {
use crate::*; use crate::*;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use crate::Alpha3Error;
fn get_descriptor_secret_key() -> DescriptorSecretKey { fn get_descriptor_secret_key() -> DescriptorSecretKey {
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap(); let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
DescriptorSecretKey::new(Network::Testnet, &mnemonic, None) DescriptorSecretKey::new(Network::Testnet, &mnemonic, None)
@@ -390,8 +392,8 @@ mod test {
fn test_descriptor_from_string() { fn test_descriptor_from_string() {
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet); let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet);
let descriptor2 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Bitcoin); let descriptor2 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Bitcoin);
// Creating a Descriptor using an extended key that doesn't match the network provided will throw a DescriptorError::Key with inner InvalidNetwork error // Creating a Descriptor using an extended key that doesn't match the network provided will throw and InvalidNetwork Error
assert!(descriptor1.is_ok()); assert!(descriptor1.is_ok());
assert_matches!(descriptor2.unwrap_err(), DescriptorError::Key { .. }); assert_matches!(descriptor2.unwrap_err(), Alpha3Error::Generic)
} }
} }

View File

@@ -1,18 +1,15 @@
use crate::bitcoin::OutPoint; use crate::bitcoin::OutPoint;
use bdk::bitcoin::psbt::PsbtParseError as BdkPsbtParseError;
use bdk::bitcoin::Network;
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError; use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
use bdk::descriptor::DescriptorError as BdkDescriptorError; use bdk_esplora::esplora_client::Error as BdkEsploraError;
use bdk::bitcoin::Network;
use bdk::descriptor::DescriptorError;
use bdk::wallet::error::{BuildFeeBumpError, CreateTxError}; use bdk::wallet::error::{BuildFeeBumpError, CreateTxError};
use bdk::wallet::tx_builder::{AddUtxoError, AllowShrinkingError}; use bdk::wallet::tx_builder::{AddUtxoError, AllowShrinkingError};
use bdk::wallet::{NewError, NewOrLoadError}; use bdk::wallet::{NewError, NewOrLoadError};
use bdk_esplora::esplora_client::{Error as BdkEsploraError, Error};
use bdk_file_store::FileError as BdkFileError; use bdk_file_store::FileError as BdkFileError;
use bdk_file_store::IterError; use bdk_file_store::IterError;
use bitcoin_internals::hex::display::DisplayHex;
use bdk::bitcoin::address::ParseError;
use std::convert::Infallible; use std::convert::Infallible;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
@@ -62,34 +59,31 @@ pub enum WalletCreationError {
}, },
} }
#[derive(Debug, thiserror::Error)]
pub enum PersistenceError {
#[error("writing to persistence error: {e}")]
Write { e: String },
}
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum EsploraError { pub enum EsploraError {
#[error("minreq error: {error_message}")] #[error("ureq error: {error_message}")]
Minreq { error_message: String }, Ureq { error_message: String },
#[error("http error with status code {status} and message {error_message}")] #[error("ureq transport error: {error_message}")]
HttpResponse { status: u16, error_message: String }, UreqTransport { error_message: String },
#[error("http error with status code: {status_code}")]
Http { status_code: u16 },
#[error("io error: {error_message}")]
Io { error_message: String },
#[error("no header found in the response")]
NoHeader,
#[error("parsing error: {error_message}")] #[error("parsing error: {error_message}")]
Parsing { error_message: String }, Parsing { error_message: String },
#[error("Invalid status code, unable to convert to u16: {error_message}")]
StatusCode { error_message: String },
#[error("bitcoin encoding error: {error_message}")] #[error("bitcoin encoding error: {error_message}")]
BitcoinEncoding { error_message: String }, BitcoinEncoding { error_message: String },
#[error("invalid hex data returned: {error_message}")] #[error("hex decoding error: {error_message}")]
HexToArray { error_message: String }, Hex { error_message: String },
#[error("invalid hex data returned: {error_message}")]
HexToBytes { error_message: String },
#[error("transaction not found")] #[error("transaction not found")]
TransactionNotFound, TransactionNotFound,
@@ -99,194 +93,6 @@ pub enum EsploraError {
#[error("header hash not found")] #[error("header hash not found")]
HeaderHashNotFound, HeaderHashNotFound,
#[error("invalid HTTP header name: {name}")]
InvalidHttpHeaderName { name: String },
#[error("invalid HTTP header value: {value}")]
InvalidHttpHeaderValue { value: String },
}
#[derive(Debug, thiserror::Error)]
pub enum FeeRateError {
#[error("arithmetic overflow on feerate")]
ArithmeticOverflow,
}
#[derive(Debug, thiserror::Error)]
pub enum TxidParseError {
#[error("invalid txid: {txid}")]
InvalidTxid { txid: String },
}
#[derive(Debug, thiserror::Error)]
pub enum AddressError {
// Errors coming from the ParseError enum
#[error("base58 address encoding error")]
Base58,
#[error("bech32 address encoding error")]
Bech32,
// Errors coming from the bitcoin::address::Error enum
#[error("witness version conversion/parsing error: {error_message}")]
WitnessVersion { error_message: String },
#[error("witness program error: {error_message}")]
WitnessProgram { error_message: String },
#[error("an uncompressed pubkey was used where it is not allowed")]
UncompressedPubkey,
#[error("script size exceed 520 bytes")]
ExcessiveScriptSize,
#[error("script is not p2pkh, p2sh, or witness program")]
UnrecognizedScript,
#[error("address {address} is not valid on {required}")]
NetworkValidation {
/// Network that was required.
required: Network,
/// Network on which the address was found to be valid.
found: Network,
/// The address itself
address: String,
},
// This is required because the bdk::bitcoin::address::Error is non-exhaustive
#[error("other address error")]
OtherAddressErr,
}
// Mapping https://docs.rs/bitcoin/latest/src/bitcoin/consensus/encode.rs.html#40-63
#[derive(Debug, thiserror::Error)]
pub enum TransactionError {
#[error("IO error")]
Io,
#[error("allocation of oversized vector")]
OversizedVectorAllocation,
#[error("invalid checksum: expected={expected} actual={actual}")]
InvalidChecksum { expected: String, actual: String },
#[error("non-minimal varint")]
NonMinimalVarInt,
#[error("parse failed")]
ParseFailed,
#[error("unsupported segwit version: {flag}")]
UnsupportedSegwitFlag { flag: u8 },
// This is required because the bdk::bitcoin::consensus::encode::Error is non-exhaustive
#[error("other transaction error")]
OtherTransactionErr,
}
#[derive(Debug, thiserror::Error)]
pub enum PsbtParseError {
#[error("error in internal PSBT data structure: {e}")]
PsbtEncoding { e: String },
#[error("error in PSBT base64 encoding: {e}")]
Base64Encoding { e: String },
}
#[derive(Debug, thiserror::Error)]
pub enum DescriptorError {
#[error("invalid hd key path")]
InvalidHdKeyPath,
#[error("the provided descriptor doesn't match its checksum")]
InvalidDescriptorChecksum,
#[error("the descriptor contains hardened derivation steps on public extended keys")]
HardenedDerivationXpub,
#[error("the descriptor contains multipath keys, which are not supported yet")]
MultiPath,
#[error("key error: {e}")]
Key { e: String },
#[error("policy error: {e}")]
Policy { e: String },
#[error("invalid descriptor character: {char}")]
InvalidDescriptorCharacter { char: String },
#[error("BIP32 error: {e}")]
Bip32 { e: String },
#[error("Base58 error: {e}")]
Base58 { e: String },
#[error("Key-related error: {e}")]
Pk { e: String },
#[error("Miniscript error: {e}")]
Miniscript { e: String },
#[error("Hex decoding error: {e}")]
Hex { e: String },
}
#[derive(Debug, thiserror::Error)]
pub enum ExtractTxError {
#[error("an absurdly high fee rate of {fee_rate} sat/vbyte")]
AbsurdFeeRate { fee_rate: u64 },
#[error("one of the inputs lacked value information (witness_utxo or non_witness_utxo)")]
MissingInputValue,
#[error("transaction would be invalid due to output value being greater than input value")]
SendingTooMuch,
#[error(
"this error is required because the bdk::bitcoin::psbt::ExtractTxError is non-exhaustive"
)]
OtherExtractTxErr,
}
impl From<BdkDescriptorError> for DescriptorError {
fn from(error: BdkDescriptorError) -> Self {
match error {
BdkDescriptorError::InvalidHdKeyPath => DescriptorError::InvalidHdKeyPath,
BdkDescriptorError::InvalidDescriptorChecksum => {
DescriptorError::InvalidDescriptorChecksum
}
BdkDescriptorError::HardenedDerivationXpub => DescriptorError::HardenedDerivationXpub,
BdkDescriptorError::MultiPath => DescriptorError::MultiPath,
BdkDescriptorError::Key(e) => DescriptorError::Key { e: e.to_string() },
BdkDescriptorError::Policy(e) => DescriptorError::Policy { e: e.to_string() },
BdkDescriptorError::InvalidDescriptorCharacter(char) => {
DescriptorError::InvalidDescriptorCharacter {
char: char.to_string(),
}
}
BdkDescriptorError::Bip32(e) => DescriptorError::Bip32 { e: e.to_string() },
BdkDescriptorError::Base58(e) => DescriptorError::Base58 { e: e.to_string() },
BdkDescriptorError::Pk(e) => DescriptorError::Pk { e: e.to_string() },
BdkDescriptorError::Miniscript(e) => DescriptorError::Miniscript { e: e.to_string() },
BdkDescriptorError::Hex(e) => DescriptorError::Hex { e: e.to_string() },
}
}
}
impl From<BdkPsbtParseError> for PsbtParseError {
fn from(error: BdkPsbtParseError) -> Self {
match error {
BdkPsbtParseError::PsbtEncoding(e) => PsbtParseError::PsbtEncoding { e: e.to_string() },
BdkPsbtParseError::Base64Encoding(e) => {
PsbtParseError::Base64Encoding { e: e.to_string() }
}
_ => {
unreachable!("this is required because of the non-exhaustive enum in rust-bitcoin")
}
}
}
} }
impl From<BdkFileError> for WalletCreationError { impl From<BdkFileError> for WalletCreationError {
@@ -317,11 +123,9 @@ impl From<NewOrLoadError<std::io::Error, IterError>> for WalletCreationError {
} }
} }
impl From<std::io::Error> for PersistenceError { impl From<DescriptorError> for Alpha3Error {
fn from(error: std::io::Error) -> Self { fn from(_: DescriptorError) -> Self {
PersistenceError::Write { Alpha3Error::Generic
e: error.to_string(),
}
} }
} }
@@ -381,26 +185,24 @@ impl From<BdkCalculateFeeError> for CalculateFeeError {
impl From<BdkEsploraError> for EsploraError { impl From<BdkEsploraError> for EsploraError {
fn from(error: BdkEsploraError) -> Self { fn from(error: BdkEsploraError) -> Self {
match error { match error {
BdkEsploraError::Minreq(e) => EsploraError::Minreq { BdkEsploraError::Ureq(e) => EsploraError::Ureq {
error_message: e.to_string(), error_message: e.to_string(),
}, },
BdkEsploraError::HttpResponse { status, message } => EsploraError::HttpResponse { BdkEsploraError::UreqTransport(e) => EsploraError::UreqTransport {
status, error_message: e.to_string(),
error_message: message,
}, },
BdkEsploraError::HttpResponse(code) => EsploraError::Http { status_code: code },
BdkEsploraError::Io(e) => EsploraError::Io {
error_message: e.to_string(),
},
BdkEsploraError::NoHeader => EsploraError::NoHeader,
BdkEsploraError::Parsing(e) => EsploraError::Parsing { BdkEsploraError::Parsing(e) => EsploraError::Parsing {
error_message: e.to_string(), error_message: e.to_string(),
}, },
Error::StatusCode(e) => EsploraError::StatusCode {
error_message: e.to_string(),
},
BdkEsploraError::BitcoinEncoding(e) => EsploraError::BitcoinEncoding { BdkEsploraError::BitcoinEncoding(e) => EsploraError::BitcoinEncoding {
error_message: e.to_string(), error_message: e.to_string(),
}, },
BdkEsploraError::HexToArray(e) => EsploraError::HexToArray { BdkEsploraError::Hex(e) => EsploraError::Hex {
error_message: e.to_string(),
},
BdkEsploraError::HexToBytes(e) => EsploraError::HexToBytes {
error_message: e.to_string(), error_message: e.to_string(),
}, },
BdkEsploraError::TransactionNotFound(_) => EsploraError::TransactionNotFound, BdkEsploraError::TransactionNotFound(_) => EsploraError::TransactionNotFound,
@@ -408,108 +210,15 @@ impl From<BdkEsploraError> for EsploraError {
EsploraError::HeaderHeightNotFound { height } EsploraError::HeaderHeightNotFound { height }
} }
BdkEsploraError::HeaderHashNotFound(_) => EsploraError::HeaderHashNotFound, BdkEsploraError::HeaderHashNotFound(_) => EsploraError::HeaderHashNotFound,
Error::InvalidHttpHeaderName(name) => EsploraError::InvalidHttpHeaderName { name },
BdkEsploraError::InvalidHttpHeaderValue(value) => {
EsploraError::InvalidHttpHeaderValue { value }
}
} }
} }
} }
impl From<bdk::bitcoin::address::Error> for AddressError {
fn from(error: bdk::bitcoin::address::Error) -> Self {
match error {
bdk::bitcoin::address::Error::WitnessVersion(error_message) => {
AddressError::WitnessVersion {
error_message: error_message.to_string(),
}
}
bdk::bitcoin::address::Error::WitnessProgram(e) => AddressError::WitnessProgram {
error_message: e.to_string(),
},
bdk::bitcoin::address::Error::UncompressedPubkey => AddressError::UncompressedPubkey,
bdk::bitcoin::address::Error::ExcessiveScriptSize => AddressError::ExcessiveScriptSize,
bdk::bitcoin::address::Error::UnrecognizedScript => AddressError::UnrecognizedScript,
bdk::bitcoin::address::Error::NetworkValidation {
required,
found,
address,
} => AddressError::NetworkValidation {
required,
found,
address: format!("{:?}", address),
},
_ => AddressError::OtherAddressErr,
}
}
}
impl From<ParseError> for AddressError {
fn from(error: ParseError) -> Self {
match error {
ParseError::Base58(_) => AddressError::Base58,
ParseError::Bech32(_) => AddressError::Bech32,
ParseError::WitnessVersion(e) => AddressError::WitnessVersion {
error_message: e.to_string(),
},
ParseError::WitnessProgram(e) => AddressError::WitnessProgram {
error_message: e.to_string(),
},
_ => AddressError::OtherAddressErr,
}
}
}
impl From<bdk::bitcoin::consensus::encode::Error> for TransactionError {
fn from(error: bdk::bitcoin::consensus::encode::Error) -> Self {
match error {
bdk::bitcoin::consensus::encode::Error::Io(_) => TransactionError::Io,
bdk::bitcoin::consensus::encode::Error::OversizedVectorAllocation { .. } => {
TransactionError::OversizedVectorAllocation
}
bdk::bitcoin::consensus::encode::Error::InvalidChecksum { expected, actual } => {
TransactionError::InvalidChecksum {
expected: DisplayHex::to_lower_hex_string(&expected),
actual: DisplayHex::to_lower_hex_string(&actual),
}
}
bdk::bitcoin::consensus::encode::Error::NonMinimalVarInt => {
TransactionError::NonMinimalVarInt
}
bdk::bitcoin::consensus::encode::Error::ParseFailed(_) => TransactionError::ParseFailed,
bdk::bitcoin::consensus::encode::Error::UnsupportedSegwitFlag(flag) => {
TransactionError::UnsupportedSegwitFlag { flag }
}
_ => TransactionError::OtherTransactionErr,
}
}
}
impl From<bdk::bitcoin::psbt::ExtractTxError> for ExtractTxError {
fn from(error: bdk::bitcoin::psbt::ExtractTxError) -> Self {
match error {
bdk::bitcoin::psbt::ExtractTxError::AbsurdFeeRate { fee_rate, .. } => {
let sat_per_vbyte = fee_rate.to_sat_per_vb_ceil();
ExtractTxError::AbsurdFeeRate {
fee_rate: sat_per_vbyte,
}
}
bdk::bitcoin::psbt::ExtractTxError::MissingInputValue { .. } => {
ExtractTxError::MissingInputValue
}
bdk::bitcoin::psbt::ExtractTxError::SendingTooMuch { .. } => {
ExtractTxError::SendingTooMuch
}
_ => ExtractTxError::OtherExtractTxErr,
}
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::error::{EsploraError, PersistenceError, WalletCreationError}; use crate::error::EsploraError;
use crate::CalculateFeeError; use crate::CalculateFeeError;
use crate::OutPoint; use crate::OutPoint;
use bdk::bitcoin::Network;
#[test] #[test]
fn test_error_missing_tx_out() { fn test_error_missing_tx_out() {
@@ -556,24 +265,28 @@ mod test {
fn test_esplora_errors() { fn test_esplora_errors() {
let cases = vec![ let cases = vec![
( (
EsploraError::Minreq { EsploraError::Ureq {
error_message: "Network error".to_string(), error_message: "Network error".to_string(),
}, },
"minreq error: Network error", "ureq error: Network error",
), ),
( (
EsploraError::HttpResponse { EsploraError::UreqTransport {
status: 404, error_message: "Timeout occurred".to_string(),
error_message: "Not found".to_string(),
}, },
"http error with status code 404 and message Not found", "ureq transport error: Timeout occurred",
), ),
( (
EsploraError::StatusCode { EsploraError::Http { status_code: 404 },
error_message: "code 1234567".to_string(), "http error with status code: 404",
},
"Invalid status code, unable to convert to u16: code 1234567",
), ),
(
EsploraError::Io {
error_message: "File not found".to_string(),
},
"io error: File not found",
),
(EsploraError::NoHeader, "no header found in the response"),
( (
EsploraError::Parsing { EsploraError::Parsing {
error_message: "Invalid JSON".to_string(), error_message: "Invalid JSON".to_string(),
@@ -587,16 +300,10 @@ mod test {
"bitcoin encoding error: Bad format", "bitcoin encoding error: Bad format",
), ),
( (
EsploraError::HexToArray { EsploraError::Hex {
error_message: "Invalid hex".to_string(), error_message: "Invalid hex".to_string(),
}, },
"invalid hex data returned: Invalid hex", "hex decoding error: Invalid hex",
),
(
EsploraError::HexToBytes {
error_message: "Invalid hex".to_string(),
},
"invalid hex data returned: Invalid hex",
), ),
(EsploraError::TransactionNotFound, "transaction not found"), (EsploraError::TransactionNotFound, "transaction not found"),
( (
@@ -610,66 +317,4 @@ mod test {
assert_eq!(error.to_string(), expected_message); assert_eq!(error.to_string(), expected_message);
} }
} }
#[test]
fn test_persistence_error() {
let io_err = std::io::Error::new(
std::io::ErrorKind::Other,
"unable to persist the new address",
);
let op_err: PersistenceError = io_err.into();
let PersistenceError::Write { e } = op_err;
assert_eq!(e, "unable to persist the new address");
}
#[test]
fn test_error_wallet_creation() {
let io_error = WalletCreationError::Io {
e: "io error".to_string(),
};
assert_eq!(
io_error.to_string(),
"io error trying to read file: io error"
);
let invalid_magic_bytes_error = WalletCreationError::InvalidMagicBytes {
got: vec![1, 2, 3, 4],
expected: vec![4, 3, 2, 1],
};
assert_eq!(
invalid_magic_bytes_error.to_string(),
"file has invalid magic bytes: expected=[4, 3, 2, 1] got=[1, 2, 3, 4]"
);
let descriptor_error = WalletCreationError::Descriptor;
assert_eq!(descriptor_error.to_string(), "error with descriptor");
let write_error = WalletCreationError::Write;
assert_eq!(write_error.to_string(), "failed to write to persistence");
let load_error = WalletCreationError::Load;
assert_eq!(load_error.to_string(), "failed to load from persistence");
let not_initialized_error = WalletCreationError::NotInitialized;
assert_eq!(
not_initialized_error.to_string(),
"wallet is not initialized, persistence backend is empty"
);
let loaded_genesis_does_not_match_error = WalletCreationError::LoadedGenesisDoesNotMatch;
assert_eq!(
loaded_genesis_does_not_match_error.to_string(),
"loaded genesis hash does not match the expected one"
);
let loaded_network_does_not_match_error = WalletCreationError::LoadedNetworkDoesNotMatch {
expected: Network::Bitcoin,
got: Some(Network::Testnet),
};
assert_eq!(
loaded_network_does_not_match_error.to_string(),
"loaded network type is not bitcoin, got Some(Testnet)"
);
}
} }

View File

@@ -1,19 +1,19 @@
use crate::error::EsploraError; use crate::error::{Alpha3Error, EsploraError};
use crate::wallet::{Update, Wallet}; use crate::wallet::{Update, Wallet};
use crate::bitcoin::Transaction;
use bdk::bitcoin::Transaction as BdkTransaction; use bdk::bitcoin::Transaction as BdkTransaction;
use bdk::wallet::Update as BdkUpdate; use bdk::wallet::Update as BdkUpdate;
use bdk_esplora::esplora_client::{BlockingClient, Builder}; use bdk_esplora::esplora_client::{BlockingClient, Builder};
use bdk_esplora::EsploraExt; use bdk_esplora::EsploraExt;
use crate::bitcoin::Transaction;
use std::sync::Arc; use std::sync::Arc;
pub struct EsploraClient(BlockingClient); pub struct EsploraClient(BlockingClient);
impl EsploraClient { impl EsploraClient {
pub fn new(url: String) -> Self { pub fn new(url: String) -> Self {
let client = Builder::new(url.as_str()).build_blocking(); let client = Builder::new(url.as_str()).build_blocking().unwrap();
Self(client) Self(client)
} }
@@ -52,11 +52,11 @@ impl EsploraClient {
// pub fn sync(); // pub fn sync();
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), EsploraError> { pub fn broadcast(&self, transaction: &Transaction) -> Result<(), Alpha3Error> {
let bdk_transaction: BdkTransaction = transaction.into(); let bdk_transaction: BdkTransaction = transaction.into();
self.0 self.0
.broadcast(&bdk_transaction) .broadcast(&bdk_transaction)
.map_err(EsploraError::from) .map_err(|_| Alpha3Error::Generic)
} }
// pub fn estimate_fee(); // pub fn estimate_fee();

View File

@@ -8,23 +8,14 @@ mod wallet;
use crate::bitcoin::Address; use crate::bitcoin::Address;
use crate::bitcoin::OutPoint; use crate::bitcoin::OutPoint;
use crate::bitcoin::Psbt; use crate::bitcoin::PartiallySignedTransaction;
use crate::bitcoin::Script; use crate::bitcoin::Script;
use crate::bitcoin::Transaction; use crate::bitcoin::Transaction;
use crate::bitcoin::TxOut; use crate::bitcoin::TxOut;
use crate::descriptor::Descriptor; use crate::descriptor::Descriptor;
use crate::error::AddressError;
use crate::error::Alpha3Error; use crate::error::Alpha3Error;
use crate::error::CalculateFeeError; use crate::error::CalculateFeeError;
use crate::error::DescriptorError;
use crate::error::EsploraError; use crate::error::EsploraError;
use crate::error::ExtractTxError;
use crate::error::FeeRateError;
use crate::error::PersistenceError;
use crate::error::PsbtParseError;
use crate::error::TransactionError;
use crate::error::TxidParseError;
use crate::error::WalletCreationError;
use crate::esplora::EsploraClient; use crate::esplora::EsploraClient;
use crate::keys::DerivationPath; use crate::keys::DerivationPath;
use crate::keys::DescriptorPublicKey; use crate::keys::DescriptorPublicKey;
@@ -33,8 +24,6 @@ use crate::keys::Mnemonic;
use crate::types::AddressIndex; use crate::types::AddressIndex;
use crate::types::AddressInfo; use crate::types::AddressInfo;
use crate::types::Balance; use crate::types::Balance;
use crate::types::CanonicalTx;
use crate::types::ChainPosition;
use crate::types::FeeRate; use crate::types::FeeRate;
use crate::types::LocalOutput; use crate::types::LocalOutput;
use crate::types::ScriptAmount; use crate::types::ScriptAmount;
@@ -44,6 +33,7 @@ use crate::wallet::TxBuilder;
use crate::wallet::Update; use crate::wallet::Update;
use crate::wallet::Wallet; use crate::wallet::Wallet;
use crate::error::WalletCreationError;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
use bdk::keys::bip39::WordCount; use bdk::keys::bip39::WordCount;
use bdk::wallet::tx_builder::ChangeSpendPolicy; use bdk::wallet::tx_builder::ChangeSpendPolicy;

View File

@@ -1,76 +1,34 @@
use crate::error::FeeRateError; use crate::bitcoin::{Address, OutPoint, Script, TxOut};
use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut};
use bdk::bitcoin::FeeRate as BdkFeeRate;
use bdk::chain::tx_graph::CanonicalTx as BdkCanonicalTx;
use bdk::chain::{ChainPosition as BdkChainPosition, ConfirmationTimeHeightAnchor};
use bdk::wallet::AddressIndex as BdkAddressIndex; use bdk::wallet::AddressIndex as BdkAddressIndex;
use bdk::wallet::AddressInfo as BdkAddressInfo; use bdk::wallet::AddressInfo as BdkAddressInfo;
use bdk::wallet::Balance as BdkBalance; use bdk::wallet::Balance as BdkBalance;
use bdk::KeychainKind; use bdk::KeychainKind;
use bdk::LocalOutput as BdkLocalOutput; use bdk::LocalOutput as BdkLocalOutput;
use bdk::FeeRate as BdkFeeRate;
use std::sync::Arc; use std::sync::Arc;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChainPosition {
Confirmed { height: u32, timestamp: u64 },
Unconfirmed { timestamp: u64 },
}
pub struct CanonicalTx {
pub transaction: Arc<Transaction>,
pub chain_position: ChainPosition,
}
impl From<BdkCanonicalTx<'_, Arc<bdk::bitcoin::Transaction>, ConfirmationTimeHeightAnchor>>
for CanonicalTx
{
fn from(
tx: BdkCanonicalTx<'_, Arc<bdk::bitcoin::Transaction>, ConfirmationTimeHeightAnchor>,
) -> Self {
let chain_position = match tx.chain_position {
BdkChainPosition::Confirmed(anchor) => ChainPosition::Confirmed {
height: anchor.confirmation_height,
timestamp: anchor.confirmation_time,
},
BdkChainPosition::Unconfirmed(timestamp) => ChainPosition::Unconfirmed { timestamp },
};
CanonicalTx {
transaction: Arc::new(Transaction::from(tx.tx_node.tx.as_ref().clone())),
chain_position,
}
}
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct FeeRate(pub BdkFeeRate); pub struct FeeRate(pub BdkFeeRate);
impl FeeRate { impl FeeRate {
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> { pub fn from_sat_per_vb(sat_per_vb: f32) -> Self {
let fee_rate: Option<BdkFeeRate> = BdkFeeRate::from_sat_per_vb(sat_per_vb); FeeRate(BdkFeeRate::from_sat_per_vb(sat_per_vb))
match fee_rate {
Some(fee_rate) => Ok(FeeRate(fee_rate)),
None => Err(FeeRateError::ArithmeticOverflow),
}
} }
pub fn from_sat_per_kwu(sat_per_kwu: u64) -> Self { pub fn from_sat_per_kwu(sat_per_kwu: f32) -> Self {
FeeRate(BdkFeeRate::from_sat_per_kwu(sat_per_kwu)) FeeRate(BdkFeeRate::from_sat_per_kwu(sat_per_kwu))
} }
pub fn to_sat_per_vb_ceil(&self) -> u64 { pub fn as_sat_per_vb(&self) -> f32 {
self.0.to_sat_per_vb_ceil() self.0.as_sat_per_vb()
} }
pub fn to_sat_per_vb_floor(&self) -> u64 { pub fn sat_per_kwu(&self) -> f32 {
self.0.to_sat_per_vb_floor() self.0.sat_per_kwu()
}
pub fn to_sat_per_kwu(&self) -> u64 {
self.0.to_sat_per_kwu()
} }
} }
@@ -179,7 +137,7 @@ impl From<BdkLocalOutput> for LocalOutput {
vout: local_utxo.outpoint.vout, vout: local_utxo.outpoint.vout,
}, },
txout: TxOut { txout: TxOut {
value: local_utxo.txout.value.to_sat(), value: local_utxo.txout.value,
script_pubkey: Arc::new(Script(local_utxo.txout.script_pubkey)), script_pubkey: Arc::new(Script(local_utxo.txout.script_pubkey)),
}, },
keychain: local_utxo.keychain, keychain: local_utxo.keychain,

View File

@@ -1,20 +1,19 @@
use crate::bitcoin::{OutPoint, Psbt, Script, Transaction}; use crate::bitcoin::{OutPoint, PartiallySignedTransaction, Transaction};
use crate::descriptor::Descriptor; use crate::descriptor::Descriptor;
use crate::error::{ use crate::error::{Alpha3Error, CalculateFeeError, WalletCreationError};
Alpha3Error, CalculateFeeError, PersistenceError, TxidParseError, WalletCreationError, use crate::types::ScriptAmount;
}; use crate::types::{Balance, FeeRate};
use crate::types::{ use crate::Script;
AddressIndex, AddressInfo, Balance, CanonicalTx, FeeRate, LocalOutput, ScriptAmount, use crate::{AddressIndex, AddressInfo};
};
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf; use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
use bdk::bitcoin::Psbt as BdkPsbt;
use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid}; use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
use bdk::wallet::tx_builder::ChangeSpendPolicy; use bdk::wallet::tx_builder::ChangeSpendPolicy;
use bdk::wallet::{ChangeSet, Update as BdkUpdate}; use bdk::wallet::{ChangeSet, Update as BdkUpdate};
use bdk::SignOptions;
use bdk::Wallet as BdkWallet; use bdk::Wallet as BdkWallet;
use bdk::{FeeRate as BdkFeeRate, SignOptions};
use bdk_file_store::Store; use bdk_file_store::Store;
use std::collections::HashSet; use std::collections::HashSet;
@@ -68,12 +67,13 @@ impl Wallet {
pub fn try_get_internal_address( pub fn try_get_internal_address(
&self, &self,
address_index: AddressIndex, address_index: AddressIndex,
) -> Result<AddressInfo, PersistenceError> { ) -> Result<AddressInfo, Alpha3Error> {
let address_info = self self.get_wallet()
.get_wallet() .try_get_internal_address(address_index.into())
.try_get_internal_address(address_index.into())? .map_or_else(
.into(); |_| Err(Alpha3Error::Generic),
Ok(address_info) |address_info| Ok(address_info.into()),
)
} }
pub fn network(&self) -> Network { pub fn network(&self) -> Network {
@@ -91,7 +91,7 @@ impl Wallet {
pub(crate) fn sign( pub(crate) fn sign(
&self, &self,
psbt: Arc<Psbt>, psbt: Arc<PartiallySignedTransaction>,
// sign_options: Option<SignOptions>, // sign_options: Option<SignOptions>,
) -> Result<bool, Alpha3Error> { ) -> Result<bool, Alpha3Error> {
let mut psbt = psbt.inner.lock().unwrap(); let mut psbt = psbt.inner.lock().unwrap();
@@ -105,19 +105,13 @@ impl Wallet {
SentAndReceivedValues { sent, received } SentAndReceivedValues { sent, received }
} }
pub fn transactions(&self) -> Vec<CanonicalTx> { pub fn transactions(&self) -> Vec<Arc<Transaction>> {
self.get_wallet() self.get_wallet()
.transactions() .transactions()
.map(|tx| tx.into()) .map(|tx| Arc::new(tx.tx_node.tx.into()))
.collect() .collect()
} }
pub fn get_tx(&self, txid: String) -> Result<Option<CanonicalTx>, TxidParseError> {
let txid =
Txid::from_str(txid.as_str()).map_err(|_| TxidParseError::InvalidTxid { txid })?;
Ok(self.get_wallet().get_tx(txid).map(|tx| tx.into()))
}
pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> { pub fn calculate_fee(&self, tx: &Transaction) -> Result<u64, CalculateFeeError> {
self.get_wallet() self.get_wallet()
.calculate_fee(&tx.into()) .calculate_fee(&tx.into())
@@ -130,14 +124,6 @@ impl Wallet {
.map(|bdk_fee_rate| Arc::new(FeeRate(bdk_fee_rate))) .map(|bdk_fee_rate| Arc::new(FeeRate(bdk_fee_rate)))
.map_err(|e| e.into()) .map_err(|e| e.into())
} }
pub fn list_unspent(&self) -> Vec<LocalOutput> {
self.get_wallet().list_unspent().map(|o| o.into()).collect()
}
pub fn list_output(&self) -> Vec<LocalOutput> {
self.get_wallet().list_output().map(|o| o.into()).collect()
}
} }
pub struct SentAndReceivedValues { pub struct SentAndReceivedValues {
@@ -488,7 +474,10 @@ impl TxBuilder {
// }) // })
// } // }
pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, Alpha3Error> { pub(crate) fn finish(
&self,
wallet: &Arc<Wallet>,
) -> Result<Arc<PartiallySignedTransaction>, Alpha3Error> {
// TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API? // TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API?
let mut wallet = wallet.get_wallet(); let mut wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_tx(); let mut tx_builder = wallet.build_tx();
@@ -544,13 +533,13 @@ impl TxBuilder {
#[derive(Clone)] #[derive(Clone)]
pub(crate) struct BumpFeeTxBuilder { pub(crate) struct BumpFeeTxBuilder {
pub(crate) txid: String, pub(crate) txid: String,
pub(crate) fee_rate: Arc<FeeRate>, pub(crate) fee_rate: f32,
pub(crate) allow_shrinking: Option<Arc<Script>>, pub(crate) allow_shrinking: Option<Arc<Script>>,
pub(crate) rbf: Option<RbfValue>, pub(crate) rbf: Option<RbfValue>,
} }
impl BumpFeeTxBuilder { impl BumpFeeTxBuilder {
pub(crate) fn new(txid: String, fee_rate: Arc<FeeRate>) -> Self { pub(crate) fn new(txid: String, fee_rate: f32) -> Self {
Self { Self {
txid, txid,
fee_rate, fee_rate,
@@ -580,11 +569,14 @@ impl BumpFeeTxBuilder {
}) })
} }
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<Arc<Psbt>, Alpha3Error> { pub(crate) fn finish(
&self,
wallet: &Wallet,
) -> Result<Arc<PartiallySignedTransaction>, Alpha3Error> {
let txid = Txid::from_str(self.txid.as_str()).map_err(|_| Alpha3Error::Generic)?; let txid = Txid::from_str(self.txid.as_str()).map_err(|_| Alpha3Error::Generic)?;
let mut wallet = wallet.get_wallet(); let mut wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_fee_bump(txid)?; let mut tx_builder = wallet.build_fee_bump(txid)?;
tx_builder.fee_rate(self.fee_rate.0); tx_builder.fee_rate(BdkFeeRate::from_sat_per_vb(self.fee_rate));
if let Some(allow_shrinking) = &self.allow_shrinking { if let Some(allow_shrinking) = &self.allow_shrinking {
tx_builder.allow_shrinking(allow_shrinking.0.clone())?; tx_builder.allow_shrinking(allow_shrinking.0.clone())?;
} }
@@ -598,7 +590,8 @@ impl BumpFeeTxBuilder {
} }
} }
} }
let psbt: BdkPsbt = tx_builder.finish().map_err(|_| Alpha3Error::Generic)?; let psbt: BdkPartiallySignedTransaction =
tx_builder.finish().map_err(|_| Alpha3Error::Generic)?;
Ok(Arc::new(psbt.into())) Ok(Arc::new(psbt.into()))
} }

View File

@@ -1,5 +1,5 @@
# bdk-jvm # bdk-jvm
This project builds a .jar package for the JVM platform that provides Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository. This project builds a .jar package for the JVM platform that provide Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository.
## How to Use ## How to Use
To use the Kotlin language bindings for [`bdk`] in your JVM project add the following to your gradle dependencies: To use the Kotlin language bindings for [`bdk`] in your JVM project add the following to your gradle dependencies:
@@ -13,6 +13,33 @@ dependencies {
} }
``` ```
You may then import and use the `org.bitcoindevkit` library in your Kotlin code like so. Note that this example is for the `0.30.0` release. For examples of the 1.0 API in the alpha releases, take a look at the tests [here](https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit).
```kotlin
import org.bitcoindevkit.*
// ...
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
val esploraClient: EsploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val wallet: Wallet = Wallet(
descriptor = externalDescriptor,
changeDescriptor = internalDescriptor,
persistenceBackendPath = "./bdkwallet.db",
network = Network.TESTNET
)
val update = esploraClient.fullScan(
wallet = wallet,
stopGap = 10uL,
parallelRequests = 1uL
)
wallet.applyUpdate(update)
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
```
### Snapshot releases ### Snapshot releases
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block: To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
```kotlin ```kotlin
@@ -29,17 +56,17 @@ dependencies {
* [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine) * [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine)
## How to build ## How to build
_Note that Kotlin version `1.9.23` or later is required to build the library._ _Note that Kotlin version `1.6.10` or later is required to build the library._
1. Install JDK 17. For example, with SDKMAN!: 1. Install JDK 11. It must be version 11 (not 17), otherwise it won't build. For example, with SDKMAN!:
```shell ```shell
curl -s "https://get.sdkman.io" | bash curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh" source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 17.0.2-tem sdk install java 11.0.19-tem
``` ```
2. Install Rust (note that we are currently building using Rust 1.77.1): 2. Install Rust (note that we are currently building using Rust 1.73.0):
```shell ```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default 1.77.1 rustup default 1.73.0
``` ```
3. Clone this repository. 3. Clone this repository.
```shell ```shell
@@ -57,7 +84,7 @@ rustup target add x86_64-apple-darwin aarch64-apple-darwin
## How to publish to your local Maven repo ## How to publish to your local Maven repo
```shell ```shell
cd bdk-jvm cd bdk-jvm
./gradlew publishToMavenLocal -P localBuild ./gradlew publishToMavenLocal --exclude-task signMavenPublication
``` ```
Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so: Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so:
@@ -66,7 +93,7 @@ signing.gnupg.keyName=<YOUR_GNUPG_ID>
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE> signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
``` ```
and use the `publishToMavenLocal` task without the `localBuild` flag: and use the `publishToMavenLocal` task without excluding the signing task:
```shell ```shell
./gradlew publishToMavenLocal ./gradlew publishToMavenLocal
``` ```

View File

@@ -1,9 +1,4 @@
plugins { plugins {
id("org.jetbrains.kotlin.jvm").version("1.9.23").apply(false)
id("org.gradle.java-library")
id("org.gradle.maven-publish")
id("org.gradle.signing")
id("org.bitcoindevkit.plugins.generate-jvm-bindings").apply(false)
id("io.github.gradle-nexus.publish-plugin") version "1.1.0" id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
} }

View File

@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx1536m org.gradle.jvmargs=-Xmx1536m
android.enableJetifier=true android.enableJetifier=true
kotlin.code.style=official kotlin.code.style=official
libraryVersion=1.0.0-alpha.9 libraryVersion=1.0.0-alpha.7

Binary file not shown.

View File

@@ -1,7 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

31
bdk-jvm/gradlew vendored
View File

@@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,11 +80,13 @@ do
esac esac
done done
# This is normally unused APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# shellcheck disable=SC2034
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -131,29 +133,22 @@ location of your Java installation."
fi fi
else else
JAVACMD=java JAVACMD=java
if ! command -v java >/dev/null 2>&1 which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #( case $MAX_FD in #(
max*) max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) || MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit" warn "Could not query maximum file descriptor limit"
esac esac
case $MAX_FD in #( case $MAX_FD in #(
'' | soft) :;; #( '' | soft) :;; #(
*) *)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" || ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD" warn "Could not set maximum file descriptor limit to $MAX_FD"
esac esac
@@ -198,10 +193,6 @@ if "$cygwin" || "$msys" ; then
done done
fi fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command; # Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in # shell script including quotes and variable substitutions, so put them in
@@ -214,12 +205,6 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \ org.gradle.wrapper.GradleWrapperMain \
"$@" "$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args. # Use "xargs" to parse quoted args.
# #
# With -n1 it outputs one arg per line, with the quotes and backslashes removed. # With -n1 it outputs one arg per line, with the quotes and backslashes removed.

15
bdk-jvm/gradlew.bat vendored
View File

@@ -14,7 +14,7 @@
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%" == "" @echo off
@rem ########################################################################## @rem ##########################################################################
@rem @rem
@rem Gradle startup script for Windows @rem Gradle startup script for Windows
@@ -25,8 +25,7 @@
if "%OS%"=="Windows_NT" setlocal if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0 set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=. if "%DIRNAME%" == "" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@@ -41,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -76,15 +75,13 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd if "%ERRORLEVEL%"=="0" goto mainEnd
:fail :fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code! rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL% if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
if %EXIT_CODE% equ 0 set EXIT_CODE=1 exit /b 1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd :mainEnd
if "%OS%"=="Windows_NT" endlocal if "%OS%"=="Windows_NT" endlocal

View File

@@ -1,20 +0,0 @@
test:
./gradlew test
offlinetests:
./gradlew test -P excludeConnectedTests
onetest TEST:
./gradlew test --tests {{TEST}}
build:
./gradlew buildJvmLib
publishlocal:
./gradlew publishToMavenLocal -P localBuild
clean:
rm -rf ../bdk-ffi/target/
rm -rf ./build/
rm -rf ./lib/build/
rm -rf ./plugins/build/

View File

@@ -6,15 +6,19 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
val libraryVersion: String by project val libraryVersion: String by project
plugins { plugins {
id("org.jetbrains.kotlin.jvm") id("org.jetbrains.kotlin.jvm") version "1.6.10"
id("org.gradle.java-library") id("java-library")
id("org.gradle.maven-publish") id("maven-publish")
id("org.gradle.signing") id("signing")
// Custom plugin to generate the native libs and bindings file // Custom plugin to generate the native libs and bindings file
id("org.bitcoindevkit.plugins.generate-jvm-bindings") id("org.bitcoindevkit.plugins.generate-jvm-bindings")
} }
repositories {
mavenCentral()
}
java { java {
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_11
@@ -37,7 +41,7 @@ tasks.test {
testing { testing {
suites { suites {
val test by getting(JvmTestSuite::class) { val test by getting(JvmTestSuite::class) {
useKotlinTest("1.9.23") useKotlinTest("1.6.10")
} }
} }
} }
@@ -107,10 +111,6 @@ afterEvaluate {
} }
signing { signing {
if (project.hasProperty("localBuild")) {
isRequired = false
}
val signingKeyId: String? by project val signingKeyId: String? by project
val signingKey: String? by project val signingKey: String? by project
val signingPassword: String? by project val signingPassword: String? by project

View File

@@ -31,9 +31,9 @@ class LiveTxBuilderTest {
assert(wallet.getBalance().total > 0uL) assert(wallet.getBalance().total > 0uL)
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET) val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: Psbt = TxBuilder() val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL) .addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(FeeRate.fromSatPerVb(2uL)) .feeRate(FeeRate.fromSatPerVb(2.0f))
.finish(wallet) .finish(wallet)
println(psbt.serialize()) println(psbt.serialize())
@@ -60,9 +60,9 @@ class LiveTxBuilderTest {
ScriptAmount(recipient2.scriptPubkey(), 4200uL), ScriptAmount(recipient2.scriptPubkey(), 4200uL),
) )
val psbt: Psbt = TxBuilder() val psbt: PartiallySignedTransaction = TxBuilder()
.setRecipients(allRecipients) .setRecipients(allRecipients)
.feeRate(FeeRate.fromSatPerVb(4uL)) .feeRate(FeeRate.fromSatPerVb(4.0f))
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN) .changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
.enableRbf() .enableRbf()
.finish(wallet) .finish(wallet)

View File

@@ -35,8 +35,8 @@ class LiveWalletTest {
println("Transactions count: ${wallet.transactions().count()}") println("Transactions count: ${wallet.transactions().count()}")
val transactions = wallet.transactions().take(3) val transactions = wallet.transactions().take(3)
for (tx in transactions) { for (tx in transactions) {
val sentAndReceived = wallet.sentAndReceived(tx.transaction) val sentAndReceived = wallet.sentAndReceived(tx)
println("Transaction: ${tx.transaction.txid()}") println("Transaction: ${tx.txid()}")
println("Sent ${sentAndReceived.sent}") println("Sent ${sentAndReceived.sent}")
println("Received ${sentAndReceived.received}") println("Received ${sentAndReceived.received}")
} }
@@ -59,9 +59,9 @@ class LiveWalletTest {
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET) val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: Psbt = TxBuilder() val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL) .addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(FeeRate.fromSatPerVb(2uL)) .feeRate(FeeRate.fromSatPerVb(2.0f))
.finish(wallet) .finish(wallet)
println(psbt.serialize()) println(psbt.serialize())
@@ -77,7 +77,7 @@ class LiveWalletTest {
println("Tx fee is: ${txFee}") println("Tx fee is: ${txFee}")
val feeRate: FeeRate = wallet.calculateFeeRate(tx) val feeRate: FeeRate = wallet.calculateFeeRate(tx)
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB") println("Tx fee rate is: ${feeRate.asSatPerVb()} sat/vB")
esploraClient.broadcast(tx) esploraClient.broadcast(tx)
} }

View File

@@ -2,15 +2,3 @@ rootProject.name = "bdk-jvm"
include(":lib") include(":lib")
includeBuild("plugins") includeBuild("plugins")
pluginManagement {
repositories {
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}

View File

@@ -1,11 +0,0 @@
test:
python -m unittest --verbose
maclocalbuild:
bash ./scripts/generate-macos-arm64.sh && python3 setup.py bdist_wheel --verbose
clean:
rm -rf ../bdk-ffi/target/
rm -rf ./bdkpython.egg-info/
rm -rf ./build/
rm -rf ./dist/

View File

@@ -9,7 +9,7 @@ cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..." echo "Generating native binaries..."
rustup default 1.77.1 rustup default 1.73.0
cargo build --profile release-smaller cargo build --profile release-smaller
echo "Copying linux libbdkffi.so..." echo "Copying linux libbdkffi.so..."

View File

@@ -2,14 +2,14 @@
set -euo pipefail set -euo pipefail
python3 --version python3 --version
pip install -r requirements.txt pip install --user -r requirements.txt
echo "Generating bdk.py..." echo "Generating bdk.py..."
cd ../bdk-ffi/ cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..." echo "Generating native binaries..."
rustup default 1.77.1 rustup default 1.73.0
rustup target add aarch64-apple-darwin rustup target add aarch64-apple-darwin
cargo build --profile release-smaller --target aarch64-apple-darwin cargo build --profile release-smaller --target aarch64-apple-darwin

View File

@@ -2,14 +2,14 @@
set -euo pipefail set -euo pipefail
python3 --version python3 --version
pip install -r requirements.txt pip install --user -r requirements.txt
echo "Generating bdk.py..." echo "Generating bdk.py..."
cd ../bdk-ffi/ cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..." echo "Generating native binaries..."
rustup default 1.77.1 rustup default 1.73.0
rustup target add x86_64-apple-darwin rustup target add x86_64-apple-darwin
cargo build --profile release-smaller --target x86_64-apple-darwin cargo build --profile release-smaller --target x86_64-apple-darwin

View File

@@ -2,14 +2,14 @@
set -euo pipefail set -euo pipefail
python3 --version python3 --version
pip install -r requirements.txt pip install --user -r requirements.txt
echo "Generating bdk.py..." echo "Generating bdk.py..."
cd ../bdk-ffi/ cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..." echo "Generating native binaries..."
rustup default 1.77.1 rustup default 1.73.0
rustup target add x86_64-pc-windows-msvc rustup target add x86_64-pc-windows-msvc
cargo build --profile release-smaller --target x86_64-pc-windows-msvc cargo build --profile release-smaller --target x86_64-pc-windows-msvc

View File

@@ -18,7 +18,7 @@ import bdkpython as bdk
setup( setup(
name="bdkpython", name="bdkpython",
version="1.0.0a9", version="1.0.0-alpha7",
description="The Python language bindings for the Bitcoin Development Kit", description="The Python language bindings for the Bitcoin Development Kit",
long_description=LONG_DESCRIPTION, long_description=LONG_DESCRIPTION,
long_description_content_type="text/markdown", long_description_content_type="text/markdown",

View File

@@ -34,7 +34,7 @@ class LiveTxBuilderTest(unittest.TestCase):
network = bdk.Network.TESTNET network = bdk.Network.TESTNET
) )
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet) psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2.0)).finish(wallet)
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi") self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
@@ -76,7 +76,7 @@ class LiveTxBuilderTest(unittest.TestCase):
bdk.ScriptAmount(recipient2.script_pubkey, 4200) bdk.ScriptAmount(recipient2.script_pubkey, 4200)
) )
psbt: bdk.Psbt = bdk.TxBuilder().set_recipients(all_recipients).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).enable_rbf().finish(wallet) psbt: bdk.PartiallySignedTransaction = bdk.TxBuilder().set_recipients(all_recipients).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2.0)).enable_rbf().finish(wallet)
wallet.sign(psbt) wallet.sign(psbt)
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi") self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")

View File

@@ -32,8 +32,8 @@ class LiveWalletTest(unittest.TestCase):
print(f"Transactions count: {len(wallet.transactions())}") print(f"Transactions count: {len(wallet.transactions())}")
transactions = wallet.transactions()[:3] transactions = wallet.transactions()[:3]
for tx in transactions: for tx in transactions:
sent_and_received = wallet.sent_and_received(tx.transaction) sent_and_received = wallet.sent_and_received(tx)
print(f"Transaction: {tx.transaction.txid()}") print(f"Transaction: {tx.txid()}")
print(f"Sent {sent_and_received.sent}") print(f"Sent {sent_and_received.sent}")
print(f"Received {sent_and_received.received}") print(f"Received {sent_and_received.received}")
@@ -64,7 +64,7 @@ class LiveWalletTest(unittest.TestCase):
network = bdk.Network.TESTNET network = bdk.Network.TESTNET
) )
psbt: bdk.Psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet) psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2.0)).finish(wallet)
# print(psbt.serialize()) # print(psbt.serialize())
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi") self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
@@ -75,7 +75,7 @@ class LiveWalletTest(unittest.TestCase):
fee = wallet.calculate_fee(tx) fee = wallet.calculate_fee(tx)
print(f"Transaction Fee: {fee}") print(f"Transaction Fee: {fee}")
fee_rate = wallet.calculate_fee_rate(tx) fee_rate = wallet.calculate_fee_rate(tx)
print(f"Transaction Fee Rate: {fee_rate.to_sat_per_vb_ceil()} sat/vB") print(f"Transaction Fee Rate: {fee_rate.as_sat_per_vb()} sat/vB")
esploraClient.broadcast(tx) esploraClient.broadcast(tx)

View File

@@ -45,9 +45,9 @@ final class LiveTxBuilderTests: XCTestCase {
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds") XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet) let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
let psbt: Psbt = try TxBuilder() let psbt: PartiallySignedTransaction = try TxBuilder()
.addRecipient(script: recipient.scriptPubkey(), amount: 4200) .addRecipient(script: recipient.scriptPubkey(), amount: 4200)
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2)) .feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2.0))
.finish(wallet: wallet) .finish(wallet: wallet)
print(psbt.serialize()) print(psbt.serialize())
@@ -86,9 +86,9 @@ final class LiveTxBuilderTests: XCTestCase {
ScriptAmount(script: recipient2.scriptPubkey(), amount: 4200) ScriptAmount(script: recipient2.scriptPubkey(), amount: 4200)
] ]
let psbt: Psbt = try TxBuilder() let psbt: PartiallySignedTransaction = try TxBuilder()
.setRecipients(recipients: allRecipients) .setRecipients(recipients: allRecipients)
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 4)) .feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 4.0))
.changePolicy(changePolicy: ChangeSpendPolicy.changeForbidden) .changePolicy(changePolicy: ChangeSpendPolicy.changeForbidden)
.enableRbf() .enableRbf()
.finish(wallet: wallet) .finish(wallet: wallet)

View File

@@ -47,8 +47,8 @@ final class LiveWalletTests: XCTestCase {
print("Transactions count: \(wallet.transactions().count)") print("Transactions count: \(wallet.transactions().count)")
let transactions = wallet.transactions().prefix(3) let transactions = wallet.transactions().prefix(3)
for tx in transactions { for tx in transactions {
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction) let sentAndReceived = wallet.sentAndReceived(tx: tx)
print("Transaction: \(tx.transaction.txid())") print("Transaction: \(tx.txid())")
print("Sent \(sentAndReceived.sent)") print("Sent \(sentAndReceived.sent)")
print("Received \(sentAndReceived.received)") print("Received \(sentAndReceived.received)")
} }
@@ -78,10 +78,10 @@ final class LiveWalletTests: XCTestCase {
print("Balance: \(wallet.getBalance().total)") print("Balance: \(wallet.getBalance().total)")
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet) let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
let psbt: Psbt = try let psbt: PartiallySignedTransaction = try
TxBuilder() TxBuilder()
.addRecipient(script: recipient.scriptPubkey(), amount: 4200) .addRecipient(script: recipient.scriptPubkey(), amount: 4200)
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2)) .feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2.0))
.finish(wallet: wallet) .finish(wallet: wallet)
print(psbt.serialize()) print(psbt.serialize())
@@ -90,12 +90,12 @@ final class LiveWalletTests: XCTestCase {
let walletDidSign: Bool = try wallet.sign(psbt: psbt) let walletDidSign: Bool = try wallet.sign(psbt: psbt)
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction") XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")
let tx: Transaction = try! psbt.extractTx() let tx: Transaction = psbt.extractTx()
print(tx.txid()) print(tx.txid())
let fee: UInt64 = try wallet.calculateFee(tx: tx) let fee: UInt64 = try wallet.calculateFee(tx: tx)
print("Transaction Fee: \(fee)") print("Transaction Fee: \(fee)")
let feeRate: FeeRate = try wallet.calculateFeeRate(tx: tx) let feeRate: FeeRate = try wallet.calculateFeeRate(tx: tx)
print("Transaction Fee Rate: \(feeRate.toSatPerVbCeil()) sat/vB") print("Transaction Fee Rate: \(feeRate.asSatPerVb()) sat/vB")
try esploraClient.broadcast(transaction: tx) try esploraClient.broadcast(transaction: tx)
} }

View File

@@ -3,15 +3,18 @@
# The results of this script can be used for locally testing your SPM package adding a local package # The results of this script can be used for locally testing your SPM package adding a local package
# to your application pointing at the bdk-swift directory. # to your application pointing at the bdk-swift directory.
rustup default 1.77.1 # Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
rustup install 1.73.0
rustup component add rust-src rustup component add rust-src
rustup target add aarch64-apple-ios # iOS arm64 rustup target add aarch64-apple-ios # iOS ARM64
rustup target add x86_64-apple-ios # iOS x86_64 rustup target add x86_64-apple-ios # iOS x86_64
rustup target add aarch64-apple-ios-sim # simulator mac M1 rustup target add aarch64-apple-ios-sim # simulator mac M1
rustup target add aarch64-apple-darwin # mac M1 rustup target add aarch64-apple-darwin # mac M1
rustup target add x86_64-apple-darwin # mac x86_64 rustup target add x86_64-apple-darwin # mac x86_64
cd ../bdk-ffi/ || exit pushd bdk-ffi
mkdir -p Sources/BitcoinDevKit
cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
@@ -25,7 +28,8 @@ lipo target/aarch64-apple-ios-sim/release-smaller/libbdkffi.a target/x86_64-appl
mkdir -p target/lipo-macos/release-smaller mkdir -p target/lipo-macos/release-smaller
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
cd ../bdk-swift/ || exit popd
pushd bdk-swift
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
@@ -35,3 +39,5 @@ cp ../bdk-ffi/target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework
cp ../bdk-ffi/target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI cp ../bdk-ffi/target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
rm Sources/BitcoinDevKit/bdkFFI.h rm Sources/BitcoinDevKit/bdkFFI.h
rm Sources/BitcoinDevKit/bdkFFI.modulemap rm Sources/BitcoinDevKit/bdkFFI.modulemap
#rm bdkFFI.xcframework.zip || true
#zip -9 -r bdkFFI.xcframework.zip bdkFFI.xcframework

View File

@@ -1,11 +0,0 @@
build:
bash ./build-local-swift.sh
test:
swift test
offlinetests:
swift test --skip LiveWalletTests --skip LiveTxBuilderTests
clean:
rm -rf ../bdk-ffi/target/