diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..2c89533 Binary files /dev/null and b/.DS_Store differ diff --git a/bdk-ffi b/bdk-ffi deleted file mode 160000 index 0648075..0000000 --- a/bdk-ffi +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0648075555b86365e2408fab484782a2b793b545 diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/bdk-python/.github/ISSUE_TEMPLATE/bug_report.md similarity index 100% rename from .github/ISSUE_TEMPLATE/bug_report.md rename to bdk-python/.github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/bdk-python/.github/ISSUE_TEMPLATE/feature_request.md similarity index 100% rename from .github/ISSUE_TEMPLATE/feature_request.md rename to bdk-python/.github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/pull_request_template.md b/bdk-python/.github/pull_request_template.md similarity index 100% rename from .github/pull_request_template.md rename to bdk-python/.github/pull_request_template.md diff --git a/.github/workflows/build.yml b/bdk-python/.github/workflows/build.yml similarity index 100% rename from .github/workflows/build.yml rename to bdk-python/.github/workflows/build.yml diff --git a/.github/workflows/publish.yml b/bdk-python/.github/workflows/publish.yml similarity index 100% rename from .github/workflows/publish.yml rename to bdk-python/.github/workflows/publish.yml diff --git a/.gitignore b/bdk-python/.gitignore similarity index 100% rename from .gitignore rename to bdk-python/.gitignore diff --git a/.gitmodules b/bdk-python/.gitmodules similarity index 100% rename from .gitmodules rename to bdk-python/.gitmodules diff --git a/LICENSE b/bdk-python/LICENSE similarity index 100% rename from LICENSE rename to bdk-python/LICENSE diff --git a/LICENSE-APACHE b/bdk-python/LICENSE-APACHE similarity index 100% rename from LICENSE-APACHE rename to bdk-python/LICENSE-APACHE diff --git a/LICENSE-MIT b/bdk-python/LICENSE-MIT similarity index 100% rename from LICENSE-MIT rename to bdk-python/LICENSE-MIT diff --git a/README.md b/bdk-python/README.md similarity index 100% rename from README.md rename to bdk-python/README.md diff --git a/bdk-python/bdk-ffi/.editorconfig b/bdk-python/bdk-ffi/.editorconfig new file mode 100644 index 0000000..4f1c5cd --- /dev/null +++ b/bdk-python/bdk-ffi/.editorconfig @@ -0,0 +1,29 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false + +[*.rs] +indent_size = 4 + +[*.kt] +indent_size = 4 + +[*.gradle] +indent_size = 4 + +[tests/**/*.rs] +charset = utf-8 +end_of_line = unset +indent_size = unset +indent_style = unset +trim_trailing_whitespace = unset +insert_final_newline = unset diff --git a/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/bug_report.md b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0708e29 --- /dev/null +++ b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: 'bug' +assignees: '' + +--- + +**Describe the bug** + + +**To Reproduce** + + +**Expected behavior** + + +**Build environment** + - BDK tag/commit: + - OS+version: + - Rust/Cargo version: + - Rust/Cargo target: + +**Additional context** + diff --git a/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/minor_release.md b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/minor_release.md new file mode 100644 index 0000000..0514d56 --- /dev/null +++ b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/minor_release.md @@ -0,0 +1,101 @@ +--- +name: Minor Release +about: Create a new minor release [for release managers only] +title: 'Release MAJOR.MINOR+1.0' +labels: 'release' +assignees: '' + +--- + +## Create a new minor 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.0*. + +#### On the day of the feature freeze + +Change the `master` branch to the next MINOR+1 version: + +- [ ] Switch to the `master` branch. +- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR+1`, eg. `bump_dev_0_22`. +- [ ] Bump the `bump_dev_MAJOR_MINOR+1` branch to the next development MINOR+1 version. + - Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`. + - The commit message should be "Bump version to MAJOR.MINOR+1.0". +- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR+1` branch to `master`. + - Title PR "Bump version to MAJOR.MINOR+1.0". + +Create a new release branch: + +- [ ] Double check that your local `master` is up-to-date with the upstream repo. +- [ ] Create a new branch called `release/MAJOR.MINOR+1` from `master`. + +Add a release candidate tag, this is optional and only needed for major `bdk-ffi` changes that +require a longer testing cycle: + +- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.1` version. + - Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.1`. + - The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.1". +- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch. + - The tag name should be `vMAJOR.MINOR+1.0-rc.1` + - Use message "Release MAJOR.MINOR+1.0 rc.1". + - Make sure the tag is signed, for extra safety use the explicit `--sign` flag. +- [ ] Push the `release/MAJOR.MINOR` branch and new tag to the `bitcoindevkit/bdk` repo. + - Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.1` tag. + +If any issues need to be fixed before the *MAJOR.MINOR+1.0* version is released: + +- [ ] Merge fix PRs to the `master` branch. +- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR+1` branch. +- [ ] Verify fixes in `release/MAJOR.MINOR+1` branch. +- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.x+1` version. + - Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.x+1`. + - The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.x+1". +- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch. + - The tag name should be `vMAJOR.MINOR+1.0-rc.x+1`, where x is the current release candidate number. + - Use tag message "Release MAJOR.MINOR+1.0 rc.x+1". + - Make sure the tag is signed, for extra safety use the explicit `--sign` flag. +- [ ] Push the new tag to the `bitcoindevkit/bdk` repo. + - Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.x+1` tag. + +#### On the day of the release + +Tag and publish new release: + +- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0` version. + - Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`. + - The commit message should be "Bump version to MAJOR.MINOR+1.0". +- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch. + - The tag name should be `vMAJOR.MINOR+1.0` + - The first line of the tag message should be "Release MAJOR.MINOR+1.0". + - In the body of the tag message put a copy of the **Summary** and **Changelog** for the release. + - Make sure the tag is signed, for extra safety use the explicit `--sign` flag. +- [ ] Wait for the CI to finish one last time. +- [ ] Push the new tag to the `bitcoindevkit/bdk` repo. +- [ ] Create the release on GitHub. + - Go to "tags", click on the dots on the right and select "Create Release". + - Set the title to `Release MAJOR.MINOR+1.0`. + - In the release notes body put the **Summary** and **Changelog**. + - Use the "+ Auto-generate release notes" button to add details from included PRs. + - Until we reach a `1.0.0` release check the "Pre-release" box. +- [ ] After downstream language repos are also updated announce the release, using the **Summary**, + on Discord, Twitter and Mastodon. +- [ ] Celebrate 🎉 + +[Semantic Versioning]: https://semver.org/ +[crates.io]: https://crates.io/crates/bdk +[docs.rs]: https://docs.rs/bdk/latest/bdk +["keep a changelog"]: https://keepachangelog.com/en/1.0.0/ diff --git a/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/patch_release.md b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/patch_release.md new file mode 100644 index 0000000..b292668 --- /dev/null +++ b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/patch_release.md @@ -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/ diff --git a/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/summer_project.md b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/summer_project.md new file mode 100644 index 0000000..a693283 --- /dev/null +++ b/bdk-python/bdk-ffi/.github/ISSUE_TEMPLATE/summer_project.md @@ -0,0 +1,77 @@ +--- +name: Summer of Bitcoin Project +about: Template to suggest a new https://www.summerofbitcoin.org/ project. +title: '' +labels: 'summer-of-bitcoin' +assignees: '' + +--- + + + +**Description** + + +**Expected Outcomes** + + +**Resources** + + + + + + +**Skills Required** + + + + + + + +**Mentor(s)** + + +**Difficulty** + + +**Competency Test (optional)** + + + + + + + diff --git a/bdk-python/bdk-ffi/.github/pull_request_template.md b/bdk-python/bdk-ffi/.github/pull_request_template.md new file mode 100644 index 0000000..42afd7e --- /dev/null +++ b/bdk-python/bdk-ffi/.github/pull_request_template.md @@ -0,0 +1,34 @@ + + +### Description + + + +### Notes to the reviewers + + + +### Changelog notice + + + + +### Checklists + +#### All Submissions: + +* [ ] I've signed all my commits +* [ ] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) +* [ ] I ran `cargo fmt` and `cargo clippy` before committing + +#### New Features: + +* [ ] I've added tests for the new feature +* [ ] I've added docs for the new feature + +#### Bugfixes: + +* [ ] This pull request breaks the existing API +* [ ] I've added tests to reproduce the issue which are now passing +* [ ] I'm linking the issue being fixed by this PR diff --git a/bdk-python/bdk-ffi/.github/workflows/audit.yml b/bdk-python/bdk-ffi/.github/workflows/audit.yml new file mode 100644 index 0000000..6143cca --- /dev/null +++ b/bdk-python/bdk-ffi/.github/workflows/audit.yml @@ -0,0 +1,19 @@ +name: Audit + +on: + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' + schedule: + - cron: '0 0 * * 0' # Once per week + +jobs: + + security_audit: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/bdk-python/bdk-ffi/.github/workflows/cont_integration.yml b/bdk-python/bdk-ffi/.github/workflows/cont_integration.yml new file mode 100644 index 0000000..4422c0d --- /dev/null +++ b/bdk-python/bdk-ffi/.github/workflows/cont_integration.yml @@ -0,0 +1,61 @@ +on: [push, pull_request] + +name: CI + +jobs: + + build-test: + name: Build and test + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - version: 1.63.0 # STABLE + clippy: true + - version: 1.61.0 # MSRV + steps: + - name: checkout + uses: actions/checkout@v2 + - name: Generate cache key + run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key + - name: cache + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }} + - name: Set default toolchain + run: rustup default ${{ matrix.rust.version }} + - name: Set profile + run: rustup set profile minimal + - name: Add clippy + if: ${{ matrix.rust.clippy }} + run: rustup component add clippy + - name: Update toolchain + run: rustup update + - name: Build + run: cargo build + - name: Clippy + if: ${{ matrix.rust.clippy }} + run: cargo clippy --all-targets -- -D warnings + - name: Test + run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test + + fmt: + name: Rust fmt + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Set default toolchain + run: rustup default nightly + - name: Set profile + run: rustup set profile minimal + - name: Add rustfmt + run: rustup component add rustfmt + - name: Update toolchain + run: rustup update + - name: Check fmt + run: cargo fmt --all -- --config format_code_in_doc_comments=true --check diff --git a/bdk-python/bdk-ffi/.gitignore b/bdk-python/bdk-ffi/.gitignore new file mode 100644 index 0000000..c91c338 --- /dev/null +++ b/bdk-python/bdk-ffi/.gitignore @@ -0,0 +1,17 @@ +target +build +Cargo.lock +/bindings/bdk-kotlin/local.properties +.gradle +wallet_db +bdk_ffi_test +local.properties +*.log +*.dylib +*.so +.DS_Store +testdb +xcuserdata +.lsp +.clj-kondo +.idea/ diff --git a/bdk-python/bdk-ffi/CHANGELOG.md b/bdk-python/bdk-ffi/CHANGELOG.md new file mode 100644 index 0000000..721b0c3 --- /dev/null +++ b/bdk-python/bdk-ffi/CHANGELOG.md @@ -0,0 +1,116 @@ +# Changelog +All notable changes to this project prior to release **0.9.0** are documented in this file. Future +changelog information can be found in each release's git tag and can be viewed with `git tag -ln100 "v*"`. +Changelog info is also documented on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases) +page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [v0.9.0] +- Breaking Changes + - Rename `get_network()` method on `Wallet` interface to `network()` [#185] + - Rename `get_transactions()` method on `Wallet` interface to `list_transactions()` [#185] + - Remove `generate_extended_key`, returned ExtendedKeyInfo [#154] + - Remove `restore_extended_key`, returned ExtendedKeyInfo [#154] + - Remove dictionary `ExtendedKeyInfo {mnenonic, xprv, fingerprint}` [#154] + - Remove interface `Transaction` [#190] + - Changed `Wallet` interface `list_transaction()` to return array of `TransactionDetails` [#190] + - Update `bdk` dependency version to 0.22 [#193] +- APIs Added [#154] + - `generate_mnemonic()`, returns string mnemonic + - `interface DescriptorSecretKey` + - `new(Network, string_mnenoinc, password)`, contructs DescriptorSecretKey + - `derive(DerivationPath)`, derives and returns child DescriptorSecretKey + - `extend(DerivationPath)`, extends and returns DescriptorSecretKey + - `as_public()`, returns DescriptorSecretKey as DescriptorPublicKey + - `as_string()`, returns DescriptorSecretKey as String + - `interface DescriptorPublicKey` + - `derive(DerivationPath)` derives and returns child DescriptorPublicKey + - `extend(DerivationPath)` extends and returns DescriptorPublicKey + - `as_string()` returns DescriptorPublicKey as String + - Add to `interface Blockchain` the `get_height()` and `get_block_hash()` methods [#184] + - Add to `interface TxBuilder` the `set_recipients(recipient: Vec)` method [#186] + - Add to `dictionary TransactionDetails` the `confirmation_time` field [#190] +- Interfaces Added [#154] + - `DescriptorSecretKey` + - `DescriptorPublicKey` + - `DerivationPath` + +[#154]: https://github.com/bitcoindevkit/bdk-ffi/pull/154 +[#184]: https://github.com/bitcoindevkit/bdk-ffi/pull/184 +[#185]: https://github.com/bitcoindevkit/bdk-ffi/pull/185 +[#193]: https://github.com/bitcoindevkit/bdk-ffi/pull/193 + +## [v0.8.0] +- Update BDK to version 0.20.0 [#169] +- APIs Added + - `TxBuilder.add_data(data: Vec)` [#163] + - `Wallet.list_unspent()` returns `Vec` [#158] + - Add coin control methods on TxBuilder [#164] + +[#163]: https://github.com/bitcoindevkit/bdk-ffi/pull/163 +[#158]: https://github.com/bitcoindevkit/bdk-ffi/pull/158 +[#164]: https://github.com/bitcoindevkit/bdk-ffi/pull/164 +[#169]: https://github.com/bitcoindevkit/bdk-ffi/pull/169 +[#190]: https://github.com/bitcoindevkit/bdk-ffi/pull/190 + +## [v0.7.0] +- Update BDK to version 0.19.0 + - fixes sqlite-db issue causing wrong balance + - adds experimental taproot descriptor and PSBT support +- APIs Removed + - `Wallet.get_new_address()`, returned String, [#137] + - `Wallet.get_last_unused_address()`, returned String [#137] +- APIs Added + - `Wallet.get_address(AddressIndex)`, returns `AddressInfo` [#137] +- APIs Changed + - `Wallet.sign(PartiallySignedBitcoinTransaction)` now returns a bool, true if finalized [#161] + +[#137]: https://github.com/bitcoindevkit/bdk-ffi/pull/137 +[#161]: https://github.com/bitcoindevkit/bdk-ffi/pull/161 + +## [v0.6.0] +- Update BDK to version 0.18.0 +- Add BumpFeeTxBuilder to bump the fee on an unconfirmed tx created by the Wallet +- Change TxBuilder.build() to TxBuilder.finish() to align with bdk function name + +## [v0.5.0] +- Fix Wallet.broadcast function, now returns a tx id as a hex string +- Remove creating a new spending Transaction via the PartiallySignedBitcoinTransaction constructor +- Add TxBuilder for creating new spending PartiallySignedBitcoinTransaction +- Add TxBuilder .add_recipient, .fee_rate, and .build functions +- Add TxBuilder .drain_wallet and .drain_to functions +- Update generate cli tool to generate all binding languages and rename to bdk-ffi-bindgen + +## [v0.4.0] +- Add dual license MIT and Apache 2.0 +- Add sqlite database support +- Fix memory database configuration enum, remove junk field + +## [v0.3.1] +- Remove hard coded sync progress value (was always returning 21.0) + +## [v0.3.0] +- Move bdk-kotlin bindings and ios example to separate repos +- Add bin to generate Python bindings +- Add `PartiallySignedBitcoinTransaction::deserialize` function as named constructor to decode from a string per [BIP 0174] +- Add `PartiallySignedBitcoinTransaction::serialize` function to encode to a string per [BIP 0174] +- Remove `PartiallySignedBitcoinTransaction.details` struct field + +[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding + +## [v0.2.0] + +[unreleased]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.9.0...HEAD +[v0.9.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.8.0...v0.9.0 +[v0.8.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.7.0...v0.8.0 +[v0.7.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.6.0...v0.7.0 +[v0.6.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.5.0...v0.6.0 +[v0.5.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.4.0...v0.5.0 +[v0.4.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.3.1...v0.4.0 +[v0.3.1]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.3.0...v0.3.1 +[v0.3.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.2.0...v0.3.0 +[v0.2.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.0.0...v0.2.0 diff --git a/bdk-python/bdk-ffi/Cargo.toml b/bdk-python/bdk-ffi/Cargo.toml new file mode 100644 index 0000000..63cd96f --- /dev/null +++ b/bdk-python/bdk-ffi/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "bdk-ffi" +version = "0.11.0" +authors = ["Steve Myers ", "Sudarsan Balaji "] +edition = "2018" +license = "MIT OR Apache-2.0" + +[workspace] +members = [".","bdk-ffi-bindgen"] +default-members = [".", "bdk-ffi-bindgen"] + +[lib] +crate-type = ["staticlib", "cdylib"] +name = "bdkffi" + +[dependencies] +bdk = { version = "0.24", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] } + +uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] } +uniffi = { version = "0.21.0", features = ["builtin-bindgen"] } + +[build-dependencies] +uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] } + +[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* diff --git a/bdk-python/bdk-ffi/DEVELOPMENT_CYCLE.md b/bdk-python/bdk-ffi/DEVELOPMENT_CYCLE.md new file mode 100644 index 0000000..00c4094 --- /dev/null +++ b/bdk-python/bdk-ffi/DEVELOPMENT_CYCLE.md @@ -0,0 +1,35 @@ +# Development Cycle + +This project follows a regular releasing schedule similar to the one [used by the Rust language] +except releases always follow the latest [`bdk`] release by one to two weeks. In short, this means +that a new release is made at a regular cadence, with all the feature/bugfixes that made it to +`master` in time. This ensures that we don't keep delaying releases waiting for +"just one more little thing". + +After making a new `bdk-ffi` release tag all downstream language bindings should also be updated. + +This project uses [Semantic Versioning], but is currently at MAJOR version zero (0.y.z) meaning it +is still in initial development. Anything MAY change at any time. The public API SHOULD NOT be +considered stable. Until we reach version `1.0.0` we will do our best to document any breaking API +changes in the changelog info attached to each release tag. + +We decided to maintain a faster release cycle while the library is still in "beta", i.e. before +release `1.0.0`: since we are constantly adding new features and, even more importantly, fixing +issues, we want developers to have access to those updates as fast as possible. For this reason we +will make a release **every 4 weeks**. + +Once the project reaches a more mature state (>= `1.0.0`), we will very likely switch to longer +release cycles of **6 weeks**. + +The "feature freeze" will happen when [`bdk`] releases a release candidate. This project will then +be updated and tested with [`bdk`] release candidates until a final release is published. This +means a new branch will be created originating from the `master` tip at that time, and in that +branch we will stop adding new features and only focus on ensuring the ones we've added are working +properly. + +To create a new release a release manager will create a new issue using a `Release` template and +follow the template instructions. + +[used by the Rust language]: https://doc.rust-lang.org/book/appendix-07-nightly-rust.html +[Semantic Versioning]: https://semver.org/ +[`bdk`]: https://github.com/bitcoindevkit/bdk diff --git a/bdk-python/bdk-ffi/LICENSE b/bdk-python/bdk-ffi/LICENSE new file mode 100644 index 0000000..9c61848 --- /dev/null +++ b/bdk-python/bdk-ffi/LICENSE @@ -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 or the MIT license , 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. \ No newline at end of file diff --git a/bdk-python/bdk-ffi/LICENSE-APACHE b/bdk-python/bdk-ffi/LICENSE-APACHE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/bdk-python/bdk-ffi/LICENSE-APACHE @@ -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. diff --git a/bdk-python/bdk-ffi/LICENSE-MIT b/bdk-python/bdk-ffi/LICENSE-MIT new file mode 100644 index 0000000..9d982a4 --- /dev/null +++ b/bdk-python/bdk-ffi/LICENSE-MIT @@ -0,0 +1,16 @@ +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. diff --git a/bdk-python/bdk-ffi/README.md b/bdk-python/bdk-ffi/README.md new file mode 100644 index 0000000..c47fd5c --- /dev/null +++ b/bdk-python/bdk-ffi/README.md @@ -0,0 +1,75 @@ +# Native language bindings for BDK + +

