Compare commits

...

131 Commits

Author SHA1 Message Date
thunderbiscuit
dc58ddc262 chore: update libraries to official release versions 2023-11-20 12:44:07 -05:00
thunderbiscuit
93d50dd34d docs: remove api-docs directory 2023-11-17 15:55:12 -05:00
thunderbiscuit
5ecbf64e60 chore: remove authors from rust, kotlin, and python libraries 2023-11-17 15:52:12 -05:00
thunderbiscuit
084f0f713a test: remove unused python tox config file 2023-11-17 15:47:43 -05:00
thunderbiscuit
1ef1c5cc6e docs: update python readme and examples 2023-11-17 15:47:09 -05:00
thunderbiscuit
0956999283 docs: recommend looking at jvm tests for examples of 1.0 api 2023-11-17 15:21:13 -05:00
thunderbiscuit
e70dedce61 docs: recommend looking at android tests for examples of 1.0 api 2023-11-17 15:20:56 -05:00
thunderbiscuit
15c1f19c96 feat: add broadcast method on esplora blocking client 2023-11-17 13:31:37 -05:00
thunderbiscuit
26352edfbe feat: add sign method on wallet type 2023-11-17 13:24:59 -05:00
thunderbiscuit
787152e0b4 feat: expose new methods on txbuilder 2023-11-17 12:14:45 -05:00
Matthew
e79ce98295 fix: handle parsing and network errors in address new 2023-11-17 09:37:29 -06:00
Matthew
b8778cdaa5 docs: add/update android+ios readme example project links 2023-11-16 09:40:52 -06:00
thunderbiscuit
f2cd561f25 chore: use rust nightly for 1.73.0 for swift library 2023-11-10 13:57:49 -06:00
thunderbiscuit
96e7479ce9 chore: bump and pin uniffi to 0.25.1 2023-11-10 08:38:59 -06:00
thunderbiscuit
a9c6aac6b9 chore: bump rust compiler version to 1.73.0 for JVM and python builds 2023-11-09 16:22:29 -06:00
thunderbiscuit
fc4240ca38 chore: bump rust compiler and android ndk versions 2023-11-09 16:10:05 -06:00
thunderbiscuit
fd85d1d754 ci: add workflow for live tests with manual trigger 2023-11-06 16:57:29 -06:00
thunderbiscuit
d37b2f37b5 test: add excluded-in-CI live tests 2023-11-06 15:22:22 -05:00
thunderbiscuit
13c751cebc feat: add Transaction and PartiallySignedTransaction types 2023-10-27 16:17:23 -04:00
thunderbiscuit
1521811e9b feat: add MVP transaction builder type 2023-10-27 16:16:59 -04:00
Matthew
372f79a10f tests: update all tests 2023-10-27 14:44:51 -05:00
thunderbiscuit
00cd55bb46 feat(wallet): add scan method on blocking esplora client 2023-10-25 12:15:45 -04:00
thunderbiscuit
7463fa7720 docs: add warning for 1.0 migration to readme 2023-10-23 11:50:41 -04:00
thunderbiscuit
790aee9b3b feat: upgrade bdk to 1.0.0-alpha.2
This is a big change that updates some of our build infrastructure
as well as upgrading the bdk dependency. It adds the simple
new_no_persist constructor on the wallet as well as the blocking
esplora client.
2023-10-23 11:33:56 -04:00
thunderbiscuit
743862fb60 chore: update minor_release template 2023-09-12 11:38:18 -04:00
thunderbiscuit
b7e38b18be chore: bump snapshot and dev versions of libraries 2023-09-12 11:37:17 -04:00
thunderbiscuit
ef73aa3490 docs: add 0.30.0 release to changelog 2023-09-12 11:35:36 -04:00
thunderbiscuit
fbec953149 docs: update docs for new pinned dependency 2023-09-06 14:51:00 -04:00
thunderbiscuit
0b07b8ed05 docs: update Kotlin API docs 2023-09-06 13:52:48 -04:00
thunderbiscuit
b59327e5ea chore: pin flate2 to 1.0.26 when building with 1.61 2023-09-06 10:40:29 -04:00
thunderbiscuit
e6500baae7 chore: update library version to 0.30.0 2023-09-06 10:25:25 -04:00
thunderbiscuit
85f220b953 build: update webpki dependency as per RUSTSEC-2023-0052 2023-09-05 14:38:16 -04:00
thunderbiscuit
106d31c9c3 Refactor: Remove unused Mutex on DescriptorSecretKey inner field 2023-08-18 13:34:28 -04:00
thunderbiscuit
4ae169f860 Refactor: Remove unused Mutex on DescriptorPublicKey inner field 2023-08-18 12:58:11 -04:00
Matthew
7717ebb097 refactor: rename inner field names 2023-08-16 15:37:33 -05:00
Matthew
c3e8469686 feat(descriptor): add bip-86 template 2023-08-11 12:09:21 -05:00
Steve Myers
faf23b7d25 Merge bitcoindevkit/bdk-ffi#389: ci: fix to work with bdk 0.28.2 and msrv 1.61.0
1da01b4a0b ci: change rust stable version to 0.71.0 (Steve Myers)
f1ba03bf50 ci: fix to work with bdk 0.28.1 and msrv 1.61.0 (Steve Myers)

Pull request description:

  ### Description

  Fixes CI to work with BDK 0.28.x and MSRV 1.61.0. For MSRV had to pin two dependencies.

  ### Notes to the reviewers

  I also updated in CI our 'stable" version of rust to be current stable which is `1.71.0`.

  ### Changelog notice

  None, only CI changes.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

ACKs for top commit:
  thunderbiscuit:
    ACK 1da01b4a0b.

Tree-SHA512: 72f7d8dbef791daebf3f4d9d92722f1f3780d173501673ca6f47af39dd2102c85944ba603e4e9213d44a294ab1cec43dff82213fe53c5dea663279473be64a15
2023-08-10 14:03:52 -05:00
Steve Myers
1da01b4a0b ci: change rust stable version to 0.71.0 2023-08-10 11:54:33 -05:00
Steve Myers
f1ba03bf50 ci: fix to work with bdk 0.28.1 and msrv 1.61.0 2023-08-10 11:28:36 -05:00
thunderbiscuit
bc182c7164 Update CI actions to their latest versions
* checkout (v3)
* setup-java (v3)
* cache (v3)
* upload-artifact (v3)
* setup-python (v4)
2023-07-05 14:03:42 -04:00
thunderbiscuit
5fc189717d Fix CI bdk-jvm 2023-07-04 16:19:55 -04:00
thunderbiscuit
849bfe79c1 Add Windows target to Kotlin/JVM published library 2023-07-04 12:32:00 -04:00
thunderbiscuit
dc79b78b2d Fix Python readme and integrate new scripts 2023-06-30 16:18:27 -04:00
thunderbiscuit
7cc08f1d6f Fix publishing Python CI workflow 2023-06-30 16:17:46 -04:00
thunderbiscuit
031fcb02da Run Python Windows tests in CI 2023-06-30 16:01:01 -04:00
thunderbiscuit
5f9b5682e5 Use macos-13 image to test and publish Python libraries 2023-06-23 14:42:36 -04:00
thunderbiscuit
d0a7315c9d Remove patch release issue template 2023-06-16 22:28:32 -04:00
thunderbiscuit
0bfc56b0e8 Fix minor release issue template 2023-06-16 22:28:09 -04:00
thunderbiscuit
3dd6c203e8 Bump snapshot and dev versions of libraries 2023-06-16 22:11:12 -04:00
thunderbiscuit
76acbf575b Add release 0.29.0 to changelog 2023-06-16 22:09:14 -04:00
thunderbiscuit
039b64de5c Update JVM readme with specific Rust version 2023-06-15 11:10:28 -04:00
thunderbiscuit
fe7e4e21c0 Update Android readme with specific Rust and NDK versions 2023-06-15 11:09:48 -04:00
thunderbiscuit
77f89afc68 Update library version to 0.29.0 2023-06-15 10:15:23 -04:00
thunderbiscuit
25033f6bd6 Add Kotlin API docs for Script.toBytes 2023-06-12 13:36:50 -04:00
thunderbiscuit
3cb2c2c394 Expose Script.to_bytes() method 2023-06-12 13:36:44 -04:00
Steve Myers
5092987b26 [swift] suppress keyword escape warnings 2023-06-06 14:17:30 -05:00
Steve Myers
aea25dbf21 Update bdk-swift local build script 2023-06-05 16:45:29 -05:00
thunderbiscuit
ed67eba910 Align JVM target version for Kotlin and Java compile tasks
This commit resolves a build error related to mismatched JVM target
versions for the Kotlin and Java compile tasks. Previously, the
'compileJava' task was targeting JVM 11, while the 'compileKotlin'
task was targeting JVM 8.

Both tasks have now been set to target JVM 11, ensuring consistency
and eliminating the build error.
2023-06-05 10:35:23 -04:00
thunderbiscuit
90606b2455 Small style cleanups in JVM Gradle plugin 2023-06-05 10:09:27 -04:00
yellowhatpro
49e8fe461e added support for x86_64-pc-windows-msvc 64-bit MSVC 2023-06-05 10:09:14 -04:00
Matthew
de88184b8c github: add feature request issue template 2023-05-30 15:27:34 -05:00
thunderbiscuit
3be2c0495f Clean up is_mine method body 2023-05-15 09:01:39 -04:00
thunderbiscuit
2c4c64515f Fix Clippy warning 2023-05-03 15:13:22 -04:00
thunderbiscuit
17323d3184 Add Kotlin API docs for isMine method 2023-05-03 14:21:37 -04:00
thunderbiscuit
b382511a9e Add is_mine method on Wallet type
Closes #354
2023-05-03 14:19:50 -04:00
Steve Myers
d3895441d3 Update bdk-swift/build-local-swift.sh for aarch64-apple-ios-sim to use release-smaller profile 2023-05-01 11:37:05 -05:00
Steve Myers
269512a673 Fix bdk-swift/build-local-swift.sh for aarch64-apple-ios-sim 2023-05-01 10:40:07 -05:00
thunderbiscuit
d27206787a Add supported Python library changes to changelog 2023-04-28 09:00:37 -04:00
thunderbiscuit
c1b1fd6f5d Update changelog for 0.28.0 release 2023-04-28 08:57:11 -04:00
thunderbiscuit
7062fbd047 Update minor_release and patch_release templates 2023-04-28 08:56:58 -04:00
thunderbiscuit
0e34a6bacf Bump snapshot and dev versions of libraries 2023-04-28 08:56:47 -04:00
thunderbiscuit
89e85a20cf Fix Python libraries' names to prepare for releasing 2023-04-25 13:53:27 -04:00
David Sterling
d8718c3f05 Update bdk-jvm README build instructions
The current build instructions for bdk-jvm are insufficient for someone to build bdk-jvm from scratch. It's not outlined that JDK 11 and Rust is required on the system.
2023-04-25 13:48:32 -04:00
thunderbiscuit
871a06d1ce Add library build task to Android test CI workflow 2023-04-24 14:17:58 -04:00
thunderbiscuit
b820d6a2ba Remove support for Python 3.6 and 3.7 2023-04-24 13:57:12 -04:00
thunderbiscuit
79d9fa2909 Use ubuntu-20.04 image for all Linux CI runs 2023-04-24 13:53:56 -04:00
thunderbiscuit
a0e0467d39 Add workflow dispatch to all test workflows 2023-04-24 13:52:42 -04:00
thunderbiscuit
f2296704e6 Pin Rust version in CI workflows to 1.67 2023-04-24 13:51:37 -04:00
thunderbiscuit
b8b60dda87 Prepare language bindings libraries for 0.28 release 2023-04-17 14:34:03 -04:00
Steve Myers
a50e19e7e0 Update bdk to 0.28 2023-04-13 10:22:53 -04:00
Steve Myers
fab9ae8ae5 Add PartiallySignedTransaction.json_serialize() function 2023-04-13 09:21:22 -04:00
thunderbiscuit
478b12c489 Add API docs for fromScript method on Address 2023-04-13 08:50:08 -04:00
andreasgriffin
63b85b9100 Add from_script method to Address type 2023-04-13 08:50:03 -04:00
Steve Myers
0e6b472793 Merge bitcoindevkit/bdk-ffi#335: Use version 21 of the Android NDK in the CI test, build, and publish workflows
beb75dd552 Use version 21 of the Android NDK in the CI test, build, and publish workflows (thunderbiscuit)

Pull request description:

  This PR reverts an update to our Android CI workflows.

  For info on why this is needed, see issues #242 and #243.

ACKs for top commit:
  notmandatory:
    ACK beb75dd552

Tree-SHA512: dfee0b1f335318b86fa490fa85e7ef5d7a032e01a78db64a186c5d75e9e77e8f0e2af58ee5407d4fe38a0aa3aabbc7b06b23189aef0dfaad1d246a354452870d
2023-04-12 21:38:07 -05:00
thunderbiscuit
beb75dd552 Use version 21 of the Android NDK in the CI test, build, and publish workflows 2023-04-05 11:10:21 -04:00
thunderbiscuit
5ee8698e0a Add Kotlin API docs for new AddressInfo type 2023-03-28 19:51:15 -04:00
thunderbiscuit
ac600a1312 Clean up type aliases and use more explicit variable names 2023-03-28 19:47:38 -04:00
thunderbiscuit
f26031db80 Update tests for new AddressInfo type 2023-03-28 15:05:42 -04:00
thunderbiscuit
e7e1a6057e Use Address type in address field on AddressInfo 2023-03-28 13:29:06 -04:00
thunderbiscuit
2b7c104f11 Clean up of docs regarding removed cli tool 2023-03-27 13:39:28 -04:00
thunderbiscuit
6bab5a159d Remove bdk-ffi-bindgen tool section in root readme 2023-03-24 12:56:06 -04:00
thunderbiscuit
b6c8b145bb Fix bdk-android publishing CI workflow 2023-03-24 12:38:32 -04:00
Steve Myers
67610abeb6 Merge bitcoindevkit/bdk-ffi#326: Add SignOptions to Wallet.sign() params
4e5537acd2 Add SignOptions to Wallet.sign() params (Steve Myers)

Pull request description:

  ### Description

  Fixes #323

  ### Notes to the reviewers

  I didn't include the `tap_leaves_options: TapLeavesOptions` because it would require more deeply nested structures and is not currently required.

  ### Changelog notice

  - Add SignOptions to Wallet.sign() params.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

  #### New Features:

  * [ ] I've added tests for the new feature
  * [x] I've added docs for the new feature

Top commit has no ACKs.

Tree-SHA512: e846388b93b5270dd6d1f86ebff462d1c69aa0ea8359d7e4955c88eed4dac1ce00f8da3ecf2432e6eba6e1e5b2f52e3e789918aa31a4b739a7d0a5185b5c3c0c
2023-03-24 11:12:23 -05:00
Steve Myers
4e5537acd2 Add SignOptions to Wallet.sign() params 2023-03-24 10:51:44 -05:00
Steve Myers
6be4ddaf7b Merge bitcoindevkit/bdk-ffi#325: Expose Address payload and network properties
cd10c75e96 Add to_qr_uri() method on Address type (thunderbiscuit)
cbd44249f3 Add kotlin api-docs for Address.payload() and Address.network() (Steve Myers)
20c31d5383 Expose Address payload and network properties (Steve Myers)
616cb21738 Update Cargo.lock (Steve Myers)

Pull request description:

  ### Description

  Fixes #319

  ### Notes to the reviewers

  The test data was generated with:
  ```
  bitcoin-cli -rpcwallet="regtest2" -datadir=/tmp/regtest1/bitcoind -regtest validateaddress "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
  {
    "isvalid": true,
    "address": "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv",
    "scriptPubKey": "001404a6545885dd87b8e147cd327f2e9db362b72346",
    "isscript": false,
    "iswitness": true,
    "witness_version": 0,
    "witness_program": "04a6545885dd87b8e147cd327f2e9db362b72346"
  }
  ```

  ### Changelog notice

  Added
  - Expose Address payload and network properties.

  ### Checklists

  #### All Submissions:

  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

  #### New Features:

  * [x] I've added tests for the new feature
  * [x] I've added docs for the new feature

ACKs for top commit:
  thunderbiscuit:
    ACK cd10c75e96.

Tree-SHA512: 65a1d550577fd4a35cfeb8fb765002e47e0703ff40fc7a68c07b78bc1ba642d33a0efbdfa8f9322024841ba84d25c0e289294c10ae4c1b4d52eb9beffa3d0e10
2023-03-23 16:39:00 -05:00
thunderbiscuit
cd10c75e96 Add to_qr_uri() method on Address type 2023-03-23 17:03:40 -04:00
Steve Myers
cbd44249f3 Add kotlin api-docs for Address.payload() and Address.network() 2023-03-23 15:41:17 -05:00
Steve Myers
20c31d5383 Expose Address payload and network properties 2023-03-23 15:41:17 -05:00
Steve Myers
616cb21738 Update Cargo.lock 2023-03-23 15:41:17 -05:00
Steve Myers
d3a6453eda Merge bitcoindevkit/bdk-ffi#307: Expose more of the Transaction type
cba69e681a Clean up of From traits implementations (thunderbiscuit)
35d8fb3139 Clean up extract_tx method on PSBT (thunderbiscuit)
f003a6275e Clean up conversion between BDK TransactionDetails to ffi TransactionDetails (thunderbiscuit)
2f62377eec Add Eq and PartialEq traits on the Transaction type (thunderbiscuit)
81e208222a Add uniffi/cli as default feature to cargo (thunderbiscuit)
3dc6596aa2 Add include_raw boolean parameter on wallet list_transactions method (thunderbiscuit)
2342265c26 Remove unused NetworkLocalUtxo type (thunderbiscuit)
6c561228c2 Use the latest version of the Android NDK (thunderbiscuit)
e86909ab3d Clean up input and output methods on Transaction type (thunderbiscuit)
8e51756a3a Fix clippy errors (thunderbiscuit)
40263b425e Remove deprecated bdk-ffi-bindgen tool (thunderbiscuit)
9437051668 Fix fmt and clippy errors (thunderbiscuit)
7557e214c8 Add optional transaction field on the TransactionDetails type (thunderbiscuit)
40ca62086c Expose all fields on the Transaction type (thunderbiscuit)
e0506deffa Add new utility methods on Transaction type (thunderbiscuit)
d3e183a498 Add Kotlin API docs for new Transaction methods (thunderbiscuit)
1e9ecfbe52 Add weight, size, and vsize methods on the Transaction type (thunderbiscuit)

