Compare commits
1 Commits
release/al
...
v0.25.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9741943b56 |
17
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
17
.github/ISSUE_TEMPLATE/enhancement_request.md
vendored
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
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,84 +4,98 @@ about: Create a new minor release [for release managers only]
|
|||||||
title: 'Release MAJOR.MINOR+1.0'
|
title: 'Release MAJOR.MINOR+1.0'
|
||||||
labels: 'release'
|
labels: 'release'
|
||||||
assignees: ''
|
assignees: ''
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Create a new minor release
|
## 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.
|
|
||||||
|
|
||||||
### Specific Libraries' Workflows
|
### Summary
|
||||||
#### _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 Workflow
|
<--release summary to be used in announcements-->
|
||||||
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`
|
### Commit
|
||||||
19. - [ ] Update bdk-android version from `SNAPSHOT` version to release version
|
|
||||||
20. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
|
<--latest commit ID to include in this release-->
|
||||||
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).
|
### Changelog
|
||||||
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.
|
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
|
||||||
```shell
|
|
||||||
git tag v0.6.0 --sign --edit
|
### Checklist
|
||||||
git push upstream v0.6.0
|
|
||||||
```
|
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
|
||||||
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`)
|
branch **development** version is *MAJOR.MINOR.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
|
#### On the day of the feature freeze
|
||||||
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
|
Change the `master` branch to the next MINOR+1 version:
|
||||||
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)
|
- [ ] Switch to the `master` branch.
|
||||||
32. - [ ] Build and publish API docs for JVM, Android, and Java on the website
|
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR+1`, eg. `bump_dev_0_22`.
|
||||||
```shell
|
- [ ] Bump the `bump_dev_MAJOR_MINOR+1` branch to the next development MINOR+1 version.
|
||||||
./gradlew dokkaHtml # bdk-jvm (Dokka)
|
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
|
||||||
./gradlew dokkaJavadoc # bdk-jvm (java-style documentation)
|
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
|
||||||
./gradlew dokkaHtml # bdk-android (Dokka)
|
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR+1` branch to `master`.
|
||||||
```
|
- Title PR "Bump version to MAJOR.MINOR+1.0".
|
||||||
33. - [ ] Post in the announcement channel
|
|
||||||
34. - [ ] Tweet about the library
|
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/
|
||||||
|
|||||||
69
.github/ISSUE_TEMPLATE/patch_release.md
vendored
Normal file
69
.github/ISSUE_TEMPLATE/patch_release.md
vendored
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
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,16 +10,10 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
security_audit:
|
security_audit:
|
||||||
name: Security audit
|
name: Security Audit
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: bdk-ffi
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Check out PR branch"
|
- uses: actions/checkout@v2
|
||||||
uses: actions/checkout@v3
|
- uses: actions-rs/audit-check@v1
|
||||||
|
with:
|
||||||
- name: "Run audit"
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
run: |
|
|
||||||
cargo install cargo-audit
|
|
||||||
cargo-audit audit
|
|
||||||
|
|||||||
126
.github/workflows/build-python-wheels.yaml
vendored
Normal file
126
.github/workflows/build-python-wheels.yaml
vendored
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
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: Rust layer CI
|
name: bdk-ffi CI
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
@@ -8,77 +8,59 @@ on:
|
|||||||
- "bdk-ffi/**"
|
- "bdk-ffi/**"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build-test:
|
build-test:
|
||||||
name: "Build and test"
|
name: Build and test
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: bdk-ffi
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
rust:
|
rust:
|
||||||
- version: 1.73.0
|
- version: 1.63.0 # STABLE
|
||||||
clippy: true
|
clippy: true
|
||||||
|
- version: 1.61.0 # MSRV
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
- name: Generate cache key
|
||||||
- name: "Generate cache key"
|
|
||||||
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
|
||||||
|
- name: cache
|
||||||
- name: "Cache"
|
uses: actions/cache@v2
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/registry
|
~/.cargo/registry
|
||||||
~/.cargo/git
|
~/.cargo/git
|
||||||
target
|
target
|
||||||
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
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 }}
|
run: rustup default ${{ matrix.rust.version }}
|
||||||
|
- name: Set profile
|
||||||
- name: "Set profile"
|
|
||||||
run: rustup set profile minimal
|
run: rustup set profile minimal
|
||||||
|
- name: Add clippy
|
||||||
- name: "Add clippy"
|
|
||||||
if: ${{ matrix.rust.clippy }}
|
if: ${{ matrix.rust.clippy }}
|
||||||
run: rustup component add clippy
|
run: rustup component add clippy
|
||||||
|
- name: Update toolchain
|
||||||
- name: "Update toolchain"
|
|
||||||
run: rustup update
|
run: rustup update
|
||||||
|
- name: Build
|
||||||
- name: "Build"
|
|
||||||
run: cargo build
|
run: cargo build
|
||||||
|
- name: Clippy
|
||||||
- name: "Clippy"
|
|
||||||
if: ${{ matrix.rust.clippy }}
|
if: ${{ matrix.rust.clippy }}
|
||||||
run: cargo clippy --all-targets --features "uniffi/bindgen-tests" -- -D warnings
|
run: cargo clippy --all-targets -- -D warnings
|
||||||
|
- name: Test
|
||||||
- name: "Test"
|
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test
|
||||||
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test --features uniffi/bindgen-tests
|
|
||||||
|
|
||||||
fmt:
|
fmt:
|
||||||
name: "Rust fmt"
|
name: Rust fmt
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: bdk-ffi
|
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
- name: Set default toolchain
|
||||||
- name: "Set default toolchain"
|
|
||||||
run: rustup default nightly
|
run: rustup default nightly
|
||||||
|
- name: Set profile
|
||||||
- name: "Set profile"
|
|
||||||
run: rustup set profile minimal
|
run: rustup set profile minimal
|
||||||
|
- name: Add rustfmt
|
||||||
- name: "Add rustfmt"
|
|
||||||
run: rustup component add rustfmt
|
run: rustup component add rustfmt
|
||||||
|
- name: Update toolchain
|
||||||
- name: "Update toolchain"
|
|
||||||
run: rustup update
|
run: rustup update
|
||||||
|
- name: Check fmt
|
||||||
- name: "Check fmt"
|
|
||||||
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
|
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check
|
||||||
|
|||||||
93
.github/workflows/live-tests.yaml
vendored
93
.github/workflows/live-tests.yaml
vendored
@@ -1,93 +0,0 @@
|
|||||||
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,17 +1,27 @@
|
|||||||
name: Publish bdk-android to Maven Central
|
name: Publish bdk-android to Maven Central
|
||||||
on: [workflow_dispatch]
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
|
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
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: "Check out PR branch"
|
- name: Install Android NDK 21.4.7075529
|
||||||
uses: actions/checkout@v3
|
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: "Cache"
|
- name: Check out PR branch
|
||||||
uses: actions/cache@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: cache
|
||||||
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/registry
|
~/.cargo/registry
|
||||||
@@ -19,24 +29,21 @@ jobs:
|
|||||||
./target
|
./target
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: "Set up JDK"
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
|
||||||
- name: "Set default Rust version to 1.73.0"
|
- name: Install rust android targets
|
||||||
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
|
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: |
|
run: |
|
||||||
cd bdk-android
|
cd bdk-android
|
||||||
./gradlew buildAndroidLib
|
./gradlew buildAndroidLib
|
||||||
|
|
||||||
- name: "Publish to Maven Local and Maven Central"
|
- name: Publish to Maven Local and Maven Central
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
||||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
||||||
|
|||||||
103
.github/workflows/publish-jvm.yaml
vendored
103
.github/workflows/publish-jvm.yaml
vendored
@@ -2,14 +2,14 @@ name: Publish bdk-jvm to Maven Central
|
|||||||
on: [workflow_dispatch]
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-macOS-native-libs:
|
build-jvm-macOS-M1-native-lib:
|
||||||
name: "Create M1 and x86_64 native binaries"
|
name: Create M1 and x86_64 JVM native binaries
|
||||||
runs-on: macos-12
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout publishing branch"
|
- name: Checkout publishing branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Cache"
|
- name: Cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@@ -18,68 +18,42 @@ jobs:
|
|||||||
./target
|
./target
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: "Set up JDK"
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
|
||||||
- name: "Set default Rust version to 1.73.0"
|
- name: Install aarch64 Rust target
|
||||||
run: rustup default 1.73.0
|
|
||||||
|
|
||||||
- name: "Install aarch64 Rust target"
|
|
||||||
run: rustup target add aarch64-apple-darwin
|
run: rustup target add aarch64-apple-darwin
|
||||||
|
|
||||||
- name: "Build bdk-jvm library"
|
- name: Build bdk-jvm library
|
||||||
run: |
|
run: |
|
||||||
cd bdk-jvm
|
cd bdk-jvm
|
||||||
./gradlew buildJvmLib
|
./gradlew buildJvmLib
|
||||||
|
|
||||||
- name: "Upload macOS native libraries for reuse in publishing job"
|
# build aarch64 + x86_64 native libraries and upload
|
||||||
|
- name: Upload macOS native libraries for reuse in publishing job
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
name: artifact-macos
|
# name: no name is required because we upload the entire directory
|
||||||
|
# the default name "artifact" will be used
|
||||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-jvm/lib/src/main/resources/
|
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-jvm/lib/src/main/resources/
|
||||||
|
|
||||||
build-windows-native-lib:
|
build-jvm-full-library:
|
||||||
name: "Create Windows native binaries"
|
|
||||||
runs-on: windows-2022
|
|
||||||
steps:
|
|
||||||
- name: "Checkout publishing branch"
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
|
|
||||||
- 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: |
|
|
||||||
cd bdk-jvm
|
|
||||||
./gradlew buildJvmLib
|
|
||||||
|
|
||||||
- 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
|
name: Create full bdk-jvm library
|
||||||
needs: [build-macOS-native-libs, build-windows-native-lib]
|
needs: [build-jvm-macOS-M1-native-lib]
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout publishing branch"
|
- name: Checkout publishing branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Cache"
|
- name: Update bdk-ffi git submodule
|
||||||
|
run: |
|
||||||
|
git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git
|
||||||
|
git submodule update --init bdk-ffi
|
||||||
|
|
||||||
|
- name: Cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@@ -88,39 +62,26 @@ jobs:
|
|||||||
./target
|
./target
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: "Set up JDK"
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
|
||||||
- name: "Set default Rust version to 1.73.0"
|
- name: Build bdk-jvm library
|
||||||
run: rustup default 1.73.0
|
|
||||||
|
|
||||||
- name: "Build bdk-jvm library"
|
|
||||||
run: |
|
run: |
|
||||||
cd bdk-jvm
|
cd bdk-jvm
|
||||||
./gradlew buildJvmLib
|
./gradlew buildJvmLib
|
||||||
|
|
||||||
- name: "Download macOS native binaries from previous job"
|
- name: Download macOS native libraries from previous job
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v3
|
||||||
|
id: download
|
||||||
with:
|
with:
|
||||||
name: artifact-macos
|
# download the artifact created in the prior job (named "artifact")
|
||||||
|
name: artifact
|
||||||
path: ./bdk-jvm/lib/src/main/resources/
|
path: ./bdk-jvm/lib/src/main/resources/
|
||||||
|
|
||||||
- name: "Download Windows native libraries from previous job"
|
- name: Publish to Maven Central
|
||||||
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:
|
env:
|
||||||
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
|
||||||
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}
|
||||||
|
|||||||
182
.github/workflows/publish-python.yaml
vendored
182
.github/workflows/publish-python.yaml
vendored
@@ -1,4 +1,4 @@
|
|||||||
name: Publish bdkpython to PyPI
|
name: Build and publish Python wheels
|
||||||
on: [workflow_dispatch]
|
on: [workflow_dispatch]
|
||||||
|
|
||||||
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
|
# 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.
|
# tries to load glibc and fails because it requires a more recent version.
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-manylinux2014-x86_64-wheels:
|
build-manylinux2014-x86_64-wheel:
|
||||||
name: "Build Manylinux 2014 x86_64 wheel"
|
name: 'Build Manylinux 2014 x86_64 wheel'
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: bdk-python
|
working-directory: bdk-python
|
||||||
@@ -17,159 +17,127 @@ jobs:
|
|||||||
image: quay.io/pypa/manylinux2014_x86_64
|
image: quay.io/pypa/manylinux2014_x86_64
|
||||||
env:
|
env:
|
||||||
PLAT: manylinux2014_x86_64
|
PLAT: manylinux2014_x86_64
|
||||||
PYBIN: "/opt/python/${{ matrix.python }}/bin"
|
PYBIN: '/opt/python/${{ matrix.python }}/bin'
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python: # Update this list whenever the docker image is updated (check /opt/python/)
|
python: # Update this list whenever the docker image is updated (check /opt/python/)
|
||||||
|
- cp36-cp36m
|
||||||
|
- cp37-cp37m
|
||||||
- cp38-cp38
|
- cp38-cp38
|
||||||
- cp39-cp39
|
- cp39-cp39
|
||||||
- cp310-cp310
|
- cp310-cp310
|
||||||
|
- pp37-pypy37_pp73
|
||||||
|
- pp38-pypy38_pp73
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
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
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: stable
|
toolchain: stable
|
||||||
|
- name: install requirements
|
||||||
- name: "Generate bdk.py and binaries"
|
run: ${PYBIN}/pip install -r requirements.txt
|
||||||
run: bash ./scripts/generate-linux.sh
|
- name: generate bindings
|
||||||
|
run: bash generate.sh
|
||||||
- name: "Build wheel"
|
- name: build wheel
|
||||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
run: ${PYBIN}/pip wheel . --no-deps -w /tmp/wheelhouse
|
||||||
# see issue #350 for more information
|
- name: repair wheel
|
||||||
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
|
run: auditwheel repair /tmp/wheelhouse/* --plat "$PLAT" -w /tmp/wheelhouse-repaired
|
||||||
|
- uses: actions/upload-artifact@v2
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
|
||||||
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
path: /tmp/wheelhouse-repaired/*.whl
|
||||||
|
|
||||||
build-macos-arm64-wheels:
|
build-macos-universal-wheel:
|
||||||
name: "Build macOS arm64 wheel"
|
name: 'Build macOS universal wheel'
|
||||||
runs-on: macos-13
|
runs-on: macos-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: bdk-python
|
working-directory: bdk-python
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python:
|
python:
|
||||||
- "3.8"
|
- '3.7'
|
||||||
- "3.9"
|
- '3.8'
|
||||||
- "3.10"
|
- '3.9'
|
||||||
|
- '3.10'
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
- name: "Install Python"
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
- run: python3 --version
|
||||||
- name: "Generate bdk.py and binaries"
|
- run: rustup target add aarch64-apple-darwin
|
||||||
run: bash ./scripts/generate-macos-arm64.sh
|
- run: pip3 install --user -r requirements.txt
|
||||||
|
- run: pip3 install --user wheel
|
||||||
- name: "Build wheel"
|
- run: bash generate.sh
|
||||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
- name: build wheel
|
||||||
# see issue #350 for more information
|
env:
|
||||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_arm64 --verbose
|
ARCHFLAGS: "-arch x86_64 -arch arm64"
|
||||||
|
run: python3 setup.py -v bdist_wheel
|
||||||
- name: "Upload artifacts"
|
- uses: actions/upload-artifact@v2
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
with:
|
||||||
name: bdkpython-macos-arm64-${{ matrix.python }}
|
name: bdkpython-macos-${{ matrix.python }}
|
||||||
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
|
path: dist/*.whl
|
||||||
|
|
||||||
build-macos-x86_64-wheels:
|
build-windows-wheel:
|
||||||
name: "Build macOS x86_64 wheel"
|
name: 'Build windows wheel'
|
||||||
runs-on: macos-13
|
runs-on: windows-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: bdk-python
|
working-directory: bdk-python
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
python:
|
python:
|
||||||
- "3.8"
|
- '3.7'
|
||||||
- "3.9"
|
- '3.8'
|
||||||
- "3.10"
|
- '3.9'
|
||||||
|
- '3.10'
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
- uses: actions/setup-python@v2
|
||||||
- name: "Install Python"
|
|
||||||
uses: actions/setup-python@v4
|
|
||||||
with:
|
with:
|
||||||
python-version: ${{ matrix.python }}
|
python-version: ${{ matrix.python }}
|
||||||
|
- run: python --version
|
||||||
- name: "Generate bdk.py and binaries"
|
- run: pip install --user -r requirements.txt
|
||||||
run: bash ./scripts/generate-macos-x86_64.sh
|
- run: bash generate.sh
|
||||||
|
shell: bash
|
||||||
- name: "Build wheel"
|
- run: pip install --user wheel
|
||||||
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
|
- name: build wheel
|
||||||
# see issue #350 for more information
|
run: python setup.py -v bdist_wheel
|
||||||
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_x86_64 --verbose
|
- uses: actions/upload-artifact@v2
|
||||||
|
|
||||||
- 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:
|
with:
|
||||||
name: bdkpython-win-${{ matrix.python }}
|
name: bdkpython-win-${{ matrix.python }}
|
||||||
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
|
path: dist/*.whl
|
||||||
|
|
||||||
publish-pypi:
|
publish-pypi:
|
||||||
name: "Publish on PyPI"
|
name: 'Publish on PyPI'
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-latest
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
working-directory: bdk-python
|
working-directory: bdk-python
|
||||||
needs: [build-manylinux2014-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
|
needs: [build-manylinux2014-x86_64-wheel, build-macos-universal-wheel, build-windows-wheel]
|
||||||
|
# needs: [build-macos-universal-wheel]
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Download artifacts in dist/ directory"
|
- name: 'Download artifacts in dist/ directory'
|
||||||
uses: actions/download-artifact@v3
|
uses: actions/download-artifact@v2
|
||||||
with:
|
with:
|
||||||
path: dist/
|
path: dist/
|
||||||
|
|
||||||
# - name: "Publish on test PyPI"
|
# - name: Display structure of downloaded files
|
||||||
|
# run: ls -R
|
||||||
|
|
||||||
|
# - name: 'Publish on test PyPI'
|
||||||
# uses: pypa/gh-action-pypi-publish@release/v1
|
# uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
# with:
|
# with:
|
||||||
# user: __token__
|
# user: __token__
|
||||||
@@ -177,7 +145,7 @@ jobs:
|
|||||||
# repository_url: https://test.pypi.org/legacy/
|
# repository_url: https://test.pypi.org/legacy/
|
||||||
# packages_dir: dist/*/
|
# packages_dir: dist/*/
|
||||||
|
|
||||||
- name: "Publish on PyPI"
|
- name: 'Publish on PyPI'
|
||||||
uses: pypa/gh-action-pypi-publish@release/v1
|
uses: pypa/gh-action-pypi-publish@release/v1
|
||||||
with:
|
with:
|
||||||
user: __token__
|
user: __token__
|
||||||
|
|||||||
46
.github/workflows/test-android.yaml
vendored
46
.github/workflows/test-android.yaml
vendored
@@ -1,6 +1,5 @@
|
|||||||
name: Test Android
|
name: Test Android
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "bdk-ffi/**"
|
- "bdk-ffi/**"
|
||||||
@@ -10,20 +9,27 @@ on:
|
|||||||
- "bdk-ffi/**"
|
- "bdk-ffi/**"
|
||||||
- "bdk-android/**"
|
- "bdk-android/**"
|
||||||
|
|
||||||
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
|
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
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: "Show default version of NDK"
|
- name: Install Android NDK 21.4.7075529
|
||||||
run: echo $ANDROID_NDK_ROOT
|
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"
|
- name: Check out PR branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Cache"
|
- name: cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/registry
|
~/.cargo/registry
|
||||||
@@ -31,27 +37,21 @@ jobs:
|
|||||||
./target
|
./target
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: "Set up JDK"
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
|
||||||
- name: "Set default Rust version to 1.73.0"
|
- name: Install rust android targets
|
||||||
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
|
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||||
|
|
||||||
- name: "Build Android library"
|
- name: Build bdk-android library
|
||||||
run: |
|
run: |
|
||||||
cd bdk-android
|
cd bdk-android
|
||||||
./gradlew buildAndroidLib --console=plain
|
./gradlew buildAndroidLib
|
||||||
|
|
||||||
# There are currently no unit tests for bdk-android (see the tests in bdk-jvm instead) and the
|
- name: Run Android tests
|
||||||
# integration tests require the macOS image which is not working with the older NDK version we
|
run: |
|
||||||
# are using, so for now we just make sure that the library builds and omit the connectedTest
|
cd bdk-android
|
||||||
# - name: "Run Android connected tests"
|
./gradlew test --console=rich
|
||||||
# run: |
|
|
||||||
# cd bdk-android
|
|
||||||
# ./gradlew connectedAndroidTest --console=plain
|
|
||||||
|
|||||||
27
.github/workflows/test-jvm.yaml
vendored
27
.github/workflows/test-jvm.yaml
vendored
@@ -1,6 +1,5 @@
|
|||||||
name: Test Kotlin/JVM
|
name: Test JVM
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "bdk-ffi/**"
|
- "bdk-ffi/**"
|
||||||
@@ -14,11 +13,11 @@ jobs:
|
|||||||
build:
|
build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- name: "Check out PR branch"
|
- name: Check out PR branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Cache"
|
- name: cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
~/.cargo/registry
|
~/.cargo/registry
|
||||||
@@ -26,16 +25,18 @@ jobs:
|
|||||||
./target
|
./target
|
||||||
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
|
||||||
|
|
||||||
- name: "Set up JDK"
|
- name: Set up JDK
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
java-version: 11
|
java-version: 11
|
||||||
|
|
||||||
- name: "Set default Rust version to 1.73.0"
|
- name: Build bdk-jvm library
|
||||||
run: rustup default 1.73.0
|
|
||||||
|
|
||||||
- name: "Run JVM tests"
|
|
||||||
run: |
|
run: |
|
||||||
cd bdk-jvm
|
cd bdk-jvm
|
||||||
./gradlew test -P excludeConnectedTests
|
./gradlew buildJvmLib
|
||||||
|
|
||||||
|
- name: Run JVM tests
|
||||||
|
run: |
|
||||||
|
cd bdk-jvm
|
||||||
|
./gradlew test --console=rich
|
||||||
|
|||||||
189
.github/workflows/test-python.yaml
vendored
189
.github/workflows/test-python.yaml
vendored
@@ -1,189 +0,0 @@
|
|||||||
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,6 +1,5 @@
|
|||||||
name: Test Swift
|
name: Test Swift
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
push:
|
||||||
paths:
|
paths:
|
||||||
- "bdk-ffi/**"
|
- "bdk-ffi/**"
|
||||||
@@ -12,15 +11,41 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
name: "Build and test"
|
|
||||||
runs-on: macos-12
|
runs-on: macos-12
|
||||||
steps:
|
steps:
|
||||||
- name: "Checkout"
|
- name: Checkout branch
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: "Build Swift package"
|
- name: Install Rust targets
|
||||||
run: bash ./bdk-swift/build-local-swift.sh
|
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: "Run Swift tests"
|
- 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
|
||||||
working-directory: bdk-swift
|
working-directory: bdk-swift
|
||||||
run: swift test --skip LiveWalletTests --skip LiveTxBuilderTests
|
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
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -18,7 +18,7 @@ bdk.kt
|
|||||||
|
|
||||||
# Swift related
|
# Swift related
|
||||||
/.build
|
/.build
|
||||||
.swiftpm
|
/.swiftpm
|
||||||
/Packages
|
/Packages
|
||||||
/*.xcodeproj
|
/*.xcodeproj
|
||||||
xcuserdata/
|
xcuserdata/
|
||||||
@@ -31,6 +31,3 @@ bdkFFI.h
|
|||||||
BitcoinDevKit.swift
|
BitcoinDevKit.swift
|
||||||
bdk.swift
|
bdk.swift
|
||||||
.build
|
.build
|
||||||
|
|
||||||
# Python related
|
|
||||||
__pycache__
|
|
||||||
144
CHANGELOG.md
144
CHANGELOG.md
@@ -1,134 +1,13 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
Changelog information can also be found in each release's git tag (which can be viewed with `git tag -ln100 "v*"`), as well as on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases) page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
|
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.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
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).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [0.30.0]
|
## [Unreleased]
|
||||||
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]
|
## [v0.9.0]
|
||||||
- Breaking Changes
|
- Breaking Changes
|
||||||
@@ -143,7 +22,7 @@ Changelog
|
|||||||
- APIs Added [#154]
|
- APIs Added [#154]
|
||||||
- `generate_mnemonic()`, returns string mnemonic
|
- `generate_mnemonic()`, returns string mnemonic
|
||||||
- `interface DescriptorSecretKey`
|
- `interface DescriptorSecretKey`
|
||||||
- `new(Network, string_mnenoinc, password)`, constructs DescriptorSecretKey
|
- `new(Network, string_mnenoinc, password)`, contructs DescriptorSecretKey
|
||||||
- `derive(DerivationPath)`, derives and returns child DescriptorSecretKey
|
- `derive(DerivationPath)`, derives and returns child DescriptorSecretKey
|
||||||
- `extend(DerivationPath)`, extends and returns DescriptorSecretKey
|
- `extend(DerivationPath)`, extends and returns DescriptorSecretKey
|
||||||
- `as_public()`, returns DescriptorSecretKey as DescriptorPublicKey
|
- `as_public()`, returns DescriptorSecretKey as DescriptorPublicKey
|
||||||
@@ -223,14 +102,9 @@ Changelog
|
|||||||
|
|
||||||
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
|
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
|
||||||
|
|
||||||
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
|
## [v0.2.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
|
[unreleased]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.9.0...HEAD
|
||||||
[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.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.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
|
[v0.7.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.6.0...v0.7.0
|
||||||
|
|||||||
1283
bdk-ffi/Cargo.lock → Cargo.lock
generated
1283
bdk-ffi/Cargo.lock → Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[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*
|
||||||
20
README.md
20
README.md
@@ -7,12 +7,9 @@
|
|||||||
<a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
|
<a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
|
||||||
</p>
|
</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
|
The workspace in this repository creates the `libbdkffi` multi-language library for the Rust-based
|
||||||
[bdk] library from the [Bitcoin Dev Kit] project.
|
[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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
@@ -26,9 +23,12 @@ The below directories (a separate repository in the case of bdk-swift) include i
|
|||||||
| Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | |
|
| Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | |
|
||||||
| Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | |
|
| Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | |
|
||||||
|
|
||||||
## Minimum Supported Rust Version (MSRV)
|
## Language bindings generator tool
|
||||||
|
Use the `bdk-ffi-bindgen` tool to generate language binding code for the above supported languages.
|
||||||
This library should compile with any combination of features with Rust 1.73.0.
|
To run `bdk-ffi-bindgen` and see the available options use the command:
|
||||||
|
```shell
|
||||||
|
cargo run -p bdk-ffi-bindgen -- --help
|
||||||
|
```
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -69,7 +69,7 @@ repositories {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.bitcoindevkit:bdk-jvm:<version>")
|
implementation("org.bitcoindevkit:bdk-jvm:<version>")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
4
api-docs/kotlin/Module1.md
Normal file
4
api-docs/kotlin/Module1.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Module bdk-android
|
||||||
|
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android.
|
||||||
|
|
||||||
|
# Package org.bitcoindevkit
|
||||||
4
api-docs/kotlin/Module2.md
Normal file
4
api-docs/kotlin/Module2.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Module bdk-jvm
|
||||||
|
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Kotlin and Java on the JVM.
|
||||||
|
|
||||||
|
# Package org.bitcoindevkit
|
||||||
46
api-docs/kotlin/build.gradle.kts
Normal file
46
api-docs/kotlin/build.gradle.kts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
api-docs/kotlin/deploy.sh
Normal file
8
api-docs/kotlin/deploy.sh
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
./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
api-docs/kotlin/gradle.properties
Normal file
1
api-docs/kotlin/gradle.properties
Normal file
@@ -0,0 +1 @@
|
|||||||
|
kotlin.code.style=official
|
||||||
BIN
api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
api-docs/kotlin/gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
api-docs/kotlin/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
api-docs/kotlin/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
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
Executable file
234
api-docs/kotlin/gradlew
vendored
Executable file
@@ -0,0 +1,234 @@
|
|||||||
|
#!/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
Normal file
89
api-docs/kotlin/gradlew.bat
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
@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
api-docs/kotlin/settings.gradle.kts
Normal file
1
api-docs/kotlin/settings.gradle.kts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
rootProject.name = "BDK Android and BDK JVM API Docs"
|
||||||
595
api-docs/kotlin/src/main/kotlin/org/bitcoindevkit/bdk.kt
Normal file
595
api-docs/kotlin/src/main/kotlin/org/bitcoindevkit/bdk.kt
Normal file
@@ -0,0 +1,595 @@
|
|||||||
|
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.
|
||||||
|
*
|
||||||
|
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
|
||||||
|
*/
|
||||||
|
data class ElectrumConfig (
|
||||||
|
var url: String,
|
||||||
|
var socks5: String?,
|
||||||
|
var retry: UByte,
|
||||||
|
var timeout: UByte?,
|
||||||
|
var stopGap: ULong
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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?
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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): String {}
|
||||||
|
|
||||||
|
/** 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: String,
|
||||||
|
changeDescriptor: String,
|
||||||
|
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 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. */
|
||||||
|
fun getBalance(): ULong {}
|
||||||
|
|
||||||
|
/** 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(address: String): 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
}
|
||||||
245
api-docs/kotlin/src/test/kotlin/org/bitcoindevkit/Samples.kt
Normal file
245
api-docs/kotlin/src/test/kotlin/org/bitcoindevkit/Samples.kt
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
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())
|
||||||
|
}
|
||||||
@@ -9,26 +9,26 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.bitcoindevkit:bdk-android:<version>")
|
implementation("org.bitcoindevkit:bdk-android:<version>")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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).
|
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
|
||||||
```kotlin
|
```kotlin
|
||||||
import org.bitcoindevkit.*
|
import org.bitcoindevkit.*
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||||
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
|
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
|
||||||
|
|
||||||
val databaseConfig = DatabaseConfig.Memory
|
val databaseConfig = DatabaseConfig.Memory
|
||||||
|
|
||||||
val blockchainConfig = BlockchainConfig.Electrum(
|
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)
|
||||||
)
|
)
|
||||||
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
||||||
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
|
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Snapshot releases
|
### Snapshot releases
|
||||||
@@ -39,12 +39,11 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.bitcoindevkit:bdk-android:<version-SNAPSHOT>")
|
implementation("org.bitcoindevkit:bdk-android:<version-SNAPSHOT>")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Example Projects
|
### Example Projects
|
||||||
* [bdk-kotlin-example-wallet](https://github.com/bitcoindevkit/bdk-kotlin-example-wallet)
|
|
||||||
* [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet)
|
* [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet)
|
||||||
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
|
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
|
||||||
|
|
||||||
@@ -56,22 +55,18 @@ _Note that Kotlin version `1.6.10` or later is required to build the library._
|
|||||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||||
```
|
```
|
||||||
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||||
3. Install Rust (note that we are currently building using Rust 1.73.0):
|
3. If building on macOS install required intel and m1 jvm targets
|
||||||
```shell
|
|
||||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
|
||||||
rustup default 1.73.0
|
|
||||||
```
|
|
||||||
4. Install required targets
|
4. Install required targets
|
||||||
```sh
|
```sh
|
||||||
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
|
||||||
```
|
```
|
||||||
5. Install Android SDK and Build-Tools for API level 30+
|
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
|
6. Setup `$ANDROID_SDK_ROOT` and `$ANDROID_NDK_ROOT` path variables (which are required by the
|
||||||
build tool), for example (note that currently, NDK version 25.2.9519653 or above is required):
|
build tool), for example (NDK major version 21 is required):
|
||||||
```shell
|
```shell
|
||||||
export ANDROID_SDK_ROOT=~/Android/Sdk
|
export ANDROID_SDK_ROOT=~/Android/Sdk
|
||||||
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
|
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.<NDK_VERSION>
|
||||||
```
|
```
|
||||||
7. Build kotlin bindings
|
7. Build kotlin bindings
|
||||||
```sh
|
```sh
|
||||||
# build Android library
|
# build Android library
|
||||||
@@ -100,22 +95,5 @@ and use the `publishToMavenLocal` task without excluding the signing task:
|
|||||||
./gradlew publishToMavenLocal
|
./gradlew publishToMavenLocal
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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 {
|
|
||||||
// ...
|
|
||||||
implementation("net.java.dev.jna:jna:5.12.1")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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`]: https://github.com/bitcoindevkit/bdk
|
||||||
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
|
|||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
libraryVersion=1.0.0-alpha.2-rc1
|
libraryVersion=0.25.0
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
// library version is defined in gradle.properties
|
// library version is defined in gradle.properties
|
||||||
val libraryVersion: String by project
|
val libraryVersion: String by project
|
||||||
|
|
||||||
@@ -53,8 +51,7 @@ dependencies {
|
|||||||
androidTestImplementation("com.github.tony19:logback-android:2.0.0")
|
androidTestImplementation("com.github.tony19:logback-android:2.0.0")
|
||||||
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
androidTestImplementation("androidx.test.ext:junit:1.1.3")
|
||||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
|
||||||
androidTestImplementation("org.jetbrains.kotlin:kotlin-test:1.6.10")
|
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1")
|
||||||
androidTestImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.6.10")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
afterEvaluate {
|
afterEvaluate {
|
||||||
@@ -82,9 +79,14 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
developers {
|
developers {
|
||||||
developer {
|
developer {
|
||||||
id.set("bdkdevelopers")
|
id.set("notmandatory")
|
||||||
name.set("Bitcoin Dev Kit Developers")
|
name.set("Steve Myers")
|
||||||
email.set("dev@bitcoindevkit.org")
|
email.set("notmandatory@noreply.github.org")
|
||||||
|
}
|
||||||
|
developer {
|
||||||
|
id.set("artfuldev")
|
||||||
|
name.set("Sudarsan Balaji")
|
||||||
|
email.set("sudarsan.balaji@artfuldev.com")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scm {
|
scm {
|
||||||
@@ -105,9 +107,3 @@ signing {
|
|||||||
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
|
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
|
||||||
sign(publishing.publications)
|
sign(publishing.publications)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This task dependency ensures that we build the bindings
|
|
||||||
// binaries before running the tests
|
|
||||||
tasks.withType<KotlinCompile> {
|
|
||||||
dependsOn("buildAndroidLib")
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
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 =
|
||||||
|
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||||
|
|
||||||
|
private val databaseConfig = DatabaseConfig.Memory
|
||||||
|
|
||||||
|
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||||
|
ElectrumConfig(
|
||||||
|
"ssl://electrum.blockstream.info:60002",
|
||||||
|
null,
|
||||||
|
5u,
|
||||||
|
null,
|
||||||
|
100u
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
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'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
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()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
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,7 +21,8 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
val buildAndroidAarch64Binary by tasks.register<Exec>("buildAndroidAarch64Binary") {
|
val buildAndroidAarch64Binary by tasks.register<Exec>("buildAndroidAarch64Binary") {
|
||||||
|
|
||||||
workingDir("${projectDir}/../../bdk-ffi")
|
workingDir("${projectDir}/../../bdk-ffi")
|
||||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
|
val cargoArgs: MutableList<String> =
|
||||||
|
mutableListOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
|
||||||
|
|
||||||
executable("cargo")
|
executable("cargo")
|
||||||
args(cargoArgs)
|
args(cargoArgs)
|
||||||
@@ -35,9 +36,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
environment(
|
environment(
|
||||||
// add build toolchain to PATH
|
// add build toolchain to PATH
|
||||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
Pair("PATH",
|
||||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||||
Pair("AR", "llvm-ar"),
|
|
||||||
|
Pair("CFLAGS", "-D__ANDROID_API__=21"),
|
||||||
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
|
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
|
||||||
Pair("CC", "aarch64-linux-android21-clang")
|
Pair("CC", "aarch64-linux-android21-clang")
|
||||||
)
|
)
|
||||||
@@ -51,7 +53,8 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
val buildAndroidX86_64Binary by tasks.register<Exec>("buildAndroidX86_64Binary") {
|
val buildAndroidX86_64Binary by tasks.register<Exec>("buildAndroidX86_64Binary") {
|
||||||
|
|
||||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
|
val cargoArgs: MutableList<String> =
|
||||||
|
mutableListOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
|
||||||
|
|
||||||
executable("cargo")
|
executable("cargo")
|
||||||
args(cargoArgs)
|
args(cargoArgs)
|
||||||
@@ -65,9 +68,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
environment(
|
environment(
|
||||||
// add build toolchain to PATH
|
// add build toolchain to PATH
|
||||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
Pair("PATH",
|
||||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||||
Pair("AR", "llvm-ar"),
|
|
||||||
|
Pair("CFLAGS", "-D__ANDROID_API__=21"),
|
||||||
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
|
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
|
||||||
Pair("CC", "x86_64-linux-android21-clang")
|
Pair("CC", "x86_64-linux-android21-clang")
|
||||||
)
|
)
|
||||||
@@ -81,7 +85,8 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
val buildAndroidArmv7Binary by tasks.register<Exec>("buildAndroidArmv7Binary") {
|
val buildAndroidArmv7Binary by tasks.register<Exec>("buildAndroidArmv7Binary") {
|
||||||
|
|
||||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
|
val cargoArgs: MutableList<String> =
|
||||||
|
mutableListOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
|
||||||
|
|
||||||
executable("cargo")
|
executable("cargo")
|
||||||
args(cargoArgs)
|
args(cargoArgs)
|
||||||
@@ -95,10 +100,12 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
environment(
|
environment(
|
||||||
// add build toolchain to PATH
|
// add build toolchain to PATH
|
||||||
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
Pair("PATH",
|
||||||
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
|
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
|
||||||
Pair("AR", "llvm-ar"),
|
|
||||||
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi21-clang"),
|
Pair("CFLAGS", "-D__ANDROID_API__=21"),
|
||||||
|
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER",
|
||||||
|
"armv7a-linux-androideabi21-clang"),
|
||||||
Pair("CC", "armv7a-linux-androideabi21-clang")
|
Pair("CC", "armv7a-linux-androideabi21-clang")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -117,15 +124,15 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
into("${project.projectDir}/../lib/src/main/jniLibs/")
|
into("${project.projectDir}/../lib/src/main/jniLibs/")
|
||||||
|
|
||||||
into("arm64-v8a") {
|
into("arm64-v8a") {
|
||||||
from("${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
from("${project.projectDir}/../../target/aarch64-linux-android/release-smaller/libbdkffi.so")
|
||||||
}
|
}
|
||||||
|
|
||||||
into("x86_64") {
|
into("x86_64") {
|
||||||
from("${project.projectDir}/../../bdk-ffi/target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
from("${project.projectDir}/../../target/x86_64-linux-android/release-smaller/libbdkffi.so")
|
||||||
}
|
}
|
||||||
|
|
||||||
into("armeabi-v7a") {
|
into("armeabi-v7a") {
|
||||||
from("${project.projectDir}/../../bdk-ffi/target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
|
from("${project.projectDir}/../../target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
|
||||||
}
|
}
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
@@ -137,17 +144,18 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
|
|||||||
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
|
||||||
dependsOn(moveNativeAndroidLibs)
|
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")
|
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")
|
executable("cargo")
|
||||||
args(cargoArgs)
|
args(
|
||||||
|
"run",
|
||||||
|
"--package",
|
||||||
|
"bdk-ffi-bindgen",
|
||||||
|
"--",
|
||||||
|
"--language",
|
||||||
|
"kotlin",
|
||||||
|
"--out-dir",
|
||||||
|
"../bdk-android/lib/src/main/kotlin"
|
||||||
|
)
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
println("Android bindings file successfully created")
|
println("Android bindings file successfully created")
|
||||||
|
|||||||
10
bdk-ffi-bindgen/Cargo.toml
Normal file
10
bdk-ffi-bindgen/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[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"
|
||||||
138
bdk-ffi-bindgen/src/main.rs
Normal file
138
bdk-ffi-bindgen/src/main.rs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
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(())
|
||||||
|
}
|
||||||
@@ -1,47 +1,19 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bdk-ffi"
|
name = "bdk-ffi"
|
||||||
version = "1.0.0-alpha.2"
|
version = "0.25.0"
|
||||||
homepage = "https://bitcoindevkit.org"
|
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
|
||||||
repository = "https://github.com/bitcoindevkit/bdk"
|
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["lib", "staticlib", "cdylib"]
|
crate-type = ["staticlib", "cdylib"]
|
||||||
name = "bdkffi"
|
name = "bdkffi"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "uniffi-bindgen"
|
|
||||||
path = "uniffi-bindgen.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["uniffi/cli"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bdk = { version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
|
bdk = { version = "0.25", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
|
||||||
|
|
||||||
# TODO 22: The bdk_esplora crate uses esplora_client which uses reqwest for async. By default it uses the system
|
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
# openssl library, which is creating problems for cross-compilation. I'd rather use rustls, but it's hidden
|
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
# 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]
|
[build-dependencies]
|
||||||
uniffi = { version = "=0.25.1", features = ["build"] }
|
uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
|
||||||
|
|
||||||
[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() {
|
fn main() {
|
||||||
uniffi::generate_scaffolding("./src/bdk.udl").unwrap();
|
uniffi_build::generate_scaffolding("src/bdk.udl").unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,13 @@
|
|||||||
namespace bdk {};
|
namespace bdk {
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// bdk crate - root module
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
enum KeychainKind {
|
|
||||||
"External",
|
|
||||||
"Internal",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// bdk crate - wallet module
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
enum BdkError {
|
enum BdkError {
|
||||||
|
"InvalidU32Bytes",
|
||||||
"Generic",
|
"Generic",
|
||||||
|
"MissingCachedScripts",
|
||||||
|
"ScriptDoesntHaveAddressForm",
|
||||||
"NoRecipients",
|
"NoRecipients",
|
||||||
"NoUtxosSelected",
|
"NoUtxosSelected",
|
||||||
"OutputBelowDustLimit",
|
"OutputBelowDustLimit",
|
||||||
@@ -35,101 +27,263 @@ enum BdkError {
|
|||||||
"SpendingPolicyRequired",
|
"SpendingPolicyRequired",
|
||||||
"InvalidPolicyPathError",
|
"InvalidPolicyPathError",
|
||||||
"Signer",
|
"Signer",
|
||||||
|
"InvalidNetwork",
|
||||||
|
"InvalidProgressValue",
|
||||||
|
"ProgressUpdateError",
|
||||||
"InvalidOutpoint",
|
"InvalidOutpoint",
|
||||||
"Descriptor",
|
"Descriptor",
|
||||||
|
"Encode",
|
||||||
"Miniscript",
|
"Miniscript",
|
||||||
"MiniscriptPsbt",
|
"MiniscriptPsbt",
|
||||||
"Bip32",
|
"Bip32",
|
||||||
|
"Secp256k1",
|
||||||
|
"Json",
|
||||||
|
"Hex",
|
||||||
"Psbt",
|
"Psbt",
|
||||||
};
|
"PsbtParse",
|
||||||
|
"Electrum",
|
||||||
enum ChangeSpendPolicy {
|
"Esplora",
|
||||||
"ChangeAllowed",
|
"Sled",
|
||||||
"OnlyChange",
|
"Rusqlite",
|
||||||
"ChangeForbidden"
|
|
||||||
};
|
|
||||||
|
|
||||||
interface Balance {
|
|
||||||
u64 immature();
|
|
||||||
|
|
||||||
u64 trusted_pending();
|
|
||||||
|
|
||||||
u64 untrusted_pending();
|
|
||||||
|
|
||||||
u64 confirmed();
|
|
||||||
|
|
||||||
u64 trusted_spendable();
|
|
||||||
|
|
||||||
u64 total();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
dictionary AddressInfo {
|
dictionary AddressInfo {
|
||||||
u32 index;
|
u32 index;
|
||||||
Address address;
|
string address;
|
||||||
KeychainKind keychain;
|
};
|
||||||
|
|
||||||
|
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]
|
[Enum]
|
||||||
interface AddressIndex {
|
interface DatabaseConfig {
|
||||||
New();
|
Memory();
|
||||||
LastUnused();
|
Sled(SledDbConfiguration config);
|
||||||
Peek(u32 index);
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary EsploraConfig {
|
||||||
|
string base_url;
|
||||||
|
string? proxy;
|
||||||
|
u8? concurrency;
|
||||||
|
u64 stop_gap;
|
||||||
|
u64? timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
[Enum]
|
||||||
|
interface BlockchainConfig {
|
||||||
|
Electrum(ElectrumConfig config);
|
||||||
|
Esplora(EsploraConfig 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;
|
||||||
|
KeychainKind keychain;
|
||||||
|
boolean is_spent;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary ScriptAmount {
|
||||||
|
Script script;
|
||||||
|
u64 amount;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Wallet {
|
interface Wallet {
|
||||||
[Name=new_no_persist, Throws=BdkError]
|
[Throws=BdkError]
|
||||||
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network);
|
constructor(string descriptor, string? change_descriptor, Network network, DatabaseConfig database_config);
|
||||||
|
|
||||||
|
[Throws=BdkError]
|
||||||
AddressInfo get_address(AddressIndex address_index);
|
AddressInfo get_address(AddressIndex address_index);
|
||||||
|
|
||||||
AddressInfo get_internal_address(AddressIndex address_index);
|
[Throws=BdkError]
|
||||||
|
Balance get_balance();
|
||||||
|
|
||||||
|
[Throws=BdkError]
|
||||||
|
boolean sign([ByRef] PartiallySignedTransaction psbt);
|
||||||
|
|
||||||
|
[Throws=BdkError]
|
||||||
|
sequence<TransactionDetails> list_transactions();
|
||||||
|
|
||||||
Network network();
|
Network network();
|
||||||
|
|
||||||
Balance get_balance();
|
[Throws=BdkError]
|
||||||
|
void sync([ByRef] Blockchain blockchain, Progress? progress);
|
||||||
boolean is_mine(Script script);
|
|
||||||
|
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
void apply_update(Update update);
|
sequence<LocalUtxo> list_unspent();
|
||||||
|
|
||||||
[Throws=BdkError]
|
|
||||||
boolean sign(PartiallySignedTransaction psbt);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Update {};
|
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();
|
||||||
|
|
||||||
|
[Throws=BdkError]
|
||||||
|
PartiallySignedTransaction combine(PartiallySignedTransaction other);
|
||||||
|
|
||||||
|
u64? fee_amount();
|
||||||
|
|
||||||
|
FeeRate? fee_rate();
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary TxBuilderResult {
|
||||||
|
PartiallySignedTransaction psbt;
|
||||||
|
TransactionDetails transaction_details;
|
||||||
|
};
|
||||||
|
|
||||||
interface TxBuilder {
|
interface TxBuilder {
|
||||||
constructor();
|
constructor();
|
||||||
|
|
||||||
TxBuilder add_recipient(Script script, u64 amount);
|
TxBuilder add_recipient(Script script, u64 amount);
|
||||||
|
|
||||||
TxBuilder set_recipients(sequence<ScriptAmount> script_amount);
|
|
||||||
|
|
||||||
TxBuilder add_unspendable(OutPoint unspendable);
|
TxBuilder add_unspendable(OutPoint unspendable);
|
||||||
|
|
||||||
TxBuilder add_utxo(OutPoint outpoint);
|
TxBuilder add_utxo(OutPoint outpoint);
|
||||||
|
|
||||||
TxBuilder change_policy(ChangeSpendPolicy change_policy);
|
TxBuilder add_utxos(sequence<OutPoint> outpoints);
|
||||||
|
|
||||||
TxBuilder do_not_spend_change();
|
TxBuilder do_not_spend_change();
|
||||||
|
|
||||||
|
TxBuilder manually_selected_only();
|
||||||
|
|
||||||
TxBuilder only_spend_change();
|
TxBuilder only_spend_change();
|
||||||
|
|
||||||
TxBuilder manually_selected_only();
|
TxBuilder unspendable(sequence<OutPoint> unspendable);
|
||||||
|
|
||||||
TxBuilder fee_rate(float sat_per_vbyte);
|
TxBuilder fee_rate(float sat_per_vbyte);
|
||||||
|
|
||||||
|
TxBuilder fee_absolute(u64 fee_amount);
|
||||||
|
|
||||||
TxBuilder drain_wallet();
|
TxBuilder drain_wallet();
|
||||||
|
|
||||||
|
TxBuilder drain_to(string address);
|
||||||
|
|
||||||
|
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]
|
[Throws=BdkError]
|
||||||
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
PartiallySignedTransaction finish([ByRef] Wallet wallet);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// bdk crate - descriptor module
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
interface Mnemonic {
|
interface Mnemonic {
|
||||||
constructor(WordCount word_count);
|
constructor(WordCount word_count);
|
||||||
|
|
||||||
@@ -179,129 +333,13 @@ interface DescriptorPublicKey {
|
|||||||
string as_string();
|
string as_string();
|
||||||
};
|
};
|
||||||
|
|
||||||
interface Descriptor {
|
interface Address {
|
||||||
[Throws=BdkError]
|
[Throws=BdkError]
|
||||||
constructor(string descriptor, Network network);
|
constructor(string address);
|
||||||
|
|
||||||
[Name=new_bip44]
|
Script script_pubkey();
|
||||||
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
|
||||||
|
|
||||||
[Name=new_bip44_public]
|
|
||||||
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
|
||||||
|
|
||||||
[Name=new_bip49]
|
|
||||||
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
|
||||||
|
|
||||||
[Name=new_bip49_public]
|
|
||||||
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
|
|
||||||
|
|
||||||
[Name=new_bip84]
|
|
||||||
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
|
|
||||||
|
|
||||||
[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();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
// bdk_esplora crate
|
|
||||||
// ------------------------------------------------------------------------
|
|
||||||
|
|
||||||
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 {
|
interface Script {
|
||||||
constructor(sequence<u8> raw_output_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;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,311 +0,0 @@
|
|||||||
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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,419 +0,0 @@
|
|||||||
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))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
@@ -1,398 +0,0 @@
|
|||||||
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"
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
1598
bdk-ffi/src/lib.rs
1598
bdk-ffi/src/lib.rs
File diff suppressed because it is too large
Load Diff
@@ -1,910 +0,0 @@
|
|||||||
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,5 +6,10 @@ class TestBdk(unittest.TestCase):
|
|||||||
def test_some_enum(self):
|
def test_some_enum(self):
|
||||||
network = Network.TESTNET
|
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__':
|
if __name__=='__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
uniffi::build_foreign_language_testcases!(
|
uniffi_macros::build_foreign_language_testcases!(
|
||||||
"tests/bindings/test.kts",
|
["src/bdk.udl",],
|
||||||
"tests/bindings/test.swift",
|
[
|
||||||
"tests/bindings/test.py",
|
"tests/bindings/test.kts",
|
||||||
|
"tests/bindings/test.swift",
|
||||||
|
"tests/bindings/test.py"
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
uniffi::uniffi_bindgen_main()
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# bdk-jvm
|
# bdk-android
|
||||||
This project builds a .jar package for the JVM platform that provide Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository.
|
This project builds a .jar package for the JVM platform that provide Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository.
|
||||||
|
|
||||||
## How to Use
|
## How to Use
|
||||||
@@ -13,22 +13,22 @@ dependencies {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
You may then import and use the `org.bitcoindevkit` library in your Kotlin code like so. Note that this example is for the `0.30.0` release. For examples of the 1.0 API in the alpha releases, take a look at the tests [here](https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit).
|
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
|
||||||
```kotlin
|
```kotlin
|
||||||
import org.bitcoindevkit.*
|
import org.bitcoindevkit.*
|
||||||
|
|
||||||
// ...
|
// ...
|
||||||
|
|
||||||
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
|
val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||||
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
|
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
|
||||||
|
|
||||||
val databaseConfig = DatabaseConfig.Memory
|
val databaseConfig = DatabaseConfig.Memory
|
||||||
|
|
||||||
val blockchainConfig = BlockchainConfig.Electrum(
|
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)
|
||||||
)
|
)
|
||||||
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
|
||||||
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
|
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Snapshot releases
|
### Snapshot releases
|
||||||
@@ -39,38 +39,31 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("org.bitcoindevkit:bdk-jvm:<version-SNAPSHOT>")
|
implementation("org.bitcoindevkit:bdk-jvm:<version-SNAPSHOT>")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
## Example Projects
|
## Example Projects
|
||||||
* [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine)
|
* [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine)
|
||||||
|
|
||||||
## How to build
|
## How to build
|
||||||
_Note that Kotlin version `1.6.10` or later is required to build the library._
|
_Note that Kotlin version `1.6.10` or later is required to build the library._
|
||||||
1. Install JDK 11. It must be version 11 (not 17), otherwise it won't build. For example, with SDKMAN!:
|
|
||||||
```shell
|
1. Clone this repository.
|
||||||
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
|
```shell
|
||||||
git clone https://github.com/bitcoindevkit/bdk-ffi
|
git clone https://github.com/bitcoindevkit/bdk-ffi
|
||||||
```
|
```
|
||||||
4. If building on macOS install required intel and m1 jvm targets
|
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
|
||||||
|
3. If building on macOS install required intel and m1 jvm targets
|
||||||
```sh
|
```sh
|
||||||
rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
rustup target add x86_64-apple-darwin aarch64-apple-darwin
|
||||||
```
|
```
|
||||||
5. Build kotlin bindings
|
4. Build kotlin bindings
|
||||||
```sh
|
```sh
|
||||||
./gradlew buildJvmLib
|
# build JVM library
|
||||||
```
|
./gradlew buildJvmLib
|
||||||
|
```
|
||||||
|
|
||||||
## How to publish to your local Maven repo
|
## How to publish to your local Maven repo
|
||||||
```shell
|
```shell
|
||||||
@@ -89,20 +82,5 @@ and use the `publishToMavenLocal` task without excluding the signing task:
|
|||||||
./gradlew publishToMavenLocal
|
./gradlew publishToMavenLocal
|
||||||
```
|
```
|
||||||
|
|
||||||
## 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 {
|
|
||||||
// ...
|
|
||||||
implementation("net.java.dev.jna:jna:5.12.1")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
[`bdk`]: https://github.com/bitcoindevkit/bdk
|
||||||
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
org.gradle.jvmargs=-Xmx1536m
|
org.gradle.jvmargs=-Xmx1536m
|
||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
libraryVersion=1.0.0-alpha.2-rc1
|
libraryVersion=0.25.0
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.*
|
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.*
|
||||||
import org.gradle.api.tasks.testing.logging.TestLogEvent.*
|
import org.gradle.api.tasks.testing.logging.TestLogEvent.*
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|
||||||
|
|
||||||
// library version is defined in gradle.properties
|
// library version is defined in gradle.properties
|
||||||
val libraryVersion: String by project
|
val libraryVersion: String by project
|
||||||
@@ -20,39 +19,21 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_11
|
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||||
targetCompatibility = JavaVersion.VERSION_11
|
targetCompatibility = JavaVersion.VERSION_1_8
|
||||||
withSourcesJar()
|
withSourcesJar()
|
||||||
withJavadocJar()
|
withJavadocJar()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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> {
|
tasks.withType<Test> {
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
testLogging {
|
testLogging {
|
||||||
events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
|
events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
|
||||||
exceptionFormat = FULL
|
exceptionFormat = FULL
|
||||||
showExceptions = true
|
showExceptions = true
|
||||||
showStackTraces = true
|
|
||||||
showCauses = true
|
showCauses = true
|
||||||
|
showStackTraces = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,9 +73,14 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
developers {
|
developers {
|
||||||
developer {
|
developer {
|
||||||
id.set("bdkdevelopers")
|
id.set("notmandatory")
|
||||||
name.set("Bitcoin Dev Kit Developers")
|
name.set("Steve Myers")
|
||||||
email.set("dev@bitcoindevkit.org")
|
email.set("notmandatory@noreply.github.org")
|
||||||
|
}
|
||||||
|
developer {
|
||||||
|
id.set("artfuldev")
|
||||||
|
name.set("Sudarsan Balaji")
|
||||||
|
email.set("sudarsan.balaji@artfuldev.com")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
scm {
|
scm {
|
||||||
@@ -115,13 +101,3 @@ signing {
|
|||||||
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
|
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
|
||||||
sign(publishing.publications)
|
sign(publishing.publications)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This task dependency ensures that we build the bindings
|
|
||||||
// binaries before running the tests
|
|
||||||
tasks.withType<KotlinCompile> {
|
|
||||||
dependsOn("buildJvmLib")
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
73
bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt
Normal file
73
bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
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 =
|
||||||
|
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||||
|
|
||||||
|
private val databaseConfig = DatabaseConfig.Memory
|
||||||
|
|
||||||
|
private val blockchainConfig = BlockchainConfig.Electrum(
|
||||||
|
ElectrumConfig(
|
||||||
|
"ssl://electrum.blockstream.info:60002",
|
||||||
|
null,
|
||||||
|
5u,
|
||||||
|
null,
|
||||||
|
100u
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
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,13 +4,11 @@ package org.bitcoindevkit.plugins
|
|||||||
val operatingSystem: OS = when {
|
val operatingSystem: OS = when {
|
||||||
System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
|
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("linux", ignoreCase = true) -> OS.LINUX
|
||||||
System.getProperty("os.name").contains("windows", ignoreCase = true) -> OS.WINDOWS
|
|
||||||
else -> OS.OTHER
|
else -> OS.OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class OS {
|
enum class OS {
|
||||||
MAC,
|
MAC,
|
||||||
LINUX,
|
LINUX,
|
||||||
WINDOWS,
|
|
||||||
OTHER,
|
OTHER,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import org.gradle.kotlin.dsl.getValue
|
|||||||
import org.gradle.kotlin.dsl.provideDelegate
|
import org.gradle.kotlin.dsl.provideDelegate
|
||||||
import org.gradle.kotlin.dsl.register
|
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> {
|
internal class UniFfiJvmPlugin : Plugin<Project> {
|
||||||
override fun apply(target: Project): Unit = target.run {
|
override fun apply(target: Project): Unit = target.run {
|
||||||
|
|
||||||
@@ -29,20 +27,13 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
|||||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-apple-darwin")
|
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-apple-darwin")
|
||||||
args(cargoArgs)
|
args(cargoArgs)
|
||||||
}
|
}
|
||||||
} else if (operatingSystem == OS.LINUX) {
|
} else if(operatingSystem == OS.LINUX) {
|
||||||
exec {
|
exec {
|
||||||
workingDir("${project.projectDir}/../../bdk-ffi")
|
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||||
executable("cargo")
|
executable("cargo")
|
||||||
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-unknown-linux-gnu")
|
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-unknown-linux-gnu")
|
||||||
args(cargoArgs)
|
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,25 +70,13 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
|||||||
ext = "so"
|
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 {
|
libsToCopy.forEach {
|
||||||
doFirst {
|
doFirst {
|
||||||
copy {
|
copy {
|
||||||
with(it) {
|
with(it) {
|
||||||
from("${project.projectDir}/../../bdk-ffi/target/${this.targetDir}/release-smaller/${libName}.${this.ext}")
|
from("${project.projectDir}/../../target/${this.targetDir}/release-smaller/libbdkffi.${this.ext}")
|
||||||
into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
|
into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,24 +89,18 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
dependsOn(moveNativeJvmLibs)
|
dependsOn(moveNativeJvmLibs)
|
||||||
|
|
||||||
// TODO 2: Is the Windows name the correct one?
|
workingDir("${project.projectDir}/../../bdk-ffi")
|
||||||
// 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")
|
executable("cargo")
|
||||||
args(cargoArgs)
|
args(
|
||||||
|
"run",
|
||||||
|
"--package",
|
||||||
|
"bdk-ffi-bindgen",
|
||||||
|
"--",
|
||||||
|
"--language",
|
||||||
|
"kotlin",
|
||||||
|
"--out-dir",
|
||||||
|
"../bdk-jvm/lib/src/main/kotlin"
|
||||||
|
)
|
||||||
|
|
||||||
doLast {
|
doLast {
|
||||||
println("JVM bindings file successfully created")
|
println("JVM bindings file successfully created")
|
||||||
|
|||||||
14
bdk-python/LICENSE
Normal file
14
bdk-python/LICENSE
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
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.
|
||||||
201
bdk-python/LICENSE-APACHE
Normal file
201
bdk-python/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
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.
|
||||||
17
bdk-python/LICENSE-MIT
Normal file
17
bdk-python/LICENSE-MIT
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
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.
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
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
|
## Run the tests
|
||||||
```shell
|
```shell
|
||||||
pip install --requirement requirements.txt
|
pip3 install --requirement requirements.txt
|
||||||
bash ./scripts/generate-linux.sh # here you should run the script appropriate for your platform
|
bash ./generate.sh
|
||||||
python setup.py bdist_wheel --verbose
|
python3 setup.py --verbose bdist_wheel
|
||||||
pip install ./dist/bdkpython-<yourversion>.whl --force-reinstall
|
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl
|
||||||
python -m unittest --verbose
|
python -m unittest --verbose tests/test_bdk.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build the package
|
## Build the package
|
||||||
@@ -23,14 +23,26 @@ python -m unittest --verbose
|
|||||||
# Install dependencies
|
# Install dependencies
|
||||||
pip install --requirement requirements.txt
|
pip install --requirement requirements.txt
|
||||||
|
|
||||||
# Generate the bindings (use the script appropriate for your platform)
|
# Generate the bindings first
|
||||||
bash ./scripts/generate-linux.sh
|
bash generate.sh
|
||||||
|
|
||||||
# Build the wheel
|
# Build the wheel
|
||||||
python setup.py --verbose bdist_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
|
||||||
```
|
```
|
||||||
|
|
||||||
## Install locally
|
## Install locally
|
||||||
```shell
|
```shell
|
||||||
pip install ./dist/bdkpython-<yourversion>.whl
|
pip install ./dist/bdkpython-<yourversion>-py3-none-any.whl
|
||||||
```
|
```
|
||||||
|
|||||||
11
bdk-python/generate.sh
Normal file
11
bdk-python/generate.sh
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/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,4 +1,3 @@
|
|||||||
semantic-version==2.9.0
|
semantic-version==2.9.0
|
||||||
|
setuptools-rust==1.1.2
|
||||||
typing_extensions==4.0.1
|
typing_extensions==4.0.1
|
||||||
setuptools==67.4.0
|
|
||||||
wheel==0.38.4
|
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
#!/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!"
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/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!"
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/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!"
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/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,6 +1,9 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
from setuptools_rust import Binding, RustExtension
|
||||||
|
|
||||||
LONG_DESCRIPTION = """# bdkpython
|
LONG_DESCRIPTION = """# bdkpython
|
||||||
The Python language bindings for the [Bitcoin Dev Kit](https://github.com/bitcoindevkit).
|
The Python language bindings for the [Bitcoin Dev Kit](https://github.com/bitcoindevkit).
|
||||||
@@ -13,22 +16,58 @@ pip install bdkpython
|
|||||||
## Simple example
|
## Simple example
|
||||||
```python
|
```python
|
||||||
import bdkpython as bdk
|
import bdkpython as bdk
|
||||||
|
|
||||||
|
|
||||||
|
descriptor = "wpkh(tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/84h/0h/0h/0/*)"
|
||||||
|
db_config = bdk.DatabaseConfig.MEMORY()
|
||||||
|
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
|
||||||
|
bdk.ElectrumConfig(
|
||||||
|
"ssl://electrum.blockstream.info:60002",
|
||||||
|
None,
|
||||||
|
5,
|
||||||
|
None,
|
||||||
|
100
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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(
|
setup(
|
||||||
name="bdkpython",
|
name='bdkpython',
|
||||||
version="1.0.0a2.dev1",
|
version='0.25.0',
|
||||||
description="The Python language bindings for the Bitcoin Development Kit",
|
description="The Python language bindings for the Bitcoin Development Kit",
|
||||||
long_description=LONG_DESCRIPTION,
|
long_description=LONG_DESCRIPTION,
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type='text/markdown',
|
||||||
include_package_data = True,
|
rust_extensions=[rust_ext],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
packages=["bdkpython"],
|
packages=['bdkpython'],
|
||||||
package_dir={"bdkpython": "./src/bdkpython"},
|
package_dir={'bdkpython': './src/bdkpython'},
|
||||||
url="https://github.com/bitcoindevkit/bdk-ffi",
|
url="https://github.com/bitcoindevkit/bdk-python",
|
||||||
author="Bitcoin Dev Kit Developers <dev@bitcoindevkit.org>",
|
author="Alekos Filini <alekos.filini@gmail.com>, Steve Myers <steve@notmandatory.org>",
|
||||||
license="MIT or Apache 2.0",
|
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,
|
|
||||||
)
|
)
|
||||||
|
|||||||
46
bdk-python/tests/test_bdk.py
Normal file
46
bdk-python/tests/test_bdk.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import bdkpython as bdk
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
descriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||||
|
db_config = bdk.DatabaseConfig.MEMORY()
|
||||||
|
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
|
||||||
|
bdk.ElectrumConfig(
|
||||||
|
"ssl://electrum.blockstream.info:60002",
|
||||||
|
None,
|
||||||
|
5,
|
||||||
|
None,
|
||||||
|
100
|
||||||
|
)
|
||||||
|
)
|
||||||
|
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()
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
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()
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
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()
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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()
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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()
|
|
||||||
16
bdk-python/tox.ini
Normal file
16
bdk-python/tox.ini
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[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,9 +29,7 @@ let package = Package(
|
|||||||
.binaryTarget(name: "bdkFFI", path: "./bdkFFI.xcframework"),
|
.binaryTarget(name: "bdkFFI", path: "./bdkFFI.xcframework"),
|
||||||
.target(
|
.target(
|
||||||
name: "BitcoinDevKit",
|
name: "BitcoinDevKit",
|
||||||
dependencies: ["bdkFFI"],
|
dependencies: ["bdkFFI"]),
|
||||||
swiftSettings: [.unsafeFlags(["-suppress-warnings"])]
|
|
||||||
),
|
|
||||||
.testTarget(
|
.testTarget(
|
||||||
name: "BitcoinDevKitTests",
|
name: "BitcoinDevKitTests",
|
||||||
dependencies: ["BitcoinDevKit"]),
|
dependencies: ["BitcoinDevKit"]),
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ This project builds a Swift package that provides [Swift] language bindings for
|
|||||||
|
|
||||||
Supported target platforms are:
|
Supported target platforms are:
|
||||||
|
|
||||||
- macOS, X86_64 and M1 (aarch64)
|
- MacOS, X86_64 and M1 (aarch64)
|
||||||
- iOS, iPhones (aarch64)
|
- iOS, iPhones (aarch64)
|
||||||
- iOS simulator, X86_64 and M1 (aarch64)
|
- iOS simulator, X86_64 and M1 (aarch64)
|
||||||
|
|
||||||
## How to Use
|
## How to Use
|
||||||
|
|
||||||
To use the Swift language bindings for [`bdk`] in your [Xcode] iOS or macOS project add
|
To use the Swift language bindings for [`bdk`] in your [Xcode] iOS or MacOS project add
|
||||||
the GitHub repository https://github.com/bitcoindevkit/bdk-swift and select one of the
|
the github repository https://github.com/bitcoindevkit/bdk-swift and select one of the
|
||||||
release versions. You may then import and use the `BitcoinDevKit` library in your Swift
|
release versions. You may then import and use the `BitcoinDevKit` library in your Swift
|
||||||
code. For example:
|
code. For example:
|
||||||
|
|
||||||
@@ -35,16 +35,16 @@ swift test
|
|||||||
|
|
||||||
### Example Projects
|
### Example Projects
|
||||||
|
|
||||||
* [BDKSwiftExampleWallet](https://github.com/bitcoindevkit/BDKSwiftExampleWallet), iOS
|
* [BdkSwiftSample](https://github.com/futurepaul/BdkSwiftSample), iOS
|
||||||
|
|
||||||
## How to Build and Publish
|
## How to Build and Publish
|
||||||
|
|
||||||
If you are a maintainer of this project or want to build and publish this project to your
|
If you are a maintainer of this project or want to build and publish this project to your
|
||||||
own GitHub repository use the following steps:
|
own Github repository use the following steps:
|
||||||
|
|
||||||
1. If it doesn't already exist, create a new `release/0.MINOR` branch from the `master` branch.
|
1. If it doesn't already exist, create a new `release/0.MINOR` branch from the `master` branch.
|
||||||
2. Add a tag `v0.MINOR.PATCH`.
|
2. Add a tag `v0.MINOR.PATCH`.
|
||||||
3. Run the `publish-spm` workflow on GitHub from the `bdk-swift` repo for version `0.MINOR.PATCH`.
|
3. Run the `publish-spm` workflow on Github from the `bdk-swift` repo for version `0.MINOR.PATCH`.
|
||||||
|
|
||||||
[Swift]: https://developer.apple.com/swift/
|
[Swift]: https://developer.apple.com/swift/
|
||||||
[Xcode]: https://developer.apple.com/documentation/Xcode
|
[Xcode]: https://developer.apple.com/documentation/Xcode
|
||||||
|
|||||||
12
bdk-swift/Tests/BitcoinDevKitTests/BitcoinDevKitTests.swift
Normal file
12
bdk-swift/Tests/BitcoinDevKitTests/BitcoinDevKitTests.swift
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import BitcoinDevKit
|
||||||
|
|
||||||
|
final class BitcoinDevKitTests: XCTestCase {
|
||||||
|
func testMemoryWalletNewAddress() throws {
|
||||||
|
let desc = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
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.
|
# 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
|
# The results of this script can be used for locally testing your SPM package adding a local package
|
||||||
# to your application pointing at the bdk-swift directory.
|
# to your application pointing at the bdk-swift directory.
|
||||||
|
#
|
||||||
# Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
|
# Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
|
||||||
|
|
||||||
rustup install 1.73.0
|
rustup install nightly-x86_64-apple-darwin
|
||||||
rustup component add rust-src
|
rustup component add rust-src --toolchain nightly-x86_64-apple-darwin
|
||||||
rustup target add aarch64-apple-ios x86_64-apple-ios
|
rustup target add aarch64-apple-ios x86_64-apple-ios
|
||||||
rustup target add aarch64-apple-ios-sim
|
rustup target add aarch64-apple-ios-sim --toolchain nightly
|
||||||
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
rustup target add aarch64-apple-darwin x86_64-apple-darwin
|
||||||
|
|
||||||
pushd bdk-ffi
|
pushd bdk-ffi
|
||||||
mkdir -p Sources/BitcoinDevKit
|
mkdir -p Sources/BitcoinDevKit
|
||||||
cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
|
cargo run --package bdk-ffi-bindgen -- --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit
|
||||||
|
popd
|
||||||
|
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-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 x86_64-apple-ios
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios
|
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios
|
||||||
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios-sim
|
cargo +nightly build --package bdk-ffi --release -Z build-std --target aarch64-apple-ios-sim
|
||||||
|
|
||||||
mkdir -p target/lipo-ios-sim/release-smaller
|
mkdir -p target/lipo-ios-sim/release-smaller
|
||||||
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
|
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
|
||||||
mkdir -p target/lipo-macos/release-smaller
|
mkdir -p target/lipo-macos/release-smaller
|
||||||
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
|
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
|
||||||
|
|
||||||
popd
|
|
||||||
pushd bdk-swift
|
pushd bdk-swift
|
||||||
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
|
||||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers
|
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers
|
||||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
|
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
|
||||||
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
|
||||||
cp ../bdk-ffi/target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
|
cp ../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 ../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
|
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.h
|
||||||
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
rm Sources/BitcoinDevKit/bdkFFI.modulemap
|
||||||
#rm bdkFFI.xcframework.zip || true
|
#rm bdkFFI.xcframework.zip || true
|
||||||
|
|||||||
Reference in New Issue
Block a user