+ MIT or Apache-2.0 Licensed + CI Status + Rustc Version 1.61.0+ + Chat on Discord +

+ +The workspace in this repository creates the `libbdkffi` multi-language library for the rust based +[bdk] library from the [Bitcoin Dev Kit] project. The `bdk-ffi-bindgen` package builds a tool for +generating the actual language binding code used to access the `libbdkffi` library. + +Each supported language has its own repository that includes this project as a [git submodule]. +The rust code in this project is a wrapper around the [bdk] library to expose it's APIs in a +uniform way using the [mozilla/uniffi-rs] bindings generator for each supported target language. + +## Supported target languages and platforms + +The below repositories include instructions for using, building, and publishing the native +language binding for [bdk] supported by this project. + +| Language | Platform | Repository | +| -------- | ------------ | ------------ | +| Kotlin | jvm | [bdk-kotlin] | +| Kotlin | android | [bdk-kotlin] | +| Swift | iOS, macOS | [bdk-swift] | +| Python | linux, macOS | [bdk-python] | + +## Language bindings generator tool + +Use the `bdk-ffi-bindgen` tool to generate language binding code for the above supported languages. +To run `bdk-ffi-bindgen` and see the available options use the command: +```shell +cargo run -p bdk-ffi-bindgen -- --help +``` + +[bdk]: https://github.com/bitcoindevkit/bdk +[Bitcoin Dev Kit]: https://github.com/bitcoindevkit +[git submodule]: https://git-scm.com/book/en/v2/Git-Tools-Submodules +[uniffi-rs]: https://github.com/mozilla/uniffi-rs + +[bdk-kotlin]: https://github.com/bitcoindevkit/bdk-kotlin +[bdk-swift]: https://github.com/bitcoindevkit/bdk-swift +[bdk-python]: https://github.com/bitcoindevkit/bdk-python + +## Contributing + +### Adding new structs and functions + +See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/) + +#### For pass by value objects + +1. create new rust struct with only fields that are supported UniFFI types +1. update mapping `bdk.udl` file with new `dictionary` + +#### For pass by reference values + +1. create wrapper rust struct/impl with only fields that are `Sync + Send` +1. update mapping `bdk.udl` file with new `interface` + +## Goals + +1. Language bindings should feel idiomatic in target languages/platforms +1. Adding new targets should be easy +1. Getting up and running should be easy +1. Contributing should be easy +1. Get it right, then automate + +## Thanks + +This project is made possible thanks to the wonderful work by the [mozilla/uniffi-rs] team. + +[mozilla/uniffi-rs]: https://github.com/mozilla/uniffi-rs diff --git a/bdk-python/bdk-ffi/bdk-ffi-bindgen/Cargo.toml b/bdk-python/bdk-ffi/bdk-ffi-bindgen/Cargo.toml new file mode 100644 index 0000000..87ee0bc --- /dev/null +++ b/bdk-python/bdk-ffi/bdk-ffi-bindgen/Cargo.toml @@ -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" diff --git a/bdk-python/bdk-ffi/bdk-ffi-bindgen/src/main.rs b/bdk-python/bdk-ffi/bdk-ffi-bindgen/src/main.rs new file mode 100644 index 0000000..8a87fc5 --- /dev/null +++ b/bdk-python/bdk-ffi/bdk-ffi-bindgen/src/main.rs @@ -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 { + 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> { + 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, +} + +fn main() -> Result<(), Box> { + 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(()) +} diff --git a/bdk-python/bdk-ffi/build.rs b/bdk-python/bdk-ffi/build.rs new file mode 100644 index 0000000..153077f --- /dev/null +++ b/bdk-python/bdk-ffi/build.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi_build::generate_scaffolding("src/bdk.udl").unwrap(); +} diff --git a/bdk-python/bdk-ffi/src/bdk.udl b/bdk-python/bdk-ffi/src/bdk.udl new file mode 100644 index 0000000..e45fe87 --- /dev/null +++ b/bdk-python/bdk-ffi/src/bdk.udl @@ -0,0 +1,337 @@ +namespace bdk { + +}; + +[Error] +enum BdkError { + "InvalidU32Bytes", + "Generic", + "MissingCachedScripts", + "ScriptDoesntHaveAddressForm", + "NoRecipients", + "NoUtxosSelected", + "OutputBelowDustLimit", + "InsufficientFunds", + "BnBTotalTriesExceeded", + "BnBNoExactMatch", + "UnknownUtxo", + "TransactionNotFound", + "TransactionConfirmed", + "IrreplaceableTransaction", + "FeeRateTooLow", + "FeeTooLow", + "FeeRateUnavailable", + "MissingKeyOrigin", + "Key", + "ChecksumMismatch", + "SpendingPolicyRequired", + "InvalidPolicyPathError", + "Signer", + "InvalidNetwork", + "InvalidProgressValue", + "ProgressUpdateError", + "InvalidOutpoint", + "Descriptor", + "Encode", + "Miniscript", + "MiniscriptPsbt", + "Bip32", + "Secp256k1", + "Json", + "Hex", + "Psbt", + "PsbtParse", + "Electrum", + "Esplora", + "Sled", + "Rusqlite", +}; + +dictionary AddressInfo { + u32 index; + string address; +}; + +enum AddressIndex { + "New", + "LastUnused", +}; + +enum Network { + "Bitcoin", + "Testnet", + "Signet", + "Regtest", +}; + +dictionary SledDbConfiguration { + string path; + string tree_name; +}; + +dictionary SqliteDbConfiguration { + string path; +}; + +dictionary Balance { + u64 immature; + u64 trusted_pending; + u64 untrusted_pending; + u64 confirmed; + u64 spendable; + u64 total; +}; + +[Enum] +interface DatabaseConfig { + Memory(); + Sled(SledDbConfiguration config); + Sqlite(SqliteDbConfiguration config); +}; + +dictionary TransactionDetails { + u64? fee; + u64 received; + u64 sent; + string txid; + BlockTime? confirmation_time; +}; + +dictionary BlockTime { + u32 height; + u64 timestamp; +}; + +enum WordCount { + "Words12", + "Words15", + "Words18", + "Words21", + "Words24", +}; + +dictionary ElectrumConfig { + string url; + string? socks5; + u8 retry; + u8? timeout; + u64 stop_gap; +}; + +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 { + [Throws=BdkError] + constructor(string descriptor, string? change_descriptor, Network network, DatabaseConfig database_config); + + [Throws=BdkError] + AddressInfo get_address(AddressIndex address_index); + + [Throws=BdkError] + Balance get_balance(); + + [Throws=BdkError] + boolean sign([ByRef] PartiallySignedTransaction psbt); + + [Throws=BdkError] + sequence list_transactions(); + + Network network(); + + [Throws=BdkError] + void sync([ByRef] Blockchain blockchain, Progress? progress); + + [Throws=BdkError] + sequence list_unspent(); +}; + +interface FeeRate { + [Name=from_sat_per_vb] + constructor(float sat_per_vb); + + float as_sat_per_vb(); +}; + +interface PartiallySignedTransaction { + [Throws=BdkError] + constructor(string psbt_base64); + + string serialize(); + + string txid(); + + sequence extract_tx(); + + [Throws=BdkError] + PartiallySignedTransaction combine(PartiallySignedTransaction other); + + u64? fee_amount(); + + FeeRate? fee_rate(); +}; + +dictionary TxBuilderResult { + PartiallySignedTransaction psbt; + TransactionDetails transaction_details; +}; + +interface TxBuilder { + constructor(); + + TxBuilder add_recipient(Script script, u64 amount); + + TxBuilder add_unspendable(OutPoint unspendable); + + TxBuilder add_utxo(OutPoint outpoint); + + TxBuilder add_utxos(sequence outpoints); + + TxBuilder do_not_spend_change(); + + TxBuilder manually_selected_only(); + + TxBuilder only_spend_change(); + + TxBuilder unspendable(sequence unspendable); + + TxBuilder fee_rate(float sat_per_vbyte); + + TxBuilder fee_absolute(u64 fee_amount); + + TxBuilder drain_wallet(); + + TxBuilder drain_to(string address); + + TxBuilder enable_rbf(); + + TxBuilder enable_rbf_with_sequence(u32 nsequence); + + TxBuilder add_data(sequence data); + + TxBuilder set_recipients(sequence recipients); + + [Throws=BdkError] + TxBuilderResult finish([ByRef] Wallet wallet); +}; + +interface BumpFeeTxBuilder { + constructor(string txid, float new_fee_rate); + + BumpFeeTxBuilder allow_shrinking(string address); + + BumpFeeTxBuilder enable_rbf(); + + BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence); + + [Throws=BdkError] + PartiallySignedTransaction finish([ByRef] Wallet wallet); +}; + +interface Mnemonic { + constructor(WordCount word_count); + + [Name=from_string, Throws=BdkError] + constructor(string mnemonic); + + [Name=from_entropy, Throws=BdkError] + constructor(sequence entropy); + + string as_string(); +}; + +interface DerivationPath { + [Throws=BdkError] + constructor(string path); +}; + +interface DescriptorSecretKey { + constructor(Network network, Mnemonic mnemonic, string? password); + + [Throws=BdkError] + DescriptorSecretKey derive(DerivationPath path); + + DescriptorSecretKey extend(DerivationPath path); + + DescriptorPublicKey as_public(); + + sequence secret_bytes(); + + string as_string(); +}; + +interface DescriptorPublicKey { + [Throws=BdkError] + DescriptorPublicKey derive(DerivationPath path); + + DescriptorPublicKey extend(DerivationPath path); + + string as_string(); +}; + +interface Address { + [Throws=BdkError] + constructor(string address); + + Script script_pubkey(); +}; + +interface Script { + constructor(sequence raw_output_script); +}; diff --git a/bdk-python/bdk-ffi/src/lib.rs b/bdk-python/bdk-ffi/src/lib.rs new file mode 100644 index 0000000..e062281 --- /dev/null +++ b/bdk-python/bdk-ffi/src/lib.rs @@ -0,0 +1,1293 @@ +use bdk::bitcoin::blockdata::script::Script as BdkScript; +use bdk::bitcoin::hashes::hex::ToHex; +use bdk::bitcoin::secp256k1::Secp256k1; +use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath; +use bdk::bitcoin::util::psbt::serialize::Serialize; +use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction; +use bdk::bitcoin::Sequence; +use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Txid}; +use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig}; +use bdk::blockchain::GetBlockHash; +use bdk::blockchain::GetHeight; +use bdk::blockchain::{ + electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig, ConfigurableBlockchain, +}; +use bdk::blockchain::{Blockchain as BdkBlockchain, Progress as BdkProgress}; +use bdk::database::any::{AnyDatabase, SledDbConfiguration, SqliteDbConfiguration}; +use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase}; +use bdk::descriptor::DescriptorXKey; +use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount}; +use bdk::keys::{ + DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey, + DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey, +}; +use bdk::miniscript::BareCtx; +use bdk::psbt::PsbtUtils; +use bdk::wallet::tx_builder::ChangeSpendPolicy; +use bdk::wallet::AddressIndex as BdkAddressIndex; +use bdk::wallet::AddressInfo as BdkAddressInfo; +use bdk::{ + Balance as BdkBalance, BlockTime, Error as BdkError, FeeRate, KeychainKind, SignOptions, + SyncOptions as BdkSyncOptions, Wallet as BdkWallet, +}; +use std::collections::HashSet; +use std::convert::{From, TryFrom}; +use std::fmt; +use std::ops::Deref; +use std::str::FromStr; +use std::sync::{Arc, Mutex, MutexGuard}; + +uniffi_macros::include_scaffolding!("bdk"); + +/// A output script and an amount of satoshis. +pub struct ScriptAmount { + pub script: Arc