Pull request description:

  ## Description
  We've recently exposed the `Transaction` type, and I think a few methods on it would be useful. ~This is a draft PR with the first 3: `weight()`, `size()`, and `vsize()`~ _Edit: it's now much more_. I think there might be other methods we might want to expose as well. [Take a look at the docs to see them all](https://docs.rs/bitcoin/0.29.2/bitcoin/blockdata/transaction/struct.Transaction.html).

  Other candidates have now been added:
  1. [is_explicitly_rbf](https://docs.rs/bitcoin/0.29.2/bitcoin/blockdata/transaction/struct.Transaction.html#method.is_explicitly_rbf)
  2. [is_lock_time_enabled](https://docs.rs/bitcoin/0.29.2/bitcoin/blockdata/transaction/struct.Transaction.html#method.is_lock_time_enabled)
  3. [is_absolute_timelock_satisfied](https://docs.rs/bitcoin/0.29.2/bitcoin/blockdata/transaction/struct.Transaction.html#method.is_absolute_timelock_satisfied)
  4. [is_coin_base](https://docs.rs/bitcoin/0.29.2/bitcoin/blockdata/transaction/struct.Transaction.html#method.is_coin_base)
  5. [txid](https://docs.rs/bitcoin/0.29.2/bitcoin/blockdata/transaction/struct.Transaction.html#method.txid)

  This PR is growing in size but I decided to add all 4 fields on the `Transaction` type. This is useful because it means we can now add the `transaction` field on the `TransactionDetails` type (also added in this PR).

  I still have a few questions regarding all the translation between the ffi and bdk/rust types, some of the traits I had to remove (Eq and PartialEq on the TransactionDetails type) as well as the usage of `Option<Arc<T>>` vs `Arc<Option<T>>`. Will outline those tomorrow.

  Closes #303
  Closes #187

  ## Changelog notice
  ```md
  APIs Added
  - `Transaction` type now exposes the `.weight()`, `.size()`, `.vsize()`, `is_explicitly_rbf()`, `is_lock_time_enabled()`, `is_coin_base(), and `txid()` methods [#307]

  [#307]: https://github.com/bitcoindevkit/bdk-ffi/pull/307
  ```

  ## Checklists

  #### All Submissions:
  * [x] I've signed all my commits
  * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md)
  * [x] I ran `cargo fmt` and `cargo clippy` before committing

  #### New Features:
  * [ ] I've added tests for the new feature
  * [x] I've added docs for the new feature

ACKs for top commit:
  notmandatory:
    ACK cba69e681a

Tree-SHA512: 83e860407b230b6cdca59c0d74a486e52e7ea34d655d418ddc115418551a61665cad8f1d6182858d8ed6d7d72a8558e595e61644efed6c704de8a0b6960a0df2
2023-03-23 15:40:19 -05:00
thunderbiscuit
cba69e681a Clean up of From traits implementations 2023-03-23 15:16:53 -04:00
thunderbiscuit
35d8fb3139 Clean up extract_tx method on PSBT 2023-03-23 13:14:48 -04:00
thunderbiscuit
f003a6275e Clean up conversion between BDK TransactionDetails to ffi TransactionDetails 2023-03-23 13:08:55 -04:00
thunderbiscuit
2f62377eec Add Eq and PartialEq traits on the Transaction type 2023-03-23 12:48:04 -04:00
thunderbiscuit
81e208222a Add uniffi/cli as default feature to cargo 2023-03-23 12:39:22 -04:00
thunderbiscuit
3dc6596aa2 Add include_raw boolean parameter on wallet list_transactions method 2023-03-23 12:32:54 -04:00
thunderbiscuit
2342265c26 Remove unused NetworkLocalUtxo type 2023-03-23 12:21:18 -04:00
thunderbiscuit
6c561228c2 Use the latest version of the Android NDK 2023-03-21 17:01:54 -04:00
thunderbiscuit
e86909ab3d Clean up input and output methods on Transaction type 2023-03-20 15:16:39 -04:00
thunderbiscuit
8e51756a3a Fix clippy errors 2023-03-14 08:22:05 -04:00
thunderbiscuit
40263b425e Remove deprecated bdk-ffi-bindgen tool 2023-03-13 15:04:48 -04:00
thunderbiscuit
9437051668 Fix fmt and clippy errors 2023-03-13 14:52:17 -04:00
thunderbiscuit
7557e214c8 Add optional transaction field on the TransactionDetails type 2023-03-13 14:49:07 -04:00
thunderbiscuit
40ca62086c Expose all fields on the Transaction type 2023-03-13 12:13:09 -04:00
thunderbiscuit
e0506deffa Add new utility methods on Transaction type
This PR adds the txid(), is_coin_base(), is_explicitly_rbf(),
and is_lock_time_enabled() methods.
Fixes #303
2023-03-13 08:54:37 -04:00
thunderbiscuit
d3e183a498 Add Kotlin API docs for new Transaction methods 2023-03-13 08:54:31 -04:00
thunderbiscuit
1e9ecfbe52 Add weight, size, and vsize methods on the Transaction type 2023-03-13 08:54:25 -04:00
thunderbiscuit
d48bacd29b Streamline CI workflows 2023-03-10 21:03:24 -05:00
thunderbiscuit
c1243f9e1c Update publish Python CI workflow 2023-03-01 13:43:24 -05:00
thunderbiscuit
9c6069e389 Run tests in Python CI workflow 2023-03-01 11:37:08 -05:00
thunderbiscuit
488edf8bd2 Fix Python build workflow to account for Linux/Windows 2023-03-01 10:02:45 -05:00
thunderbiscuit
90763d42a2 Fix Swift script and CI workflow 2023-02-28 12:42:54 -05:00
thunderbiscuit
44b2ef1382 Fix cargo test workflow 2023-02-28 12:04:43 -05:00
thunderbiscuit
25617d1f23 Fix cargo clippy CI workflow 2023-02-28 09:27:56 -05:00
thunderbiscuit
2fcafe2b80 Fix Rust CI workflow 2023-02-28 08:31:51 -05:00
thunderbiscuit
5728b50100 Fix formatting warning in Gradle plugins 2023-02-27 20:07:02 -05:00
thunderbiscuit
d08317775b Update Python build scripts for uniffi-rs 0.23.0 2023-02-27 20:04:32 -05:00
thunderbiscuit
c93f292b0e Upgrade uniffi-rs to 0.23.0 and migrate Gradle plugins 2023-02-27 14:43:52 -05:00
thunderbiscuit
a75c868eb2 Update minor_release and patch_release templates 2023-02-22 14:27:03 -05:00
thunderbiscuit
974ff66caf Bump snapshot and dev versions of libraries 2023-02-21 15:45:46 -05:00
thunderbiscuit
2309b19209 Update changelog for all versions since 0.10.0 2023-02-21 15:42:50 -05:00
93 changed files with 4139 additions and 4732 deletions

View File

@@ -0,0 +1,17 @@
---
name: Enhancement request
about: Request a new feature or change to an existing feature
title: ''
labels: 'enhancement'
assignees: ''
---
**Describe the enhancement**
<!-- A clear and concise description of what you would like added or changed. -->
**Use case**
<!-- Tell us how you or others will use this new feature or change to an existing feature. -->
**Additional context**
<!-- Add any other context about the enhancement here. -->

View File

@@ -4,98 +4,84 @@ about: Create a new minor release [for release managers only]
title: 'Release MAJOR.MINOR+1.0'
labels: 'release'
assignees: ''
---
## Create a new minor release
## Bumping BDK Rust Version
1. - [ ] Open a PR with an update to `Cargo.toml` to the new bdk release candidate and ensure all CI workflows run correctly. Fix errors if necessary.
2. - [ ] Once the new bdk release is out, update the PR to replace the release candidate with the full release and merge.
### Summary
### Specific Libraries' Workflows
#### _Android_
3. - [ ] Update the API docs to reflect the changes in the API
4. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch.
5. - [ ] Build the library and run the tests, and adjust if necessary.
```shell
# start an emulator prior to running the tests
cd ./bdk-android/
./gradlew buildAndroidLib
./gradlew connectedAndroidTest
```
6. - [ ] Update the readme if necessary
#### _JVM_
7. - [ ] Update the API docs to reflect the changes in the API
8. - [ ] Delete the `target` directory in bdk-ffi and all previous artifacts to make sure you're building the library from scratch
9. - [ ] Build the library and run the tests, and adjust if necessary
```shell
cd ./bdk-jvm/
./gradlew buildJvmLib
./gradlew test
```
10. - [ ] Update the readme if necessary
#### _Swift_
11. - [ ] Run the tests and adjust if necessary
```shell
./bdk-swift/build-local-swift.sh
cd ./bdk-swift/
swift test
```
12. - [ ] Update the readme if necessary
#### _Python_
13. - [ ] Delete the `.tox`, `dist`, `build`, and `bdkpython.egg-info` and rust `target` directories to make sure you are building the library from scratch without any caches
14. - [ ] Build the library
```shell
cd ./bdk-python/
pip3 install --requirement requirements.txt
bash ./scripts/generate-macos-arm64.sh # run the script for your particular platform
python3 setup.py --verbose bdist_wheel
```
15. - [ ] Run the tests and adjust if necessary
```shell
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
python -m unittest --verbose tests/test_bdk.py
```
16. - [ ] Update the readme and `setup.py` if necessary
<--release summary to be used in announcements-->
### Commit
<--latest commit ID to include in this release-->
### Changelog
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
### Checklist
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
branch **development** version is *MAJOR.MINOR.0*.
#### On the day of the feature freeze
Change the `master` branch to the next MINOR+1 version:
- [ ] Switch to the `master` branch.
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR+1`, eg. `bump_dev_0_22`.
- [ ] Bump the `bump_dev_MAJOR_MINOR+1` branch to the next development MINOR+1 version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR+1` branch to `master`.
- Title PR "Bump version to MAJOR.MINOR+1.0".
Create a new release branch:
- [ ] Double check that your local `master` is up-to-date with the upstream repo.
- [ ] Create a new branch called `release/MAJOR.MINOR+1` from `master`.
Add a release candidate tag, this is optional and only needed for major `bdk-ffi` changes that
require a longer testing cycle:
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.1` version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.1`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.1".
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
- The tag name should be `vMAJOR.MINOR+1.0-rc.1`
- Use message "Release MAJOR.MINOR+1.0 rc.1".
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
- [ ] Push the `release/MAJOR.MINOR` branch and new tag to the `bitcoindevkit/bdk` repo.
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.1` tag.
If any issues need to be fixed before the *MAJOR.MINOR+1.0* version is released:
- [ ] Merge fix PRs to the `master` branch.
- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR+1` branch.
- [ ] Verify fixes in `release/MAJOR.MINOR+1` branch.
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.x+1` version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.x+1`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.x+1".
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
- The tag name should be `vMAJOR.MINOR+1.0-rc.x+1`, where x is the current release candidate number.
- Use tag message "Release MAJOR.MINOR+1.0 rc.x+1".
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.x+1` tag.
#### On the day of the release
Tag and publish new release:
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0` version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
- The tag name should be `vMAJOR.MINOR+1.0`
- The first line of the tag message should be "Release MAJOR.MINOR+1.0".
- In the body of the tag message put a copy of the **Summary** and **Changelog** for the release.
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
- [ ] Wait for the CI to finish one last time.
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
- [ ] Create the release on GitHub.
- Go to "tags", click on the dots on the right and select "Create Release".
- Set the title to `Release MAJOR.MINOR+1.0`.
- In the release notes body put the **Summary** and **Changelog**.
- Use the "+ Auto-generate release notes" button to add details from included PRs.
- Until we reach a `1.0.0` release check the "Pre-release" box.
- [ ] After downstream language repos are also updated announce the release, using the **Summary**,
on Discord, Twitter and Mastodon.
- [ ] Celebrate 🎉
[Semantic Versioning]: https://semver.org/
[crates.io]: https://crates.io/crates/bdk
[docs.rs]: https://docs.rs/bdk/latest/bdk
["keep a changelog"]: https://keepachangelog.com/en/1.0.0/
### Release Workflow
17. - [ ] Update the Android, JVM, Python, and Swift libraries as per the _Specific Libraries' Workflows_ section above. Open a single PR on master for all of these changes called `Prepare language bindings libraries for 0.X release`. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/315).
18. - [ ] Create a new branch off of `master` called `release/version`
19. - [ ] Update bdk-android version from `SNAPSHOT` version to release version
20. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
21. - [ ] Update bdk-python version from `.dev` version to release version
22. - [ ] Open a PR to that release branch that updates the Android, JVM, and Python libraries' versions in step 19, 20, and 21. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/316).
23. - [ ] Get a review and ACK and merge the PR updating all the languages to their release versions
24. - [ ] Create the tag for the release and make sure to add the changelog info to the tag (works better if you prepare the tag message on the side in a text editor). Push the tag to GitHub.
```shell
git tag v0.6.0 --sign --edit
git push upstream v0.6.0
```
25. - [ ] Trigger manual releases for all 4 libraries (for Swift, trigger the release on `master` and simply add the version number in the text field when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`)
26. - [ ] Make sure the released libraries work and contain the artifacts you would expect
27. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file
28. - [ ] Bump the versions on master from `0.9.0-SNAPSHOT` to `0.10.0-SNAPSHOT`, `0.6.0.dev0` to `0.7.0.dev0`
29. - [ ] Apply changes to the minor_release and patch_release issue templates if they need any
30. - [ ] Open a PR on master with the changes in steps 29, 30, and 31. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/317). Get a review and merge the PR.
31. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one)
32. - [ ] Build and publish API docs for JVM, Android, and Java on the website
```shell
./gradlew dokkaHtml # bdk-jvm (Dokka)
./gradlew dokkaJavadoc # bdk-jvm (java-style documentation)
./gradlew dokkaHtml # bdk-android (Dokka)
```
33. - [ ] Post in the announcement channel
34. - [ ] Tweet about the library

View File

@@ -1,69 +0,0 @@
---
name: Patch Release
about: Create a new patch release [for release managers only]
title: 'Release MAJOR.MINOR.PATCH+1'
labels: 'release'
assignees: ''
---
## Create a new patch release
### Summary
<--release summary to be used in announcements-->
### Commit
<--latest commit ID to include in this release-->
### Changelog
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
### Checklist
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
branch **development** version is *MAJOR.MINOR.PATCH*.
### On the day of the patch release
Change the `master` branch to the new PATCH+1 version:
- [ ] Switch to the `master` branch.
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR_PATCH+1`, eg. `bump_dev_0_22_1`.
- [ ] Bump the `bump_dev_MAJOR_MINOR` branch to the next development PATCH+1 version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR.PATCH+1`.
- The commit message should be "Bump version to MAJOR.MINOR.PATCH+1".
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR_PATCH+1` branch to `master`.
- Title PR "Bump version to MAJOR.MINOR.PATCH+1".
Cherry-pick, tag and publish new PATCH+1 release:
- [ ] Merge fix PRs to the `master` branch.
- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR` branch to be patched.
- [ ] Verify fixes in `release/MAJOR.MINOR` branch.
- [ ] Bump the `release/MAJOR.MINOR.PATCH+1` branch to `MAJOR.MINOR.PATCH+1` version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR.MINOR.PATCH+1`.
- The commit message should be "Bump version to MAJOR.MINOR.PATCH+1".
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR` branch.
- The tag name should be `vMAJOR.MINOR.PATCH+1`
- The first line of the tag message should be "Release MAJOR.MINOR.PATCH+1".
- In the body of the tag message put a copy of the **Summary** and **Changelog** for the release.
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
- [ ] Wait for the CI to finish one last time.
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
- [ ] Create the release on GitHub.
- Go to "tags", click on the dots on the right and select "Create Release".
- Set the title to `Release MAJOR.MINOR.PATCH+1`.
- In the release notes body put the **Summary** and **Changelog**.
- Use the "+ Auto-generate release notes" button to add details from included PRs.
- Until we reach a `1.0.0` release check the "Pre-release" box.
- [ ] After downstream language repos are also updated announce the release, using the **Summary**,
on Discord, Twitter and Mastodon.
- [ ] Celebrate 🎉
[Semantic Versioning]: https://semver.org/
[crates.io]: https://crates.io/crates/bdk
[docs.rs]: https://docs.rs/bdk/latest/bdk
["keep a changelog"]: https://keepachangelog.com/en/1.0.0/

View File

@@ -10,10 +10,16 @@ on:
jobs:
security_audit:
name: Security Audit
name: Security audit
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-ffi
steps:
- uses: actions/checkout@v2
- uses: actions-rs/audit-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: "Check out PR branch"
uses: actions/checkout@v3
- name: "Run audit"
run: |
cargo install cargo-audit
cargo-audit audit

View File

@@ -1,126 +0,0 @@
name: Build Python wheels
on:
push:
paths:
- "bdk-ffi/**"
- "bdk-python/**"
pull_request:
paths:
- "bdk-ffi/**"
- "bdk-python/**"
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
# tries to load glibc and fails because it requires a more recent version.
jobs:
build-manylinux2014-x86_64-wheel:
name: 'Build Manylinux 2014 x86_64 wheel'
runs-on: ubuntu-latest
defaults:
run:
working-directory: bdk-python
container:
image: quay.io/pypa/manylinux2014_x86_64
env:
PLAT: manylinux2014_x86_64
PYBIN: '/opt/python/${{ matrix.python }}/bin'
strategy:
matrix:
python: # Update this list whenever the docker image is updated (check /opt/python/)
# - cp36-cp36m
# - cp37-cp37m
# - cp38-cp38
# - cp39-cp39
- cp310-cp310
# - pp37-pypy37_pp73
# - pp38-pypy38_pp73
steps:
- name: checkout
uses: actions/checkout@v2
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: install requirements
run: ${PYBIN}/pip install -r requirements.txt
- name: generate bindings
run: bash generate.sh
- name: build wheel
run: ${PYBIN}/pip wheel . --no-deps -w /tmp/wheelhouse
- name: repair wheel
run: auditwheel repair /tmp/wheelhouse/* --plat "$PLAT" -w /tmp/wheelhouse-repaired
- uses: actions/upload-artifact@v2
with:
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
path: /tmp/wheelhouse-repaired/*.whl
build-macos-universal-wheel:
name: 'Build macOS universal wheel'
runs-on: macos-latest
defaults:
run:
working-directory: bdk-python
strategy:
matrix:
python:
# - '3.7'
# - '3.8'
# - '3.9'
- '3.10'
steps:
- name: checkout
uses: actions/checkout@v2
with:
submodules: true
- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}
- run: python3 --version
- run: rustup target add aarch64-apple-darwin
- run: pip3 install --user -r requirements.txt
- run: pip3 install --user wheel
- run: bash generate.sh
- name: build wheel
env:
ARCHFLAGS: "-arch x86_64 -arch arm64"
run: python3 setup.py -v bdist_wheel
- uses: actions/upload-artifact@v2
with:
name: bdkpython-macos-${{ matrix.python }}
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/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: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl

View File

@@ -1,4 +1,4 @@
name: bdk-ffi CI
name: Rust layer CI
on:
push:
paths:
@@ -8,59 +8,77 @@ on:
- "bdk-ffi/**"
jobs:
build-test:
name: Build and test
runs-on: ubuntu-latest
name: "Build and test"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-ffi
strategy:
matrix:
rust:
- version: 1.63.0 # STABLE
- version: 1.73.0
clippy: true
- version: 1.61.0 # MSRV
steps:
- name: checkout
uses: actions/checkout@v2
- name: Generate cache key
- name: "Checkout"
uses: actions/checkout@v3
- name: "Generate cache key"
run: echo "${{ matrix.rust.version }} ${{ matrix.features }}" | tee .cache_key
- name: cache
uses: actions/cache@v2
- name: "Cache"
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('.cache_key') }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: Set default toolchain
- name: "Set default toolchain"
run: rustup default ${{ matrix.rust.version }}
- name: Set profile
- name: "Set profile"
run: rustup set profile minimal
- name: Add clippy
- name: "Add clippy"
if: ${{ matrix.rust.clippy }}
run: rustup component add clippy
- name: Update toolchain
- name: "Update toolchain"
run: rustup update
- name: Build
- name: "Build"
run: cargo build
- name: Clippy
- name: "Clippy"
if: ${{ matrix.rust.clippy }}
run: cargo clippy --all-targets -- -D warnings
- name: Test
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test
run: cargo clippy --all-targets --features "uniffi/bindgen-tests" -- -D warnings
- name: "Test"
run: CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test --features uniffi/bindgen-tests
fmt:
name: Rust fmt
runs-on: ubuntu-latest
name: "Rust fmt"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-ffi
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set default toolchain
- name: "Checkout"
uses: actions/checkout@v3
- name: "Set default toolchain"
run: rustup default nightly
- name: Set profile
- name: "Set profile"
run: rustup set profile minimal
- name: Add rustfmt
- name: "Add rustfmt"
run: rustup component add rustfmt
- name: Update toolchain
- name: "Update toolchain"
run: rustup update
- name: Check fmt
- name: "Check fmt"
run: cargo fmt --all -- --config format_code_in_doc_comments=true --check

93
.github/workflows/live-tests.yaml vendored Normal file
View File

@@ -0,0 +1,93 @@
name: Run All Live Tests
on:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # Once per week
jobs:
jvm-tests:
name: "Build and test JVM library on Linux"
runs-on: ubuntu-20.04
steps:
- name: "Checkout publishing branch"
uses: actions/checkout@v3
- name: "Cache"
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
./target
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: "Set up JDK"
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: "Set default Rust version to 1.73.0"
run: rustup default 1.73.0
- name: "Build bdk-jvm library"
run: |
cd bdk-jvm
./gradlew buildJvmLib
- name: "Run live JVM tests"
run: |
cd bdk-jvm
./gradlew test
swift-tests:
name: "Build and test iOS library on macOS"
runs-on: macos-12
steps:
- name: "Checkout"
uses: actions/checkout@v3
- name: "Build Swift package"
run: bash ./bdk-swift/build-local-swift.sh
- name: "Run live Swift tests"
working-directory: bdk-swift
run: swift test
python-tests:
name: "Build and test Python library on Linux"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-python
container:
image: quay.io/pypa/manylinux2014_x86_64
env:
PLAT: manylinux2014_x86_64
PYBIN: "/opt/python/${{ matrix.python }}/bin"
strategy:
matrix:
python:
- cp310-cp310
steps:
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-linux.sh
- name: "Build wheel"
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
# see issue #350 for more information
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
- name: "Install wheel"
run: ${PYBIN}/pip install ./dist/*.whl
- name: "Run live Python tests"
run: ${PYBIN}/python -m unittest --verbose

View File

@@ -1,27 +1,17 @@
name: Publish bdk-android to Maven Central
on: [workflow_dispatch]
env:
ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
# By default, the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT
# ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Install Android NDK 21.4.7075529
run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
- name: "Check out PR branch"
uses: actions/checkout@v3
- name: Check out PR branch
uses: actions/checkout@v2
- name: cache
uses: actions/cache@v2
- name: "Cache"
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
@@ -29,21 +19,24 @@ jobs:
./target
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: Set up JDK
uses: actions/setup-java@v2
- name: "Set up JDK"
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Install rust android targets
- name: "Set default Rust version to 1.73.0"
run: rustup default 1.73.0
- name: "Install Rust Android targets"
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
- name: Build bdk-android library
- name: "Build bdk-android library"
run: |
cd bdk-android
./gradlew buildAndroidLib
- name: Publish to Maven Local and Maven Central
- name: "Publish to Maven Local and Maven Central"
env:
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}

View File

@@ -2,14 +2,14 @@ name: Publish bdk-jvm to Maven Central
on: [workflow_dispatch]
jobs:
build-jvm-macOS-M1-native-lib:
name: Create M1 and x86_64 JVM native binaries
build-macOS-native-libs:
name: "Create M1 and x86_64 native binaries"
runs-on: macos-12
steps:
- name: Checkout publishing branch
uses: actions/checkout@v2
- name: "Checkout publishing branch"
uses: actions/checkout@v3
- name: Cache
- name: "Cache"
uses: actions/cache@v3
with:
path: |
@@ -18,42 +18,68 @@ jobs:
./target
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: Set up JDK
uses: actions/setup-java@v2
- name: "Set up JDK"
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Install aarch64 Rust target
- name: "Set default Rust version to 1.73.0"
run: rustup default 1.73.0
- name: "Install aarch64 Rust target"
run: rustup target add aarch64-apple-darwin
- name: Build bdk-jvm library
- name: "Build bdk-jvm library"
run: |
cd bdk-jvm
./gradlew buildJvmLib
# build aarch64 + x86_64 native libraries and upload
- name: Upload macOS native libraries for reuse in publishing job
- name: "Upload macOS native libraries for reuse in publishing job"
uses: actions/upload-artifact@v3
with:
# name: no name is required because we upload the entire directory
# the default name "artifact" will be used
name: artifact-macos
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-jvm/lib/src/main/resources/
build-jvm-full-library:
name: Create full bdk-jvm library
needs: [build-jvm-macOS-M1-native-lib]
runs-on: ubuntu-22.04
build-windows-native-lib:
name: "Create Windows native binaries"
runs-on: windows-2022
steps:
- name: Checkout publishing branch
uses: actions/checkout@v2
- name: "Checkout publishing branch"
uses: actions/checkout@v3
- name: Update bdk-ffi git submodule
- name: "Set up JDK"
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: "Set default Rust version to 1.73.0"
run: rustup default 1.73.0
- name: "Install x86_64-pc-windows-msvc Rust target"
run: rustup target add x86_64-pc-windows-msvc
- name: "Build bdk-jvm library"
run: |
git submodule set-url bdk-ffi https://github.com/bitcoindevkit/bdk-ffi.git
git submodule update --init bdk-ffi
cd bdk-jvm
./gradlew buildJvmLib
- name: Cache
- name: "Upload Windows native libraries for reuse in publishing job"
uses: actions/upload-artifact@v3
with:
name: artifact-windows
path: D:\a\bdk-ffi\bdk-ffi\bdk-jvm\lib\src\main\resources\
build-full-library:
name: Create full bdk-jvm library
needs: [build-macOS-native-libs, build-windows-native-lib]
runs-on: ubuntu-20.04
steps:
- name: "Checkout publishing branch"
uses: actions/checkout@v3
- name: "Cache"
uses: actions/cache@v3
with:
path: |
@@ -62,26 +88,39 @@ jobs:
./target
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: Set up JDK
uses: actions/setup-java@v2
- name: "Set up JDK"
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Build bdk-jvm library
- name: "Set default Rust version to 1.73.0"
run: rustup default 1.73.0
- name: "Build bdk-jvm library"
run: |
cd bdk-jvm
./gradlew buildJvmLib
- name: Download macOS native libraries from previous job
- name: "Download macOS native binaries from previous job"
uses: actions/download-artifact@v3
id: download
with:
# download the artifact created in the prior job (named "artifact")
name: artifact
name: artifact-macos
path: ./bdk-jvm/lib/src/main/resources/
- name: Publish to Maven Central
- name: "Download Windows native libraries from previous job"
uses: actions/download-artifact@v3
with:
name: artifact-windows
path: ./bdk-jvm/lib/src/main/resources/
- name: "Upload library code and binaries"
uses: actions/upload-artifact@v3
with:
name: artifact-full
path: ./bdk-jvm/lib/
- name: "Publish to Maven Central"
env:
ORG_GRADLE_PROJECT_signingKeyId: ${{ secrets.PGP_KEY_ID }}
ORG_GRADLE_PROJECT_signingKey: ${{ secrets.PGP_SECRET_KEY }}

View File

@@ -1,4 +1,4 @@
name: Build and publish Python wheels
name: Publish bdkpython to PyPI
on: [workflow_dispatch]
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
@@ -7,9 +7,9 @@ on: [workflow_dispatch]
# tries to load glibc and fails because it requires a more recent version.
jobs:
build-manylinux2014-x86_64-wheel:
name: 'Build Manylinux 2014 x86_64 wheel'
runs-on: ubuntu-latest
build-manylinux2014-x86_64-wheels:
name: "Build Manylinux 2014 x86_64 wheel"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-python
@@ -17,127 +17,159 @@ jobs:
image: quay.io/pypa/manylinux2014_x86_64
env:
PLAT: manylinux2014_x86_64
PYBIN: '/opt/python/${{ matrix.python }}/bin'
PYBIN: "/opt/python/${{ matrix.python }}/bin"
strategy:
matrix:
python: # Update this list whenever the docker image is updated (check /opt/python/)
- cp36-cp36m
- cp37-cp37m
- cp38-cp38
- cp39-cp39
- cp310-cp310
- pp37-pypy37_pp73
- pp38-pypy38_pp73
steps:
- name: checkout
uses: actions/checkout@v2
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
# TODO 2: Other CI workflows use explicit Rust compiler versions, I think we should do the same here
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: install requirements
run: ${PYBIN}/pip install -r requirements.txt
- name: generate bindings
run: bash generate.sh
- name: build wheel
run: ${PYBIN}/pip wheel . --no-deps -w /tmp/wheelhouse
- name: repair wheel
run: auditwheel repair /tmp/wheelhouse/* --plat "$PLAT" -w /tmp/wheelhouse-repaired
- uses: actions/upload-artifact@v2
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-linux.sh
- name: "Build wheel"
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
# see issue #350 for more information
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
- uses: actions/upload-artifact@v3
with:
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
path: /tmp/wheelhouse-repaired/*.whl
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-macos-universal-wheel:
name: 'Build macOS universal wheel'
runs-on: macos-latest
build-macos-arm64-wheels:
name: "Build macOS arm64 wheel"
runs-on: macos-13
defaults:
run:
working-directory: bdk-python
strategy:
matrix:
python:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
- "3.8"
- "3.9"
- "3.10"
steps:
- name: checkout
uses: actions/checkout@v2
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- uses: actions/setup-python@v2
- name: "Install Python"
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- run: python3 --version
- run: rustup target add aarch64-apple-darwin
- run: pip3 install --user -r requirements.txt
- run: pip3 install --user wheel
- run: bash generate.sh
- name: build wheel
env:
ARCHFLAGS: "-arch x86_64 -arch arm64"
run: python3 setup.py -v bdist_wheel
- uses: actions/upload-artifact@v2
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-macos-arm64.sh
- name: "Build wheel"
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
# see issue #350 for more information
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_arm64 --verbose
- name: "Upload artifacts"
uses: actions/upload-artifact@v3
with:
name: bdkpython-macos-${{ matrix.python }}
name: bdkpython-macos-arm64-${{ matrix.python }}
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-windows-wheel:
name: 'Build windows wheel'
runs-on: windows-latest
build-macos-x86_64-wheels:
name: "Build macOS x86_64 wheel"
runs-on: macos-13
defaults:
run:
working-directory: bdk-python
strategy:
matrix:
python:
- '3.7'
- '3.8'
- '3.9'
- '3.10'
- "3.8"
- "3.9"
- "3.10"
steps:
- name: checkout
uses: actions/checkout@v2
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- uses: actions/setup-python@v2
- name: "Install Python"
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- run: python --version
- run: pip install --user -r requirements.txt
- run: bash generate.sh
shell: bash
- run: pip install --user wheel
- name: build wheel
run: python setup.py -v bdist_wheel
- uses: actions/upload-artifact@v2
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-macos-x86_64.sh
- name: "Build wheel"
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
# see issue #350 for more information
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_x86_64 --verbose
- uses: actions/upload-artifact@v3
with:
name: bdkpython-macos-x86_64-${{ matrix.python }}
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-windows-wheels:
name: "Build Windows wheel"
runs-on: windows-2022
defaults:
run:
working-directory: bdk-python
strategy:
matrix:
python:
- "3.8"
- "3.9"
- "3.10"
steps:
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-windows.sh
- name: "Build wheel"
run: python setup.py bdist_wheel --verbose
- name: "Upload artifacts"
uses: actions/upload-artifact@v3
with:
name: bdkpython-win-${{ matrix.python }}
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
publish-pypi:
name: 'Publish on PyPI'
runs-on: ubuntu-latest
name: "Publish on PyPI"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-python
needs: [build-manylinux2014-x86_64-wheel, build-macos-universal-wheel, build-windows-wheel]
# needs: [build-macos-universal-wheel]
needs: [build-manylinux2014-x86_64-wheels, build-macos-arm64-wheels, build-macos-x86_64-wheels, build-windows-wheels]
steps:
- name: Checkout
uses: actions/checkout@v2
- name: "Checkout"
uses: actions/checkout@v3
- name: 'Download artifacts in dist/ directory'
uses: actions/download-artifact@v2
- name: "Download artifacts in dist/ directory"
uses: actions/download-artifact@v3
with:
path: dist/
# - name: Display structure of downloaded files
# run: ls -R
# - name: 'Publish on test PyPI'
# - name: "Publish on test PyPI"
# uses: pypa/gh-action-pypi-publish@release/v1
# with:
# user: __token__
@@ -145,7 +177,7 @@ jobs:
# repository_url: https://test.pypi.org/legacy/
# packages_dir: dist/*/
- name: 'Publish on PyPI'
- name: "Publish on PyPI"
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__

View File

@@ -1,5 +1,6 @@
name: Test Android
on:
workflow_dispatch:
push:
paths:
- "bdk-ffi/**"
@@ -9,27 +10,20 @@ on:
- "bdk-ffi/**"
- "bdk-android/**"
env:
ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/21.4.7075529
# By default, the new ubuntu-20.04 images use the following ANDROID_NDK_ROOT
# ANDROID_NDK_ROOT: /usr/local/lib/android/sdk/ndk/25.0.8775105
# The default Android NDK on the ubuntu-22.04 image is 25.2.9519653
jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Install Android NDK 21.4.7075529
run: |
ANDROID_ROOT=/usr/local/lib/android
ANDROID_SDK_ROOT=${ANDROID_ROOT}/sdk
SDKMANAGER=${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager
echo "y" | $SDKMANAGER "ndk;21.4.7075529"
- name: "Show default version of NDK"
run: echo $ANDROID_NDK_ROOT
- name: Check out PR branch
uses: actions/checkout@v2
- name: "Check out PR branch"
uses: actions/checkout@v3
- name: cache
uses: actions/cache@v2
- name: "Cache"
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
@@ -37,16 +31,27 @@ jobs:
./target
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: Set up JDK
uses: actions/setup-java@v2
- name: "Set up JDK"
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Install rust android targets
- name: "Set default Rust version to 1.73.0"
run: rustup default 1.73.0
- name: "Install Rust Android targets"
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
- name: Run Android tests
- name: "Build Android library"
run: |
cd bdk-android
./gradlew test --console=rich
./gradlew buildAndroidLib --console=plain
# There are currently no unit tests for bdk-android (see the tests in bdk-jvm instead) and the
# integration tests require the macOS image which is not working with the older NDK version we
# are using, so for now we just make sure that the library builds and omit the connectedTest
# - name: "Run Android connected tests"
# run: |
# cd bdk-android
# ./gradlew connectedAndroidTest --console=plain

View File

@@ -1,5 +1,6 @@
name: Test JVM
name: Test Kotlin/JVM
on:
workflow_dispatch:
push:
paths:
- "bdk-ffi/**"
@@ -13,11 +14,11 @@ jobs:
build:
runs-on: ubuntu-20.04
steps:
- name: Check out PR branch
uses: actions/checkout@v2
- name: "Check out PR branch"
uses: actions/checkout@v3
- name: cache
uses: actions/cache@v2
- name: "Cache"
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
@@ -25,13 +26,16 @@ jobs:
./target
key: ${{ runner.os }}-${{ hashFiles('**/Cargo.toml','**/Cargo.lock') }}
- name: Set up JDK
uses: actions/setup-java@v2
- name: "Set up JDK"
uses: actions/setup-java@v3
with:
distribution: temurin
java-version: 11
- name: Run JVM tests
- name: "Set default Rust version to 1.73.0"
run: rustup default 1.73.0
- name: "Run JVM tests"
run: |
cd bdk-jvm
./gradlew test --console=rich
./gradlew test -P excludeConnectedTests

189
.github/workflows/test-python.yaml vendored Normal file
View File

@@ -0,0 +1,189 @@
name: Test Python
on:
workflow_dispatch:
push:
paths:
- "bdk-ffi/**"
- "bdk-python/**"
pull_request:
paths:
- "bdk-ffi/**"
- "bdk-python/**"
# We use manylinux2014 because older CentOS versions used by 2010 and 1 have a very old glibc version, which
# makes it very hard to use GitHub's javascript actions (checkout, upload-artifact, etc).
# They mount their own nodejs interpreter inside your container, but since that's not statically linked it
# tries to load glibc and fails because it requires a more recent version.
jobs:
build-manylinux2014-x86_64-wheels:
name: "Build and test Manylinux 2014 x86_64 wheels"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-python
container:
image: quay.io/pypa/manylinux2014_x86_64
env:
PLAT: manylinux2014_x86_64
PYBIN: "/opt/python/${{ matrix.python }}/bin"
strategy:
matrix:
python:
- cp38-cp38
- cp39-cp39
- cp310-cp310
steps:
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-linux.sh
- name: "Build wheel"
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
# see issue #350 for more information
run: ${PYBIN}/python setup.py bdist_wheel --plat-name manylinux_2_17_x86_64 --verbose
- name: "Install wheel"
run: ${PYBIN}/pip install ./dist/*.whl
- name: "Run tests"
run: ${PYBIN}/python -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
- name: "Upload artifact test"
uses: actions/upload-artifact@v3
with:
name: bdkpython-manylinux2014-x86_64-${{ matrix.python }}
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-macos-arm64-wheels:
name: "Build and test macOS arm64 wheels"
runs-on: macos-13
defaults:
run:
working-directory: bdk-python
strategy:
matrix:
python:
- "3.8"
- "3.9"
- "3.10"
steps:
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- name: "Install Python"
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-macos-arm64.sh
- name: "Build wheel"
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
# see issue #350 for more information
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_arm64 --verbose
# You can't install the arm64 wheel on the CI, so we skip these steps and simply test that the wheel builds
# - name: "Install wheel and run tests"
# run: |
# pip3 install ./dist/*.whl
# python3 -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
- name: "Upload artifact test"
uses: actions/upload-artifact@v3
with:
name: bdkpython-macos-arm64-${{ matrix.python }}
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-macos-x86_64-wheels:
name: "Build and test macOS x86_64 wheels"
runs-on: macos-13
defaults:
run:
working-directory: bdk-python
strategy:
matrix:
python:
- "3.8"
- "3.9"
- "3.10"
steps:
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-macos-x86_64.sh
- name: "Build wheel"
# Specifying the plat-name argument is necessary to build a wheel with the correct name,
# see issue #350 for more information
run: python3 setup.py bdist_wheel --plat-name macosx_11_0_x86_64 --verbose
- name: "Install wheel"
run: pip3 install ./dist/*.whl
- name: "Run tests"
run: python3 -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose
- name: "Upload artifact test"
uses: actions/upload-artifact@v3
with:
name: bdkpython-macos-x86_64-${{ matrix.python }}
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-windows-wheels:
name: "Build and test Windows wheels"
runs-on: windows-2022
defaults:
run:
working-directory: bdk-python
strategy:
matrix:
python:
- "3.8"
- "3.9"
- "3.10"
steps:
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- name: "Install Python"
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}
- name: "Generate bdk.py and binaries"
run: bash ./scripts/generate-windows.sh
- name: "Build wheel"
run: python setup.py bdist_wheel --verbose
- name: "Upload artifact test"
uses: actions/upload-artifact@v3
with:
name: bdkpython-windows-${{ matrix.python }}
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
- name: "Install dependencies"
run: Get-ChildItem 'D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl' | ForEach-Object {pip install $_.FullName}
shell: powershell
- name: "Run tests"
run: python -m unittest discover --start "./tests/" --pattern "test_offline_*.py" --verbose

View File

@@ -1,5 +1,6 @@
name: Test Swift
on:
workflow_dispatch:
push:
paths:
- "bdk-ffi/**"
@@ -11,41 +12,15 @@ on:
jobs:
build:
name: "Build and test"
runs-on: macos-12
steps:
- name: Checkout branch
uses: actions/checkout@v2
- name: "Checkout"
uses: actions/checkout@v3
- name: Install Rust targets
run: |
rustup install nightly-x86_64-apple-darwin
rustup component add rust-src --toolchain nightly-x86_64-apple-darwin
rustup target add aarch64-apple-darwin x86_64-apple-darwin
- name: "Build Swift package"
run: bash ./bdk-swift/build-local-swift.sh
- name: Run bdk-ffi-bindgen
working-directory: bdk-ffi
run: cargo run --package bdk-ffi-bindgen -- --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit
- name: Build bdk-ffi for x86_64-apple-darwin
run: cargo build --profile release-smaller --target x86_64-apple-darwin
- name: Build bdk-ffi for aarch64-apple-darwin
run: cargo build --profile release-smaller --target aarch64-apple-darwin
- name: Create lipo-ios-sim and lipo-macos
run: |
mkdir -p target/lipo-macos/release-smaller
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
- name: Create bdkFFI.xcframework
- name: "Run Swift tests"
working-directory: bdk-swift
run: |
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
cp ../target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
rm Sources/BitcoinDevKit/bdkFFI.h
rm Sources/BitcoinDevkit/bdkFFI.modulemap
- name: Run Swift tests
working-directory: bdk-swift
run: swift test
run: swift test --skip LiveWalletTests --skip LiveTxBuilderTests

2
.gitignore vendored
View File

@@ -18,7 +18,7 @@ bdk.kt
# Swift related
/.build
/.swiftpm
.swiftpm
/Packages
/*.xcodeproj
xcuserdata/

View File

@@ -1,13 +1,134 @@
# Changelog
All notable changes to this project prior to release **0.9.0** are documented in this file. Future
changelog information can be found in each release's git tag and can be viewed with `git tag -ln100 "v*"`.
Changelog info is also documented on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases)
page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
Changelog information can also be found in each release's git tag (which can be viewed with `git tag -ln100 "v*"`), as well as on the [GitHub releases](https://github.com/bitcoindevkit/bdk-ffi/releases) page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [0.30.0]
This release has a new API and a few internal optimizations and refactorings.
- APIs Added
- Add BIP-86 descriptor templates [#388]
[#388]: https://github.com/bitcoindevkit/bdk-ffi/pull/388
## [0.29.0]
This release has a number of new APIs, and adds support for Windows in bdk-jvm.
Changelog
- Add support for Windows in bdk-jvm [#336]
- Add support for older version of Linux distros in bdk-jvm [#345]
- APIs added
- Expose `is_mine()` method on the `Wallet` type [#355]
- Expose `to_bytes()` method on the `Script` type [#369]
[#336]: https://github.com/bitcoindevkit/bdk-ffi/pull/336
[#345]: https://github.com/bitcoindevkit/bdk-ffi/pull/345
[#355]: https://github.com/bitcoindevkit/bdk-ffi/pull/355
[#369]: https://github.com/bitcoindevkit/bdk-ffi/pull/369
## [v0.28.0]
- Update BDK to version 0.28.0 [#341]
- Drop support of pypy releases of Python libraries [#351]
- Drop support for Python 3.6 and 3.7 [#351]
- Drop support for very old Linux versions that do not support the manylinux_2_17_x86_64 platform tag [#351]
- APIs changed:
- Expose Address payload and network properties. [#325]
- Add SignOptions to Wallet.sign() params. [#326]
- address field on `AddressInfo` type is now of type `Address` [#333]
- new PartiallySignedTransaction.json_serialize() function to get JSON serialized value of all PSBT fields. [#334]
- Add from_script constructor to `Address` type [#337]
[#325]: https://github.com/bitcoindevkit/bdk-ffi/pull/325
[#326]: https://github.com/bitcoindevkit/bdk-ffi/pull/326
[#333]: https://github.com/bitcoindevkit/bdk-ffi/pull/333
[#334]: https://github.com/bitcoindevkit/bdk-ffi/pull/334
[#337]: https://github.com/bitcoindevkit/bdk-ffi/pull/337
[#341]: https://github.com/bitcoindevkit/bdk-ffi/pull/341
[#351]: https://github.com/bitcoindevkit/bdk-ffi/pull/351
## [v0.27.1]
- Update BDK to version 0.27.1 [#312]
- APIs changed
- `PartiallySignedTransaction.extract_tx()` returns a `Transaction` instead of the transaction bytes. [#296]
- `Blockchain.broadcast()` takes a `Transaction` instead of a `PartiallySignedTransaction`. [#296]
- APIs added
- New `Transaction` structure that can be created from or serialized to consensus encoded bytes. [#296]
- Add Wallet.get_internal_address() API [#304]
- Add `AddressIndex::Peek(index)` and `AddressIndex::Reset(index)` APIs [#305]
[#296]: https://github.com/bitcoindevkit/bdk-ffi/pull/296
[#304]: https://github.com/bitcoindevkit/bdk-ffi/pull/304
[#305]: https://github.com/bitcoindevkit/bdk-ffi/pull/305
[#312]: https://github.com/bitcoindevkit/bdk-ffi/pull/312
## [v0.26.0]
- Update BDK to version 0.26.0 [#288]
- APIs changed
- The descriptor and change_descriptor arguments on the wallet constructor now take a `Descriptor` instead of a `String`. [#260]
- TxBuilder.drain_to() argument is now `Script` instead of address `String`. [#279]
- APIs added
- Added RpcConfig, BlockchainConfig::Rpc, and Auth [#125]
- Added Descriptor type in [#260] with the following methods:
- Default constructor requires a descriptor in String format and a Network
- new_bip44 constructor returns a Descriptor with structure pkh(key/44'/{0,1}'/0'/{0,1}/*)
- new_bip44_public constructor returns a Descriptor with structure pkh(key/{0,1}/*)
- new_bip49 constructor returns a Descriptor with structure sh(wpkh(key/49'/{0,1}'/0'/{0,1}/*))
- new_bip49_public constructor returns a Descriptor with structure sh(wpkh(key/{0,1}/*))
- new_bip84 constructor returns a Descriptor with structure wpkh(key/84'/{0,1}'/0'/{0,1}/*)
- new_bip84_public constructor returns a Descriptor with structure wpkh(key/{0,1}/*)
- as_string returns the public version of the output descriptor
- as_string_private returns the private version of the output descriptor if available, otherwise return the public version
[#125]: https://github.com/bitcoindevkit/bdk-ffi/pull/125
[#260]: https://github.com/bitcoindevkit/bdk-ffi/pull/260
[#279]: https://github.com/bitcoindevkit/bdk-ffi/pull/279
[#288]: https://github.com/bitcoindevkit/bdk-ffi/pull/288
## [v0.25.0]
- Update BDK to version 0.25.0 [#272]
- APIs Added:
- from_string() constructors now available on DescriptorSecretKey and DescriptorPublicKey [#247]
[#247]: https://github.com/bitcoindevkit/bdk-ffi/pull/247
[#272]: https://github.com/bitcoindevkit/bdk-ffi/pull/272
## [v0.11.0]
- Update BDK to version 0.24.0 [#221]
- APIs changed
- The constructor on the DescriptorSecretKey type now takes a Mnemonic instead of a String.
- APIs added
- Added Mnemonic struct [#219] with following methods:
- new(word_count: WordCount) generates and returns Mnemonic with random entropy
- from_string(mnemonic: String) converts string Mnemonic to Mnemonic type with error
- from_entropy(entropy: Vec<u8>) generates and returns Mnemonic with given entropy
- as_string() view Mnemonic as string
- APIs removed
- generate_mnemonic(word_count: WordCount)
[#219]: https://github.com/bitcoindevkit/bdk-ffi/pull/219
[#221]: https://github.com/bitcoindevkit/bdk-ffi/pull/221
## [v0.10.0]
- Update BDK to version 0.23.0 [#204]
- Update uniffi-rs to latest version 0.21.0 [#216]
- Breaking Changes
- Changed `TxBuilder.finish()` to return new `TxBuilderResult` [#209]
- `TxBuilder.add_recipient()` now takes a `Script` instead of an `Address` [#192]
- `AddressAmount` is now `ScriptAmount` [#192]
- APIs Added
- Added `TxBuilderResult` with PSBT and TransactionDetails [#209]
- `Address` and `Script` structs have been added [#192]
- Add `PartiallySignedBitcoinTransaction.extract_tx()` function [#192]
- Add `secret_bytes()` method on the `DescriptorSecretKey` [#199]
- Add `PartiallySignedBitcoinTransaction.combine()` method [#200]
[#192]: https://github.com/bitcoindevkit/bdk-ffi/pull/192
[#199]: https://github.com/bitcoindevkit/bdk-ffi/pull/199
[#200]: https://github.com/bitcoindevkit/bdk-ffi/pull/200
[#204]: https://github.com/bitcoindevkit/bdk-ffi/pull/204
[#209]: https://github.com/bitcoindevkit/bdk-ffi/pull/209
[#216]: https://github.com/bitcoindevkit/bdk-ffi/pull/216
## [v0.9.0]
- Breaking Changes
@@ -22,7 +143,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- APIs Added [#154]
- `generate_mnemonic()`, returns string mnemonic
- `interface DescriptorSecretKey`
- `new(Network, string_mnenoinc, password)`, contructs DescriptorSecretKey
- `new(Network, string_mnenoinc, password)`, constructs DescriptorSecretKey
- `derive(DerivationPath)`, derives and returns child DescriptorSecretKey
- `extend(DerivationPath)`, extends and returns DescriptorSecretKey
- `as_public()`, returns DescriptorSecretKey as DescriptorPublicKey
@@ -102,9 +223,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
[BIP 0174]:https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki#encoding
## [v0.2.0]
[unreleased]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.9.0...HEAD
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
[v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0
[v0.28.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.27.1...v0.28.0
[v0.27.1]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.26.0...v0.27.1
[v0.26.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.25.0...v0.26.0
[v0.25.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.11.0...v0.25.0
[v0.11.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.10.0...v0.11.0
[v0.10.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.9.0...v0.10.0
[v0.9.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.8.0...v0.9.0
[v0.8.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.7.0...v0.8.0
[v0.7.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.6.0...v0.7.0

View File

@@ -1,12 +0,0 @@
[workspace]
members = ["bdk-ffi", "bdk-ffi-bindgen"]
default-members = ["bdk-ffi", "bdk-ffi-bindgen"]
exclude = ["api-docs", "bdk-android", "bdk-jvm", "bdk-python", "bdk-swift"]
[profile.release-smaller]
inherits = "release"
opt-level = 'z' # Optimize for size.
lto = true # Enable Link Time Optimization
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
panic = 'abort' # Abort on panic
strip = true # Strip symbols from binary*

View File

@@ -7,9 +7,12 @@
<a href="https://discord.gg/d7NkDKm"><img alt="Chat on Discord" src="https://img.shields.io/discord/753336465005608961?logo=discord"></a>
</p>
## 🚨 Warning 🚨
The `master` branch of this repository is being migrated to the [bdk 1.0 API](https://github.com/bitcoindevkit/bdk) and is incomplete. For production-ready libraries, use the [`0.30.X`](https://github.com/bitcoindevkit/bdk-ffi/tree/release/0.30) releases.
## Readme
The workspace in this repository creates the `libbdkffi` multi-language library for the Rust-based
[bdk] library from the [Bitcoin Dev Kit] project. The `bdk-ffi-bindgen` package builds a tool for
generating the actual language binding code used to access the `libbdkffi` library.
[bdk] library from the [Bitcoin Dev Kit] project.
Each supported language and the platform(s) it's packaged for has its own directory. The Rust code in this project is in the bdk-ffi directory and is a wrapper around the [bdk] library to expose its APIs in a uniform way using the [mozilla/uniffi-rs] bindings generator for each supported target language.
@@ -23,12 +26,9 @@ The below directories (a separate repository in the case of bdk-swift) include i
| Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | |
| Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | |
## Language bindings generator tool
Use the `bdk-ffi-bindgen` tool to generate language binding code for the above supported languages.
To run `bdk-ffi-bindgen` and see the available options use the command:
```shell
cargo run -p bdk-ffi-bindgen -- --help
```
## Minimum Supported Rust Version (MSRV)
This library should compile with any combination of features with Rust 1.73.0.
## Contributing
@@ -57,8 +57,8 @@ See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
repositories {
mavenCentral()
}
dependencies {
implementation("org.bitcoindevkit:bdk-android:<version>")
dependencies {
implementation("org.bitcoindevkit:bdk-android:<version>")
}
```
@@ -68,8 +68,8 @@ dependencies {
repositories {
mavenCentral()
}
dependencies {
implementation("org.bitcoindevkit:bdk-jvm:<version>")
dependencies {
implementation("org.bitcoindevkit:bdk-jvm:<version>")
}
```

View File

@@ -1,6 +0,0 @@
# API documentation
The Bitcoin Dev Kit language bindings make use of the [uniffi-rs](https://github.com/mozilla/uniffi-rs) library to produce their bindings. While efforts are ongoing to allow inline documentation on the Rust side to be ported to the bindings code, this is not currently possible.
This directory contains our temporary solution to this problem. A set of files mimicking the bindings libraries in their function signatures, but without any implementation. This allows for documentation build tools to produce API docs similarly to how we would like them to be if they could be inlined.
You can find the resulting API documentation websites in the ["API Reference" section of the sidebar](https://bitcoindevkit.org/getting-started/) under the "Docs" tab on the bitcoindevkit official website.

View File

@@ -1,4 +0,0 @@
# Module bdk-android
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Android.
# Package org.bitcoindevkit

View File

@@ -1,4 +0,0 @@
# Module bdk-jvm
The [bitcoindevkit](https://bitcoindevkit.org/) language bindings library for Kotlin and Java on the JVM.
# Package org.bitcoindevkit

View File

@@ -1,46 +0,0 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.7.10"
// API docs
id("org.jetbrains.dokka") version "1.7.10"
}
repositories {
mavenCentral()
}
dependencies {
testImplementation(kotlin("test"))
}
tasks.test {
useJUnitPlatform()
}
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}
// tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
// dokkaSourceSets {
// named("main") {
// moduleName.set("bdk-android")
// moduleVersion.set("0.11.0")
// includes.from("Module1.md")
// samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
// }
// }
// }
tasks.withType<org.jetbrains.dokka.gradle.DokkaTask>().configureEach {
dokkaSourceSets {
named("main") {
moduleName.set("bdk-jvm")
moduleVersion.set("0.11.0")
includes.from("Module2.md")
samples.from("src/test/kotlin/org/bitcoindevkit/Samples.kt")
}
}
}

View File

@@ -1,8 +0,0 @@
./gradlew dokkaHtml
cd build/dokka/html
git init .
git add .
git switch --create gh-pages
git commit -m "Deploy"
git remote add origin git@github.com:bitcoindevkit/bdk-kotlin.git
git push --set-upstream origin gh-pages --force

View File

@@ -1 +0,0 @@
kotlin.code.style=official

Binary file not shown.

View File

@@ -1,5 +0,0 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@@ -1,234 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@@ -1 +0,0 @@
rootProject.name = "BDK Android and BDK JVM API Docs"

View File

@@ -1,750 +0,0 @@
package org.bitcoindevkit
/**
* The cryptocurrency to act on.
*
* @sample org.bitcoindevkit.networkSample
*/
enum class Network {
/** Bitcoin's mainnet. */
BITCOIN,
/** Bitcoins testnet. */
TESTNET,
/** Bitcoins signet. */
SIGNET,
/** Bitcoins 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 wallets external descriptor.
*
* If youre unsure which one to use, use `AddressIndex.New`.
*
* @sample org.bitcoindevkit.addressIndexSample
*/
sealed class AddressIndex {
/** Return a new address after incrementing the current descriptor index. */
object New: AddressIndex(),
/**
* 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.
*/
object LastUnused: AddressIndex(),
/**
* Return the address for a specific descriptor index. Does not change the current descriptor
* index used by [AddressIndex.New] and [AddressIndex.LastUsed].
* Use with caution, if an index is given that is less than the current descriptor index
* then the returned address may have already been used.
*/
data class Peek(val index: UInt): AddressIndex(),
/**
* Return the address for a specific descriptor index and reset the current descriptor index
* used by [AddressIndex.New] and [AddressIndex.LastUsed] to this value.
* Use with caution, if an index is given that is less than the current descriptor index
* then the returned address and subsequent addresses returned by calls to [AddressIndex.New]
* and [AddressIndex.LastUsed] may have already been used. Also if the index is reset to a
* value earlier than the [Blockchain] stopGap (default is 20) then a
* larger stopGap should be used to monitor for all possibly used addresses.
*/
data class Reset(val index: UInt): AddressIndex()
}
/**
* Balance differentiated in various categories.
*
* @property immature All coinbase outputs not yet matured.
* @property trustedPending Unconfirmed UTXOs generated by a wallet tx.
* @property untrustedPending Unconfirmed UTXOs received from an external wallet.
* @property confirmed Confirmed and immediately spendable balance.
* @property spendable The sum of trustedPending and confirmed coins.
* @property total The whole balance visible to the wallet.
*
* @sample org.bitcoindevkit.balanceSample
*/
data class Balance (
var immature: ULong,
var trustedPending: ULong,
var untrustedPending: ULong,
var confirmed: ULong,
var spendable: ULong,
var total: ULong
)
/**
* Type that can contain any of the database configurations defined by the library.
*
* @sample org.bitcoindevkit.memoryDatabaseConfigSample
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
*/
sealed class DatabaseConfig {
/** Configuration for an in-memory database. */
object Memory : DatabaseConfig()
/** Configuration for a Sled database. */
data class Sled(val config: SledDbConfiguration) : DatabaseConfig()
/** Configuration for a SQLite database. */
data class Sqlite(val config: SqliteDbConfiguration) : DatabaseConfig()
}
/**
* Configuration type for a SQLite database.
*
* @property path Main directory of the DB.
*
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
*/
data class SqliteDbConfiguration(
var path: String,
)
/**
* Configuration type for a SledDB database.
*
* @property path Main directory of the DB.
* @property treeName Name of the database tree, a separated namespace for the data.
*/
data class SledDbConfiguration(
var path: String,
var treeName: String,
)
/**
* Configuration for an Electrum blockchain.
*
* @property url URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with `ssl://` or `tcp://` and include a port, e.g. `ssl://electrum.blockstream.info:60002`.
* @property socks5 URL of the socks5 proxy server or a Tor service.
* @property retry Request retry count.
* @property timeout Request timeout (seconds).
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
* @property validateDomain Validate the domain when using SSL.
*
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
*/
data class ElectrumConfig(
var url: String,
var socks5: String?,
var retry: UByte,
var timeout: UByte?,
var stopGap: ULong,
var validateDomain: Boolean
)
/**
* Configuration for an Esplora blockchain.
*
* @property baseUrl Base URL of the esplora service, e.g. `https://blockstream.info/api/`.
* @property proxy Optional URL of the proxy to use to make requests to the Esplora server.
* @property concurrency Number of parallel requests sent to the esplora service (default: 4).
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
* @property timeout Socket timeout.
*
* @sample org.bitcoindevkit.esploraBlockchainConfigSample
*/
data class EsploraConfig(
var baseUrl: String,
var proxy: String?,
var concurrency: UByte?,
var stopGap: ULong,
var timeout: ULong?
)
/**
* Authentication mechanism for RPC connection to full node.
*/
sealed class Auth {
/** No authentication */
object None: Auth()
/** Authentication with username and password, usually [Auth.Cookie] should be preferred */
data class UserPass(val username: String, val password: String): Auth()
/** Authentication with a cookie file */
data class Cookie(val file: String): Auth()
}
/**
* Sync parameters for Bitcoin Core RPC.
*
* In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
* `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
* how the `importdescriptors` RPC calls are to be made.
*
* @property startScriptCount The minimum number of scripts to scan for on initial sync.
* @property startTime Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
* @property forceStartTime Forces every sync to use `start_time` as import timestamp.
* @property pollRateSec RPC poll rate (in seconds) to get state updates.
*/
data class RcpSyncParams(
val startScriptCount: ULong,
val startTime: Ulong,
val forceStartTime: Boolean,
val pollRateSec: ULong,
)
/**
* RpcBlockchain configuration options
*
* @property url The bitcoin node url.
* @property auth The bicoin node authentication mechanism.
* @property network The network we are using (it will be checked the bitcoin node network matches this).
* @property walletName The wallet name in the bitcoin node.
* @property syncParams Sync parameters.
*/
data class RpcConfig(
val url: String,
val auth: Auth,
val network: Network,
val walletName: String,
val syncParams: RcpSyncParams?,
)
/**
* Type that can contain any of the blockchain configurations defined by the library.
*
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
*/
sealed class BlockchainConfig {
/** Electrum client. */
data class Electrum(val config: ElectrumConfig) : BlockchainConfig()
/** Esplora client. */
data class Esplora(val config: EsploraConfig) : BlockchainConfig()
/** Bitcoin Core RPC client. */
data class Rpc(val config: RpcConfig) : BlockchainConfig()
}
/**
* A wallet transaction.
*
* @property fee Fee value (sats) if available. The availability of the fee depends on the backend. Its 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(transaction: Transaction) {}
/** Estimate the fee rate required to confirm a transaction in a given target of blocks. */
fun estimateFee(target: ULong): FeeRate {}
/** Get the current height of the blockchain. */
fun getHeight(): UInt {}
/** Get the block hash of a given block. */
fun getBlockHash(height: UInt): String {}
}
/**
* A bitcoin transaction.
*
* @constructor Build a new Bitcoin Transaction.
*
* @param transactionBytes The transaction bytes, bitcoin consensus encoded.
*/
class Transaction(transactionBytes: List<UByte>) {
/** Return the transaction bytes, bitcoin consensus encoded. */
fun serialize(): List<UByte> {}
}
/**
* A partially signed bitcoin transaction.
*
* @constructor Build a new Partially Signed Bitcoin Transaction.
*
* @param psbtBase64 The PSBT in base64 format.
*/
class PartiallySignedTransaction(psbtBase64: String) {
/** Return the PSBT in string format, using a base64 encoding. */
fun serialize(): String {}
/** Get the txid of the PSBT. */
fun txid(): String {}
/** Extract the transaction. */
fun extractTx(): Transaction {}
/**
* 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: PartiallySignedTransaction): PartiallySignedTransaction
}
/**
* A reference to a transaction output.
*
* @property txid The referenced transactions txid.
* @property vout The index of the referenced output in its transactions vout.
*/
data class OutPoint (
var txid: String,
var vout: UInt
)
/**
* A transaction output, which defines new coins to be created from old ones.
*
* @property value The value of the output, in satoshis.
* @property address The address of the output.
*/
data class TxOut (
var value: ULong,
var address: String
)
/**
* An unspent output owned by a [Wallet].
*
* @property outpoint Reference to a transaction output.
* @property txout Transaction output.
* @property keychain Type of keychain.
* @property isSpent Whether this UTXO is spent or not.
*/
data class LocalUtxo (
var outpoint: OutPoint,
var txout: TxOut,
var keychain: KeychainKind,
var isSpent: Boolean
)
/**
* Types of keychains.
*/
enum class KeychainKind {
/** External. */
EXTERNAL,
/** Internal, usually used for change outputs. */
INTERNAL,
}
/**
* Block height and timestamp of a block.
*
* @property height Confirmation block height.
* @property timestamp Confirmation block timestamp.
*/
data class BlockTime (
var height: UInt,
var timestamp: ULong,
)
/**
* A Bitcoin wallet.
* The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
* 1. Output descriptors from which it can derive addresses.
* 2. A Database where it tracks transactions and utxos related to the descriptors.
* 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
*
* @constructor Create a BDK wallet.
*
* @param descriptor The main (or "external") descriptor.
* @param changeDescriptor? The change (or "internal") descriptor.
* @param network The network to act on.
* @param databaseConfig The database configuration.
*
* @sample org.bitcoindevkit.walletSample
*/
class Wallet(
descriptor: Descriptor,
changeDescriptor: Descriptor?,
network: Network,
databaseConfig: DatabaseConfig,
) {
/**
* Return a derived address using the external descriptor, see [AddressIndex] for available address index
* selection strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end
* with a * character) then the same address will always be returned for any [AddressIndex].
*/
fun getAddress(addressIndex: AddressIndex): AddressInfo {}
/**
* Return 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].
*/
fun getInternalAddress(addressIndex: AddressIndex): AddressInfo {}
/** Return the wallet's balance, across different categories. See [Balance] for the categories. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
fun getBalance(): Balance {}
/** Sign a transaction with all the wallets signers. */
fun sign(psbt: PartiallySignedTransaction): 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. Its 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. Its important to note that the "must-be-spent" utxos
* added with [TxBuilder.addUtxo] have priority over these. See the Rust docs of the two linked methods for more details.
*/
fun unspendable(unspendable: List<OutPoint>): TxBuilder {}
/** Set a custom fee rate. */
fun feeRate(satPerVbyte: Float): TxBuilder {}
/** Set an absolute fee. */
fun feeAbsolute(feeAmount: ULong): TxBuilder {}
/** Spend all the available inputs. This respects filters like [TxBuilder.unspendable] and the change policy. */
fun drainWallet(): TxBuilder {}
/**
* Sets the address to drain excess coins to. Usually, when there are excess coins they are
* sent to a change address generated by the wallet. This option replaces the usual change address
* with an arbitrary ScriptPubKey of your choosing. Just as with a change output, if the
* drain output is not needed (the excess coins are too small) it will not be included in the resulting
* transaction. The only difference is that it is valid to use [drainTo] without setting any ordinary recipients
* with [addRecipient] (but it is perfectly fine to add recipients as well). If you choose not to set any
* recipients, you should either provide the utxos that the transaction should spend via [addUtxos], or set
* [drainWallet] to spend all of them. When bumping the fees of a transaction made with this option,
* you probably want to use [BumpFeeTxBuilder.allowShrinking] to allow this output to be reduced to pay for the extra fees.
*/
fun drainTo(script: Script): TxBuilder {}
/** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
fun enableRbf(): TxBuilder {}
/**
* Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
* contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
* is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
*/
fun enableRbfWithSequence(nsequence: UInt): TxBuilder {}
/** Finish building the transaction. Returns a [TxBuilderResult]. */
fun finish(wallet: Wallet): TxBuilderResult {}
}
/**
* A object holding a ScriptPubKey and an amount.
*
* @property script The ScriptPubKey.
* @property amount The amount.
*/
data class ScriptAmount (
var script: Script,
var amount: ULong
)
/**
* The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
*/
class BumpFeeTxBuilder() {
/**
* Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this scriptPubKey
* in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output
* to shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is
* preserved then it is currently not guaranteed to be in the same position as it was originally. Returns an error
* if scriptPubkey cant be found among the recipients of the transaction we are bumping.
*/
fun allowShrinking(address: String): BumpFeeTxBuilder {}
/** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
fun enableRbf(): BumpFeeTxBuilder {}
/**
* Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors
* contain an "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence`
* is higher than `0xFFFFFFFD` an error will be thrown, since it would not be a valid nSequence to signal RBF.
*/
fun enableRbfWithSequence(nsequence: UInt): BumpFeeTxBuilder {}
/** Finish building the transaction. Returns a [TxBuilderResult]. */
fun finish(wallet: Wallet): TxBuilderResult {}
}
/**
* A BIP-32 derivation path.
*
* @param path The derivation path. Must start with `m`. Use this type to derive or extend a [DescriptorSecretKey]
* or [DescriptorPublicKey].
*/
class DerivationPath(path: String) {}
/**
* An extended secret key.
*
* @param network The network this DescriptorSecretKey is to be used on.
* @param mnemonic The mnemonic.
* @param password The optional passphrase that can be provided as per BIP-39.
*
* @sample org.bitcoindevkit.descriptorSecretKeyDeriveSample
* @sample org.bitcoindevkit.descriptorSecretKeyExtendSample
*/
class DescriptorSecretKey(network: Network, mnemonic: Mnemonic, password: String?) {
/** Build a DescriptorSecretKey from a String */
fun fromString(secretKey: String): DescriptorSecretKey {}
/** Derive a private descriptor at a given path. */
fun derive(path: DerivationPath): DescriptorSecretKey {}
/** Extend the private descriptor with a custom path. */
fun extend(path: DerivationPath): DescriptorSecretKey {}
/** Return the public version of the descriptor. */
fun asPublic(): DescriptorPublicKey {}
/** Return the raw private key as bytes. */
fun secretBytes(): List<UByte>
/** Return the private descriptor as a string. */
fun asString(): String {}
}
/**
* An extended public key.
*
* @param network The network this DescriptorPublicKey is to be used on.
* @param mnemonic The mnemonic.
* @param password The optional passphrase that can be provided as per BIP-39.
*/
class DescriptorPublicKey(network: Network, mnemonic: String, password: String?) {
/** Build a DescriptorPublicKey from a String */
fun fromString(publicKey: String): DescriptorPublicKey {}
/** Derive a public descriptor at a given path. */
fun derive(path: DerivationPath): DescriptorPublicKey
/** Extend the public descriptor with a custom path. */
fun extend(path: DerivationPath): DescriptorPublicKey
/** Return the public descriptor as a string. */
fun asString(): String
}
/**
* A output descriptor.
*
* @param descriptor The descriptor in string format.
* @param network The network this descriptor is to be used on.
*
* @sample org.bitcoindevkit.descriptorTemplates1
* @sample org.bitcoindevkit.descriptorTemplates2
*/
class Descriptor(descriptor: String, network: Network) {
/**
* BIP44 template. Expands to pkh(key/44'/{0,1}'/0'/{0,1}/\*)
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
*/
fun newBip44(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
/**
* BIP44 public template. Expands to pkh(key/{0,1}/\*)
* This assumes that the key used has already been derived with m/44'/0'/0' for Mainnet or m/44'/1'/0' for Testnet.
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
*/
fun newBip44Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
/**
* BIP49 template. Expands to sh(wpkh(key/49'/{0,1}'/0'/{0,1}/\*))
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
*/
fun newBip49(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
/**
* BIP49 public template. Expands to sh(wpkh(key/{0,1}/\*))
* This assumes that the key used has already been derived with m/49'/0'/0' for Mainnet or m/49'/1'/0' for Testnet.
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
*/
fun newBip49Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
/**
* BIP84 template. Expands to wpkh(key/84'/{0,1}'/0'/{0,1}/\*)
* Since there are hardened derivation steps, this template requires a private derivable key (generally a xprv/tprv).
*/
fun newBip84(secretKey: DescriptorSecretKey, keychain: KeychainKind, network: Network) {}
/**
* BIP84 public template. Expands to wpkh(key/{0,1}/\*)
* This assumes that the key used has already been derived with m/84'/0'/0' for Mainnet or m/84'/1'/0' for Testnet.
* This template requires the parent fingerprint to populate correctly the metadata of PSBTs.
*/
fun newBip84Public(publicKey: DescriptorPublicKey, fingerprint: String, keychain: KeychainKind, network: Network) {}
/** Return the public version of the output descriptor. */
fun asString(): String {}
/** Return the private version of the output descriptor if available, otherwise return the public version. */
fun asStringPrivate(): String {}
}
/**
* An enum describing entropy length (aka word count) in the mnemonic.
*/
enum class WordCount {
/** 12 words mnemonic (128 bits entropy). */
WORDS12,
/** 15 words mnemonic (160 bits entropy). */
WORDS15,
/** 18 words mnemonic (192 bits entropy). */
WORDS18,
/** 21 words mnemonic (224 bits entropy). */
WORDS21,
/** 24 words mnemonic (256 bits entropy). */
WORDS24,
}
/**
* The value returned from calling the `.finish()` method on the [TxBuilder] or [BumpFeeTxBuilder].
*
* @property psbt The PSBT
* @property transactionDetails The transaction details.
*
* @sample org.bitcoindevkit.txBuilderResultSample1
* @sample org.bitcoindevkit.txBuilderResultSample2
*/
data class TxBuilderResult (
var psbt: PartiallySignedTransaction,
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
}

View File

@@ -1,281 +0,0 @@
package org.bitcoindevkit
fun networkSample() {
val wallet = Wallet(
descriptor = descriptor,
changeDescriptor = changeDescriptor,
network = Network.TESTNET,
databaseConfig = DatabaseConfig.Memory
)
}
fun balanceSample() {
object LogProgress : Progress {
override fun update(progress: Float, message: String?) {}
}
val memoryDatabaseConfig = DatabaseConfig.Memory
private val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig(
"ssl://electrum.blockstream.info:60002",
null,
5u,
null,
200u
)
)
val wallet = Wallet(descriptor, null, Network.TESTNET, memoryDatabaseConfig)
val blockchain = Blockchain(blockchainConfig)
wallet.sync(blockchain, LogProgress)
val balance: Balance = wallet.getBalance()
println("Total wallet balance is ${balance.total}")
}
fun electrumBlockchainConfigSample() {
val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig(
url = "ssl://electrum.blockstream.info:60002",
socks5 = null,
retry = 5u,
timeout = null,
stopGap = 200u
)
)
}
fun esploraBlockchainConfigSample() {
val esploraURL: String = "http://10.0.2.2:3002"
val esploraConfig: EsploraConfig = EsploraConfig(
baseUrl = esploraURL,
proxy = null,
concurrency = 4u,
stopGap = 20UL,
timeout = null
)
val blockchainConfig = BlockchainConfig.Esplora(config = esploraConfig)
}
fun memoryDatabaseConfigSample() {
val memoryDatabaseConfig = DatabaseConfig.Memory
}
fun sqliteDatabaseConfigSample() {
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
}
fun addressIndexSample() {
val wallet: Wallet = Wallet(
descriptor = descriptor,
changeDescriptor = changeDescriptor,
network = Network.TESTNET,
databaseConfig = DatabaseConfig.Memory
)
fun peekAddress100(): AddressInfo {
return wallet.getAddress(AddressIndex.Peek(100u))
}
}
fun addressInfoSample() {
val wallet: Wallet = Wallet(
descriptor = descriptor,
changeDescriptor = changeDescriptor,
network = Network.TESTNET,
databaseConfig = DatabaseConfig.Memory
)
val newAddress: AddressInfo = wallet.getAddress(AddressIndex.New)
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)
val feeRate: FeeRate = blockchain.estimateFee(3u)
val (psbt, txDetails) = TxBuilder()
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
.feeRate(feeRate.asSatPerVb())
.finish(wallet)
wallet.sign(psbt)
blockchain.broadcast(psbt)
}
fun txBuilderResultSample1() {
val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
// TxBuilderResult is a data class, which means you can use destructuring declarations on it to
// open it up into its component parts
val (psbt, txDetails) = TxBuilder()
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
.feeRate(1.2f)
.finish(wallet)
println("Txid is ${txDetails.txid}")
wallet.sign(psbt)
}
fun txBuilderResultSample2() {
val faucetAddress = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
val txBuilderResult: TxBuilderResult = TxBuilder()
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
.feeRate(1.2f)
.finish(wallet)
val psbt = txBuilderResult.psbt
val txDetails = txBuilderResult.transactionDetails
println("Txid is ${txDetails.txid}")
wallet.sign(psbt)
}
fun descriptorSecretKeyExtendSample() {
// The `DescriptorSecretKey.extend()` method allows you to extend a key to any given path.
// val mnemonic: String = generateMnemonic(WordCount.WORDS12)
val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
// the initial DescriptorSecretKey will always be at the "master" node,
// i.e. the derivation path is empty
val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
network = Network.TESTNET,
mnemonic = mnemonic,
password = ""
)
println(bip32RootKey.asString())
// tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/*
// the derive method will also automatically apply the wildcard (*) to your path,
// i.e the following will generate the typical testnet BIP84 external wallet path
// m/84h/1h/0h/0/*
val bip84ExternalPath: DerivationPath = DerivationPath("m/84h/1h/0h/0")
val externalExtendedKey: DescriptorSecretKey = bip32RootKey.extend(bip84ExternalPath).asString()
println(externalExtendedKey)
// tprv8ZgxMBicQKsPfM8Trx2apvdEkmxbJkYY3ZsmcgKb2bfnLNcBhtCstqQTeFesMRLEJXpjGDinAUJUHprXMwph8dQBdS1HAoxEis8Knimxovf/84'/1'/0'/0/*
// to create the descriptor you'll need to use this extended key in a descriptor function,
// i.e. wpkh(), tr(), etc.
val externalDescriptor = "wpkh($externalExtendedKey)"
}
fun descriptorSecretKeyDeriveSample() {
// The DescriptorSecretKey.derive() method allows you to derive an extended key for a given
// node in the derivation tree (for example to create an xpub for a particular account)
val mnemonic: Mnemonic = Mnemonic("scene change clap smart together mind wheel knee clip normal trial unusual")
val bip32RootKey: DescriptorSecretKey = DescriptorSecretKey(
network = Network.TESTNET,
mnemonic = mnemonic,
password = ""
)
val bip84Account0: DerivationPath = DerivationPath("m/84h/1h/0h")
val xpubAccount0: DescriptorSecretKey = bip32RootKey.derive(bip84Account0)
println(xpubAccount0.asString())
// [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/*
val internalPath: DerivationPath = DerivationPath("m/0")
val externalExtendedKey = xpubAccount0.extend(internalPath).asString()
println(externalExtendedKey)
// [5512949b/84'/1'/0']tprv8ghw3FWfWTeLCEXcr8f8Q8Lz4QPCELYv3jhBXjAm7XagA6R5hreeWLTJeLBfMj7Ni6Q3PdV1o8NbvNBHE59W97EkRJSU4JkvTQjaNUmQubE/0/*
// to create the descriptor you'll need to use this extended key in a descriptor function,
// i.e. wpkh(), tr(), etc.
val externalDescriptor = "wpkh($externalExtendedKey)"
}
fun createTransaction() {
val wallet = BdkWallet(
descriptor = externalDescriptor,
changeDescriptor = internalDescriptor,
network = Network.TESTNET,
databaseConfig = memoryDatabaseConfig,
)
val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig(
"ssl://electrum.blockstream.info:60002",
null,
5u,
null,
200u
)
)
val paymentAddress: Address = Address("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
val (psbt, txDetails) = TxBuilder()
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
.feeRate(1.2f)
.finish(wallet)
wallet.sign(psbt)
blockchain.broadcast(psbt)
}
fun walletSample() {
val externalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/0/*)"
val internalDescriptor = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEfVULesmhEfZYyBXdE/84h/1h/0h/1/*)"
val sqliteDatabaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration("bdk-sqlite"))
val wallet = Wallet(
descriptor = externalDescriptor,
changeDescriptor = internalDescriptor,
network = Network.TESTNET,
databaseConfig = sqliteDatabaseConfig,
)
}
fun mnemonicSample() {
val mnemonic0: Mnemonic = Mnemonic(WordCount.WORDS12)
val mnemonic1: Mnemonic = Mnemonic.fromString("scene change clap smart together mind wheel knee clip normal trial unusual")
val entropy: List<UByte> = listOf<UByte>(0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u)
val mnemonic2: Mnemonic = Mnemonic.fromEntropy(entropy)
println(mnemonic0.asString(), mnemonic1.asString(), mnemonic2.asString())
}
fun descriptorTemplates1() {
// Bip84 private descriptor
val recoveryPhrase: String = "scene change clap smart together mind wheel knee clip normal trial unusual"
val mnemonic = Mnemonic.fromString(recoveryPhrase)
val bip32ExtendedRootKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
val bip84ExternalDescriptor: Descriptor = Descriptor.newBip84(bip32ExtendedRootKey, KeychainKind.EXTERNAL, Network.TESTNET)
}
fun descriptorTemplates2() {
// Bip49 public descriptor
// assume we already have the xpub for m/49'/0'/1' created on an external device that only shared the xpub with the wallet
// using the template requires the parent fingerprint to populate correctly the metadata of PSBTs, which the external device would provide
// the xpub (tpub for testnet): tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR
// the fingerprint: d1d04177
val descriptorPublicKey: DescriptorPublicKey = DescriptorPublicKey.fromString("tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR")
val bip49PublicDescriptor: Descriptor = Descriptor.newBip49Public(
publicKey = descriptorPublicKey,
fingerprint = "d1d04177",
keychain = KeychainKind.EXTERNAL,
network = Network.TESTNET,
)
println(bip49PublicDescriptor.asString()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
println(bip49PublicDescriptor.asStringPrivate()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
// Creating it starting from the full xprv derived from a mnemonic will give you the same public descriptor
val mnemonic = Mnemonic.fromString("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect")
val bip32ExtendedRootKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
val bip49PrivateDescriptor: Descriptor = Descriptor.newBip49(bip32ExtendedRootKey, KeychainKind.EXTERNAL, Network.TESTNET)
println(bip49PrivateDescriptor.asString()) // sh(wpkh([d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/0/*))#a7lxzefl
}

View File

@@ -8,19 +8,19 @@ repositories {
mavenCentral()
}
dependencies {
implementation("org.bitcoindevkit:bdk-android:<version>")
dependencies {
implementation("org.bitcoindevkit:bdk-android:<version>")
}
```
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
You may then import and use the `org.bitcoindevkit` library in your Kotlin code like so. Note that this example is for the `0.30.0` release. For examples of the 1.0 API in the alpha releases, take a look at the tests [here](https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit).
```kotlin
import org.bitcoindevkit.*
// ...
val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
val databaseConfig = DatabaseConfig.Memory
@@ -38,12 +38,13 @@ repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
implementation("org.bitcoindevkit:bdk-android:<version-SNAPSHOT>")
dependencies {
implementation("org.bitcoindevkit:bdk-android:<version-SNAPSHOT>")
}
```
### Example Projects
* [bdk-kotlin-example-wallet](https://github.com/bitcoindevkit/bdk-kotlin-example-wallet)
* [Devkit Wallet](https://github.com/thunderbiscuit/devkit-wallet)
* [Padawan Wallet](https://github.com/thunderbiscuit/padawan-wallet)
@@ -54,19 +55,23 @@ _Note that Kotlin version `1.6.10` or later is required to build the library._
```shell
git clone https://github.com/bitcoindevkit/bdk-ffi
```
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
3. If building on macOS install required intel and m1 jvm targets
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
3. Install Rust (note that we are currently building using Rust 1.73.0):
```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default 1.73.0
```
4. Install required targets
```sh
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
```
```sh
rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
```
5. Install Android SDK and Build-Tools for API level 30+
6. Setup `$ANDROID_SDK_ROOT` and `$ANDROID_NDK_ROOT` path variables (which are required by the
build tool), for example (NDK major version 21 is required):
```shell
export ANDROID_SDK_ROOT=~/Android/Sdk
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/21.<NDK_VERSION>
```
build tool), for example (note that currently, NDK version 25.2.9519653 or above is required):
```shell
export ANDROID_SDK_ROOT=~/Android/Sdk
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
```
7. Build kotlin bindings
```sh
# build Android library

View File

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

View File

@@ -53,7 +53,8 @@ dependencies {
androidTestImplementation("com.github.tony19:logback-android:2.0.0")
androidTestImplementation("androidx.test.ext:junit:1.1.3")
androidTestImplementation("androidx.test.espresso:espresso-core:3.4.0")
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1")
androidTestImplementation("org.jetbrains.kotlin:kotlin-test:1.6.10")
androidTestImplementation("org.jetbrains.kotlin:kotlin-test-junit:1.6.10")
}
afterEvaluate {
@@ -81,14 +82,9 @@ afterEvaluate {
}
developers {
developer {
id.set("notmandatory")
name.set("Steve Myers")
email.set("notmandatory@noreply.github.org")
}
developer {
id.set("artfuldev")
name.set("Sudarsan Balaji")
email.set("sudarsan.balaji@artfuldev.com")
id.set("bdkdevelopers")
name.set("Bitcoin Dev Kit Developers")
email.set("dev@bitcoindevkit.org")
}
}
scm {

View File

@@ -1,81 +0,0 @@
package org.bitcoindevkit
import org.junit.Assert.*
import org.junit.Test
import android.app.Application
import android.content.Context.MODE_PRIVATE
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class AndroidLibTest {
private fun getTestDataDir(): String {
val context = ApplicationProvider.getApplicationContext<Application>()
return context.getDir("bdk-test", MODE_PRIVATE).toString()
}
private fun cleanupTestDataDir(testDataDir: String) {
File(testDataDir).deleteRecursively()
}
class LogProgress : Progress {
private val log: Logger = LoggerFactory.getLogger(AndroidLibTest::class.java)
override fun update(progress: Float, message: String?) {
log.debug("Syncing...")
}
}
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
private val databaseConfig = DatabaseConfig.Memory
private val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig(
"ssl://electrum.blockstream.info:60002",
null,
5u,
null,
100u,
true,
)
)
@Test
fun memoryWalletNewAddress() {
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val address = wallet.getAddress(AddressIndex.New).address
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
}
@Test
fun memoryWalletSyncGetBalance() {
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val blockchain = Blockchain(blockchainConfig)
wallet.sync(blockchain, LogProgress())
val balance: Balance = wallet.getBalance()
assertTrue(balance.total > 0u)
}
@Test
fun sqliteWalletSyncGetBalance() {
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val blockchain = Blockchain(blockchainConfig)
wallet.sync(blockchain, LogProgress())
val balance: Balance = wallet.getBalance()
assertTrue(balance.total > 0u)
cleanupTestDataDir(testDataDir)
}
}

View File

@@ -0,0 +1,30 @@
package org.bitcoindevkit
import org.junit.Test
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class)
class LiveTxBuilderTest {
@Test
fun testTxBuilder() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
assert(wallet.getBalance().total() > 0uL)
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(2.0f)
.finish(wallet)
println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
}
}

View File

@@ -0,0 +1,58 @@
package org.bitcoindevkit
import org.junit.Test
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
import kotlin.test.assertTrue
@RunWith(AndroidJUnit4::class)
class LiveWalletTest {
@Test
fun testSyncedBalance() {
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient: EsploraClient = EsploraClient("https://mempool.space/testnet/api")
// val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
val balance: Balance = wallet.getBalance()
println("Balance: $balance")
assert(wallet.getBalance().total() > 0uL)
}
@Test
fun testBroadcastTransaction() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
println("New address: ${wallet.getAddress(AddressIndex.New).address}")
assert(wallet.getBalance().total() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(4.0f)
.finish(wallet)
println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
val walletDidSign = wallet.sign(psbt)
assertTrue(walletDidSign)
val tx: Transaction = psbt.extractTx()
println("Txid is: ${tx.txid()}")
esploraClient.broadcast(tx)
}
}

View File

@@ -0,0 +1,22 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
import kotlin.test.assertEquals
@RunWith(AndroidJUnit4::class)
class OfflineDescriptorTest {
@Test
fun testDescriptorBip86() {
val mnemonic: Mnemonic = Mnemonic.fromString("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
assertEquals(
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
actual = descriptor.asString()
)
}
}

View File

@@ -0,0 +1,56 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class OfflineWalletTest {
@Test
fun testDescriptorBip86() {
val mnemonic: Mnemonic = Mnemonic(WordCount.WORDS12)
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
}
@Test
fun testNewAddress() {
val descriptor: Descriptor = Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Network.TESTNET
)
val wallet: Wallet = Wallet.newNoPersist(
descriptor,
null,
Network.TESTNET
)
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
assertEquals(
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
actual = addressInfo.address.asString()
)
}
@Test
fun testBalance() {
val descriptor: Descriptor = Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Network.TESTNET
)
val wallet: Wallet = Wallet.newNoPersist(
descriptor,
null,
Network.TESTNET
)
assertEquals(
expected = 0uL,
actual = wallet.getBalance().total()
)
}
}

View File

@@ -21,8 +21,7 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
val buildAndroidAarch64Binary by tasks.register<Exec>("buildAndroidAarch64Binary") {
workingDir("${projectDir}/../../bdk-ffi")
val cargoArgs: MutableList<String> =
mutableListOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-linux-android")
executable("cargo")
args(cargoArgs)
@@ -36,10 +35,9 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
environment(
// add build toolchain to PATH
Pair("PATH",
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_API__=21"),
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
Pair("AR", "llvm-ar"),
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
Pair("CC", "aarch64-linux-android21-clang")
)
@@ -53,8 +51,7 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
val buildAndroidX86_64Binary by tasks.register<Exec>("buildAndroidX86_64Binary") {
workingDir("${project.projectDir}/../../bdk-ffi")
val cargoArgs: MutableList<String> =
mutableListOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-linux-android")
executable("cargo")
args(cargoArgs)
@@ -68,10 +65,9 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
environment(
// add build toolchain to PATH
Pair("PATH",
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_API__=21"),
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
Pair("AR", "llvm-ar"),
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
Pair("CC", "x86_64-linux-android21-clang")
)
@@ -85,8 +81,7 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
val buildAndroidArmv7Binary by tasks.register<Exec>("buildAndroidArmv7Binary") {
workingDir("${project.projectDir}/../../bdk-ffi")
val cargoArgs: MutableList<String> =
mutableListOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "armv7-linux-androideabi")
executable("cargo")
args(cargoArgs)
@@ -100,12 +95,10 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
environment(
// add build toolchain to PATH
Pair("PATH",
"${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_API__=21"),
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER",
"armv7a-linux-androideabi21-clang"),
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=21"),
Pair("AR", "llvm-ar"),
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi21-clang"),
Pair("CC", "armv7a-linux-androideabi21-clang")
)
@@ -124,15 +117,15 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
into("${project.projectDir}/../lib/src/main/jniLibs/")
into("arm64-v8a") {
from("${project.projectDir}/../../target/aarch64-linux-android/release-smaller/libbdkffi.so")
from("${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so")
}
into("x86_64") {
from("${project.projectDir}/../../target/x86_64-linux-android/release-smaller/libbdkffi.so")
from("${project.projectDir}/../../bdk-ffi/target/x86_64-linux-android/release-smaller/libbdkffi.so")
}
into("armeabi-v7a") {
from("${project.projectDir}/../../target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
from("${project.projectDir}/../../bdk-ffi/target/armv7-linux-androideabi/release-smaller/libbdkffi.so")
}
doLast {
@@ -144,18 +137,17 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
val generateAndroidBindings by tasks.register<Exec>("generateAndroidBindings") {
dependsOn(moveNativeAndroidLibs)
// val libraryPath = "${project.projectDir}/../../bdk-ffi/target/aarch64-linux-android/release-smaller/libbdkffi.so"
// workingDir("${project.projectDir}/../../bdk-ffi")
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
// The code above worked for uniffi 0.24.3 using the --library flag
// The code below works for uniffi 0.23.0
workingDir("${project.projectDir}/../../bdk-ffi")
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-android/lib/src/main/kotlin", "--no-format")
executable("cargo")
args(
"run",
"--package",
"bdk-ffi-bindgen",
"--",
"--language",
"kotlin",
"--out-dir",
"../bdk-android/lib/src/main/kotlin"
)
args(cargoArgs)
doLast {
println("Android bindings file successfully created")

View File

@@ -1,10 +0,0 @@
[package]
name = "bdk-ffi-bindgen"
version = "0.2.0"
edition = "2021"
[dependencies]
anyhow = "1.0.45" # remove after upgrading to next version of uniffi
structopt = "0.3"
uniffi_bindgen = "0.21.0"
camino = "1.0.9"

View File

@@ -1,138 +0,0 @@
use camino::Utf8Path;
use std::fmt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use structopt::StructOpt;
#[derive(Debug, Eq, PartialEq)]
pub enum Language {
Kotlin,
Python,
Swift,
}
impl fmt::Display for Language {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Language::Kotlin => write!(f, "kotlin"),
Language::Swift => write!(f, "swift"),
Language::Python => write!(f, "python"),
}
}
}
#[derive(Debug)]
pub enum Error {
UnsupportedLanguage,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?}", self)
}
}
impl FromStr for Language {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"kotlin" => Ok(Language::Kotlin),
"python" => Ok(Language::Python),
"swift" => Ok(Language::Swift),
_ => Err(Error::UnsupportedLanguage),
}
}
}
fn generate_bindings(opt: &Opt) -> anyhow::Result<(), anyhow::Error> {
let path: &Utf8Path = Utf8Path::from_path(&opt.udl_file).unwrap();
let out_dir: &Utf8Path = Utf8Path::from_path(&opt.out_dir).unwrap();
uniffi_bindgen::generate_bindings(
path,
None,
vec![opt.language.to_string().as_str()],
Some(out_dir),
None,
false,
)?;
Ok(())
}
fn fixup_python_lib_path(
out_dir: &Path,
lib_name: &Path,
) -> Result<(), Box<dyn std::error::Error>> {
use std::fs;
use std::io::Write;
const LOAD_INDIRECT_DEF: &str = "def loadIndirect():";
let bindings_file = out_dir.join("bdk.py");
let mut data = fs::read_to_string(&bindings_file)?;
let pos = data
.find(LOAD_INDIRECT_DEF)
.unwrap_or_else(|| panic!("loadIndirect not found in `{}`", bindings_file.display()));
let range = pos..pos + LOAD_INDIRECT_DEF.len();
let replacement = format!(
r#"
def loadIndirect():
import glob
return getattr(ctypes.cdll, glob.glob(os.path.join(os.path.dirname(os.path.abspath(__file__)), '{}.*'))[0])
def _loadIndirectOld():"#,
&lib_name.to_str().expect("lib name")
);
data.replace_range(range, &replacement);
let mut file = fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(&bindings_file)?;
file.write_all(data.as_bytes())?;
Ok(())
}
#[derive(Debug, StructOpt)]
#[structopt(
name = "bdk-ffi-bindgen",
about = "A tool to generate bdk-ffi language bindings"
)]
struct Opt {
/// UDL file
#[structopt(env = "BDKFFI_BINDGEN_UDL", short, long, default_value("src/bdk.udl"), parse(try_from_str = PathBuf::from_str))]
udl_file: PathBuf,
/// Language to generate bindings for
#[structopt(env = "BDKFFI_BINDGEN_LANGUAGE", short, long, possible_values(&["kotlin","swift","python"]), parse(try_from_str = Language::from_str))]
language: Language,
/// Output directory to put generated language bindings
#[structopt(env = "BDKFFI_BINDGEN_OUTPUT_DIR", short, long, parse(try_from_str = PathBuf::from_str))]
out_dir: PathBuf,
/// Python fix up lib path
#[structopt(env = "BDKFFI_BINDGEN_PYTHON_FIXUP_PATH", short, long, parse(try_from_str = PathBuf::from_str))]
python_fixup_path: Option<PathBuf>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let opt = Opt::from_args();
println!("Input UDL file is {:?}", opt.udl_file);
println!("Chosen language is {}", opt.language);
println!("Output directory is {:?}", opt.out_dir);
generate_bindings(&opt)?;
if opt.language == Language::Python {
if let Some(path) = opt.python_fixup_path {
println!("Fixing up python lib path, {:?}", &path);
fixup_python_lib_path(&opt.out_dir, &path)?;
}
}
Ok(())
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,47 @@
[package]
name = "bdk-ffi"
version = "0.27.0"
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
version = "1.0.0-alpha.2"
homepage = "https://bitcoindevkit.org"
repository = "https://github.com/bitcoindevkit/bdk"
edition = "2018"
license = "MIT OR Apache-2.0"
[lib]
crate-type = ["staticlib", "cdylib"]
crate-type = ["lib", "staticlib", "cdylib"]
name = "bdkffi"
[[bin]]
name = "uniffi-bindgen"
path = "uniffi-bindgen.rs"
[features]
default = ["uniffi/cli"]
[dependencies]
bdk = { version = "0.27.1", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
bdk = { version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
# TODO 22: The bdk_esplora crate uses esplora_client which uses reqwest for async. By default it uses the system
# openssl library, which is creating problems for cross-compilation. I'd rather use rustls, but it's hidden
# behind a feature flag. We need to look into whether openssl-sys is really required by bdk or if using rustls
# would work just as well. This here is a temporary workaround which removes the async feature on the bdk_esplora crate.
# See PR #1179 https://github.com/bitcoindevkit/bdk/pull/1179 for the fix in bdk.
# bdk = { git = "https://github.com/thunderbiscuit/bdk.git", branch = "test-rust-tls", version = "1.0.0-alpha.2", features = ["all-keys", "keys-bip39"] }
# bdk_esplora = { git = "https://github.com/thunderbiscuit/bdk.git", branch = "test-rust-tls", version = "0.4.0", package = "bdk_esplora", default-features = false, features = ["std", "blocking", "async-https-rustls"] }
bdk_esplora = { version = "0.4.0", default-features = false, features = ["std", "blocking"] }
uniffi = { version = "=0.25.1" }
[build-dependencies]
uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi = { version = "=0.25.1", features = ["build"] }
[dev-dependencies]
uniffi = { version = "=0.25.1", features = ["bindgen-tests"] }
assert_matches = "1.5.0"
[profile.release-smaller]
inherits = "release"
opt-level = 'z' # Optimize for size.
lto = true # Enable Link Time Optimization
codegen-units = 1 # Reduce number of codegen units to increase optimizations.
panic = "abort" # Abort on panic
strip = "debuginfo" # Partially strip symbols from binary

View File

@@ -1,3 +1,3 @@
fn main() {
uniffi_build::generate_scaffolding("src/bdk.udl").unwrap();
uniffi::generate_scaffolding("./src/bdk.udl").unwrap();
}

View File

@@ -1,13 +1,21 @@
namespace bdk {
namespace bdk {};
// ------------------------------------------------------------------------
// bdk crate - root module
// ------------------------------------------------------------------------
enum KeychainKind {
"External",
"Internal",
};
// ------------------------------------------------------------------------
// bdk crate - wallet module
// ------------------------------------------------------------------------
[Error]
enum BdkError {
"InvalidU32Bytes",
"Generic",
"MissingCachedScripts",
"ScriptDoesntHaveAddressForm",
"NoRecipients",
"NoUtxosSelected",
"OutputBelowDustLimit",
@@ -27,30 +35,38 @@ enum BdkError {
"SpendingPolicyRequired",
"InvalidPolicyPathError",
"Signer",
"InvalidNetwork",
"InvalidProgressValue",
"ProgressUpdateError",
"InvalidOutpoint",
"Descriptor",
"Encode",
"Miniscript",
"MiniscriptPsbt",
"Bip32",
"Secp256k1",
"Json",
"Hex",
"Psbt",
"PsbtParse",
"Electrum",
"Esplora",
"Sled",
"Rusqlite",
"Rpc",
};
enum ChangeSpendPolicy {
"ChangeAllowed",
"OnlyChange",
"ChangeForbidden"
};
interface Balance {
u64 immature();
u64 trusted_pending();
u64 untrusted_pending();
u64 confirmed();
u64 trusted_spendable();
u64 total();
};
dictionary AddressInfo {
u32 index;
string address;
Address address;
KeychainKind keychain;
};
[Enum]
@@ -58,273 +74,62 @@ interface AddressIndex {
New();
LastUnused();
Peek(u32 index);
Reset(u32 index);
};
enum Network {
"Bitcoin",
"Testnet",
"Signet",
"Regtest",
};
dictionary SledDbConfiguration {
string path;
string tree_name;
};
dictionary SqliteDbConfiguration {
string path;
};
dictionary Balance {
u64 immature;
u64 trusted_pending;
u64 untrusted_pending;
u64 confirmed;
u64 spendable;
u64 total;
};
[Enum]
interface DatabaseConfig {
Memory();
Sled(SledDbConfiguration config);
Sqlite(SqliteDbConfiguration config);
};
dictionary TransactionDetails {
u64? fee;
u64 received;
u64 sent;
string txid;
BlockTime? confirmation_time;
};
dictionary BlockTime {
u32 height;
u64 timestamp;
};
enum WordCount {
"Words12",
"Words15",
"Words18",
"Words21",
"Words24",
};
dictionary ElectrumConfig {
string url;
string? socks5;
u8 retry;
u8? timeout;
u64 stop_gap;
boolean validate_domain;
};
dictionary EsploraConfig {
string base_url;
string? proxy;
u8? concurrency;
u64 stop_gap;
u64? timeout;
};
[Enum]
interface Auth {
None();
UserPass(string username, string password);
Cookie(string file);
};
dictionary RpcSyncParams {
u64 start_script_count;
u64 start_time;
boolean force_start_time;
u64 poll_rate_sec;
};
dictionary RpcConfig {
string url;
Auth auth;
Network network;
string wallet_name;
RpcSyncParams? sync_params;
};
[Enum]
interface BlockchainConfig {
Electrum(ElectrumConfig config);
Esplora(EsploraConfig config);
Rpc(RpcConfig config);
};
interface Blockchain {
[Throws=BdkError]
constructor(BlockchainConfig config);
[Throws=BdkError]
void broadcast([ByRef] Transaction transaction);
[Throws=BdkError]
FeeRate estimate_fee(u64 target);
[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(Descriptor descriptor, Descriptor? change_descriptor, Network network, DatabaseConfig database_config);
[Name=new_no_persist, Throws=BdkError]
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network);
[Throws=BdkError]
AddressInfo get_address(AddressIndex address_index);
[Throws=BdkError]
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();
[Throws=BdkError]
void sync([ByRef] Blockchain blockchain, Progress? progress);
Balance get_balance();
boolean is_mine(Script script);
[Throws=BdkError]
sequence<LocalUtxo> list_unspent();
};
interface FeeRate {
[Name=from_sat_per_vb]
constructor(float sat_per_vb);
float as_sat_per_vb();
};
interface Transaction {
[Throws=BdkError]
constructor(sequence<u8> transaction_bytes);
sequence<u8> serialize();
};
interface PartiallySignedTransaction {
[Throws=BdkError]
constructor(string psbt_base64);
string serialize();
string txid();
Transaction extract_tx();
void apply_update(Update update);
[Throws=BdkError]
PartiallySignedTransaction combine(PartiallySignedTransaction other);
u64? fee_amount();
FeeRate? fee_rate();
boolean sign(PartiallySignedTransaction psbt);
};
dictionary TxBuilderResult {
PartiallySignedTransaction psbt;
TransactionDetails transaction_details;
};
interface Update {};
interface TxBuilder {
constructor();
TxBuilder add_recipient(Script script, u64 amount);
TxBuilder set_recipients(sequence<ScriptAmount> script_amount);
TxBuilder add_unspendable(OutPoint unspendable);
TxBuilder add_utxo(OutPoint outpoint);
TxBuilder add_utxos(sequence<OutPoint> outpoints);
TxBuilder change_policy(ChangeSpendPolicy change_policy);
TxBuilder do_not_spend_change();
TxBuilder manually_selected_only();
TxBuilder only_spend_change();
TxBuilder unspendable(sequence<OutPoint> unspendable);
TxBuilder manually_selected_only();
TxBuilder fee_rate(float sat_per_vbyte);
TxBuilder fee_absolute(u64 fee_amount);
TxBuilder drain_wallet();
TxBuilder drain_to(Script script);
TxBuilder enable_rbf();
TxBuilder enable_rbf_with_sequence(u32 nsequence);
TxBuilder add_data(sequence<u8> data);
TxBuilder set_recipients(sequence<ScriptAmount> recipients);
[Throws=BdkError]
TxBuilderResult finish([ByRef] Wallet wallet);
};
interface BumpFeeTxBuilder {
constructor(string txid, float new_fee_rate);
BumpFeeTxBuilder allow_shrinking(string address);
BumpFeeTxBuilder enable_rbf();
BumpFeeTxBuilder enable_rbf_with_sequence(u32 nsequence);
[Throws=BdkError]
PartiallySignedTransaction finish([ByRef] Wallet wallet);
};
// ------------------------------------------------------------------------
// bdk crate - descriptor module
// ------------------------------------------------------------------------
interface Mnemonic {
constructor(WordCount word_count);
@@ -396,18 +201,107 @@ interface Descriptor {
[Name=new_bip84_public]
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
[Name=new_bip86]
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
[Name=new_bip86_public]
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
string as_string();
string as_string_private();
};
interface Address {
[Throws=BdkError]
constructor(string address);
// ------------------------------------------------------------------------
// bdk_esplora crate
// ------------------------------------------------------------------------
Script script_pubkey();
interface EsploraClient {
constructor(string url);
[Throws=BdkError]
Update scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);
[Throws=BdkError]
void broadcast(Transaction transaction);
};
// ------------------------------------------------------------------------
// bdk-ffi-defined types
// ------------------------------------------------------------------------
dictionary ScriptAmount {
Script script;
u64 amount;
};
// ------------------------------------------------------------------------
// bdk crate - bitcoin re-exports
// ------------------------------------------------------------------------
interface Script {
constructor(sequence<u8> raw_output_script);
sequence<u8> to_bytes();
};
enum Network {
"Bitcoin",
"Testnet",
"Signet",
"Regtest",
};
enum WordCount {
"Words12",
"Words15",
"Words18",
"Words21",
"Words24",
};
interface Address {
[Throws=BdkError]
constructor(string address, Network network);
Network network();
Script script_pubkey();
string to_qr_uri();
string as_string();
};
interface Transaction {
[Throws=BdkError]
constructor(sequence<u8> transaction_bytes);
string txid();
u64 size();
u64 vsize();
boolean is_coin_base();
boolean is_explicitly_rbf();
boolean is_lock_time_enabled();
i32 version();
};
interface PartiallySignedTransaction {
[Throws=BdkError]
constructor(string psbt_base64);
string serialize();
Transaction extract_tx();
};
dictionary OutPoint {
string txid;
u32 vout;
};

311
bdk-ffi/src/bitcoin.rs Normal file
View File

@@ -0,0 +1,311 @@
use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked};
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
use bdk::bitcoin::consensus::Decodable;
use bdk::bitcoin::network::constants::Network as BdkNetwork;
use bdk::bitcoin::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::bitcoin::Address as BdkAddress;
use bdk::bitcoin::OutPoint as BdkOutPoint;
use bdk::bitcoin::Transaction as BdkTransaction;
use bdk::bitcoin::Txid;
use bdk::Error as BdkError;
use std::io::Cursor;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
/// A Bitcoin script.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Script(pub(crate) BdkScriptBuf);
impl Script {
pub fn new(raw_output_script: Vec<u8>) -> Self {
let script: BdkScriptBuf = raw_output_script.into();
Script(script)
}
pub fn to_bytes(&self) -> Vec<u8> {
self.0.to_bytes()
}
}
impl From<BdkScriptBuf> for Script {
fn from(script: BdkScriptBuf) -> Self {
Script(script)
}
}
pub enum Network {
/// Mainnet Bitcoin.
Bitcoin,
/// Bitcoin's testnet network.
Testnet,
/// Bitcoin's signet network.
Signet,
/// Bitcoin's regtest network.
Regtest,
}
impl From<Network> for BdkNetwork {
fn from(network: Network) -> Self {
match network {
Network::Bitcoin => BdkNetwork::Bitcoin,
Network::Testnet => BdkNetwork::Testnet,
Network::Signet => BdkNetwork::Signet,
Network::Regtest => BdkNetwork::Regtest,
}
}
}
impl From<BdkNetwork> for Network {
fn from(network: BdkNetwork) -> Self {
match network {
BdkNetwork::Bitcoin => Network::Bitcoin,
BdkNetwork::Testnet => Network::Testnet,
BdkNetwork::Signet => Network::Signet,
BdkNetwork::Regtest => Network::Regtest,
_ => panic!("Network {} not supported", network),
}
}
}
/// A Bitcoin address.
#[derive(Debug, PartialEq, Eq)]
pub struct Address {
inner: BdkAddress<NetworkChecked>,
}
impl Address {
pub fn new(address: String, network: Network) -> Result<Self, BdkError> {
let parsed_address = address
.parse::<bdk::bitcoin::Address<NetworkUnchecked>>()
.map_err(|e| BdkError::Generic(e.to_string()))?;
let network_checked_address = parsed_address
.require_network(network.into())
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(Address {
inner: network_checked_address,
})
}
/// alternative constructor
// fn from_script(script: Arc<Script>, network: Network) -> Result<Self, BdkError> {
// BdkAddress::from_script(&script.inner, network)
// .map(|a| Address { inner: a })
// .map_err(|e| BdkError::Generic(e.to_string()))
// }
//
// fn payload(&self) -> Payload {
// match &self.inner.payload.clone() {
// BdkPayload::PubkeyHash(pubkey_hash) => Payload::PubkeyHash {
// pubkey_hash: pubkey_hash.to_vec(),
// },
// BdkPayload::ScriptHash(script_hash) => Payload::ScriptHash {
// script_hash: script_hash.to_vec(),
// },
// BdkPayload::WitnessProgram { version, program } => Payload::WitnessProgram {
// version: *version,
// program: program.clone(),
// },
// }
// }
pub fn network(&self) -> Network {
self.inner.network.into()
}
pub fn script_pubkey(&self) -> Arc<Script> {
Arc::new(Script(self.inner.script_pubkey()))
}
pub fn to_qr_uri(&self) -> String {
self.inner.to_qr_uri()
}
pub fn as_string(&self) -> String {
self.inner.to_string()
}
}
impl From<Address> for BdkAddress {
fn from(address: Address) -> Self {
address.inner
}
}
impl From<BdkAddress> for Address {
fn from(address: BdkAddress) -> Self {
Address { inner: address }
}
}
/// A Bitcoin transaction.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transaction {
inner: BdkTransaction,
}
impl Transaction {
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
let mut decoder = Cursor::new(transaction_bytes);
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(Transaction { inner: tx })
}
pub fn txid(&self) -> String {
self.inner.txid().to_string()
}
// fn weight(&self) -> u64 {
// self.inner.weight() as u64
// }
pub fn size(&self) -> u64 {
self.inner.size() as u64
}
pub fn vsize(&self) -> u64 {
self.inner.vsize() as u64
}
// fn serialize(&self) -> Vec<u8> {
// self.inner.serialize()
// }
pub fn is_coin_base(&self) -> bool {
self.inner.is_coin_base()
}
pub fn is_explicitly_rbf(&self) -> bool {
self.inner.is_explicitly_rbf()
}
pub fn is_lock_time_enabled(&self) -> bool {
self.inner.is_lock_time_enabled()
}
pub fn version(&self) -> i32 {
self.inner.version
}
// fn lock_time(&self) -> u32 {
// self.inner.lock_time.0
// }
// fn input(&self) -> Vec<TxIn> {
// self.inner.input.iter().map(|x| x.into()).collect()
// }
//
// fn output(&self) -> Vec<TxOut> {
// self.inner.output.iter().map(|x| x.into()).collect()
// }
}
impl From<BdkTransaction> for Transaction {
fn from(tx: BdkTransaction) -> Self {
Transaction { inner: tx }
}
}
impl From<Transaction> for BdkTransaction {
fn from(tx: Transaction) -> Self {
tx.inner
}
}
pub struct PartiallySignedTransaction {
pub(crate) inner: Mutex<BdkPartiallySignedTransaction>,
}
impl PartiallySignedTransaction {
pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
let psbt: BdkPartiallySignedTransaction =
BdkPartiallySignedTransaction::from_str(&psbt_base64)
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(PartiallySignedTransaction {
inner: Mutex::new(psbt),
})
}
pub(crate) fn serialize(&self) -> String {
let psbt = self.inner.lock().unwrap().clone();
psbt.to_string()
}
// pub(crate) fn txid(&self) -> String {
// let tx = self.inner.lock().unwrap().clone().extract_tx();
// let txid = tx.txid();
// txid.to_hex()
// }
/// Return the transaction.
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
let tx = self.inner.lock().unwrap().clone().extract_tx();
Arc::new(tx.into())
}
// /// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
// ///
// /// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
// pub(crate) fn combine(
// &self,
// other: Arc<PartiallySignedTransaction>,
// ) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
// let other_psbt = other.inner.lock().unwrap().clone();
// let mut original_psbt = self.inner.lock().unwrap().clone();
//
// original_psbt.combine(other_psbt)?;
// Ok(Arc::new(PartiallySignedTransaction {
// inner: Mutex::new(original_psbt),
// }))
// }
// /// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
// /// If the PSBT is missing a TxOut for an input returns None.
// pub(crate) fn fee_amount(&self) -> Option<u64> {
// self.inner.lock().unwrap().fee_amount()
// }
// /// The transaction's fee rate. This value will only be accurate if calculated AFTER the
// /// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the
// /// transaction.
// /// If the PSBT is missing a TxOut for an input returns None.
// pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
// self.inner.lock().unwrap().fee_rate().map(Arc::new)
// }
// /// Serialize the PSBT data structure as a String of JSON.
// pub(crate) fn json_serialize(&self) -> String {
// let psbt = self.inner.lock().unwrap();
// serde_json::to_string(psbt.deref()).unwrap()
// }
}
impl From<BdkPartiallySignedTransaction> for PartiallySignedTransaction {
fn from(psbt: BdkPartiallySignedTransaction) -> Self {
PartiallySignedTransaction {
inner: Mutex::new(psbt),
}
}
}
/// A reference to a transaction output.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct OutPoint {
/// The referenced transaction's txid.
pub txid: String,
/// The index of the referenced output in its transaction's vout.
pub vout: u32,
}
impl From<&OutPoint> for BdkOutPoint {
fn from(outpoint: &OutPoint) -> Self {
BdkOutPoint {
txid: Txid::from_str(&outpoint.txid).unwrap(),
vout: outpoint.vout,
}
}
}

View File

@@ -1,201 +0,0 @@
// use crate::BlockchainConfig;
use crate::{BdkError, Transaction};
use bdk::bitcoin::Network;
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::rpc::Auth as BdkAuth;
use bdk::blockchain::rpc::RpcSyncParams as BdkRpcSyncParams;
use bdk::blockchain::Blockchain as BdkBlockchain;
use bdk::blockchain::GetBlockHash;
use bdk::blockchain::GetHeight;
use bdk::blockchain::{
electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig,
rpc::RpcConfig as BdkRpcConfig, ConfigurableBlockchain,
};
use bdk::FeeRate;
use std::convert::{From, TryFrom};
use std::path::PathBuf;
use std::sync::{Arc, Mutex, MutexGuard};
pub(crate) struct Blockchain {
blockchain_mutex: Mutex<AnyBlockchain>,
}
impl Blockchain {
pub(crate) fn new(blockchain_config: BlockchainConfig) -> Result<Self, BdkError> {
let any_blockchain_config = match blockchain_config {
BlockchainConfig::Electrum { config } => {
AnyBlockchainConfig::Electrum(ElectrumBlockchainConfig {
retry: config.retry,
socks5: config.socks5,
timeout: config.timeout,
url: config.url,
stop_gap: usize::try_from(config.stop_gap).unwrap(),
validate_domain: config.validate_domain,
})
}
BlockchainConfig::Esplora { config } => {
AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url: config.base_url,
proxy: config.proxy,
concurrency: config.concurrency,
stop_gap: usize::try_from(config.stop_gap).unwrap(),
timeout: config.timeout,
})
}
BlockchainConfig::Rpc { config } => AnyBlockchainConfig::Rpc(BdkRpcConfig {
url: config.url,
auth: config.auth.into(),
network: config.network,
wallet_name: config.wallet_name,
sync_params: config.sync_params.map(|p| p.into()),
}),
};
let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?;
Ok(Self {
blockchain_mutex: Mutex::new(blockchain),
})
}
pub(crate) fn get_blockchain(&self) -> MutexGuard<AnyBlockchain> {
self.blockchain_mutex.lock().expect("blockchain")
}
pub(crate) fn broadcast(&self, transaction: &Transaction) -> Result<(), BdkError> {
let tx = &transaction.internal;
self.get_blockchain().broadcast(tx)
}
pub(crate) fn estimate_fee(&self, target: u64) -> Result<Arc<FeeRate>, BdkError> {
let result: Result<FeeRate, bdk::Error> =
self.get_blockchain().estimate_fee(target as usize);
result.map(Arc::new)
}
pub(crate) fn get_height(&self) -> Result<u32, BdkError> {
self.get_blockchain().get_height()
}
pub(crate) fn get_block_hash(&self, height: u32) -> Result<String, BdkError> {
self.get_blockchain()
.get_block_hash(u64::from(height))
.map(|hash| hash.to_string())
}
}
/// Configuration for an ElectrumBlockchain
pub struct ElectrumConfig {
/// 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
pub url: String,
/// URL of the socks5 proxy server or a Tor service
pub socks5: Option<String>,
/// Request retry count
pub retry: u8,
/// Request timeout (seconds)
pub timeout: Option<u8>,
/// Stop searching addresses for transactions after finding an unused gap of this length
pub stop_gap: u64,
/// Validate the domain when using SSL
pub validate_domain: bool,
}
/// Configuration for an EsploraBlockchain
pub struct EsploraConfig {
/// Base URL of the esplora service
/// e.g. https://blockstream.info/api/
pub base_url: String,
/// Optional URL of the proxy to use to make requests to the Esplora server
/// The string should be formatted as: <protocol>://<user>:<password>@host:<port>.
/// Note that the format of this value and the supported protocols change slightly between the
/// sync version of esplora (using ureq) and the async version (using reqwest). For more
/// details check with the documentation of the two crates. Both of them are compiled with
/// the socks feature enabled.
/// The proxy is ignored when targeting wasm32.
pub proxy: Option<String>,
/// Number of parallel requests sent to the esplora service (default: 4)
pub concurrency: Option<u8>,
/// Stop searching addresses for transactions after finding an unused gap of this length.
pub stop_gap: u64,
/// Socket timeout.
pub timeout: Option<u64>,
}
pub enum Auth {
/// No authentication
None,
/// Authentication with username and password, usually [Auth::Cookie] should be preferred
UserPass {
/// Username
username: String,
/// Password
password: String,
},
/// Authentication with a cookie file
Cookie {
/// Cookie file
file: String,
},
}
impl From<Auth> for BdkAuth {
fn from(auth: Auth) -> Self {
match auth {
Auth::None => BdkAuth::None,
Auth::UserPass { username, password } => BdkAuth::UserPass { username, password },
Auth::Cookie { file } => BdkAuth::Cookie {
file: PathBuf::from(file),
},
}
}
}
/// Sync parameters for Bitcoin Core RPC.
///
/// In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
/// `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
/// how the `importdescriptors` RPC calls are to be made.
pub struct RpcSyncParams {
/// The minimum number of scripts to scan for on initial sync.
pub start_script_count: u64,
/// Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
pub start_time: u64,
/// Forces every sync to use `start_time` as import timestamp.
pub force_start_time: bool,
/// RPC poll rate (in seconds) to get state updates.
pub poll_rate_sec: u64,
}
impl From<RpcSyncParams> for BdkRpcSyncParams {
fn from(params: RpcSyncParams) -> Self {
BdkRpcSyncParams {
start_script_count: params.start_script_count as usize,
start_time: params.start_time,
force_start_time: params.force_start_time,
poll_rate_sec: params.poll_rate_sec,
}
}
}
/// RpcBlockchain configuration options
pub struct RpcConfig {
/// The bitcoin node url
pub url: String,
/// The bitcoin node authentication mechanism
pub auth: Auth,
/// The network we are using (it will be checked the bitcoin node network matches this)
pub network: Network,
/// The wallet name in the bitcoin node, consider using [crate::wallet::wallet_name_from_descriptor] for this
pub wallet_name: String,
/// Sync parameters
pub sync_params: Option<RpcSyncParams>,
}
/// Type that can contain any of the blockchain configurations defined by the library.
pub enum BlockchainConfig {
/// Electrum client
Electrum { config: ElectrumConfig },
/// Esplora client
Esplora { config: EsploraConfig },
/// Bitcoin Core RPC client
Rpc { config: RpcConfig },
}

View File

@@ -1,14 +0,0 @@
use bdk::database::any::{SledDbConfiguration, SqliteDbConfiguration};
/// Type that can contain any of the database configurations defined by the library
/// This allows storing a single configuration that can be loaded into an AnyDatabaseConfig
/// instance. Wallets that plan to offer users the ability to switch blockchain backend at runtime
/// will find this particularly useful.
pub enum DatabaseConfig {
/// Memory database has no config
Memory,
/// Simple key-value embedded database based on sled
Sled { config: SledDbConfiguration },
/// Sqlite embedded database using rusqlite
Sqlite { config: SqliteDbConfiguration },
}

View File

@@ -1,29 +1,33 @@
use crate::{BdkError, DescriptorPublicKey, DescriptorSecretKey};
use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::Fingerprint;
use bdk::bitcoin::Network;
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
use bdk::keys::{
DescriptorPublicKey as BdkDescriptorPublicKey, DescriptorSecretKey as BdkDescriptorSecretKey,
};
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, DescriptorTemplate,
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
DescriptorTemplate,
};
use bdk::Error as BdkError;
use bdk::KeychainKind;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
#[derive(Debug)]
pub(crate) struct Descriptor {
pub(crate) extended_descriptor: ExtendedDescriptor,
pub(crate) key_map: KeyMap,
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)?;
let (extended_descriptor, key_map) =
descriptor.into_wallet_descriptor(&secp, network.into())?;
Ok(Self {
extended_descriptor,
key_map,
@@ -35,19 +39,23 @@ impl Descriptor {
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
let derivable_key = &secret_key.inner;
match derivable_key.deref() {
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).unwrap();
let (extended_descriptor, key_map, _) = Bip44(derivable_key, keychain_kind)
.build(network.into())
.unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorSecretKey::Single(_) => {
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
}
@@ -60,14 +68,17 @@ impl Descriptor {
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
let derivable_key = &public_key.inner;
match derivable_key.deref() {
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)
.build(network.into())
.unwrap();
Self {
@@ -75,7 +86,7 @@ impl Descriptor {
key_map,
}
}
BdkDescriptorPublicKey::Single(_) => {
BdkDescriptorPublicKey::MultiXPub(_) => {
unreachable!()
}
}
@@ -86,19 +97,23 @@ impl Descriptor {
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
let derivable_key = &secret_key.inner;
match derivable_key.deref() {
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).unwrap();
let (extended_descriptor, key_map, _) = Bip49(derivable_key, keychain_kind)
.build(network.into())
.unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorSecretKey::Single(_) => {
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
}
@@ -111,14 +126,17 @@ impl Descriptor {
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
let derivable_key = &public_key.inner;
match derivable_key.deref() {
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)
.build(network.into())
.unwrap();
Self {
@@ -126,7 +144,7 @@ impl Descriptor {
key_map,
}
}
BdkDescriptorPublicKey::Single(_) => {
BdkDescriptorPublicKey::MultiXPub(_) => {
unreachable!()
}
}
@@ -137,19 +155,23 @@ impl Descriptor {
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
let derivable_key = &secret_key.inner;
match derivable_key.deref() {
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).unwrap();
let (extended_descriptor, key_map, _) = Bip84(derivable_key, keychain_kind)
.build(network.into())
.unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorSecretKey::Single(_) => {
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
}
@@ -162,14 +184,17 @@ impl Descriptor {
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
let derivable_key = &public_key.inner;
match derivable_key.deref() {
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)
.build(network.into())
.unwrap();
Self {
@@ -177,9 +202,67 @@ impl 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!()
}
}
}
@@ -194,12 +277,11 @@ impl Descriptor {
}
}
// 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.
// // 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::*;
use assert_matches::assert_matches;
use bdk::descriptor::DescriptorError::Key;
@@ -239,17 +321,27 @@ mod test {
.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, KeychainKind::External, Network::Testnet);
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());
// for the public versions of the templates these are incorrect, bug report and fix in bitcoindevkit/bdk#817 and bitcoindevkit/bdk#818
println!("Template 86: {}", template_private_86.as_string());
let template_public_44 = Descriptor::new_bip44_public(
handmade_public_44,
"d1d04177".to_string(),
@@ -268,9 +360,16 @@ mod test {
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(),
@@ -284,6 +383,10 @@ mod test {
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(),
@@ -297,6 +400,10 @@ mod test {
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() {
@@ -309,26 +416,4 @@ mod test {
bdk::Error::Descriptor(Key(InvalidNetwork))
)
}
#[test]
fn test_wallet_from_descriptor() {
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap();
let wallet1 = Wallet::new(
Arc::new(Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap()),
None,
Network::Testnet,
DatabaseConfig::Memory
);
let wallet2 = Wallet::new(
Arc::new(descriptor1),
None,
Network::Bitcoin,
DatabaseConfig::Memory,
);
// Creating a wallet using a Descriptor with an extended key that doesn't match the network provided in the wallet constructor will throw and InvalidNetwork Error
assert!(wallet1.is_ok());
assert_matches!(
wallet2.unwrap_err(),
bdk::Error::Descriptor(Key(InvalidNetwork))
)
}
}

68
bdk-ffi/src/esplora.rs Normal file
View File

@@ -0,0 +1,68 @@
use crate::wallet::{Update, Wallet};
use std::ops::Deref;
use bdk::wallet::Update as BdkUpdate;
use bdk::Error as BdkError;
use bdk_esplora::esplora_client::{BlockingClient, Builder};
use bdk_esplora::EsploraExt;
use std::sync::Arc;
pub struct EsploraClient(BlockingClient);
impl EsploraClient {
pub fn new(url: String) -> Self {
let client = Builder::new(url.as_str()).build_blocking().unwrap();
Self(client)
}
// This is a temporary solution for scanning. The long-term solution involves not passing
// the wallet to the client at all.
pub fn scan(
&self,
wallet: Arc<Wallet>,
stop_gap: u64,
parallel_requests: u64,
) -> Result<Arc<Update>, BdkError> {
let wallet = wallet.get_wallet();
let previous_tip = wallet.latest_checkpoint();
let keychain_spks = wallet.spks_of_all_keychains().into_iter().collect();
let (update_graph, last_active_indices) = self
.0
.scan_txs_with_keychains(
keychain_spks,
None,
None,
stop_gap as usize,
parallel_requests as usize,
)
.unwrap();
let missing_heights = update_graph.missing_heights(wallet.local_chain());
let chain_update = self
.0
.update_local_chain(previous_tip, missing_heights)
.unwrap();
let update = BdkUpdate {
last_active_indices,
graph: update_graph,
chain: Some(chain_update),
};
Ok(Arc::new(Update(update)))
}
// pub fn sync();
pub fn broadcast(&self, transaction: Arc<crate::bitcoin::Transaction>) -> Result<(), BdkError> {
let bdk_transaction: bdk::bitcoin::Transaction = transaction.deref().clone().into();
self.0
.broadcast(&bdk_transaction)
.map_err(|e| BdkError::Generic(e.to_string()))
}
// pub fn estimate_fee();
}

View File

@@ -1,15 +1,19 @@
use crate::BdkError;
use crate::Network;
use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
use bdk::bitcoin::Network;
use bdk::descriptor::DescriptorXKey;
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount};
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};
@@ -17,22 +21,27 @@ 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 {
internal: BdkMnemonic,
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((word_count, Language::English)).unwrap();
BdkMnemonic::generate_with_entropy((word_count, Language::English), entropy).unwrap();
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
Mnemonic { internal: mnemonic }
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 { internal: m })
.map(|m| Mnemonic { inner: m })
.map_err(|e| BdkError::Generic(e.to_string()))
}
@@ -40,47 +49,47 @@ impl Mnemonic {
/// 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 { internal: m })
.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.internal.to_string()
self.inner.to_string()
}
}
pub(crate) struct DerivationPath {
derivation_path_mutex: Mutex<BdkDerivationPath>,
inner_mutex: Mutex<BdkDerivationPath>,
}
impl DerivationPath {
pub(crate) fn new(path: String) -> Result<Self, BdkError> {
BdkDerivationPath::from_str(&path)
.map(|x| DerivationPath {
derivation_path_mutex: Mutex::new(x),
inner_mutex: Mutex::new(x),
})
.map_err(|e| BdkError::Generic(e.to_string()))
}
}
#[derive(Debug)]
pub(crate) struct DescriptorSecretKey {
pub(crate) descriptor_secret_key_mutex: Mutex<BdkDescriptorSecretKey>,
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.internal.clone();
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).unwrap(),
xkey: xkey.into_xprv(network.into()).unwrap(),
derivation_path: BdkDerivationPath::master(),
wildcard: bdk::descriptor::Wildcard::Unhardened,
wildcard: Wildcard::Unhardened,
});
Self {
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
inner: descriptor_secret_key,
}
}
@@ -88,15 +97,18 @@ impl DescriptorSecretKey {
let descriptor_secret_key = BdkDescriptorSecretKey::from_str(private_key.as_str())
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(Self {
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
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.descriptor_secret_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
match descriptor_secret_key.deref() {
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() {
@@ -110,19 +122,22 @@ impl DescriptorSecretKey {
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self {
descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key),
inner: derived_descriptor_secret_key,
}))
}
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
"Cannot derive from a single key".to_string(),
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.descriptor_secret_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
match descriptor_secret_key.deref() {
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 {
@@ -132,36 +147,34 @@ impl DescriptorSecretKey {
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self {
descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key),
inner: extended_descriptor_secret_key,
}))
}
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
"Cannot extend from a single key".to_string(),
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
.descriptor_secret_key_mutex
.lock()
.unwrap()
.to_public(&secp)
.unwrap();
let descriptor_public_key = self.inner.to_public(&secp).unwrap();
Arc::new(DescriptorPublicKey {
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
inner: descriptor_public_key,
})
}
/// Get the private key as bytes.
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
let secret_bytes: Vec<u8> = match descriptor_secret_key.deref() {
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::Single(_) => {
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
};
@@ -170,13 +183,13 @@ impl DescriptorSecretKey {
}
pub(crate) fn as_string(&self) -> String {
self.descriptor_secret_key_mutex.lock().unwrap().to_string()
self.inner.to_string()
}
}
#[derive(Debug)]
pub(crate) struct DescriptorPublicKey {
pub(crate) descriptor_public_key_mutex: Mutex<BdkDescriptorPublicKey>,
pub struct DescriptorPublicKey {
pub(crate) inner: BdkDescriptorPublicKey,
}
impl DescriptorPublicKey {
@@ -184,16 +197,19 @@ impl DescriptorPublicKey {
let descriptor_public_key = BdkDescriptorPublicKey::from_str(public_key.as_str())
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(Self {
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
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.descriptor_public_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
let descriptor_public_key = &self.inner;
let path = path.inner_mutex.lock().unwrap().deref().clone();
match descriptor_public_key.deref() {
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() {
@@ -207,19 +223,22 @@ impl DescriptorPublicKey {
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self {
descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key),
inner: derived_descriptor_public_key,
}))
}
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
"Cannot derive from a single key".to_string(),
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.descriptor_public_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
match descriptor_public_key.deref() {
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 {
@@ -229,34 +248,34 @@ impl DescriptorPublicKey {
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self {
descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key),
inner: extended_descriptor_public_key,
}))
}
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
"Cannot extend from a single key".to_string(),
BdkDescriptorPublicKey::MultiXPub(_) => Err(BdkError::Generic(
"Cannot derive from a multi xpub".to_string(),
)),
}
}
pub(crate) fn as_string(&self) -> String {
self.descriptor_public_key_mutex.lock().unwrap().to_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.
//
// // 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::hashes::hex::ToHex;
use bdk::bitcoin::Network;
use std::sync::Arc;
fn get_descriptor_secret_key() -> DescriptorSecretKey {
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, Arc::new(mnemonic), None)
DescriptorSecretKey::new(Network::Testnet.into(), Arc::new(mnemonic), None)
}
fn derive_dsk(
@@ -293,14 +312,14 @@ mod test {
#[test]
fn test_generate_descriptor_secret_key() {
let master_dsk = get_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_descriptor_secret_key();
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();
@@ -310,7 +329,7 @@ mod test {
#[test]
fn test_derive_descriptors_keys() {
let master_dsk = get_descriptor_secret_key();
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();
@@ -320,7 +339,7 @@ mod test {
#[test]
fn test_extend_descriptor_keys() {
let master_dsk = get_descriptor_secret_key();
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();
@@ -334,7 +353,7 @@ mod test {
}
#[test]
fn test_from_str_descriptor_secret_key() {
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();
@@ -347,8 +366,8 @@ mod test {
}
#[test]
fn test_derive_and_extend_descriptor_secret_key() {
let master_dsk = get_descriptor_secret_key();
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/*");
@@ -359,18 +378,21 @@ mod test {
#[test]
fn test_derive_hardened_path_using_public() {
let master_dpk = get_descriptor_secret_key().as_public();
let master_dpk = get_inner().as_public();
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
assert!(derived_dpk.is_err());
}
#[test]
fn test_retrieve_master_secret_key() {
let master_dpk = get_descriptor_secret_key();
let master_private_key = master_dpk.secret_bytes().to_hex();
assert_eq!(
master_private_key,
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
)
}
// 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"
// )
// }
}

View File

@@ -1,38 +1,37 @@
mod blockchain;
mod database;
mod bitcoin;
mod descriptor;
mod esplora;
mod keys;
mod psbt;
mod wallet;
use crate::blockchain::{
Auth, Blockchain, BlockchainConfig, ElectrumConfig, EsploraConfig, RpcConfig, RpcSyncParams,
};
use crate::database::DatabaseConfig;
// TODO 6: Why are these imports required?
use crate::bitcoin::Address;
use crate::bitcoin::Network;
use crate::bitcoin::OutPoint;
use crate::bitcoin::PartiallySignedTransaction;
use crate::bitcoin::Script;
use crate::bitcoin::Transaction;
use crate::descriptor::Descriptor;
use crate::esplora::EsploraClient;
use crate::keys::DerivationPath;
use crate::keys::{DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
use crate::psbt::PartiallySignedTransaction;
use crate::wallet::{BumpFeeTxBuilder, TxBuilder, Wallet};
use bdk::bitcoin::blockdata::script::Script as BdkScript;
use bdk::bitcoin::consensus::Decodable;
use bdk::bitcoin::psbt::serialize::Serialize;
use bdk::bitcoin::{
Address as BdkAddress, Network, OutPoint as BdkOutPoint, Transaction as BdkTransaction, Txid,
};
use bdk::blockchain::Progress as BdkProgress;
use bdk::database::any::{SledDbConfiguration, SqliteDbConfiguration};
use crate::keys::DescriptorPublicKey;
use crate::keys::DescriptorSecretKey;
use crate::keys::Mnemonic;
use crate::wallet::TxBuilder;
use crate::wallet::Update;
use crate::wallet::Wallet;
use bdk::keys::bip39::WordCount;
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};
use std::convert::From;
use std::fmt;
use std::io::Cursor;
use std::str::FromStr;
use bdk::wallet::Balance as BdkBalance;
use bdk::Error as BdkError;
use bdk::KeychainKind;
use std::sync::Arc;
uniffi_macros::include_scaffolding!("bdk");
uniffi::include_scaffolding!("bdk");
/// A output script and an amount of satoshis.
pub struct ScriptAmount {
@@ -42,17 +41,20 @@ pub struct ScriptAmount {
/// A derived address and the index it was found at.
pub struct AddressInfo {
/// Child index of this address
/// Child index of this address.
pub index: u32,
/// Address
pub address: String,
/// Address.
pub address: Arc<Address>,
/// Type of keychain.
pub keychain: KeychainKind,
}
impl From<BdkAddressInfo> for AddressInfo {
fn from(x: bdk::wallet::AddressInfo) -> AddressInfo {
fn from(address_info: BdkAddressInfo) -> Self {
AddressInfo {
index: x.index,
address: x.address.to_string(),
index: address_info.index,
address: Arc::new(address_info.address.into()),
keychain: address_info.keychain,
}
}
}
@@ -74,249 +76,310 @@ pub enum AddressIndex {
/// Use with caution, if an index is given that is less than the current descriptor index
/// then the returned address may have already been used.
Peek { index: u32 },
/// Return the address for a specific descriptor index and reset the current descriptor index
/// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value.
/// Use with caution, if an index is given that is less than the current descriptor index
/// then the returned address and subsequent addresses returned by calls to `AddressIndex::New`
/// and `AddressIndex::LastUsed` may have already been used. Also if the index is reset to a
/// value earlier than the [`crate::blockchain::Blockchain`] stop_gap (default is 20) then a
/// larger stop_gap should be used to monitor for all possibly used addresses.
Reset { index: u32 },
}
impl From<AddressIndex> for BdkAddressIndex {
fn from(x: AddressIndex) -> BdkAddressIndex {
match x {
fn from(address_index: AddressIndex) -> Self {
match address_index {
AddressIndex::New => BdkAddressIndex::New,
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
AddressIndex::Peek { index } => BdkAddressIndex::Peek(index),
AddressIndex::Reset { index } => BdkAddressIndex::Reset(index),
}
}
}
/// A wallet transaction
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TransactionDetails {
/// Transaction id.
pub txid: String,
/// Received value (sats)
/// Sum of owned outputs of this transaction.
pub received: u64,
/// Sent value (sats)
/// Sum of owned inputs of this transaction.
pub sent: u64,
/// Fee value (sats) if confirmed.
/// 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.
pub fee: Option<u64>,
/// If the transaction is confirmed, contains height and timestamp of the block containing the
/// transaction, unconfirmed transaction contains `None`.
pub confirmation_time: Option<BlockTime>,
}
impl From<&bdk::TransactionDetails> for TransactionDetails {
fn from(x: &bdk::TransactionDetails) -> TransactionDetails {
TransactionDetails {
fee: x.fee,
txid: x.txid.to_string(),
received: x.received,
sent: x.sent,
confirmation_time: x.confirmation_time.clone(),
// TODO 9: Peek is not correctly implemented
impl From<&AddressIndex> for BdkAddressIndex {
fn from(address_index: &AddressIndex) -> Self {
match address_index {
AddressIndex::New => BdkAddressIndex::New,
AddressIndex::LastUnused => BdkAddressIndex::LastUnused,
AddressIndex::Peek { index } => BdkAddressIndex::Peek(*index),
}
}
}
/// A reference to a transaction output.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct OutPoint {
/// The referenced transaction's txid.
txid: String,
/// The index of the referenced output in its transaction's vout.
vout: u32,
}
impl From<&OutPoint> for BdkOutPoint {
fn from(x: &OutPoint) -> BdkOutPoint {
BdkOutPoint {
txid: Txid::from_str(&x.txid).unwrap(),
vout: x.vout,
impl From<BdkAddressIndex> for AddressIndex {
fn from(address_index: BdkAddressIndex) -> Self {
match address_index {
BdkAddressIndex::New => AddressIndex::New,
BdkAddressIndex::LastUnused => AddressIndex::LastUnused,
_ => panic!("Mmmm not working"),
}
}
}
impl From<&BdkAddressIndex> for AddressIndex {
fn from(address_index: &BdkAddressIndex) -> Self {
match address_index {
BdkAddressIndex::New => AddressIndex::New,
BdkAddressIndex::LastUnused => AddressIndex::LastUnused,
_ => panic!("Mmmm not working"),
}
}
}
// /// A wallet transaction
// #[derive(Debug, Clone, PartialEq, Eq, Default)]
// pub struct TransactionDetails {
// pub transaction: Option<Arc<Transaction>>,
// /// Transaction id.
// pub txid: String,
// /// Received value (sats)
// /// Sum of owned outputs of this transaction.
// pub received: u64,
// /// Sent value (sats)
// /// Sum of owned inputs of this transaction.
// pub sent: u64,
// /// Fee value (sats) if confirmed.
// /// 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.
// pub fee: Option<u64>,
// /// If the transaction is confirmed, contains height and timestamp of the block containing the
// /// transaction, unconfirmed transaction contains `None`.
// pub confirmation_time: Option<BlockTime>,
// }
//
// impl From<BdkTransactionDetails> for TransactionDetails {
// fn from(tx_details: BdkTransactionDetails) -> Self {
// let optional_tx: Option<Arc<Transaction>> =
// tx_details.transaction.map(|tx| Arc::new(tx.into()));
//
// TransactionDetails {
// transaction: optional_tx,
// fee: tx_details.fee,
// txid: tx_details.txid.to_string(),
// received: tx_details.received,
// sent: tx_details.sent,
// confirmation_time: tx_details.confirmation_time,
// }
// }
// }
//
// /// A reference to a transaction output.
// #[derive(Clone, Debug, PartialEq, Eq, Hash)]
// pub struct OutPoint {
// /// The referenced transaction's txid.
// txid: String,
// /// The index of the referenced output in its transaction's vout.
// vout: u32,
// }
//
// impl From<&OutPoint> for BdkOutPoint {
// fn from(outpoint: &OutPoint) -> Self {
// BdkOutPoint {
// txid: Txid::from_str(&outpoint.txid).unwrap(),
// vout: outpoint.vout,
// }
// }
// }
pub struct Balance {
// All coinbase outputs not yet matured
pub immature: u64,
/// Unconfirmed UTXOs generated by a wallet tx
pub trusted_pending: u64,
/// Unconfirmed UTXOs received from an external wallet
pub untrusted_pending: u64,
/// Confirmed and immediately spendable balance
pub confirmed: u64,
/// Get sum of trusted_pending and confirmed coins
pub spendable: u64,
/// Get the whole balance visible to the wallet
pub total: u64,
pub inner: BdkBalance,
}
impl From<BdkBalance> for Balance {
fn from(bdk_balance: BdkBalance) -> Self {
Balance {
immature: bdk_balance.immature,
trusted_pending: bdk_balance.trusted_pending,
untrusted_pending: bdk_balance.untrusted_pending,
confirmed: bdk_balance.confirmed,
spendable: bdk_balance.get_spendable(),
total: bdk_balance.get_total(),
}
impl Balance {
/// All coinbase outputs not yet matured.
fn immature(&self) -> u64 {
self.inner.immature
}
/// Unconfirmed UTXOs generated by a wallet tx.
fn trusted_pending(&self) -> u64 {
self.inner.trusted_pending
}
/// Unconfirmed UTXOs received from an external wallet.
fn untrusted_pending(&self) -> u64 {
self.inner.untrusted_pending
}
/// Confirmed and immediately spendable balance.
fn confirmed(&self) -> u64 {
self.inner.confirmed
}
/// Get sum of trusted_pending and confirmed coins.
fn trusted_spendable(&self) -> u64 {
self.inner.trusted_spendable()
}
/// Get the whole balance visible to the wallet.
fn total(&self) -> u64 {
self.inner.total()
}
}
/// A transaction output, which defines new coins to be created from old ones.
pub struct TxOut {
/// The value of the output, in satoshis.
value: u64,
/// The address of the output.
address: String,
}
// impl From<BdkBalance> for Balance {
// fn from(bdk_balance: BdkBalance) -> Self {
// Balance { inner: bdk_balance }
// }
// }
pub struct LocalUtxo {
outpoint: OutPoint,
txout: TxOut,
keychain: KeychainKind,
is_spent: bool,
}
// /// A transaction output, which defines new coins to be created from old ones.
// #[derive(Debug, Clone)]
// pub struct TxOut {
// /// The value of the output, in satoshis.
// value: u64,
// /// The address of the output.
// script_pubkey: Arc<Script>,
// }
//
// impl From<&BdkTxOut> for TxOut {
// fn from(tx_out: &BdkTxOut) -> Self {
// TxOut {
// value: tx_out.value,
// script_pubkey: Arc::new(Script {
// inner: tx_out.script_pubkey.clone(),
// }),
// }
// }
// }
//
// pub struct LocalUtxo {
// outpoint: OutPoint,
// txout: TxOut,
// keychain: KeychainKind,
// is_spent: bool,
// }
//
// impl From<BdkLocalUtxo> for LocalUtxo {
// fn from(local_utxo: BdkLocalUtxo) -> Self {
// LocalUtxo {
// outpoint: OutPoint {
// txid: local_utxo.outpoint.txid.to_string(),
// vout: local_utxo.outpoint.vout,
// },
// txout: TxOut {
// value: local_utxo.txout.value,
// script_pubkey: Arc::new(Script {
// inner: local_utxo.txout.script_pubkey,
// }),
// },
// keychain: local_utxo.keychain,
// is_spent: local_utxo.is_spent,
// }
// }
// }
//
// /// Trait that logs at level INFO every update received (if any).
// pub trait Progress: Send + Sync + 'static {
// /// 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.
// fn update(&self, progress: f32, message: Option<String>);
// }
//
// struct ProgressHolder {
// progress: Box<dyn Progress>,
// }
//
// impl BdkProgress for ProgressHolder {
// fn update(&self, progress: f32, message: Option<String>) -> Result<(), BdkError> {
// self.progress.update(progress, message);
// Ok(())
// }
// }
//
// impl Debug for ProgressHolder {
// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// f.debug_struct("ProgressHolder").finish_non_exhaustive()
// }
// }
//
// #[derive(Debug, Clone)]
// pub struct TxIn {
// pub previous_output: OutPoint,
// pub script_sig: Arc<Script>,
// pub sequence: u32,
// pub witness: Vec<Vec<u8>>,
// }
//
// impl From<&BdkTxIn> for TxIn {
// fn from(tx_in: &BdkTxIn) -> Self {
// TxIn {
// previous_output: OutPoint {
// txid: tx_in.previous_output.txid.to_string(),
// vout: tx_in.previous_output.vout,
// },
// script_sig: Arc::new(Script {
// inner: tx_in.script_sig.clone(),
// }),
// sequence: tx_in.sequence.0,
// witness: tx_in.witness.to_vec(),
// }
// }
// }
// This trait is used to convert the bdk TxOut type with field `script_pubkey: Script`
// into the bdk-ffi TxOut type which has a field `address: String` instead
trait NetworkLocalUtxo {
fn from_utxo(x: &bdk::LocalUtxo, network: Network) -> LocalUtxo;
}
// /// The method used to produce an address.
// #[derive(Debug)]
// pub enum Payload {
// /// P2PKH address.
// PubkeyHash { pubkey_hash: Vec<u8> },
// /// P2SH address.
// ScriptHash { script_hash: Vec<u8> },
// /// Segwit address.
// WitnessProgram {
// /// The witness program version.
// version: WitnessVersion,
// /// The witness program.
// program: Vec<u8>,
// },
// }
impl NetworkLocalUtxo for LocalUtxo {
fn from_utxo(x: &bdk::LocalUtxo, network: Network) -> LocalUtxo {
LocalUtxo {
outpoint: OutPoint {
txid: x.outpoint.txid.to_string(),
vout: x.outpoint.vout,
},
txout: TxOut {
value: x.txout.value,
address: BdkAddress::from_script(&x.txout.script_pubkey, network)
.unwrap()
.to_string(),
},
keychain: x.keychain,
is_spent: x.is_spent,
}
}
}
/// Trait that logs at level INFO every update received (if any).
pub trait Progress: Send + Sync + 'static {
/// 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.
fn update(&self, progress: f32, message: Option<String>);
}
struct ProgressHolder {
progress: Box<dyn Progress>,
}
impl BdkProgress for ProgressHolder {
fn update(&self, progress: f32, message: Option<String>) -> Result<(), BdkError> {
self.progress.update(progress, message);
Ok(())
}
}
impl fmt::Debug for ProgressHolder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProgressHolder").finish_non_exhaustive()
}
}
/// A Bitcoin transaction.
#[derive(Debug)]
pub struct Transaction {
internal: BdkTransaction,
}
impl Transaction {
fn new(transaction_bytes: Vec<u8>) -> Result<Self, BdkError> {
let mut decoder = Cursor::new(transaction_bytes);
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
Ok(Transaction { internal: tx })
}
fn serialize(&self) -> Vec<u8> {
self.internal.serialize()
}
}
/// A Bitcoin address.
struct Address {
address: BdkAddress,
}
impl Address {
fn new(address: String) -> Result<Self, BdkError> {
BdkAddress::from_str(address.as_str())
.map(|a| Address { address: a })
.map_err(|e| BdkError::Generic(e.to_string()))
}
fn script_pubkey(&self) -> Arc<Script> {
Arc::new(Script {
script: self.address.script_pubkey(),
})
}
}
/// A Bitcoin script.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Script {
script: BdkScript,
}
impl Script {
fn new(raw_output_script: Vec<u8>) -> Self {
let script: BdkScript = BdkScript::from(raw_output_script);
Script { script }
}
}
#[derive(Clone, Debug)]
enum RbfValue {
Default,
Value(u32),
}
/// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
/// transaction details.
pub struct TxBuilderResult {
pub(crate) psbt: Arc<PartiallySignedTransaction>,
pub transaction_details: TransactionDetails,
}
uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
// 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 super::Transaction;
use bdk::bitcoin::hashes::hex::FromHex;
// Verify that bdk-ffi Transaction can be created from valid bytes and serialized back into the same bytes.
#[test]
fn test_transaction_serde() {
let test_tx_bytes = Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700").unwrap();
let new_tx_from_bytes = Transaction::new(test_tx_bytes.clone()).unwrap();
let serialized_tx_to_bytes = new_tx_from_bytes.serialize();
assert_eq!(test_tx_bytes, serialized_tx_to_bytes);
}
}
// impl From<BdkScript> for Script {
// fn from(bdk_script: BdkScript) -> Self {
// Script { inner: bdk_script }
// }
// }
//
// #[derive(Clone, Debug)]
// enum RbfValue {
// Default,
// Value(u32),
// }
//
// /// The result after calling the TxBuilder finish() function. Contains unsigned PSBT and
// /// transaction details.
// pub struct TxBuilderResult {
// pub(crate) psbt: Arc<PartiallySignedTransaction>,
// pub transaction_details: TransactionDetails,
// }
//
// uniffi::deps::static_assertions::assert_impl_all!(Wallet: Sync, Send);
//
// // 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 super::Transaction;
// use crate::Network::Regtest;
// use crate::{Address, Payload};
// use assert_matches::assert_matches;
// use bdk::bitcoin::hashes::hex::FromHex;
// use bdk::bitcoin::util::address::WitnessVersion;
//
// // Verify that bdk-ffi Transaction can be created from valid bytes and serialized back into the same bytes.
// #[test]
// fn test_transaction_serde() {
// let test_tx_bytes = Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700").unwrap();
// let new_tx_from_bytes = Transaction::new(test_tx_bytes.clone()).unwrap();
// let serialized_tx_to_bytes = new_tx_from_bytes.serialize();
// assert_eq!(test_tx_bytes, serialized_tx_to_bytes);
// }
//
// // Verify that bdk-ffi Address.payload includes expected WitnessProgram variant, version and program bytes.
// #[test]
// fn test_address_witness_program() {
// let address =
// Address::new("bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv".to_string()).unwrap();
// let payload = address.payload();
// assert_matches!(payload, Payload::WitnessProgram { version, program } => {
// assert_eq!(version,WitnessVersion::V0);
// assert_eq!(program, Vec::from_hex("04a6545885dd87b8e147cd327f2e9db362b72346").unwrap());
// });
// assert_eq!(address.network(), Regtest);
// }
// }

View File

@@ -1,111 +0,0 @@
use bdk::bitcoin::hashes::hex::ToHex;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::psbt::PsbtUtils;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use crate::{BdkError, FeeRate, Transaction};
#[derive(Debug)]
pub(crate) struct PartiallySignedTransaction {
pub(crate) internal: Mutex<BdkPartiallySignedTransaction>,
}
impl PartiallySignedTransaction {
pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
let psbt: BdkPartiallySignedTransaction =
BdkPartiallySignedTransaction::from_str(&psbt_base64)?;
Ok(PartiallySignedTransaction {
internal: Mutex::new(psbt),
})
}
pub(crate) fn serialize(&self) -> String {
let psbt = self.internal.lock().unwrap().clone();
psbt.to_string()
}
pub(crate) fn txid(&self) -> String {
let tx = self.internal.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.internal.lock().unwrap().clone().extract_tx();
Arc::new(Transaction { internal: tx })
}
/// 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.internal.lock().unwrap().clone();
let mut original_psbt = self.internal.lock().unwrap().clone();
original_psbt.combine(other_psbt)?;
Ok(Arc::new(PartiallySignedTransaction {
internal: 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.internal.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.internal.lock().unwrap().fee_rate().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::wallet::{TxBuilder, Wallet};
use bdk::wallet::get_funded_wallet;
use std::sync::Mutex;
#[test]
fn test_psbt_fee() {
let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
let test_wallet = Wallet {
wallet_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()
.fee_rate(2.0)
.drain_wallet()
.drain_to(drain_to_script.clone());
//dbg!(&tx_builder);
assert!(tx_builder.drain_wallet);
assert_eq!(tx_builder.drain_to, Some(drain_to_script.script.clone()));
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
assert!(tx_builder_result.psbt.fee_rate().is_some());
assert_eq!(
tx_builder_result.psbt.fee_rate().unwrap().as_sat_per_vb(),
2.682927
);
assert!(tx_builder_result.psbt.fee_amount().is_some());
assert_eq!(tx_builder_result.psbt.fee_amount().unwrap(), 220);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,5 @@ class TestBdk(unittest.TestCase):
def test_some_enum(self):
network = Network.TESTNET
def test_some_dict(self):
a = AddressInfo(index=42, address="testaddress")
self.assertEqual(42, a.index)
self.assertEqual("testaddress", a.address)
if __name__=='__main__':
unittest.main()

View File

@@ -1,8 +1,5 @@
uniffi_macros::build_foreign_language_testcases!(
["src/bdk.udl",],
[
"tests/bindings/test.kts",
"tests/bindings/test.swift",
"tests/bindings/test.py"
]
uniffi::build_foreign_language_testcases!(
"tests/bindings/test.kts",
"tests/bindings/test.swift",
"tests/bindings/test.py",
);

View File

@@ -0,0 +1,3 @@
fn main() {
uniffi::uniffi_bindgen_main()
}

View File

@@ -1,4 +1,4 @@
# bdk-android
# bdk-jvm
This project builds a .jar package for the JVM platform that provide Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository.
## How to Use
@@ -13,20 +13,20 @@ dependencies {
}
```
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
You may then import and use the `org.bitcoindevkit` library in your Kotlin code like so. Note that this example is for the `0.30.0` release. For examples of the 1.0 API in the alpha releases, take a look at the tests [here](https://github.com/bitcoindevkit/bdk-ffi/tree/master/bdk-jvm/lib/src/test/kotlin/org/bitcoindevkit).
```kotlin
import org.bitcoindevkit.*
// ...
val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
val externalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
val internalDescriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)", Network.TESTNET)
val databaseConfig = DatabaseConfig.Memory
val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
)
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
)
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
```
@@ -38,32 +38,39 @@ repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
implementation("org.bitcoindevkit:bdk-jvm:<version-SNAPSHOT>")
dependencies {
implementation("org.bitcoindevkit:bdk-jvm:<version-SNAPSHOT>")
}
```
## Example Projects
* [Tatooine Faucet](https://github.com/thunderbiscuit/tatooine)
## How to build
_Note that Kotlin version `1.6.10` or later is required to build the library._
1. Clone this repository.
1. Install JDK 11. It must be version 11 (not 17), otherwise it won't build. For example, with SDKMAN!:
```shell
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 11.0.19-tem
```
2. Install Rust (note that we are currently building using Rust 1.73.0):
```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default 1.73.0
```
3. Clone this repository.
```shell
git clone https://github.com/bitcoindevkit/bdk-ffi
```
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
3. If building on macOS install required intel and m1 jvm targets
4. If building on macOS install required intel and m1 jvm targets
```sh
rustup target add x86_64-apple-darwin aarch64-apple-darwin
```
4. Build kotlin bindings
```sh
# build JVM library
./gradlew buildJvmLib
```
5. Build kotlin bindings
```sh
./gradlew buildJvmLib
```
## How to publish to your local Maven repo
```shell
@@ -88,6 +95,7 @@ Depending on the JVM version you use, you might not have the JNA dependency on y
```shell
class file for com.sun.jna.Pointer not found
```
The solution is to add JNA as a dependency like so:
```kotlin
dependencies {

View File

@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx1536m
android.enableJetifier=true
kotlin.code.style=official
libraryVersion=0.27.0-SNAPSHOT
libraryVersion=1.0.0-alpha.2a

View File

@@ -20,21 +20,39 @@ repositories {
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
withSourcesJar()
withJavadocJar()
}
tasks.withType<Test> {
useJUnitPlatform()
// This block ensures that the tests that require access to a blockchain are not
// run if the -P excludeConnectedTests flag is passed to gradle.
// This ensures our CI runs are not fickle by not requiring access to testnet.
// This is a workaround until we have a proper regtest setup for the CI.
// Note that the command in the CI is ./gradlew test -P excludeConnectedTests
tasks.test {
if (project.hasProperty("excludeConnectedTests")) {
exclude("**/LiveWalletTest.class")
exclude("**/LiveTxBuilderTest.class")
}
}
testing {
suites {
val test by getting(JvmTestSuite::class) {
useKotlinTest("1.6.10")
}
}
}
tasks.withType<Test> {
testLogging {
events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
exceptionFormat = FULL
showExceptions = true
showCauses = true
showStackTraces = true
showCauses = true
}
}
@@ -74,14 +92,9 @@ afterEvaluate {
}
developers {
developer {
id.set("notmandatory")
name.set("Steve Myers")
email.set("notmandatory@noreply.github.org")
}
developer {
id.set("artfuldev")
name.set("Sudarsan Balaji")
email.set("sudarsan.balaji@artfuldev.com")
id.set("bdkdevelopers")
name.set("Bitcoin Dev Kit Developers")
email.set("dev@bitcoindevkit.org")
}
}
scm {
@@ -107,4 +120,8 @@ signing {
// binaries before running the tests
tasks.withType<KotlinCompile> {
dependsOn("buildJvmLib")
kotlinOptions {
jvmTarget = "11"
}
}

View File

@@ -1,73 +0,0 @@
package org.bitcoindevkit
import org.junit.Assert.*
import org.junit.Test
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.File
import java.nio.file.Files
/**
* Library test, which will execute on linux host.
*/
class JvmLibTest {
private fun getTestDataDir(): String {
return Files.createTempDirectory("bdk-test").toString()
}
private fun cleanupTestDataDir(testDataDir: String) {
File(testDataDir).deleteRecursively()
}
class LogProgress : Progress {
private val log: Logger = LoggerFactory.getLogger(JvmLibTest::class.java)
override fun update(progress: Float, message: String?) {
log.debug("Syncing...")
}
}
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
private val databaseConfig = DatabaseConfig.Memory
private val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig(
"ssl://electrum.blockstream.info:60002",
null,
5u,
null,
100u,
true,
)
)
@Test
fun memoryWalletNewAddress() {
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val address = wallet.getAddress(AddressIndex.New).address
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
}
@Test
fun memoryWalletSyncGetBalance() {
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val blockchain = Blockchain(blockchainConfig)
wallet.sync(blockchain, LogProgress())
val balance: Balance = wallet.getBalance()
assertTrue(balance.total > 0u)
}
@Test
fun sqliteWalletSyncGetBalance() {
val testDataDir = getTestDataDir() + "/bdk-wallet.sqlite"
val databaseConfig = DatabaseConfig.Sqlite(SqliteDbConfiguration(testDataDir))
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val blockchain = Blockchain(blockchainConfig)
wallet.sync(blockchain, LogProgress())
val balance: Balance = wallet.getBalance()
assertTrue(balance.total > 0u)
cleanupTestDataDir(testDataDir)
}
}

View File

@@ -0,0 +1,29 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class LiveTxBuilderTest {
@Test
fun testTxBuilder() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
assert(wallet.getBalance().total() > 0uL)
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(2.0f)
.finish(wallet)
println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
}
}

View File

@@ -0,0 +1,53 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertTrue
class LiveWalletTest {
@Test
fun testSyncedBalance() {
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet: Wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient: EsploraClient = EsploraClient("https://mempool.space/testnet/api")
// val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
assert(wallet.getBalance().total() > 0uL)
}
@Test
fun testBroadcastTransaction() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet.newNoPersist(descriptor, null, Network.TESTNET)
val esploraClient = EsploraClient("https://mempool.space/testnet/api")
val update = esploraClient.scan(wallet, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total()}")
println("New address: ${wallet.getAddress(AddressIndex.New).address.asString()}")
assert(wallet.getBalance().total() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)
val psbt: PartiallySignedTransaction = TxBuilder()
.addRecipient(recipient.scriptPubkey(), 4200uL)
.feeRate(2.0f)
.finish(wallet)
println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
val walletDidSign = wallet.sign(psbt)
assertTrue(walletDidSign)
val tx: Transaction = psbt.extractTx()
println("Txid is: ${tx.txid()}")
esploraClient.broadcast(tx)
}
}

View File

@@ -0,0 +1,18 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
class OfflineDescriptorTest {
@Test
fun testDescriptorBip86() {
val mnemonic: Mnemonic = Mnemonic.fromString("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
assertEquals(
expected = "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
actual = descriptor.asString()
)
}
}

View File

@@ -0,0 +1,53 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
class OfflineWalletTest {
@Test
fun testDescriptorBip86() {
val mnemonic: Mnemonic = Mnemonic(WordCount.WORDS12)
val descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(Network.TESTNET, mnemonic, null)
val descriptor: Descriptor = Descriptor.newBip86(descriptorSecretKey, KeychainKind.EXTERNAL, Network.TESTNET)
assertTrue(descriptor.asString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
}
@Test
fun testNewAddress() {
val descriptor: Descriptor = Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Network.TESTNET
)
val wallet: Wallet = Wallet.newNoPersist(
descriptor,
null,
Network.TESTNET
)
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
assertEquals(
expected = "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e",
actual = addressInfo.address.asString()
)
}
@Test
fun testBalance() {
val descriptor: Descriptor = Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Network.TESTNET
)
val wallet: Wallet = Wallet.newNoPersist(
descriptor,
null,
Network.TESTNET
)
assertEquals(
expected = 0uL,
actual = wallet.getBalance().total()
)
}
}

View File

@@ -4,11 +4,13 @@ package org.bitcoindevkit.plugins
val operatingSystem: OS = when {
System.getProperty("os.name").contains("mac", ignoreCase = true) -> OS.MAC
System.getProperty("os.name").contains("linux", ignoreCase = true) -> OS.LINUX
System.getProperty("os.name").contains("windows", ignoreCase = true) -> OS.WINDOWS
else -> OS.OTHER
}
enum class OS {
MAC,
LINUX,
WINDOWS,
OTHER,
}

View File

@@ -8,6 +8,8 @@ import org.gradle.kotlin.dsl.getValue
import org.gradle.kotlin.dsl.provideDelegate
import org.gradle.kotlin.dsl.register
// TODO 18: Migrate hard coded strings to constants all in the same location so they're at least easy
// to find and reason about.
internal class UniFfiJvmPlugin : Plugin<Project> {
override fun apply(target: Project): Unit = target.run {
@@ -27,13 +29,20 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "aarch64-apple-darwin")
args(cargoArgs)
}
} else if(operatingSystem == OS.LINUX) {
} else if (operatingSystem == OS.LINUX) {
exec {
workingDir("${project.projectDir}/../../bdk-ffi")
executable("cargo")
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-unknown-linux-gnu")
args(cargoArgs)
}
} else if (operatingSystem == OS.WINDOWS) {
exec {
workingDir("${project.projectDir}/../../bdk-ffi")
executable("cargo")
val cargoArgs: List<String> = listOf("build", "--profile", "release-smaller", "--target", "x86_64-pc-windows-msvc")
args(cargoArgs)
}
}
}
@@ -70,13 +79,25 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
ext = "so"
)
)
} else if (operatingSystem == OS.WINDOWS) {
libsToCopy.add(
CopyMetadata(
targetDir = "x86_64-pc-windows-msvc",
resDir = "win32-x86-64",
ext = "dll"
)
)
}
val libName = when (operatingSystem) {
OS.WINDOWS -> "bdkffi"
else -> "libbdkffi"
}
libsToCopy.forEach {
doFirst {
copy {
with(it) {
from("${project.projectDir}/../../target/${this.targetDir}/release-smaller/libbdkffi.${this.ext}")
from("${project.projectDir}/../../bdk-ffi/target/${this.targetDir}/release-smaller/${libName}.${this.ext}")
into("${project.projectDir}/../../bdk-jvm/lib/src/main/resources/${this.resDir}/")
}
}
@@ -89,18 +110,24 @@ internal class UniFfiJvmPlugin : Plugin<Project> {
dependsOn(moveNativeJvmLibs)
workingDir("${project.projectDir}/../../bdk-ffi")
// TODO 2: Is the Windows name the correct one?
// TODO 3: This will not work on mac Intel (x86_64 architecture)
// val libraryPath = when (operatingSystem) {
// OS.LINUX -> "./target/x86_64-unknown-linux-gnu/release-smaller/libbdkffi.so"
// OS.MAC -> "./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib"
// OS.WINDOWS -> "./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll"
// else -> throw Exception("Unsupported OS")
// }
// workingDir("${project.projectDir}/../../bdk-ffi/")
// val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "--library", libraryPath, "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin/", "--no-format")
// The code above was for the migration to uniffi 0.24.3 using the --library flag
// The code below works with uniffi 0.23.0
workingDir("${project.projectDir}/../../bdk-ffi/")
val cargoArgs: List<String> = listOf("run", "--bin", "uniffi-bindgen", "generate", "src/bdk.udl", "--language", "kotlin", "--out-dir", "../bdk-jvm/lib/src/main/kotlin", "--no-format")
executable("cargo")
args(
"run",
"--package",
"bdk-ffi-bindgen",
"--",
"--language",
"kotlin",
"--out-dir",
"../bdk-jvm/lib/src/main/kotlin"
)
args(cargoArgs)
doLast {
println("JVM bindings file successfully created")

3
bdk-python/MANIFEST.in Normal file
View File

@@ -0,0 +1,3 @@
include ./src/bdkpython/libbdkffi.dylib
include ./src/bdkpython/libbdkffi.so
include ./src/bdkpython/bdkffi.dll

View File

@@ -11,11 +11,11 @@ pip install bdkpython
## Run the tests
```shell
pip3 install --requirement requirements.txt
bash ./generate.sh
python3 setup.py --verbose bdist_wheel
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl
python -m unittest --verbose tests/test_bdk.py
pip install --requirement requirements.txt
bash ./scripts/generate-linux.sh # here you should run the script appropriate for your platform
python setup.py bdist_wheel --verbose
pip install ./dist/bdkpython-<yourversion>.whl --force-reinstall
python -m unittest --verbose
```
## Build the package
@@ -23,26 +23,14 @@ python -m unittest --verbose tests/test_bdk.py
# Install dependencies
pip install --requirement requirements.txt
# Generate the bindings first
bash generate.sh
# Generate the bindings (use the script appropriate for your platform)
bash ./scripts/generate-linux.sh
# Build the wheel
python3 setup.py --verbose bdist_wheel
```
## Run tox to build and test locally
```shell
# install dev requirements
pip install --requirement requirements-dev.txt
# build bindings glue code (located at .bdk-python/src/bdkpython/bdk.py)
source ./generate.sh
# build and test
tox -vv
python setup.py --verbose bdist_wheel
```
## Install locally
```shell
pip install ./dist/bdkpython-<yourversion>-py3-none-any.whl
pip install ./dist/bdkpython-<yourversion>.whl
```

View File

@@ -1,11 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR=$(dirname "$(realpath $0)")
PY_SRC="${SCRIPT_DIR}/src/bdkpython/"
echo "Generating bdk.py..."
# GENERATE_PYTHON_BINDINGS_OUT="$PY_SRC" GENERATE_PYTHON_BINDINGS_FIXUP_LIB_PATH=bdkffi cargo run --manifest-path ./bdk-ffi/Cargo.toml --release --bin generate --features generate-python
# BDKFFI_BINDGEN_PYTHON_FIXUP_PATH=bdkffi cargo run --manifest-path ./bdk-ffi/Cargo.toml --package bdk-ffi-bindgen -- --language python --udl-file ./bdk-ffi/src/bdk.udl --out-dir ./src/bdkpython/
BDKFFI_BINDGEN_OUTPUT_DIR="$PY_SRC" BDKFFI_BINDGEN_PYTHON_FIXUP_PATH=bdkffi cargo run --manifest-path ../bdk-ffi/Cargo.toml --package bdk-ffi-bindgen -- --language python --udl-file ../bdk-ffi/src/bdk.udl

View File

@@ -1,3 +1,4 @@
semantic-version==2.9.0
setuptools-rust==1.1.2
typing_extensions==4.0.1
setuptools==67.4.0
wheel==0.38.4

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
${PYBIN}/python --version
${PYBIN}/pip install -r requirements.txt
echo "Generating bdk.py..."
cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..."
rustup default 1.73.0
cargo build --profile release-smaller
echo "Copying linux libbdkffi.so..."
cp ./target/release-smaller/libbdkffi.so ../bdk-python/src/bdkpython/libbdkffi.so
echo "All done!"

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
python3 --version
pip install --user -r requirements.txt
echo "Generating bdk.py..."
cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..."
rustup default 1.73.0
rustup target add aarch64-apple-darwin
cargo build --profile release-smaller --target aarch64-apple-darwin
echo "Copying libraries libbdkffi.dylib..."
cp ./target/aarch64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
echo "All done!"

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
python3 --version
pip install --user -r requirements.txt
echo "Generating bdk.py..."
cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..."
rustup default 1.73.0
rustup target add x86_64-apple-darwin
cargo build --profile release-smaller --target x86_64-apple-darwin
echo "Copying libraries libbdkffi.dylib..."
cp ./target/x86_64-apple-darwin/release-smaller/libbdkffi.dylib ../bdk-python/src/bdkpython/libbdkffi.dylib
echo "All done!"

View File

@@ -0,0 +1,19 @@
#!/usr/bin/env bash
set -euo pipefail
python3 --version
pip install --user -r requirements.txt
echo "Generating bdk.py..."
cd ../bdk-ffi/
cargo run --bin uniffi-bindgen generate src/bdk.udl --language python --out-dir ../bdk-python/src/bdkpython/ --no-format
echo "Generating native binaries..."
rustup default 1.73.0
rustup target add x86_64-pc-windows-msvc
cargo build --profile release-smaller --target x86_64-pc-windows-msvc
echo "Copying libraries bdkffi.dll..."
cp ./target/x86_64-pc-windows-msvc/release-smaller/bdkffi.dll ../bdk-python/src/bdkpython/bdkffi.dll
echo "All done!"

View File

@@ -1,9 +1,6 @@
#!/usr/bin/env python
import os
from setuptools import setup
from setuptools_rust import Binding, RustExtension
LONG_DESCRIPTION = """# bdkpython
The Python language bindings for the [Bitcoin Dev Kit](https://github.com/bitcoindevkit).
@@ -16,59 +13,22 @@ pip install bdkpython
## Simple example
```python
import bdkpython as bdk
descriptor = bdk.Descriptor("wpkh(tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/84h/0h/0h/0/*)", bdk.Network.TESTNET)
db_config = bdk.DatabaseConfig.MEMORY()
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
bdk.ElectrumConfig(
"ssl://electrum.blockstream.info:60002",
None,
5,
None,
100,
True,
)
)
blockchain = bdk.Blockchain(blockchain_config)
wallet = bdk.Wallet(
descriptor=descriptor,
change_descriptor=None,
network=bdk.Network.TESTNET,
database_config=db_config,
)
# print new receive address
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
address = address_info.address
index = address_info.index
print(f"New BIP84 testnet address: {address} at index {index}")
# print wallet balance
wallet.sync(blockchain, None)
balance = wallet.get_balance()
print(f"Wallet balance is: {balance.total}")
"""
rust_ext = RustExtension(
target="bdkpython.bdkffi",
path="../bdk-ffi/Cargo.toml",
binding=Binding.NoBinding,
)
setup(
name='bdkpython',
version='0.27.0.dev0',
name="bdkpython",
version="1.0.0a2.dev0",
description="The Python language bindings for the Bitcoin Development Kit",
long_description=LONG_DESCRIPTION,
long_description_content_type='text/markdown',
rust_extensions=[rust_ext],
long_description_content_type="text/markdown",
include_package_data = True,
zip_safe=False,
packages=['bdkpython'],
package_dir={'bdkpython': './src/bdkpython'},
packages=["bdkpython"],
package_dir={"bdkpython": "./src/bdkpython"},
url="https://github.com/bitcoindevkit/bdk-ffi",
author="Alekos Filini <alekos.filini@gmail.com>, Steve Myers <steve@notmandatory.org>",
author="Bitcoin Dev Kit Developers <dev@bitcoindevkit.org>",
license="MIT or Apache 2.0",
# This is required to ensure the library name includes the python version, abi, and platform tags
# See issue #350 for more information
has_ext_modules=lambda: True,
)

View File

@@ -1 +1 @@
from bdkpython.bdk import *
from bdkpython.bdk import *

View File

View File

@@ -1,48 +0,0 @@
import bdkpython as bdk
import unittest
descriptor = bdk.Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", bdk.Network.TESTNET)
db_config = bdk.DatabaseConfig.MEMORY()
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
bdk.ElectrumConfig(
"ssl://electrum.blockstream.info:60002",
None,
5,
None,
100,
True,
)
)
blockchain = bdk.Blockchain(blockchain_config)
class TestSimpleBip84Wallet(unittest.TestCase):
def test_address_bip84_testnet(self):
wallet = bdk.Wallet(
descriptor=descriptor,
change_descriptor=None,
network=bdk.Network.TESTNET,
database_config=db_config
)
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
address = address_info.address
# print(f"New address is {address}")
assert address == "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", f"Wrong address {address}, should be tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"
def test_wallet_balance(self):
wallet = bdk.Wallet(
descriptor=descriptor,
change_descriptor=None,
network=bdk.Network.TESTNET,
database_config=db_config,
)
wallet.sync(blockchain, None)
balance = wallet.get_balance()
# print(f"Balance is {balance.total} sat")
assert balance.total > 0, "Balance is 0, send testnet coins to tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,38 @@
import bdkpython as bdk
import unittest
class TestLiveTxBuilder(unittest.TestCase):
def test_tx_builder(self):
descriptor: bdk.Descriptor = bdk.Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
bdk.Network.TESTNET
)
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
descriptor,
None,
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://mempool.space/testnet/api")
update = esploraClient.scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
)
wallet.apply_update(update)
self.assertGreater(wallet.get_balance().total(), 0)
recipient = bdk.Address(
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network = bdk.Network.TESTNET
)
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(2.0).finish(wallet)
# print(psbt.serialize())
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,63 @@
import bdkpython as bdk
import unittest
class TestLiveWallet(unittest.TestCase):
def test_synced_balance(self):
descriptor: bdk.Descriptor = bdk.Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
bdk.Network.TESTNET
)
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
descriptor,
None,
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://mempool.space/testnet/api")
update = esploraClient.scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
)
wallet.apply_update(update)
self.assertGreater(wallet.get_balance().total(), 0)
def test_broadcast_transaction(self):
descriptor: bdk.Descriptor = bdk.Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
bdk.Network.TESTNET
)
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
descriptor,
None,
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://mempool.space/testnet/api")
update = esploraClient.scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
)
wallet.apply_update(update)
self.assertGreater(wallet.get_balance().total(), 0)
recipient = bdk.Address(
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network = bdk.Network.TESTNET
)
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(2.0).finish(wallet)
# print(psbt.serialize())
self.assertTrue(psbt.serialize().startswith("cHNi"), "The PSBT should start with cHNi")
walletDidSign = wallet.sign(psbt)
self.assertTrue(walletDidSign)
tx = psbt.extract_tx()
esploraClient.broadcast(tx)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,18 @@
import bdkpython as bdk
import unittest
class TestSimpleWallet(unittest.TestCase):
def test_descriptor_bip86(self):
mnemonic: bdk.Mnemonic = bdk.Mnemonic.from_string("space echo position wrist orient erupt relief museum myself grain wisdom tumble")
descriptor_secret_key: bdk.DescriptorSecretKey = bdk.DescriptorSecretKey(bdk.Network.TESTNET, mnemonic, None)
descriptor: bdk.Descriptor = bdk.Descriptor.new_bip86(descriptor_secret_key, bdk.KeychainKind.EXTERNAL, bdk.Network.TESTNET)
self.assertEqual(
"tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx",
descriptor.as_string()
)
if __name__ == '__main__':
unittest.main()

View File

@@ -0,0 +1,34 @@
import bdkpython as bdk
import unittest
class TestSimpleWallet(unittest.TestCase):
def test_new_address(self):
descriptor: bdk.Descriptor = bdk.Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
bdk.Network.TESTNET
)
wallet: Wallet = bdk.Wallet.new_no_persist(
descriptor,
None,
bdk.Network.TESTNET
)
address_info: bdk.AddressInfo = wallet.get_address(bdk.AddressIndex.NEW())
self.assertEqual("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address_info.address.as_string())
def test_balance(self):
descriptor: bdk.Descriptor = bdk.Descriptor(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
bdk.Network.TESTNET
)
wallet: bdk.Wallet = bdk.Wallet.new_no_persist(
descriptor,
None,
bdk.Network.TESTNET
)
self.assertEqual(wallet.get_balance().total(), 0)
if __name__ == '__main__':
unittest.main()

View File

@@ -1,16 +0,0 @@
[tox]
envlist =
py38
py39
[testenv]
usedevelop=true
deps =
-rrequirements.txt
-rrequirements-dev.txt
commands =
python3 setup.py -v build
python3 setup.py -v install
pytest --verbose --override-ini console_output_style=count
python3 setup.py --verbose bdist_wheel

View File

@@ -29,7 +29,9 @@ let package = Package(
.binaryTarget(name: "bdkFFI", path: "./bdkFFI.xcframework"),
.target(
name: "BitcoinDevKit",
dependencies: ["bdkFFI"]),
dependencies: ["bdkFFI"],
swiftSettings: [.unsafeFlags(["-suppress-warnings"])]
),
.testTarget(
name: "BitcoinDevKitTests",
dependencies: ["BitcoinDevKit"]),

View File

@@ -35,7 +35,7 @@ swift test
### Example Projects
* [BdkSwiftSample](https://github.com/futurepaul/BdkSwiftSample), iOS
* [BDKSwiftExampleWallet](https://github.com/bitcoindevkit/BDKSwiftExampleWallet), iOS
## How to Build and Publish

View File

@@ -1,15 +0,0 @@
import XCTest
@testable import BitcoinDevKit
final class BitcoinDevKitTests: XCTestCase {
func testMemoryWalletNewAddress() throws {
let desc = try Descriptor(
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
network: Network.regtest
)
let databaseConfig = DatabaseConfig.memory
let wallet = try Wallet.init(descriptor: desc, changeDescriptor: nil, network: Network.regtest, databaseConfig: databaseConfig)
let addressInfo = try wallet.getAddress(addressIndex: AddressIndex.new)
XCTAssertEqual(addressInfo.address, "bcrt1qzg4mckdh50nwdm9hkzq06528rsu73hjxytqkxs")
}
}

View File

@@ -0,0 +1,34 @@
import XCTest
@testable import BitcoinDevKit
final class LiveTxBuilderTests: XCTestCase {
func testTxBuilder() throws {
let descriptor = try Descriptor(
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
network: Network.testnet
)
let wallet = try Wallet.newNoPersist(
descriptor: descriptor,
changeDescriptor: nil,
network: .testnet
)
let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
let update = try esploraClient.scan(
wallet: wallet,
stopGap: 10,
parallelRequests: 1
)
try wallet.applyUpdate(update: update)
XCTAssertGreaterThan(wallet.getBalance().total(), UInt64(0), "Wallet must have positive balance, please add funds")
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
let psbt: PartiallySignedTransaction = try TxBuilder()
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
.feeRate(satPerVbyte: 2.0)
.finish(wallet: wallet)
print(psbt.serialize())
XCTAssertTrue(psbt.serialize().hasPrefix("cHNi"), "PSBT should start with cHNI")
}
}

View File

@@ -0,0 +1,65 @@
import XCTest
@testable import BitcoinDevKit
final class LiveWalletTests: XCTestCase {
func testSyncedBalance() throws {
let descriptor = try Descriptor(
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
network: Network.testnet
)
let wallet = try Wallet.newNoPersist(
descriptor: descriptor,
changeDescriptor: nil,
network: .testnet
)
let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
let update = try esploraClient.scan(
wallet: wallet,
stopGap: 10,
parallelRequests: 1
)
try wallet.applyUpdate(update: update)
XCTAssertGreaterThan(wallet.getBalance().total(), UInt64(0))
}
func testBroadcastTransaction() throws {
let descriptor = try Descriptor(
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
network: Network.testnet
)
let wallet = try Wallet.newNoPersist(
descriptor: descriptor,
changeDescriptor: nil,
network: .testnet
)
let esploraClient = EsploraClient(url: "https://mempool.space/testnet/api")
let update = try esploraClient.scan(
wallet: wallet,
stopGap: 10,
parallelRequests: 1
)
try wallet.applyUpdate(update: update)
XCTAssertGreaterThan(wallet.getBalance().total(), UInt64(0), "Wallet must have positive balance, please add funds")
print("Balance: \(wallet.getBalance().total())")
let recipient: Address = try Address(address: "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", network: .testnet)
let psbt: PartiallySignedTransaction = try
TxBuilder()
.addRecipient(script: recipient.scriptPubkey(), amount: 4200)
.feeRate(satPerVbyte: 2.0)
.finish(wallet: wallet)
print(psbt.serialize())
XCTAssertTrue(psbt.serialize().hasPrefix("cHNi"), "PSBT should start with cHNI")
let walletDidSign: Bool = try wallet.sign(psbt: psbt)
XCTAssertTrue(walletDidSign, "Wallet did not sign transaction")
let tx: Transaction = psbt.extractTx()
print(tx.txid())
try esploraClient.broadcast(transaction: tx)
}
}

View File

@@ -0,0 +1,20 @@
import XCTest
@testable import BitcoinDevKit
final class OfflineDescriptorTests: XCTestCase {
func testDescriptorBip86() throws {
let mnemonic: Mnemonic = try Mnemonic.fromString(mnemonic: "space echo position wrist orient erupt relief museum myself grain wisdom tumble")
let descriptorSecretKey: DescriptorSecretKey = DescriptorSecretKey(
network: Network.testnet,
mnemonic: mnemonic,
password: nil
)
let descriptor: Descriptor = Descriptor.newBip86(
secretKey: descriptorSecretKey,
keychain: KeychainKind.external,
network: Network.testnet
)
XCTAssertEqual(descriptor.asString(), "tr([be1eec8f/86'/1'/0']tpubDCTtszwSxPx3tATqDrsSyqScPNnUChwQAVAkanuDUCJQESGBbkt68nXXKRDifYSDbeMa2Xg2euKbXaU3YphvGWftDE7ozRKPriT6vAo3xsc/0/*)#m7puekcx")
}
}

View File

@@ -0,0 +1,33 @@
import XCTest
@testable import BitcoinDevKit
final class OfflineWalletTests: XCTestCase {
func testNewAddress() throws {
let descriptor: Descriptor = try Descriptor(
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
network: Network.testnet
)
let wallet: Wallet = try Wallet.newNoPersist(
descriptor: descriptor,
changeDescriptor: nil,
network: .testnet
)
let addressInfo: AddressInfo = wallet.getAddress(addressIndex: AddressIndex.new)
XCTAssertEqual(addressInfo.address.asString(), "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e")
}
func testBalance() throws {
let descriptor: Descriptor = try Descriptor(
descriptor: "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
network: Network.testnet
)
let wallet: Wallet = try Wallet.newNoPersist(
descriptor: descriptor,
changeDescriptor: nil,
network: .testnet
)
XCTAssertEqual(wallet.getBalance().total(), 0)
}
}

View File

@@ -2,39 +2,39 @@
# This script builds local swift-bdk Swift language bindings and corresponding bdkFFI.xcframework.
# The results of this script can be used for locally testing your SPM package adding a local package
# to your application pointing at the bdk-swift directory.
#
# Run the script from the repo root directory, ie: ./bdk-swift/build-local-swift.sh
rustup install nightly-x86_64-apple-darwin
rustup component add rust-src --toolchain nightly-x86_64-apple-darwin
rustup install 1.73.0
rustup component add rust-src
rustup target add aarch64-apple-ios x86_64-apple-ios
rustup target add aarch64-apple-ios-sim --toolchain nightly
rustup target add aarch64-apple-ios-sim
rustup target add aarch64-apple-darwin x86_64-apple-darwin
pushd bdk-ffi
mkdir -p Sources/BitcoinDevKit
cargo run --package bdk-ffi-bindgen -- --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit
popd
cargo run --bin uniffi-bindgen generate src/bdk.udl --language swift --out-dir ../bdk-swift/Sources/BitcoinDevKit --no-format
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-darwin
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-darwin
cargo build --package bdk-ffi --profile release-smaller --target x86_64-apple-ios
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios
cargo +nightly build --package bdk-ffi --release -Z build-std --target aarch64-apple-ios-sim
cargo build --package bdk-ffi --profile release-smaller --target aarch64-apple-ios-sim
mkdir -p target/lipo-ios-sim/release-smaller
lipo target/aarch64-apple-ios-sim/release/libbdkffi.a target/x86_64-apple-ios/release-smaller/libbdkffi.a -create -output target/lipo-ios-sim/release-smaller/libbdkffi.a
lipo target/aarch64-apple-ios-sim/release-smaller/libbdkffi.a target/x86_64-apple-ios/release-smaller/libbdkffi.a -create -output target/lipo-ios-sim/release-smaller/libbdkffi.a
mkdir -p target/lipo-macos/release-smaller
lipo target/aarch64-apple-darwin/release-smaller/libbdkffi.a target/x86_64-apple-darwin/release-smaller/libbdkffi.a -create -output target/lipo-macos/release-smaller/libbdkffi.a
popd
pushd bdk-swift
mv Sources/BitcoinDevKit/bdk.swift Sources/BitcoinDevKit/BitcoinDevKit.swift
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64/bdkFFI.framework/Headers
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/Headers
cp Sources/BitcoinDevKit/bdkFFI.h bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/Headers
cp ../target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
cp ../target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
cp ../target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
cp ../bdk-ffi/target/aarch64-apple-ios/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64/bdkFFI.framework/bdkFFI
cp ../bdk-ffi/target/lipo-ios-sim/release-smaller/libbdkffi.a bdkFFI.xcframework/ios-arm64_x86_64-simulator/bdkFFI.framework/bdkFFI
cp ../bdk-ffi/target/lipo-macos/release-smaller/libbdkffi.a bdkFFI.xcframework/macos-arm64_x86_64/bdkFFI.framework/bdkFFI
rm Sources/BitcoinDevKit/bdkFFI.h
rm Sources/BitcoinDevKit/bdkFFI.modulemap
#rm bdkFFI.xcframework.zip || true