Compare commits
51 Commits
v1.0.0-alp
...
release/1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38ff60adda | ||
|
|
74bd1fcb5f | ||
|
|
86ffd1c551 | ||
|
|
7b0ce66c6d | ||
|
|
c45b29aa0b | ||
|
|
edea8e8c80 | ||
|
|
6bc974ed2e | ||
|
|
2f7652b979 | ||
|
|
fa17a862ce | ||
|
|
77cfee718c | ||
|
|
aa1c0de244 | ||
|
|
aee84f9634 | ||
|
|
a35fdaee61 | ||
|
|
dccc85d8ff | ||
|
|
f1744d192f | ||
|
|
cacb78f4dc | ||
|
|
54f3235254 | ||
|
|
806e29b93c | ||
|
|
97a104fd5f | ||
|
|
0e617bc986 | ||
|
|
ab2e97e782 | ||
|
|
84f2497aeb | ||
|
|
ab87355f9d | ||
|
|
a40702ebd9 | ||
|
|
0eadff1327 | ||
|
|
78ef936369 | ||
|
|
7b9e71714f | ||
|
|
f698d46392 | ||
|
|
ab9763bb58 | ||
|
|
aa035588a0 | ||
|
|
e774c94998 | ||
|
|
5e41275f29 | ||
|
|
15ac8c8ffb | ||
|
|
358b43c31e | ||
|
|
08f1957fe0 | ||
|
|
a8128e5056 | ||
|
|
b879bf4a50 | ||
|
|
43ff1fb394 | ||
|
|
a8541ecd40 | ||
|
|
c666bb247a | ||
|
|
d066007d9c | ||
|
|
c6d17b77ec | ||
|
|
9a996b1a94 | ||
|
|
c68c13fbfa | ||
|
|
0b851d441f | ||
|
|
267685bd58 | ||
|
|
896303f487 | ||
|
|
9ea9e9384c | ||
|
|
2e0cd058f4 | ||
|
|
c3313fb922 | ||
|
|
5fc30c6c26 |
104
.github/ISSUE_TEMPLATE/release.md
vendored
104
.github/ISSUE_TEMPLATE/release.md
vendored
@@ -6,84 +6,90 @@ labels: 'release'
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Create a new minor release
|
||||
## Bumping BDK Rust Version
|
||||
# Part 1: Bump BDK Rust Version
|
||||
|
||||
1. - [ ] Open a PR with an update to `Cargo.toml` to the new bdk release candidate and ensure all CI workflows run correctly. Fix errors if necessary.
|
||||
2. - [ ] Once the new bdk release is out, update the PR to replace the release candidate with the full release and merge.
|
||||
|
||||
### Specific Libraries' Workflows
|
||||
#### _Android_
|
||||
# Part 2: Prepare Libraries for Release Branch
|
||||
|
||||
### _Android_
|
||||
|
||||
3. - [ ] Update the API docs to reflect the changes in the API
|
||||
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 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).
|
||||
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.
|
||||
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).
|
||||
```shell
|
||||
# start an emulator prior to running the tests
|
||||
cd ./bdk-android/
|
||||
./gradlew buildAndroidLib
|
||||
./gradlew connectedAndroidTest
|
||||
just clean
|
||||
just build
|
||||
just test
|
||||
```
|
||||
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
|
||||
8. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch
|
||||
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.
|
||||
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
|
||||
cd ./bdk-jvm/
|
||||
./gradlew buildJvmLib
|
||||
./gradlew test
|
||||
just clean
|
||||
just build
|
||||
just test
|
||||
```
|
||||
10. - [ ] Update the readme if necessary
|
||||
1. - [ ] Update the readme if necessary
|
||||
#### _Swift_
|
||||
11. - [ ] Run the tests and adjust if necessary
|
||||
1. - [ ] Run the tests and adjust if necessary
|
||||
10. - [ ] Update the readme if necessary
|
||||
|
||||
### _Swift_
|
||||
|
||||
11. - [ ] Delete the `target` directory in bdk-ffi
|
||||
12. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
./bdk-swift/build-local-swift.sh
|
||||
cd ./bdk-swift/
|
||||
swift test
|
||||
just clean
|
||||
just build
|
||||
just test
|
||||
```
|
||||
12. - [ ] Update the readme if necessary
|
||||
1. - [ ] Update the readme if necessary
|
||||
#### _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. - [ ] Build the library
|
||||
13. - [ ] Update the readme if necessary
|
||||
|
||||
### _Python_
|
||||
|
||||
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
|
||||
15. - [ ] Build the library
|
||||
```shell
|
||||
cd ./bdk-python/
|
||||
just clean
|
||||
pip3 install --requirement requirements.txt
|
||||
bash ./scripts/generate-macos-arm64.sh # run the script for your particular platform
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
```
|
||||
1. - [ ] Run the tests and adjust if necessary
|
||||
16. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
|
||||
python -m unittest --verbose
|
||||
```
|
||||
1. - [ ] Update the readme and `setup.py` if necessary
|
||||
17. - [ ] 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).
|
||||
|
||||
### 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).
|
||||
18. - [ ] Create a new branch off of `master` called `release/version`
|
||||
19. - [ ] Update bdk-android version from `SNAPSHOT` version to release version
|
||||
20. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
|
||||
21. - [ ] Update bdk-python version from `.dev` version to release version
|
||||
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).
|
||||
23. - [ ] Get a review and ACK and merge the PR updating all the languages to their release versions
|
||||
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.
|
||||
## Part 3: Release Workflow
|
||||
|
||||
19. - [ ] Create a new branch off of `master` called `release/<feature version>`, e.g. `release/0.31`
|
||||
20. - [ ] Update bdk-android version from `SNAPSHOT` version to release version
|
||||
21. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
|
||||
22. - [ ] 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).
|
||||
24. - [ ] 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.
|
||||
```shell
|
||||
git tag v0.6.0 --sign --edit
|
||||
git push upstream v0.6.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`)
|
||||
26. - [ ] Make sure the released libraries work and contain the artifacts you would expect
|
||||
27. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file
|
||||
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`
|
||||
29. - [ ] Apply changes to the minor_release and patch_release issue templates if they need any
|
||||
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.
|
||||
31. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one)
|
||||
32. - [ ] Post in the announcement channel
|
||||
33. - [ ] Tweet about the library
|
||||
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`).
|
||||
27. - [ ] 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
|
||||
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`
|
||||
30. - [ ] 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.
|
||||
32. - [ ] 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
|
||||
34. - [ ] Tweet about the library
|
||||
|
||||
2
.github/workflows/cont_integration.yml
vendored
2
.github/workflows/cont_integration.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.73.0
|
||||
- version: 1.77.1
|
||||
clippy: true
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
|
||||
4
.github/workflows/live-tests.yaml
vendored
4
.github/workflows/live-tests.yaml
vendored
@@ -27,8 +27,8 @@ jobs:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
|
||||
8
.github/workflows/publish-android.yaml
vendored
8
.github/workflows/publish-android.yaml
vendored
@@ -23,10 +23,10 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
@@ -36,7 +36,7 @@ jobs:
|
||||
cd bdk-android
|
||||
./gradlew buildAndroidLib
|
||||
|
||||
- name: "Publish to Maven Local and Maven Central"
|
||||
- name: "Publish to Maven Central"
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
||||
|
||||
18
.github/workflows/publish-jvm.yaml
vendored
18
.github/workflows/publish-jvm.yaml
vendored
@@ -22,10 +22,10 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install aarch64 Rust target"
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
@@ -52,10 +52,10 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install x86_64-pc-windows-msvc Rust target"
|
||||
run: rustup target add x86_64-pc-windows-msvc
|
||||
@@ -92,10 +92,10 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
|
||||
8
.github/workflows/publish-python.yaml
vendored
8
.github/workflows/publish-python.yaml
vendored
@@ -24,6 +24,8 @@ jobs:
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
- cp311-cp311
|
||||
- cp312-cp312
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
@@ -59,6 +61,8 @@ jobs:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
@@ -96,6 +100,8 @@ jobs:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
@@ -132,6 +138,8 @@ jobs:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
6
.github/workflows/test-android.yaml
vendored
6
.github/workflows/test-android.yaml
vendored
@@ -35,10 +35,10 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
|
||||
6
.github/workflows/test-jvm.yaml
vendored
6
.github/workflows/test-jvm.yaml
vendored
@@ -30,10 +30,10 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
java-version: 17
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
- name: "Set default Rust version to 1.77.1"
|
||||
run: rustup default 1.77.1
|
||||
|
||||
- name: "Run JVM tests"
|
||||
run: |
|
||||
|
||||
8
.github/workflows/test-python.yaml
vendored
8
.github/workflows/test-python.yaml
vendored
@@ -33,6 +33,8 @@ jobs:
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
- cp311-cp311
|
||||
- cp312-cp312
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
@@ -74,6 +76,8 @@ jobs:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
@@ -117,6 +121,8 @@ jobs:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
@@ -158,6 +164,8 @@ jobs:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
- "3.11"
|
||||
- "3.12"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
3
.github/workflows/test-swift.yaml
vendored
3
.github/workflows/test-swift.yaml
vendored
@@ -19,7 +19,8 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Build Swift package"
|
||||
run: bash ./bdk-swift/build-local-swift.sh
|
||||
working-directory: bdk-swift
|
||||
run: bash ./build-local-swift.sh
|
||||
|
||||
- name: "Run Swift tests"
|
||||
working-directory: bdk-swift
|
||||
|
||||
@@ -3,6 +3,9 @@ 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).
|
||||
|
||||
## [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]
|
||||
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
|
||||
@@ -242,6 +245,8 @@ Changelog
|
||||
|
||||
[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.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
|
||||
|
||||
25
README.md
25
README.md
@@ -26,22 +26,21 @@ The below directories (a separate repository in the case of bdk-swift) include i
|
||||
| Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | |
|
||||
| Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | |
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
## 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
|
||||
|
||||
This library should compile with any combination of features with Rust 1.73.0.
|
||||
just build
|
||||
just offlinetests
|
||||
just publishlocal
|
||||
```
|
||||
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
This library should compile with any combination of features with Rust 1.77.1.
|
||||
|
||||
## Contributing
|
||||
|
||||
### 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`
|
||||
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.
|
||||
|
||||
## Goals
|
||||
1. Language bindings should feel idiomatic in target languages/platforms
|
||||
|
||||
@@ -13,33 +13,6 @@ 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
|
||||
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
|
||||
```kotlin
|
||||
@@ -58,17 +31,17 @@ dependencies {
|
||||
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
|
||||
|
||||
### How to build
|
||||
_Note that Kotlin version `1.6.10` or later is required to build the library._
|
||||
_Note that Kotlin version `1.9.23` or later is required to build the library._
|
||||
|
||||
1. Clone this repository.
|
||||
```shell
|
||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||
```
|
||||
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||
3. Install Rust (note that we are currently building using Rust 1.73.0):
|
||||
3. Install Rust (note that we are currently building using Rust 1.77.1):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
```
|
||||
4. Install required targets
|
||||
```sh
|
||||
@@ -95,7 +68,7 @@ export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
|
||||
## How to publish to your local Maven repo
|
||||
```shell
|
||||
cd bdk-android
|
||||
./gradlew publishToMavenLocal --exclude-task signMavenPublication
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
```
|
||||
|
||||
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:
|
||||
@@ -104,7 +77,7 @@ signing.gnupg.keyName=<YOUR_GNUPG_ID>
|
||||
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
|
||||
```
|
||||
|
||||
and use the `publishToMavenLocal` task without excluding the signing task:
|
||||
and use the `publishToMavenLocal` task without the `localBuild` flag:
|
||||
```shell
|
||||
./gradlew publishToMavenLocal
|
||||
```
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.android.tools.build:gradle:7.1.2")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
|
||||
id("com.android.library").version("8.3.1").apply(false)
|
||||
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
|
||||
|
||||
@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.7
|
||||
libraryVersion=1.0.0-alpha.9
|
||||
|
||||
BIN
bdk-android/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
bdk-android/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
41
bdk-android/gradlew
vendored
41
bdk-android/gradlew
vendored
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +80,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
# 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
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,22 +131,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
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
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
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 ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | 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" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# 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:
|
||||
# * 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 -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
@@ -205,6 +214,12 @@ set -- \
|
||||
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.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
15
bdk-android/gradlew.bat
vendored
15
bdk-android/gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
17
bdk-android/justfile
Normal file
17
bdk-android/justfile
Normal file
@@ -0,0 +1,17 @@
|
||||
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/
|
||||
@@ -5,25 +5,21 @@ val libraryVersion: String by project
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android") version "1.6.10"
|
||||
id("maven-publish")
|
||||
id("signing")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.gradle.maven-publish")
|
||||
id("org.gradle.signing")
|
||||
|
||||
// Custom plugin to generate the native libs and bindings file
|
||||
id("org.bitcoindevkit.plugins.generate-android-bindings")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = 33
|
||||
namespace = "org.bitcoindevkit"
|
||||
compileSdk = 34
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 21
|
||||
targetSdk = 33
|
||||
targetSdk = 34
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
@@ -43,6 +39,20 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(17))
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("net.java.dev.jna:jna:5.14.0@aar")
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
|
||||
@@ -96,9 +106,22 @@ 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 {
|
||||
if (project.hasProperty("localBuild")) {
|
||||
isRequired = false
|
||||
}
|
||||
|
||||
val signingKeyId: String? by project
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
@@ -106,8 +129,7 @@ signing {
|
||||
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> {
|
||||
dependsOn("buildAndroidLib")
|
||||
}
|
||||
|
||||
@@ -33,9 +33,9 @@ class LiveTxBuilderTest {
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(2.0f))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
@@ -61,9 +61,9 @@ class LiveTxBuilderTest {
|
||||
ScriptAmount(recipient2.scriptPubkey(), 4200uL),
|
||||
)
|
||||
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.setRecipients(allRecipients)
|
||||
.feeRate(FeeRate.fromSatPerVb(4.0f))
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
|
||||
.enableRbf()
|
||||
.finish(wallet)
|
||||
|
||||
@@ -37,8 +37,8 @@ class LiveWalletTest {
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx)
|
||||
println("Transaction: ${tx.txid()}")
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.txid()}")
|
||||
println("Sent ${sentAndReceived.sent}")
|
||||
println("Received ${sentAndReceived.received}")
|
||||
}
|
||||
@@ -61,9 +61,9 @@ class LiveWalletTest {
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(4.0f))
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
@@ -79,7 +79,7 @@ class LiveWalletTest {
|
||||
println("Tx fee is: ${txFee}")
|
||||
|
||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||
println("Tx fee rate is: ${feeRate.asSatPerVb()} sat/vB")
|
||||
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.bitcoindevkit">
|
||||
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -113,6 +113,8 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
val moveNativeAndroidLibs by tasks.register<Copy>("moveNativeAndroidLibs") {
|
||||
|
||||
dependsOn(buildAndroidAarch64Binary)
|
||||
dependsOn(buildAndroidArmv7Binary)
|
||||
dependsOn(buildAndroidX86_64Binary)
|
||||
|
||||
into("${project.projectDir}/../lib/src/main/jniLibs/")
|
||||
|
||||
|
||||
@@ -2,3 +2,17 @@ rootProject.name = "bdk-android"
|
||||
|
||||
include(":lib")
|
||||
includeBuild("plugins")
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
607
bdk-ffi/Cargo.lock
generated
607
bdk-ffi/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "1.0.0-alpha.7"
|
||||
version = "1.0.0-alpha.9"
|
||||
homepage = "https://bitcoindevkit.org"
|
||||
repository = "https://github.com/bitcoindevkit/bdk"
|
||||
edition = "2018"
|
||||
@@ -18,12 +18,14 @@ path = "uniffi-bindgen.rs"
|
||||
default = ["uniffi/cli"]
|
||||
|
||||
[dependencies]
|
||||
bdk = { version = "1.0.0-alpha.7", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.9.0", default-features = false, features = ["std", "blocking"] }
|
||||
# bdk_esplora = { version = "0.9.0", default-features = false, features = ["std", "blocking", "async-https-rustls"] }
|
||||
bdk_file_store = { version = "0.7.0" }
|
||||
bdk = { version = "1.0.0-alpha.9", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.11.0", default-features = false, features = ["std", "blocking"] }
|
||||
esplora-client = { version = "0.7.0", default-features = false, features = ["blocking-https-rustls"] }
|
||||
# bdk_esplora = { version = "0.10.0", default-features = false, features = ["std", "blocking", "async-https-rustls"] }
|
||||
bdk_file_store = { version = "0.9.0" }
|
||||
|
||||
uniffi = { version = "=0.26.1" }
|
||||
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
|
||||
thiserror = "1.0.58"
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
5
bdk-ffi/justfile
Normal file
5
bdk-ffi/justfile
Normal file
@@ -0,0 +1,5 @@
|
||||
build:
|
||||
cargo build
|
||||
|
||||
test:
|
||||
cargo test --lib
|
||||
@@ -27,19 +27,89 @@ interface WalletCreationError {
|
||||
LoadedNetworkDoesNotMatch(Network expected, Network? got);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface PersistenceError {
|
||||
Write(string e);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface EsploraError {
|
||||
Ureq(string error_message);
|
||||
UreqTransport(string error_message);
|
||||
Http(u16 status_code);
|
||||
Io(string error_message);
|
||||
NoHeader();
|
||||
Minreq(string error_message);
|
||||
HttpResponse(u16 status, string error_message);
|
||||
Parsing(string error_message);
|
||||
StatusCode(string error_message);
|
||||
BitcoinEncoding(string error_message);
|
||||
Hex(string error_message);
|
||||
HexToArray(string error_message);
|
||||
HexToBytes(string error_message);
|
||||
TransactionNotFound();
|
||||
HeaderHeightNotFound(u32 height);
|
||||
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();
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -90,20 +160,33 @@ dictionary TxOut {
|
||||
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
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb]
|
||||
constructor(f32 sat_per_vb);
|
||||
[Name=from_sat_per_vb, Throws=FeeRateError]
|
||||
constructor(u64 sat_per_vb);
|
||||
|
||||
[Name=from_sat_per_kwu]
|
||||
constructor(f32 sat_per_kwu);
|
||||
constructor(u64 sat_per_kwu);
|
||||
|
||||
f32 as_sat_per_vb();
|
||||
u64 to_sat_per_vb_ceil();
|
||||
|
||||
f32 sat_per_kwu();
|
||||
u64 to_sat_per_vb_floor();
|
||||
|
||||
u64 to_sat_per_kwu();
|
||||
};
|
||||
|
||||
enum ChangeSpendPolicy {
|
||||
@@ -118,7 +201,7 @@ interface Wallet {
|
||||
|
||||
AddressInfo get_address(AddressIndex address_index);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=PersistenceError]
|
||||
AddressInfo try_get_internal_address(AddressIndex address_index);
|
||||
|
||||
Network network();
|
||||
@@ -131,17 +214,24 @@ interface Wallet {
|
||||
boolean is_mine([ByRef] Script script);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
boolean sign(PartiallySignedTransaction psbt);
|
||||
boolean sign(Psbt psbt);
|
||||
|
||||
SentAndReceivedValues sent_and_received([ByRef] Transaction tx);
|
||||
|
||||
sequence<Transaction> transactions();
|
||||
sequence<CanonicalTx> transactions();
|
||||
|
||||
[Throws=TxidParseError]
|
||||
CanonicalTx? get_tx(string txid);
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
u64 calculate_fee([ByRef] Transaction tx);
|
||||
|
||||
[Throws=CalculateFeeError]
|
||||
FeeRate calculate_fee_rate([ByRef] Transaction tx);
|
||||
|
||||
sequence<LocalOutput> list_unspent();
|
||||
|
||||
sequence<LocalOutput> list_output();
|
||||
};
|
||||
|
||||
interface Update {};
|
||||
@@ -180,11 +270,11 @@ interface TxBuilder {
|
||||
TxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
||||
Psbt finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
interface BumpFeeTxBuilder {
|
||||
constructor(string txid, f32 fee_rate);
|
||||
constructor(string txid, FeeRate fee_rate);
|
||||
|
||||
BumpFeeTxBuilder allow_shrinking(Script script_pubkey);
|
||||
|
||||
@@ -193,7 +283,7 @@ interface BumpFeeTxBuilder {
|
||||
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
||||
Psbt finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -250,7 +340,7 @@ interface DescriptorPublicKey {
|
||||
};
|
||||
|
||||
interface Descriptor {
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=DescriptorError]
|
||||
constructor(string descriptor, Network network);
|
||||
|
||||
[Name=new_bip44]
|
||||
@@ -292,7 +382,7 @@ interface EsploraClient {
|
||||
[Throws=EsploraError]
|
||||
Update full_scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);
|
||||
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=EsploraError]
|
||||
void broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
@@ -337,7 +427,7 @@ enum WordCount {
|
||||
};
|
||||
|
||||
interface Address {
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=AddressError]
|
||||
constructor(string address, Network network);
|
||||
|
||||
Network network();
|
||||
@@ -352,30 +442,33 @@ interface Address {
|
||||
};
|
||||
|
||||
interface Transaction {
|
||||
[Throws=Alpha3Error]
|
||||
[Throws=TransactionError]
|
||||
constructor(sequence<u8> transaction_bytes);
|
||||
|
||||
string txid();
|
||||
|
||||
u64 size();
|
||||
u64 total_size();
|
||||
|
||||
u64 vsize();
|
||||
|
||||
boolean is_coin_base();
|
||||
boolean is_coinbase();
|
||||
|
||||
boolean is_explicitly_rbf();
|
||||
|
||||
boolean is_lock_time_enabled();
|
||||
|
||||
i32 version();
|
||||
|
||||
sequence<u8> serialize();
|
||||
};
|
||||
|
||||
interface PartiallySignedTransaction {
|
||||
[Throws=Alpha3Error]
|
||||
interface Psbt {
|
||||
[Throws=PsbtParseError]
|
||||
constructor(string psbt_base64);
|
||||
|
||||
string serialize();
|
||||
|
||||
[Throws=ExtractTxError]
|
||||
Transaction extract_tx();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
use crate::error::{AddressError, PsbtParseError, TransactionError};
|
||||
|
||||
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::blockdata::transaction::TxOut as BdkTxOut;
|
||||
use bdk::bitcoin::consensus::encode::serialize;
|
||||
use bdk::bitcoin::consensus::Decodable;
|
||||
use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||
use bdk::bitcoin::psbt::ExtractTxError;
|
||||
use bdk::bitcoin::Address as BdkAddress;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::bitcoin::OutPoint as BdkOutPoint;
|
||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::bitcoin::Txid;
|
||||
|
||||
use crate::error::Alpha3Error;
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
@@ -40,14 +43,9 @@ pub struct Address {
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub fn new(address: String, network: Network) -> Result<Self, Alpha3Error> {
|
||||
let parsed_address = address
|
||||
.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()
|
||||
.map_err(|_| Alpha3Error::Generic)?;
|
||||
|
||||
let network_checked_address = parsed_address
|
||||
.require_network(network)
|
||||
.map_err(|_| Alpha3Error::Generic)?;
|
||||
pub fn new(address: String, network: Network) -> Result<Self, AddressError> {
|
||||
let parsed_address = address.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()?;
|
||||
let network_checked_address = parsed_address.require_network(network)?;
|
||||
|
||||
Ok(Address {
|
||||
inner: network_checked_address,
|
||||
@@ -77,7 +75,7 @@ impl Address {
|
||||
// }
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
self.inner.network
|
||||
*self.inner.network()
|
||||
}
|
||||
|
||||
pub fn script_pubkey(&self) -> Arc<Script> {
|
||||
@@ -120,10 +118,9 @@ pub struct Transaction {
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, Alpha3Error> {
|
||||
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, TransactionError> {
|
||||
let mut decoder = Cursor::new(transaction_bytes);
|
||||
let tx: BdkTransaction =
|
||||
BdkTransaction::consensus_decode(&mut decoder).map_err(|_| Alpha3Error::Generic)?;
|
||||
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
|
||||
Ok(Transaction { inner: tx })
|
||||
}
|
||||
|
||||
@@ -135,20 +132,16 @@ impl Transaction {
|
||||
// self.inner.weight() as u64
|
||||
// }
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.inner.size() as u64
|
||||
pub fn total_size(&self) -> u64 {
|
||||
self.inner.total_size() as u64
|
||||
}
|
||||
|
||||
pub fn vsize(&self) -> u64 {
|
||||
self.inner.vsize() as u64
|
||||
}
|
||||
|
||||
// fn serialize(&self) -> Vec<u8> {
|
||||
// self.inner.serialize()
|
||||
// }
|
||||
|
||||
pub fn is_coin_base(&self) -> bool {
|
||||
self.inner.is_coin_base()
|
||||
pub fn is_coinbase(&self) -> bool {
|
||||
self.inner.is_coinbase()
|
||||
}
|
||||
|
||||
pub fn is_explicitly_rbf(&self) -> bool {
|
||||
@@ -160,7 +153,11 @@ impl Transaction {
|
||||
}
|
||||
|
||||
pub fn version(&self) -> i32 {
|
||||
self.inner.version
|
||||
self.inner.version.0
|
||||
}
|
||||
|
||||
pub fn serialize(&self) -> Vec<u8> {
|
||||
serialize(&self.inner)
|
||||
}
|
||||
|
||||
// fn lock_time(&self) -> u32 {
|
||||
@@ -194,17 +191,14 @@ impl From<&Transaction> for BdkTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PartiallySignedTransaction {
|
||||
pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
|
||||
pub struct Psbt {
|
||||
pub(crate) inner: Mutex<BdkPsbt>,
|
||||
}
|
||||
|
||||
impl PartiallySignedTransaction {
|
||||
pub(crate) fn new(psbt_base64: String) -> Result<Self, Alpha3Error> {
|
||||
let psbt: BdkPartiallySignedTransaction =
|
||||
BdkPartiallySignedTransaction::from_str(&psbt_base64)
|
||||
.map_err(|_| Alpha3Error::Generic)?;
|
||||
|
||||
Ok(PartiallySignedTransaction {
|
||||
impl Psbt {
|
||||
pub(crate) fn new(psbt_base64: String) -> Result<Self, PsbtParseError> {
|
||||
let psbt: BdkPsbt = BdkPsbt::from_str(&psbt_base64)?;
|
||||
Ok(Psbt {
|
||||
inner: Mutex::new(psbt),
|
||||
})
|
||||
}
|
||||
@@ -220,9 +214,10 @@ impl PartiallySignedTransaction {
|
||||
// txid.to_hex()
|
||||
// }
|
||||
|
||||
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
|
||||
let tx = self.inner.lock().unwrap().clone().extract_tx();
|
||||
Arc::new(tx.into())
|
||||
pub(crate) fn extract_tx(&self) -> Result<Arc<Transaction>, ExtractTxError> {
|
||||
let tx: BdkTransaction = self.inner.lock().unwrap().clone().extract_tx()?;
|
||||
let transaction: Transaction = tx.into();
|
||||
Ok(Arc::new(transaction))
|
||||
}
|
||||
|
||||
// /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
|
||||
@@ -262,9 +257,9 @@ impl PartiallySignedTransaction {
|
||||
// }
|
||||
}
|
||||
|
||||
impl From<BdkPartiallySignedTransaction> for PartiallySignedTransaction {
|
||||
fn from(psbt: BdkPartiallySignedTransaction) -> Self {
|
||||
PartiallySignedTransaction {
|
||||
impl From<BdkPsbt> for Psbt {
|
||||
fn from(psbt: BdkPsbt) -> Self {
|
||||
Psbt {
|
||||
inner: Mutex::new(psbt),
|
||||
}
|
||||
}
|
||||
@@ -303,7 +298,7 @@ pub struct TxOut {
|
||||
impl From<&BdkTxOut> for TxOut {
|
||||
fn from(tx_out: &BdkTxOut) -> Self {
|
||||
TxOut {
|
||||
value: tx_out.value,
|
||||
value: tx_out.value.to_sat(),
|
||||
script_pubkey: Arc::new(Script(tx_out.script_pubkey.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::error::Alpha3Error;
|
||||
use crate::error::DescriptorError;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
|
||||
@@ -23,7 +23,7 @@ pub struct Descriptor {
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, Alpha3Error> {
|
||||
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, DescriptorError> {
|
||||
let secp = Secp256k1::new();
|
||||
let (extended_descriptor, key_map) = descriptor.into_wallet_descriptor(&secp, network)?;
|
||||
Ok(Self {
|
||||
@@ -276,8 +276,6 @@ mod test {
|
||||
use crate::*;
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
use crate::Alpha3Error;
|
||||
|
||||
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();
|
||||
DescriptorSecretKey::new(Network::Testnet, &mnemonic, None)
|
||||
@@ -392,8 +390,8 @@ mod test {
|
||||
fn test_descriptor_from_string() {
|
||||
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet);
|
||||
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 and InvalidNetwork Error
|
||||
// Creating a Descriptor using an extended key that doesn't match the network provided will throw a DescriptorError::Key with inner InvalidNetwork error
|
||||
assert!(descriptor1.is_ok());
|
||||
assert_matches!(descriptor2.unwrap_err(), Alpha3Error::Generic)
|
||||
assert_matches!(descriptor2.unwrap_err(), DescriptorError::Key { .. });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
use crate::bitcoin::OutPoint;
|
||||
|
||||
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
|
||||
use bdk_esplora::esplora_client::Error as BdkEsploraError;
|
||||
|
||||
use bdk::bitcoin::psbt::PsbtParseError as BdkPsbtParseError;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::descriptor::DescriptorError;
|
||||
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
|
||||
use bdk::descriptor::DescriptorError as BdkDescriptorError;
|
||||
use bdk::wallet::error::{BuildFeeBumpError, CreateTxError};
|
||||
use bdk::wallet::tx_builder::{AddUtxoError, AllowShrinkingError};
|
||||
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::IterError;
|
||||
use bitcoin_internals::hex::display::DisplayHex;
|
||||
|
||||
use bdk::bitcoin::address::ParseError;
|
||||
use std::convert::Infallible;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -59,31 +62,34 @@ pub enum WalletCreationError {
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum PersistenceError {
|
||||
#[error("writing to persistence error: {e}")]
|
||||
Write { e: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EsploraError {
|
||||
#[error("ureq error: {error_message}")]
|
||||
Ureq { error_message: String },
|
||||
#[error("minreq error: {error_message}")]
|
||||
Minreq { error_message: String },
|
||||
|
||||
#[error("ureq transport error: {error_message}")]
|
||||
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("http error with status code {status} and message {error_message}")]
|
||||
HttpResponse { status: u16, error_message: String },
|
||||
|
||||
#[error("parsing error: {error_message}")]
|
||||
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}")]
|
||||
BitcoinEncoding { error_message: String },
|
||||
|
||||
#[error("hex decoding error: {error_message}")]
|
||||
Hex { error_message: String },
|
||||
#[error("invalid hex data returned: {error_message}")]
|
||||
HexToArray { error_message: String },
|
||||
|
||||
#[error("invalid hex data returned: {error_message}")]
|
||||
HexToBytes { error_message: String },
|
||||
|
||||
#[error("transaction not found")]
|
||||
TransactionNotFound,
|
||||
@@ -93,6 +99,194 @@ pub enum EsploraError {
|
||||
|
||||
#[error("header hash not found")]
|
||||
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 {
|
||||
@@ -123,9 +317,11 @@ impl From<NewOrLoadError<std::io::Error, IterError>> for WalletCreationError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DescriptorError> for Alpha3Error {
|
||||
fn from(_: DescriptorError) -> Self {
|
||||
Alpha3Error::Generic
|
||||
impl From<std::io::Error> for PersistenceError {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
PersistenceError::Write {
|
||||
e: error.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,24 +381,26 @@ impl From<BdkCalculateFeeError> for CalculateFeeError {
|
||||
impl From<BdkEsploraError> for EsploraError {
|
||||
fn from(error: BdkEsploraError) -> Self {
|
||||
match error {
|
||||
BdkEsploraError::Ureq(e) => EsploraError::Ureq {
|
||||
BdkEsploraError::Minreq(e) => EsploraError::Minreq {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkEsploraError::UreqTransport(e) => EsploraError::UreqTransport {
|
||||
error_message: e.to_string(),
|
||||
BdkEsploraError::HttpResponse { status, message } => EsploraError::HttpResponse {
|
||||
status,
|
||||
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 {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
Error::StatusCode(e) => EsploraError::StatusCode {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkEsploraError::BitcoinEncoding(e) => EsploraError::BitcoinEncoding {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkEsploraError::Hex(e) => EsploraError::Hex {
|
||||
BdkEsploraError::HexToArray(e) => EsploraError::HexToArray {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkEsploraError::HexToBytes(e) => EsploraError::HexToBytes {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkEsploraError::TransactionNotFound(_) => EsploraError::TransactionNotFound,
|
||||
@@ -210,15 +408,108 @@ impl From<BdkEsploraError> for EsploraError {
|
||||
EsploraError::HeaderHeightNotFound { height }
|
||||
}
|
||||
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)]
|
||||
mod test {
|
||||
use crate::error::EsploraError;
|
||||
use crate::error::{EsploraError, PersistenceError, WalletCreationError};
|
||||
use crate::CalculateFeeError;
|
||||
use crate::OutPoint;
|
||||
use bdk::bitcoin::Network;
|
||||
|
||||
#[test]
|
||||
fn test_error_missing_tx_out() {
|
||||
@@ -265,28 +556,24 @@ mod test {
|
||||
fn test_esplora_errors() {
|
||||
let cases = vec![
|
||||
(
|
||||
EsploraError::Ureq {
|
||||
EsploraError::Minreq {
|
||||
error_message: "Network error".to_string(),
|
||||
},
|
||||
"ureq error: Network error",
|
||||
"minreq error: Network error",
|
||||
),
|
||||
(
|
||||
EsploraError::UreqTransport {
|
||||
error_message: "Timeout occurred".to_string(),
|
||||
EsploraError::HttpResponse {
|
||||
status: 404,
|
||||
error_message: "Not found".to_string(),
|
||||
},
|
||||
"ureq transport error: Timeout occurred",
|
||||
"http error with status code 404 and message Not found",
|
||||
),
|
||||
(
|
||||
EsploraError::Http { status_code: 404 },
|
||||
"http error with status code: 404",
|
||||
),
|
||||
(
|
||||
EsploraError::Io {
|
||||
error_message: "File not found".to_string(),
|
||||
EsploraError::StatusCode {
|
||||
error_message: "code 1234567".to_string(),
|
||||
},
|
||||
"io error: File not found",
|
||||
"Invalid status code, unable to convert to u16: code 1234567",
|
||||
),
|
||||
(EsploraError::NoHeader, "no header found in the response"),
|
||||
(
|
||||
EsploraError::Parsing {
|
||||
error_message: "Invalid JSON".to_string(),
|
||||
@@ -300,10 +587,16 @@ mod test {
|
||||
"bitcoin encoding error: Bad format",
|
||||
),
|
||||
(
|
||||
EsploraError::Hex {
|
||||
EsploraError::HexToArray {
|
||||
error_message: "Invalid hex".to_string(),
|
||||
},
|
||||
"hex decoding error: Invalid hex",
|
||||
"invalid hex data returned: Invalid hex",
|
||||
),
|
||||
(
|
||||
EsploraError::HexToBytes {
|
||||
error_message: "Invalid hex".to_string(),
|
||||
},
|
||||
"invalid hex data returned: Invalid hex",
|
||||
),
|
||||
(EsploraError::TransactionNotFound, "transaction not found"),
|
||||
(
|
||||
@@ -317,4 +610,66 @@ mod test {
|
||||
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)"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
use crate::error::{Alpha3Error, EsploraError};
|
||||
use crate::error::EsploraError;
|
||||
use crate::wallet::{Update, Wallet};
|
||||
|
||||
use crate::bitcoin::Transaction;
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::wallet::Update as BdkUpdate;
|
||||
use bdk_esplora::esplora_client::{BlockingClient, Builder};
|
||||
use bdk_esplora::EsploraExt;
|
||||
|
||||
use crate::bitcoin::Transaction;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct EsploraClient(BlockingClient);
|
||||
|
||||
impl EsploraClient {
|
||||
pub fn new(url: String) -> Self {
|
||||
let client = Builder::new(url.as_str()).build_blocking().unwrap();
|
||||
let client = Builder::new(url.as_str()).build_blocking();
|
||||
Self(client)
|
||||
}
|
||||
|
||||
@@ -52,11 +52,11 @@ impl EsploraClient {
|
||||
|
||||
// pub fn sync();
|
||||
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), Alpha3Error> {
|
||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), EsploraError> {
|
||||
let bdk_transaction: BdkTransaction = transaction.into();
|
||||
self.0
|
||||
.broadcast(&bdk_transaction)
|
||||
.map_err(|_| Alpha3Error::Generic)
|
||||
.map_err(EsploraError::from)
|
||||
}
|
||||
|
||||
// pub fn estimate_fee();
|
||||
|
||||
@@ -8,14 +8,23 @@ mod wallet;
|
||||
|
||||
use crate::bitcoin::Address;
|
||||
use crate::bitcoin::OutPoint;
|
||||
use crate::bitcoin::PartiallySignedTransaction;
|
||||
use crate::bitcoin::Psbt;
|
||||
use crate::bitcoin::Script;
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::bitcoin::TxOut;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::error::AddressError;
|
||||
use crate::error::Alpha3Error;
|
||||
use crate::error::CalculateFeeError;
|
||||
use crate::error::DescriptorError;
|
||||
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::keys::DerivationPath;
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
@@ -24,6 +33,8 @@ use crate::keys::Mnemonic;
|
||||
use crate::types::AddressIndex;
|
||||
use crate::types::AddressInfo;
|
||||
use crate::types::Balance;
|
||||
use crate::types::CanonicalTx;
|
||||
use crate::types::ChainPosition;
|
||||
use crate::types::FeeRate;
|
||||
use crate::types::LocalOutput;
|
||||
use crate::types::ScriptAmount;
|
||||
@@ -33,7 +44,6 @@ use crate::wallet::TxBuilder;
|
||||
use crate::wallet::Update;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
use crate::error::WalletCreationError;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::keys::bip39::WordCount;
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
|
||||
@@ -1,34 +1,76 @@
|
||||
use crate::bitcoin::{Address, OutPoint, Script, TxOut};
|
||||
use crate::error::FeeRateError;
|
||||
|
||||
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::AddressInfo as BdkAddressInfo;
|
||||
use bdk::wallet::Balance as BdkBalance;
|
||||
use bdk::KeychainKind;
|
||||
|
||||
use bdk::LocalOutput as BdkLocalOutput;
|
||||
|
||||
use bdk::FeeRate as BdkFeeRate;
|
||||
|
||||
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)]
|
||||
pub struct FeeRate(pub BdkFeeRate);
|
||||
|
||||
impl FeeRate {
|
||||
pub fn from_sat_per_vb(sat_per_vb: f32) -> Self {
|
||||
FeeRate(BdkFeeRate::from_sat_per_vb(sat_per_vb))
|
||||
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
|
||||
let fee_rate: Option<BdkFeeRate> = 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: f32) -> Self {
|
||||
pub fn from_sat_per_kwu(sat_per_kwu: u64) -> Self {
|
||||
FeeRate(BdkFeeRate::from_sat_per_kwu(sat_per_kwu))
|
||||
}
|
||||
|
||||
pub fn as_sat_per_vb(&self) -> f32 {
|
||||
self.0.as_sat_per_vb()
|
||||
pub fn to_sat_per_vb_ceil(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_ceil()
|
||||
}
|
||||
|
||||
pub fn sat_per_kwu(&self) -> f32 {
|
||||
self.0.sat_per_kwu()
|
||||
pub fn to_sat_per_vb_floor(&self) -> u64 {
|
||||
self.0.to_sat_per_vb_floor()
|
||||
}
|
||||
|
||||
pub fn to_sat_per_kwu(&self) -> u64 {
|
||||
self.0.to_sat_per_kwu()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +179,7 @@ impl From<BdkLocalOutput> for LocalOutput {
|
||||
vout: local_utxo.outpoint.vout,
|
||||
},
|
||||
txout: TxOut {
|
||||
value: local_utxo.txout.value,
|
||||
value: local_utxo.txout.value.to_sat(),
|
||||
script_pubkey: Arc::new(Script(local_utxo.txout.script_pubkey)),
|
||||
},
|
||||
keychain: local_utxo.keychain,
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
use crate::bitcoin::{OutPoint, PartiallySignedTransaction, Transaction};
|
||||
use crate::bitcoin::{OutPoint, Psbt, Script, Transaction};
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::error::{Alpha3Error, CalculateFeeError, WalletCreationError};
|
||||
use crate::types::ScriptAmount;
|
||||
use crate::types::{Balance, FeeRate};
|
||||
use crate::Script;
|
||||
use crate::{AddressIndex, AddressInfo};
|
||||
use crate::error::{
|
||||
Alpha3Error, CalculateFeeError, PersistenceError, TxidParseError, WalletCreationError,
|
||||
};
|
||||
use crate::types::{
|
||||
AddressIndex, AddressInfo, Balance, CanonicalTx, FeeRate, LocalOutput, ScriptAmount,
|
||||
};
|
||||
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||
use bdk::bitcoin::Network;
|
||||
use bdk::bitcoin::Psbt as BdkPsbt;
|
||||
use bdk::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use bdk::wallet::{ChangeSet, Update as BdkUpdate};
|
||||
use bdk::SignOptions;
|
||||
use bdk::Wallet as BdkWallet;
|
||||
use bdk::{FeeRate as BdkFeeRate, SignOptions};
|
||||
use bdk_file_store::Store;
|
||||
|
||||
use std::collections::HashSet;
|
||||
@@ -67,13 +68,12 @@ impl Wallet {
|
||||
pub fn try_get_internal_address(
|
||||
&self,
|
||||
address_index: AddressIndex,
|
||||
) -> Result<AddressInfo, Alpha3Error> {
|
||||
self.get_wallet()
|
||||
.try_get_internal_address(address_index.into())
|
||||
.map_or_else(
|
||||
|_| Err(Alpha3Error::Generic),
|
||||
|address_info| Ok(address_info.into()),
|
||||
)
|
||||
) -> Result<AddressInfo, PersistenceError> {
|
||||
let address_info = self
|
||||
.get_wallet()
|
||||
.try_get_internal_address(address_index.into())?
|
||||
.into();
|
||||
Ok(address_info)
|
||||
}
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
@@ -91,7 +91,7 @@ impl Wallet {
|
||||
|
||||
pub(crate) fn sign(
|
||||
&self,
|
||||
psbt: Arc<PartiallySignedTransaction>,
|
||||
psbt: Arc<Psbt>,
|
||||
// sign_options: Option<SignOptions>,
|
||||
) -> Result<bool, Alpha3Error> {
|
||||
let mut psbt = psbt.inner.lock().unwrap();
|
||||
@@ -105,13 +105,19 @@ impl Wallet {
|
||||
SentAndReceivedValues { sent, received }
|
||||
}
|
||||
|
||||
pub fn transactions(&self) -> Vec<Arc<Transaction>> {
|
||||
pub fn transactions(&self) -> Vec<CanonicalTx> {
|
||||
self.get_wallet()
|
||||
.transactions()
|
||||
.map(|tx| Arc::new(tx.tx_node.tx.into()))
|
||||
.map(|tx| tx.into())
|
||||
.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> {
|
||||
self.get_wallet()
|
||||
.calculate_fee(&tx.into())
|
||||
@@ -124,6 +130,14 @@ impl Wallet {
|
||||
.map(|bdk_fee_rate| Arc::new(FeeRate(bdk_fee_rate)))
|
||||
.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 {
|
||||
@@ -474,10 +488,7 @@ impl TxBuilder {
|
||||
// })
|
||||
// }
|
||||
|
||||
pub(crate) fn finish(
|
||||
&self,
|
||||
wallet: &Arc<Wallet>,
|
||||
) -> Result<Arc<PartiallySignedTransaction>, Alpha3Error> {
|
||||
pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, Alpha3Error> {
|
||||
// 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 tx_builder = wallet.build_tx();
|
||||
@@ -533,13 +544,13 @@ impl TxBuilder {
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct BumpFeeTxBuilder {
|
||||
pub(crate) txid: String,
|
||||
pub(crate) fee_rate: f32,
|
||||
pub(crate) fee_rate: Arc<FeeRate>,
|
||||
pub(crate) allow_shrinking: Option<Arc<Script>>,
|
||||
pub(crate) rbf: Option<RbfValue>,
|
||||
}
|
||||
|
||||
impl BumpFeeTxBuilder {
|
||||
pub(crate) fn new(txid: String, fee_rate: f32) -> Self {
|
||||
pub(crate) fn new(txid: String, fee_rate: Arc<FeeRate>) -> Self {
|
||||
Self {
|
||||
txid,
|
||||
fee_rate,
|
||||
@@ -569,14 +580,11 @@ impl BumpFeeTxBuilder {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn finish(
|
||||
&self,
|
||||
wallet: &Wallet,
|
||||
) -> Result<Arc<PartiallySignedTransaction>, Alpha3Error> {
|
||||
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<Arc<Psbt>, Alpha3Error> {
|
||||
let txid = Txid::from_str(self.txid.as_str()).map_err(|_| Alpha3Error::Generic)?;
|
||||
let mut wallet = wallet.get_wallet();
|
||||
let mut tx_builder = wallet.build_fee_bump(txid)?;
|
||||
tx_builder.fee_rate(BdkFeeRate::from_sat_per_vb(self.fee_rate));
|
||||
tx_builder.fee_rate(self.fee_rate.0);
|
||||
if let Some(allow_shrinking) = &self.allow_shrinking {
|
||||
tx_builder.allow_shrinking(allow_shrinking.0.clone())?;
|
||||
}
|
||||
@@ -590,8 +598,7 @@ impl BumpFeeTxBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
let psbt: BdkPartiallySignedTransaction =
|
||||
tx_builder.finish().map_err(|_| Alpha3Error::Generic)?;
|
||||
let psbt: BdkPsbt = tx_builder.finish().map_err(|_| Alpha3Error::Generic)?;
|
||||
|
||||
Ok(Arc::new(psbt.into()))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# bdk-jvm
|
||||
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.
|
||||
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.
|
||||
|
||||
## How to Use
|
||||
To use the Kotlin language bindings for [`bdk`] in your JVM project add the following to your gradle dependencies:
|
||||
@@ -13,33 +13,6 @@ 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
|
||||
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
|
||||
```kotlin
|
||||
@@ -56,17 +29,17 @@ dependencies {
|
||||
* [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine)
|
||||
|
||||
## How to build
|
||||
_Note that Kotlin version `1.6.10` or later is required to build the library._
|
||||
1. Install JDK 11. It must be version 11 (not 17), otherwise it won't build. For example, with SDKMAN!:
|
||||
_Note that Kotlin version `1.9.23` or later is required to build the library._
|
||||
1. Install JDK 17. For example, with SDKMAN!:
|
||||
```shell
|
||||
curl -s "https://get.sdkman.io" | bash
|
||||
source "$HOME/.sdkman/bin/sdkman-init.sh"
|
||||
sdk install java 11.0.19-tem
|
||||
sdk install java 17.0.2-tem
|
||||
```
|
||||
2. Install Rust (note that we are currently building using Rust 1.73.0):
|
||||
2. Install Rust (note that we are currently building using Rust 1.77.1):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
```
|
||||
3. Clone this repository.
|
||||
```shell
|
||||
@@ -84,7 +57,7 @@ rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
||||
## How to publish to your local Maven repo
|
||||
```shell
|
||||
cd bdk-jvm
|
||||
./gradlew publishToMavenLocal --exclude-task signMavenPublication
|
||||
./gradlew publishToMavenLocal -P localBuild
|
||||
```
|
||||
|
||||
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:
|
||||
@@ -93,7 +66,7 @@ signing.gnupg.keyName=<YOUR_GNUPG_ID>
|
||||
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
|
||||
```
|
||||
|
||||
and use the `publishToMavenLocal` task without excluding the signing task:
|
||||
and use the `publishToMavenLocal` task without the `localBuild` flag:
|
||||
```shell
|
||||
./gradlew publishToMavenLocal
|
||||
```
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
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"
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=1.0.0-alpha.7
|
||||
libraryVersion=1.0.0-alpha.9
|
||||
|
||||
BIN
bdk-jvm/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
bdk-jvm/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
31
bdk-jvm/gradlew
vendored
31
bdk-jvm/gradlew
vendored
@@ -55,7 +55,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -80,13 +80,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# 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"'
|
||||
# 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
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,22 +131,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
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
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
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 ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | 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" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,6 +198,10 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
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;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
@@ -205,6 +214,12 @@ set -- \
|
||||
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.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
15
bdk-jvm/gradlew.bat
vendored
15
bdk-jvm/gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@@ -25,7 +25,8 @@
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
20
bdk-jvm/justfile
Normal file
20
bdk-jvm/justfile
Normal file
@@ -0,0 +1,20 @@
|
||||
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/
|
||||
@@ -6,19 +6,15 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
val libraryVersion: String by project
|
||||
|
||||
plugins {
|
||||
id("org.jetbrains.kotlin.jvm") version "1.6.10"
|
||||
id("java-library")
|
||||
id("maven-publish")
|
||||
id("signing")
|
||||
id("org.jetbrains.kotlin.jvm")
|
||||
id("org.gradle.java-library")
|
||||
id("org.gradle.maven-publish")
|
||||
id("org.gradle.signing")
|
||||
|
||||
// Custom plugin to generate the native libs and bindings file
|
||||
id("org.bitcoindevkit.plugins.generate-jvm-bindings")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
@@ -41,7 +37,7 @@ tasks.test {
|
||||
testing {
|
||||
suites {
|
||||
val test by getting(JvmTestSuite::class) {
|
||||
useKotlinTest("1.6.10")
|
||||
useKotlinTest("1.9.23")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,6 +107,10 @@ afterEvaluate {
|
||||
}
|
||||
|
||||
signing {
|
||||
if (project.hasProperty("localBuild")) {
|
||||
isRequired = false
|
||||
}
|
||||
|
||||
val signingKeyId: String? by project
|
||||
val signingKey: String? by project
|
||||
val signingPassword: String? by project
|
||||
|
||||
@@ -31,9 +31,9 @@ class LiveTxBuilderTest {
|
||||
assert(wallet.getBalance().total > 0uL)
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(2.0f))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
@@ -60,9 +60,9 @@ class LiveTxBuilderTest {
|
||||
ScriptAmount(recipient2.scriptPubkey(), 4200uL),
|
||||
)
|
||||
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.setRecipients(allRecipients)
|
||||
.feeRate(FeeRate.fromSatPerVb(4.0f))
|
||||
.feeRate(FeeRate.fromSatPerVb(4uL))
|
||||
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
|
||||
.enableRbf()
|
||||
.finish(wallet)
|
||||
|
||||
@@ -35,8 +35,8 @@ class LiveWalletTest {
|
||||
println("Transactions count: ${wallet.transactions().count()}")
|
||||
val transactions = wallet.transactions().take(3)
|
||||
for (tx in transactions) {
|
||||
val sentAndReceived = wallet.sentAndReceived(tx)
|
||||
println("Transaction: ${tx.txid()}")
|
||||
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
|
||||
println("Transaction: ${tx.transaction.txid()}")
|
||||
println("Sent ${sentAndReceived.sent}")
|
||||
println("Received ${sentAndReceived.received}")
|
||||
}
|
||||
@@ -59,9 +59,9 @@ class LiveWalletTest {
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
val psbt: Psbt = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(FeeRate.fromSatPerVb(2.0f))
|
||||
.feeRate(FeeRate.fromSatPerVb(2uL))
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
@@ -77,7 +77,7 @@ class LiveWalletTest {
|
||||
println("Tx fee is: ${txFee}")
|
||||
|
||||
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
|
||||
println("Tx fee rate is: ${feeRate.asSatPerVb()} sat/vB")
|
||||
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
}
|
||||
|
||||
@@ -2,3 +2,15 @@ rootProject.name = "bdk-jvm"
|
||||
|
||||
include(":lib")
|
||||
includeBuild("plugins")
|
||||
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
11
bdk-python/justfile
Normal file
11
bdk-python/justfile
Normal file
@@ -0,0 +1,11 @@
|
||||
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/
|
||||
@@ -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
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
cargo build --profile release-smaller
|
||||
|
||||
echo "Copying linux libbdkffi.so..."
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
set -euo pipefail
|
||||
python3 --version
|
||||
pip install --user -r requirements.txt
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
rustup target add aarch64-apple-darwin
|
||||
cargo build --profile release-smaller --target aarch64-apple-darwin
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
set -euo pipefail
|
||||
python3 --version
|
||||
pip install --user -r requirements.txt
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
rustup target add x86_64-apple-darwin
|
||||
cargo build --profile release-smaller --target x86_64-apple-darwin
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
set -euo pipefail
|
||||
python3 --version
|
||||
pip install --user -r requirements.txt
|
||||
pip install -r requirements.txt
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
cd ../bdk-ffi/
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
|
||||
|
||||
echo "Generating native binaries..."
|
||||
rustup default 1.73.0
|
||||
rustup default 1.77.1
|
||||
rustup target add x86_64-pc-windows-msvc
|
||||
cargo build --profile release-smaller --target x86_64-pc-windows-msvc
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import bdkpython as bdk
|
||||
|
||||
setup(
|
||||
name="bdkpython",
|
||||
version="1.0.0-alpha7",
|
||||
version="1.0.0a9",
|
||||
description="The Python language bindings for the Bitcoin Development Kit",
|
||||
long_description=LONG_DESCRIPTION,
|
||||
long_description_content_type="text/markdown",
|
||||
|
||||
@@ -34,7 +34,7 @@ class LiveTxBuilderTest(unittest.TestCase):
|
||||
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.0)).finish(wallet)
|
||||
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
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)
|
||||
psbt: bdk.Psbt = bdk.TxBuilder().set_recipients(all_recipients).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).enable_rbf().finish(wallet)
|
||||
wallet.sign(psbt)
|
||||
|
||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||
|
||||
@@ -32,8 +32,8 @@ class LiveWalletTest(unittest.TestCase):
|
||||
print(f"Transactions count: {len(wallet.transactions())}")
|
||||
transactions = wallet.transactions()[:3]
|
||||
for tx in transactions:
|
||||
sent_and_received = wallet.sent_and_received(tx)
|
||||
print(f"Transaction: {tx.txid()}")
|
||||
sent_and_received = wallet.sent_and_received(tx.transaction)
|
||||
print(f"Transaction: {tx.transaction.txid()}")
|
||||
print(f"Sent {sent_and_received.sent}")
|
||||
print(f"Received {sent_and_received.received}")
|
||||
|
||||
@@ -64,7 +64,7 @@ class LiveWalletTest(unittest.TestCase):
|
||||
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.0)).finish(wallet)
|
||||
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)
|
||||
# print(psbt.serialize())
|
||||
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)
|
||||
print(f"Transaction Fee: {fee}")
|
||||
fee_rate = wallet.calculate_fee_rate(tx)
|
||||
print(f"Transaction Fee Rate: {fee_rate.as_sat_per_vb()} sat/vB")
|
||||
print(f"Transaction Fee Rate: {fee_rate.to_sat_per_vb_ceil()} sat/vB")
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
|
||||
|
||||
@@ -45,9 +45,9 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
XCTAssertGreaterThan(wallet.getBalance().total, UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
|
||||
let psbt: PartiallySignedTransaction = try TxBuilder()
|
||||
let psbt: Psbt = try TxBuilder()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2.0))
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2))
|
||||
.finish(wallet: wallet)
|
||||
|
||||
print(psbt.serialize())
|
||||
@@ -86,9 +86,9 @@ final class LiveTxBuilderTests: XCTestCase {
|
||||
ScriptAmount(script: recipient2.scriptPubkey(), amount: 4200)
|
||||
]
|
||||
|
||||
let psbt: PartiallySignedTransaction = try TxBuilder()
|
||||
let psbt: Psbt = try TxBuilder()
|
||||
.setRecipients(recipients: allRecipients)
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 4.0))
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 4))
|
||||
.changePolicy(changePolicy: ChangeSpendPolicy.changeForbidden)
|
||||
.enableRbf()
|
||||
.finish(wallet: wallet)
|
||||
|
||||
@@ -47,8 +47,8 @@ final class LiveWalletTests: XCTestCase {
|
||||
print("Transactions count: \(wallet.transactions().count)")
|
||||
let transactions = wallet.transactions().prefix(3)
|
||||
for tx in transactions {
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx)
|
||||
print("Transaction: \(tx.txid())")
|
||||
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||
print("Transaction: \(tx.transaction.txid())")
|
||||
print("Sent \(sentAndReceived.sent)")
|
||||
print("Received \(sentAndReceived.received)")
|
||||
}
|
||||
@@ -78,10 +78,10 @@ final class LiveWalletTests: XCTestCase {
|
||||
print("Balance: \(wallet.getBalance().total)")
|
||||
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
|
||||
let psbt: PartiallySignedTransaction = try
|
||||
let psbt: Psbt = try
|
||||
TxBuilder()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2.0))
|
||||
.feeRate(feeRate: FeeRate.fromSatPerVb(satPerVb: 2))
|
||||
.finish(wallet: wallet)
|
||||
|
||||
print(psbt.serialize())
|
||||
@@ -90,12 +90,12 @@ final class LiveWalletTests: XCTestCase {
|
||||
let walletDidSign: Bool = try wallet.sign(psbt: psbt)
|
||||
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")
|
||||
|
||||
let tx: Transaction = psbt.extractTx()
|
||||
let tx: Transaction = try! psbt.extractTx()
|
||||
print(tx.txid())
|
||||
let fee: UInt64 = try wallet.calculateFee(tx: tx)
|
||||
print("Transaction Fee: \(fee)")
|
||||
let feeRate: FeeRate = try wallet.calculateFeeRate(tx: tx)
|
||||
print("Transaction Fee Rate: \(feeRate.asSatPerVb()) sat/vB")
|
||||
print("Transaction Fee Rate: \(feeRate.toSatPerVbCeil()) sat/vB")
|
||||
|
||||
try esploraClient.broadcast(transaction: tx)
|
||||
}
|
||||
|
||||
@@ -3,18 +3,15 @@
|
||||
# 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.
|
||||
|
||||
# Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
|
||||
|
||||
rustup install 1.73.0
|
||||
rustup default 1.77.1
|
||||
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 aarch64-apple-ios-sim # simulator mac M1
|
||||
rustup target add aarch64-apple-darwin # mac M1
|
||||
rustup target add x86_64-apple-darwin # mac x86_64
|
||||
|
||||
pushd bdk-ffi
|
||||
mkdir -p Sources/BitcoinDevKit
|
||||
cd ../bdk-ffi/ || exit
|
||||
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
|
||||
@@ -28,8 +25,7 @@ lipo target/aarch64-apple-ios-sim/release-smaller/libbdkffi.a target/x86_64-appl
|
||||
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
|
||||
|
||||
popd
|
||||
pushd bdk-swift
|
||||
cd ../bdk-swift/ || exit
|
||||
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_x86_64-simulator/bdkFFI.framework/Headers
|
||||
@@ -39,5 +35,3 @@ 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
|
||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
||||
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
||||
#rm bdkFFI.xcframework.zip || true
|
||||
#zip -9 -r bdkFFI.xcframework.zip bdkFFI.xcframework
|
||||
11
bdk-swift/justfile
Normal file
11
bdk-swift/justfile
Normal file
@@ -0,0 +1,11 @@
|
||||
build:
|
||||
bash ./build-local-swift.sh
|
||||
|
||||
test:
|
||||
swift test
|
||||
|
||||
offlinetests:
|
||||
swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
||||
|
||||
clean:
|
||||
rm -rf ../bdk-ffi/target/
|
||||
Reference in New Issue
Block a user