Compare commits
174 Commits
release/0.
...
release/al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77463fa629 | ||
|
|
9df6f6dbc1 | ||
|
|
93d50dd34d | ||
|
|
5ecbf64e60 | ||
|
|
084f0f713a | ||
|
|
1ef1c5cc6e | ||
|
|
0956999283 | ||
|
|
e70dedce61 | ||
|
|
15c1f19c96 | ||
|
|
26352edfbe | ||
|
|
787152e0b4 | ||
|
|
e79ce98295 | ||
|
|
b8778cdaa5 | ||
|
|
f2cd561f25 | ||
|
|
96e7479ce9 | ||
|
|
a9c6aac6b9 | ||
|
|
fc4240ca38 | ||
|
|
fd85d1d754 | ||
|
|
d37b2f37b5 | ||
|
|
13c751cebc | ||
|
|
1521811e9b | ||
|
|
372f79a10f | ||
|
|
00cd55bb46 | ||
|
|
7463fa7720 | ||
|
|
790aee9b3b | ||
|
|
743862fb60 | ||
|
|
b7e38b18be | ||
|
|
ef73aa3490 | ||
|
|
fbec953149 | ||
|
|
0b07b8ed05 | ||
|
|
b59327e5ea | ||
|
|
e6500baae7 | ||
|
|
85f220b953 | ||
|
|
106d31c9c3 | ||
|
|
4ae169f860 | ||
|
|
7717ebb097 | ||
|
|
c3e8469686 | ||
|
|
faf23b7d25 | ||
|
|
1da01b4a0b | ||
|
|
f1ba03bf50 | ||
|
|
bc182c7164 | ||
|
|
5fc189717d | ||
|
|
849bfe79c1 | ||
|
|
dc79b78b2d | ||
|
|
7cc08f1d6f | ||
|
|
031fcb02da | ||
|
|
5f9b5682e5 | ||
|
|
d0a7315c9d | ||
|
|
0bfc56b0e8 | ||
|
|
3dd6c203e8 | ||
|
|
76acbf575b | ||
|
|
039b64de5c | ||
|
|
fe7e4e21c0 | ||
|
|
77f89afc68 | ||
|
|
25033f6bd6 | ||
|
|
3cb2c2c394 | ||
|
|
5092987b26 | ||
|
|
aea25dbf21 | ||
|
|
ed67eba910 | ||
|
|
90606b2455 | ||
|
|
49e8fe461e | ||
|
|
de88184b8c | ||
|
|
3be2c0495f | ||
|
|
2c4c64515f | ||
|
|
17323d3184 | ||
|
|
b382511a9e | ||
|
|
d3895441d3 | ||
|
|
269512a673 | ||
|
|
d27206787a | ||
|
|
c1b1fd6f5d | ||
|
|
7062fbd047 | ||
|
|
0e34a6bacf | ||
|
|
89e85a20cf | ||
|
|
d8718c3f05 | ||
|
|
871a06d1ce | ||
|
|
b820d6a2ba | ||
|
|
79d9fa2909 | ||
|
|
a0e0467d39 | ||
|
|
f2296704e6 | ||
|
|
b8b60dda87 | ||
|
|
a50e19e7e0 | ||
|
|
fab9ae8ae5 | ||
|
|
478b12c489 | ||
|
|
63b85b9100 | ||
|
|
0e6b472793 | ||
|
|
beb75dd552 | ||
|
|
5ee8698e0a | ||
|
|
ac600a1312 | ||
|
|
f26031db80 | ||
|
|
e7e1a6057e | ||
|
|
2b7c104f11 | ||
|
|
6bab5a159d | ||
|
|
b6c8b145bb | ||
|
|
67610abeb6 | ||
|
|
4e5537acd2 | ||
|
|
6be4ddaf7b | ||
|
|
cd10c75e96 | ||
|
|
cbd44249f3 | ||
|
|
20c31d5383 | ||
|
|
616cb21738 | ||
|
|
d3a6453eda | ||
|
|
cba69e681a | ||
|
|
35d8fb3139 | ||
|
|
f003a6275e | ||
|
|
2f62377eec | ||
|
|
81e208222a | ||
|
|
3dc6596aa2 | ||
|
|
2342265c26 | ||
|
|
6c561228c2 | ||
|
|
e86909ab3d | ||
|
|
8e51756a3a | ||
|
|
40263b425e | ||
|
|
9437051668 | ||
|
|
7557e214c8 | ||
|
|
40ca62086c | ||
|
|
e0506deffa | ||
|
|
d3e183a498 | ||
|
|
1e9ecfbe52 | ||
|
|
d48bacd29b | ||
|
|
c1243f9e1c | ||
|
|
9c6069e389 | ||
|
|
488edf8bd2 | ||
|
|
90763d42a2 | ||
|
|
44b2ef1382 | ||
|
|
25617d1f23 | ||
|
|
2fcafe2b80 | ||
|
|
5728b50100 | ||
|
|
d08317775b | ||
|
|
c93f292b0e | ||
|
|
a75c868eb2 | ||
|
|
974ff66caf | ||
|
|
2309b19209 | ||
|
|
3128fad690 | ||
|
|
a1b112cbbb | ||
|
|
553c337241 | ||
|
|
90d12a96c5 | ||
|
|
5ca1d17adb | ||
|
|
f121372c73 | ||
|
|
44a78cc459 | ||
|
|
2dbad2ddd5 | ||
|
|
ec71ef58be | ||
|
|
4ca7919ca9 | ||
|
|
07aa1f8950 | ||
|
|
d42789db9b | ||
|
|
10f893a4b3 | ||
|
|
13043065b6 | ||
|
|
5fa0b916a2 | ||
|
|
7611a65620 | ||
|
|
458a162c2a | ||
|
|
d15783dba0 | ||
|
|
2723577a84 | ||
|
|
7fefb8a7b9 | ||
|
|
aa842c3844 | ||
|
|
df019c11ec | ||
|
|
11d7d6b80f | ||
|
|
85f8a8a526 | ||
|
|
5e75c856c5 | ||
|
|
b854c78dde | ||
|
|
f5d4750ae4 | ||
|
|
f75ead02ff | ||
|
|
8e54ada436 | ||
|
|
302ad8dea8 | ||
|
|
561e93f6dd | ||
|
|
b8230799cf | ||
|
|
d2bec60046 | ||
|
|
aadc622006 | ||
|
|
202dcfa2b5 | ||
|
|
46bd9a1f15 | ||
|
|
6fcb8985f1 | ||
|
|
dd58a9d548 | ||
|
|
d2a4e2adba | ||
|
|
cbc740c407 | ||
|
|
f50ecdb7e7 | ||
|
|
f01e0e30f3 |
17
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
17
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Enhancement request
|
||||
about: Request a new feature or change to an existing feature
|
||||
title: ''
|
||||
labels: 'enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the enhancement**
|
||||
<!-- A clear and concise description of what you would like added or changed. -->
|
||||
|
||||
**Use case**
|
||||
<!-- Tell us how you or others will use this new feature or change to an existing feature. -->
|
||||
|
||||
**Additional context**
|
||||
<!-- Add any other context about the enhancement here. -->
|
||||
166
.github/ISSUE_TEMPLATE/minor_release.md
vendored
166
.github/ISSUE_TEMPLATE/minor_release.md
vendored
@@ -4,98 +4,84 @@ about: Create a new minor release [for release managers only]
|
||||
title: 'Release MAJOR.MINOR+1.0'
|
||||
labels: 'release'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Create a new minor release
|
||||
## Bumping BDK Rust Version
|
||||
1. - [ ] Open a PR with an update to `Cargo.toml` to the new bdk release candidate and ensure all CI workflows run correctly. Fix errors if necessary.
|
||||
2. - [ ] Once the new bdk release is out, update the PR to replace the release candidate with the full release and merge.
|
||||
|
||||
### Summary
|
||||
### Specific Libraries' Workflows
|
||||
#### _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.
|
||||
```shell
|
||||
# start an emulator prior to running the tests
|
||||
cd ./bdk-android/
|
||||
./gradlew buildAndroidLib
|
||||
./gradlew connectedAndroidTest
|
||||
```
|
||||
6. - [ ] Update the readme if necessary
|
||||
#### _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
|
||||
9. - [ ] Build the library and run the tests, and adjust if necessary
|
||||
```shell
|
||||
cd ./bdk-jvm/
|
||||
./gradlew buildJvmLib
|
||||
./gradlew test
|
||||
```
|
||||
10. - [ ] Update the readme if necessary
|
||||
#### _Swift_
|
||||
11. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
./bdk-swift/build-local-swift.sh
|
||||
cd ./bdk-swift/
|
||||
swift test
|
||||
```
|
||||
12. - [ ] Update the readme if necessary
|
||||
#### _Python_
|
||||
13. - [ ] Delete the `.tox`, `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
|
||||
```shell
|
||||
cd ./bdk-python/
|
||||
pip3 install --requirement requirements.txt
|
||||
bash ./scripts/generate-macos-arm64.sh # run the script for your particular platform
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
```
|
||||
15. - [ ] Run the tests and adjust if necessary
|
||||
```shell
|
||||
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
|
||||
python -m unittest --verbose tests/test_bdk.py
|
||||
```
|
||||
16. - [ ] Update the readme and `setup.py` if necessary
|
||||
|
||||
<--release summary to be used in announcements-->
|
||||
|
||||
### Commit
|
||||
|
||||
<--latest commit ID to include in this release-->
|
||||
|
||||
### Changelog
|
||||
|
||||
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
|
||||
|
||||
### Checklist
|
||||
|
||||
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
|
||||
branch **development** version is *MAJOR.MINOR.0*.
|
||||
|
||||
#### On the day of the feature freeze
|
||||
|
||||
Change the `master` branch to the next MINOR+1 version:
|
||||
|
||||
- [ ] Switch to the `master` branch.
|
||||
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR+1`, eg. `bump_dev_0_22`.
|
||||
- [ ] Bump the `bump_dev_MAJOR_MINOR+1` branch to the next development MINOR+1 version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
|
||||
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR+1` branch to `master`.
|
||||
- Title PR "Bump version to MAJOR.MINOR+1.0".
|
||||
|
||||
Create a new release branch:
|
||||
|
||||
- [ ] Double check that your local `master` is up-to-date with the upstream repo.
|
||||
- [ ] Create a new branch called `release/MAJOR.MINOR+1` from `master`.
|
||||
|
||||
Add a release candidate tag, this is optional and only needed for major `bdk-ffi` changes that
|
||||
require a longer testing cycle:
|
||||
|
||||
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.1` version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.1`.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.1".
|
||||
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
|
||||
- The tag name should be `vMAJOR.MINOR+1.0-rc.1`
|
||||
- Use message "Release MAJOR.MINOR+1.0 rc.1".
|
||||
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||
- [ ] Push the `release/MAJOR.MINOR` branch and new tag to the `bitcoindevkit/bdk` repo.
|
||||
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.1` tag.
|
||||
|
||||
If any issues need to be fixed before the *MAJOR.MINOR+1.0* version is released:
|
||||
|
||||
- [ ] Merge fix PRs to the `master` branch.
|
||||
- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR+1` branch.
|
||||
- [ ] Verify fixes in `release/MAJOR.MINOR+1` branch.
|
||||
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.x+1` version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.x+1`.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.x+1".
|
||||
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
|
||||
- The tag name should be `vMAJOR.MINOR+1.0-rc.x+1`, where x is the current release candidate number.
|
||||
- Use tag message "Release MAJOR.MINOR+1.0 rc.x+1".
|
||||
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
|
||||
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.x+1` tag.
|
||||
|
||||
#### On the day of the release
|
||||
|
||||
Tag and publish new release:
|
||||
|
||||
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0` version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
|
||||
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
|
||||
- The tag name should be `vMAJOR.MINOR+1.0`
|
||||
- The first line of the tag message should be "Release MAJOR.MINOR+1.0".
|
||||
- In the body of the tag message put a copy of the **Summary** and **Changelog** for the release.
|
||||
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||
- [ ] Wait for the CI to finish one last time.
|
||||
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
|
||||
- [ ] Create the release on GitHub.
|
||||
- Go to "tags", click on the dots on the right and select "Create Release".
|
||||
- Set the title to `Release MAJOR.MINOR+1.0`.
|
||||
- In the release notes body put the **Summary** and **Changelog**.
|
||||
- Use the "+ Auto-generate release notes" button to add details from included PRs.
|
||||
- Until we reach a `1.0.0` release check the "Pre-release" box.
|
||||
- [ ] After downstream language repos are also updated announce the release, using the **Summary**,
|
||||
on Discord, Twitter and Mastodon.
|
||||
- [ ] Celebrate 🎉
|
||||
|
||||
[Semantic Versioning]: https://semver.org/
|
||||
[crates.io]: https://crates.io/crates/bdk
|
||||
[docs.rs]: https://docs.rs/bdk/latest/bdk
|
||||
["keep a changelog"]: https://keepachangelog.com/en/1.0.0/
|
||||
### 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.
|
||||
```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. - [ ] Build and publish API docs for JVM, Android, and Java on the website
|
||||
```shell
|
||||
./gradlew dokkaHtml # bdk-jvm (Dokka)
|
||||
./gradlew dokkaJavadoc # bdk-jvm (java-style documentation)
|
||||
./gradlew dokkaHtml # bdk-android (Dokka)
|
||||
```
|
||||
33. - [ ] Post in the announcement channel
|
||||
34. - [ ] Tweet about the library
|
||||
|
||||
69
.github/ISSUE_TEMPLATE/patch_release.md
vendored
69
.github/ISSUE_TEMPLATE/patch_release.md
vendored
@@ -1,69 +0,0 @@
|
||||
---
|
||||
name: Patch Release
|
||||
about: Create a new patch release [for release managers only]
|
||||
title: 'Release MAJOR.MINOR.PATCH+1'
|
||||
labels: 'release'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Create a new patch release
|
||||
|
||||
### Summary
|
||||
|
||||
<--release summary to be used in announcements-->
|
||||
|
||||
### Commit
|
||||
|
||||
<--latest commit ID to include in this release-->
|
||||
|
||||
### Changelog
|
||||
|
||||
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
|
||||
|
||||
### Checklist
|
||||
|
||||
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
|
||||
branch **development** version is *MAJOR.MINOR.PATCH*.
|
||||
|
||||
### On the day of the patch release
|
||||
|
||||
Change the `master` branch to the new PATCH+1 version:
|
||||
|
||||
- [ ] Switch to the `master` branch.
|
||||
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR_PATCH+1`, eg. `bump_dev_0_22_1`.
|
||||
- [ ] Bump the `bump_dev_MAJOR_MINOR` branch to the next development PATCH+1 version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR.PATCH+1`.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR.PATCH+1".
|
||||
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR_PATCH+1` branch to `master`.
|
||||
- Title PR "Bump version to MAJOR.MINOR.PATCH+1".
|
||||
|
||||
Cherry-pick, tag and publish new PATCH+1 release:
|
||||
|
||||
- [ ] Merge fix PRs to the `master` branch.
|
||||
- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR` branch to be patched.
|
||||
- [ ] Verify fixes in `release/MAJOR.MINOR` branch.
|
||||
- [ ] Bump the `release/MAJOR.MINOR.PATCH+1` branch to `MAJOR.MINOR.PATCH+1` version.
|
||||
- Change the `Cargo.toml` version value to `MAJOR.MINOR.MINOR.PATCH+1`.
|
||||
- The commit message should be "Bump version to MAJOR.MINOR.PATCH+1".
|
||||
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR` branch.
|
||||
- The tag name should be `vMAJOR.MINOR.PATCH+1`
|
||||
- The first line of the tag message should be "Release MAJOR.MINOR.PATCH+1".
|
||||
- In the body of the tag message put a copy of the **Summary** and **Changelog** for the release.
|
||||
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
|
||||
- [ ] Wait for the CI to finish one last time.
|
||||
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
|
||||
- [ ] Create the release on GitHub.
|
||||
- Go to "tags", click on the dots on the right and select "Create Release".
|
||||
- Set the title to `Release MAJOR.MINOR.PATCH+1`.
|
||||
- In the release notes body put the **Summary** and **Changelog**.
|
||||
- Use the "+ Auto-generate release notes" button to add details from included PRs.
|
||||
- Until we reach a `1.0.0` release check the "Pre-release" box.
|
||||
- [ ] After downstream language repos are also updated announce the release, using the **Summary**,
|
||||
on Discord, Twitter and Mastodon.
|
||||
- [ ] Celebrate 🎉
|
||||
|
||||
[Semantic Versioning]: https://semver.org/
|
||||
[crates.io]: https://crates.io/crates/bdk
|
||||
[docs.rs]: https://docs.rs/bdk/latest/bdk
|
||||
["keep a changelog"]: https://keepachangelog.com/en/1.0.0/
|
||||
16
.github/workflows/audit.yml
vendored
16
.github/workflows/audit.yml
vendored
@@ -10,10 +10,16 @@ on:
|
||||
|
||||
jobs:
|
||||
security_audit:
|
||||
name: Security Audit
|
||||
name: Security audit
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/audit-check@v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Run audit"
|
||||
run: |
|
||||
cargo install cargo-audit
|
||||
cargo-audit audit
|
||||
|
||||
126
.github/workflows/build-python-wheels.yaml
vendored
126
.github/workflows/build-python-wheels.yaml
vendored
@@ -1,126 +0,0 @@
|
||||
name: Build Python wheels
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
|
||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
||||
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
|
||||
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
|
||||
# tries to load glibc and fails because it requires a more recent version.
|
||||
|
||||
jobs:
|
||||
build-manylinux2014-x86_64-wheel:
|
||||
name: 'Build Manylinux 2014 x86_64 wheel'
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
env:
|
||||
PLAT: manylinux2014_x86_64
|
||||
PYBIN: '/opt/python/${{ matrix.python }}/bin'
|
||||
strategy:
|
||||
matrix:
|
||||
python: # Update this list whenever the docker image is updated (check /opt/python/)
|
||||
# - cp36-cp36m
|
||||
# - cp37-cp37m
|
||||
# - cp38-cp38
|
||||
# - cp39-cp39
|
||||
- cp310-cp310
|
||||
# - pp37-pypy37_pp73
|
||||
# - pp38-pypy38_pp73
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: install requirements
|
||||
run: ${PYBIN}/pip install -r requirements.txt
|
||||
- name: generate bindings
|
||||
run: bash generate.sh
|
||||
- name: build wheel
|
||||
run: ${PYBIN}/pip wheel . --no-deps -w /tmp/wheelhouse
|
||||
- name: repair wheel
|
||||
run: auditwheel repair /tmp/wheelhouse/* --plat "$PLAT" -w /tmp/wheelhouse-repaired
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||
path: /tmp/wheelhouse-repaired/*.whl
|
||||
|
||||
build-macos-universal-wheel:
|
||||
name: 'Build macOS universal wheel'
|
||||
runs-on: macos-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
# - '3.7'
|
||||
# - '3.8'
|
||||
# - '3.9'
|
||||
- '3.10'
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- run: python3 --version
|
||||
- run: rustup target add aarch64-apple-darwin
|
||||
- run: pip3 install --user -r requirements.txt
|
||||
- run: pip3 install --user wheel
|
||||
- run: bash generate.sh
|
||||
- name: build wheel
|
||||
env:
|
||||
ARCHFLAGS: "-arch x86_64 -arch arm64"
|
||||
run: python3 setup.py -v bdist_wheel
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bdkpython-macos-${{ matrix.python }}
|
||||
path: dist/*.whl
|
||||
|
||||
build-windows-wheel:
|
||||
name: 'Build windows wheel'
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
# - '3.7'
|
||||
# - '3.8'
|
||||
# - '3.9'
|
||||
- '3.10'
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- run: python --version
|
||||
- run: pip install --user -r requirements.txt
|
||||
- run: bash generate.sh
|
||||
shell: bash
|
||||
- run: pip install --user wheel
|
||||
- name: build wheel
|
||||
run: python setup.py -v bdist_wheel
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bdkpython-win-${{ matrix.python }}
|
||||
path: dist/*.whl
|
||||
76
.github/workflows/cont_integration.yml
vendored
76
.github/workflows/cont_integration.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: bdk-ffi CI
|
||||
name: Rust layer CI
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
@@ -8,59 +8,77 @@ on:
|
||||
- "bdk-ffi/**"
|
||||
|
||||
jobs:
|
||||
|
||||
build-test:
|
||||
name: Build and test
|
||||
runs-on: ubuntu-latest
|
||||
name: "Build and test"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- version: 1.63.0 # STABLE
|
||||
- version: 1.73.0
|
||||
clippy: true
|
||||
- version: 1.61.0 # MSRV
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Generate cache key
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Generate cache key"
|
||||
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
||||
- name: cache
|
||||
uses: actions/cache@v2
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
target
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
- name: Set default toolchain
|
||||
|
||||
- name: "Set default toolchain"
|
||||
run: rustup default ${{ matrix.rust.version }}
|
||||
- name: Set profile
|
||||
|
||||
- name: "Set profile"
|
||||
run: rustup set profile minimal
|
||||
- name: Add clippy
|
||||
|
||||
- name: "Add clippy"
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: rustup component add clippy
|
||||
- name: Update toolchain
|
||||
|
||||
- name: "Update toolchain"
|
||||
run: rustup update
|
||||
- name: Build
|
||||
|
||||
- name: "Build"
|
||||
run: cargo build
|
||||
- name: Clippy
|
||||
|
||||
- name: "Clippy"
|
||||
if: ${{ matrix.rust.clippy }}
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
- name: Test
|
||||
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test
|
||||
run: cargo clippy --all-targets --features "uniffi/bindgen-tests" -- -D warnings
|
||||
|
||||
- name: "Test"
|
||||
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test --features uniffi/bindgen-tests
|
||||
|
||||
fmt:
|
||||
name: Rust fmt
|
||||
runs-on: ubuntu-latest
|
||||
name: "Rust fmt"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-ffi
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set default toolchain
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Set default toolchain"
|
||||
run: rustup default nightly
|
||||
- name: Set profile
|
||||
|
||||
- name: "Set profile"
|
||||
run: rustup set profile minimal
|
||||
- name: Add rustfmt
|
||||
|
||||
- name: "Add rustfmt"
|
||||
run: rustup component add rustfmt
|
||||
- name: Update toolchain
|
||||
|
||||
- name: "Update toolchain"
|
||||
run: rustup update
|
||||
- name: Check fmt
|
||||
|
||||
- name: "Check fmt"
|
||||
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
|
||||
|
||||
93
.github/workflows/live-tests.yaml
vendored
Normal file
93
.github/workflows/live-tests.yaml
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
name: Run All Live Tests
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Once per week
|
||||
|
||||
jobs:
|
||||
jvm-tests:
|
||||
name: "Build and test JVM library on Linux"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: "Run live JVM tests"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew test
|
||||
|
||||
swift-tests:
|
||||
name: "Build and test iOS library on macOS"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Build Swift package"
|
||||
run: bash ./bdk-swift/build-local-swift.sh
|
||||
|
||||
- name: "Run live Swift tests"
|
||||
working-directory: bdk-swift
|
||||
run: swift test
|
||||
|
||||
python-tests:
|
||||
name: "Build and test Python library on Linux"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
env:
|
||||
PLAT: manylinux2014_x86_64
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- cp310-cp310
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: ${PYBIN}/pip install ./dist/*.whl
|
||||
|
||||
- name: "Run live Python tests"
|
||||
run: ${PYBIN}/python -m unittest --verbose
|
||||
33
.github/workflows/publish-android.yaml
vendored
33
.github/workflows/publish-android.yaml
vendored
@@ -1,27 +1,17 @@
|
||||
name: Publish bdk-android to Maven Central
|
||||
on: [workflow_dispatch]
|
||||
|
||||
env:
|
||||
ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
|
||||
# By default, the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT
|
||||
# ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105
|
||||
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Install Android NDK 21.4.7075529
|
||||
run: |
|
||||
ANDROID_ROOT=/usr/local/lib/android
|
||||
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
|
||||
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
|
||||
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check out PR branch
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: cache
|
||||
uses: actions/cache@v2
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
@@ -29,21 +19,24 @@ jobs:
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: Install rust android targets
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
|
||||
- name: Build bdk-android library
|
||||
- name: "Build bdk-android library"
|
||||
run: |
|
||||
cd bdk-android
|
||||
./gradlew buildAndroidLib
|
||||
|
||||
- name: Publish to Maven Local and Maven Central
|
||||
- name: "Publish to Maven Local and Maven Central"
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
||||
|
||||
101
.github/workflows/publish-jvm.yaml
vendored
101
.github/workflows/publish-jvm.yaml
vendored
@@ -2,14 +2,14 @@ name: Publish bdk-jvm to Maven Central
|
||||
on: [workflow_dispatch]
|
||||
|
||||
jobs:
|
||||
build-jvm-macOS-M1-native-lib:
|
||||
name: Create M1 and x86_64 JVM native binaries
|
||||
build-macOS-native-libs:
|
||||
name: "Create M1 and x86_64 native binaries"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Checkout publishing branch
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cache
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -18,42 +18,68 @@ jobs:
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: Install aarch64 Rust target
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
|
||||
- name: "Install aarch64 Rust target"
|
||||
run: rustup target add aarch64-apple-darwin
|
||||
|
||||
- name: Build bdk-jvm library
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
# build aarch64 + x86_64 native libraries and upload
|
||||
- name: Upload macOS native libraries for reuse in publishing job
|
||||
- name: "Upload macOS native libraries for reuse in publishing job"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
# name: no name is required because we upload the entire directory
|
||||
# the default name "artifact" will be used
|
||||
name: artifact-macos
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-jvm/lib/src/main/resources/
|
||||
|
||||
build-jvm-full-library:
|
||||
name: Create full bdk-jvm library
|
||||
needs: [build-jvm-macOS-M1-native-lib]
|
||||
runs-on: ubuntu-22.04
|
||||
build-windows-native-lib:
|
||||
name: "Create Windows native binaries"
|
||||
runs-on: windows-2022
|
||||
steps:
|
||||
- name: Checkout publishing branch
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Update bdk-ffi git submodule
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
|
||||
- name: "Install x86_64-pc-windows-msvc Rust target"
|
||||
run: rustup target add x86_64-pc-windows-msvc
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git
|
||||
git submodule update --init bdk-ffi
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: Cache
|
||||
- name: "Upload Windows native libraries for reuse in publishing job"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifact-windows
|
||||
path: D:\a\bdk-ffi\bdk-ffi\bdk-jvm\lib\src\main\resources\
|
||||
|
||||
build-full-library:
|
||||
name: Create full bdk-jvm library
|
||||
needs: [build-macOS-native-libs, build-windows-native-lib]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: "Checkout publishing branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
@@ -62,26 +88,39 @@ jobs:
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: Build bdk-jvm library
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
|
||||
- name: "Build bdk-jvm library"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew buildJvmLib
|
||||
|
||||
- name: Download macOS native libraries from previous job
|
||||
- name: "Download macOS native binaries from previous job"
|
||||
uses: actions/download-artifact@v3
|
||||
id: download
|
||||
with:
|
||||
# download the artifact created in the prior job (named "artifact")
|
||||
name: artifact
|
||||
name: artifact-macos
|
||||
path: ./bdk-jvm/lib/src/main/resources/
|
||||
|
||||
- name: Publish to Maven Central
|
||||
- name: "Download Windows native libraries from previous job"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: artifact-windows
|
||||
path: ./bdk-jvm/lib/src/main/resources/
|
||||
|
||||
- name: "Upload library code and binaries"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: artifact-full
|
||||
path: ./bdk-jvm/lib/
|
||||
|
||||
- name: "Publish to Maven Central"
|
||||
env:
|
||||
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
||||
|
||||
184
.github/workflows/publish-python.yaml
vendored
184
.github/workflows/publish-python.yaml
vendored
@@ -1,4 +1,4 @@
|
||||
name: Build and publish Python wheels
|
||||
name: Publish bdkpython to PyPI
|
||||
on: [workflow_dispatch]
|
||||
|
||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
||||
@@ -7,9 +7,9 @@ on: [workflow_dispatch]
|
||||
# tries to load glibc and fails because it requires a more recent version.
|
||||
|
||||
jobs:
|
||||
build-manylinux2014-x86_64-wheel:
|
||||
name: 'Build Manylinux 2014 x86_64 wheel'
|
||||
runs-on: ubuntu-latest
|
||||
build-manylinux2014-x86_64-wheels:
|
||||
name: "Build Manylinux 2014 x86_64 wheel"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
@@ -17,127 +17,159 @@ jobs:
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
env:
|
||||
PLAT: manylinux2014_x86_64
|
||||
PYBIN: '/opt/python/${{ matrix.python }}/bin'
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
python: # Update this list whenever the docker image is updated (check /opt/python/)
|
||||
- cp36-cp36m
|
||||
- cp37-cp37m
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
- pp37-pypy37_pp73
|
||||
- pp38-pypy38_pp73
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
# TODO 2: Other CI workflows use explicit Rust compiler versions, I think we should do the same here
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: install requirements
|
||||
run: ${PYBIN}/pip install -r requirements.txt
|
||||
- name: generate bindings
|
||||
run: bash generate.sh
|
||||
- name: build wheel
|
||||
run: ${PYBIN}/pip wheel . --no-deps -w /tmp/wheelhouse
|
||||
- name: repair wheel
|
||||
run: auditwheel repair /tmp/wheelhouse/* --plat "$PLAT" -w /tmp/wheelhouse-repaired
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||
path: /tmp/wheelhouse-repaired/*.whl
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-universal-wheel:
|
||||
name: 'Build macOS universal wheel'
|
||||
runs-on: macos-latest
|
||||
build-macos-arm64-wheels:
|
||||
name: "Build macOS arm64 wheel"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- '3.7'
|
||||
- '3.8'
|
||||
- '3.9'
|
||||
- '3.10'
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- run: python3 --version
|
||||
- run: rustup target add aarch64-apple-darwin
|
||||
- run: pip3 install --user -r requirements.txt
|
||||
- run: pip3 install --user wheel
|
||||
- run: bash generate.sh
|
||||
- name: build wheel
|
||||
env:
|
||||
ARCHFLAGS: "-arch x86_64 -arch arm64"
|
||||
run: python3 setup.py -v bdist_wheel
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: bdkpython-macos-${{ matrix.python }}
|
||||
path: dist/*.whl
|
||||
|
||||
build-windows-wheel:
|
||||
name: 'Build windows wheel'
|
||||
runs-on: windows-latest
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-arm64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_arm64 --verbose
|
||||
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-arm64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-x86_64-wheels:
|
||||
name: "Build macOS x86_64 wheel"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- '3.7'
|
||||
- '3.8'
|
||||
- '3.9'
|
||||
- '3.10'
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v2
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
- run: python --version
|
||||
- run: pip install --user -r requirements.txt
|
||||
- run: bash generate.sh
|
||||
shell: bash
|
||||
- run: pip install --user wheel
|
||||
- name: build wheel
|
||||
run: python setup.py -v bdist_wheel
|
||||
- uses: actions/upload-artifact@v2
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-x86_64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_x86_64 --verbose
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-x86_64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-windows-wheels:
|
||||
name: "Build Windows wheel"
|
||||
runs-on: windows-2022
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-windows.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
run: python setup.py bdist_wheel --verbose
|
||||
|
||||
- name: "Upload artifacts"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-win-${{ matrix.python }}
|
||||
path: dist/*.whl
|
||||
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
|
||||
|
||||
publish-pypi:
|
||||
name: 'Publish on PyPI'
|
||||
runs-on: ubuntu-latest
|
||||
name: "Publish on PyPI"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
needs: [build-manylinux2014-x86_64-wheel, build-macos-universal-wheel, build-windows-wheel]
|
||||
# needs: [build-macos-universal-wheel]
|
||||
needs: [build-manylinux2014-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: 'Download artifacts in dist/ directory'
|
||||
uses: actions/download-artifact@v2
|
||||
- name: "Download artifacts in dist/ directory"
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: dist/
|
||||
|
||||
# - name: Display structure of downloaded files
|
||||
# run: ls -R
|
||||
|
||||
# - name: 'Publish on test PyPI'
|
||||
# - name: "Publish on test PyPI"
|
||||
# uses: pypa/gh-action-pypi-publish@release/v1
|
||||
# with:
|
||||
# user: __token__
|
||||
@@ -145,7 +177,7 @@ jobs:
|
||||
# repository_url: https://test.pypi.org/legacy/
|
||||
# packages_dir: dist/*/
|
||||
|
||||
- name: 'Publish on PyPI'
|
||||
- name: "Publish on PyPI"
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
user: __token__
|
||||
|
||||
43
.github/workflows/test-android.yaml
vendored
43
.github/workflows/test-android.yaml
vendored
@@ -1,5 +1,6 @@
|
||||
name: Test Android
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
@@ -9,27 +10,20 @@ on:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-android/**"
|
||||
|
||||
env:
|
||||
ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
|
||||
# By default, the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT
|
||||
# ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105
|
||||
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Install Android NDK 21.4.7075529
|
||||
run: |
|
||||
ANDROID_ROOT=/usr/local/lib/android
|
||||
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
|
||||
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
|
||||
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
|
||||
- name: "Show default version of NDK"
|
||||
run: echo $ANDROID_NDK_ROOT
|
||||
|
||||
- name: Check out PR branch
|
||||
uses: actions/checkout@v2
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: cache
|
||||
uses: actions/cache@v2
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
@@ -37,16 +31,27 @@ jobs:
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: Install rust android targets
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
|
||||
- name: "Install Rust Android targets"
|
||||
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
|
||||
- name: Run Android tests
|
||||
- name: "Build Android library"
|
||||
run: |
|
||||
cd bdk-android
|
||||
./gradlew test --console=rich
|
||||
./gradlew buildAndroidLib --console=plain
|
||||
|
||||
# There are currently no unit tests for bdk-android (see the tests in bdk-jvm instead) and the
|
||||
# integration tests require the macOS image which is not working with the older NDK version we
|
||||
# are using, so for now we just make sure that the library builds and omit the connectedTest
|
||||
# - name: "Run Android connected tests"
|
||||
# run: |
|
||||
# cd bdk-android
|
||||
# ./gradlew connectedAndroidTest --console=plain
|
||||
|
||||
22
.github/workflows/test-jvm.yaml
vendored
22
.github/workflows/test-jvm.yaml
vendored
@@ -1,5 +1,6 @@
|
||||
name: Test JVM
|
||||
name: Test Kotlin/JVM
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
@@ -13,11 +14,11 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Check out PR branch
|
||||
uses: actions/checkout@v2
|
||||
- name: "Check out PR branch"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: cache
|
||||
uses: actions/cache@v2
|
||||
- name: "Cache"
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
@@ -25,13 +26,16 @@ jobs:
|
||||
./target
|
||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v2
|
||||
- name: "Set up JDK"
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: 11
|
||||
|
||||
- name: Run JVM tests
|
||||
- name: "Set default Rust version to 1.73.0"
|
||||
run: rustup default 1.73.0
|
||||
|
||||
- name: "Run JVM tests"
|
||||
run: |
|
||||
cd bdk-jvm
|
||||
./gradlew test --console=rich
|
||||
./gradlew test -P excludeConnectedTests
|
||||
|
||||
189
.github/workflows/test-python.yaml
vendored
Normal file
189
.github/workflows/test-python.yaml
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
name: Test Python
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
pull_request:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
- "bdk-python/**"
|
||||
|
||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
||||
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
|
||||
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
|
||||
# tries to load glibc and fails because it requires a more recent version.
|
||||
|
||||
jobs:
|
||||
build-manylinux2014-x86_64-wheels:
|
||||
name: "Build and test Manylinux 2014 x86_64 wheels"
|
||||
runs-on: ubuntu-20.04
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
container:
|
||||
image: quay.io/pypa/manylinux2014_x86_64
|
||||
env:
|
||||
PLAT: manylinux2014_x86_64
|
||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- cp38-cp38
|
||||
- cp39-cp39
|
||||
- cp310-cp310
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-linux.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: ${PYBIN}/pip install ./dist/*.whl
|
||||
|
||||
- name: "Run tests"
|
||||
run: ${PYBIN}/python -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-arm64-wheels:
|
||||
name: "Build and test macOS arm64 wheels"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-arm64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_arm64 --verbose
|
||||
|
||||
# You can't install the arm64 wheel on the CI, so we skip these steps and simply test that the wheel builds
|
||||
# - name: "Install wheel and run tests"
|
||||
# run: |
|
||||
# pip3 install ./dist/*.whl
|
||||
# python3 -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-arm64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-macos-x86_64-wheels:
|
||||
name: "Build and test macOS x86_64 wheels"
|
||||
runs-on: macos-13
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-macos-x86_64.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
||||
# see issue #350 for more information
|
||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_x86_64 --verbose
|
||||
|
||||
- name: "Install wheel"
|
||||
run: pip3 install ./dist/*.whl
|
||||
|
||||
- name: "Run tests"
|
||||
run: python3 -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-macos-x86_64-${{ matrix.python }}
|
||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
||||
|
||||
build-windows-wheels:
|
||||
name: "Build and test Windows wheels"
|
||||
runs-on: windows-2022
|
||||
defaults:
|
||||
run:
|
||||
working-directory: bdk-python
|
||||
strategy:
|
||||
matrix:
|
||||
python:
|
||||
- "3.8"
|
||||
- "3.9"
|
||||
- "3.10"
|
||||
steps:
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: "Install Python"
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python }}
|
||||
|
||||
- name: "Generate bdk.py and binaries"
|
||||
run: bash ./scripts/generate-windows.sh
|
||||
|
||||
- name: "Build wheel"
|
||||
run: python setup.py bdist_wheel --verbose
|
||||
|
||||
- name: "Upload artifact test"
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bdkpython-windows-${{ matrix.python }}
|
||||
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: Get-ChildItem 'D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl' | ForEach-Object {pip install $_.FullName}
|
||||
shell: powershell
|
||||
|
||||
- name: "Run tests"
|
||||
run: python -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
|
||||
41
.github/workflows/test-swift.yaml
vendored
41
.github/workflows/test-swift.yaml
vendored
@@ -1,5 +1,6 @@
|
||||
name: Test Swift
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- "bdk-ffi/**"
|
||||
@@ -11,41 +12,15 @@ on:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: "Build and test"
|
||||
runs-on: macos-12
|
||||
steps:
|
||||
- name: Checkout branch
|
||||
uses: actions/checkout@v2
|
||||
- name: "Checkout"
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust targets
|
||||
run: |
|
||||
rustup install nightly-x86_64-apple-darwin
|
||||
rustup component add rust-src --toolchain nightly-x86_64-apple-darwin
|
||||
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
- name: "Build Swift package"
|
||||
run: bash ./bdk-swift/build-local-swift.sh
|
||||
|
||||
- name: Run bdk-ffi-bindgen
|
||||
working-directory: bdk-ffi
|
||||
run: cargo run --package bdk-ffi-bindgen -- --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit
|
||||
|
||||
- name: Build bdk-ffi for x86_64-apple-darwin
|
||||
run: cargo build --profile release-smaller --target x86_64-apple-darwin
|
||||
|
||||
- name: Build bdk-ffi for aarch64-apple-darwin
|
||||
run: cargo build --profile release-smaller --target aarch64-apple-darwin
|
||||
|
||||
- name: Create lipo-ios-sim and lipo-macos
|
||||
run: |
|
||||
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
|
||||
|
||||
- name: Create bdkFFI.xcframework
|
||||
- name: "Run Swift tests"
|
||||
working-directory: bdk-swift
|
||||
run: |
|
||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
||||
cp ../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
|
||||
|
||||
- name: Run Swift tests
|
||||
working-directory: bdk-swift
|
||||
run: swift test
|
||||
run: swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -18,7 +18,7 @@ bdk.kt
|
||||
|
||||
# Swift related
|
||||
/.build
|
||||
/.swiftpm
|
||||
.swiftpm
|
||||
/Packages
|
||||
/*.xcodeproj
|
||||
xcuserdata/
|
||||
@@ -31,3 +31,6 @@ bdkFFI.h
|
||||
BitcoinDevKit.swift
|
||||
bdk.swift
|
||||
.build
|
||||
|
||||
# Python related
|
||||
__pycache__
|
||||
144
CHANGELOG.md
144
CHANGELOG.md
@@ -1,13 +1,134 @@
|
||||
# Changelog
|
||||
All notable changes to this project prior to release **0.9.0** are documented in this file. Future
|
||||
changelog information can be found in each release's git tag and can be viewed with `git tag -ln100 "v*"`.
|
||||
Changelog info is also documented on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases)
|
||||
page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
|
||||
Changelog information can also be found in each release's git tag (which can be viewed with `git tag -ln100 "v*"`), as well as on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases) page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## [0.30.0]
|
||||
This release has a new API and a few internal optimizations and refactorings.
|
||||
|
||||
- APIs Added
|
||||
- Add BIP-86 descriptor templates [#388]
|
||||
|
||||
[#388]: https://github.com/bitcoindevkit/bdk-ffi/pull/388
|
||||
|
||||
## [0.29.0]
|
||||
This release has a number of new APIs, and adds support for Windows in bdk-jvm.
|
||||
|
||||
Changelog
|
||||
- Add support for Windows in bdk-jvm [#336]
|
||||
- Add support for older version of Linux distros in bdk-jvm [#345]
|
||||
- APIs added
|
||||
- Expose `is_mine()` method on the `Wallet` type [#355]
|
||||
- Expose `to_bytes()` method on the `Script` type [#369]
|
||||
|
||||
[#336]: https://github.com/bitcoindevkit/bdk-ffi/pull/336
|
||||
[#345]: https://github.com/bitcoindevkit/bdk-ffi/pull/345
|
||||
[#355]: https://github.com/bitcoindevkit/bdk-ffi/pull/355
|
||||
[#369]: https://github.com/bitcoindevkit/bdk-ffi/pull/369
|
||||
|
||||
## [v0.28.0]
|
||||
- Update BDK to version 0.28.0 [#341]
|
||||
- Drop support of pypy releases of Python libraries [#351]
|
||||
- Drop support for Python 3.6 and 3.7 [#351]
|
||||
- Drop support for very old Linux versions that do not support the manylinux_2_17_x86_64 platform tag [#351]
|
||||
- APIs changed:
|
||||
- Expose Address payload and network properties. [#325]
|
||||
- Add SignOptions to Wallet.sign() params. [#326]
|
||||
- address field on `AddressInfo` type is now of type `Address` [#333]
|
||||
- new PartiallySignedTransaction.json_serialize() function to get JSON serialized value of all PSBT fields. [#334]
|
||||
- Add from_script constructor to `Address` type [#337]
|
||||
|
||||
[#325]: https://github.com/bitcoindevkit/bdk-ffi/pull/325
|
||||
[#326]: https://github.com/bitcoindevkit/bdk-ffi/pull/326
|
||||
[#333]: https://github.com/bitcoindevkit/bdk-ffi/pull/333
|
||||
[#334]: https://github.com/bitcoindevkit/bdk-ffi/pull/334
|
||||
[#337]: https://github.com/bitcoindevkit/bdk-ffi/pull/337
|
||||
[#341]: https://github.com/bitcoindevkit/bdk-ffi/pull/341
|
||||
[#351]: https://github.com/bitcoindevkit/bdk-ffi/pull/351
|
||||
|
||||
## [v0.27.1]
|
||||
- Update BDK to version 0.27.1 [#312]
|
||||
- APIs changed
|
||||
- `PartiallySignedTransaction.extract_tx()` returns a `Transaction` instead of the transaction bytes. [#296]
|
||||
- `Blockchain.broadcast()` takes a `Transaction` instead of a `PartiallySignedTransaction`. [#296]
|
||||
- APIs added
|
||||
- New `Transaction` structure that can be created from or serialized to consensus encoded bytes. [#296]
|
||||
- Add Wallet.get_internal_address() API [#304]
|
||||
- Add `AddressIndex::Peek(index)` and `AddressIndex::Reset(index)` APIs [#305]
|
||||
|
||||
[#296]: https://github.com/bitcoindevkit/bdk-ffi/pull/296
|
||||
[#304]: https://github.com/bitcoindevkit/bdk-ffi/pull/304
|
||||
[#305]: https://github.com/bitcoindevkit/bdk-ffi/pull/305
|
||||
[#312]: https://github.com/bitcoindevkit/bdk-ffi/pull/312
|
||||
|
||||
## [v0.26.0]
|
||||
- Update BDK to version 0.26.0 [#288]
|
||||
- APIs changed
|
||||
- The descriptor and change_descriptor arguments on the wallet constructor now take a `Descriptor` instead of a `String`. [#260]
|
||||
- TxBuilder.drain_to() argument is now `Script` instead of address `String`. [#279]
|
||||
- APIs added
|
||||
- Added RpcConfig, BlockchainConfig::Rpc, and Auth [#125]
|
||||
- Added Descriptor type in [#260] with the following methods:
|
||||
- Default constructor requires a descriptor in String format and a Network
|
||||
- new_bip44 constructor returns a Descriptor with structure pkh(key/44'/{0,1}'/0'/{0,1}/*)
|
||||
- new_bip44_public constructor returns a Descriptor with structure pkh(key/{0,1}/*)
|
||||
- new_bip49 constructor returns a Descriptor with structure sh(wpkh(key/49'/{0,1}'/0'/{0,1}/*))
|
||||
- new_bip49_public constructor returns a Descriptor with structure sh(wpkh(key/{0,1}/*))
|
||||
- new_bip84 constructor returns a Descriptor with structure wpkh(key/84'/{0,1}'/0'/{0,1}/*)
|
||||
- new_bip84_public constructor returns a Descriptor with structure wpkh(key/{0,1}/*)
|
||||
- as_string returns the public version of the output descriptor
|
||||
- as_string_private returns the private version of the output descriptor if available, otherwise return the public version
|
||||
|
||||
[#125]: https://github.com/bitcoindevkit/bdk-ffi/pull/125
|
||||
[#260]: https://github.com/bitcoindevkit/bdk-ffi/pull/260
|
||||
[#279]: https://github.com/bitcoindevkit/bdk-ffi/pull/279
|
||||
[#288]: https://github.com/bitcoindevkit/bdk-ffi/pull/288
|
||||
|
||||
## [v0.25.0]
|
||||
- Update BDK to version 0.25.0 [#272]
|
||||
- APIs Added:
|
||||
- from_string() constructors now available on DescriptorSecretKey and DescriptorPublicKey [#247]
|
||||
|
||||
[#247]: https://github.com/bitcoindevkit/bdk-ffi/pull/247
|
||||
[#272]: https://github.com/bitcoindevkit/bdk-ffi/pull/272
|
||||
|
||||
## [v0.11.0]
|
||||
- Update BDK to version 0.24.0 [#221]
|
||||
- APIs changed
|
||||
- The constructor on the DescriptorSecretKey type now takes a Mnemonic instead of a String.
|
||||
- APIs added
|
||||
- Added Mnemonic struct [#219] with following methods:
|
||||
- new(word_count: WordCount) generates and returns Mnemonic with random entropy
|
||||
- from_string(mnemonic: String) converts string Mnemonic to Mnemonic type with error
|
||||
- from_entropy(entropy: Vec<u8>) generates and returns Mnemonic with given entropy
|
||||
- as_string() view Mnemonic as string
|
||||
- APIs removed
|
||||
- generate_mnemonic(word_count: WordCount)
|
||||
|
||||
[#219]: https://github.com/bitcoindevkit/bdk-ffi/pull/219
|
||||
[#221]: https://github.com/bitcoindevkit/bdk-ffi/pull/221
|
||||
|
||||
## [v0.10.0]
|
||||
- Update BDK to version 0.23.0 [#204]
|
||||
- Update uniffi-rs to latest version 0.21.0 [#216]
|
||||
- Breaking Changes
|
||||
- Changed `TxBuilder.finish()` to return new `TxBuilderResult` [#209]
|
||||
- `TxBuilder.add_recipient()` now takes a `Script` instead of an `Address` [#192]
|
||||
- `AddressAmount` is now `ScriptAmount` [#192]
|
||||
- APIs Added
|
||||
- Added `TxBuilderResult` with PSBT and TransactionDetails [#209]
|
||||
- `Address` and `Script` structs have been added [#192]
|
||||
- Add `PartiallySignedBitcoinTransaction.extract_tx()` function [#192]
|
||||
- Add `secret_bytes()` method on the `DescriptorSecretKey` [#199]
|
||||
- Add `PartiallySignedBitcoinTransaction.combine()` method [#200]
|
||||
|
||||
[#192]: https://github.com/bitcoindevkit/bdk-ffi/pull/192
|
||||
[#199]: https://github.com/bitcoindevkit/bdk-ffi/pull/199
|
||||
[#200]: https://github.com/bitcoindevkit/bdk-ffi/pull/200
|
||||
[#204]: https://github.com/bitcoindevkit/bdk-ffi/pull/204
|
||||
[#209]: https://github.com/bitcoindevkit/bdk-ffi/pull/209
|
||||
[#216]: https://github.com/bitcoindevkit/bdk-ffi/pull/216
|
||||
|
||||
## [v0.9.0]
|
||||
- Breaking Changes
|
||||
@@ -22,7 +143,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- APIs Added [#154]
|
||||
- `generate_mnemonic()`, returns string mnemonic
|
||||
- `interface DescriptorSecretKey`
|
||||
- `new(Network, string_mnenoinc, password)`, contructs DescriptorSecretKey
|
||||
- `new(Network, string_mnenoinc, password)`, constructs DescriptorSecretKey
|
||||
- `derive(DerivationPath)`, derives and returns child DescriptorSecretKey
|
||||
- `extend(DerivationPath)`, extends and returns DescriptorSecretKey
|
||||
- `as_public()`, returns DescriptorSecretKey as DescriptorPublicKey
|
||||
@@ -102,9 +223,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
|
||||
|
||||
## [v0.2.0]
|
||||
|
||||
[unreleased]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.9.0...HEAD
|
||||
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
|
||||
[v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0
|
||||
[v0.28.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.27.1...v0.28.0
|
||||
[v0.27.1]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.26.0...v0.27.1
|
||||
[v0.26.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.25.0...v0.26.0
|
||||
[v0.25.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.11.0...v0.25.0
|
||||
[v0.11.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.10.0...v0.11.0
|
||||
[v0.10.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.9.0...v0.10.0
|
||||
[v0.9.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.8.0...v0.9.0
|
||||
[v0.8.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.7.0...v0.8.0
|
||||
[v0.7.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.6.0...v0.7.0
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@@ -1,12 +0,0 @@
|
||||
[workspace]
|
||||
members = ["bdk-ffi", "bdk-ffi-bindgen"]
|
||||
default-members = ["bdk-ffi", "bdk-ffi-bindgen"]
|
||||
exclude = ["api-docs", "bdk-android", "bdk-jvm", "bdk-python", "bdk-swift"]
|
||||
|
||||
[profile.release-smaller]
|
||||
inherits = "release"
|
||||
opt-level = 'z' # Optimize for size.
|
||||
lto = true # Enable Link Time Optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||
panic = 'abort' # Abort on panic
|
||||
strip = true # Strip symbols from binary*
|
||||
24
README.md
24
README.md
@@ -7,9 +7,12 @@
|
||||
<a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
|
||||
</p>
|
||||
|
||||
## 🚨 Warning 🚨
|
||||
The `master` branch of this repository is being migrated to the [bdk 1.0 API](https://github.com/bitcoindevkit/bdk) and is incomplete. For production-ready libraries, use the [`0.30.X`](https://github.com/bitcoindevkit/bdk-ffi/tree/release/0.30) releases.
|
||||
|
||||
## Readme
|
||||
The workspace in this repository creates the `libbdkffi` multi-language library for the Rust-based
|
||||
[bdk] library from the [Bitcoin Dev Kit] project. The `bdk-ffi-bindgen` package builds a tool for
|
||||
generating the actual language binding code used to access the `libbdkffi` library.
|
||||
[bdk] library from the [Bitcoin Dev Kit] project.
|
||||
|
||||
Each supported language and the platform(s) it's packaged for has its own directory. The Rust code in this project is in the bdk-ffi directory and is a wrapper around the [bdk] library to expose its APIs in a uniform way using the [mozilla/uniffi-rs] bindings generator for each supported target language.
|
||||
|
||||
@@ -23,12 +26,9 @@ 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] | |
|
||||
|
||||
## Language bindings generator tool
|
||||
Use the `bdk-ffi-bindgen` tool to generate language binding code for the above supported languages.
|
||||
To run `bdk-ffi-bindgen` and see the available options use the command:
|
||||
```shell
|
||||
cargo run -p bdk-ffi-bindgen -- --help
|
||||
```
|
||||
## Minimum Supported Rust Version (MSRV)
|
||||
|
||||
This library should compile with any combination of features with Rust 1.73.0.
|
||||
|
||||
## Contributing
|
||||
|
||||
@@ -57,8 +57,8 @@ See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
@@ -68,8 +68,8 @@ dependencies {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version>")
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# API documentation
|
||||
The Bitcoin Dev Kit language bindings make use of the [uniffi-rs](https://github.com/mozilla/uniffi-rs) library to produce their bindings. While efforts are ongoing to allow inline documentation on the Rust side to be ported to the bindings code, this is not currently possible.
|
||||
|
||||
This directory contains our temporary solution to this problem. A set of files mimicking the bindings libraries in their function signatures, but without any implementation. This allows for documentation build tools to produce API docs similarly to how we would like them to be if they could be inlined.
|
||||
|
||||
You can find the resulting API documentation websites in the ["API Reference" section of the sidebar](https://bitcoindevkit.org/getting-started/) under the "Docs" tab on the bitcoindevkit official website.
|
||||
@@ -1,4 +0,0 @@
|
||||
# Module bdk-android
|
||||
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android.
|
||||
|
||||
# Package org.bitcoindevkit
|
||||
@@ -1,4 +0,0 @@
|
||||
# Module bdk-jvm
|
||||
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Kotlin and Java on the JVM.
|
||||
|
||||
# Package org.bitcoindevkit
|
||||
@@ -1,46 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.7.10"
|
||||
|
||||
// API docs
|
||||
id("org.jetbrains.dokka") version "1.7.10"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
// tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
|
||||
// dokkaSourceSets {
|
||||
// named("main") {
|
||||
// moduleName.set("bdk-android")
|
||||
// moduleVersion.set("0.11.0")
|
||||
// includes.from("Module1.md")
|
||||
// samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
|
||||
dokkaSourceSets {
|
||||
named("main") {
|
||||
moduleName.set("bdk-jvm")
|
||||
moduleVersion.set("0.11.0")
|
||||
includes.from("Module2.md")
|
||||
samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
./gradlew dokkaHtml
|
||||
cd build/dokka/html
|
||||
git init .
|
||||
git add .
|
||||
git switch --create gh-pages
|
||||
git commit -m "Deploy"
|
||||
git remote add origin git@github.com:bitcoindevkit/bdk-kotlin.git
|
||||
git push --set-upstream origin gh-pages --force
|
||||
@@ -1 +0,0 @@
|
||||
kotlin.code.style=official
|
||||
BIN
api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar
vendored
BIN
api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
@@ -1,5 +0,0 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
234
api-docs/kotlin/gradlew
vendored
234
api-docs/kotlin/gradlew
vendored
@@ -1,234 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# 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
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
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"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
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.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
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.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
89
api-docs/kotlin/gradlew.bat
vendored
89
api-docs/kotlin/gradlew.bat
vendored
@@ -1,89 +0,0 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="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
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -1 +0,0 @@
|
||||
rootProject.name = "BDK Android and BDK JVM API Docs"
|
||||
@@ -1,706 +0,0 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
/**
|
||||
* The cryptocurrency to act on.
|
||||
*
|
||||
* @sample org.bitcoindevkit.networkSample
|
||||
*/
|
||||
enum class Network {
|
||||
/** Bitcoin's mainnet. */
|
||||
BITCOIN,
|
||||
|
||||
/** Bitcoin’s testnet. */
|
||||
TESTNET,
|
||||
|
||||
/** Bitcoin’s signet. */
|
||||
SIGNET,
|
||||
|
||||
/** Bitcoin’s regtest. */
|
||||
REGTEST,
|
||||
}
|
||||
|
||||
/**
|
||||
* A derived address and the index it was found at.
|
||||
*
|
||||
* @property index Child index of this address.
|
||||
* @property address Address.
|
||||
*
|
||||
* @sample org.bitcoindevkit.addressInfoSample
|
||||
*/
|
||||
data class AddressInfo (
|
||||
var index: UInt,
|
||||
var address: String
|
||||
)
|
||||
|
||||
/**
|
||||
* The address index selection strategy to use to derive an address from the wallet’s external descriptor.
|
||||
*
|
||||
* If you’re unsure which one to use, use `AddressIndex.NEW`.
|
||||
*
|
||||
* @sample org.bitcoindevkit.addressIndexSample
|
||||
*/
|
||||
enum class AddressIndex {
|
||||
/** Return a new address after incrementing the current descriptor index. */
|
||||
NEW,
|
||||
|
||||
/** Return the address for the current descriptor index if it has not been used in a received transaction.
|
||||
* Otherwise return a new address as with `AddressIndex.NEW`. Use with caution, if the wallet
|
||||
* has not yet detected an address has been used it could return an already used address.
|
||||
* This function is primarily meant for situations where the caller is untrusted;
|
||||
* for example when deriving donation addresses on-demand for a public web page.
|
||||
*/
|
||||
LAST_UNUSED,
|
||||
}
|
||||
|
||||
/**
|
||||
* Balance differentiated in various categories.
|
||||
*
|
||||
* @property immature All coinbase outputs not yet matured.
|
||||
* @property trustedPending Unconfirmed UTXOs generated by a wallet tx.
|
||||
* @property untrustedPending Unconfirmed UTXOs received from an external wallet.
|
||||
* @property confirmed Confirmed and immediately spendable balance.
|
||||
* @property spendable The sum of trustedPending and confirmed coins.
|
||||
* @property total The whole balance visible to the wallet.
|
||||
*
|
||||
* @sample org.bitcoindevkit.balanceSample
|
||||
*/
|
||||
data class Balance (
|
||||
var immature: ULong,
|
||||
var trustedPending: ULong,
|
||||
var untrustedPending: ULong,
|
||||
var confirmed: ULong,
|
||||
var spendable: ULong,
|
||||
var total: ULong
|
||||
)
|
||||
|
||||
/**
|
||||
* Type that can contain any of the database configurations defined by the library.
|
||||
*
|
||||
* @sample org.bitcoindevkit.memoryDatabaseConfigSample
|
||||
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
|
||||
*/
|
||||
sealed class DatabaseConfig {
|
||||
/** Configuration for an in-memory database. */
|
||||
object Memory : DatabaseConfig()
|
||||
|
||||
/** Configuration for a Sled database. */
|
||||
data class Sled(val config: SledDbConfiguration) : DatabaseConfig()
|
||||
|
||||
/** Configuration for a SQLite database. */
|
||||
data class Sqlite(val config: SqliteDbConfiguration) : DatabaseConfig()
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration type for a SQLite database.
|
||||
*
|
||||
* @property path Main directory of the DB.
|
||||
*
|
||||
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
|
||||
*/
|
||||
data class SqliteDbConfiguration(
|
||||
var path: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Configuration type for a SledDB database.
|
||||
*
|
||||
* @property path Main directory of the DB.
|
||||
* @property treeName Name of the database tree, a separated namespace for the data.
|
||||
*/
|
||||
data class SledDbConfiguration(
|
||||
var path: String,
|
||||
var treeName: String,
|
||||
)
|
||||
|
||||
/**
|
||||
* Configuration for an Electrum blockchain.
|
||||
*
|
||||
* @property url URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with `ssl://` or `tcp://` and include a port, e.g. `ssl://electrum.blockstream.info:60002`.
|
||||
* @property socks5 URL of the socks5 proxy server or a Tor service.
|
||||
* @property retry Request retry count.
|
||||
* @property timeout Request timeout (seconds).
|
||||
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
|
||||
* @property validateDomain Validate the domain when using SSL.
|
||||
*
|
||||
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
|
||||
*/
|
||||
data class ElectrumConfig(
|
||||
var url: String,
|
||||
var socks5: String?,
|
||||
var retry: UByte,
|
||||
var timeout: UByte?,
|
||||
var stopGap: ULong,
|
||||
var validateDomain: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Configuration for an Esplora blockchain.
|
||||
*
|
||||
* @property baseUrl Base URL of the esplora service, e.g. `https://blockstream.info/api/`.
|
||||
* @property proxy Optional URL of the proxy to use to make requests to the Esplora server.
|
||||
* @property concurrency Number of parallel requests sent to the esplora service (default: 4).
|
||||
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
|
||||
* @property timeout Socket timeout.
|
||||
*
|
||||
* @sample org.bitcoindevkit.esploraBlockchainConfigSample
|
||||
*/
|
||||
data class EsploraConfig(
|
||||
var baseUrl: String,
|
||||
var proxy: String?,
|
||||
var concurrency: UByte?,
|
||||
var stopGap: ULong,
|
||||
var timeout: ULong?
|
||||
)
|
||||
|
||||
/**
|
||||
* Authentication mechanism for RPC connection to full node.
|
||||
*/
|
||||
sealed class Auth {
|
||||
/** No authentication */
|
||||
object None: Auth()
|
||||
|
||||
/** Authentication with username and password, usually [Auth.Cookie] should be preferred */
|
||||
data class UserPass(val username: String, val password: String): Auth()
|
||||
|
||||
/** Authentication with a cookie file */
|
||||
data class Cookie(val file: String): Auth()
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync parameters for Bitcoin Core RPC.
|
||||
*
|
||||
* In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
|
||||
* `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
|
||||
* how the `importdescriptors` RPC calls are to be made.
|
||||
*
|
||||
* @property startScriptCount The minimum number of scripts to scan for on initial sync.
|
||||
* @property startTime Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
|
||||
* @property forceStartTime Forces every sync to use `start_time` as import timestamp.
|
||||
* @property pollRateSec RPC poll rate (in seconds) to get state updates.
|
||||
*/
|
||||
data class RcpSyncParams(
|
||||
val startScriptCount: ULong,
|
||||
val startTime: Ulong,
|
||||
val forceStartTime: Boolean,
|
||||
val pollRateSec: ULong,
|
||||
)
|
||||
|
||||
/**
|
||||
* RpcBlockchain configuration options
|
||||
*
|
||||
* @property url The bitcoin node url.
|
||||
* @property auth The bicoin node authentication mechanism.
|
||||
* @property network The network we are using (it will be checked the bitcoin node network matches this).
|
||||
* @property walletName The wallet name in the bitcoin node.
|
||||
* @property syncParams Sync parameters.
|
||||
*/
|
||||
data class RpcConfig(
|
||||
val url: String,
|
||||
val auth: Auth,
|
||||
val network: Network,
|
||||
val walletName: String,
|
||||
val syncParams: RcpSyncParams?,
|
||||
)
|
||||
|
||||
/**
|
||||
* Type that can contain any of the blockchain configurations defined by the library.
|
||||
*
|
||||
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
|
||||
*/
|
||||
sealed class BlockchainConfig {
|
||||
/** Electrum client. */
|
||||
data class Electrum(val config: ElectrumConfig) : BlockchainConfig()
|
||||
|
||||
/** Esplora client. */
|
||||
data class Esplora(val config: EsploraConfig) : BlockchainConfig()
|
||||
|
||||
/** Bitcoin Core RPC client. */
|
||||
data class Rpc(val config: RpcConfig) : BlockchainConfig()
|
||||
}
|
||||
|
||||
/**
|
||||
* A wallet transaction.
|
||||
*
|
||||
* @property fee Fee value (sats) if available. The availability of the fee depends on the backend. It’s never None with an Electrum server backend, but it could be None with a Bitcoin RPC node without txindex that receive funds while offline.
|
||||
* @property received Received value (sats) Sum of owned outputs of this transaction.
|
||||
* @property sent Sent value (sats) Sum of owned inputs of this transaction.
|
||||
* @property txid Transaction id.
|
||||
* @property confirmationTime If the transaction is confirmed, [BlockTime] contains height and timestamp of the block containing the transaction. This property is null for unconfirmed transactions.
|
||||
*/
|
||||
data class TransactionDetails (
|
||||
var fee: ULong?,
|
||||
var received: ULong,
|
||||
var sent: ULong,
|
||||
var txid: String,
|
||||
var confirmationTime: BlockTime?
|
||||
)
|
||||
|
||||
/**
|
||||
* A blockchain backend.
|
||||
*
|
||||
* @constructor Create the new blockchain client.
|
||||
*
|
||||
* @param config The blockchain configuration required.
|
||||
*
|
||||
* @sample org.bitcoindevkit.blockchainSample
|
||||
*/
|
||||
class Blockchain(
|
||||
config: BlockchainConfig
|
||||
) {
|
||||
/** Broadcast a transaction. */
|
||||
fun broadcast(psbt: PartiallySignedBitcoinTransaction) {}
|
||||
|
||||
/** Get the current height of the blockchain. */
|
||||
fun getHeight(): UInt {}
|
||||
|
||||
/** Get the block hash of a given block. */
|
||||
fun getBlockHash(height: UInt): String {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A partially signed bitcoin transaction.
|
||||
*
|
||||
* @constructor Build a new Partially Signed Bitcoin Transaction.
|
||||
*
|
||||
* @param psbtBase64 The PSBT in base64 format.
|
||||
*/
|
||||
class PartiallySignedBitcoinTransaction(psbtBase64: String) {
|
||||
/** Return the PSBT in string format, using a base64 encoding. */
|
||||
fun serialize(): String {}
|
||||
|
||||
/** Get the txid of the PSBT. */
|
||||
fun txid(): String {}
|
||||
|
||||
/** Return the transaction as bytes. */
|
||||
fun extractTx(): List<UByte>
|
||||
|
||||
/**
|
||||
* Combines this PartiallySignedTransaction with another PSBT as described by BIP 174.
|
||||
* In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||
*/
|
||||
fun combine(other: PartiallySignedBitcoinTransaction): PartiallySignedBitcoinTransaction
|
||||
}
|
||||
|
||||
/**
|
||||
* A reference to a transaction output.
|
||||
*
|
||||
* @property txid The referenced transaction’s txid.
|
||||
* @property vout The index of the referenced output in its transaction’s vout.
|
||||
*/
|
||||
data class OutPoint (
|
||||
var txid: String,
|
||||
var vout: UInt
|
||||
)
|
||||
|
||||
/**
|
||||
* A transaction output, which defines new coins to be created from old ones.
|
||||
*
|
||||
* @property value The value of the output, in satoshis.
|
||||
* @property address The address of the output.
|
||||
*/
|
||||
data class TxOut (
|
||||
var value: ULong,
|
||||
var address: String
|
||||
)
|
||||
|
||||
/**
|
||||
* An unspent output owned by a [Wallet].
|
||||
*
|
||||
* @property outpoint Reference to a transaction output.
|
||||
* @property txout Transaction output.
|
||||
* @property keychain Type of keychain.
|
||||
* @property isSpent Whether this UTXO is spent or not.
|
||||
*/
|
||||
data class LocalUtxo (
|
||||
var outpoint: OutPoint,
|
||||
var txout: TxOut,
|
||||
var keychain: KeychainKind,
|
||||
var isSpent: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* Types of keychains.
|
||||
*/
|
||||
enum class KeychainKind {
|
||||
/** External. */
|
||||
EXTERNAL,
|
||||
|
||||
/** Internal, usually used for change outputs. */
|
||||
INTERNAL,
|
||||
}
|
||||
|
||||
/**
|
||||
* Block height and timestamp of a block.
|
||||
*
|
||||
* @property height Confirmation block height.
|
||||
* @property timestamp Confirmation block timestamp.
|
||||
*/
|
||||
data class BlockTime (
|
||||
var height: UInt,
|
||||
var timestamp: ULong,
|
||||
)
|
||||
|
||||
/**
|
||||
* A Bitcoin wallet.
|
||||
* The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
|
||||
* 1. Output descriptors from which it can derive addresses.
|
||||
* 2. A Database where it tracks transactions and utxos related to the descriptors.
|
||||
* 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
|
||||
*
|
||||
* @constructor Create a BDK wallet.
|
||||
*
|
||||
* @param descriptor The main (or "external") descriptor.
|
||||
* @param changeDescriptor? The change (or "internal") descriptor.
|
||||
* @param network The network to act on.
|
||||
* @param databaseConfig The database configuration.
|
||||
*
|
||||
* @sample org.bitcoindevkit.walletSample
|
||||
*/
|
||||
class Wallet(
|
||||
descriptor: Descriptor,
|
||||
changeDescriptor: Descriptor?,
|
||||
network: Network,
|
||||
databaseConfig: DatabaseConfig,
|
||||
) {
|
||||
/**
|
||||
* Return a derived address using the external descriptor, see [AddressIndex] for available address index
|
||||
* selection strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end
|
||||
* with a * character) then the same address will always be returned for any [AddressIndex].
|
||||
*/
|
||||
fun getAddress(addressIndex: AddressIndex): AddressInfo {}
|
||||
|
||||
/** Return the wallet's balance, across different categories. See [Balance] for the categories. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
|
||||
fun getBalance(): Balance {}
|
||||
|
||||
/** Sign a transaction with all the wallet’s signers. */
|
||||
fun sign(psbt: PartiallySignedBitcoinTransaction): Boolean {}
|
||||
|
||||
/** Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually. */
|
||||
fun listTransactions(): List<TransactionDetails> {}
|
||||
|
||||
/** Get the Bitcoin network the wallet is using. */
|
||||
fun network(): Network {}
|
||||
|
||||
/** Sync the internal database with the blockchain. */
|
||||
fun sync(blockchain: Blockchain, progress: Progress?) {}
|
||||
|
||||
/** Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
|
||||
fun listUnspent(): List<LocalUtxo> {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class that logs at level INFO every update received (if any).
|
||||
*/
|
||||
class Progress {
|
||||
/** Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an optional text message that can be displayed to the user. */
|
||||
fun update(progress: Float, message: String?) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A transaction builder.
|
||||
*
|
||||
* After creating the TxBuilder, you set options on it until finally calling `.finish` to consume the builder and generate the transaction.
|
||||
*
|
||||
* Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
|
||||
*
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample1
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample2
|
||||
*/
|
||||
class TxBuilder() {
|
||||
/** Add data as an output using OP_RETURN. */
|
||||
fun addData(data: List<UByte>): TxBuilder {}
|
||||
|
||||
/** Add a recipient to the internal list. */
|
||||
fun addRecipient(script: Script, amount: ULong): TxBuilder {}
|
||||
|
||||
/** Set the list of recipients by providing a list of [ScriptAmount]. */
|
||||
fun setRecipients(recipients: List<ScriptAmount>): TxBuilder {}
|
||||
|
||||
/** Add a utxo to the internal list of unspendable utxos. It’s important to note that the "must-be-spent" utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details. */
|
||||
fun addUnspendable(unspendable: OutPoint): TxBuilder {}
|
||||
|
||||
/** Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable" utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent. */
|
||||
fun addUtxo(outpoint: OutPoint): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Add the list of outpoints to the internal list of UTXOs that must be spent. If an error
|
||||
* occurs while adding any of the UTXOs then none of them are added and the error is returned.
|
||||
* These have priority over the "unspendable" utxos, meaning that if a utxo is present both
|
||||
* in the "utxos" and the "unspendable" list, it will be spent.
|
||||
*/
|
||||
fun addUtxos(outpoints: List<OutPoint>): TxBuilder {}
|
||||
|
||||
/** Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */
|
||||
fun doNotSpendChange(): TxBuilder {}
|
||||
|
||||
/** Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are needed to make the transaction valid. */
|
||||
fun manuallySelectedOnly(): TxBuilder {}
|
||||
|
||||
/** Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See [TxBuilder.unspendable]. */
|
||||
fun onlySpendChange(): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Replace the internal list of unspendable utxos with a new list. It’s important to note that the "must-be-spent" utxos
|
||||
* added with [TxBuilder.addUtxo] have priority over these. See the Rust docs of the two linked methods for more details.
|
||||
*/
|
||||
fun unspendable(unspendable: List<OutPoint>): TxBuilder {}
|
||||
|
||||
/** Set a custom fee rate. */
|
||||
fun feeRate(satPerVbyte: Float): TxBuilder {}
|
||||
|
||||
/** Set an absolute fee. */
|
||||
fun feeAbsolute(feeAmount: ULong): TxBuilder {}
|
||||
|
||||
/** Spend all the available inputs. This respects filters like [TxBuilder.unspendable] and the change policy. */
|
||||
fun drainWallet(): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Sets the address to drain excess coins to. Usually, when there are excess coins they are
|
||||
* sent to a change address generated by the wallet. This option replaces the usual change address
|
||||
* with an arbitrary ScriptPubKey of your choosing. Just as with a change output, if the
|
||||
* drain output is not needed (the excess coins are too small) it will not be included in the resulting
|
||||
* transaction. The only difference is that it is valid to use [drainTo] without setting any ordinary recipients
|
||||
* with [addRecipient] (but it is perfectly fine to add recipients as well). If you choose not to set any
|
||||
* recipients, you should either provide the utxos that the transaction should spend via [addUtxos], or set
|
||||
* [drainWallet] to spend all of them. When bumping the fees of a transaction made with this option,
|
||||
* you probably want to use [BumpFeeTxBuilder.allowShrinking] to allow this output to be reduced to pay for the extra fees.
|
||||
*/
|
||||
fun drainTo(script: Script): TxBuilder {}
|
||||
|
||||
/** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
|
||||
fun enableRbf(): TxBuilder {}
|
||||
|
||||
/**
|
||||
* Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
|
||||
* contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
|
||||
* is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
*/
|
||||
fun enableRbfWithSequence(nsequence: UInt): TxBuilder {}
|
||||
|
||||
/** Finish building the transaction. Returns a [TxBuilderResult]. */
|
||||
fun finish(wallet: Wallet): TxBuilderResult {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A object holding a ScriptPubKey and an amount.
|
||||
*
|
||||
* @property script The ScriptPubKey.
|
||||
* @property amount The amount.
|
||||
*/
|
||||
data class ScriptAmount (
|
||||
var script: Script,
|
||||
var amount: ULong
|
||||
)
|
||||
|
||||
/**
|
||||
* The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
|
||||
*/
|
||||
class BumpFeeTxBuilder() {
|
||||
/**
|
||||
* Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this scriptPubKey
|
||||
* in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output
|
||||
* to shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is
|
||||
* preserved then it is currently not guaranteed to be in the same position as it was originally. Returns an error
|
||||
* if scriptPubkey can’t be found among the recipients of the transaction we are bumping.
|
||||
*/
|
||||
fun allowShrinking(address: String): BumpFeeTxBuilder {}
|
||||
|
||||
/** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
|
||||
fun enableRbf(): BumpFeeTxBuilder {}
|
||||
|
||||
/**
|
||||
* Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
|
||||
* contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
|
||||
* is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
*/
|
||||
fun enableRbfWithSequence(nsequence: UInt): BumpFeeTxBuilder {}
|
||||
|
||||
/** Finish building the transaction. Returns a [TxBuilderResult]. */
|
||||
fun finish(wallet: Wallet): TxBuilderResult {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A BIP-32 derivation path.
|
||||
*
|
||||
* @param path The derivation path. Must start with `m`. Use this type to derive or extend a [DescriptorSecretKey]
|
||||
* or [DescriptorPublicKey].
|
||||
*/
|
||||
class DerivationPath(path: String) {}
|
||||
|
||||
/**
|
||||
* An extended secret key.
|
||||
*
|
||||
* @param network The network this DescriptorSecretKey is to be used on.
|
||||
* @param mnemonic The mnemonic.
|
||||
* @param password The optional passphrase that can be provided as per BIP-39.
|
||||
*
|
||||
* @sample org.bitcoindevkit.descriptorSecretKeyDeriveSample
|
||||
* @sample org.bitcoindevkit.descriptorSecretKeyExtendSample
|
||||
*/
|
||||
class DescriptorSecretKey(network: Network, mnemonic: Mnemonic, password: String?) {
|
||||
/** Build a DescriptorSecretKey from a String */
|
||||
fun fromString(secretKey: String): DescriptorSecretKey {}
|
||||
|
||||
/** Derive a private descriptor at a given path. */
|
||||
fun derive(path: DerivationPath): DescriptorSecretKey {}
|
||||
|
||||
/** Extend the private descriptor with a custom path. */
|
||||
fun extend(path: DerivationPath): DescriptorSecretKey {}
|
||||
|
||||
/** Return the public version of the descriptor. */
|
||||
fun asPublic(): DescriptorPublicKey {}
|
||||
|
||||
/** Return the raw private key as bytes. */
|
||||
fun secretBytes(): List<UByte>
|
||||
|
||||
/** Return the private descriptor as a string. */
|
||||
fun asString(): String {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An extended public key.
|
||||
*
|
||||
* @param network The network this DescriptorPublicKey is to be used on.
|
||||
* @param mnemonic The mnemonic.
|
||||
* @param password The optional passphrase that can be provided as per BIP-39.
|
||||
*/
|
||||
class DescriptorPublicKey(network: Network, mnemonic: String, password: String?) {
|
||||
/** Build a DescriptorPublicKey from a String */
|
||||
fun fromString(publicKey: String): DescriptorPublicKey {}
|
||||
|
||||
/** Derive a public descriptor at a given path. */
|
||||
fun derive(path: DerivationPath): DescriptorPublicKey
|
||||
|
||||
/** Extend the public descriptor with a custom path. */
|
||||
fun extend(path: DerivationPath): DescriptorPublicKey
|
||||
|
||||
/** Return the public descriptor as a string. */
|
||||
fun asString(): String
|
||||
}
|
||||
|
||||
/**
|
||||
* A output descriptor.
|
||||
*
|
||||
* @param descriptor The descriptor in string format.
|
||||
* @param network The network this descriptor is to be used on.
|
||||
*
|
||||
* @sample org.bitcoindevkit.descriptorTemplates1
|
||||
* @sample org.bitcoindevkit.descriptorTemplates2
|
||||
*/
|
||||
class Descriptor(descriptor: String, network: Network) {
|
||||
/**
|
||||
* BIP44 template. Expands to pkh(key/44'/{0,1}'/0'/{0,1}/\*)
|
||||
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
|
||||
*/
|
||||
fun newBip44(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP44 public template. Expands to pkh(key/{0,1}/\*)
|
||||
* This assumes that the key used has already been derived with m/44'/0'/0' for Mainnet or m/44'/1'/0' for Testnet.
|
||||
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
*/
|
||||
fun newBip44Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP49 template. Expands to sh(wpkh(key/49'/{0,1}'/0'/{0,1}/\*))
|
||||
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
|
||||
*/
|
||||
fun newBip49(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP49 public template. Expands to sh(wpkh(key/{0,1}/\*))
|
||||
* This assumes that the key used has already been derived with m/49'/0'/0' for Mainnet or m/49'/1'/0' for Testnet.
|
||||
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
*/
|
||||
fun newBip49Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP84 template. Expands to wpkh(key/84'/{0,1}'/0'/{0,1}/\*)
|
||||
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
|
||||
*/
|
||||
fun newBip84(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/**
|
||||
* BIP84 public template. Expands to wpkh(key/{0,1}/\*)
|
||||
* This assumes that the key used has already been derived with m/84'/0'/0' for Mainnet or m/84'/1'/0' for Testnet.
|
||||
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
|
||||
*/
|
||||
fun newBip84Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
|
||||
|
||||
/** Return the public version of the output descriptor. */
|
||||
fun asString(): String {}
|
||||
|
||||
/** Return the private version of the output descriptor if available, otherwise return the public version. */
|
||||
fun asStringPrivate(): String {}
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum describing entropy length (aka word count) in the mnemonic.
|
||||
*/
|
||||
enum class WordCount {
|
||||
/** 12 words mnemonic (128 bits entropy). */
|
||||
WORDS12,
|
||||
|
||||
/** 15 words mnemonic (160 bits entropy). */
|
||||
WORDS15,
|
||||
|
||||
/** 18 words mnemonic (192 bits entropy). */
|
||||
WORDS18,
|
||||
|
||||
/** 21 words mnemonic (224 bits entropy). */
|
||||
WORDS21,
|
||||
|
||||
/** 24 words mnemonic (256 bits entropy). */
|
||||
WORDS24,
|
||||
}
|
||||
|
||||
/**
|
||||
* The value returned from calling the `.finish()` method on the [TxBuilder] or [BumpFeeTxBuilder].
|
||||
*
|
||||
* @property psbt The PSBT
|
||||
* @property transactionDetails The transaction details.
|
||||
*
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample1
|
||||
* @sample org.bitcoindevkit.txBuilderResultSample2
|
||||
*/
|
||||
data class TxBuilderResult (
|
||||
var psbt: PartiallySignedBitcoinTransaction,
|
||||
var transactionDetails: TransactionDetails
|
||||
)
|
||||
|
||||
/**
|
||||
* A bitcoin script.
|
||||
*/
|
||||
class Script(rawOutputScript: List<UByte>)
|
||||
|
||||
/**
|
||||
* A bitcoin address.
|
||||
*
|
||||
* @param address The address in string format.
|
||||
*/
|
||||
class Address(address: String) {
|
||||
/** Return the ScriptPubKey. */
|
||||
fun scriptPubkey(): Script
|
||||
}
|
||||
|
||||
/**
|
||||
* Mnemonic phrases are a human-readable version of the private keys. Supported number of words are 12, 15, 18, 21 and 24.
|
||||
*
|
||||
* @constructor Generates Mnemonic with a random entropy.
|
||||
* @param mnemonic The mnemonic as a string of space-separated words.
|
||||
*
|
||||
* @sample org.bitcoindevkit.mnemonicSample
|
||||
*/
|
||||
class Mnemonic(mnemonic: String) {
|
||||
/* Returns Mnemonic as string */
|
||||
fun asString(): String
|
||||
|
||||
/* Parse a Mnemonic from a given string. */
|
||||
fun fromString(): Mnemonic
|
||||
|
||||
/*
|
||||
* Create a new Mnemonic in the specified language from the given entropy. Entropy must be a
|
||||
* multiple of 32 bits (4 bytes) and 128-256 bits in length.
|
||||
*/
|
||||
fun fromEntropy(): Mnemonic
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
fun networkSample() {
|
||||
val wallet = Wallet(
|
||||
descriptor = descriptor,
|
||||
changeDescriptor = changeDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = DatabaseConfig.Memory
|
||||
)
|
||||
}
|
||||
|
||||
fun balanceSample() {
|
||||
object LogProgress : Progress {
|
||||
override fun update(progress: Float, message: String?) {}
|
||||
}
|
||||
|
||||
val memoryDatabaseConfig = DatabaseConfig.Memory
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
200u
|
||||
)
|
||||
)
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, memoryDatabaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress)
|
||||
|
||||
val balance: Balance = wallet.getBalance()
|
||||
println("Total wallet balance is ${balance.total}")
|
||||
}
|
||||
|
||||
fun electrumBlockchainConfigSample() {
|
||||
val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
url = "ssl://electrum.blockstream.info:60002",
|
||||
socks5 = null,
|
||||
retry = 5u,
|
||||
timeout = null,
|
||||
stopGap = 200u
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun esploraBlockchainConfigSample() {
|
||||
val esploraURL: String = "http://10.0.2.2:3002"
|
||||
val esploraConfig: EsploraConfig = EsploraConfig(
|
||||
baseUrl = esploraURL,
|
||||
proxy = null,
|
||||
concurrency = 4u,
|
||||
stopGap = 20UL,
|
||||
timeout = null
|
||||
)
|
||||
val blockchainConfig = BlockchainConfig.Esplora(config = esploraConfig)
|
||||
}
|
||||
|
||||
fun memoryDatabaseConfigSample() {
|
||||
val memoryDatabaseConfig = DatabaseConfig.Memory
|
||||
}
|
||||
|
||||
fun sqliteDatabaseConfigSample() {
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
|
||||
}
|
||||
|
||||
fun addressIndexSample() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor = descriptor,
|
||||
changeDescriptor = changeDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = DatabaseConfig.Memory
|
||||
)
|
||||
|
||||
fun getLastUnusedAddress(): AddressInfo {
|
||||
return wallet.getAddress(AddressIndex.LAST_UNUSED)
|
||||
}
|
||||
}
|
||||
|
||||
fun addressInfoSample() {
|
||||
val wallet: Wallet = Wallet(
|
||||
descriptor = descriptor,
|
||||
changeDescriptor = changeDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = DatabaseConfig.Memory
|
||||
)
|
||||
|
||||
fun getLastUnusedAddress(): AddressInfo {
|
||||
return wallet.getAddress(AddressIndex.NEW)
|
||||
}
|
||||
|
||||
val newAddress: AddressInfo = getLastUnusedAddress()
|
||||
|
||||
println("New address at index ${newAddress.index} is ${newAddress.address}")
|
||||
}
|
||||
|
||||
fun blockchainSample() {
|
||||
val blockchainConfig: BlockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
electrumURL,
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
10u
|
||||
)
|
||||
)
|
||||
|
||||
val blockchain: Blockchain = Blockchain(blockchainConfig)
|
||||
|
||||
blockchain.broadcast(signedPsbt)
|
||||
}
|
||||
|
||||
|
||||
fun txBuilderResultSample1() {
|
||||
val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||
// TxBuilderResult is a data class, which means you can use destructuring declarations on it to
|
||||
// open it up into its component parts
|
||||
val (psbt, txDetails) = TxBuilder()
|
||||
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
|
||||
.feeRate(1.2f)
|
||||
.finish(wallet)
|
||||
|
||||
println("Txid is ${txDetails.txid}")
|
||||
wallet.sign(psbt)
|
||||
}
|
||||
|
||||
fun txBuilderResultSample2() {
|
||||
val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||
val txBuilderResult: TxBuilderResult = TxBuilder()
|
||||
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
|
||||
.feeRate(1.2f)
|
||||
.finish(wallet)
|
||||
|
||||
val psbt = txBuilderResult.psbt
|
||||
val txDetails = txBuilderResult.transactionDetails
|
||||
|
||||
println("Txid is ${txDetails.txid}")
|
||||
wallet.sign(psbt)
|
||||
}
|
||||
|
||||
fun descriptorSecretKeyExtendSample() {
|
||||
// The `DescriptorSecretKey.extend()` method allows you to extend a key to any given path.
|
||||
|
||||
// val mnemonic: String = generateMnemonic(WordCount.WORDS12)
|
||||
val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
|
||||
|
||||
// the initial DescriptorSecretKey will always be at the "master" node,
|
||||
// i.e. the derivation path is empty
|
||||
val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
|
||||
network = Network.TESTNET,
|
||||
mnemonic = mnemonic,
|
||||
password = ""
|
||||
)
|
||||
println(bip32RootKey.asString())
|
||||
// tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/*
|
||||
|
||||
// the derive method will also automatically apply the wildcard (*) to your path,
|
||||
// i.e the following will generate the typical testnet BIP84 external wallet path
|
||||
// m/84h/1h/0h/0/*
|
||||
val bip84ExternalPath: DerivationPath = DerivationPath("m/84h/1h/0h/0")
|
||||
val externalExtendedKey: DescriptorSecretKey = bip32RootKey.extend(bip84ExternalPath).asString()
|
||||
println(externalExtendedKey)
|
||||
// tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/84'/1'/0'/0/*
|
||||
|
||||
// to create the descriptor you'll need to use this extended key in a descriptor function,
|
||||
// i.e. wpkh(), tr(), etc.
|
||||
val externalDescriptor = "wpkh($externalExtendedKey)"
|
||||
}
|
||||
|
||||
fun descriptorSecretKeyDeriveSample() {
|
||||
// The DescriptorSecretKey.derive() method allows you to derive an extended key for a given
|
||||
// node in the derivation tree (for example to create an xpub for a particular account)
|
||||
|
||||
val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
|
||||
val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
|
||||
network = Network.TESTNET,
|
||||
mnemonic = mnemonic,
|
||||
password = ""
|
||||
)
|
||||
|
||||
val bip84Account0: DerivationPath = DerivationPath("m/84h/1h/0h")
|
||||
val xpubAccount0: DescriptorSecretKey = bip32RootKey.derive(bip84Account0)
|
||||
println(xpubAccount0.asString())
|
||||
// [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/*
|
||||
|
||||
val internalPath: DerivationPath = DerivationPath("m/0")
|
||||
val externalExtendedKey = xpubAccount0.extend(internalPath).asString()
|
||||
println(externalExtendedKey)
|
||||
// [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/0/*
|
||||
|
||||
// to create the descriptor you'll need to use this extended key in a descriptor function,
|
||||
// i.e. wpkh(), tr(), etc.
|
||||
val externalDescriptor = "wpkh($externalExtendedKey)"
|
||||
}
|
||||
|
||||
fun createTransaction() {
|
||||
val wallet = BdkWallet(
|
||||
descriptor = externalDescriptor,
|
||||
changeDescriptor = internalDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = memoryDatabaseConfig,
|
||||
)
|
||||
val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
200u
|
||||
)
|
||||
)
|
||||
|
||||
val paymentAddress: Address = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
|
||||
val (psbt, txDetails) = TxBuilder()
|
||||
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
|
||||
.feeRate(1.2f)
|
||||
.finish(wallet)
|
||||
|
||||
wallet.sign(psbt)
|
||||
blockchain.broadcast(psbt)
|
||||
}
|
||||
|
||||
fun walletSample() {
|
||||
val externalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/0/*)"
|
||||
val internalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/1/*)"
|
||||
val sqliteDatabaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
|
||||
|
||||
val wallet = Wallet(
|
||||
descriptor = externalDescriptor,
|
||||
changeDescriptor = internalDescriptor,
|
||||
network = Network.TESTNET,
|
||||
databaseConfig = sqliteDatabaseConfig,
|
||||
)
|
||||
}
|
||||
|
||||
fun mnemonicSample() {
|
||||
val mnemonic0: Mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
|
||||
val mnemonic1: Mnemonic = Mnemonic.fromString("scene change clap smart together mind wheel knee clip normal trial unusual")
|
||||
|
||||
val entropy: List<UByte> = listOf<UByte>(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)
|
||||
val mnemonic2: Mnemonic = Mnemonic.fromEntropy(entropy)
|
||||
|
||||
println(mnemonic0.asString(), mnemonic1.asString(), mnemonic2.asString())
|
||||
}
|
||||
|
||||
fun descriptorTemplates1() {
|
||||
// Bip84 private descriptor
|
||||
val recoveryPhrase: String = "scene change clap smart together mind wheel knee clip normal trial unusual"
|
||||
val mnemonic = Mnemonic.fromString(recoveryPhrase)
|
||||
val bip32ExtendedRootKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val bip84ExternalDescriptor: Descriptor = Descriptor.newBip84(bip32ExtendedRootKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
}
|
||||
|
||||
fun descriptorTemplates2() {
|
||||
// Bip49 public descriptor
|
||||
// assume we already have the xpub for m/49'/0'/1' created on an external device that only shared the xpub with the wallet
|
||||
// using the template requires the parent fingerprint to populate correctly the metadata of PSBTs, which the external device would provide
|
||||
// the xpub (tpub for testnet): tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR
|
||||
// the fingerprint: d1d04177
|
||||
val descriptorPublicKey: DescriptorPublicKey = DescriptorPublicKey.fromString("tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR")
|
||||
val bip49PublicDescriptor: Descriptor = Descriptor.newBip49Public(
|
||||
publicKey = descriptorPublicKey,
|
||||
fingerprint = "d1d04177",
|
||||
keychain = KeychainKind.EXTERNAL,
|
||||
network = Network.TESTNET,
|
||||
)
|
||||
println(bip49PublicDescriptor.asString()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
|
||||
println(bip49PublicDescriptor.asStringPrivate()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
|
||||
|
||||
// Creating it starting from the full xprv derived from a mnemonic will give you the same public descriptor
|
||||
val mnemonic = Mnemonic.fromString("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect")
|
||||
val bip32ExtendedRootKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val bip49PrivateDescriptor: Descriptor = Descriptor.newBip49(bip32ExtendedRootKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
println(bip49PrivateDescriptor.asString()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
|
||||
}
|
||||
@@ -8,19 +8,19 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||
}
|
||||
```
|
||||
|
||||
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
|
||||
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 = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
|
||||
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 databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
@@ -28,7 +28,7 @@ val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
|
||||
)
|
||||
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
||||
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
|
||||
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
|
||||
```
|
||||
|
||||
### Snapshot releases
|
||||
@@ -38,12 +38,13 @@ repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version-SNAPSHOT>")
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-android:<version-SNAPSHOT>")
|
||||
}
|
||||
```
|
||||
|
||||
### Example Projects
|
||||
* [bdk-kotlin-example-wallet](https://github.com/bitcoindevkit/bdk-kotlin-example-wallet)
|
||||
* [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet)
|
||||
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
|
||||
|
||||
@@ -54,19 +55,23 @@ _Note that Kotlin version `1.6.10` or later is required to build the library._
|
||||
```shell
|
||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||
```
|
||||
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||
3. If building on macOS install required intel and m1 jvm targets
|
||||
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||
3. Install Rust (note that we are currently building using Rust 1.73.0):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.73.0
|
||||
```
|
||||
4. Install required targets
|
||||
```sh
|
||||
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
```
|
||||
```sh
|
||||
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||
```
|
||||
5. Install Android SDK and Build-Tools for API level 30+
|
||||
6. Setup `$ANDROID_SDK_ROOT` and `$ANDROID_NDK_ROOT` path variables (which are required by the
|
||||
build tool), for example (NDK major version 21 is required):
|
||||
```shell
|
||||
export ANDROID_SDK_ROOT=~/Android/Sdk
|
||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.<NDK_VERSION>
|
||||
```
|
||||
build tool), for example (note that currently, NDK version 25.2.9519653 or above is required):
|
||||
```shell
|
||||
export ANDROID_SDK_ROOT=~/Android/Sdk
|
||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
|
||||
```
|
||||
7. Build kotlin bindings
|
||||
```sh
|
||||
# build Android library
|
||||
@@ -96,6 +101,7 @@ and use the `publishToMavenLocal` task without excluding the signing task:
|
||||
```
|
||||
|
||||
## Known issues
|
||||
### JNA dependency
|
||||
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
|
||||
```shell
|
||||
class file for com.sun.jna.Pointer not found
|
||||
@@ -108,5 +114,8 @@ dependencies {
|
||||
}
|
||||
```
|
||||
|
||||
### x86 emulators
|
||||
For some older versions of macOS, Android Studio will recommend users install the x86 version of the emulator by default. This will not work with the bdk-android library, as we do not support 32-bit architectures. Make sure you install an x86_64 emulator to work with bdk-android.
|
||||
|
||||
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
||||
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
||||
|
||||
@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=0.26.0-SNAPSHOT
|
||||
libraryVersion=1.0.0-alpha.2-rc1
|
||||
|
||||
@@ -53,7 +53,8 @@ dependencies {
|
||||
androidTestImplementation("com.github.tony19:logback-android:2.0.0")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1")
|
||||
androidTestImplementation("org.jetbrains.kotlin:kotlin-test:1.6.10")
|
||||
androidTestImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.6.10")
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
@@ -81,14 +82,9 @@ afterEvaluate {
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("notmandatory")
|
||||
name.set("Steve Myers")
|
||||
email.set("notmandatory@noreply.github.org")
|
||||
}
|
||||
developer {
|
||||
id.set("artfuldev")
|
||||
name.set("Sudarsan Balaji")
|
||||
email.set("sudarsan.balaji@artfuldev.com")
|
||||
id.set("bdkdevelopers")
|
||||
name.set("Bitcoin Dev Kit Developers")
|
||||
email.set("dev@bitcoindevkit.org")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import android.app.Application
|
||||
import android.content.Context.MODE_PRIVATE
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AndroidLibTest {
|
||||
|
||||
private fun getTestDataDir(): String {
|
||||
val context = ApplicationProvider.getApplicationContext<Application>()
|
||||
return context.getDir("bdk-test", MODE_PRIVATE).toString()
|
||||
}
|
||||
|
||||
private fun cleanupTestDataDir(testDataDir: String) {
|
||||
File(testDataDir).deleteRecursively()
|
||||
}
|
||||
|
||||
class LogProgress : Progress {
|
||||
private val log: Logger = LoggerFactory.getLogger(AndroidLibTest::class.java)
|
||||
|
||||
override fun update(progress: Float, message: String?) {
|
||||
log.debug("Syncing...")
|
||||
}
|
||||
}
|
||||
|
||||
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
|
||||
private val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
100u,
|
||||
true,
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun memoryWalletNewAddress() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val address = wallet.getAddress(AddressIndex.NEW).address
|
||||
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun memoryWalletSyncGetBalance() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sqliteWalletSyncGetBalance() {
|
||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
cleanupTestDataDir(testDataDir)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import org.junit.Test
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveTxBuilderTest {
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total()}")
|
||||
|
||||
assert(wallet.getBalance().total() > 0uL)
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(2.0f)
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import org.junit.Test
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class LiveWalletTest {
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val esploraClient: EsploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
// val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
|
||||
val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total()}")
|
||||
val balance: Balance = wallet.getBalance()
|
||||
println("Balance: $balance")
|
||||
|
||||
assert(wallet.getBalance().total() > 0uL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total()}")
|
||||
println("New address: ${wallet.getAddress(AddressIndex.New).address}")
|
||||
|
||||
assert(wallet.getBalance().total() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(4.0f)
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
|
||||
val walletDidSign = wallet.sign(psbt)
|
||||
assertTrue(walletDidSign)
|
||||
|
||||
val tx: Transaction = psbt.extractTx()
|
||||
|
||||
println("Txid is: ${tx.txid()}")
|
||||
esploraClient.broadcast(tx)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OfflineDescriptorTest {
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic.fromString("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertEquals(
|
||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
actual = descriptor.asString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class OfflineWalletTest {
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNewAddress() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet.newNoPersist(
|
||||
descriptor,
|
||||
null,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
|
||||
|
||||
assertEquals(
|
||||
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
|
||||
actual = addressInfo.address.asString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet.newNoPersist(
|
||||
descriptor,
|
||||
null,
|
||||
Network.TESTNET
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.getBalance().total()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,7 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
val buildAndroidAarch64Binary by tasks.register<Exec>("buildAndroidAarch64Binary") {
|
||||
|
||||
workingDir("${projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: MutableList<String> =
|
||||
mutableListOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
@@ -36,10 +35,9 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH",
|
||||
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
|
||||
Pair("CFLAGS", "-D__ANDROID_API__=21"),
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
|
||||
Pair("CC", "aarch64-linux-android21-clang")
|
||||
)
|
||||
@@ -53,8 +51,7 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
val buildAndroidX86_64Binary by tasks.register<Exec>("buildAndroidX86_64Binary") {
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: MutableList<String> =
|
||||
mutableListOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
@@ -68,10 +65,9 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH",
|
||||
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
|
||||
Pair("CFLAGS", "-D__ANDROID_API__=21"),
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
|
||||
Pair("CC", "x86_64-linux-android21-clang")
|
||||
)
|
||||
@@ -85,8 +81,7 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
val buildAndroidArmv7Binary by tasks.register<Exec>("buildAndroidArmv7Binary") {
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: MutableList<String> =
|
||||
mutableListOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
|
||||
|
||||
executable("cargo")
|
||||
args(cargoArgs)
|
||||
@@ -100,12 +95,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
|
||||
environment(
|
||||
// add build toolchain to PATH
|
||||
Pair("PATH",
|
||||
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
|
||||
Pair("CFLAGS", "-D__ANDROID_API__=21"),
|
||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER",
|
||||
"armv7a-linux-androideabi21-clang"),
|
||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
||||
Pair("AR", "llvm-ar"),
|
||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi21-clang"),
|
||||
Pair("CC", "armv7a-linux-androideabi21-clang")
|
||||
)
|
||||
|
||||
@@ -124,15 +117,15 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
into("${project.projectDir}/../lib/src/main/jniLibs/")
|
||||
|
||||
into("arm64-v8a") {
|
||||
from("${project.projectDir}/../../target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("x86_64") {
|
||||
from("${project.projectDir}/../../target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
into("armeabi-v7a") {
|
||||
from("${project.projectDir}/../../target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
|
||||
}
|
||||
|
||||
doLast {
|
||||
@@ -144,18 +137,17 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
||||
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
||||
dependsOn(moveNativeAndroidLibs)
|
||||
|
||||
// val libraryPath = "${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so"
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
// The code above worked for uniffi 0.24.3 using the --library flag
|
||||
// The code below works for uniffi 0.23.0
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
|
||||
|
||||
executable("cargo")
|
||||
args(
|
||||
"run",
|
||||
"--package",
|
||||
"bdk-ffi-bindgen",
|
||||
"--",
|
||||
"--language",
|
||||
"kotlin",
|
||||
"--out-dir",
|
||||
"../bdk-android/lib/src/main/kotlin"
|
||||
)
|
||||
args(cargoArgs)
|
||||
|
||||
doLast {
|
||||
println("Android bindings file successfully created")
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
[package]
|
||||
name = "bdk-ffi-bindgen"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.45" # remove after upgrading to next version of uniffi
|
||||
structopt = "0.3"
|
||||
uniffi_bindgen = "0.21.0"
|
||||
camino = "1.0.9"
|
||||
@@ -1,138 +0,0 @@
|
||||
use camino::Utf8Path;
|
||||
use std::fmt;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum Language {
|
||||
Kotlin,
|
||||
Python,
|
||||
Swift,
|
||||
}
|
||||
|
||||
impl fmt::Display for Language {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Language::Kotlin => write!(f, "kotlin"),
|
||||
Language::Swift => write!(f, "swift"),
|
||||
Language::Python => write!(f, "python"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
UnsupportedLanguage,
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Language {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"kotlin" => Ok(Language::Kotlin),
|
||||
"python" => Ok(Language::Python),
|
||||
"swift" => Ok(Language::Swift),
|
||||
_ => Err(Error::UnsupportedLanguage),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_bindings(opt: &Opt) -> anyhow::Result<(), anyhow::Error> {
|
||||
let path: &Utf8Path = Utf8Path::from_path(&opt.udl_file).unwrap();
|
||||
let out_dir: &Utf8Path = Utf8Path::from_path(&opt.out_dir).unwrap();
|
||||
uniffi_bindgen::generate_bindings(
|
||||
path,
|
||||
None,
|
||||
vec![opt.language.to_string().as_str()],
|
||||
Some(out_dir),
|
||||
None,
|
||||
false,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fixup_python_lib_path(
|
||||
out_dir: &Path,
|
||||
lib_name: &Path,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
use std::fs;
|
||||
use std::io::Write;
|
||||
|
||||
const LOAD_INDIRECT_DEF: &str = "def loadIndirect():";
|
||||
|
||||
let bindings_file = out_dir.join("bdk.py");
|
||||
let mut data = fs::read_to_string(&bindings_file)?;
|
||||
|
||||
let pos = data
|
||||
.find(LOAD_INDIRECT_DEF)
|
||||
.unwrap_or_else(|| panic!("loadIndirect not found in `{}`", bindings_file.display()));
|
||||
let range = pos..pos + LOAD_INDIRECT_DEF.len();
|
||||
|
||||
let replacement = format!(
|
||||
r#"
|
||||
def loadIndirect():
|
||||
import glob
|
||||
return getattr(ctypes.cdll, glob.glob(os.path.join(os.path.dirname(os.path.abspath(__file__)), '{}.*'))[0])
|
||||
|
||||
def _loadIndirectOld():"#,
|
||||
&lib_name.to_str().expect("lib name")
|
||||
);
|
||||
data.replace_range(range, &replacement);
|
||||
|
||||
let mut file = fs::OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(&bindings_file)?;
|
||||
file.write_all(data.as_bytes())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug, StructOpt)]
|
||||
#[structopt(
|
||||
name = "bdk-ffi-bindgen",
|
||||
about = "A tool to generate bdk-ffi language bindings"
|
||||
)]
|
||||
struct Opt {
|
||||
/// UDL file
|
||||
#[structopt(env = "BDKFFI_BINDGEN_UDL", short, long, default_value("src/bdk.udl"), parse(try_from_str = PathBuf::from_str))]
|
||||
udl_file: PathBuf,
|
||||
|
||||
/// Language to generate bindings for
|
||||
#[structopt(env = "BDKFFI_BINDGEN_LANGUAGE", short, long, possible_values(&["kotlin","swift","python"]), parse(try_from_str = Language::from_str))]
|
||||
language: Language,
|
||||
|
||||
/// Output directory to put generated language bindings
|
||||
#[structopt(env = "BDKFFI_BINDGEN_OUTPUT_DIR", short, long, parse(try_from_str = PathBuf::from_str))]
|
||||
out_dir: PathBuf,
|
||||
|
||||
/// Python fix up lib path
|
||||
#[structopt(env = "BDKFFI_BINDGEN_PYTHON_FIXUP_PATH", short, long, parse(try_from_str = PathBuf::from_str))]
|
||||
python_fixup_path: Option<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let opt = Opt::from_args();
|
||||
|
||||
println!("Input UDL file is {:?}", opt.udl_file);
|
||||
println!("Chosen language is {}", opt.language);
|
||||
println!("Output directory is {:?}", opt.out_dir);
|
||||
|
||||
generate_bindings(&opt)?;
|
||||
|
||||
if opt.language == Language::Python {
|
||||
if let Some(path) = opt.python_fixup_path {
|
||||
println!("Fixing up python lib path, {:?}", &path);
|
||||
fixup_python_lib_path(&opt.out_dir, &path)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
1241
Cargo.lock → bdk-ffi/Cargo.lock
generated
1241
Cargo.lock → bdk-ffi/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,21 +1,47 @@
|
||||
[package]
|
||||
name = "bdk-ffi"
|
||||
version = "0.26.0"
|
||||
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||
version = "1.0.0-alpha.2"
|
||||
homepage = "https://bitcoindevkit.org"
|
||||
repository = "https://github.com/bitcoindevkit/bdk"
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib", "cdylib"]
|
||||
crate-type = ["lib", "staticlib", "cdylib"]
|
||||
name = "bdkffi"
|
||||
|
||||
[[bin]]
|
||||
name = "uniffi-bindgen"
|
||||
path = "uniffi-bindgen.rs"
|
||||
|
||||
[features]
|
||||
default = ["uniffi/cli"]
|
||||
|
||||
[dependencies]
|
||||
bdk = { version = "0.26", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
|
||||
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||
bdk = { version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
|
||||
|
||||
# TODO 22: The bdk_esplora crate uses esplora_client which uses reqwest for async. By default it uses the system
|
||||
# openssl library, which is creating problems for cross-compilation. I'd rather use rustls, but it's hidden
|
||||
# behind a feature flag. We need to look into whether openssl-sys is really required by bdk or if using rustls
|
||||
# would work just as well. This here is a temporary workaround which removes the async feature on the bdk_esplora crate.
|
||||
# See PR #1179 https://github.com/bitcoindevkit/bdk/pull/1179 for the fix in bdk.
|
||||
# bdk = { git = "https://github.com/thunderbiscuit/bdk.git", branch = "test-rust-tls", version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
|
||||
# bdk_esplora = { git = "https://github.com/thunderbiscuit/bdk.git", branch = "test-rust-tls", version = "0.4.0", package = "bdk_esplora", default-features = false, features = ["std", "blocking", "async-https-rustls"] }
|
||||
|
||||
bdk_esplora = { version = "0.4.0", default-features = false, features = ["std", "blocking"] }
|
||||
uniffi = { version = "=0.25.1" }
|
||||
|
||||
[build-dependencies]
|
||||
uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||
uniffi = { version = "=0.25.1", features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
uniffi = { version = "=0.25.1", features = ["bindgen-tests"] }
|
||||
assert_matches = "1.5.0"
|
||||
|
||||
[profile.release-smaller]
|
||||
inherits = "release"
|
||||
opt-level = 'z' # Optimize for size.
|
||||
lto = true # Enable Link Time Optimization
|
||||
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
|
||||
panic = "abort" # Abort on panic
|
||||
strip = "debuginfo" # Partially strip symbols from binary
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
fn main() {
|
||||
uniffi_build::generate_scaffolding("src/bdk.udl").unwrap();
|
||||
uniffi::generate_scaffolding("./src/bdk.udl").unwrap();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
namespace bdk {
|
||||
namespace bdk {};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - root module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
enum KeychainKind {
|
||||
"External",
|
||||
"Internal",
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - wallet module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
[Error]
|
||||
enum BdkError {
|
||||
"InvalidU32Bytes",
|
||||
"Generic",
|
||||
"MissingCachedScripts",
|
||||
"ScriptDoesntHaveAddressForm",
|
||||
"NoRecipients",
|
||||
"NoUtxosSelected",
|
||||
"OutputBelowDustLimit",
|
||||
@@ -27,288 +35,101 @@ enum BdkError {
|
||||
"SpendingPolicyRequired",
|
||||
"InvalidPolicyPathError",
|
||||
"Signer",
|
||||
"InvalidNetwork",
|
||||
"InvalidProgressValue",
|
||||
"ProgressUpdateError",
|
||||
"InvalidOutpoint",
|
||||
"Descriptor",
|
||||
"Encode",
|
||||
"Miniscript",
|
||||
"MiniscriptPsbt",
|
||||
"Bip32",
|
||||
"Secp256k1",
|
||||
"Json",
|
||||
"Hex",
|
||||
"Psbt",
|
||||
"PsbtParse",
|
||||
"Electrum",
|
||||
"Esplora",
|
||||
"Sled",
|
||||
"Rusqlite",
|
||||
"Rpc",
|
||||
};
|
||||
|
||||
enum ChangeSpendPolicy {
|
||||
"ChangeAllowed",
|
||||
"OnlyChange",
|
||||
"ChangeForbidden"
|
||||
};
|
||||
|
||||
interface Balance {
|
||||
u64 immature();
|
||||
|
||||
u64 trusted_pending();
|
||||
|
||||
u64 untrusted_pending();
|
||||
|
||||
u64 confirmed();
|
||||
|
||||
u64 trusted_spendable();
|
||||
|
||||
u64 total();
|
||||
};
|
||||
|
||||
dictionary AddressInfo {
|
||||
u32 index;
|
||||
string address;
|
||||
};
|
||||
|
||||
enum AddressIndex {
|
||||
"New",
|
||||
"LastUnused",
|
||||
};
|
||||
|
||||
enum Network {
|
||||
"Bitcoin",
|
||||
"Testnet",
|
||||
"Signet",
|
||||
"Regtest",
|
||||
};
|
||||
|
||||
dictionary SledDbConfiguration {
|
||||
string path;
|
||||
string tree_name;
|
||||
};
|
||||
|
||||
dictionary SqliteDbConfiguration {
|
||||
string path;
|
||||
};
|
||||
|
||||
dictionary Balance {
|
||||
u64 immature;
|
||||
u64 trusted_pending;
|
||||
u64 untrusted_pending;
|
||||
u64 confirmed;
|
||||
u64 spendable;
|
||||
u64 total;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface DatabaseConfig {
|
||||
Memory();
|
||||
Sled(SledDbConfiguration config);
|
||||
Sqlite(SqliteDbConfiguration config);
|
||||
};
|
||||
|
||||
dictionary TransactionDetails {
|
||||
u64? fee;
|
||||
u64 received;
|
||||
u64 sent;
|
||||
string txid;
|
||||
BlockTime? confirmation_time;
|
||||
};
|
||||
|
||||
dictionary BlockTime {
|
||||
u32 height;
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
enum WordCount {
|
||||
"Words12",
|
||||
"Words15",
|
||||
"Words18",
|
||||
"Words21",
|
||||
"Words24",
|
||||
};
|
||||
|
||||
dictionary ElectrumConfig {
|
||||
string url;
|
||||
string? socks5;
|
||||
u8 retry;
|
||||
u8? timeout;
|
||||
u64 stop_gap;
|
||||
boolean validate_domain;
|
||||
};
|
||||
|
||||
dictionary EsploraConfig {
|
||||
string base_url;
|
||||
string? proxy;
|
||||
u8? concurrency;
|
||||
u64 stop_gap;
|
||||
u64? timeout;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface Auth {
|
||||
None();
|
||||
UserPass(string username, string password);
|
||||
Cookie(string file);
|
||||
};
|
||||
|
||||
dictionary RpcSyncParams {
|
||||
u64 start_script_count;
|
||||
u64 start_time;
|
||||
boolean force_start_time;
|
||||
u64 poll_rate_sec;
|
||||
};
|
||||
|
||||
dictionary RpcConfig {
|
||||
string url;
|
||||
Auth auth;
|
||||
Network network;
|
||||
string wallet_name;
|
||||
RpcSyncParams? sync_params;
|
||||
};
|
||||
|
||||
[Enum]
|
||||
interface BlockchainConfig {
|
||||
Electrum(ElectrumConfig config);
|
||||
Esplora(EsploraConfig config);
|
||||
Rpc(RpcConfig config);
|
||||
};
|
||||
|
||||
interface Blockchain {
|
||||
[Throws=BdkError]
|
||||
constructor(BlockchainConfig config);
|
||||
|
||||
[Throws=BdkError]
|
||||
void broadcast([ByRef] PartiallySignedTransaction psbt);
|
||||
|
||||
[Throws=BdkError]
|
||||
u32 get_height();
|
||||
|
||||
[Throws=BdkError]
|
||||
string get_block_hash(u32 height);
|
||||
};
|
||||
|
||||
callback interface Progress {
|
||||
void update(f32 progress, string? message);
|
||||
};
|
||||
|
||||
dictionary OutPoint {
|
||||
string txid;
|
||||
u32 vout;
|
||||
};
|
||||
|
||||
dictionary TxOut {
|
||||
u64 value;
|
||||
string address;
|
||||
};
|
||||
|
||||
enum KeychainKind {
|
||||
"External",
|
||||
"Internal",
|
||||
};
|
||||
|
||||
dictionary LocalUtxo {
|
||||
OutPoint outpoint;
|
||||
TxOut txout;
|
||||
Address address;
|
||||
KeychainKind keychain;
|
||||
boolean is_spent;
|
||||
};
|
||||
|
||||
dictionary ScriptAmount {
|
||||
Script script;
|
||||
u64 amount;
|
||||
[Enum]
|
||||
interface AddressIndex {
|
||||
New();
|
||||
LastUnused();
|
||||
Peek(u32 index);
|
||||
};
|
||||
|
||||
interface Wallet {
|
||||
[Throws=BdkError]
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network, DatabaseConfig database_config);
|
||||
[Name=new_no_persist, Throws=BdkError]
|
||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network);
|
||||
|
||||
[Throws=BdkError]
|
||||
AddressInfo get_address(AddressIndex address_index);
|
||||
|
||||
[Throws=BdkError]
|
||||
Balance get_balance();
|
||||
|
||||
[Throws=BdkError]
|
||||
boolean sign([ByRef] PartiallySignedTransaction psbt);
|
||||
|
||||
[Throws=BdkError]
|
||||
sequence<TransactionDetails> list_transactions();
|
||||
AddressInfo get_internal_address(AddressIndex address_index);
|
||||
|
||||
Network network();
|
||||
|
||||
[Throws=BdkError]
|
||||
void sync([ByRef] Blockchain blockchain, Progress? progress);
|
||||
Balance get_balance();
|
||||
|
||||
boolean is_mine(Script script);
|
||||
|
||||
[Throws=BdkError]
|
||||
sequence<LocalUtxo> list_unspent();
|
||||
};
|
||||
|
||||
interface FeeRate {
|
||||
[Name=from_sat_per_vb]
|
||||
constructor(float sat_per_vb);
|
||||
|
||||
float as_sat_per_vb();
|
||||
};
|
||||
|
||||
interface PartiallySignedTransaction {
|
||||
[Throws=BdkError]
|
||||
constructor(string psbt_base64);
|
||||
|
||||
string serialize();
|
||||
|
||||
string txid();
|
||||
|
||||
sequence<u8> extract_tx();
|
||||
void apply_update(Update update);
|
||||
|
||||
[Throws=BdkError]
|
||||
PartiallySignedTransaction combine(PartiallySignedTransaction other);
|
||||
|
||||
u64? fee_amount();
|
||||
|
||||
FeeRate? fee_rate();
|
||||
boolean sign(PartiallySignedTransaction psbt);
|
||||
};
|
||||
|
||||
dictionary TxBuilderResult {
|
||||
PartiallySignedTransaction psbt;
|
||||
TransactionDetails transaction_details;
|
||||
};
|
||||
interface Update {};
|
||||
|
||||
interface TxBuilder {
|
||||
constructor();
|
||||
|
||||
TxBuilder add_recipient(Script script, u64 amount);
|
||||
|
||||
TxBuilder set_recipients(sequence<ScriptAmount> script_amount);
|
||||
|
||||
TxBuilder add_unspendable(OutPoint unspendable);
|
||||
|
||||
TxBuilder add_utxo(OutPoint outpoint);
|
||||
|
||||
TxBuilder add_utxos(sequence<OutPoint> outpoints);
|
||||
TxBuilder change_policy(ChangeSpendPolicy change_policy);
|
||||
|
||||
TxBuilder do_not_spend_change();
|
||||
|
||||
TxBuilder manually_selected_only();
|
||||
|
||||
TxBuilder only_spend_change();
|
||||
|
||||
TxBuilder unspendable(sequence<OutPoint> unspendable);
|
||||
TxBuilder manually_selected_only();
|
||||
|
||||
TxBuilder fee_rate(float sat_per_vbyte);
|
||||
|
||||
TxBuilder fee_absolute(u64 fee_amount);
|
||||
|
||||
TxBuilder drain_wallet();
|
||||
|
||||
TxBuilder drain_to(Script script);
|
||||
|
||||
TxBuilder enable_rbf();
|
||||
|
||||
TxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
TxBuilder add_data(sequence<u8> data);
|
||||
|
||||
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
|
||||
|
||||
[Throws=BdkError]
|
||||
TxBuilderResult finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
interface BumpFeeTxBuilder {
|
||||
constructor(string txid, float new_fee_rate);
|
||||
|
||||
BumpFeeTxBuilder allow_shrinking(string address);
|
||||
|
||||
BumpFeeTxBuilder enable_rbf();
|
||||
|
||||
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
|
||||
|
||||
[Throws=BdkError]
|
||||
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - descriptor module
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface Mnemonic {
|
||||
constructor(WordCount word_count);
|
||||
|
||||
@@ -380,18 +201,107 @@ interface Descriptor {
|
||||
[Name=new_bip84_public]
|
||||
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip86]
|
||||
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
||||
|
||||
[Name=new_bip86_public]
|
||||
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
||||
|
||||
string as_string();
|
||||
|
||||
string as_string_private();
|
||||
};
|
||||
|
||||
interface Address {
|
||||
[Throws=BdkError]
|
||||
constructor(string address);
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_esplora crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
Script script_pubkey();
|
||||
interface EsploraClient {
|
||||
constructor(string url);
|
||||
|
||||
[Throws=BdkError]
|
||||
Update scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);
|
||||
|
||||
[Throws=BdkError]
|
||||
void broadcast(Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk-ffi-defined types
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
dictionary ScriptAmount {
|
||||
Script script;
|
||||
u64 amount;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk crate - bitcoin re-exports
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface Script {
|
||||
constructor(sequence<u8> raw_output_script);
|
||||
|
||||
sequence<u8> to_bytes();
|
||||
};
|
||||
|
||||
enum Network {
|
||||
"Bitcoin",
|
||||
"Testnet",
|
||||
"Signet",
|
||||
"Regtest",
|
||||
};
|
||||
|
||||
enum WordCount {
|
||||
"Words12",
|
||||
"Words15",
|
||||
"Words18",
|
||||
"Words21",
|
||||
"Words24",
|
||||
};
|
||||
|
||||
interface Address {
|
||||
[Throws=BdkError]
|
||||
constructor(string address, Network network);
|
||||
|
||||
Network network();
|
||||
|
||||
Script script_pubkey();
|
||||
|
||||
string to_qr_uri();
|
||||
|
||||
string as_string();
|
||||
};
|
||||
|
||||
interface Transaction {
|
||||
[Throws=BdkError]
|
||||
constructor(sequence<u8> transaction_bytes);
|
||||
|
||||
string txid();
|
||||
|
||||
u64 size();
|
||||
|
||||
u64 vsize();
|
||||
|
||||
boolean is_coin_base();
|
||||
|
||||
boolean is_explicitly_rbf();
|
||||
|
||||
boolean is_lock_time_enabled();
|
||||
|
||||
i32 version();
|
||||
};
|
||||
|
||||
interface PartiallySignedTransaction {
|
||||
[Throws=BdkError]
|
||||
constructor(string psbt_base64);
|
||||
|
||||
string serialize();
|
||||
|
||||
Transaction extract_tx();
|
||||
};
|
||||
|
||||
dictionary OutPoint {
|
||||
string txid;
|
||||
u32 vout;
|
||||
};
|
||||
|
||||
311
bdk-ffi/src/bitcoin.rs
Normal file
311
bdk-ffi/src/bitcoin.rs
Normal file
@@ -0,0 +1,311 @@
|
||||
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::consensus::Decodable;
|
||||
use bdk::bitcoin::network::constants::Network as BdkNetwork;
|
||||
use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
|
||||
use bdk::bitcoin::Address as BdkAddress;
|
||||
use bdk::bitcoin::OutPoint as BdkOutPoint;
|
||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||
use bdk::bitcoin::Txid;
|
||||
use bdk::Error as BdkError;
|
||||
|
||||
use std::io::Cursor;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// A Bitcoin script.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Script(pub(crate) BdkScriptBuf);
|
||||
|
||||
impl Script {
|
||||
pub fn new(raw_output_script: Vec<u8>) -> Self {
|
||||
let script: BdkScriptBuf = raw_output_script.into();
|
||||
Script(script)
|
||||
}
|
||||
|
||||
pub fn to_bytes(&self) -> Vec<u8> {
|
||||
self.0.to_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkScriptBuf> for Script {
|
||||
fn from(script: BdkScriptBuf) -> Self {
|
||||
Script(script)
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Network {
|
||||
/// Mainnet Bitcoin.
|
||||
Bitcoin,
|
||||
/// Bitcoin's testnet network.
|
||||
Testnet,
|
||||
/// Bitcoin's signet network.
|
||||
Signet,
|
||||
/// Bitcoin's regtest network.
|
||||
Regtest,
|
||||
}
|
||||
|
||||
impl From<Network> for BdkNetwork {
|
||||
fn from(network: Network) -> Self {
|
||||
match network {
|
||||
Network::Bitcoin => BdkNetwork::Bitcoin,
|
||||
Network::Testnet => BdkNetwork::Testnet,
|
||||
Network::Signet => BdkNetwork::Signet,
|
||||
Network::Regtest => BdkNetwork::Regtest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkNetwork> for Network {
|
||||
fn from(network: BdkNetwork) -> Self {
|
||||
match network {
|
||||
BdkNetwork::Bitcoin => Network::Bitcoin,
|
||||
BdkNetwork::Testnet => Network::Testnet,
|
||||
BdkNetwork::Signet => Network::Signet,
|
||||
BdkNetwork::Regtest => Network::Regtest,
|
||||
_ => panic!("Network {} not supported", network),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin address.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Address {
|
||||
inner: BdkAddress<NetworkChecked>,
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub fn new(address: String, network: Network) -> Result<Self, BdkError> {
|
||||
let parsed_address = address
|
||||
.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
|
||||
let network_checked_address = parsed_address
|
||||
.require_network(network.into())
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
|
||||
Ok(Address {
|
||||
inner: network_checked_address,
|
||||
})
|
||||
}
|
||||
|
||||
/// alternative constructor
|
||||
// fn from_script(script: Arc<Script>, network: Network) -> Result<Self, BdkError> {
|
||||
// BdkAddress::from_script(&script.inner, network)
|
||||
// .map(|a| Address { inner: a })
|
||||
// .map_err(|e| BdkError::Generic(e.to_string()))
|
||||
// }
|
||||
//
|
||||
// fn payload(&self) -> Payload {
|
||||
// match &self.inner.payload.clone() {
|
||||
// BdkPayload::PubkeyHash(pubkey_hash) => Payload::PubkeyHash {
|
||||
// pubkey_hash: pubkey_hash.to_vec(),
|
||||
// },
|
||||
// BdkPayload::ScriptHash(script_hash) => Payload::ScriptHash {
|
||||
// script_hash: script_hash.to_vec(),
|
||||
// },
|
||||
// BdkPayload::WitnessProgram { version, program } => Payload::WitnessProgram {
|
||||
// version: *version,
|
||||
// program: program.clone(),
|
||||
// },
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
self.inner.network.into()
|
||||
}
|
||||
|
||||
pub fn script_pubkey(&self) -> Arc<Script> {
|
||||
Arc::new(Script(self.inner.script_pubkey()))
|
||||
}
|
||||
|
||||
pub fn to_qr_uri(&self) -> String {
|
||||
self.inner.to_qr_uri()
|
||||
}
|
||||
|
||||
pub fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Address> for BdkAddress {
|
||||
fn from(address: Address) -> Self {
|
||||
address.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkAddress> for Address {
|
||||
fn from(address: BdkAddress) -> Self {
|
||||
Address { inner: address }
|
||||
}
|
||||
}
|
||||
|
||||
/// A Bitcoin transaction.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Transaction {
|
||||
inner: BdkTransaction,
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
|
||||
let mut decoder = Cursor::new(transaction_bytes);
|
||||
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
Ok(Transaction { inner: tx })
|
||||
}
|
||||
|
||||
pub fn txid(&self) -> String {
|
||||
self.inner.txid().to_string()
|
||||
}
|
||||
|
||||
// fn weight(&self) -> u64 {
|
||||
// self.inner.weight() as u64
|
||||
// }
|
||||
|
||||
pub fn size(&self) -> u64 {
|
||||
self.inner.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_explicitly_rbf(&self) -> bool {
|
||||
self.inner.is_explicitly_rbf()
|
||||
}
|
||||
|
||||
pub fn is_lock_time_enabled(&self) -> bool {
|
||||
self.inner.is_lock_time_enabled()
|
||||
}
|
||||
|
||||
pub fn version(&self) -> i32 {
|
||||
self.inner.version
|
||||
}
|
||||
|
||||
// fn lock_time(&self) -> u32 {
|
||||
// self.inner.lock_time.0
|
||||
// }
|
||||
|
||||
// fn input(&self) -> Vec<TxIn> {
|
||||
// self.inner.input.iter().map(|x| x.into()).collect()
|
||||
// }
|
||||
//
|
||||
// fn output(&self) -> Vec<TxOut> {
|
||||
// self.inner.output.iter().map(|x| x.into()).collect()
|
||||
// }
|
||||
}
|
||||
|
||||
impl From<BdkTransaction> for Transaction {
|
||||
fn from(tx: BdkTransaction) -> Self {
|
||||
Transaction { inner: tx }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Transaction> for BdkTransaction {
|
||||
fn from(tx: Transaction) -> Self {
|
||||
tx.inner
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PartiallySignedTransaction {
|
||||
pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
|
||||
}
|
||||
|
||||
impl PartiallySignedTransaction {
|
||||
pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
|
||||
let psbt: BdkPartiallySignedTransaction =
|
||||
BdkPartiallySignedTransaction::from_str(&psbt_base64)
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
|
||||
Ok(PartiallySignedTransaction {
|
||||
inner: Mutex::new(psbt),
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn serialize(&self) -> String {
|
||||
let psbt = self.inner.lock().unwrap().clone();
|
||||
psbt.to_string()
|
||||
}
|
||||
|
||||
// pub(crate) fn txid(&self) -> String {
|
||||
// let tx = self.inner.lock().unwrap().clone().extract_tx();
|
||||
// let txid = tx.txid();
|
||||
// txid.to_hex()
|
||||
// }
|
||||
|
||||
/// Return the transaction.
|
||||
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
|
||||
let tx = self.inner.lock().unwrap().clone().extract_tx();
|
||||
Arc::new(tx.into())
|
||||
}
|
||||
|
||||
// /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
|
||||
// ///
|
||||
// /// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
|
||||
// pub(crate) fn combine(
|
||||
// &self,
|
||||
// other: Arc<PartiallySignedTransaction>,
|
||||
// ) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||
// let other_psbt = other.inner.lock().unwrap().clone();
|
||||
// let mut original_psbt = self.inner.lock().unwrap().clone();
|
||||
//
|
||||
// original_psbt.combine(other_psbt)?;
|
||||
// Ok(Arc::new(PartiallySignedTransaction {
|
||||
// inner: Mutex::new(original_psbt),
|
||||
// }))
|
||||
// }
|
||||
|
||||
// /// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
|
||||
// /// If the PSBT is missing a TxOut for an input returns None.
|
||||
// pub(crate) fn fee_amount(&self) -> Option<u64> {
|
||||
// self.inner.lock().unwrap().fee_amount()
|
||||
// }
|
||||
|
||||
// /// The transaction's fee rate. This value will only be accurate if calculated AFTER the
|
||||
// /// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the
|
||||
// /// transaction.
|
||||
// /// If the PSBT is missing a TxOut for an input returns None.
|
||||
// pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
|
||||
// self.inner.lock().unwrap().fee_rate().map(Arc::new)
|
||||
// }
|
||||
|
||||
// /// Serialize the PSBT data structure as a String of JSON.
|
||||
// pub(crate) fn json_serialize(&self) -> String {
|
||||
// let psbt = self.inner.lock().unwrap();
|
||||
// serde_json::to_string(psbt.deref()).unwrap()
|
||||
// }
|
||||
}
|
||||
|
||||
impl From<BdkPartiallySignedTransaction> for PartiallySignedTransaction {
|
||||
fn from(psbt: BdkPartiallySignedTransaction) -> Self {
|
||||
PartiallySignedTransaction {
|
||||
inner: Mutex::new(psbt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference to a transaction output.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct OutPoint {
|
||||
/// The referenced transaction's txid.
|
||||
pub txid: String,
|
||||
/// The index of the referenced output in its transaction's vout.
|
||||
pub vout: u32,
|
||||
}
|
||||
|
||||
impl From<&OutPoint> for BdkOutPoint {
|
||||
fn from(outpoint: &OutPoint) -> Self {
|
||||
BdkOutPoint {
|
||||
txid: Txid::from_str(&outpoint.txid).unwrap(),
|
||||
vout: outpoint.vout,
|
||||
}
|
||||
}
|
||||
}
|
||||
419
bdk-ffi/src/descriptor.rs
Normal file
419
bdk-ffi/src/descriptor.rs
Normal file
@@ -0,0 +1,419 @@
|
||||
use crate::keys::DescriptorPublicKey;
|
||||
use crate::keys::DescriptorSecretKey;
|
||||
use crate::Network;
|
||||
|
||||
use bdk::bitcoin::bip32::Fingerprint;
|
||||
use bdk::bitcoin::key::Secp256k1;
|
||||
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
|
||||
use bdk::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
|
||||
use bdk::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
|
||||
use bdk::template::{
|
||||
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
|
||||
DescriptorTemplate,
|
||||
};
|
||||
use bdk::Error as BdkError;
|
||||
use bdk::KeychainKind;
|
||||
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Descriptor {
|
||||
pub extended_descriptor: ExtendedDescriptor,
|
||||
pub key_map: KeyMap,
|
||||
}
|
||||
|
||||
impl Descriptor {
|
||||
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, BdkError> {
|
||||
let secp = Secp256k1::new();
|
||||
let (extended_descriptor, key_map) =
|
||||
descriptor.into_wallet_descriptor(&secp, network.into())?;
|
||||
Ok(Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip44(
|
||||
secret_key: Arc<DescriptorSecretKey>,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) = Bip44(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip44_public(
|
||||
public_key: Arc<DescriptorPublicKey>,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip44Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip49(
|
||||
secret_key: Arc<DescriptorSecretKey>,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) = Bip49(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip49_public(
|
||||
public_key: Arc<DescriptorPublicKey>,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip49Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip84(
|
||||
secret_key: Arc<DescriptorSecretKey>,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) = Bip84(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip84_public(
|
||||
public_key: Arc<DescriptorPublicKey>,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip84Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip86(
|
||||
secret_key: Arc<DescriptorSecretKey>,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let derivable_key = &secret_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) = Bip86(derivable_key, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn new_bip86_public(
|
||||
public_key: Arc<DescriptorPublicKey>,
|
||||
fingerprint: String,
|
||||
keychain_kind: KeychainKind,
|
||||
network: Network,
|
||||
) -> Self {
|
||||
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
|
||||
let derivable_key = &public_key.inner;
|
||||
|
||||
match derivable_key {
|
||||
BdkDescriptorPublicKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derivable_key = descriptor_x_key.xkey;
|
||||
let (extended_descriptor, key_map, _) =
|
||||
Bip86Public(derivable_key, fingerprint, keychain_kind)
|
||||
.build(network.into())
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
extended_descriptor,
|
||||
key_map,
|
||||
}
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_string_private(&self) -> String {
|
||||
let descriptor = &self.extended_descriptor;
|
||||
let key_map = &self.key_map;
|
||||
descriptor.to_string_with_secret(key_map)
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.extended_descriptor.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// // crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::*;
|
||||
use assert_matches::assert_matches;
|
||||
use bdk::descriptor::DescriptorError::Key;
|
||||
use bdk::keys::KeyError::InvalidNetwork;
|
||||
|
||||
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, Arc::new(mnemonic), None)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_descriptor_templates() {
|
||||
let master: Arc<DescriptorSecretKey> = Arc::new(get_descriptor_secret_key());
|
||||
println!("Master: {:?}", master.as_string());
|
||||
// tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h
|
||||
let handmade_public_44 = master
|
||||
.derive(Arc::new(
|
||||
DerivationPath::new("m/44h/1h/0h".to_string()).unwrap(),
|
||||
))
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 44: {}", handmade_public_44.as_string());
|
||||
// Public 44: [d1d04177/44'/1'/0']tpubDCoPjomfTqh1e7o1WgGpQtARWtkueXQAepTeNpWiitS3Sdv8RKJ1yvTrGHcwjDXp2SKyMrTEca4LoN7gEUiGCWboyWe2rz99Kf4jK4m2Zmx/*
|
||||
let handmade_public_49 = master
|
||||
.derive(Arc::new(
|
||||
DerivationPath::new("m/49h/1h/0h".to_string()).unwrap(),
|
||||
))
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 49: {}", handmade_public_49.as_string());
|
||||
// Public 49: [d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/*
|
||||
let handmade_public_84 = master
|
||||
.derive(Arc::new(
|
||||
DerivationPath::new("m/84h/1h/0h".to_string()).unwrap(),
|
||||
))
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 84: {}", handmade_public_84.as_string());
|
||||
// Public 84: [d1d04177/84'/1'/0']tpubDDNxbq17egjFk2edjv8oLnzxk52zny9aAYNv9CMqTzA4mQDiQq818sEkNe9Gzmd4QU8558zftqbfoVBDQorG3E4Wq26tB2JeE4KUoahLkx6/*
|
||||
let handmade_public_86 = master
|
||||
.derive(Arc::new(
|
||||
DerivationPath::new("m/86h/1h/0h".to_string()).unwrap(),
|
||||
))
|
||||
.unwrap()
|
||||
.as_public();
|
||||
println!("Public 86: {}", handmade_public_86.as_string());
|
||||
// Public 86: [d1d04177/86'/1'/0']tpubDCJzjbcGbdEfXMWaL6QmgVmuSfXkrue7m2YNoacWwyc7a2XjXaKojRqNEbo41CFL3PyYmKdhwg2fkGpLX4SQCbQjCGxAkWHJTw9WEeenrJb/*
|
||||
let template_private_44 =
|
||||
Descriptor::new_bip44(master.clone(), KeychainKind::External, Network::Testnet);
|
||||
let template_private_49 =
|
||||
Descriptor::new_bip49(master.clone(), KeychainKind::External, Network::Testnet);
|
||||
let template_private_84 =
|
||||
Descriptor::new_bip84(master.clone(), KeychainKind::External, Network::Testnet);
|
||||
let template_private_86 =
|
||||
Descriptor::new_bip86(master, KeychainKind::External, Network::Testnet);
|
||||
// the extended public keys are the same when creating them manually as they are with the templates
|
||||
println!("Template 49: {}", template_private_49.as_string());
|
||||
println!("Template 44: {}", template_private_44.as_string());
|
||||
println!("Template 84: {}", template_private_84.as_string());
|
||||
println!("Template 86: {}", template_private_86.as_string());
|
||||
let template_public_44 = Descriptor::new_bip44_public(
|
||||
handmade_public_44,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
let template_public_49 = Descriptor::new_bip49_public(
|
||||
handmade_public_49,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
let template_public_84 = Descriptor::new_bip84_public(
|
||||
handmade_public_84,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
let template_public_86 = Descriptor::new_bip86_public(
|
||||
handmade_public_86,
|
||||
"d1d04177".to_string(),
|
||||
KeychainKind::External,
|
||||
Network::Testnet,
|
||||
);
|
||||
println!("Template public 49: {}", template_public_49.as_string());
|
||||
println!("Template public 44: {}", template_public_44.as_string());
|
||||
println!("Template public 84: {}", template_public_84.as_string());
|
||||
println!("Template public 86: {}", template_public_86.as_string());
|
||||
// when using a public key, both as_string and as_string_private return the same string
|
||||
assert_eq!(
|
||||
template_public_44.as_string_private(),
|
||||
template_public_44.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_49.as_string_private(),
|
||||
template_public_49.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_84.as_string_private(),
|
||||
template_public_84.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_public_86.as_string_private(),
|
||||
template_public_86.as_string()
|
||||
);
|
||||
// when using as_string on a private key, we get the same result as when using it on a public key
|
||||
assert_eq!(
|
||||
template_private_44.as_string(),
|
||||
template_public_44.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_49.as_string(),
|
||||
template_public_49.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_84.as_string(),
|
||||
template_public_84.as_string()
|
||||
);
|
||||
assert_eq!(
|
||||
template_private_86.as_string(),
|
||||
template_public_86.as_string()
|
||||
);
|
||||
}
|
||||
#[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
|
||||
assert!(descriptor1.is_ok());
|
||||
assert_matches!(
|
||||
descriptor2.unwrap_err(),
|
||||
bdk::Error::Descriptor(Key(InvalidNetwork))
|
||||
)
|
||||
}
|
||||
}
|
||||
68
bdk-ffi/src/esplora.rs
Normal file
68
bdk-ffi/src/esplora.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use crate::wallet::{Update, Wallet};
|
||||
use std::ops::Deref;
|
||||
|
||||
use bdk::wallet::Update as BdkUpdate;
|
||||
use bdk::Error as BdkError;
|
||||
use bdk_esplora::esplora_client::{BlockingClient, Builder};
|
||||
use bdk_esplora::EsploraExt;
|
||||
|
||||
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();
|
||||
Self(client)
|
||||
}
|
||||
|
||||
// This is a temporary solution for scanning. The long-term solution involves not passing
|
||||
// the wallet to the client at all.
|
||||
pub fn scan(
|
||||
&self,
|
||||
wallet: Arc<Wallet>,
|
||||
stop_gap: u64,
|
||||
parallel_requests: u64,
|
||||
) -> Result<Arc<Update>, BdkError> {
|
||||
let wallet = wallet.get_wallet();
|
||||
|
||||
let previous_tip = wallet.latest_checkpoint();
|
||||
let keychain_spks = wallet.spks_of_all_keychains().into_iter().collect();
|
||||
|
||||
let (update_graph, last_active_indices) = self
|
||||
.0
|
||||
.scan_txs_with_keychains(
|
||||
keychain_spks,
|
||||
None,
|
||||
None,
|
||||
stop_gap as usize,
|
||||
parallel_requests as usize,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let missing_heights = update_graph.missing_heights(wallet.local_chain());
|
||||
let chain_update = self
|
||||
.0
|
||||
.update_local_chain(previous_tip, missing_heights)
|
||||
.unwrap();
|
||||
|
||||
let update = BdkUpdate {
|
||||
last_active_indices,
|
||||
graph: update_graph,
|
||||
chain: Some(chain_update),
|
||||
};
|
||||
|
||||
Ok(Arc::new(Update(update)))
|
||||
}
|
||||
|
||||
// pub fn sync();
|
||||
|
||||
pub fn broadcast(&self, transaction: Arc<crate::bitcoin::Transaction>) -> Result<(), BdkError> {
|
||||
let bdk_transaction: bdk::bitcoin::Transaction = transaction.deref().clone().into();
|
||||
self.0
|
||||
.broadcast(&bdk_transaction)
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
// pub fn estimate_fee();
|
||||
}
|
||||
398
bdk-ffi/src/keys.rs
Normal file
398
bdk-ffi/src/keys.rs
Normal file
@@ -0,0 +1,398 @@
|
||||
use crate::Network;
|
||||
|
||||
use bdk::bitcoin::bip32::DerivationPath as BdkDerivationPath;
|
||||
use bdk::bitcoin::key::Secp256k1;
|
||||
use bdk::bitcoin::secp256k1::rand;
|
||||
use bdk::bitcoin::secp256k1::rand::Rng;
|
||||
use bdk::keys::bip39::WordCount;
|
||||
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic};
|
||||
use bdk::keys::{
|
||||
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
|
||||
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
|
||||
};
|
||||
use bdk::miniscript::descriptor::{DescriptorXKey, Wildcard};
|
||||
use bdk::miniscript::BareCtx;
|
||||
use bdk::Error as BdkError;
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// Mnemonic phrases are a human-readable version of the private keys.
|
||||
/// Supported number of words are 12, 15, 18, 21 and 24.
|
||||
pub(crate) struct Mnemonic {
|
||||
inner: BdkMnemonic,
|
||||
}
|
||||
|
||||
impl Mnemonic {
|
||||
/// Generates Mnemonic with a random entropy
|
||||
pub(crate) fn new(word_count: WordCount) -> Self {
|
||||
// TODO 4: I DON'T KNOW IF THIS IS A DECENT WAY TO GENERATE ENTROPY PLEASE CONFIRM
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut entropy = [0u8; 32];
|
||||
rng.fill(&mut entropy);
|
||||
|
||||
let generated_key: GeneratedKey<_, BareCtx> =
|
||||
BdkMnemonic::generate_with_entropy((word_count, Language::English), entropy).unwrap();
|
||||
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
|
||||
Mnemonic { inner: mnemonic }
|
||||
}
|
||||
|
||||
/// Parse a Mnemonic with given string
|
||||
pub(crate) fn from_string(mnemonic: String) -> Result<Self, BdkError> {
|
||||
BdkMnemonic::from_str(&mnemonic)
|
||||
.map(|m| Mnemonic { inner: m })
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
/// Create a new Mnemonic in the specified language from the given entropy.
|
||||
/// Entropy must be a multiple of 32 bits (4 bytes) and 128-256 bits in length.
|
||||
pub(crate) fn from_entropy(entropy: Vec<u8>) -> Result<Self, BdkError> {
|
||||
BdkMnemonic::from_entropy(entropy.as_slice())
|
||||
.map(|m| Mnemonic { inner: m })
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
/// Returns Mnemonic as string
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DerivationPath {
|
||||
inner_mutex: Mutex<BdkDerivationPath>,
|
||||
}
|
||||
|
||||
impl DerivationPath {
|
||||
pub(crate) fn new(path: String) -> Result<Self, BdkError> {
|
||||
BdkDerivationPath::from_str(&path)
|
||||
.map(|x| DerivationPath {
|
||||
inner_mutex: Mutex::new(x),
|
||||
})
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorSecretKey {
|
||||
pub(crate) inner: BdkDescriptorSecretKey,
|
||||
}
|
||||
|
||||
impl DescriptorSecretKey {
|
||||
pub(crate) fn new(network: Network, mnemonic: Arc<Mnemonic>, password: Option<String>) -> Self {
|
||||
let mnemonic = mnemonic.inner.clone();
|
||||
let xkey: ExtendedKey = (mnemonic, password).into_extended_key().unwrap();
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: None,
|
||||
xkey: xkey.into_xprv(network.into()).unwrap(),
|
||||
derivation_path: BdkDerivationPath::master(),
|
||||
wildcard: Wildcard::Unhardened,
|
||||
});
|
||||
Self {
|
||||
inner: descriptor_secret_key,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_string(private_key: String) -> Result<Self, BdkError> {
|
||||
let descriptor_secret_key = BdkDescriptorSecretKey::from_str(private_key.as_str())
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
Ok(Self {
|
||||
inner: descriptor_secret_key,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_secret_key = &self.inner;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
|
||||
None => (descriptor_x_key.xkey.fingerprint(&secp), path),
|
||||
};
|
||||
let derived_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: Some(key_source),
|
||||
xkey: derived_xprv,
|
||||
derivation_path: BdkDerivationPath::default(),
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: derived_descriptor_secret_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let descriptor_secret_key = &self.inner;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_secret_key {
|
||||
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
|
||||
origin: descriptor_x_key.origin.clone(),
|
||||
xkey: descriptor_x_key.xkey,
|
||||
derivation_path: extended_path,
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: extended_descriptor_secret_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi key".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_public(&self) -> Arc<DescriptorPublicKey> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = self.inner.to_public(&secp).unwrap();
|
||||
Arc::new(DescriptorPublicKey {
|
||||
inner: descriptor_public_key,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the private key as bytes.
|
||||
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
|
||||
let inner = &self.inner;
|
||||
let secret_bytes: Vec<u8> = match inner {
|
||||
BdkDescriptorSecretKey::Single(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
|
||||
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
|
||||
}
|
||||
BdkDescriptorSecretKey::MultiXPrv(_) => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
|
||||
secret_bytes
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DescriptorPublicKey {
|
||||
pub(crate) inner: BdkDescriptorPublicKey,
|
||||
}
|
||||
|
||||
impl DescriptorPublicKey {
|
||||
pub(crate) fn from_string(public_key: String) -> Result<Self, BdkError> {
|
||||
let descriptor_public_key = BdkDescriptorPublicKey::from_str(public_key.as_str())
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
Ok(Self {
|
||||
inner: descriptor_public_key,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let secp = Secp256k1::new();
|
||||
let descriptor_public_key = &self.inner;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
|
||||
match descriptor_public_key {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?;
|
||||
let key_source = match descriptor_x_key.origin.clone() {
|
||||
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
|
||||
None => (descriptor_x_key.xkey.fingerprint(), path),
|
||||
};
|
||||
let derived_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
||||
origin: Some(key_source),
|
||||
xkey: derived_xpub,
|
||||
derivation_path: BdkDerivationPath::default(),
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: derived_descriptor_public_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi xpub".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn extend(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
|
||||
let descriptor_public_key = &self.inner;
|
||||
let path = path.inner_mutex.lock().unwrap().deref().clone();
|
||||
match descriptor_public_key {
|
||||
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
|
||||
"Cannot extend from a single key".to_string(),
|
||||
)),
|
||||
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
|
||||
let extended_path = descriptor_x_key.derivation_path.extend(path);
|
||||
let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
|
||||
origin: descriptor_x_key.origin.clone(),
|
||||
xkey: descriptor_x_key.xkey,
|
||||
derivation_path: extended_path,
|
||||
wildcard: descriptor_x_key.wildcard,
|
||||
});
|
||||
Ok(Arc::new(Self {
|
||||
inner: extended_descriptor_public_key,
|
||||
}))
|
||||
}
|
||||
BdkDescriptorPublicKey::MultiXPub(_) => Err(BdkError::Generic(
|
||||
"Cannot derive from a multi xpub".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn as_string(&self) -> String {
|
||||
self.inner.to_string()
|
||||
}
|
||||
}
|
||||
//
|
||||
// // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// // crate.
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
|
||||
use crate::BdkError;
|
||||
// use bdk::bitcoin::hashes::hex::ToHex;
|
||||
use bdk::bitcoin::Network;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn get_inner() -> 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.into(), Arc::new(mnemonic), None)
|
||||
}
|
||||
|
||||
fn derive_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.derive(path)
|
||||
}
|
||||
|
||||
fn extend_dsk(
|
||||
key: &DescriptorSecretKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorSecretKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.extend(path)
|
||||
}
|
||||
|
||||
fn derive_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.derive(path)
|
||||
}
|
||||
|
||||
fn extend_dpk(
|
||||
key: &DescriptorPublicKey,
|
||||
path: &str,
|
||||
) -> Result<Arc<DescriptorPublicKey>, BdkError> {
|
||||
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
|
||||
key.extend(path)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generate_descriptor_secret_key() {
|
||||
let master_dsk = get_inner();
|
||||
assert_eq!(master_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*");
|
||||
assert_eq!(master_dsk.as_public().as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_self() {
|
||||
let master_dsk = get_inner();
|
||||
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m").unwrap();
|
||||
assert_eq!(derived_dsk.as_string(), "[d1d04177]tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*");
|
||||
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
|
||||
let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m").unwrap();
|
||||
assert_eq!(derived_dpk.as_string(), "[d1d04177]tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_descriptors_keys() {
|
||||
let master_dsk = get_inner();
|
||||
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
|
||||
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
|
||||
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
|
||||
let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m/0").unwrap();
|
||||
assert_eq!(derived_dpk.as_string(), "[d1d04177/0]tpubD9oaCiP1MPmQdndm7DCD3D3QU34pWd6BbKSRedoZF1UJcNhEk3PJwkALNYkhxeTKL29oGNR7psqvT1KZydCGqUDEKXN6dVQJY2R8ooLPy8m/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extend_descriptor_keys() {
|
||||
let master_dsk = get_inner();
|
||||
let extended_dsk: &DescriptorSecretKey = &extend_dsk(&master_dsk, "m/0").unwrap();
|
||||
assert_eq!(extended_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/0/*");
|
||||
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
|
||||
let extended_dpk: &DescriptorPublicKey = &extend_dpk(master_dpk, "m/0").unwrap();
|
||||
assert_eq!(extended_dpk.as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/0/*");
|
||||
let wif = "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch";
|
||||
let extended_key = DescriptorSecretKey::from_string(wif.to_string()).unwrap();
|
||||
let result = extended_key.derive(Arc::new(DerivationPath::new("m/0".to_string()).unwrap()));
|
||||
dbg!(&result);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_str_inner() {
|
||||
let key1 = "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch";
|
||||
let key2 = "tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1/1/1/*";
|
||||
let private_descriptor_key1 = DescriptorSecretKey::from_string(key1.to_string()).unwrap();
|
||||
let private_descriptor_key2 = DescriptorSecretKey::from_string(key2.to_string()).unwrap();
|
||||
dbg!(private_descriptor_key1);
|
||||
dbg!(private_descriptor_key2);
|
||||
// Should error out because you can't produce a DescriptorSecretKey from an xpub
|
||||
let key0 = "tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi";
|
||||
assert!(DescriptorSecretKey::from_string(key0.to_string()).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_and_extend_inner() {
|
||||
let master_dsk = get_inner();
|
||||
// derive DescriptorSecretKey with path "m/0" from master
|
||||
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
|
||||
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
|
||||
// extend derived_dsk with path "m/0"
|
||||
let extended_dsk: &DescriptorSecretKey = &extend_dsk(derived_dsk, "m/0").unwrap();
|
||||
assert_eq!(extended_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/0/*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derive_hardened_path_using_public() {
|
||||
let master_dpk = get_inner().as_public();
|
||||
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
|
||||
assert!(derived_dpk.is_err());
|
||||
}
|
||||
|
||||
// TODO 7: It appears that the to_hex() method is not available anymore.
|
||||
// Look into the correct way to pull the hex out of the DescriptorSecretKey.
|
||||
// Note: ToHex was removed in bitcoin_hashes 0.12.0
|
||||
// #[test]
|
||||
// fn test_retrieve_master_secret_key() {
|
||||
// let master_dpk = get_inner();
|
||||
// let master_private_key = master_dpk.secret_bytes().to_hex();
|
||||
// assert_eq!(
|
||||
// master_private_key,
|
||||
// "e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
|
||||
// )
|
||||
// }
|
||||
}
|
||||
2011
bdk-ffi/src/lib.rs
2011
bdk-ffi/src/lib.rs
File diff suppressed because it is too large
Load Diff
910
bdk-ffi/src/wallet.rs
Normal file
910
bdk-ffi/src/wallet.rs
Normal file
@@ -0,0 +1,910 @@
|
||||
use crate::bitcoin::{OutPoint, PartiallySignedTransaction};
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::{AddressIndex, AddressInfo, Network, ScriptAmount};
|
||||
use crate::{Balance, Script};
|
||||
use std::collections::HashSet;
|
||||
|
||||
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
|
||||
use bdk::bitcoin::OutPoint as BdkOutPoint;
|
||||
use bdk::wallet::Update as BdkUpdate;
|
||||
use bdk::{Error as BdkError, FeeRate};
|
||||
use bdk::{SignOptions, Wallet as BdkWallet};
|
||||
|
||||
use bdk::wallet::tx_builder::ChangeSpendPolicy;
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Wallet {
|
||||
// TODO 8: Do we really need the mutex on the wallet? Could this be an Arc?
|
||||
inner_mutex: Mutex<BdkWallet>,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
pub fn new_no_persist(
|
||||
descriptor: Arc<Descriptor>,
|
||||
change_descriptor: Option<Arc<Descriptor>>,
|
||||
network: Network,
|
||||
) -> Result<Self, BdkError> {
|
||||
let descriptor = descriptor.as_string_private();
|
||||
let change_descriptor = change_descriptor.map(|d| d.as_string_private());
|
||||
|
||||
let wallet =
|
||||
BdkWallet::new_no_persist(&descriptor, change_descriptor.as_ref(), network.into())?;
|
||||
|
||||
Ok(Wallet {
|
||||
inner_mutex: Mutex::new(wallet),
|
||||
})
|
||||
}
|
||||
|
||||
// TODO 10: Do we need this mutex
|
||||
pub(crate) fn get_wallet(&self) -> MutexGuard<BdkWallet> {
|
||||
self.inner_mutex.lock().expect("wallet")
|
||||
}
|
||||
|
||||
pub fn get_address(&self, address_index: AddressIndex) -> AddressInfo {
|
||||
self.get_wallet().get_address(address_index.into()).into()
|
||||
}
|
||||
|
||||
pub fn network(&self) -> Network {
|
||||
self.get_wallet().network().into()
|
||||
}
|
||||
|
||||
pub fn get_internal_address(&self, address_index: AddressIndex) -> AddressInfo {
|
||||
self.get_wallet()
|
||||
.get_internal_address(address_index.into())
|
||||
.into()
|
||||
}
|
||||
|
||||
// TODO 16: Why is the Arc required here?
|
||||
pub fn get_balance(&self) -> Arc<Balance> {
|
||||
let bdk_balance = self.get_wallet().get_balance();
|
||||
let balance = Balance { inner: bdk_balance };
|
||||
Arc::new(balance)
|
||||
}
|
||||
|
||||
pub fn apply_update(&self, update: Arc<Update>) -> Result<(), BdkError> {
|
||||
self.get_wallet()
|
||||
.apply_update(update.0.clone())
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
|
||||
pub fn is_mine(&self, script: Arc<Script>) -> bool {
|
||||
// TODO: Both of the following lines work. Which is better?
|
||||
self.get_wallet().is_mine(&script.0)
|
||||
// self.get_wallet().is_mine(script.0.clone().as_script())
|
||||
}
|
||||
|
||||
/// Sign a transaction with all the wallet's signers, in the order specified by every signer's
|
||||
/// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that
|
||||
/// has the value true if the PSBT was finalized, or false otherwise.
|
||||
///
|
||||
/// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
|
||||
/// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
|
||||
/// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
|
||||
/// in this library will.
|
||||
pub(crate) fn sign(
|
||||
&self,
|
||||
psbt: Arc<PartiallySignedTransaction>,
|
||||
// sign_options: Option<SignOptions>,
|
||||
) -> Result<bool, BdkError> {
|
||||
let mut psbt = psbt.inner.lock().unwrap();
|
||||
self.get_wallet()
|
||||
.sign(&mut psbt, SignOptions::default())
|
||||
.map_err(|e| BdkError::Generic(e.to_string()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Update(pub(crate) BdkUpdate);
|
||||
|
||||
// /// A Bitcoin wallet.
|
||||
// /// The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
|
||||
// /// 1. Output descriptors from which it can derive addresses.
|
||||
// /// 2. A Database where it tracks transactions and utxos related to the descriptors.
|
||||
// /// 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
|
||||
// impl Wallet {
|
||||
// pub fn new(
|
||||
// descriptor: Arc<Descriptor>,
|
||||
// change_descriptor: Option<Arc<Descriptor>>,
|
||||
// network: Network,
|
||||
// ) -> Result<Self, BdkError> {
|
||||
// let wallet = BdkWallet::new_no_persist()?;
|
||||
// Ok(Wallet {
|
||||
// inner: wallet,
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// Return whether or not a script is part of this wallet (either internal or external).
|
||||
// pub(crate) fn is_mine(&self, script: Arc<Script>) -> bool {
|
||||
// self.inner.is_mine(&script.inner)
|
||||
// }
|
||||
//
|
||||
// /// Sync the internal database with the blockchain.
|
||||
// // pub(crate) fn sync(
|
||||
// // &self,
|
||||
// // blockchain: &Blockchain,
|
||||
// // progress: Option<Box<dyn Progress>>,
|
||||
// // ) -> Result<(), BdkError> {
|
||||
// // let bdk_sync_opts = BdkSyncOptions {
|
||||
// // progress: progress.map(|p| {
|
||||
// // Box::new(ProgressHolder { progress: p })
|
||||
// // as Box<(dyn bdk::blockchain::Progress + 'static)>
|
||||
// // }),
|
||||
// // };
|
||||
// //
|
||||
// // let blockchain = blockchain.get_blockchain();
|
||||
// // self.get_wallet().sync(blockchain.deref(), bdk_sync_opts)
|
||||
// // }
|
||||
//
|
||||
// /// Return a derived address using the external descriptor, see AddressIndex for available address index selection
|
||||
// /// strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end with a * character)
|
||||
// /// then the same address will always be returned for any AddressIndex.
|
||||
// /// MIGRATION 1.0: The wallet needs to be mutated for this method to work... does that mean I should bring back the Mutex?
|
||||
// /// Is this thread-safe?
|
||||
// pub(crate) fn get_address(&mut self, address_index: AddressIndex) -> AddressInfo {
|
||||
// AddressInfo::from(self.inner.get_address(address_index.into()))
|
||||
// }
|
||||
//
|
||||
// /// Return a derived address using the internal (change) descriptor.
|
||||
// ///
|
||||
// /// If the wallet doesn't have an internal descriptor it will use the external descriptor.
|
||||
// ///
|
||||
// /// see [`AddressIndex`] for available address index selection strategies. If none of the keys
|
||||
// /// in the descriptor are derivable (i.e. does not end with /*) then the same address will always
|
||||
// /// be returned for any [`AddressIndex`].
|
||||
// pub(crate) fn get_internal_address(&mut self, address_index: AddressIndex, ) -> AddressInfo {
|
||||
// AddressInfo::from(self.inner.get_internal_address(address_index.into()))
|
||||
// }
|
||||
//
|
||||
// /// Return the balance, meaning the sum of this wallet’s unspent outputs’ values. Note that this method only operates
|
||||
// /// on the internal database, which first needs to be Wallet.sync manually.
|
||||
// pub(crate) fn get_balance(&self) -> Balance {
|
||||
// Balance::from(self.inner.get_balance())
|
||||
// }
|
||||
//
|
||||
// /// Sign a transaction with all the wallet's signers, in the order specified by every signer's
|
||||
// /// [`SignerOrdering`]. This function returns the `Result` type with an encapsulated `bool` that
|
||||
// /// has the value true if the PSBT was finalized, or false otherwise.
|
||||
// ///
|
||||
// /// The [`SignOptions`] can be used to tweak the behavior of the software signers, and the way
|
||||
// /// the transaction is finalized at the end. Note that it can't be guaranteed that *every*
|
||||
// /// signers will follow the options, but the "software signers" (WIF keys and `xprv`) defined
|
||||
// /// in this library will.
|
||||
// pub(crate) fn sign(
|
||||
// &self,
|
||||
// psbt: &PartiallySignedTransaction,
|
||||
// sign_options: Option<SignOptions>,
|
||||
// ) -> Result<bool, BdkError> {
|
||||
// let mut psbt = psbt.inner.lock().unwrap();
|
||||
// self.inner.sign(
|
||||
// &mut psbt,
|
||||
// sign_options.map(SignOptions::into).unwrap_or_default(),
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// /// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
|
||||
// pub(crate) fn list_transactions(
|
||||
// &self,
|
||||
// include_raw: bool,
|
||||
// ) -> Result<Vec<TransactionDetails>, BdkError> {
|
||||
// let transaction_details = self.inner.list_transactions(include_raw)?;
|
||||
// Ok(transaction_details
|
||||
// .into_iter()
|
||||
// .map(TransactionDetails::from)
|
||||
// .collect())
|
||||
// }
|
||||
//
|
||||
// /// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
|
||||
// /// which first needs to be Wallet.sync manually.
|
||||
// pub(crate) fn list_unspent(&self) -> Result<Vec<LocalUtxo>, BdkError> {
|
||||
// let unspents: Vec<BdkLocalUtxo> = self.inner.list_unspent()?;
|
||||
// Ok(unspents.into_iter().map(LocalUtxo::from).collect())
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Options for a software signer
|
||||
// ///
|
||||
// /// Adjust the behavior of our software signers and the way a transaction is finalized
|
||||
// #[derive(Debug, Clone, Default)]
|
||||
// pub struct SignOptions {
|
||||
// /// Whether the signer should trust the `witness_utxo`, if the `non_witness_utxo` hasn't been
|
||||
// /// provided
|
||||
// ///
|
||||
// /// Defaults to `false` to mitigate the "SegWit bug" which should trick the wallet into
|
||||
// /// paying a fee larger than expected.
|
||||
// ///
|
||||
// /// Some wallets, especially if relatively old, might not provide the `non_witness_utxo` for
|
||||
// /// SegWit transactions in the PSBT they generate: in those cases setting this to `true`
|
||||
// /// should correctly produce a signature, at the expense of an increased trust in the creator
|
||||
// /// of the PSBT.
|
||||
// ///
|
||||
// /// For more details see: <https://blog.trezor.io/details-of-firmware-updates-for-trezor-one-version-1-9-1-and-trezor-model-t-version-2-3-1-1eba8f60f2dd>
|
||||
// pub trust_witness_utxo: bool,
|
||||
//
|
||||
// /// Whether the wallet should assume a specific height has been reached when trying to finalize
|
||||
// /// a transaction
|
||||
// ///
|
||||
// /// The wallet will only "use" a timelock to satisfy the spending policy of an input if the
|
||||
// /// timelock height has already been reached. This option allows overriding the "current height" to let the
|
||||
// /// wallet use timelocks in the future to spend a coin.
|
||||
// pub assume_height: Option<u32>,
|
||||
//
|
||||
// /// Whether the signer should use the `sighash_type` set in the PSBT when signing, no matter
|
||||
// /// what its value is
|
||||
// ///
|
||||
// /// Defaults to `false` which will only allow signing using `SIGHASH_ALL`.
|
||||
// pub allow_all_sighashes: bool,
|
||||
//
|
||||
// /// Whether to remove partial signatures from the PSBT inputs while finalizing PSBT.
|
||||
// ///
|
||||
// /// Defaults to `true` which will remove partial signatures during finalization.
|
||||
// pub remove_partial_sigs: bool,
|
||||
//
|
||||
// /// Whether to try finalizing the PSBT after the inputs are signed.
|
||||
// ///
|
||||
// /// Defaults to `true` which will try finalizing PSBT after inputs are signed.
|
||||
// pub try_finalize: bool,
|
||||
//
|
||||
// // Specifies which Taproot script-spend leaves we should sign for. This option is
|
||||
// // ignored if we're signing a non-taproot PSBT.
|
||||
// //
|
||||
// // Defaults to All, i.e., the wallet will sign all the leaves it has a key for.
|
||||
// // TODO pub tap_leaves_options: TapLeavesOptions,
|
||||
// /// Whether we should try to sign a taproot transaction with the taproot internal key
|
||||
// /// or not. This option is ignored if we're signing a non-taproot PSBT.
|
||||
// ///
|
||||
// /// Defaults to `true`, i.e., we always try to sign with the taproot internal key.
|
||||
// pub sign_with_tap_internal_key: bool,
|
||||
//
|
||||
// /// Whether we should grind ECDSA signature to ensure signing with low r
|
||||
// /// or not.
|
||||
// /// Defaults to `true`, i.e., we always grind ECDSA signature to sign with low r.
|
||||
// pub allow_grinding: bool,
|
||||
// }
|
||||
//
|
||||
// impl From<SignOptions> for BdkSignOptions {
|
||||
// fn from(sign_options: SignOptions) -> Self {
|
||||
// BdkSignOptions {
|
||||
// trust_witness_utxo: sign_options.trust_witness_utxo,
|
||||
// assume_height: sign_options.assume_height,
|
||||
// allow_all_sighashes: sign_options.allow_all_sighashes,
|
||||
// remove_partial_sigs: sign_options.remove_partial_sigs,
|
||||
// try_finalize: sign_options.try_finalize,
|
||||
// tap_leaves_options: Default::default(),
|
||||
// sign_with_tap_internal_key: sign_options.sign_with_tap_internal_key,
|
||||
// allow_grinding: sign_options.allow_grinding,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
/// A transaction builder.
|
||||
/// After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction.
|
||||
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TxBuilder {
|
||||
pub(crate) recipients: Vec<(BdkScriptBuf, u64)>,
|
||||
pub(crate) utxos: Vec<OutPoint>,
|
||||
pub(crate) unspendable: HashSet<OutPoint>,
|
||||
pub(crate) change_policy: ChangeSpendPolicy,
|
||||
pub(crate) manually_selected_only: bool,
|
||||
pub(crate) fee_rate: Option<f32>,
|
||||
// pub(crate) fee_absolute: Option<u64>,
|
||||
pub(crate) drain_wallet: bool,
|
||||
// pub(crate) drain_to: Option<BdkScript>,
|
||||
// pub(crate) rbf: Option<RbfValue>,
|
||||
// pub(crate) data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TxBuilder {
|
||||
pub(crate) fn new() -> Self {
|
||||
TxBuilder {
|
||||
recipients: Vec::new(),
|
||||
utxos: Vec::new(),
|
||||
unspendable: HashSet::new(),
|
||||
change_policy: ChangeSpendPolicy::ChangeAllowed,
|
||||
manually_selected_only: false,
|
||||
fee_rate: None,
|
||||
// fee_absolute: None,
|
||||
drain_wallet: false,
|
||||
// drain_to: None,
|
||||
// rbf: None,
|
||||
// data: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a recipient to the internal list.
|
||||
pub(crate) fn add_recipient(&self, script: Arc<Script>, amount: u64) -> Arc<Self> {
|
||||
let mut recipients: Vec<(BdkScriptBuf, u64)> = self.recipients.clone();
|
||||
recipients.append(&mut vec![(script.0.clone(), amount)]);
|
||||
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
|
||||
let recipients = recipients
|
||||
.iter()
|
||||
.map(|script_amount| (script_amount.script.0.clone(), script_amount.amount))
|
||||
.collect();
|
||||
Arc::new(TxBuilder {
|
||||
recipients,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Add a utxo to the internal list of unspendable utxos. It’s important to note that the "must-be-spent"
|
||||
/// utxos added with [TxBuilder.addUtxo] have priority over this. See the Rust docs of the two linked methods for more details.
|
||||
pub(crate) fn add_unspendable(&self, unspendable: OutPoint) -> Arc<Self> {
|
||||
let mut unspendable_hash_set = self.unspendable.clone();
|
||||
unspendable_hash_set.insert(unspendable);
|
||||
Arc::new(TxBuilder {
|
||||
unspendable: unspendable_hash_set,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable"
|
||||
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
|
||||
pub(crate) fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
|
||||
self.add_utxos(vec![outpoint])
|
||||
}
|
||||
|
||||
/// Add the list of outpoints to the internal list of UTXOs that must be spent. If an error occurs while adding
|
||||
/// any of the UTXOs then none of them are added and the error is returned. These have priority over the "unspendable"
|
||||
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
|
||||
pub(crate) fn add_utxos(&self, mut outpoints: Vec<OutPoint>) -> Arc<Self> {
|
||||
let mut utxos = self.utxos.to_vec();
|
||||
utxos.append(&mut outpoints);
|
||||
Arc::new(TxBuilder {
|
||||
utxos,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn change_policy(&self, change_policy: ChangeSpendPolicy) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See TxBuilder.unspendable.
|
||||
pub(crate) fn do_not_spend_change(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy: ChangeSpendPolicy::ChangeForbidden,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See TxBuilder.unspendable.
|
||||
pub(crate) fn only_spend_change(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
change_policy: ChangeSpendPolicy::OnlyChange,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are
|
||||
/// needed to make the transaction valid.
|
||||
pub(crate) fn manually_selected_only(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
manually_selected_only: true,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
// /// Replace the internal list of unspendable utxos with a new list. It’s important to note that the "must-be-spent" utxos added with
|
||||
// /// TxBuilder.addUtxo have priority over these. See the Rust docs of the two linked methods for more details.
|
||||
// pub(crate) fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
|
||||
// Arc::new(TxBuilder {
|
||||
// unspendable: unspendable.into_iter().collect(),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
|
||||
/// Set a custom fee rate.
|
||||
pub(crate) fn fee_rate(&self, sat_per_vb: f32) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
fee_rate: Some(sat_per_vb),
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
// /// Set an absolute fee.
|
||||
// pub(crate) fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> {
|
||||
// Arc::new(TxBuilder {
|
||||
// fee_absolute: Some(fee_amount),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
|
||||
/// Spend all the available inputs. This respects filters like TxBuilder.unspendable and the change policy.
|
||||
pub(crate) fn drain_wallet(&self) -> Arc<Self> {
|
||||
Arc::new(TxBuilder {
|
||||
drain_wallet: true,
|
||||
..self.clone()
|
||||
})
|
||||
}
|
||||
|
||||
// /// Sets the address to drain excess coins to. Usually, when there are excess coins they are sent to a change address
|
||||
// /// generated by the wallet. This option replaces the usual change address with an arbitrary ScriptPubKey of your choosing.
|
||||
// /// Just as with a change output, if the drain output is not needed (the excess coins are too small) it will not be included
|
||||
// /// in the resulting transaction. The only difference is that it is valid to use drain_to without setting any ordinary recipients
|
||||
// /// with add_recipient (but it is perfectly fine to add recipients as well). If you choose not to set any recipients, you should
|
||||
// /// either provide the utxos that the transaction should spend via add_utxos, or set drain_wallet to spend all of them.
|
||||
// /// When bumping the fees of a transaction made with this option, you probably want to use BumpFeeTxBuilder.allow_shrinking
|
||||
// /// to allow this output to be reduced to pay for the extra fees.
|
||||
// pub(crate) fn drain_to(&self, script: Arc<Script>) -> Arc<Self> {
|
||||
// Arc::new(TxBuilder {
|
||||
// drain_to: Some(script.inner.clone()),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// /// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
|
||||
// pub(crate) fn enable_rbf(&self) -> Arc<Self> {
|
||||
// Arc::new(TxBuilder {
|
||||
// rbf: Some(RbfValue::Default),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// /// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
|
||||
// /// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
|
||||
// /// an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
// pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
|
||||
// Arc::new(TxBuilder {
|
||||
// rbf: Some(RbfValue::Value(nsequence)),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
|
||||
/// Add data as an output using OP_RETURN.
|
||||
// pub(crate) fn add_data(&self, data: Vec<u8>) -> Arc<Self> {
|
||||
// Arc::new(TxBuilder {
|
||||
// data,
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
|
||||
/// Finish building the transaction. Returns the BIP174 PSBT.
|
||||
pub(crate) fn finish(
|
||||
&self,
|
||||
wallet: &Wallet,
|
||||
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||
// 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();
|
||||
for (script, amount) in &self.recipients {
|
||||
tx_builder.add_recipient(script.clone(), *amount);
|
||||
}
|
||||
tx_builder.change_policy(self.change_policy);
|
||||
if !self.utxos.is_empty() {
|
||||
let bdk_utxos: Vec<BdkOutPoint> = self.utxos.iter().map(BdkOutPoint::from).collect();
|
||||
let utxos: &[BdkOutPoint] = &bdk_utxos;
|
||||
tx_builder.add_utxos(utxos)?;
|
||||
}
|
||||
// if !self.unspendable.is_empty() {
|
||||
// let bdk_unspendable: Vec<BdkOutPoint> =
|
||||
// self.unspendable.iter().map(BdkOutPoint::from).collect();
|
||||
// tx_builder.unspendable(bdk_unspendable);
|
||||
// }
|
||||
if self.manually_selected_only {
|
||||
tx_builder.manually_selected_only();
|
||||
}
|
||||
if let Some(sat_per_vb) = self.fee_rate {
|
||||
tx_builder.fee_rate(FeeRate::from_sat_per_vb(sat_per_vb));
|
||||
}
|
||||
// if let Some(fee_amount) = self.fee_absolute {
|
||||
// tx_builder.fee_absolute(fee_amount);
|
||||
// }
|
||||
if self.drain_wallet {
|
||||
tx_builder.drain_wallet();
|
||||
}
|
||||
// if let Some(script) = &self.drain_to {
|
||||
// tx_builder.drain_to(script.clone());
|
||||
// }
|
||||
// if let Some(rbf) = &self.rbf {
|
||||
// match *rbf {
|
||||
// RbfValue::Default => {
|
||||
// tx_builder.enable_rbf();
|
||||
// }
|
||||
// RbfValue::Value(nsequence) => {
|
||||
// tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if !&self.data.is_empty() {
|
||||
// tx_builder.add_data(self.data.as_slice());
|
||||
// }
|
||||
|
||||
// tx_builder.finish().map(|psbt| psbt.serialize_hex())
|
||||
// tx_builder.finish().into()
|
||||
let psbt = tx_builder.finish()?;
|
||||
|
||||
Ok(Arc::new(psbt.into()))
|
||||
}
|
||||
}
|
||||
|
||||
// /// The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
|
||||
// #[derive(Clone)]
|
||||
// pub(crate) struct BumpFeeTxBuilder {
|
||||
// pub(crate) txid: String,
|
||||
// pub(crate) fee_rate: f32,
|
||||
// pub(crate) allow_shrinking: Option<String>,
|
||||
// pub(crate) rbf: Option<RbfValue>,
|
||||
// }
|
||||
//
|
||||
// impl BumpFeeTxBuilder {
|
||||
// pub(crate) fn new(txid: String, fee_rate: f32) -> Self {
|
||||
// Self {
|
||||
// txid,
|
||||
// fee_rate,
|
||||
// allow_shrinking: None,
|
||||
// rbf: None,
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this script_pubkey
|
||||
// /// in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output to
|
||||
// /// shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is preserved
|
||||
// /// then it is currently not guaranteed to be in the same position as it was originally. Returns an error if script_pubkey
|
||||
// /// can’t be found among the recipients of the transaction we are bumping.
|
||||
// pub(crate) fn allow_shrinking(&self, address: String) -> Arc<Self> {
|
||||
// Arc::new(Self {
|
||||
// allow_shrinking: Some(address),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// /// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
|
||||
// pub(crate) fn enable_rbf(&self) -> Arc<Self> {
|
||||
// Arc::new(Self {
|
||||
// rbf: Some(RbfValue::Default),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// /// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
|
||||
// /// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
|
||||
// /// an error will be thrown, since it would not be a valid nSequence to signal RBF.
|
||||
// pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
|
||||
// Arc::new(Self {
|
||||
// rbf: Some(RbfValue::Value(nsequence)),
|
||||
// ..self.clone()
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// /// Finish building the transaction. Returns the BIP174 PSBT.
|
||||
// pub(crate) fn finish(
|
||||
// &self,
|
||||
// wallet: &Wallet,
|
||||
// ) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
|
||||
// let wallet = wallet.get_wallet();
|
||||
// let txid = Txid::from_str(self.txid.as_str())?;
|
||||
// let mut tx_builder = wallet.build_fee_bump(txid)?;
|
||||
// tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
|
||||
// if let Some(allow_shrinking) = &self.allow_shrinking {
|
||||
// let address = BdkAddress::from_str(allow_shrinking)
|
||||
// .map_err(|e| BdkError::Generic(e.to_string()))?;
|
||||
// let script = address.script_pubkey();
|
||||
// tx_builder.allow_shrinking(script)?;
|
||||
// }
|
||||
// if let Some(rbf) = &self.rbf {
|
||||
// match *rbf {
|
||||
// RbfValue::Default => {
|
||||
// tx_builder.enable_rbf();
|
||||
// }
|
||||
// RbfValue::Value(nsequence) => {
|
||||
// tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// tx_builder
|
||||
// .finish()
|
||||
// .map(|(psbt, _)| PartiallySignedTransaction {
|
||||
// inner: Mutex::new(psbt),
|
||||
// })
|
||||
// .map(Arc::new)
|
||||
// }
|
||||
// }
|
||||
|
||||
// // The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
|
||||
// // These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
|
||||
// // crate.
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use crate::database::DatabaseConfig;
|
||||
// use crate::descriptor::Descriptor;
|
||||
// use crate::keys::{DescriptorSecretKey, Mnemonic};
|
||||
// use crate::wallet::{AddressIndex, TxBuilder, Wallet};
|
||||
// use crate::Script;
|
||||
// use assert_matches::assert_matches;
|
||||
// use bdk::bitcoin::{Address, Network};
|
||||
// // use bdk::wallet::get_funded_wallet;
|
||||
// use bdk::KeychainKind;
|
||||
// use std::str::FromStr;
|
||||
// use std::sync::{Arc, Mutex};
|
||||
//
|
||||
// // #[test]
|
||||
// // fn test_drain_wallet() {
|
||||
// // let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
|
||||
// // let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
|
||||
// // let test_wallet = Wallet {
|
||||
// // inner_mutex: Mutex::new(funded_wallet),
|
||||
// // };
|
||||
// // let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
|
||||
// // let drain_to_script = crate::Address::new(drain_to_address)
|
||||
// // .unwrap()
|
||||
// // .script_pubkey();
|
||||
// // let tx_builder = TxBuilder::new()
|
||||
// // .drain_wallet()
|
||||
// // .drain_to(drain_to_script.clone());
|
||||
// // assert!(tx_builder.drain_wallet);
|
||||
// // assert_eq!(tx_builder.drain_to, Some(drain_to_script.inner.clone()));
|
||||
// //
|
||||
// // let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
|
||||
// // let psbt = tx_builder_result.psbt.inner.lock().unwrap().clone();
|
||||
// // let tx_details = tx_builder_result.transaction_details;
|
||||
// //
|
||||
// // // confirm one input with 50,000 sats
|
||||
// // assert_eq!(psbt.inputs.len(), 1);
|
||||
// // let input_value = psbt
|
||||
// // .inputs
|
||||
// // .get(0)
|
||||
// // .cloned()
|
||||
// // .unwrap()
|
||||
// // .non_witness_utxo
|
||||
// // .unwrap()
|
||||
// // .output
|
||||
// // .get(0)
|
||||
// // .unwrap()
|
||||
// // .value;
|
||||
// // assert_eq!(input_value, 50_000_u64);
|
||||
// //
|
||||
// // // confirm one output to correct address with all sats - fee
|
||||
// // assert_eq!(psbt.outputs.len(), 1);
|
||||
// // let output_address = Address::from_script(
|
||||
// // &psbt
|
||||
// // .unsigned_tx
|
||||
// // .output
|
||||
// // .get(0)
|
||||
// // .cloned()
|
||||
// // .unwrap()
|
||||
// // .script_pubkey,
|
||||
// // Network::Testnet,
|
||||
// // )
|
||||
// // .unwrap();
|
||||
// // assert_eq!(
|
||||
// // output_address,
|
||||
// // Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap()
|
||||
// // );
|
||||
// // let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value;
|
||||
// // assert_eq!(output_value, 49_890_u64); // input - fee
|
||||
// //
|
||||
// // assert_eq!(
|
||||
// // tx_details.txid,
|
||||
// // "312f1733badab22dc26b8dcbc83ba5629fb7b493af802e8abe07d865e49629c5"
|
||||
// // );
|
||||
// // assert_eq!(tx_details.received, 0);
|
||||
// // assert_eq!(tx_details.sent, 50000);
|
||||
// // assert!(tx_details.fee.is_some());
|
||||
// // assert_eq!(tx_details.fee.unwrap(), 110);
|
||||
// // assert!(tx_details.confirmation_time.is_none());
|
||||
// // }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_peek_reset_address() {
|
||||
// let test_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
|
||||
// let descriptor = Descriptor::new(test_wpkh.to_string(), Network::Regtest).unwrap();
|
||||
// let change_descriptor = Descriptor::new(
|
||||
// test_wpkh.to_string().replace("/0/*", "/1/*"),
|
||||
// Network::Regtest,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let wallet = Wallet::new(
|
||||
// Arc::new(descriptor),
|
||||
// Some(Arc::new(change_descriptor)),
|
||||
// Network::Regtest,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::Peek { index: 2 })
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::Peek { index: 1 })
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// // new index still 0
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
|
||||
// );
|
||||
//
|
||||
// // new index now 1
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// // new index now 2
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
|
||||
// );
|
||||
//
|
||||
// // peek index 1
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::Peek { index: 1 })
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// // reset to index 0
|
||||
// // assert_eq!(
|
||||
// // wallet
|
||||
// // .get_address(AddressIndex::Reset { index: 0 })
|
||||
// // .unwrap()
|
||||
// // .address
|
||||
// // .as_string(),
|
||||
// // "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
|
||||
// // );
|
||||
//
|
||||
// // new index 1 again
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_get_address() {
|
||||
// let test_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
|
||||
// let descriptor = Descriptor::new(test_wpkh.to_string(), Network::Regtest).unwrap();
|
||||
// let change_descriptor = Descriptor::new(
|
||||
// test_wpkh.to_string().replace("/0/*", "/1/*"),
|
||||
// Network::Regtest,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let wallet = Wallet::new(
|
||||
// Arc::new(descriptor),
|
||||
// Some(Arc::new(change_descriptor)),
|
||||
// Network::Regtest,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_address(AddressIndex::LastUnused)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_internal_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qpmz73cyx00r4a5dea469j40ax6d6kqyd67nnpj"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_internal_address(AddressIndex::New)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
|
||||
// );
|
||||
//
|
||||
// assert_eq!(
|
||||
// wallet
|
||||
// .get_internal_address(AddressIndex::LastUnused)
|
||||
// .unwrap()
|
||||
// .address
|
||||
// .as_string(),
|
||||
// "bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
|
||||
// );
|
||||
// }
|
||||
//
|
||||
// #[test]
|
||||
// fn test_is_mine() {
|
||||
// // is_mine should return true for addresses generated by the wallet
|
||||
// let mnemonic: Mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
|
||||
// let secret_key: DescriptorSecretKey =
|
||||
// DescriptorSecretKey::new(Network::Testnet, Arc::new(mnemonic), None);
|
||||
// let descriptor: Descriptor = Descriptor::new_bip84(
|
||||
// Arc::new(secret_key),
|
||||
// KeychainKind::External,
|
||||
// Network::Testnet,
|
||||
// );
|
||||
// let wallet: Wallet = Wallet::new(
|
||||
// Arc::new(descriptor),
|
||||
// None,
|
||||
// Network::Testnet,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// // let address = wallet.get_address(AddressIndex::New).unwrap();
|
||||
// // let script: Arc<Script> = address.address.script_pubkey();
|
||||
//
|
||||
// // let is_mine_1: bool = wallet.is_mine(script).unwrap();
|
||||
// // assert!(is_mine_1);
|
||||
//
|
||||
// // is_mine returns false when provided a script that is not in the wallet
|
||||
// let other_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
|
||||
// let other_descriptor = Descriptor::new(other_wpkh.to_string(), Network::Testnet).unwrap();
|
||||
//
|
||||
// let other_wallet = Wallet::new(
|
||||
// Arc::new(other_descriptor),
|
||||
// None,
|
||||
// Network::Testnet,
|
||||
// DatabaseConfig::Memory,
|
||||
// )
|
||||
// .unwrap();
|
||||
//
|
||||
// let other_address = other_wallet.get_address(AddressIndex::New).unwrap();
|
||||
// let other_script: Arc<Script> = other_address.address.script_pubkey();
|
||||
// let is_mine_2: bool = wallet.is_mine(other_script).unwrap();
|
||||
// assert_matches!(is_mine_2, false);
|
||||
// }
|
||||
// }
|
||||
@@ -6,10 +6,5 @@ class TestBdk(unittest.TestCase):
|
||||
def test_some_enum(self):
|
||||
network = Network.TESTNET
|
||||
|
||||
def test_some_dict(self):
|
||||
a = AddressInfo(index=42, address="testaddress")
|
||||
self.assertEqual(42, a.index)
|
||||
self.assertEqual("testaddress", a.address)
|
||||
|
||||
if __name__=='__main__':
|
||||
unittest.main()
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
uniffi_macros::build_foreign_language_testcases!(
|
||||
["src/bdk.udl",],
|
||||
[
|
||||
"tests/bindings/test.kts",
|
||||
"tests/bindings/test.swift",
|
||||
"tests/bindings/test.py"
|
||||
]
|
||||
uniffi::build_foreign_language_testcases!(
|
||||
"tests/bindings/test.kts",
|
||||
"tests/bindings/test.swift",
|
||||
"tests/bindings/test.py",
|
||||
);
|
||||
|
||||
3
bdk-ffi/uniffi-bindgen.rs
Normal file
3
bdk-ffi/uniffi-bindgen.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
fn main() {
|
||||
uniffi::uniffi_bindgen_main()
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# bdk-android
|
||||
# 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.
|
||||
|
||||
## How to Use
|
||||
@@ -13,22 +13,22 @@ dependencies {
|
||||
}
|
||||
```
|
||||
|
||||
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
|
||||
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 = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
|
||||
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 databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
|
||||
)
|
||||
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
|
||||
)
|
||||
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
||||
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
|
||||
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
|
||||
```
|
||||
|
||||
### Snapshot releases
|
||||
@@ -38,32 +38,39 @@ repositories {
|
||||
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version-SNAPSHOT>")
|
||||
dependencies {
|
||||
implementation("org.bitcoindevkit:bdk-jvm:<version-SNAPSHOT>")
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Example Projects
|
||||
* [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. Clone this repository.
|
||||
1. Install JDK 11. It must be version 11 (not 17), otherwise it won't build. 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
|
||||
```
|
||||
2. Install Rust (note that we are currently building using Rust 1.73.0):
|
||||
```shell
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
rustup default 1.73.0
|
||||
```
|
||||
3. Clone this repository.
|
||||
```shell
|
||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||
```
|
||||
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||
3. If building on macOS install required intel and m1 jvm targets
|
||||
4. If building on macOS install required intel and m1 jvm targets
|
||||
```sh
|
||||
rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
||||
```
|
||||
4. Build kotlin bindings
|
||||
```sh
|
||||
# build JVM library
|
||||
./gradlew buildJvmLib
|
||||
```
|
||||
5. Build kotlin bindings
|
||||
```sh
|
||||
./gradlew buildJvmLib
|
||||
```
|
||||
|
||||
## How to publish to your local Maven repo
|
||||
```shell
|
||||
@@ -83,10 +90,12 @@ and use the `publishToMavenLocal` task without excluding the signing task:
|
||||
```
|
||||
|
||||
## Known issues
|
||||
## JNA dependency
|
||||
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
|
||||
```shell
|
||||
class file for com.sun.jna.Pointer not found
|
||||
```
|
||||
|
||||
The solution is to add JNA as a dependency like so:
|
||||
```kotlin
|
||||
dependencies {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
org.gradle.jvmargs=-Xmx1536m
|
||||
android.enableJetifier=true
|
||||
kotlin.code.style=official
|
||||
libraryVersion=0.26.0-SNAPSHOT
|
||||
libraryVersion=1.0.0-alpha.2-rc1
|
||||
|
||||
@@ -20,21 +20,39 @@ repositories {
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
withSourcesJar()
|
||||
withJavadocJar()
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
useJUnitPlatform()
|
||||
// This block ensures that the tests that require access to a blockchain are not
|
||||
// run if the -P excludeConnectedTests flag is passed to gradle.
|
||||
// This ensures our CI runs are not fickle by not requiring access to testnet.
|
||||
// This is a workaround until we have a proper regtest setup for the CI.
|
||||
// Note that the command in the CI is ./gradlew test -P excludeConnectedTests
|
||||
tasks.test {
|
||||
if (project.hasProperty("excludeConnectedTests")) {
|
||||
exclude("**/LiveWalletTest.class")
|
||||
exclude("**/LiveTxBuilderTest.class")
|
||||
}
|
||||
}
|
||||
|
||||
testing {
|
||||
suites {
|
||||
val test by getting(JvmTestSuite::class) {
|
||||
useKotlinTest("1.6.10")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
testLogging {
|
||||
events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
|
||||
exceptionFormat = FULL
|
||||
showExceptions = true
|
||||
showCauses = true
|
||||
showStackTraces = true
|
||||
showCauses = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,14 +92,9 @@ afterEvaluate {
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id.set("notmandatory")
|
||||
name.set("Steve Myers")
|
||||
email.set("notmandatory@noreply.github.org")
|
||||
}
|
||||
developer {
|
||||
id.set("artfuldev")
|
||||
name.set("Sudarsan Balaji")
|
||||
email.set("sudarsan.balaji@artfuldev.com")
|
||||
id.set("bdkdevelopers")
|
||||
name.set("Bitcoin Dev Kit Developers")
|
||||
email.set("dev@bitcoindevkit.org")
|
||||
}
|
||||
}
|
||||
scm {
|
||||
@@ -107,4 +120,8 @@ signing {
|
||||
// binaries before running the tests
|
||||
tasks.withType<KotlinCompile> {
|
||||
dependsOn("buildJvmLib")
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import org.slf4j.Logger
|
||||
import org.slf4j.LoggerFactory
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
|
||||
/**
|
||||
* Library test, which will execute on linux host.
|
||||
*/
|
||||
class JvmLibTest {
|
||||
|
||||
private fun getTestDataDir(): String {
|
||||
return Files.createTempDirectory("bdk-test").toString()
|
||||
}
|
||||
|
||||
private fun cleanupTestDataDir(testDataDir: String) {
|
||||
File(testDataDir).deleteRecursively()
|
||||
}
|
||||
|
||||
class LogProgress : Progress {
|
||||
private val log: Logger = LoggerFactory.getLogger(JvmLibTest::class.java)
|
||||
|
||||
override fun update(progress: Float, message: String?) {
|
||||
log.debug("Syncing...")
|
||||
}
|
||||
}
|
||||
|
||||
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
||||
|
||||
private val databaseConfig = DatabaseConfig.Memory
|
||||
|
||||
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||
ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
null,
|
||||
5u,
|
||||
null,
|
||||
100u,
|
||||
true,
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun memoryWalletNewAddress() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val address = wallet.getAddress(AddressIndex.NEW).address
|
||||
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun memoryWalletSyncGetBalance() {
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sqliteWalletSyncGetBalance() {
|
||||
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
|
||||
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
|
||||
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
|
||||
val blockchain = Blockchain(blockchainConfig)
|
||||
wallet.sync(blockchain, LogProgress())
|
||||
val balance: Balance = wallet.getBalance()
|
||||
assertTrue(balance.total > 0u)
|
||||
cleanupTestDataDir(testDataDir)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class LiveTxBuilderTest {
|
||||
@Test
|
||||
fun testTxBuilder() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total()}")
|
||||
|
||||
assert(wallet.getBalance().total() > 0uL)
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(2.0f)
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class LiveWalletTest {
|
||||
@Test
|
||||
fun testSyncedBalance() {
|
||||
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val esploraClient: EsploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
// val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
|
||||
val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total()}")
|
||||
|
||||
assert(wallet.getBalance().total() > 0uL)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBroadcastTransaction() {
|
||||
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
|
||||
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
|
||||
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
|
||||
val update = esploraClient.scan(wallet, 10uL, 1uL)
|
||||
|
||||
wallet.applyUpdate(update)
|
||||
println("Balance: ${wallet.getBalance().total()}")
|
||||
println("New address: ${wallet.getAddress(AddressIndex.New).address.asString()}")
|
||||
|
||||
assert(wallet.getBalance().total() > 0uL) {
|
||||
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
|
||||
}
|
||||
|
||||
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
|
||||
|
||||
val psbt: PartiallySignedTransaction = TxBuilder()
|
||||
.addRecipient(recipient.scriptPubkey(), 4200uL)
|
||||
.feeRate(2.0f)
|
||||
.finish(wallet)
|
||||
|
||||
println(psbt.serialize())
|
||||
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
|
||||
|
||||
val walletDidSign = wallet.sign(psbt)
|
||||
assertTrue(walletDidSign)
|
||||
|
||||
val tx: Transaction = psbt.extractTx()
|
||||
|
||||
println("Txid is: ${tx.txid()}")
|
||||
esploraClient.broadcast(tx)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class OfflineDescriptorTest {
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic.fromString("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertEquals(
|
||||
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
actual = descriptor.asString()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package org.bitcoindevkit
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
class OfflineWalletTest {
|
||||
@Test
|
||||
fun testDescriptorBip86() {
|
||||
val mnemonic: Mnemonic = Mnemonic(WordCount.WORDS12)
|
||||
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
|
||||
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
|
||||
|
||||
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNewAddress() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet.newNoPersist(
|
||||
descriptor,
|
||||
null,
|
||||
Network.TESTNET
|
||||
)
|
||||
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
|
||||
|
||||
assertEquals(
|
||||
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
|
||||
actual = addressInfo.address.asString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testBalance() {
|
||||
val descriptor: Descriptor = Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
Network.TESTNET
|
||||
)
|
||||
val wallet: Wallet = Wallet.newNoPersist(
|
||||
descriptor,
|
||||
null,
|
||||
Network.TESTNET
|
||||
)
|
||||
|
||||
assertEquals(
|
||||
expected = 0uL,
|
||||
actual = wallet.getBalance().total()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,13 @@ package org.bitcoindevkit.plugins
|
||||
val operatingSystem: OS = when {
|
||||
System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
|
||||
System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX
|
||||
System.getProperty("os.name").contains("windows", ignoreCase = true) -> OS.WINDOWS
|
||||
else -> OS.OTHER
|
||||
}
|
||||
|
||||
enum class OS {
|
||||
MAC,
|
||||
LINUX,
|
||||
WINDOWS,
|
||||
OTHER,
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import org.gradle.kotlin.dsl.getValue
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.register
|
||||
|
||||
// TODO 18: Migrate hard coded strings to constants all in the same location so they're at least easy
|
||||
// to find and reason about.
|
||||
internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
override fun apply(target: Project): Unit = target.run {
|
||||
|
||||
@@ -27,13 +29,20 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-apple-darwin")
|
||||
args(cargoArgs)
|
||||
}
|
||||
} else if(operatingSystem == OS.LINUX) {
|
||||
} else if (operatingSystem == OS.LINUX) {
|
||||
exec {
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
executable("cargo")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-unknown-linux-gnu")
|
||||
args(cargoArgs)
|
||||
}
|
||||
} else if (operatingSystem == OS.WINDOWS) {
|
||||
exec {
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
executable("cargo")
|
||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-pc-windows-msvc")
|
||||
args(cargoArgs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,13 +79,25 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
ext = "so"
|
||||
)
|
||||
)
|
||||
} else if (operatingSystem == OS.WINDOWS) {
|
||||
libsToCopy.add(
|
||||
CopyMetadata(
|
||||
targetDir = "x86_64-pc-windows-msvc",
|
||||
resDir = "win32-x86-64",
|
||||
ext = "dll"
|
||||
)
|
||||
)
|
||||
}
|
||||
val libName = when (operatingSystem) {
|
||||
OS.WINDOWS -> "bdkffi"
|
||||
else -> "libbdkffi"
|
||||
}
|
||||
|
||||
libsToCopy.forEach {
|
||||
doFirst {
|
||||
copy {
|
||||
with(it) {
|
||||
from("${project.projectDir}/../../target/${this.targetDir}/release-smaller/libbdkffi.${this.ext}")
|
||||
from("${project.projectDir}/../../bdk-ffi/target/${this.targetDir}/release-smaller/${libName}.${this.ext}")
|
||||
into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
|
||||
}
|
||||
}
|
||||
@@ -89,18 +110,24 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||
|
||||
dependsOn(moveNativeJvmLibs)
|
||||
|
||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||
// TODO 2: Is the Windows name the correct one?
|
||||
// TODO 3: This will not work on mac Intel (x86_64 architecture)
|
||||
// val libraryPath = when (operatingSystem) {
|
||||
// OS.LINUX -> "./target/x86_64-unknown-linux-gnu/release-smaller/libbdkffi.so"
|
||||
// OS.MAC -> "./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib"
|
||||
// OS.WINDOWS -> "./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll"
|
||||
// else -> throw Exception("Unsupported OS")
|
||||
// }
|
||||
|
||||
// workingDir("${project.projectDir}/../../bdk-ffi/")
|
||||
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin/", "--no-format")
|
||||
|
||||
// The code above was for the migration to uniffi 0.24.3 using the --library flag
|
||||
// The code below works with uniffi 0.23.0
|
||||
workingDir("${project.projectDir}/../../bdk-ffi/")
|
||||
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin", "--no-format")
|
||||
executable("cargo")
|
||||
args(
|
||||
"run",
|
||||
"--package",
|
||||
"bdk-ffi-bindgen",
|
||||
"--",
|
||||
"--language",
|
||||
"kotlin",
|
||||
"--out-dir",
|
||||
"../bdk-jvm/lib/src/main/kotlin"
|
||||
)
|
||||
args(cargoArgs)
|
||||
|
||||
doLast {
|
||||
println("JVM bindings file successfully created")
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
This software is licensed under [Apache 2.0](LICENSE-APACHE) or
|
||||
[MIT](LICENSE-MIT), at your option.
|
||||
|
||||
Some files retain their own copyright notice, however, for full authorship
|
||||
information, see version control history.
|
||||
|
||||
Except as otherwise noted in individual files, all files in this repository are
|
||||
licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
http://opensource.org/licenses/MIT>, at your option.
|
||||
|
||||
You may not use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of this software or any files in this repository except in
|
||||
accordance with one or both of these licenses.
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,17 +0,0 @@
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
3
bdk-python/MANIFEST.in
Normal file
3
bdk-python/MANIFEST.in
Normal file
@@ -0,0 +1,3 @@
|
||||
include ./src/bdkpython/libbdkffi.dylib
|
||||
include ./src/bdkpython/libbdkffi.so
|
||||
include ./src/bdkpython/bdkffi.dll
|
||||
@@ -11,11 +11,11 @@ pip install bdkpython
|
||||
|
||||
## Run the tests
|
||||
```shell
|
||||
pip3 install --requirement requirements.txt
|
||||
bash ./generate.sh
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl
|
||||
python -m unittest --verbose tests/test_bdk.py
|
||||
pip install --requirement requirements.txt
|
||||
bash ./scripts/generate-linux.sh # here you should run the script appropriate for your platform
|
||||
python setup.py bdist_wheel --verbose
|
||||
pip install ./dist/bdkpython-<yourversion>.whl --force-reinstall
|
||||
python -m unittest --verbose
|
||||
```
|
||||
|
||||
## Build the package
|
||||
@@ -23,26 +23,14 @@ python -m unittest --verbose tests/test_bdk.py
|
||||
# Install dependencies
|
||||
pip install --requirement requirements.txt
|
||||
|
||||
# Generate the bindings first
|
||||
bash generate.sh
|
||||
# Generate the bindings (use the script appropriate for your platform)
|
||||
bash ./scripts/generate-linux.sh
|
||||
|
||||
# Build the wheel
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
```
|
||||
|
||||
## Run tox to build and test locally
|
||||
```shell
|
||||
# install dev requirements
|
||||
pip install --requirement requirements-dev.txt
|
||||
|
||||
# build bindings glue code (located at .bdk-python/src/bdkpython/bdk.py)
|
||||
source ./generate.sh
|
||||
|
||||
# build and test
|
||||
tox -vv
|
||||
python setup.py --verbose bdist_wheel
|
||||
```
|
||||
|
||||
## Install locally
|
||||
```shell
|
||||
pip install ./dist/bdkpython-<yourversion>-py3-none-any.whl
|
||||
pip install ./dist/bdkpython-<yourversion>.whl
|
||||
```
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(dirname "$(realpath $0)")
|
||||
PY_SRC="${SCRIPT_DIR}/src/bdkpython/"
|
||||
|
||||
echo "Generating bdk.py..."
|
||||
# GENERATE_PYTHON_BINDINGS_OUT="$PY_SRC" GENERATE_PYTHON_BINDINGS_FIXUP_LIB_PATH=bdkffi cargo run --manifest-path ./bdk-ffi/Cargo.toml --release --bin generate --features generate-python
|
||||
# BDKFFI_BINDGEN_PYTHON_FIXUP_PATH=bdkffi cargo run --manifest-path ./bdk-ffi/Cargo.toml --package bdk-ffi-bindgen -- --language python --udl-file ./bdk-ffi/src/bdk.udl --out-dir ./src/bdkpython/
|
||||
BDKFFI_BINDGEN_OUTPUT_DIR="$PY_SRC" BDKFFI_BINDGEN_PYTHON_FIXUP_PATH=bdkffi cargo run --manifest-path ../bdk-ffi/Cargo.toml --package bdk-ffi-bindgen -- --language python --udl-file ../bdk-ffi/src/bdk.udl
|
||||
@@ -1,3 +1,4 @@
|
||||
semantic-version==2.9.0
|
||||
setuptools-rust==1.1.2
|
||||
typing_extensions==4.0.1
|
||||
setuptools==67.4.0
|
||||
wheel==0.38.4
|
||||
|
||||
18
bdk-python/scripts/generate-linux.sh
Normal file
18
bdk-python/scripts/generate-linux.sh
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
${PYBIN}/python --version
|
||||
${PYBIN}/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
|
||||
cargo build --profile release-smaller
|
||||
|
||||
echo "Copying linux libbdkffi.so..."
|
||||
cp ./target/release-smaller/libbdkffi.so ../bdk-python/src/bdkpython/libbdkffi.so
|
||||
|
||||
echo "All done!"
|
||||
19
bdk-python/scripts/generate-macos-arm64.sh
Normal file
19
bdk-python/scripts/generate-macos-arm64.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
python3 --version
|
||||
pip install --user -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 target add aarch64-apple-darwin
|
||||
cargo build --profile release-smaller --target aarch64-apple-darwin
|
||||
|
||||
echo "Copying libraries libbdkffi.dylib..."
|
||||
cp ./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
|
||||
echo "All done!"
|
||||
19
bdk-python/scripts/generate-macos-x86_64.sh
Normal file
19
bdk-python/scripts/generate-macos-x86_64.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
python3 --version
|
||||
pip install --user -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 target add x86_64-apple-darwin
|
||||
cargo build --profile release-smaller --target x86_64-apple-darwin
|
||||
|
||||
echo "Copying libraries libbdkffi.dylib..."
|
||||
cp ./target/x86_64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
|
||||
|
||||
echo "All done!"
|
||||
19
bdk-python/scripts/generate-windows.sh
Normal file
19
bdk-python/scripts/generate-windows.sh
Normal file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
python3 --version
|
||||
pip install --user -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 target add x86_64-pc-windows-msvc
|
||||
cargo build --profile release-smaller --target x86_64-pc-windows-msvc
|
||||
|
||||
echo "Copying libraries bdkffi.dll..."
|
||||
cp ./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll ../bdk-python/src/bdkpython/bdkffi.dll
|
||||
|
||||
echo "All done!"
|
||||
@@ -1,9 +1,6 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
|
||||
from setuptools import setup
|
||||
from setuptools_rust import Binding, RustExtension
|
||||
|
||||
LONG_DESCRIPTION = """# bdkpython
|
||||
The Python language bindings for the [Bitcoin Dev Kit](https://github.com/bitcoindevkit).
|
||||
@@ -16,59 +13,22 @@ pip install bdkpython
|
||||
## Simple example
|
||||
```python
|
||||
import bdkpython as bdk
|
||||
|
||||
|
||||
descriptor = bdk.Descriptor("wpkh(tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/84h/0h/0h/0/*)", bdk.Network.TESTNET)
|
||||
db_config = bdk.DatabaseConfig.MEMORY()
|
||||
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
|
||||
bdk.ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
None,
|
||||
5,
|
||||
None,
|
||||
100,
|
||||
True,
|
||||
)
|
||||
)
|
||||
blockchain = bdk.Blockchain(blockchain_config)
|
||||
|
||||
wallet = bdk.Wallet(
|
||||
descriptor=descriptor,
|
||||
change_descriptor=None,
|
||||
network=bdk.Network.TESTNET,
|
||||
database_config=db_config,
|
||||
)
|
||||
|
||||
# print new receive address
|
||||
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED)
|
||||
address = address_info.address
|
||||
index = address_info.index
|
||||
print(f"New BIP84 testnet address: {address} at index {index}")
|
||||
|
||||
|
||||
# print wallet balance
|
||||
wallet.sync(blockchain, None)
|
||||
balance = wallet.get_balance()
|
||||
print(f"Wallet balance is: {balance.total}")
|
||||
"""
|
||||
|
||||
rust_ext = RustExtension(
|
||||
target="bdkpython.bdkffi",
|
||||
path="../bdk-ffi/Cargo.toml",
|
||||
binding=Binding.NoBinding,
|
||||
)
|
||||
|
||||
setup(
|
||||
name='bdkpython',
|
||||
version='0.26.0.dev0',
|
||||
name="bdkpython",
|
||||
version="1.0.0a2.dev1",
|
||||
description="The Python language bindings for the Bitcoin Development Kit",
|
||||
long_description=LONG_DESCRIPTION,
|
||||
long_description_content_type='text/markdown',
|
||||
rust_extensions=[rust_ext],
|
||||
long_description_content_type="text/markdown",
|
||||
include_package_data = True,
|
||||
zip_safe=False,
|
||||
packages=['bdkpython'],
|
||||
package_dir={'bdkpython': './src/bdkpython'},
|
||||
url="https://github.com/bitcoindevkit/bdk-python",
|
||||
author="Alekos Filini <alekos.filini@gmail.com>, Steve Myers <steve@notmandatory.org>",
|
||||
packages=["bdkpython"],
|
||||
package_dir={"bdkpython": "./src/bdkpython"},
|
||||
url="https://github.com/bitcoindevkit/bdk-ffi",
|
||||
author="Bitcoin Dev Kit Developers <dev@bitcoindevkit.org>",
|
||||
license="MIT or Apache 2.0",
|
||||
# This is required to ensure the library name includes the python version, abi, and platform tags
|
||||
# See issue #350 for more information
|
||||
has_ext_modules=lambda: True,
|
||||
)
|
||||
|
||||
@@ -1 +1 @@
|
||||
from bdkpython.bdk import *
|
||||
from bdkpython.bdk import *
|
||||
0
bdk-python/tests/__init__.py
Normal file
0
bdk-python/tests/__init__.py
Normal file
@@ -1,48 +0,0 @@
|
||||
import bdkpython as bdk
|
||||
import unittest
|
||||
|
||||
|
||||
descriptor = bdk.Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", bdk.Network.TESTNET)
|
||||
db_config = bdk.DatabaseConfig.MEMORY()
|
||||
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
|
||||
bdk.ElectrumConfig(
|
||||
"ssl://electrum.blockstream.info:60002",
|
||||
None,
|
||||
5,
|
||||
None,
|
||||
100,
|
||||
True,
|
||||
)
|
||||
)
|
||||
blockchain = bdk.Blockchain(blockchain_config)
|
||||
|
||||
|
||||
class TestSimpleBip84Wallet(unittest.TestCase):
|
||||
|
||||
def test_address_bip84_testnet(self):
|
||||
wallet = bdk.Wallet(
|
||||
descriptor=descriptor,
|
||||
change_descriptor=None,
|
||||
network=bdk.Network.TESTNET,
|
||||
database_config=db_config
|
||||
)
|
||||
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED)
|
||||
address = address_info.address
|
||||
# print(f"New address is {address}")
|
||||
assert address == "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", f"Wrong address {address}, should be tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"
|
||||
|
||||
def test_wallet_balance(self):
|
||||
wallet = bdk.Wallet(
|
||||
descriptor=descriptor,
|
||||
change_descriptor=None,
|
||||
network=bdk.Network.TESTNET,
|
||||
database_config=db_config,
|
||||
)
|
||||
wallet.sync(blockchain, None)
|
||||
balance = wallet.get_balance()
|
||||
# print(f"Balance is {balance.total} sat")
|
||||
assert balance.total > 0, "Balance is 0, send testnet coins to tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
38
bdk-python/tests/test_live_tx_builder.py
Normal file
38
bdk-python/tests/test_live_tx_builder.py
Normal file
@@ -0,0 +1,38 @@
|
||||
import bdkpython as bdk
|
||||
import unittest
|
||||
|
||||
class TestLiveTxBuilder(unittest.TestCase):
|
||||
|
||||
def test_tx_builder(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
|
||||
descriptor,
|
||||
None,
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://mempool.space/testnet/api")
|
||||
update = esploraClient.scan(
|
||||
wallet = wallet,
|
||||
stop_gap = 10,
|
||||
parallel_requests = 1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
|
||||
self.assertGreater(wallet.get_balance().total(), 0)
|
||||
|
||||
recipient = bdk.Address(
|
||||
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network = bdk.Network.TESTNET
|
||||
)
|
||||
|
||||
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(2.0).finish(wallet)
|
||||
# print(psbt.serialize())
|
||||
|
||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
63
bdk-python/tests/test_live_wallet.py
Normal file
63
bdk-python/tests/test_live_wallet.py
Normal file
@@ -0,0 +1,63 @@
|
||||
import bdkpython as bdk
|
||||
import unittest
|
||||
|
||||
class TestLiveWallet(unittest.TestCase):
|
||||
|
||||
def test_synced_balance(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
|
||||
descriptor,
|
||||
None,
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://mempool.space/testnet/api")
|
||||
update = esploraClient.scan(
|
||||
wallet = wallet,
|
||||
stop_gap = 10,
|
||||
parallel_requests = 1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
|
||||
self.assertGreater(wallet.get_balance().total(), 0)
|
||||
|
||||
def test_broadcast_transaction(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
|
||||
descriptor,
|
||||
None,
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://mempool.space/testnet/api")
|
||||
update = esploraClient.scan(
|
||||
wallet = wallet,
|
||||
stop_gap = 10,
|
||||
parallel_requests = 1
|
||||
)
|
||||
wallet.apply_update(update)
|
||||
|
||||
self.assertGreater(wallet.get_balance().total(), 0)
|
||||
|
||||
recipient = bdk.Address(
|
||||
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
|
||||
network = bdk.Network.TESTNET
|
||||
)
|
||||
|
||||
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(2.0).finish(wallet)
|
||||
# print(psbt.serialize())
|
||||
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
|
||||
|
||||
walletDidSign = wallet.sign(psbt)
|
||||
self.assertTrue(walletDidSign)
|
||||
tx = psbt.extract_tx()
|
||||
|
||||
esploraClient.broadcast(tx)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
18
bdk-python/tests/test_offline_descriptor.py
Normal file
18
bdk-python/tests/test_offline_descriptor.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import bdkpython as bdk
|
||||
import unittest
|
||||
|
||||
class TestSimpleWallet(unittest.TestCase):
|
||||
|
||||
def test_descriptor_bip86(self):
|
||||
mnemonic: bdk.Mnemonic = bdk.Mnemonic.from_string("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
|
||||
descriptor_secret_key: bdk.DescriptorSecretKey = bdk.DescriptorSecretKey(bdk.Network.TESTNET, mnemonic, None)
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor.new_bip86(descriptor_secret_key, bdk.KeychainKind.EXTERNAL, bdk.Network.TESTNET)
|
||||
|
||||
self.assertEqual(
|
||||
"tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
|
||||
descriptor.as_string()
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
34
bdk-python/tests/test_offline_wallet.py
Normal file
34
bdk-python/tests/test_offline_wallet.py
Normal file
@@ -0,0 +1,34 @@
|
||||
import bdkpython as bdk
|
||||
import unittest
|
||||
|
||||
class TestSimpleWallet(unittest.TestCase):
|
||||
|
||||
def test_new_address(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
wallet: Wallet = bdk.Wallet.new_no_persist(
|
||||
descriptor,
|
||||
None,
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
address_info: bdk.AddressInfo = wallet.get_address(bdk.AddressIndex.NEW())
|
||||
|
||||
self.assertEqual("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address_info.address.as_string())
|
||||
|
||||
def test_balance(self):
|
||||
descriptor: bdk.Descriptor = bdk.Descriptor(
|
||||
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
|
||||
descriptor,
|
||||
None,
|
||||
bdk.Network.TESTNET
|
||||
)
|
||||
|
||||
self.assertEqual(wallet.get_balance().total(), 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,16 +0,0 @@
|
||||
[tox]
|
||||
envlist =
|
||||
py38
|
||||
py39
|
||||
|
||||
|
||||
[testenv]
|
||||
usedevelop=true
|
||||
deps =
|
||||
-rrequirements.txt
|
||||
-rrequirements-dev.txt
|
||||
commands =
|
||||
python3 setup.py -v build
|
||||
python3 setup.py -v install
|
||||
pytest --verbose --override-ini console_output_style=count
|
||||
python3 setup.py --verbose bdist_wheel
|
||||
@@ -29,7 +29,9 @@ let package = Package(
|
||||
.binaryTarget(name: "bdkFFI", path: "./bdkFFI.xcframework"),
|
||||
.target(
|
||||
name: "BitcoinDevKit",
|
||||
dependencies: ["bdkFFI"]),
|
||||
dependencies: ["bdkFFI"],
|
||||
swiftSettings: [.unsafeFlags(["-suppress-warnings"])]
|
||||
),
|
||||
.testTarget(
|
||||
name: "BitcoinDevKitTests",
|
||||
dependencies: ["BitcoinDevKit"]),
|
||||
|
||||
@@ -35,7 +35,7 @@ swift test
|
||||
|
||||
### Example Projects
|
||||
|
||||
* [BdkSwiftSample](https://github.com/futurepaul/BdkSwiftSample), iOS
|
||||
* [BDKSwiftExampleWallet](https://github.com/bitcoindevkit/BDKSwiftExampleWallet), iOS
|
||||
|
||||
## How to Build and Publish
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
final class BitcoinDevKitTests: XCTestCase {
|
||||
func testMemoryWalletNewAddress() throws {
|
||||
let desc = try Descriptor(
|
||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
network: Network.regtest
|
||||
)
|
||||
let databaseConfig = DatabaseConfig.memory
|
||||
let wallet = try Wallet.init(descriptor: desc, changeDescriptor: nil, network: Network.regtest, databaseConfig: databaseConfig)
|
||||
let addressInfo = try wallet.getAddress(addressIndex: AddressIndex.new)
|
||||
XCTAssertEqual(addressInfo.address, "bcrt1qzg4mckdh50nwdm9hkzq06528rsu73hjxytqkxs")
|
||||
}
|
||||
}
|
||||
34
bdk-swift/Tests/BitcoinDevKitTests/LiveTxBuilderTests.swift
Normal file
34
bdk-swift/Tests/BitcoinDevKitTests/LiveTxBuilderTests.swift
Normal file
@@ -0,0 +1,34 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
final class LiveTxBuilderTests: XCTestCase {
|
||||
func testTxBuilder() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.testnet
|
||||
)
|
||||
let wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .testnet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
|
||||
let update = try esploraClient.scan(
|
||||
wallet: wallet,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
|
||||
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()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.feeRate(satPerVbyte: 2.0)
|
||||
.finish(wallet: wallet)
|
||||
|
||||
print(psbt.serialize())
|
||||
XCTAssertTrue(psbt.serialize().hasPrefix("cHNi"), "PSBT should start with cHNI")
|
||||
}
|
||||
}
|
||||
65
bdk-swift/Tests/BitcoinDevKitTests/LiveWalletTests.swift
Normal file
65
bdk-swift/Tests/BitcoinDevKitTests/LiveWalletTests.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
final class LiveWalletTests: XCTestCase {
|
||||
func testSyncedBalance() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.testnet
|
||||
)
|
||||
let wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .testnet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
|
||||
let update = try esploraClient.scan(
|
||||
wallet: wallet,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
|
||||
XCTAssertGreaterThan(wallet.getBalance().total(), UInt64(0))
|
||||
}
|
||||
|
||||
func testBroadcastTransaction() throws {
|
||||
let descriptor = try Descriptor(
|
||||
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||
network: Network.testnet
|
||||
)
|
||||
let wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .testnet
|
||||
)
|
||||
let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
|
||||
let update = try esploraClient.scan(
|
||||
wallet: wallet,
|
||||
stopGap: 10,
|
||||
parallelRequests: 1
|
||||
)
|
||||
try wallet.applyUpdate(update: update)
|
||||
|
||||
XCTAssertGreaterThan(wallet.getBalance().total(), UInt64(0), "Wallet must have positive balance, please add funds")
|
||||
|
||||
print("Balance: \(wallet.getBalance().total())")
|
||||
|
||||
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
|
||||
let psbt: PartiallySignedTransaction = try
|
||||
TxBuilder()
|
||||
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
|
||||
.feeRate(satPerVbyte: 2.0)
|
||||
.finish(wallet: wallet)
|
||||
|
||||
print(psbt.serialize())
|
||||
XCTAssertTrue(psbt.serialize().hasPrefix("cHNi"), "PSBT should start with cHNI")
|
||||
|
||||
let walletDidSign: Bool = try wallet.sign(psbt: psbt)
|
||||
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")
|
||||
|
||||
let tx: Transaction = psbt.extractTx()
|
||||
print(tx.txid())
|
||||
try esploraClient.broadcast(transaction: tx)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
final class OfflineDescriptorTests: XCTestCase {
|
||||
func testDescriptorBip86() throws {
|
||||
let mnemonic: Mnemonic = try Mnemonic.fromString(mnemonic: "space echo position wrist orient erupt relief museum myself grain wisdom tumble")
|
||||
let descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(
|
||||
network: Network.testnet,
|
||||
mnemonic: mnemonic,
|
||||
password: nil
|
||||
)
|
||||
let descriptor: Descriptor = Descriptor.newBip86(
|
||||
secretKey: descriptorSecretKey,
|
||||
keychain: KeychainKind.external,
|
||||
network: Network.testnet
|
||||
)
|
||||
|
||||
XCTAssertEqual(descriptor.asString(), "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx")
|
||||
}
|
||||
}
|
||||
33
bdk-swift/Tests/BitcoinDevKitTests/OfflineWalletTests.swift
Normal file
33
bdk-swift/Tests/BitcoinDevKitTests/OfflineWalletTests.swift
Normal file
@@ -0,0 +1,33 @@
|
||||
import XCTest
|
||||
@testable import BitcoinDevKit
|
||||
|
||||
final class OfflineWalletTests: XCTestCase {
|
||||
func testNewAddress() throws {
|
||||
let descriptor: Descriptor = try Descriptor(
|
||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
network: Network.testnet
|
||||
)
|
||||
let wallet: Wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .testnet
|
||||
)
|
||||
let addressInfo: AddressInfo = wallet.getAddress(addressIndex: AddressIndex.new)
|
||||
|
||||
XCTAssertEqual(addressInfo.address.asString(), "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")
|
||||
}
|
||||
|
||||
func testBalance() throws {
|
||||
let descriptor: Descriptor = try Descriptor(
|
||||
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
|
||||
network: Network.testnet
|
||||
)
|
||||
let wallet: Wallet = try Wallet.newNoPersist(
|
||||
descriptor: descriptor,
|
||||
changeDescriptor: nil,
|
||||
network: .testnet
|
||||
)
|
||||
|
||||
XCTAssertEqual(wallet.getBalance().total(), 0)
|
||||
}
|
||||
}
|
||||
@@ -2,39 +2,39 @@
|
||||
# This script builds local swift-bdk Swift language bindings and corresponding bdkFFI.xcframework.
|
||||
# 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 nightly-x86_64-apple-darwin
|
||||
rustup component add rust-src --toolchain nightly-x86_64-apple-darwin
|
||||
rustup install 1.73.0
|
||||
rustup component add rust-src
|
||||
rustup target add aarch64-apple-ios x86_64-apple-ios
|
||||
rustup target add aarch64-apple-ios-sim --toolchain nightly
|
||||
rustup target add aarch64-apple-ios-sim
|
||||
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||
|
||||
pushd bdk-ffi
|
||||
mkdir -p Sources/BitcoinDevKit
|
||||
cargo run --package bdk-ffi-bindgen -- --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit
|
||||
popd
|
||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
||||
|
||||
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-darwin
|
||||
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-ios
|
||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios
|
||||
cargo +nightly build --package bdk-ffi --release -Z build-std --target aarch64-apple-ios-sim
|
||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios-sim
|
||||
|
||||
mkdir -p target/lipo-ios-sim/release-smaller
|
||||
lipo target/aarch64-apple-ios-sim/release/libbdkffi.a target/x86_64-apple-ios/release-smaller/libbdkffi.a -create -output target/lipo-ios-sim/release-smaller/libbdkffi.a
|
||||
lipo target/aarch64-apple-ios-sim/release-smaller/libbdkffi.a target/x86_64-apple-ios/release-smaller/libbdkffi.a -create -output target/lipo-ios-sim/release-smaller/libbdkffi.a
|
||||
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
|
||||
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
|
||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
||||
cp ../target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
|
||||
cp ../target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
|
||||
cp ../target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
|
||||
cp ../bdk-ffi/target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
|
||||
rm Sources/BitcoinDevKit/bdkFFI.h
|
||||
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
||||
#rm bdkFFI.xcframework.zip || true
|
||||
|
||||
Reference in New Issue
Block a user