Compare commits

..

358 Commits

Author SHA1 Message Date
thunderbiscuit
f2a29c5b05
chore: build python wheels using manylinux_2_28_x86_64 2024-07-28 08:26:55 -05:00
Matthew
34543311bb
chore: clean up import paths that use blockdata 2024-07-01 12:53:00 -05:00
thunderbiscuit
323eb08350
test: clean up persistence test for bdk-swift 2024-07-01 10:34:06 -04:00
thunderbiscuit
7e5897bd1c
chore: disable default-features on bdk_electrum dependency 2024-06-27 15:25:32 -04:00
thunderbiscuit
ffe0c8c1a4
feat: tighten visibility of inner fields not required by external users 2024-06-27 12:32:50 -04:00
thunderbiscuit
cddd5f25d8
refactor: use tuple struct construction for SqliteStore type 2024-06-27 12:17:00 -04:00
thunderbiscuit
f4366ac49f
refactor: remove unused stale code in errors module 2024-06-27 12:08:53 -04:00
thunderbiscuit
e2abc3620a
fix: add jvm live tests to exclude when using excludeConnectedTests property 2024-06-27 08:52:37 -04:00
thunderbiscuit
c66b48467a
test: add offline tests for wallet new_or_load method 2024-06-27 08:52:20 -04:00
thunderbiscuit
33026108a7
feat: add new_or_load method on wallet type 2024-06-26 13:05:26 -04:00
thunderbiscuit
92d40a9f46
fix: use ring flag on rustls dependency for electrum client 2024-06-26 11:30:02 -04:00
thunderbiscuit
3b89af5a6e
test: update tests for alpha 13 release 2024-06-24 10:15:46 -04:00
thunderbiscuit
f66f8417cf
feat: bump rust libraries to alpha 13 2024-06-24 10:15:30 -04:00
itorod
92aeeab436
chore: bump uniffi to 0.28.0 2024-06-24 13:39:37 +00:00
thunderbiscuit
a2794f25b0
test: fix live tests 2024-06-17 17:37:06 -04:00
thunderbiscuit
1a1920e7e3
chore: bump minimum supported android version to 24 2024-06-17 17:37:06 -04:00
Steve Myers
6642c5808b
chore(swift): rename build script to build-xcframework.sh and update to use xcodebuild tool 2024-06-17 17:30:33 -04:00
Matthew
e97e9b731c
feat: add display trait to descriptor 2024-06-13 10:08:45 -05:00
Matthew
94d31ff7ed
feat: add display trait to mnemonic 2024-06-12 20:10:06 -05:00
Matthew
84f1329e84
feat: add address from_script method 2024-06-11 10:04:15 -05:00
thunderbiscuit
efef60082b
feat: use display trait for string representation of address type 2024-06-10 14:35:03 -04:00
Matthew
53afd9c238
chore: callback to foreign trait 2024-06-07 19:30:03 -05:00
Matthew
b9128b0e6a
chore(swift): add new live tests to justfile and ci workflow 2024-06-06 09:00:57 -05:00
thunderbiscuit
9d3733389d
fix: remove ruby config in uniffi.toml 2024-06-05 21:18:04 -04:00
thunderbiscuit
65702f401b
feat: add sqlite error 2024-06-04 10:51:28 -04:00
thunderbiscuit
19b4e1159a
chore: update rust bdk_wallet to alpha 12
This commit also introduces the sqlite store and removes the flat file
 store
2024-06-04 09:11:32 -04:00
thunderbiscuit
e5e7aba208
chore: use serde_json exported from bdk_bitcoind_rpc 2024-05-29 14:57:01 -04:00
thunderbiscuit
9790d60324
feat: add json_serialize method on psbt type 2024-05-29 14:57:00 -04:00
thunderbiscuit
093eb1fc7e
feat: add json_serialize method on psbt type 2024-05-29 14:57:00 -04:00
thunderbiscuit
5ef2bf8a1e
feat: add fee method on psbt type 2024-05-29 14:57:00 -04:00
thunderbiscuit
7fc8da5451
feat: add psbt error 2024-05-29 14:57:00 -04:00
thunderbiscuit
5d41984377
chore: upgrade uniffi dependency to 0.27.1 2024-05-29 13:55:42 -04:00
Matthew
89f803a753
feat: inspect spks on request types 2024-05-29 12:14:17 -05:00
thunderbiscuit
1f7c782d49
chore: bump development versions to alpha 12 2024-05-27 15:12:13 -04:00
thunderbiscuit
af9346c4eb
docs: fix links in changelog 2024-05-27 15:11:06 -04:00
thunderbiscuit
5cf25a50c4
docs: add alpha 11 changes to changelog 2024-05-27 15:07:50 -04:00
thunderbiscuit
c6174199dd
test: fix swift tests to print amount in sats 2024-05-21 12:06:55 -04:00
thunderbiscuit
9c45254c3e
test: fix live android tests 2024-05-21 11:36:29 -04:00
thunderbiscuit
260a0a65b3
chore: bump library version to alpha 11 2024-05-21 11:35:46 -04:00
thunderbiscuit
72985f14ad
tests: update python tests to use amount type 2024-05-21 10:30:48 -04:00
thunderbiscuit
5e3e24906f
feat: add lock_time method on transaction type 2024-05-16 14:30:47 -04:00
thunderbiscuit
c702894143
feat: add output method on transaction type 2024-05-16 14:28:43 -04:00
thunderbiscuit
ecdd7c239b
feat: add input method on transaction type 2024-05-16 14:03:28 -04:00
thunderbiscuit
ca8a3d0471
refactor: streamline blockchain clients error names 2024-05-16 10:41:04 -04:00
thunderbiscuit
8f4c80cb98
test: add electrum client test 2024-05-16 10:17:51 -04:00
thunderbiscuit
4aec4b0434
feat: add broadcast method on electrum client 2024-05-16 10:17:51 -04:00
thunderbiscuit
1913c45ef9
feat: add sync method on electrum client 2024-05-16 10:17:51 -04:00
thunderbiscuit
815fe5f62d
feat: add full_scan method on electrum client 2024-05-16 10:17:50 -04:00
thunderbiscuit
8d30c86076
feat: add simple electrum client 2024-05-16 10:17:50 -04:00
thunderbiscuit
c88b33473b
test: add memory wallet test 2024-05-16 10:16:58 -04:00
thunderbiscuit
79e7ab73ea
feat: add memory wallet 2024-05-15 14:11:02 -04:00
Matthew
f169b1a52f
chore: standard capitalization in error messages 2024-05-14 16:33:03 -05:00
Matthew
97d9bb6fbf
chore: bump rust bdk to alpha 11 2024-05-14 14:44:26 -05:00
thunderbiscuit
f27bada9c9
test: better messages when tests fail for low balance 2024-05-10 12:45:56 -04:00
thunderbiscuit
1b0b50a954
test: use signet for live tests 2024-05-10 10:51:07 -04:00
thunderbiscuit
6e207802b2
ci: fix live tests workflow for swift library 2024-05-10 10:35:21 -04:00
thunderbiscuit
ac15ed7380
ci: explicitly use rust compiler 1.77.1 on python ci runs 2024-05-10 10:10:43 -04:00
thunderbiscuit
e9a76287c8
feat: expose commit method on wallet type 2024-05-08 12:22:49 -04:00
thunderbiscuit
72b5bfd4c9
feat: add sync and full_scan methods on esplora client 2024-05-07 15:49:50 -04:00
thunderbiscuit
19723240b7
chore: bump rust bdk to alpha 10 2024-05-07 15:46:49 -04:00
Matthew
7d951578d0
refactor: standardize justfile task names and parameters across projects 2024-05-06 14:57:26 -05:00
Matthew
b7fe91b003
refactor(error): alias types from external crates 2024-05-06 13:29:28 -05:00
thunderbiscuit
330dc96b8a
build: migrate uniffi bindings files generation to library mode 2024-05-03 09:43:26 -04:00
thunderbiscuit
5557bb94ea
fix: python test command in justfile 2024-05-03 09:43:20 -04:00
Matthew
9b5b96710e
chore: add from to cannotconnecterror 2024-05-02 13:12:37 -05:00
Matthew
75d155c67a
chore: add error tests 2024-05-01 15:48:26 -05:00
Matthew
6522dfdd26
chore: remove allow_shrinking 2024-05-01 11:31:28 -05:00
Matthew
ebaa6fda2f
feat: add bumpfee finish related error 2024-05-01 11:31:27 -05:00
thunderbiscuit
5e8271e158
refactor: remove pre-1.0 code 2024-04-30 16:13:18 -04:00
thunderbiscuit
431ab90f04
fix: set rust compiler version before running cargo commands in python 2024-04-30 11:46:01 -04:00
thunderbiscuit
d4736a64d1
build: stop build if android ndk root is not defined for android lib 2024-04-30 10:53:15 -04:00
thunderbiscuit
f31678bf37
chore: bump development versions 2024-04-29 11:49:55 -04:00
thunderbiscuit
8130a419f2
fix: adjust errors on keys module 2024-04-29 09:47:37 -04:00
thunderbiscuit
262704751c
feat: add check command to justfile 2024-04-29 09:39:37 -04:00
thunderbiscuit
00a8e1ba8b
refactor: use tuple struct for psbt type 2024-04-29 09:39:37 -04:00
thunderbiscuit
d0514f678e
refactor: use tuple struct for transaction type 2024-04-29 09:39:36 -04:00
thunderbiscuit
f6cc63539d
refactor: use tuple struct for address type 2024-04-29 09:39:36 -04:00
thunderbiscuit
df64a96dd2
refactor: use tuple struct for descriptorpublickey type 2024-04-29 09:39:35 -04:00
thunderbiscuit
4cd6a80ce0
refactor: use tuple struct for descriptorsecretkey type 2024-04-29 09:37:24 -04:00
thunderbiscuit
e48af63fe6
refactor: use tuple struct for Mnemonic type 2024-04-29 09:34:13 -04:00
thunderbiscuit
eff4abcbfb
feat: add weight method on transaction type 2024-04-29 09:20:31 -04:00
Matthew
e1a93379ce
feat: add descriptor key related error 2024-04-26 14:46:40 -05:00
Matthew
e609b57bff
feat: add finish related error 2024-04-25 17:07:20 -05:00
thunderbiscuit
282fcfce0a
refactor: move feerate type to bitcoin module 2024-04-24 21:39:08 -04:00
thunderbiscuit
4dd4e91ccd
fix: clippy warnings 2024-04-24 21:35:18 -04:00
thunderbiscuit
4f8b7f4ba5
build: use correct rustup command for ios library build script 2024-04-24 21:35:18 -04:00
thunderbiscuit
4d737d3393
build: use rust compiler 1.77.1 for all builds 2024-04-24 21:35:18 -04:00
thunderbiscuit
e14124b454
chore: bump dependencies in cargo lockfile 2024-04-24 21:35:12 -04:00
Matthew
0a75fc1279
feat: add derivation path related error 2024-04-24 13:59:23 -05:00
Matthew
6ac386c8df
feat: add mnemonic related error 2024-04-23 14:05:57 -05:00
Matthew
126bc61df6
feat: add apply_update related error 2024-04-23 10:52:46 -05:00
Matthew
c63e7ad392
feat: add sign related error 2024-04-22 12:10:50 -05:00
thunderbiscuit
edea8e8c80
docs: update release workflow 2024-04-19 11:39:03 -04:00
thunderbiscuit
6bc974ed2e
fix: add targetSdk variable to android defaultConfig gradle block 2024-04-19 10:48:40 -04:00
thunderbiscuit
2f7652b979
build: add clean task to all libraries 2024-04-19 10:45:28 -04:00
Matthew
fa17a862ce
feat: add broadcast related error 2024-04-19 08:36:07 -05:00
thunderbiscuit
77cfee718c
feat: add serialize method on Transaction type 2024-04-18 14:40:02 -04:00
thunderbiscuit
aa1c0de244
fix: use alpha 9 types for tests 2024-04-18 14:37:47 -04:00
thunderbiscuit
aee84f9634
fix: remove field name collision on esplora error for JVM 2024-04-17 15:09:32 -04:00
thunderbiscuit
a35fdaee61
chore: activate blocking-https-rustls feature on esplora-client dependency 2024-04-17 15:09:31 -04:00
thunderbiscuit
dccc85d8ff
build: add build command to bdk-ffi directory
This is simply to bring it in line with the other libraries.
2024-04-17 15:09:31 -04:00
thunderbiscuit
f1744d192f
chore: bump bdk version to alpha 9 2024-04-17 15:09:25 -04:00
thunderbiscuit
cacb78f4dc
docs: remove bdk-jvm and bdk-android examples from readmes 2024-04-15 13:36:54 -04:00
thunderbiscuit
54f3235254
feat: expose list_unspent and list_output methods on wallet 2024-04-11 11:45:02 -04:00
thunderbiscuit
806e29b93c
feat: expose get_tx method on wallet type 2024-04-11 11:44:51 -04:00
thunderbiscuit
97a104fd5f
feat: add descriptor related errors 2024-04-10 13:31:28 -04:00
thunderbiscuit
0e617bc986
feat: add psbt related errors 2024-04-10 13:28:56 -04:00
thunderbiscuit
ab2e97e782
feat: add transaction related errors 2024-04-10 13:26:43 -04:00
thunderbiscuit
84f2497aeb
feat: add address related errors 2024-04-10 13:25:35 -04:00
thunderbiscuit
ab87355f9d
docs: update readmes for jvm and android libraries 2024-04-10 11:37:53 -04:00
thunderbiscuit
a40702ebd9
build: fix skipping artifact signature task when publishing locally 2024-04-09 16:15:50 -04:00
thunderbiscuit
0eadff1327
build: small fixes related to gradle 8.7 upgrade 2024-04-09 16:15:08 -04:00
thunderbiscuit
78ef936369
build: specify jdk 17 for all android compilation tasks 2024-04-09 16:14:29 -04:00
thunderbiscuit
7b9e71714f
build: remove deprecated package declaration in android manifest 2024-04-09 16:13:19 -04:00
thunderbiscuit
f698d46392
feat: use bdk names for CanonicalTx and ChainPosition exposed structures 2024-04-09 12:36:35 -04:00
Matthew
ab9763bb58
feat: add transaction details 2024-04-06 21:25:43 -05:00
thunderbiscuit
aa035588a0
chore: bump bdk to alpha 8 and use bitcoin::FeeRate 2024-04-05 13:10:02 -04:00
thunderbiscuit
e774c94998
build: bump gradle wrapper version to 8.7 2024-04-05 13:08:32 -04:00
thunderbiscuit
5e41275f29
fix: use jdk 17 for all publishing tasks 2024-04-05 13:08:32 -04:00
thunderbiscuit
15ac8c8ffb
build: prune unused dependency repositories for android and jvm 2024-04-05 13:08:32 -04:00
thunderbiscuit
358b43c31e
docs: update readme for new kotlin version 2024-04-05 13:08:31 -04:00
thunderbiscuit
08f1957fe0
build: use jdk 17 for android and jvm builds 2024-04-05 13:08:31 -04:00
thunderbiscuit
a8128e5056
chore: bump kotlin version to 1.9.23 2024-04-05 13:08:31 -04:00
thunderbiscuit
b879bf4a50
build: update jvm build tools and configurations 2024-04-05 13:08:31 -04:00
thunderbiscuit
43ff1fb394
build: update android build tools and configurations 2024-04-05 13:08:31 -04:00
Matthew
a8541ecd40
test: add test for wallet creation error 2024-04-03 15:05:33 -05:00
thunderbiscuit
c666bb247a
feat: release python libraries for 3.11 and 3.12 2024-04-03 10:37:08 -04:00
thunderbiscuit
d066007d9c
build: use swift local build script from bdk-swift directory 2024-04-02 21:55:59 -04:00
Matthew
c6d17b77ec
refactor(wallet): use walleterror for error handling in try_get_internal_address 2024-04-02 16:52:02 -05:00
thunderbiscuit
9a996b1a94
docs: mention the just tool and new justfiles in root readme 2024-04-01 15:13:04 -04:00
thunderbiscuit
c68c13fbfa
docs: clean up unecessary docs in root readme 2024-04-01 15:07:36 -04:00
thunderbiscuit
0b851d441f
fix: remove --user flag in python generate scripts 2024-04-01 15:03:57 -04:00
thunderbiscuit
267685bd58
chore: add just files to streamline common tasks 2024-03-28 10:52:43 -04:00
thunderbiscuit
896303f487
docs: fix release issue template 2024-03-27 10:11:18 -04:00
thunderbiscuit
9ea9e9384c
chore: bump snapshot and development versions 2024-03-27 10:04:16 -04:00
thunderbiscuit
2e0cd058f4
docs: add alpha 7 release to changelog 2024-03-27 09:57:58 -04:00
thunderbiscuit
c3313fb922
ci: fix publish to maven central job name 2024-03-27 09:57:22 -04:00
thunderbiscuit
5fc30c6c26
chore: add developer information for jvm, android, and python libraries 2024-03-27 09:45:48 -04:00
thunderbiscuit
38ccbb17be
docs: fix setup.py code example 2024-03-26 11:57:50 -04:00
thunderbiscuit
89f36a6c88
docs: update bdk-jvm readme 2024-03-26 11:36:20 -04:00
thunderbiscuit
6ea0518a54
docs: update bdk-android readme 2024-03-26 11:35:15 -04:00
thunderbiscuit
738f53e991
chore: bump library version to alpha.7 2024-03-26 10:39:16 -04:00
thunderbiscuit
61e58240fc
feat: use thiserror library to handle error creation 2024-03-25 13:02:17 -04:00
thunderbiscuit
06fa9d751b
chore: bump rust bdk version to alpha 7 2024-03-25 13:00:39 -04:00
Matthew
145652beaa
fix: update bdk-swift framework info.plist for xcode 15.3 reqs 2024-03-25 11:39:58 -05:00
Matthew
2dd6ee33f1
fix: live tests 2024-03-06 10:30:42 -06:00
Matthew
b249dae875
feat: add esplora error 2024-03-06 10:09:46 -06:00
thunderbiscuit
43c1ca66b8
feat: add specific errors for wallet persistence 2024-02-16 16:09:10 -05:00
thunderbiscuit
68a9eb693d
test: fix tests to account for persistence 2024-02-16 16:09:10 -05:00
thunderbiscuit
6022a703c6
feat: add wallet persistence 2024-02-16 16:08:59 -05:00
Matthew
99a3d74a4a
chore: bump rust dependencies to alpha 6 versions 2024-02-15 11:53:50 -06:00
Matthew
50f102bbd3
chore: bump jna 2024-02-06 10:37:24 -06:00
Matthew
141705e2ed
chore: remove bdknetwork type 2024-02-05 13:55:47 -06:00
Matthew
619884eaed
chore: bump uniffi to 0.26 2024-02-05 13:55:47 -06:00
Matthew
794f2bc995
chore: bump rust dependencies to alpha 5 versions 2024-01-31 11:51:33 -06:00
Matthew
991de09219
chore: bump rust dependencies to alpha 4 versions 2024-01-24 09:49:26 -06:00
Matthew
6f4b4c9155
chore: remove swiftsettings from bdk-swift 2024-01-22 15:32:03 -06:00
thunderbiscuit
34c76feb71
docs: add 0.31.0 release to changelog 2024-01-22 08:42:09 -05:00
Matthew
8e9d2ddc14
feat: use FeeRate type in TxBuilder 2024-01-19 11:03:47 -06:00
thunderbiscuit
7319aea562
fix: rename esplora scan to full_scan 2024-01-11 13:14:50 -05:00
thunderbiscuit
54beb23f13
fix: fix clippy warnings 2024-01-10 17:23:10 -05:00
thunderbiscuit
ccf5fbda6e
feat: add generic alpha 3 error 2024-01-10 17:22:58 -05:00
thunderbiscuit
4f6c198168
chore: bump rust dependencies to alpha 3 versions 2024-01-10 17:22:34 -05:00
Matthew
3789c1dcd6
feat: add calculate_fee and calculate_fee_rate on wallet 2024-01-08 12:18:36 -06:00
Matthew
fc25cd709a
feat: add is_valid_for_network to address 2024-01-04 14:35:16 -06:00
thunderbiscuit
cdec63efa3
docs: remove 0.x api docs 2023-12-15 13:21:33 -05:00
thunderbiscuit
bcb77c45f4
refactor: use byref in udl instead of arc when possible 2023-12-15 13:19:06 -05:00
Matthew
a1a45996fc
feat: add transactions method on wallet 2023-12-15 11:03:25 -06:00
thunderbiscuit
bbc6e1a43c
test: add tests for more advanced txbuilder operations 2023-12-14 12:19:01 -05:00
thunderbiscuit
252bcaa444
feat: add bumpfeetxbuilder 2023-12-07 15:36:48 -05:00
thunderbiscuit
643d254ab6
feat: add enable_rbf_with_sequence method on txbuilder 2023-12-07 14:51:15 -05:00
thunderbiscuit
c79a39914e
feat: add enable_rbf method on txbuilder 2023-12-07 14:46:57 -05:00
thunderbiscuit
3df92527e9
feat: add drain_to method on txbuilder 2023-12-07 14:35:48 -05:00
thunderbiscuit
fe65c70915
feat: add unspendable method on txbuilder 2023-12-07 14:35:48 -05:00
thunderbiscuit
ce219e4ac4
feat: add fee_absolute method on txbuilder 2023-12-07 14:35:43 -05:00
thunderbiscuit
c9019e9fcf
docs: add information and examples to adr readme 2023-12-07 13:22:09 -05:00
thunderbiscuit
53b59d5100
docs: add wrapping ADR file 2023-12-07 13:22:09 -05:00
thunderbiscuit
4a3cf49f24
docs: add naming ADR file 2023-12-07 13:22:09 -05:00
thunderbiscuit
40940c38af
docs: add readme for adr directory 2023-12-07 13:22:09 -05:00
Matthew
46e0324f28
feat: add sent_and_received method on wallet 2023-12-07 10:32:15 -06:00
thunderbiscuit
6b177f0893
feat: add new types module 2023-12-06 13:37:08 -05:00
Matthew
05ce7dad31
refactor: restructure balance 2023-12-06 11:52:34 -06:00
thunderbiscuit
e5ded1a726
build: bump snapshot versions to alpha.2b 2023-11-21 15:14:53 -05:00
thunderbiscuit
022e29faaa
docs: update release issue template 2023-11-21 14:51:18 -05:00
thunderbiscuit
0c0dfd5f42
build: state rust targets explicitly in swift build script 2023-11-21 14:48:49 -05:00
thunderbiscuit
24e09a40cb
docs: update changelog for 1.0.0-alpha.2a release 2023-11-21 14:47:58 -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
1da01b4a0bcf5d6291cb78216520e68a8ea91048 ci: change rust stable version to 0.71.0 (Steve Myers)
f1ba03bf508a7110e2060934eb975558ee38ea13 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 1da01b4a0bcf5d6291cb78216520e68a8ea91048.

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
beb75dd5524c0f16f0715d979b685c56ba95c15d 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 beb75dd5524c0f16f0715d979b685c56ba95c15d

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
4e5537acd27808c631f51480581b4ff4e4927924 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
cd10c75e965584160235db8abb79540d55ce9c80 Add to_qr_uri() method on Address type (thunderbiscuit)
cbd44249f31dd5d92a7e5087f6bc5f2de99163b6 Add kotlin api-docs for Address.payload() and Address.network() (Steve Myers)
20c31d53838ad956c72f4f442654d894ee8d68e1 Expose Address payload and network properties (Steve Myers)
616cb21738d5c3c60743bf27cae3c27944ecd979 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 cd10c75e965584160235db8abb79540d55ce9c80.

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
cba69e681a2116c98895d021723617f71db1d0ca Clean up of From traits implementations (thunderbiscuit)
35d8fb3139e1de645e0299c27762f2fb938a99f6 Clean up extract_tx method on PSBT (thunderbiscuit)
f003a6275ec8b00d0bb3276722c6c6a260056aa4 Clean up conversion between BDK TransactionDetails to ffi TransactionDetails (thunderbiscuit)
2f62377eec547e0654899a19771ba44add7066ae Add Eq and PartialEq traits on the Transaction type (thunderbiscuit)
81e208222ad954f4df0402cad3276556a8dd676d Add uniffi/cli as default feature to cargo (thunderbiscuit)
3dc6596aa250430940c6946ce4b0b3146e717184 Add include_raw boolean parameter on wallet list_transactions method (thunderbiscuit)
2342265c26a158c38fa04bd837daefdd6a269f5f Remove unused NetworkLocalUtxo type (thunderbiscuit)
6c561228c2873398729c9f4dce49cd6f33ba4c9f Use the latest version of the Android NDK (thunderbiscuit)
e86909ab3d24ec5bd8aba983d792f7d191312885 Clean up input and output methods on Transaction type (thunderbiscuit)
8e51756a3ae770d0e271c39839765c9c8a2de733 Fix clippy errors (thunderbiscuit)
40263b425e49d979216a42ab78a42e8522ba1088 Remove deprecated bdk-ffi-bindgen tool (thunderbiscuit)
9437051668609a9809651e8fd7982510ef873148 Fix fmt and clippy errors (thunderbiscuit)
7557e214c86bd6ea96a224aae78d957746d9645e Add optional transaction field on the TransactionDetails type (thunderbiscuit)
40ca62086c3a15a014303659ea4d3f3794a0a088 Expose all fields on the Transaction type (thunderbiscuit)
e0506deffa155ed2bd9794e904bd10e42a523578 Add new utility methods on Transaction type (thunderbiscuit)
d3e183a498dcf0764544adeb0c8d2232b0f00df0 Add Kotlin API docs for new Transaction methods (thunderbiscuit)
1e9ecfbe52a254c0ac529d34fc3c84aefa43740c 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 cba69e681a2116c98895d021723617f71db1d0ca

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
thunderbiscuit
3128fad690
Update AddressIndex enum use in Python test and example 2023-02-18 09:53:13 -05:00
thunderbiscuit
a1b112cbbb
Remove license files from bdk-python 2023-02-18 09:42:33 -05:00
thunderbiscuit
553c337241
Update JVM readme 2023-02-17 17:31:00 -05:00
thunderbiscuit
90d12a96c5
Update Android readme to add section on x86 emulators 2023-02-17 16:00:41 -05:00
thunderbiscuit
5ca1d17adb
Fix Android tests to account for new AddressIndex sealed class 2023-02-17 15:49:20 -05:00
thunderbiscuit
f121372c73
Fix PartiallySignedTransaction type name in Kotlin API docs 2023-02-17 15:23:47 -05:00
Steve Myers
44a78cc459
Update bdk from 0.27 to 0.27.1 2023-02-17 09:14:28 -06:00
Steve Myers
2dbad2ddd5
Merge bitcoindevkit/bdk-ffi#308: Bump bdk version to 0.27 and bdk-ffi to 0.27.0
ec71ef58be96088f4aff8dbcd95f5b7d8125c080 Bump bdk version to 0.27 and bdk-ffi to 0.27.0 (thunderbiscuit)

Pull request description:

  ## Description
  This PR updates BDK to the latest version, `0.27.0`.

  ### Changelog notice
  ```txt
  - Update BDK to latest version 0.27 [#308]

  [#308](https://github.com/bitcoindevkit/bdk-ffi/pull/308)
  ```

  #### 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:
  notmandatory:
    ACK ec71ef58be96088f4aff8dbcd95f5b7d8125c080

Tree-SHA512: 2d97b2af5247a5021fb7e5db70a903ded243b8f298133da1b83f7610d4e8ffc5e8e1b65e60ae07a781334de9c1630dfc6f9d58f62ff240783c9c22afc1f8f907
2023-02-14 17:00:29 -06:00
thunderbiscuit
ec71ef58be
Bump bdk version to 0.27 and bdk-ffi to 0.27.0 2023-02-14 12:45:29 -06:00
thunderbiscuit
4ca7919ca9
Clean up samples in Kotlin API docs 2023-02-02 12:26:29 -05:00
thunderbiscuit
07aa1f8950
Add new AddressIndex variants to Kotlin API docs 2023-02-02 12:26:28 -05:00
Ed Ball
d42789db9b
fix: incorrect peek_address index 2023-02-02 12:26:27 -05:00
Ed Ball
10f893a4b3
test: reset_address 2023-02-02 12:26:26 -05:00
Ed Ball
13043065b6
expose: get_address reset method 2023-02-02 12:26:25 -05:00
Ed Ball
5fa0b916a2
test: more descriptive and complete peek_address tests 2023-02-02 12:26:24 -05:00
Ed Ball
7611a65620
fix jvm test AddressIndex class capitalisation 2023-02-02 12:26:23 -05:00
Ed Ball
458a162c2a
remove redundant to_string from tests 2023-02-02 12:26:22 -05:00
Ed Ball
d15783dba0
add missing bdk-ffi AddressIndex Peek docstrings 2023-02-02 12:26:20 -05:00
Ed Ball
2723577a84
test: get_address peek ffi tests 2023-02-02 12:26:14 -05:00
Ed Ball
7fefb8a7b9
expose: get_address peek 2023-02-02 12:25:42 -05:00
Ed Ball
aa842c3844
fix incorrect address in get_internal_address test 2023-02-01 20:30:24 +01:00
thunderbiscuit
df019c11ec
Add new getInternalAddress method to Kotlin API docs 2023-02-01 14:03:49 -05:00
Ed Ball
11d7d6b80f
test: get_internal_address LastUnused 2023-02-01 14:43:22 +01:00
Ed Ball
85f8a8a526
fix: redundant problematic to_string in get_internal_address tests 2023-02-01 14:37:36 +01:00
Ed Ball
5e75c856c5
test: get internal/external address 2023-01-31 22:25:30 +01:00
Ed Ball
b854c78dde
feat: expose get_internal_address 2023-01-28 19:10:54 +01:00
thunderbiscuit
f5d4750ae4
Fix Python CI release workflow 2023-01-24 13:22:58 -05:00
Steve Myers
f75ead02ff
Merge bitcoindevkit/bdk-ffi#296: Add Transaction struct, update PSBT and Blockchain to use it
8e54ada436f84fb5bbcd5f448b7fccc186387bb4 Add Transaction struct, update PSBT and Blockchain to use it (Steve Myers)

Pull request description:

  ### Description

  Add new `Transaction` structure that can be created from transaction consensus encoded bytes. Update `PartiallySignedTransaction.extract_tx()` to return a `Transaction` instead of a the transaction bytes. Update `Blockchain.broadcast()` to take a `Transaction` parameter.

  ### Notes to the reviewers

  Fixes #157.

  ### Changelog notice

  #### Added

  - New `Transaction` structure that can be created from or serialized to consensus encoded bytes.

  #### Changed

  - `PartiallySignedTransaction.extract_tx()` returns a `Transaction` instead of a the transaction bytes.
  - `Blockchain.broadcast()` takes a `Transaction` instead of a `PartiallySignedTransaction`.

  ### 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

  #### Bugfixes:

  * [ ] This pull request breaks the existing API
  * [ ] I've added tests to reproduce the issue which are now passing
  * [ ] I'm linking the issue being fixed by this PR

ACKs for top commit:
  thunderbiscuit:
    Tested ACK 8e54ada436f84fb5bbcd5f448b7fccc186387bb4. I ran a script using bdk-jvm and your test vector. Looks good! Another win for the LDK/BDK integration.

Tree-SHA512: f5bfdafad93f0b4ada0756a8e68bdb12f2de680d8ae8a5083dbbeded6fc314bd79c8913f0b7f01e2eb807676092777ad8bf9466996cd51fa9587b8bb53170ca8
2023-01-23 15:47:39 -06:00
Steve Myers
8e54ada436
Add Transaction struct, update PSBT and Blockchain to use it 2023-01-23 14:30:36 -06:00
thunderbiscuit
302ad8dea8
Move Rust tests to their respective files 2023-01-19 22:59:53 -05:00
thunderbiscuit
561e93f6dd
Add pycache directory to gitignore 2023-01-19 16:29:21 -05:00
thunderbiscuit
b8230799cf
Fix GitHub repository link in setup.py file 2023-01-19 16:11:52 -05:00
thunderbiscuit
d2bec60046
Bump development versions for JVM, Android, and Python 2023-01-17 15:49:46 -05:00
thunderbiscuit
aadc622006
Refactor: Fix up imports 2023-01-17 15:47:48 -05:00
thunderbiscuit
202dcfa2b5
Refactor: separate database from lib.rs 2023-01-17 15:37:10 -05:00
thunderbiscuit
46bd9a1f15
Refactor: separate keys from lib.rs 2023-01-17 15:35:20 -05:00
thunderbiscuit
6fcb8985f1
Refactor: separate descriptor from lib.rs 2023-01-17 15:29:23 -05:00
thunderbiscuit
dd58a9d548
Refactor: separate blockchain from lib.rs 2023-01-17 15:29:05 -05:00
thunderbiscuit
d2a4e2adba
Refactor: separate transaction builders from lib.rs 2023-01-17 15:18:55 -05:00
thunderbiscuit
cbc740c407
Refactor: separate psbt struct from lib.rs 2023-01-17 15:18:48 -05:00
thunderbiscuit
f50ecdb7e7
Refactor: separate wallet struct from lib.rs 2023-01-17 15:18:41 -05:00
Shobit Beltangdy
f01e0e30f3
expose estimate_fee api
bdk::blockchain::Blockchain contains an `estimate_fee` api. This commit adds the bindings for estimate_fee.

This will fix https://github.com/bitcoindevkit/bdk-ffi/issues/287

Tested this by adding a unit test in lib.rs. Also generated the python code and verified that it is able to invoke estimate_fee and get the fee rate
2023-01-13 16:47:12 -08:00
144 changed files with 9035 additions and 5885 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

@ -1,101 +0,0 @@
---
name: Minor Release
about: Create a new minor release [for release managers only]
title: 'Release MAJOR.MINOR+1.0'
labels: 'release'
assignees: ''
---
## Create a new minor release
### Summary
<--release summary to be used in announcements-->
### Commit
<--latest commit ID to include in this release-->
### Changelog
<--add notices from PRs merged since the prior release, see ["keep a changelog"]-->
### Checklist
Release numbering must follow [Semantic Versioning]. These steps assume the current `master`
branch **development** version is *MAJOR.MINOR.0*.
#### On the day of the feature freeze
Change the `master` branch to the next MINOR+1 version:
- [ ] Switch to the `master` branch.
- [ ] Create a new PR branch called `bump_dev_MAJOR_MINOR+1`, eg. `bump_dev_0_22`.
- [ ] Bump the `bump_dev_MAJOR_MINOR+1` branch to the next development MINOR+1 version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
- [ ] Create PR and merge the `bump_dev_MAJOR_MINOR+1` branch to `master`.
- Title PR "Bump version to MAJOR.MINOR+1.0".
Create a new release branch:
- [ ] Double check that your local `master` is up-to-date with the upstream repo.
- [ ] Create a new branch called `release/MAJOR.MINOR+1` from `master`.
Add a release candidate tag, this is optional and only needed for major `bdk-ffi` changes that
require a longer testing cycle:
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.1` version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.1`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.1".
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
- The tag name should be `vMAJOR.MINOR+1.0-rc.1`
- Use message "Release MAJOR.MINOR+1.0 rc.1".
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
- [ ] Push the `release/MAJOR.MINOR` branch and new tag to the `bitcoindevkit/bdk` repo.
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.1` tag.
If any issues need to be fixed before the *MAJOR.MINOR+1.0* version is released:
- [ ] Merge fix PRs to the `master` branch.
- [ ] Git cherry-pick fix commits to the `release/MAJOR.MINOR+1` branch.
- [ ] Verify fixes in `release/MAJOR.MINOR+1` branch.
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0-rc.x+1` version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0-rc.x+1`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0-rc.x+1".
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
- The tag name should be `vMAJOR.MINOR+1.0-rc.x+1`, where x is the current release candidate number.
- Use tag message "Release MAJOR.MINOR+1.0 rc.x+1".
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
- Use `git push --tags` option to push the new `vMAJOR.MINOR+1.0-rc.x+1` tag.
#### On the day of the release
Tag and publish new release:
- [ ] Bump the `release/MAJOR.MINOR+1` branch to `MAJOR.MINOR+1.0` version.
- Change the `Cargo.toml` version value to `MAJOR.MINOR+1.0`.
- The commit message should be "Bump version to MAJOR.MINOR+1.0".
- [ ] Add a tag to the `HEAD` commit in the `release/MAJOR.MINOR+1` branch.
- The tag name should be `vMAJOR.MINOR+1.0`
- The first line of the tag message should be "Release MAJOR.MINOR+1.0".
- In the body of the tag message put a copy of the **Summary** and **Changelog** for the release.
- Make sure the tag is signed, for extra safety use the explicit `--sign` flag.
- [ ] Wait for the CI to finish one last time.
- [ ] Push the new tag to the `bitcoindevkit/bdk` repo.
- [ ] Create the release on GitHub.
- Go to "tags", click on the dots on the right and select "Create Release".
- Set the title to `Release MAJOR.MINOR+1.0`.
- In the release notes body put the **Summary** and **Changelog**.
- Use the "+ Auto-generate release notes" button to add details from included PRs.
- Until we reach a `1.0.0` release check the "Pre-release" box.
- [ ] After downstream language repos are also updated announce the release, using the **Summary**,
on Discord, Twitter and Mastodon.
- [ ] Celebrate 🎉
[Semantic Versioning]: https://semver.org/
[crates.io]: https://crates.io/crates/bdk
[docs.rs]: https://docs.rs/bdk/latest/bdk
["keep a changelog"]: https://keepachangelog.com/en/1.0.0/

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/

95
.github/ISSUE_TEMPLATE/release.md vendored Normal file
View File

@ -0,0 +1,95 @@
---
name: Release
about: Create a new release [for release managers only]
title: 'Release MAJOR.MINOR.PATCH'
labels: 'release'
assignees: ''
---
# Part 1: Bump BDK Rust Version
1. - [ ] Open a PR with an update to `Cargo.toml` to the new bdk release candidate and ensure all CI workflows run correctly. Fix errors if necessary.
2. - [ ] Once the new bdk release is out, update the PR to replace the release candidate with the full release and merge.
# Part 2: Prepare Libraries for Release Branch
### _Android_
3. - [ ] Update the API docs to reflect the changes in the API
4. - [ ] Delete the `target` directory in bdk-ffi and all `build` directories (in root, `lib`, and `plugins`) in the bdk-android directory to make sure you're building the library from scratch.
5. - [ ] Build the library and run the offline and live tests, and adjust them if necessary (note that you'll need an Android emulator running).
```shell
# start an emulator prior to running the tests
cd ./bdk-android/
just clean
just build
just test
```
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 `build` directories (in root, `lib`, and `plugins`) in bdk-jvm directory to make sure you're building the library from scratch.
9. - [ ] Build the library and run the tests, and adjust if necessary
```shell
cd ./bdk-jvm/
just clean
just build
just test
```
10. - [ ] Update the readme if necessary
### _Swift_
11. - [ ] Delete the `target` directory in bdk-ffi
12. - [ ] Run the tests and adjust if necessary
```shell
cd ./bdk-swift/
just clean
just build
just test
```
13. - [ ] Update the readme if necessary
### _Python_
14. - [ ] Delete the `dist`, `build`, and `bdkpython.egg-info` and rust `target` directories to make sure you are building the library from scratch without any caches
15. - [ ] Build the library
```shell
cd ./bdk-python/
just clean
pip3 install --requirement requirements.txt
bash ./scripts/generate-macos-arm64.sh # run the script for your particular platform
python3 setup.py --verbose bdist_wheel
```
16. - [ ] Run the tests and adjust if necessary
```shell
pip3 install ./dist/bdkpython-<yourversion>-py3-none-any.whl --force-reinstall
python -m unittest --verbose
```
17. - [ ] Update the readme and `setup.py` if necessary
18. - [ ] Update the Android, JVM, Python, and Swift libraries as per the _Specific Libraries' Workflows_ section above. Open a single PR on master for all of these changes called `Prepare language bindings libraries for 0.X release`. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/315).
## Part 3: Release Workflow
19. - [ ] Create a new branch off of `master` called `release/<feature version>`, e.g. `release/0.31`
20. - [ ] Update bdk-android version from `SNAPSHOT` version to release version
21. - [ ] Update bdk-jvm version from `SNAPSHOT` version to release version
22. - [ ] Update bdk-python version from `.dev` version to release version
23. - [ ] Open a PR to that release branch that updates the Android, JVM, and Python libraries' versions in the three steps above. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/316).
24. - [ ] Get a review and ACK and merge the PR updating all the languages to their release versions
25. - [ ] Create the tag for the release and make sure to add the changelog info to the tag (works better if you prepare the tag message on the side in a text editor). Push the tag to GitHub.
```shell
git tag v0.6.0 --sign --edit
git push upstream v0.6.0
```
26. - [ ] Trigger manual releases for all 4 libraries (for Swift, go on the [bdk-swift](https://github.com/bitcoindevkit/bdk-swift) trigger the release on `master` and simply add the version number and tag name in the text fields when running the workflow manually. Note that the version number must not contain the `v`, i.e. `0.26.0`, but the tag will have it, i.e. `v0.26.0`).
27. - [ ] Make sure the released libraries work and contain the artifacts you would expect
28. - [ ] Aggregate all the changelog notices from the PRs and add them to the changelog file
29. - [ ] Bump the versions on master from `0.9.0-SNAPSHOT` to `0.10.0-SNAPSHOT`, `0.6.0.dev0` to `0.7.0.dev0`
30. - [ ] Apply changes to the minor_release and patch_release issue templates if they need any
31. - [ ] Open a PR on master with the changes in steps 29, 30, and 31. See [example PR here](https://github.com/bitcoindevkit/bdk-ffi/pull/317). Get a review and merge the PR.
32. - [ ] Make release on GitHub (set as pre-release and generate auto release notes between the previous tag and the new one)
33. - [ ] Post in the announcement channel
34. - [ ] Tweet about the library

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.77.1
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.14.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

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

@ -0,0 +1,96 @@
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.77.1"
run: rustup default 1.77.1
- 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"
working-directory: bdk-swift
run: bash ./build-xcframework.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
- name: "Install Rust 1.77.1"
uses: actions-rs/toolchain@v1
with:
toolchain: 1.77.1
- 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
java-version: 17
- name: Install rust android targets
- name: "Set default Rust version to 1.77.1"
run: rustup default 1.77.1
- name: "Install Rust Android targets"
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
- 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 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
java-version: 17
- name: Install aarch64 Rust target
- name: "Set default Rust version to 1.77.1"
run: rustup default 1.77.1
- name: "Install aarch64 Rust target"
run: rustup target add aarch64-apple-darwin
- 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: 17
- name: "Set default Rust version to 1.77.1"
run: rustup default 1.77.1
- name: "Install x86_64-pc-windows-msvc Rust target"
run: rustup target add x86_64-pc-windows-msvc
- 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
java-version: 17
- name: Build bdk-jvm library
- name: "Set default Rust version to 1.77.1"
run: rustup default 1.77.1
- 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,143 +1,179 @@
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
# 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
build-manylinux_2_28-x86_64-wheels:
name: "Build Manylinux 2.28 x86_64 wheel"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-python
container:
image: quay.io/pypa/manylinux2014_x86_64
image: quay.io/pypa/manylinux_2_28_x86_64
env:
PLAT: manylinux2014_x86_64
PYBIN: '/opt/python/${{ matrix.python }}/bin'
PLAT: manylinux_2_28_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
- cp311-cp311
- cp312-cp312
steps:
- name: checkout
uses: actions/checkout@v2
- name: "Checkout"
uses: actions/checkout@v3
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
- name: "Install Rust 1.77.1"
uses: actions-rs/toolchain@v1
with:
toolchain: 1.77.1
- 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_28_x86_64 --verbose
- uses: actions/upload-artifact@v3
with:
name: bdkpython-manylinux_2_28_x86_64-${{ matrix.python }}
path: /home/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
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"
- "3.11"
- "3.12"
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"
- "3.11"
- "3.12"
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"
- "3.11"
- "3.12"
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-manylinux_2_28-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 +181,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
java-version: 17
- name: Install rust android targets
- name: "Set default Rust version to 1.77.1"
run: rustup default 1.77.1
- name: "Install Rust Android targets"
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
- 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
java-version: 17
- name: Run JVM tests
- name: "Set default Rust version to 1.77.1"
run: rustup default 1.77.1
- name: "Run JVM tests"
run: |
cd bdk-jvm
./gradlew test --console=rich
./gradlew test -P excludeConnectedTests

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

@ -0,0 +1,194 @@
name: Test Python
on:
workflow_dispatch:
push:
paths:
- "bdk-ffi/**"
- "bdk-python/**"
pull_request:
paths:
- "bdk-ffi/**"
- "bdk-python/**"
jobs:
build-manylinux_2_28-x86_64-wheels:
name: "Build and test Manylinux 2.28 x86_64 wheels"
runs-on: ubuntu-20.04
defaults:
run:
working-directory: bdk-python
container:
image: quay.io/pypa/manylinux_2_28_x86_64
env:
PLAT: manylinux_2_28_x86_64
PYBIN: "/opt/python/${{ matrix.python }}/bin"
strategy:
matrix:
python:
- cp38-cp38
- cp39-cp39
- cp310-cp310
- cp311-cp311
- cp312-cp312
steps:
- name: "Checkout"
uses: actions/checkout@v3
with:
submodules: true
- name: "Install Rust 1.77.1"
uses: actions-rs/toolchain@v1
with:
toolchain: 1.77.1
- 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_28_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-manylinux_2_28_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"
- "3.11"
- "3.12"
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"
- "3.11"
- "3.12"
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"
- "3.11"
- "3.12"
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,16 @@ 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: 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: "Build Swift package"
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
run: bash ./build-xcframework.sh
- name: Run Swift tests
- name: "Run Swift tests"
working-directory: bdk-swift
run: swift test
run: swift test --skip LiveElectrumClientTests --skip LiveMemoryWalletTests --skip LiveTransactionTests --skip LiveTxBuilderTests --skip LiveWalletTests

7
.gitignore vendored
View File

@ -18,7 +18,7 @@ bdk.kt
# Swift related
/.build
/.swiftpm
.swiftpm
/Packages
/*.xcodeproj
xcuserdata/
@ -31,3 +31,8 @@ bdkFFI.h
BitcoinDevKit.swift
bdk.swift
.build
*.xcframework/
Info.plist
# Python related
__pycache__

View File

@ -1,13 +1,173 @@
# 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).
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]
## [v1.0.0-alpha.11]
This release brings the latest alpha 11 release of the Rust bdk_wallet library, as well as the new Electrum client, the new memory wallet, and a whole lot of new types and APIs across the library. Also of note are the much simpler-to-use full_scan and sync workflows for syncing wallets.
Added:
- `Amount` type [#533]
- `TxIn` type [#536]
- `Transaction.input()` method [#536]
- `Transaction.output()` method [#536]
- `Transaction.lock_time()` method [#536]
- `Electrum` client [#535]
- Memory wallet [#528]
[#528]: https://github.com/bitcoindevkit/bdk-ffi/pull/528
[#533]: https://github.com/bitcoindevkit/bdk-ffi/pull/533
[#535]: https://github.com/bitcoindevkit/bdk-ffi/pull/535
[#536]: https://github.com/bitcoindevkit/bdk-ffi/pull/536
## [v1.0.0-alpha.7]
This release brings back into the 1.0 API a number of APIs from the 0.31 release, and adds the new flat file persistence feature, as well as more fine-grain errors.
## [v1.0.0-alpha.2a]
This release is the first alpha release of the 1.0 API for the bindings libraries. Here is what is now available:
- Create and recover wallets using descriptors, including the four descriptor templates
- Sync a wallet using a blocking Esplora client
- Query the wallet for balance and addresses
- Create and sign transactions using the transaction builder
- Broadcast transactions
## [v0.31.0]
This release updates the bindings libraries to bdk version 0.29.0, updating rust-bitcoin to version 0.30.2.
- APIs Changed:
- `BumpFeeTxBuilder.allow_shrinking()` now takes a `Script` as its argument [#443]
- The `Address` constructor now takes a `Network` argument [#443]
- The `Payload::PubkeyHash` and `Payload::ScriptHash` now have string arguments instead of byte arrays [#443]
- APIs Added:
- The `Address` type now has the `is_valid_for_network()` method [#443]
[#443]: https://github.com/bitcoindevkit/bdk-ffi/pull/443
## [v0.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
## [v0.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 +182,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 +262,18 @@ 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
[v1.0.0-alpha.11]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.7...v1.0.0-alpha.11
[v1.0.0-alpha.7]: https://github.com/bitcoindevkit/bdk-ffi/compare/v1.0.0-alpha.2a...v1.0.0-alpha.7
[v1.0.0-alpha.2a]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.31.0...v1.0.0-alpha.2a
[v0.31.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.30.0...v0.31.0
[v0.30.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.29.0...v0.30.0
[v0.29.0]: https://github.com/bitcoindevkit/bdk-ffi/compare/v0.28.0...v0.29.0
[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.31.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,25 +26,21 @@ The below directories (a separate repository in the case of bdk-swift) include i
| Swift | iOS, macOS | [bdk-swift (GitHub)] | [Readme bdk-swift] | |
| Python | linux, macOS, Windows | [bdk-python (PyPI)] | [Readme bdk-python] | |
## 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
## Building and Testing the Libraries
If you are familiar with the build tools for the specific languages you wish to build the libraries for, you can use their normal build/test workflows. We also include some [just](https://just.systems/) files to simplify the work across different languages. If you have the just tool installed on your system, you can simply call the commands defined in the `justfile`s, for example:
```sh
cd bdk-android
just build
just offlinetests
just publishlocal
```
## Minimum Supported Rust Version (MSRV)
This library should compile with any combination of features with Rust 1.77.1.
## Contributing
### Adding new structs and functions
See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
#### For pass by value objects
1. Create new rust struct with only fields that are supported UniFFI types
2. Update mapping `bdk.udl` file with new `dictionary`
#### For pass by reference values
1. Create wrapper rust struct/impl with only fields that are `Sync + Send`
2. Update mapping `bdk.udl` file with new `interface`
To add new structs and functions, see the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/) and the [uniffi-examples](https://thunderbiscuit.github.io/uniffi-examples/) repository.
## Goals
1. Language bindings should feel idiomatic in target languages/platforms
@ -57,8 +56,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 +67,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,706 +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
*/
enum class AddressIndex {
/** Return a new address after incrementing the current descriptor index. */
NEW,
/** Return the address for the current descriptor index if it has not been used in a received transaction.
* Otherwise return a new address as with `AddressIndex.NEW`. Use with caution, if the wallet
* has not yet detected an address has been used it could return an already used address.
* This function is primarily meant for situations where the caller is untrusted;
* for example when deriving donation addresses on-demand for a public web page.
*/
LAST_UNUSED,
}
/**
* Balance differentiated in various categories.
*
* @property immature All coinbase outputs not yet matured.
* @property trustedPending Unconfirmed UTXOs generated by a wallet tx.
* @property untrustedPending Unconfirmed UTXOs received from an external wallet.
* @property confirmed Confirmed and immediately spendable balance.
* @property spendable The sum of trustedPending and confirmed coins.
* @property total The whole balance visible to the wallet.
*
* @sample org.bitcoindevkit.balanceSample
*/
data class Balance (
var immature: ULong,
var trustedPending: ULong,
var untrustedPending: ULong,
var confirmed: ULong,
var spendable: ULong,
var total: ULong
)
/**
* Type that can contain any of the database configurations defined by the library.
*
* @sample org.bitcoindevkit.memoryDatabaseConfigSample
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
*/
sealed class DatabaseConfig {
/** Configuration for an in-memory database. */
object Memory : DatabaseConfig()
/** Configuration for a Sled database. */
data class Sled(val config: SledDbConfiguration) : DatabaseConfig()
/** Configuration for a SQLite database. */
data class Sqlite(val config: SqliteDbConfiguration) : DatabaseConfig()
}
/**
* Configuration type for a SQLite database.
*
* @property path Main directory of the DB.
*
* @sample org.bitcoindevkit.sqliteDatabaseConfigSample
*/
data class SqliteDbConfiguration(
var path: String,
)
/**
* Configuration type for a SledDB database.
*
* @property path Main directory of the DB.
* @property treeName Name of the database tree, a separated namespace for the data.
*/
data class SledDbConfiguration(
var path: String,
var treeName: String,
)
/**
* Configuration for an Electrum blockchain.
*
* @property url URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with `ssl://` or `tcp://` and include a port, e.g. `ssl://electrum.blockstream.info:60002`.
* @property socks5 URL of the socks5 proxy server or a Tor service.
* @property retry Request retry count.
* @property timeout Request timeout (seconds).
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
* @property validateDomain Validate the domain when using SSL.
*
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
*/
data class ElectrumConfig(
var url: String,
var socks5: String?,
var retry: UByte,
var timeout: UByte?,
var stopGap: ULong,
var validateDomain: Boolean
)
/**
* Configuration for an Esplora blockchain.
*
* @property baseUrl Base URL of the esplora service, e.g. `https://blockstream.info/api/`.
* @property proxy Optional URL of the proxy to use to make requests to the Esplora server.
* @property concurrency Number of parallel requests sent to the esplora service (default: 4).
* @property stopGap Stop searching addresses for transactions after finding an unused gap of this length.
* @property timeout Socket timeout.
*
* @sample org.bitcoindevkit.esploraBlockchainConfigSample
*/
data class EsploraConfig(
var baseUrl: String,
var proxy: String?,
var concurrency: UByte?,
var stopGap: ULong,
var timeout: ULong?
)
/**
* Authentication mechanism for RPC connection to full node.
*/
sealed class Auth {
/** No authentication */
object None: Auth()
/** Authentication with username and password, usually [Auth.Cookie] should be preferred */
data class UserPass(val username: String, val password: String): Auth()
/** Authentication with a cookie file */
data class Cookie(val file: String): Auth()
}
/**
* Sync parameters for Bitcoin Core RPC.
*
* In general, BDK tries to sync `scriptPubKey`s cached in `Database` with
* `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
* how the `importdescriptors` RPC calls are to be made.
*
* @property startScriptCount The minimum number of scripts to scan for on initial sync.
* @property startTime Time in unix seconds in which initial sync will start scanning from (0 to start from genesis).
* @property forceStartTime Forces every sync to use `start_time` as import timestamp.
* @property pollRateSec RPC poll rate (in seconds) to get state updates.
*/
data class RcpSyncParams(
val startScriptCount: ULong,
val startTime: Ulong,
val forceStartTime: Boolean,
val pollRateSec: ULong,
)
/**
* RpcBlockchain configuration options
*
* @property url The bitcoin node url.
* @property auth The bicoin node authentication mechanism.
* @property network The network we are using (it will be checked the bitcoin node network matches this).
* @property walletName The wallet name in the bitcoin node.
* @property syncParams Sync parameters.
*/
data class RpcConfig(
val url: String,
val auth: Auth,
val network: Network,
val walletName: String,
val syncParams: RcpSyncParams?,
)
/**
* Type that can contain any of the blockchain configurations defined by the library.
*
* @sample org.bitcoindevkit.electrumBlockchainConfigSample
*/
sealed class BlockchainConfig {
/** Electrum client. */
data class Electrum(val config: ElectrumConfig) : BlockchainConfig()
/** Esplora client. */
data class Esplora(val config: EsploraConfig) : BlockchainConfig()
/** Bitcoin Core RPC client. */
data class Rpc(val config: RpcConfig) : BlockchainConfig()
}
/**
* A wallet transaction.
*
* @property fee Fee value (sats) if available. The availability of the fee depends on the backend. 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(psbt: PartiallySignedBitcoinTransaction) {}
/** Get the current height of the blockchain. */
fun getHeight(): UInt {}
/** Get the block hash of a given block. */
fun getBlockHash(height: UInt): String {}
}
/**
* A partially signed bitcoin transaction.
*
* @constructor Build a new Partially Signed Bitcoin Transaction.
*
* @param psbtBase64 The PSBT in base64 format.
*/
class PartiallySignedBitcoinTransaction(psbtBase64: String) {
/** Return the PSBT in string format, using a base64 encoding. */
fun serialize(): String {}
/** Get the txid of the PSBT. */
fun txid(): String {}
/** Return the transaction as bytes. */
fun extractTx(): List<UByte>
/**
* Combines this PartiallySignedTransaction with another PSBT as described by BIP 174.
* In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
*/
fun combine(other: PartiallySignedBitcoinTransaction): PartiallySignedBitcoinTransaction
}
/**
* A reference to a transaction output.
*
* @property txid The referenced 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 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: PartiallySignedBitcoinTransaction): Boolean {}
/** Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually. */
fun listTransactions(): List<TransactionDetails> {}
/** Get the Bitcoin network the wallet is using. */
fun network(): Network {}
/** Sync the internal database with the blockchain. */
fun sync(blockchain: Blockchain, progress: Progress?) {}
/** Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
fun listUnspent(): List<LocalUtxo> {}
}
/**
* Class that logs at level INFO every update received (if any).
*/
class Progress {
/** Send a new progress update. The progress value should be in the range 0.0 - 100.0, and the message value is an optional text message that can be displayed to the user. */
fun update(progress: Float, message: String?) {}
}
/**
* A transaction builder.
*
* After creating the TxBuilder, you set options on it until finally calling `.finish` to consume the builder and generate the transaction.
*
* Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
*
* @sample org.bitcoindevkit.txBuilderResultSample1
* @sample org.bitcoindevkit.txBuilderResultSample2
*/
class TxBuilder() {
/** Add data as an output using OP_RETURN. */
fun addData(data: List<UByte>): TxBuilder {}
/** Add a recipient to the internal list. */
fun addRecipient(script: Script, amount: ULong): TxBuilder {}
/** Set the list of recipients by providing a list of [ScriptAmount]. */
fun setRecipients(recipients: List<ScriptAmount>): TxBuilder {}
/** Add a utxo to the internal list of unspendable utxos. 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: PartiallySignedBitcoinTransaction,
var transactionDetails: TransactionDetails
)
/**
* A bitcoin script.
*/
class Script(rawOutputScript: List<UByte>)
/**
* A bitcoin address.
*
* @param address The address in string format.
*/
class Address(address: String) {
/** Return the ScriptPubKey. */
fun scriptPubkey(): Script
}
/**
* Mnemonic phrases are a human-readable version of the private keys. Supported number of words are 12, 15, 18, 21 and 24.
*
* @constructor Generates Mnemonic with a random entropy.
* @param mnemonic The mnemonic as a string of space-separated words.
*
* @sample org.bitcoindevkit.mnemonicSample
*/
class Mnemonic(mnemonic: String) {
/* Returns Mnemonic as string */
fun asString(): String
/* Parse a Mnemonic from a given string. */
fun fromString(): Mnemonic
/*
* Create a new Mnemonic in the specified language from the given entropy. Entropy must be a
* multiple of 32 bits (4 bytes) and 128-256 bits in length.
*/
fun fromEntropy(): Mnemonic
}

View File

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

View File

@ -8,29 +8,11 @@ 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:
```kotlin
import org.bitcoindevkit.*
// ...
val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
val databaseConfig = DatabaseConfig.Memory
val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
)
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
```
### Snapshot releases
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
```kotlin
@ -38,42 +20,52 @@ 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)
### How to build
_Note that Kotlin version `1.6.10` or later is required to build the library._
_Note that Kotlin version `1.9.23` or later is required to build the library._
1. Clone this repository.
```shell
git clone https://github.com/bitcoindevkit/bdk-ffi
```
2. Follow the "General" bdk-ffi ["Getting Started (Developer)"] instructions.
3. 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.77.1):
```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default 1.77.1
```
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>
```
6. Setup `ANDROID_SDK_ROOT` and `ANDROID_NDK_ROOT` path variables which are required by the build tool. Note that currently, NDK version 25.2.9519653 or above is required. For example:
```shell
# macOS
export ANDROID_SDK_ROOT=~/Library/Android/sdk
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
# linux
export ANDROID_SDK_ROOT=/usr/local/lib/android/sdk
export ANDROID_NDK_ROOT=$ANDROID_SDK_ROOT/ndk/25.2.9519653
```
7. Build kotlin bindings
```sh
# build Android library
cd bdk-android
./gradlew buildAndroidLib
```
8. Start android emulator (must be x86_64) and run tests
1. Start android emulator and run tests
```sh
./gradlew connectedAndroidTest
```
@ -81,7 +73,7 @@ git clone https://github.com/bitcoindevkit/bdk-ffi
## How to publish to your local Maven repo
```shell
cd bdk-android
./gradlew publishToMavenLocal --exclude-task signMavenPublication
./gradlew publishToMavenLocal -P localBuild
```
Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so:
@ -90,12 +82,13 @@ signing.gnupg.keyName=<YOUR_GNUPG_ID>
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
```
and use the `publishToMavenLocal` task without excluding the signing task:
and use the `publishToMavenLocal` task without the `localBuild` flag:
```shell
./gradlew publishToMavenLocal
```
## Known issues
### JNA dependency
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
```shell
class file for com.sun.jna.Pointer not found
@ -108,5 +101,8 @@ dependencies {
}
```
### x86 emulators
For some older versions of macOS, Android Studio will recommend users install the x86 version of the emulator by default. This will not work with the bdk-android library, as we do not support 32-bit architectures. Make sure you install an x86_64 emulator to work with bdk-android.
[`bdk`]: https://github.com/bitcoindevkit/bdk
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi

View File

@ -1,14 +1,10 @@
buildscript {
repositories {
google()
}
dependencies {
classpath("com.android.tools.build:gradle:7.1.2")
}
}
plugins {
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
id("com.android.library").version("8.3.1").apply(false)
id("org.jetbrains.kotlin.android").version("1.9.23").apply(false)
id("org.gradle.maven-publish")
id("org.gradle.signing")
id("org.bitcoindevkit.plugins.generate-android-bindings").apply(false)
id("io.github.gradle-nexus.publish-plugin").version("1.1.0").apply(true)
}
// library version is defined in gradle.properties

View File

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

Binary file not shown.

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

41
bdk-android/gradlew vendored
View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +80,11 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -133,22 +131,29 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
@ -205,6 +214,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

View File

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

20
bdk-android/justfile Normal file
View File

@ -0,0 +1,20 @@
default:
just --list
build:
./gradlew buildAndroidLib
clean:
rm -rf ../bdk-ffi/target/
rm -rf ./build/
rm -rf ./lib/build/
rm -rf ./plugins/build/
publish-local:
./gradlew publishToMavenLocal -P localBuild
test:
./gradlew connectedAndroidTest
test-specific TEST:
./gradlew test --tests {{TEST}}

View File

@ -5,25 +5,21 @@ val libraryVersion: String by project
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android") version "1.6.10"
id("maven-publish")
id("signing")
id("org.jetbrains.kotlin.android")
id("org.gradle.maven-publish")
id("org.gradle.signing")
// Custom plugin to generate the native libs and bindings file
id("org.bitcoindevkit.plugins.generate-android-bindings")
}
repositories {
mavenCentral()
google()
}
android {
compileSdk = 31
namespace = "org.bitcoindevkit"
compileSdk = 34
defaultConfig {
minSdk = 21
targetSdk = 31
minSdk = 24
targetSdk = 34
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}
@ -43,8 +39,22 @@ android {
}
}
kotlin {
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "17"
}
}
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
dependencies {
implementation("net.java.dev.jna:jna:5.8.0@aar")
implementation("net.java.dev.jna:jna:5.14.0@aar")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
implementation("androidx.appcompat:appcompat:1.4.0")
implementation("androidx.core:core-ktx:1.7.0")
@ -53,7 +63,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 +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 {
@ -100,9 +106,22 @@ afterEvaluate {
}
}
}
// This is required because we must ensure the moveNativeAndroidLibs task is executed after
// the mergeReleaseJniLibFolders (hard requirement introduced by our upgrade to Gradle 8.7)
tasks.named("mergeReleaseJniLibFolders") {
dependsOn(":lib:moveNativeAndroidLibs")
}
tasks.named("mergeDebugJniLibFolders") {
dependsOn(":lib:moveNativeAndroidLibs")
}
}
signing {
if (project.hasProperty("localBuild")) {
isRequired = false
}
val signingKeyId: String? by project
val signingKey: String? by project
val signingPassword: String? by project
@ -110,8 +129,7 @@ signing {
sign(publishing.publications)
}
// This task dependency ensures that we build the bindings
// binaries before running the tests
// This task dependency ensures that we build the bindings binaries before running the tests
tasks.withType<KotlinCompile> {
dependsOn("buildAndroidLib")
}

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,82 @@
package org.bitcoindevkit
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Test
import org.junit.runner.RunWith
import java.io.File
import kotlin.test.AfterTest
import kotlin.test.assertTrue
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
@RunWith(AndroidJUnit4::class)
class LiveTxBuilderTest {
private val persistenceFilePath = InstrumentationRegistry.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence3.sqlite"
private val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
private val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
@AfterTest
fun cleanup() {
val file = File(persistenceFilePath)
if (file.exists()) {
file.delete()
}
}
@Test
fun testTxBuilder() {
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
val psbt: Psbt = TxBuilder()
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
.feeRate(FeeRate.fromSatPerVb(2uL))
.finish(wallet)
println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
}
@Test
fun complexTxBuilder() {
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
val allRecipients: List<ScriptAmount> = listOf(
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
)
val psbt: Psbt = TxBuilder()
.setRecipients(allRecipients)
.feeRate(FeeRate.fromSatPerVb(4uL))
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
.enableRbf()
.finish(wallet)
wallet.sign(psbt)
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
}
}

View File

@ -0,0 +1,91 @@
package org.bitcoindevkit
import org.junit.Test
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.runner.RunWith
import java.io.File
import kotlin.test.AfterTest
import kotlin.test.assertTrue
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
@RunWith(AndroidJUnit4::class)
class LiveWalletTest {
private val persistenceFilePath = InstrumentationRegistry
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence2.sqlite"
private val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
private val changeDescriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.SIGNET)
@AfterTest
fun cleanup() {
val file = File(persistenceFilePath)
if (file.exists()) {
file.delete()
}
}
@Test
fun testSyncedBalance() {
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
val balance: Balance = wallet.balance()
println("Balance: $balance")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
println("Transactions count: ${wallet.transactions().count()}")
val transactions = wallet.transactions().take(3)
for (tx in transactions) {
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
println("Transaction: ${tx.transaction.computeTxid()}")
println("Sent ${sentAndReceived.sent}")
println("Received ${sentAndReceived.received}")
}
}
@Test
fun testBroadcastTransaction() {
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
val psbt: Psbt = TxBuilder()
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
.feeRate(FeeRate.fromSatPerVb(4uL))
.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.computeTxid()}")
val txFee: Amount = wallet.calculateFee(tx)
println("Tx fee is: ${txFee.toSat()}")
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
esploraClient.broadcast(tx)
}
}

View File

@ -0,0 +1,21 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith
@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.toString()
)
}
}

View File

@ -0,0 +1,70 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.assertFalse
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.runner.RunWith
import java.io.File
import kotlin.test.AfterTest
@RunWith(AndroidJUnit4::class)
class OfflineWalletTest {
private val persistenceFilePath = InstrumentationRegistry
.getInstrumentation().targetContext.filesDir.path + "/bdk_persistence1.sqlite"
private val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
private val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
@AfterTest
fun cleanup() {
val file = File(persistenceFilePath)
if (file.exists()) {
file.delete()
}
}
@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.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
}
@Test
fun testNewAddress() {
val wallet: Wallet = Wallet(
descriptor,
changeDescriptor,
Network.TESTNET
)
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
assertFalse(addressInfo.address.isValidForNetwork(Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
assertEquals(
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
actual = addressInfo.address.toString()
)
}
@Test
fun testBalance() {
val wallet: Wallet = Wallet(
descriptor,
changeDescriptor,
Network.TESTNET
)
assertEquals(
expected = 0uL,
actual = wallet.balance().total.toSat()
)
}
}

View File

@ -1,6 +1,3 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.bitcoindevkit">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

View File

@ -1,6 +1,5 @@
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

View File

@ -17,31 +17,27 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
OS.OTHER -> throw Error("Cannot build Android library from current architecture")
}
// if ANDROID_NDK_ROOT is not set, stop build
if (System.getenv("ANDROID_NDK_ROOT") == null) {
throw IllegalStateException("ANDROID_NDK_ROOT environment variable is not set; cannot build library")
}
// arm64-v8a is the most popular hardware architecture for Android
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)
// if ANDROID_NDK_ROOT is not set then set it to github actions default
if (System.getenv("ANDROID_NDK_ROOT") == null) {
environment(
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
)
}
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_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android21-clang"),
Pair("CC", "aarch64-linux-android21-clang")
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
Pair("AR", "llvm-ar"),
Pair("CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER", "aarch64-linux-android24-clang"),
Pair("CC", "aarch64-linux-android24-clang")
)
doLast {
@ -53,27 +49,18 @@ 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)
// if ANDROID_NDK_ROOT is not set then set it to github actions default
if (System.getenv("ANDROID_NDK_ROOT") == null) {
environment(
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
)
}
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_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android21-clang"),
Pair("CC", "x86_64-linux-android21-clang")
Pair("PATH", "${System.getenv("PATH")}:${System.getenv("ANDROID_NDK_ROOT")}/toolchains/llvm/prebuilt/$llvmArchPath/bin"),
Pair("CFLAGS", "-D__ANDROID_MIN_SDK_VERSION__=24"),
Pair("AR", "llvm-ar"),
Pair("CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER", "x86_64-linux-android24-clang"),
Pair("CC", "x86_64-linux-android24-clang")
)
doLast {
@ -85,28 +72,18 @@ 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)
// if ANDROID_NDK_ROOT is not set then set it to github actions default
if (System.getenv("ANDROID_NDK_ROOT") == null) {
environment(
Pair("ANDROID_NDK_ROOT", "${System.getenv("ANDROID_SDK_ROOT")}/ndk-bundle")
)
}
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("CC", "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__=24"),
Pair("AR", "llvm-ar"),
Pair("CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER", "armv7a-linux-androideabi24-clang"),
Pair("CC", "armv7a-linux-androideabi24-clang")
)
doLast {
@ -120,19 +97,21 @@ internal class UniFfiAndroidPlugin : Plugin<Project> {
val moveNativeAndroidLibs by tasks.register<Copy>("moveNativeAndroidLibs") {
dependsOn(buildAndroidAarch64Binary)
dependsOn(buildAndroidArmv7Binary)
dependsOn(buildAndroidX86_64Binary)
into("${project.projectDir}/../lib/src/main/jniLibs/")
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 +123,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", "--config", "uniffi-android.toml", "--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

@ -2,3 +2,17 @@ rootProject.name = "bdk-android"
include(":lib")
includeBuild("plugins")
pluginManagement {
repositories {
gradlePluginPortal()
google()
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
google()
}
}

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.26.0"
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
version = "1.0.0-alpha.11"
homepage = "https://bitcoindevkit.org"
repository = "https://github.com/bitcoindevkit/bdk"
edition = "2018"
license = "MIT OR Apache-2.0"
[lib]
crate-type = ["staticlib", "cdylib"]
crate-type = ["lib", "staticlib", "cdylib"]
name = "bdkffi"
[[bin]]
name = "uniffi-bindgen"
path = "uniffi-bindgen.rs"
[features]
default = ["uniffi/cli"]
[dependencies]
bdk = { version = "0.26", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys", "keys-bip39"] }
bdk_esplora = { version = "0.15.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
# NOTE: This is a temporary workaround to use the electrum-client with the use-rustls-ring feature. It points to a fork
# of bdk in which the bdk_electrum library uses the electrum-client with the use-rustls-ring feature.
bdk_electrum = { git = "https://github.com/thunderbiscuit/bdk/", package = "bdk_electrum", branch = "feature/electrum-client-ring-ffi-alpha13", default-features = false, features = ["use-rustls-ring"] }
# bdk_electrum = { version = "0.15.0" }
bdk_sqlite = { version = "0.2.0" }
bdk_bitcoind_rpc = { version = "0.12.0" }
bitcoin-internals = { version = "0.2.0", features = ["alloc"] }
uniffi = { version = "=0.28.0" }
thiserror = "1.0.58"
[build-dependencies]
uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi = { version = "=0.28.0", features = ["build"] }
[dev-dependencies]
uniffi = { version = "=0.28.0", 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();
}

12
bdk-ffi/justfile Normal file
View File

@ -0,0 +1,12 @@
default:
just --list
build:
cargo build
check:
cargo fmt
cargo clippy
test:
cargo test --lib

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,679 @@
use crate::error::{
AddressParseError, FeeRateError, FromScriptError, PsbtError, PsbtParseError, TransactionError,
};
use bdk_bitcoind_rpc::bitcoincore_rpc::jsonrpc::serde_json;
use bdk_wallet::bitcoin::address::{NetworkChecked, NetworkUnchecked};
use bdk_wallet::bitcoin::amount::ParseAmountError;
use bdk_wallet::bitcoin::consensus::encode::serialize;
use bdk_wallet::bitcoin::consensus::Decodable;
use bdk_wallet::bitcoin::io::Cursor;
use bdk_wallet::bitcoin::psbt::ExtractTxError;
use bdk_wallet::bitcoin::Address as BdkAddress;
use bdk_wallet::bitcoin::Amount as BdkAmount;
use bdk_wallet::bitcoin::FeeRate as BdkFeeRate;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::bitcoin::OutPoint as BdkOutPoint;
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
use bdk_wallet::bitcoin::TxIn as BdkTxIn;
use bdk_wallet::bitcoin::TxOut as BdkTxOut;
use bdk_wallet::bitcoin::Txid;
use std::fmt::Display;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Amount(pub(crate) BdkAmount);
impl Amount {
pub fn from_sat(sat: u64) -> Self {
Amount(BdkAmount::from_sat(sat))
}
pub fn from_btc(btc: f64) -> Result<Self, ParseAmountError> {
let bdk_amount = BdkAmount::from_btc(btc).map_err(ParseAmountError::from)?;
Ok(Amount(bdk_amount))
}
pub fn to_sat(&self) -> u64 {
self.0.to_sat()
}
pub fn to_btc(&self) -> f64 {
self.0.to_btc()
}
}
impl From<Amount> for BdkAmount {
fn from(amount: Amount) -> Self {
amount.0
}
}
impl From<BdkAmount> for Amount {
fn from(amount: BdkAmount) -> Self {
Amount(amount)
}
}
#[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)
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct Address(BdkAddress<NetworkChecked>);
impl Address {
pub fn new(address: String, network: Network) -> Result<Self, AddressParseError> {
let parsed_address = address.parse::<bdk_wallet::bitcoin::Address<NetworkUnchecked>>()?;
let network_checked_address = parsed_address.require_network(network)?;
Ok(Address(network_checked_address))
}
pub fn from_script(script: Arc<Script>, network: Network) -> Result<Self, FromScriptError> {
let address = BdkAddress::from_script(&script.0.clone(), network)?;
Ok(Address(address))
}
pub fn script_pubkey(&self) -> Arc<Script> {
Arc::new(Script(self.0.script_pubkey()))
}
pub fn to_qr_uri(&self) -> String {
self.0.to_qr_uri()
}
pub fn is_valid_for_network(&self, network: Network) -> bool {
let address_str = self.0.to_string();
if let Ok(unchecked_address) = address_str.parse::<BdkAddress<NetworkUnchecked>>() {
unchecked_address.is_valid_for_network(network)
} else {
false
}
}
}
impl Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Address> for BdkAddress {
fn from(address: Address) -> Self {
address.0
}
}
impl From<BdkAddress> for Address {
fn from(address: BdkAddress) -> Self {
Address(address)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transaction(BdkTransaction);
impl Transaction {
pub fn new(transaction_bytes: Vec<u8>) -> Result<Self, TransactionError> {
let mut decoder = Cursor::new(transaction_bytes);
let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?;
Ok(Transaction(tx))
}
pub fn compute_txid(&self) -> String {
self.0.compute_txid().to_string()
}
pub fn weight(&self) -> u64 {
self.0.weight().to_wu()
}
pub fn total_size(&self) -> u64 {
self.0.total_size() as u64
}
pub fn vsize(&self) -> u64 {
self.0.vsize() as u64
}
pub fn is_coinbase(&self) -> bool {
self.0.is_coinbase()
}
pub fn is_explicitly_rbf(&self) -> bool {
self.0.is_explicitly_rbf()
}
pub fn is_lock_time_enabled(&self) -> bool {
self.0.is_lock_time_enabled()
}
pub fn version(&self) -> i32 {
self.0.version.0
}
pub fn serialize(&self) -> Vec<u8> {
serialize(&self.0)
}
pub fn input(&self) -> Vec<TxIn> {
self.0.input.iter().map(|tx_in| tx_in.into()).collect()
}
pub fn output(&self) -> Vec<TxOut> {
self.0.output.iter().map(|tx_out| tx_out.into()).collect()
}
pub fn lock_time(&self) -> u32 {
self.0.lock_time.to_consensus_u32()
}
}
impl From<BdkTransaction> for Transaction {
fn from(tx: BdkTransaction) -> Self {
Transaction(tx)
}
}
impl From<&BdkTransaction> for Transaction {
fn from(tx: &BdkTransaction) -> Self {
Transaction(tx.clone())
}
}
impl From<&Transaction> for BdkTransaction {
fn from(tx: &Transaction) -> Self {
tx.0.clone()
}
}
pub struct Psbt(pub(crate) Mutex<BdkPsbt>);
impl Psbt {
pub(crate) fn new(psbt_base64: String) -> Result<Self, PsbtParseError> {
let psbt: BdkPsbt = BdkPsbt::from_str(&psbt_base64)?;
Ok(Psbt(Mutex::new(psbt)))
}
pub(crate) fn serialize(&self) -> String {
let psbt = self.0.lock().unwrap().clone();
psbt.to_string()
}
pub(crate) fn extract_tx(&self) -> Result<Arc<Transaction>, ExtractTxError> {
let tx: BdkTransaction = self.0.lock().unwrap().clone().extract_tx()?;
let transaction: Transaction = tx.into();
Ok(Arc::new(transaction))
}
pub(crate) fn fee(&self) -> Result<u64, PsbtError> {
self.0
.lock()
.unwrap()
.fee()
.map(|fee| fee.to_sat())
.map_err(PsbtError::from)
}
pub(crate) fn combine(&self, other: Arc<Psbt>) -> Result<Arc<Psbt>, PsbtError> {
let mut original_psbt = self.0.lock().unwrap().clone();
let other_psbt = other.0.lock().unwrap().clone();
original_psbt.combine(other_psbt)?;
Ok(Arc::new(Psbt(Mutex::new(original_psbt))))
}
pub(crate) fn json_serialize(&self) -> String {
let psbt = self.0.lock().unwrap();
serde_json::to_string(psbt.deref()).unwrap()
}
}
impl From<BdkPsbt> for Psbt {
fn from(psbt: BdkPsbt) -> Self {
Psbt(Mutex::new(psbt))
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct OutPoint {
pub txid: String,
pub vout: u32,
}
impl From<&OutPoint> for BdkOutPoint {
fn from(outpoint: &OutPoint) -> Self {
BdkOutPoint {
txid: Txid::from_str(&outpoint.txid).unwrap(),
vout: outpoint.vout,
}
}
}
impl From<&BdkOutPoint> for OutPoint {
fn from(outpoint: &BdkOutPoint) -> Self {
OutPoint {
txid: outpoint.txid.to_string(),
vout: outpoint.vout,
}
}
}
#[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(tx_in.script_sig.clone())),
sequence: tx_in.sequence.0,
witness: tx_in.witness.to_vec(),
}
}
}
#[derive(Debug, Clone)]
pub struct TxOut {
pub value: u64,
pub script_pubkey: Arc<Script>,
}
impl From<&BdkTxOut> for TxOut {
fn from(tx_out: &BdkTxOut) -> Self {
TxOut {
value: tx_out.value.to_sat(),
script_pubkey: Arc::new(Script(tx_out.script_pubkey.clone())),
}
}
}
#[derive(Clone, Debug)]
pub struct FeeRate(pub(crate) BdkFeeRate);
impl FeeRate {
pub fn from_sat_per_vb(sat_per_vb: u64) -> Result<Self, FeeRateError> {
let fee_rate: Option<BdkFeeRate> = BdkFeeRate::from_sat_per_vb(sat_per_vb);
match fee_rate {
Some(fee_rate) => Ok(FeeRate(fee_rate)),
None => Err(FeeRateError::ArithmeticOverflow),
}
}
pub fn from_sat_per_kwu(sat_per_kwu: u64) -> Self {
FeeRate(BdkFeeRate::from_sat_per_kwu(sat_per_kwu))
}
pub fn to_sat_per_vb_ceil(&self) -> u64 {
self.0.to_sat_per_vb_ceil()
}
pub fn to_sat_per_vb_floor(&self) -> u64 {
self.0.to_sat_per_vb_floor()
}
pub fn to_sat_per_kwu(&self) -> u64 {
self.0.to_sat_per_kwu()
}
}
#[cfg(test)]
mod tests {
use crate::bitcoin::Address;
use crate::bitcoin::Network;
#[test]
fn test_is_valid_for_network() {
// ====Docs tests====
// https://docs.rs/bitcoin/0.29.2/src/bitcoin/util/address.rs.html#798-802
let docs_address_testnet_str = "2N83imGV3gPwBzKJQvWJ7cRUY2SpUyU6A5e";
let docs_address_testnet =
Address::new(docs_address_testnet_str.to_string(), Network::Testnet).unwrap();
assert!(
docs_address_testnet.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
docs_address_testnet.is_valid_for_network(Network::Signet),
"Address should be valid for Signet"
);
assert!(
docs_address_testnet.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
let docs_address_mainnet_str = "32iVBEu4dxkUQk9dJbZUiBiQdmypcEyJRf";
let docs_address_mainnet =
Address::new(docs_address_mainnet_str.to_string(), Network::Bitcoin).unwrap();
assert!(
docs_address_mainnet.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
// ====Bech32====
// | Network | Prefix | Address Type |
// |-----------------|---------|--------------|
// | Bitcoin Mainnet | `bc1` | Bech32 |
// | Bitcoin Testnet | `tb1` | Bech32 |
// | Bitcoin Signet | `tb1` | Bech32 |
// | Bitcoin Regtest | `bcrt1` | Bech32 |
// Bech32 - Bitcoin
// Valid for:
// - Bitcoin
// Not valid for:
// - Testnet
// - Signet
// - Regtest
let bitcoin_mainnet_bech32_address_str = "bc1qxhmdufsvnuaaaer4ynz88fspdsxq2h9e9cetdj";
let bitcoin_mainnet_bech32_address = Address::new(
bitcoin_mainnet_bech32_address_str.to_string(),
Network::Bitcoin,
)
.unwrap();
assert!(
bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
assert!(
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Signet),
"Address should not be valid for Signet"
);
assert!(
!bitcoin_mainnet_bech32_address.is_valid_for_network(Network::Regtest),
"Address should not be valid for Regtest"
);
// Bech32 - Testnet
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
// - Regtest
let bitcoin_testnet_bech32_address_str =
"tb1p4nel7wkc34raczk8c4jwk5cf9d47u2284rxn98rsjrs4w3p2sheqvjmfdh";
let bitcoin_testnet_bech32_address = Address::new(
bitcoin_testnet_bech32_address_str.to_string(),
Network::Testnet,
)
.unwrap();
assert!(
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_testnet_bech32_address.is_valid_for_network(Network::Signet),
"Address should be valid for Signet"
);
assert!(
!bitcoin_testnet_bech32_address.is_valid_for_network(Network::Regtest),
"Address should not not be valid for Regtest"
);
// Bech32 - Signet
// Valid for:
// - Signet
// - Testnet
// Not valid for:
// - Bitcoin
// - Regtest
let bitcoin_signet_bech32_address_str =
"tb1pwzv7fv35yl7ypwj8w7al2t8apd6yf4568cs772qjwper74xqc99sk8x7tk";
let bitcoin_signet_bech32_address = Address::new(
bitcoin_signet_bech32_address_str.to_string(),
Network::Signet,
)
.unwrap();
assert!(
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_signet_bech32_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_signet_bech32_address.is_valid_for_network(Network::Signet),
"Address should be valid for Signet"
);
assert!(
!bitcoin_signet_bech32_address.is_valid_for_network(Network::Regtest),
"Address should not not be valid for Regtest"
);
// Bech32 - Regtest
// Valid for:
// - Regtest
// Not valid for:
// - Bitcoin
// - Testnet
// - Signet
let bitcoin_regtest_bech32_address_str = "bcrt1q39c0vrwpgfjkhasu5mfke9wnym45nydfwaeems";
let bitcoin_regtest_bech32_address = Address::new(
bitcoin_regtest_bech32_address_str.to_string(),
Network::Regtest,
)
.unwrap();
assert!(
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_regtest_bech32_address.is_valid_for_network(Network::Signet),
"Address should not be valid for Signet"
);
assert!(
bitcoin_regtest_bech32_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// ====P2PKH====
// | Network | Prefix for P2PKH | Prefix for P2SH |
// |------------------------------------|------------------|-----------------|
// | Bitcoin Mainnet | `1` | `3` |
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
// P2PKH - Bitcoin
// Valid for:
// - Bitcoin
// Not valid for:
// - Testnet
// - Regtest
let bitcoin_mainnet_p2pkh_address_str = "1FfmbHfnpaZjKFvyi1okTjJJusN455paPH";
let bitcoin_mainnet_p2pkh_address = Address::new(
bitcoin_mainnet_p2pkh_address_str.to_string(),
Network::Bitcoin,
)
.unwrap();
assert!(
bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
assert!(
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_mainnet_p2pkh_address.is_valid_for_network(Network::Regtest),
"Address should not be valid for Regtest"
);
// P2PKH - Testnet
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_testnet_p2pkh_address_str = "mucFNhKMYoBQYUAEsrFVscQ1YaFQPekBpg";
let bitcoin_testnet_p2pkh_address = Address::new(
bitcoin_testnet_p2pkh_address_str.to_string(),
Network::Testnet,
)
.unwrap();
assert!(
!bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_testnet_p2pkh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// P2PKH - Regtest
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_regtest_p2pkh_address_str = "msiGFK1PjCk8E6FXeoGkQPTscmcpyBdkgS";
let bitcoin_regtest_p2pkh_address = Address::new(
bitcoin_regtest_p2pkh_address_str.to_string(),
Network::Regtest,
)
.unwrap();
assert!(
!bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_regtest_p2pkh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// ====P2SH====
// | Network | Prefix for P2PKH | Prefix for P2SH |
// |------------------------------------|------------------|-----------------|
// | Bitcoin Mainnet | `1` | `3` |
// | Bitcoin Testnet, Regtest, Signet | `m` or `n` | `2` |
// P2SH - Bitcoin
// Valid for:
// - Bitcoin
// Not valid for:
// - Testnet
// - Regtest
let bitcoin_mainnet_p2sh_address_str = "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy";
let bitcoin_mainnet_p2sh_address = Address::new(
bitcoin_mainnet_p2sh_address_str.to_string(),
Network::Bitcoin,
)
.unwrap();
assert!(
bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
"Address should be valid for Bitcoin"
);
assert!(
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Testnet),
"Address should not be valid for Testnet"
);
assert!(
!bitcoin_mainnet_p2sh_address.is_valid_for_network(Network::Regtest),
"Address should not be valid for Regtest"
);
// P2SH - Testnet
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_testnet_p2sh_address_str = "2NFUBBRcTJbYc1D4HSCbJhKZp6YCV4PQFpQ";
let bitcoin_testnet_p2sh_address = Address::new(
bitcoin_testnet_p2sh_address_str.to_string(),
Network::Testnet,
)
.unwrap();
assert!(
!bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_testnet_p2sh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
// P2SH - Regtest
// Valid for:
// - Testnet
// - Regtest
// Not valid for:
// - Bitcoin
let bitcoin_regtest_p2sh_address_str = "2NEb8N5B9jhPUCBchz16BB7bkJk8VCZQjf3";
let bitcoin_regtest_p2sh_address = Address::new(
bitcoin_regtest_p2sh_address_str.to_string(),
Network::Regtest,
)
.unwrap();
assert!(
!bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Bitcoin),
"Address should not be valid for Bitcoin"
);
assert!(
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Testnet),
"Address should be valid for Testnet"
);
assert!(
bitcoin_regtest_p2sh_address.is_valid_for_network(Network::Regtest),
"Address should be valid for Regtest"
);
}
}

402
bdk-ffi/src/descriptor.rs Normal file
View File

@ -0,0 +1,402 @@
use crate::error::DescriptorError;
use crate::keys::DescriptorPublicKey;
use crate::keys::DescriptorSecretKey;
use std::fmt::Display;
use bdk_wallet::bitcoin::bip32::Fingerprint;
use bdk_wallet::bitcoin::key::Secp256k1;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::descriptor::{ExtendedDescriptor, IntoWalletDescriptor};
use bdk_wallet::keys::DescriptorPublicKey as BdkDescriptorPublicKey;
use bdk_wallet::keys::{DescriptorSecretKey as BdkDescriptorSecretKey, KeyMap};
use bdk_wallet::template::{
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, Bip86, Bip86Public,
DescriptorTemplate,
};
use bdk_wallet::KeychainKind;
use std::str::FromStr;
#[derive(Debug)]
pub struct Descriptor {
pub extended_descriptor: ExtendedDescriptor,
pub key_map: KeyMap,
}
impl Descriptor {
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, DescriptorError> {
let secp = Secp256k1::new();
let (extended_descriptor, key_map) = descriptor.into_wallet_descriptor(&secp, network)?;
Ok(Self {
extended_descriptor,
key_map,
})
}
pub(crate) fn new_bip44(
secret_key: &DescriptorSecretKey,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = &secret_key.0;
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();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip44_public(
public_key: &DescriptorPublicKey,
fingerprint: String,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = &public_key.0;
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)
.unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorPublicKey::MultiXPub(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip49(
secret_key: &DescriptorSecretKey,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = &secret_key.0;
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();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip49_public(
public_key: &DescriptorPublicKey,
fingerprint: String,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = &public_key.0;
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)
.unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorPublicKey::MultiXPub(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip84(
secret_key: &DescriptorSecretKey,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = &secret_key.0;
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();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip84_public(
public_key: &DescriptorPublicKey,
fingerprint: String,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = &public_key.0;
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)
.unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorPublicKey::MultiXPub(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip86(
secret_key: &DescriptorSecretKey,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = &secret_key.0;
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).unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip86_public(
public_key: &DescriptorPublicKey,
fingerprint: String,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = &public_key.0;
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)
.unwrap();
Self {
extended_descriptor,
key_map,
}
}
BdkDescriptorPublicKey::MultiXPub(_) => {
unreachable!()
}
}
}
pub(crate) fn to_string_with_secret(&self) -> String {
let descriptor = &self.extended_descriptor;
let key_map = &self.key_map;
descriptor.to_string_with_secret(key_map)
}
}
impl Display for Descriptor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.extended_descriptor)
}
}
#[cfg(test)]
mod test {
use crate::*;
use assert_matches::assert_matches;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::KeychainKind;
fn get_descriptor_secret_key() -> DescriptorSecretKey {
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
DescriptorSecretKey::new(Network::Testnet, &mnemonic, None)
}
#[test]
fn test_descriptor_templates() {
let master: DescriptorSecretKey = get_descriptor_secret_key();
println!("Master: {:?}", master.as_string());
// tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h
let handmade_public_44 = master
.derive(&DerivationPath::new("m/44h/1h/0h".to_string()).unwrap())
.unwrap()
.as_public();
println!("Public 44: {}", handmade_public_44.as_string());
// Public 44: [d1d04177/44'/1'/0']tpubDCoPjomfTqh1e7o1WgGpQtARWtkueXQAepTeNpWiitS3Sdv8RKJ1yvTrGHcwjDXp2SKyMrTEca4LoN7gEUiGCWboyWe2rz99Kf4jK4m2Zmx/*
let handmade_public_49 = master
.derive(&DerivationPath::new("m/49h/1h/0h".to_string()).unwrap())
.unwrap()
.as_public();
println!("Public 49: {}", handmade_public_49.as_string());
// Public 49: [d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/*
let handmade_public_84 = master
.derive(&DerivationPath::new("m/84h/1h/0h".to_string()).unwrap())
.unwrap()
.as_public();
println!("Public 84: {}", handmade_public_84.as_string());
// Public 84: [d1d04177/84'/1'/0']tpubDDNxbq17egjFk2edjv8oLnzxk52zny9aAYNv9CMqTzA4mQDiQq818sEkNe9Gzmd4QU8558zftqbfoVBDQorG3E4Wq26tB2JeE4KUoahLkx6/*
let handmade_public_86 = master
.derive(&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, KeychainKind::External, Network::Testnet);
let template_private_49 =
Descriptor::new_bip49(&master, KeychainKind::External, Network::Testnet);
let template_private_84 =
Descriptor::new_bip84(&master, 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);
println!("Template 44: {}", template_private_44);
println!("Template 84: {}", template_private_84);
println!("Template 86: {}", template_private_86);
let template_public_44 = Descriptor::new_bip44_public(
&handmade_public_44,
"d1d04177".to_string(),
KeychainKind::External,
Network::Testnet,
);
let template_public_49 = Descriptor::new_bip49_public(
&handmade_public_49,
"d1d04177".to_string(),
KeychainKind::External,
Network::Testnet,
);
let template_public_84 = Descriptor::new_bip84_public(
&handmade_public_84,
"d1d04177".to_string(),
KeychainKind::External,
Network::Testnet,
);
let template_public_86 = Descriptor::new_bip86_public(
&handmade_public_86,
"d1d04177".to_string(),
KeychainKind::External,
Network::Testnet,
);
println!("Template public 49: {}", template_public_49);
println!("Template public 44: {}", template_public_44);
println!("Template public 84: {}", template_public_84);
println!("Template public 86: {}", template_public_86);
// when using a public key, both to_string and as_string_private return the same string
assert_eq!(
template_public_44.to_string_with_secret(),
template_public_44.to_string()
);
assert_eq!(
template_public_49.to_string_with_secret(),
template_public_49.to_string()
);
assert_eq!(
template_public_84.to_string_with_secret(),
template_public_84.to_string()
);
assert_eq!(
template_public_86.to_string_with_secret(),
template_public_86.to_string()
);
// when using to_string on a private key, we get the same result as when using it on a public key
assert_eq!(
template_private_44.to_string(),
template_public_44.to_string()
);
assert_eq!(
template_private_49.to_string(),
template_public_49.to_string()
);
assert_eq!(
template_private_84.to_string(),
template_public_84.to_string()
);
assert_eq!(
template_private_86.to_string(),
template_public_86.to_string()
);
}
#[test]
fn test_descriptor_from_string() {
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet);
let descriptor2 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Bitcoin);
// Creating a Descriptor using an extended key that doesn't match the network provided will throw a DescriptorError::Key with inner InvalidNetwork error
assert!(descriptor1.is_ok());
assert_matches!(descriptor2.unwrap_err(), DescriptorError::Key { .. });
}
}

101
bdk-ffi/src/electrum.rs Normal file
View File

@ -0,0 +1,101 @@
use crate::bitcoin::Transaction;
use crate::error::ElectrumError;
use crate::types::{FullScanRequest, SyncRequest};
use crate::wallet::Update;
use bdk_electrum::BdkElectrumClient as BdkBdkElectrumClient;
use bdk_electrum::{ElectrumFullScanResult, ElectrumSyncResult};
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult;
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult;
use bdk_wallet::wallet::Update as BdkUpdate;
use bdk_wallet::KeychainKind;
use std::collections::BTreeMap;
use std::sync::Arc;
// NOTE: We are keeping our naming convention where the alias of the inner type is the Rust type
// prefixed with `Bdk`. In this case the inner type is `BdkElectrumClient`, so the alias is
// funnily enough named `BdkBdkElectrumClient`.
pub struct ElectrumClient(BdkBdkElectrumClient<bdk_electrum::electrum_client::Client>);
impl ElectrumClient {
pub fn new(url: String) -> Result<Self, ElectrumError> {
let inner_client: bdk_electrum::electrum_client::Client =
bdk_electrum::electrum_client::Client::new(url.as_str())?;
let client = BdkBdkElectrumClient::new(inner_client);
Ok(Self(client))
}
pub fn full_scan(
&self,
request: Arc<FullScanRequest>,
stop_gap: u64,
batch_size: u64,
fetch_prev_txouts: bool,
) -> Result<Arc<Update>, ElectrumError> {
// using option and take is not ideal but the only way to take full ownership of the request
let request: BdkFullScanRequest<KeychainKind> = request
.0
.lock()
.unwrap()
.take()
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
let electrum_result: ElectrumFullScanResult<KeychainKind> = self.0.full_scan(
request,
stop_gap as usize,
batch_size as usize,
fetch_prev_txouts,
)?;
let full_scan_result: BdkFullScanResult<KeychainKind> =
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
let update = BdkUpdate {
last_active_indices: full_scan_result.last_active_indices,
graph: full_scan_result.graph_update,
chain: Some(full_scan_result.chain_update),
};
Ok(Arc::new(Update(update)))
}
pub fn sync(
&self,
request: Arc<SyncRequest>,
batch_size: u64,
fetch_prev_txouts: bool,
) -> Result<Arc<Update>, ElectrumError> {
// using option and take is not ideal but the only way to take full ownership of the request
let request: BdkSyncRequest = request
.0
.lock()
.unwrap()
.take()
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
let electrum_result: ElectrumSyncResult =
self.0
.sync(request, batch_size as usize, fetch_prev_txouts)?;
let sync_result: BdkSyncResult =
electrum_result.with_confirmation_time_height_anchor(&self.0)?;
let update = BdkUpdate {
last_active_indices: BTreeMap::default(),
graph: sync_result.graph_update,
chain: Some(sync_result.chain_update),
};
Ok(Arc::new(Update(update)))
}
pub fn broadcast(&self, transaction: &Transaction) -> Result<String, ElectrumError> {
let bdk_transaction: BdkTransaction = transaction.into();
self.0
.transaction_broadcast(&bdk_transaction)
.map_err(ElectrumError::from)
.map(|txid| txid.to_string())
}
}

1960
bdk-ffi/src/error.rs Normal file

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,84 @@
use crate::bitcoin::Transaction;
use crate::error::EsploraError;
use crate::types::{FullScanRequest, SyncRequest};
use crate::wallet::Update;
use bdk_esplora::esplora_client::{BlockingClient, Builder};
use bdk_esplora::EsploraExt;
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult;
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult;
use bdk_wallet::wallet::Update as BdkUpdate;
use bdk_wallet::KeychainKind;
use std::collections::BTreeMap;
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();
Self(client)
}
pub fn full_scan(
&self,
request: Arc<FullScanRequest>,
stop_gap: u64,
parallel_requests: u64,
) -> Result<Arc<Update>, EsploraError> {
// using option and take is not ideal but the only way to take full ownership of the request
let request: BdkFullScanRequest<KeychainKind> = request
.0
.lock()
.unwrap()
.take()
.ok_or(EsploraError::RequestAlreadyConsumed)?;
let result: BdkFullScanResult<KeychainKind> =
self.0
.full_scan(request, stop_gap as usize, parallel_requests as usize)?;
let update = BdkUpdate {
last_active_indices: result.last_active_indices,
graph: result.graph_update,
chain: Some(result.chain_update),
};
Ok(Arc::new(Update(update)))
}
pub fn sync(
&self,
request: Arc<SyncRequest>,
parallel_requests: u64,
) -> Result<Arc<Update>, EsploraError> {
// using option and take is not ideal but the only way to take full ownership of the request
let request: BdkSyncRequest = request
.0
.lock()
.unwrap()
.take()
.ok_or(EsploraError::RequestAlreadyConsumed)?;
let result: BdkSyncResult = self.0.sync(request, parallel_requests as usize)?;
let update = BdkUpdate {
last_active_indices: BTreeMap::default(),
graph: result.graph_update,
chain: Some(result.chain_update),
};
Ok(Arc::new(Update(update)))
}
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), EsploraError> {
let bdk_transaction: BdkTransaction = transaction.into();
self.0
.broadcast(&bdk_transaction)
.map_err(EsploraError::from)
}
}

357
bdk-ffi/src/keys.rs Normal file
View File

@ -0,0 +1,357 @@
use crate::error::{Bip32Error, Bip39Error, DescriptorKeyError};
use std::fmt::Display;
use bdk_wallet::bitcoin::bip32::DerivationPath as BdkDerivationPath;
use bdk_wallet::bitcoin::key::Secp256k1;
use bdk_wallet::bitcoin::secp256k1::rand;
use bdk_wallet::bitcoin::secp256k1::rand::Rng;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::keys::bip39::WordCount;
use bdk_wallet::keys::bip39::{Language, Mnemonic as BdkMnemonic};
use bdk_wallet::keys::{
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
};
use bdk_wallet::miniscript::descriptor::{DescriptorXKey, Wildcard};
use bdk_wallet::miniscript::BareCtx;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
pub(crate) struct Mnemonic(BdkMnemonic);
impl Mnemonic {
pub(crate) fn new(word_count: WordCount) -> Self {
// TODO 4: I DON'T KNOW IF THIS IS A DECENT WAY TO GENERATE ENTROPY PLEASE CONFIRM
let mut rng = rand::thread_rng();
let mut entropy = [0u8; 32];
rng.fill(&mut entropy);
let generated_key: GeneratedKey<_, BareCtx> =
BdkMnemonic::generate_with_entropy((word_count, Language::English), entropy).unwrap();
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
Mnemonic(mnemonic)
}
pub(crate) fn from_string(mnemonic: String) -> Result<Self, Bip39Error> {
BdkMnemonic::from_str(&mnemonic)
.map(Mnemonic)
.map_err(Bip39Error::from)
}
pub(crate) fn from_entropy(entropy: Vec<u8>) -> Result<Self, Bip39Error> {
BdkMnemonic::from_entropy(entropy.as_slice())
.map(Mnemonic)
.map_err(Bip39Error::from)
}
}
impl Display for Mnemonic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
pub(crate) struct DerivationPath {
inner_mutex: Mutex<BdkDerivationPath>,
}
impl DerivationPath {
pub(crate) fn new(path: String) -> Result<Self, Bip32Error> {
BdkDerivationPath::from_str(&path)
.map(|x| DerivationPath {
inner_mutex: Mutex::new(x),
})
.map_err(Bip32Error::from)
}
}
#[derive(Debug)]
pub struct DescriptorSecretKey(pub(crate) BdkDescriptorSecretKey);
impl DescriptorSecretKey {
pub(crate) fn new(network: Network, mnemonic: &Mnemonic, password: Option<String>) -> Self {
let mnemonic = mnemonic.0.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(),
derivation_path: BdkDerivationPath::master(),
wildcard: Wildcard::Unhardened,
});
Self(descriptor_secret_key)
}
pub(crate) fn from_string(private_key: String) -> Result<Self, DescriptorKeyError> {
let descriptor_secret_key = BdkDescriptorSecretKey::from_str(private_key.as_str())
.map_err(DescriptorKeyError::from)?;
Ok(Self(descriptor_secret_key))
}
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
let secp = Secp256k1::new();
let descriptor_secret_key = &self.0;
let path = path.inner_mutex.lock().unwrap().deref().clone();
match descriptor_secret_key {
BdkDescriptorSecretKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
let derived_xprv = descriptor_x_key
.xkey
.derive_priv(&secp, &path)
.map_err(DescriptorKeyError::from)?;
let key_source = match descriptor_x_key.origin.clone() {
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
None => (descriptor_x_key.xkey.fingerprint(&secp), path),
};
let derived_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
origin: Some(key_source),
xkey: derived_xprv,
derivation_path: BdkDerivationPath::default(),
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self(derived_descriptor_secret_key)))
}
BdkDescriptorSecretKey::MultiXPrv(_) => Err(DescriptorKeyError::InvalidKeyType),
}
}
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
let descriptor_secret_key = &self.0;
let path = path.inner_mutex.lock().unwrap().deref().clone();
match descriptor_secret_key {
BdkDescriptorSecretKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
let extended_path = descriptor_x_key.derivation_path.extend(path);
let extended_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
origin: descriptor_x_key.origin.clone(),
xkey: descriptor_x_key.xkey,
derivation_path: extended_path,
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self(extended_descriptor_secret_key)))
}
BdkDescriptorSecretKey::MultiXPrv(_) => Err(DescriptorKeyError::InvalidKeyType),
}
}
pub(crate) fn as_public(&self) -> Arc<DescriptorPublicKey> {
let secp = Secp256k1::new();
let descriptor_public_key = self.0.to_public(&secp).unwrap();
Arc::new(DescriptorPublicKey(descriptor_public_key))
}
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
let inner = &self.0;
let secret_bytes: Vec<u8> = match inner {
BdkDescriptorSecretKey::Single(_) => {
unreachable!()
}
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
}
BdkDescriptorSecretKey::MultiXPrv(_) => {
unreachable!()
}
};
secret_bytes
}
pub(crate) fn as_string(&self) -> String {
self.0.to_string()
}
}
#[derive(Debug)]
pub struct DescriptorPublicKey(pub(crate) BdkDescriptorPublicKey);
impl DescriptorPublicKey {
pub(crate) fn from_string(public_key: String) -> Result<Self, DescriptorKeyError> {
let descriptor_public_key = BdkDescriptorPublicKey::from_str(public_key.as_str())
.map_err(DescriptorKeyError::from)?;
Ok(Self(descriptor_public_key))
}
pub(crate) fn derive(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
let secp = Secp256k1::new();
let descriptor_public_key = &self.0;
let path = path.inner_mutex.lock().unwrap().deref().clone();
match descriptor_public_key {
BdkDescriptorPublicKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
let derived_xpub = descriptor_x_key
.xkey
.derive_pub(&secp, &path)
.map_err(DescriptorKeyError::from)?;
let key_source = match descriptor_x_key.origin.clone() {
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
None => (descriptor_x_key.xkey.fingerprint(), path),
};
let derived_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
origin: Some(key_source),
xkey: derived_xpub,
derivation_path: BdkDerivationPath::default(),
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self(derived_descriptor_public_key)))
}
BdkDescriptorPublicKey::MultiXPub(_) => Err(DescriptorKeyError::InvalidKeyType),
}
}
pub(crate) fn extend(&self, path: &DerivationPath) -> Result<Arc<Self>, DescriptorKeyError> {
let descriptor_public_key = &self.0;
let path = path.inner_mutex.lock().unwrap().deref().clone();
match descriptor_public_key {
BdkDescriptorPublicKey::Single(_) => Err(DescriptorKeyError::InvalidKeyType),
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
let extended_path = descriptor_x_key.derivation_path.extend(path);
let extended_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
origin: descriptor_x_key.origin.clone(),
xkey: descriptor_x_key.xkey,
derivation_path: extended_path,
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self(extended_descriptor_public_key)))
}
BdkDescriptorPublicKey::MultiXPub(_) => Err(DescriptorKeyError::InvalidKeyType),
}
}
pub(crate) fn as_string(&self) -> String {
self.0.to_string()
}
}
#[cfg(test)]
mod test {
use crate::error::DescriptorKeyError;
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
use bdk_wallet::bitcoin::Network;
use std::sync::Arc;
fn get_inner() -> DescriptorSecretKey {
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
DescriptorSecretKey::new(Network::Testnet, &mnemonic, None)
}
fn derive_dsk(
key: &DescriptorSecretKey,
path: &str,
) -> Result<Arc<DescriptorSecretKey>, DescriptorKeyError> {
let path = DerivationPath::new(path.to_string()).unwrap();
key.derive(&path)
}
fn extend_dsk(
key: &DescriptorSecretKey,
path: &str,
) -> Result<Arc<DescriptorSecretKey>, DescriptorKeyError> {
let path = DerivationPath::new(path.to_string()).unwrap();
key.extend(&path)
}
fn derive_dpk(
key: &DescriptorPublicKey,
path: &str,
) -> Result<Arc<DescriptorPublicKey>, DescriptorKeyError> {
let path = DerivationPath::new(path.to_string()).unwrap();
key.derive(&path)
}
fn extend_dpk(
key: &DescriptorPublicKey,
path: &str,
) -> Result<Arc<DescriptorPublicKey>, DescriptorKeyError> {
let path = DerivationPath::new(path.to_string()).unwrap();
key.extend(&path)
}
#[test]
fn test_generate_descriptor_secret_key() {
let master_dsk = get_inner();
assert_eq!(master_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*");
assert_eq!(master_dsk.as_public().as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*");
}
#[test]
fn test_derive_self() {
let master_dsk = get_inner();
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m").unwrap();
assert_eq!(derived_dsk.as_string(), "[d1d04177]tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*");
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m").unwrap();
assert_eq!(derived_dpk.as_string(), "[d1d04177]tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*");
}
#[test]
fn test_derive_descriptors_keys() {
let master_dsk = get_inner();
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
let derived_dpk: &DescriptorPublicKey = &derive_dpk(master_dpk, "m/0").unwrap();
assert_eq!(derived_dpk.as_string(), "[d1d04177/0]tpubD9oaCiP1MPmQdndm7DCD3D3QU34pWd6BbKSRedoZF1UJcNhEk3PJwkALNYkhxeTKL29oGNR7psqvT1KZydCGqUDEKXN6dVQJY2R8ooLPy8m/*");
}
#[test]
fn test_extend_descriptor_keys() {
let master_dsk = get_inner();
let extended_dsk: &DescriptorSecretKey = &extend_dsk(&master_dsk, "m/0").unwrap();
assert_eq!(extended_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/0/*");
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
let extended_dpk: &DescriptorPublicKey = &extend_dpk(master_dpk, "m/0").unwrap();
assert_eq!(extended_dpk.as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/0/*");
let wif = "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch";
let extended_key = DescriptorSecretKey::from_string(wif.to_string()).unwrap();
let result = extended_key.derive(&DerivationPath::new("m/0".to_string()).unwrap());
dbg!(&result);
assert!(result.is_err());
}
#[test]
fn test_from_str_inner() {
let key1 = "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch";
let key2 = "tprv8ZgxMBicQKsPcwcD4gSnMti126ZiETsuX7qwrtMypr6FBwAP65puFn4v6c3jrN9VwtMRMph6nyT63NrfUL4C3nBzPcduzVSuHD7zbX2JKVc/1/1/1/*";
let private_descriptor_key1 = DescriptorSecretKey::from_string(key1.to_string()).unwrap();
let private_descriptor_key2 = DescriptorSecretKey::from_string(key2.to_string()).unwrap();
dbg!(private_descriptor_key1);
dbg!(private_descriptor_key2);
// Should error out because you can't produce a DescriptorSecretKey from an xpub
let key0 = "tpubDBrgjcxBxnXyL575sHdkpKohWu5qHKoQ7TJXKNrYznh5fVEGBv89hA8ENW7A8MFVpFUSvgLqc4Nj1WZcpePX6rrxviVtPowvMuGF5rdT2Vi";
assert!(DescriptorSecretKey::from_string(key0.to_string()).is_err());
}
#[test]
fn test_derive_and_extend_inner() {
let master_dsk = get_inner();
// derive DescriptorSecretKey with path "m/0" from master
let derived_dsk: &DescriptorSecretKey = &derive_dsk(&master_dsk, "m/0").unwrap();
assert_eq!(derived_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/*");
// extend derived_dsk with path "m/0"
let extended_dsk: &DescriptorSecretKey = &extend_dsk(derived_dsk, "m/0").unwrap();
assert_eq!(extended_dsk.as_string(), "[d1d04177/0]tprv8d7Y4JLmD25jkKbyDZXcdoPHu1YtMHuH21qeN7mFpjfumtSU7eZimFYUCSa3MYzkEYfSNRBV34GEr2QXwZCMYRZ7M1g6PUtiLhbJhBZEGYJ/0/*");
}
#[test]
fn test_derive_hardened_path_using_public() {
let master_dpk = get_inner().as_public();
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
assert!(derived_dpk.is_err());
}
// TODO 7: It appears that the to_hex() method is not available anymore.
// Look into the correct way to pull the hex out of the DescriptorSecretKey.
// Note: ToHex was removed in bitcoin_hashes 0.12.0
// #[test]
// fn test_retrieve_master_secret_key() {
// let master_dpk = get_inner();
// let master_private_key = master_dpk.secret_bytes().to_hex();
// assert_eq!(
// master_private_key,
// "e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
// )
// }
}

File diff suppressed because it is too large Load Diff

39
bdk-ffi/src/store.rs Normal file
View File

@ -0,0 +1,39 @@
use crate::error::SqliteError;
use crate::types::ChangeSet;
use bdk_sqlite::rusqlite::Connection;
use bdk_sqlite::{Store as BdkSqliteStore, Store};
use bdk_wallet::chain::ConfirmationTimeHeightAnchor;
use bdk_wallet::KeychainKind;
use std::sync::{Arc, Mutex, MutexGuard};
pub struct SqliteStore(Mutex<BdkSqliteStore<KeychainKind, ConfirmationTimeHeightAnchor>>);
impl SqliteStore {
pub fn new(path: String) -> Result<Self, SqliteError> {
let connection = Connection::open(path)?;
let db = Store::new(connection)?;
Ok(Self(Mutex::new(db)))
}
pub(crate) fn get_store(
&self,
) -> MutexGuard<BdkSqliteStore<KeychainKind, ConfirmationTimeHeightAnchor>> {
self.0.lock().expect("sqlite store")
}
pub fn write(&self, changeset: &ChangeSet) -> Result<(), SqliteError> {
self.get_store()
.write(&changeset.0)
.map_err(SqliteError::from)
}
pub fn read(&self) -> Result<Option<Arc<ChangeSet>>, SqliteError> {
self.get_store()
.read()
.map_err(SqliteError::from)
.map(|optional_bdk_change_set| optional_bdk_change_set.map(ChangeSet::from))
.map(|optional_change_set| optional_change_set.map(Arc::new))
}
}

173
bdk-ffi/src/types.rs Normal file
View File

@ -0,0 +1,173 @@
use crate::bitcoin::Amount;
use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut};
use crate::InspectError;
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
use bdk_wallet::bitcoin::Transaction as BdkTransaction;
use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest;
use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest;
use bdk_wallet::chain::tx_graph::CanonicalTx as BdkCanonicalTx;
use bdk_wallet::chain::{ChainPosition as BdkChainPosition, ConfirmationTimeHeightAnchor};
use bdk_wallet::wallet::AddressInfo as BdkAddressInfo;
use bdk_wallet::wallet::Balance as BdkBalance;
use bdk_wallet::KeychainKind;
use bdk_wallet::LocalOutput as BdkLocalOutput;
use bdk_electrum::bdk_chain::CombinedChangeSet;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChainPosition {
Confirmed { height: u32, timestamp: u64 },
Unconfirmed { timestamp: u64 },
}
pub struct CanonicalTx {
pub transaction: Arc<Transaction>,
pub chain_position: ChainPosition,
}
impl From<BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>> for CanonicalTx {
fn from(tx: BdkCanonicalTx<'_, Arc<BdkTransaction>, ConfirmationTimeHeightAnchor>) -> Self {
let chain_position = match tx.chain_position {
BdkChainPosition::Confirmed(anchor) => ChainPosition::Confirmed {
height: anchor.confirmation_height,
timestamp: anchor.confirmation_time,
},
BdkChainPosition::Unconfirmed(timestamp) => ChainPosition::Unconfirmed { timestamp },
};
CanonicalTx {
transaction: Arc::new(Transaction::from(tx.tx_node.tx.as_ref().clone())),
chain_position,
}
}
}
pub struct ScriptAmount {
pub script: Arc<Script>,
pub amount: Arc<Amount>,
}
pub struct AddressInfo {
pub index: u32,
pub address: Arc<Address>,
pub keychain: KeychainKind,
}
impl From<BdkAddressInfo> for AddressInfo {
fn from(address_info: BdkAddressInfo) -> Self {
AddressInfo {
index: address_info.index,
address: Arc::new(address_info.address.into()),
keychain: address_info.keychain,
}
}
}
pub struct Balance {
pub immature: Arc<Amount>,
pub trusted_pending: Arc<Amount>,
pub untrusted_pending: Arc<Amount>,
pub confirmed: Arc<Amount>,
pub trusted_spendable: Arc<Amount>,
pub total: Arc<Amount>,
}
impl From<BdkBalance> for Balance {
fn from(bdk_balance: BdkBalance) -> Self {
Balance {
immature: Arc::new(bdk_balance.immature.into()),
trusted_pending: Arc::new(bdk_balance.trusted_pending.into()),
untrusted_pending: Arc::new(bdk_balance.untrusted_pending.into()),
confirmed: Arc::new(bdk_balance.confirmed.into()),
trusted_spendable: Arc::new(bdk_balance.trusted_spendable().into()),
total: Arc::new(bdk_balance.total().into()),
}
}
}
pub struct ChangeSet(pub(crate) CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>);
impl From<CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>> for ChangeSet {
fn from(change_set: CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>) -> Self {
ChangeSet(change_set)
}
}
pub struct LocalOutput {
pub outpoint: OutPoint,
pub txout: TxOut,
pub keychain: KeychainKind,
pub is_spent: bool,
}
impl From<BdkLocalOutput> for LocalOutput {
fn from(local_utxo: BdkLocalOutput) -> Self {
LocalOutput {
outpoint: OutPoint {
txid: local_utxo.outpoint.txid.to_string(),
vout: local_utxo.outpoint.vout,
},
txout: TxOut {
value: local_utxo.txout.value.to_sat(),
script_pubkey: Arc::new(Script(local_utxo.txout.script_pubkey)),
},
keychain: local_utxo.keychain,
is_spent: local_utxo.is_spent,
}
}
}
// Callback for the FullScanRequest
pub trait FullScanScriptInspector: Sync + Send {
fn inspect(&self, keychain: KeychainKind, index: u32, script: Arc<Script>);
}
// Callback for the SyncRequest
pub trait SyncScriptInspector: Sync + Send {
fn inspect(&self, script: Arc<Script>, total: u64);
}
pub struct FullScanRequest(pub(crate) Mutex<Option<BdkFullScanRequest<KeychainKind>>>);
pub struct SyncRequest(pub(crate) Mutex<Option<BdkSyncRequest>>);
impl SyncRequest {
pub fn inspect_spks(
&self,
inspector: Arc<dyn SyncScriptInspector>,
) -> Result<Arc<Self>, InspectError> {
let mut guard = self.0.lock().unwrap();
if let Some(sync_request) = guard.take() {
let total = sync_request.spks.len() as u64;
let sync_request = sync_request.inspect_spks(move |spk| {
inspector.inspect(Arc::new(BdkScriptBuf::from(spk).into()), total)
});
Ok(Arc::new(SyncRequest(Mutex::new(Some(sync_request)))))
} else {
Err(InspectError::RequestAlreadyConsumed)
}
}
}
impl FullScanRequest {
pub fn inspect_spks_for_all_keychains(
&self,
inspector: Arc<dyn FullScanScriptInspector>,
) -> Result<Arc<Self>, InspectError> {
let mut guard = self.0.lock().unwrap();
if let Some(full_scan_request) = guard.take() {
let inspector = Arc::new(inspector);
let full_scan_request =
full_scan_request.inspect_spks_for_all_keychains(move |k, spk_i, script| {
inspector.inspect(k, spk_i, Arc::new(BdkScriptBuf::from(script).into()))
});
Ok(Arc::new(FullScanRequest(Mutex::new(Some(
full_scan_request,
)))))
} else {
Err(InspectError::RequestAlreadyConsumed)
}
}
}

726
bdk-ffi/src/wallet.rs Normal file
View File

@ -0,0 +1,726 @@
use crate::bitcoin::Amount;
use crate::bitcoin::{FeeRate, OutPoint, Psbt, Script, Transaction};
use crate::descriptor::Descriptor;
use crate::error::{
CalculateFeeError, CannotConnectError, CreateTxError, SignerError, TxidParseError,
WalletCreationError,
};
use crate::types::{
AddressInfo, Balance, CanonicalTx, ChangeSet, FullScanRequest, LocalOutput, ScriptAmount,
SyncRequest,
};
use bdk_wallet::bitcoin::amount::Amount as BdkAmount;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::bitcoin::Psbt as BdkPsbt;
use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf;
use bdk_wallet::bitcoin::{OutPoint as BdkOutPoint, Sequence, Txid};
use bdk_wallet::chain::{CombinedChangeSet, ConfirmationTimeHeightAnchor};
use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy;
use bdk_wallet::wallet::Update as BdkUpdate;
use bdk_wallet::Wallet as BdkWallet;
use bdk_wallet::{KeychainKind, SignOptions};
use std::collections::HashSet;
use std::str::FromStr;
use std::sync::{Arc, Mutex, MutexGuard};
pub struct Wallet {
inner_mutex: Mutex<BdkWallet>,
}
impl Wallet {
pub fn new(
descriptor: Arc<Descriptor>,
change_descriptor: Arc<Descriptor>,
network: Network,
) -> Result<Self, WalletCreationError> {
let descriptor = descriptor.to_string_with_secret();
let change_descriptor = change_descriptor.to_string_with_secret();
let wallet: BdkWallet = BdkWallet::new(&descriptor, &change_descriptor, network)?;
Ok(Wallet {
inner_mutex: Mutex::new(wallet),
})
}
pub fn new_or_load(
descriptor: Arc<Descriptor>,
change_descriptor: Arc<Descriptor>,
change_set: Option<Arc<ChangeSet>>,
network: Network,
) -> Result<Self, WalletCreationError> {
let descriptor = descriptor.to_string_with_secret();
let change_descriptor = change_descriptor.to_string_with_secret();
let change_set: Option<CombinedChangeSet<KeychainKind, ConfirmationTimeHeightAnchor>> =
change_set.map(|cs| cs.0.clone());
let wallet: BdkWallet =
BdkWallet::new_or_load(&descriptor, &change_descriptor, change_set, network)?;
Ok(Wallet {
inner_mutex: Mutex::new(wallet),
})
}
pub(crate) fn get_wallet(&self) -> MutexGuard<BdkWallet> {
self.inner_mutex.lock().expect("wallet")
}
pub fn reveal_next_address(&self, keychain_kind: KeychainKind) -> AddressInfo {
self.get_wallet().reveal_next_address(keychain_kind).into()
}
pub fn apply_update(&self, update: Arc<Update>) -> Result<(), CannotConnectError> {
self.get_wallet()
.apply_update(update.0.clone())
.map_err(CannotConnectError::from)
}
pub fn network(&self) -> Network {
self.get_wallet().network()
}
pub fn balance(&self) -> Balance {
let bdk_balance = self.get_wallet().balance();
Balance::from(bdk_balance)
}
pub fn is_mine(&self, script: &Script) -> bool {
self.get_wallet().is_mine(&script.0)
}
pub(crate) fn sign(
&self,
psbt: Arc<Psbt>,
// sign_options: Option<SignOptions>,
) -> Result<bool, SignerError> {
let mut psbt = psbt.0.lock().unwrap();
self.get_wallet()
.sign(&mut psbt, SignOptions::default())
.map_err(SignerError::from)
}
pub fn sent_and_received(&self, tx: &Transaction) -> SentAndReceivedValues {
let (sent, received) = self.get_wallet().sent_and_received(&tx.into());
SentAndReceivedValues {
sent: Arc::new(sent.into()),
received: Arc::new(received.into()),
}
}
pub fn transactions(&self) -> Vec<CanonicalTx> {
self.get_wallet()
.transactions()
.map(|tx| tx.into())
.collect()
}
pub fn get_tx(&self, txid: String) -> Result<Option<CanonicalTx>, TxidParseError> {
let txid =
Txid::from_str(txid.as_str()).map_err(|_| TxidParseError::InvalidTxid { txid })?;
Ok(self.get_wallet().get_tx(txid).map(|tx| tx.into()))
}
pub fn calculate_fee(&self, tx: &Transaction) -> Result<Arc<Amount>, CalculateFeeError> {
self.get_wallet()
.calculate_fee(&tx.into())
.map(Amount::from)
.map(Arc::new)
.map_err(|e| e.into())
}
pub fn calculate_fee_rate(&self, tx: &Transaction) -> Result<Arc<FeeRate>, CalculateFeeError> {
self.get_wallet()
.calculate_fee_rate(&tx.into())
.map(|bdk_fee_rate| Arc::new(FeeRate(bdk_fee_rate)))
.map_err(|e| e.into())
}
pub fn list_unspent(&self) -> Vec<LocalOutput> {
self.get_wallet().list_unspent().map(|o| o.into()).collect()
}
pub fn list_output(&self) -> Vec<LocalOutput> {
self.get_wallet().list_output().map(|o| o.into()).collect()
}
pub fn start_full_scan(&self) -> Arc<FullScanRequest> {
let request = self.get_wallet().start_full_scan();
Arc::new(FullScanRequest(Mutex::new(Some(request))))
}
pub fn start_sync_with_revealed_spks(&self) -> Arc<SyncRequest> {
let request = self.get_wallet().start_sync_with_revealed_spks();
Arc::new(SyncRequest(Mutex::new(Some(request))))
}
pub fn take_staged(&self) -> Option<Arc<ChangeSet>> {
self.get_wallet()
.take_staged()
.map(|change_set| Arc::new(change_set.into()))
}
}
pub struct SentAndReceivedValues {
pub sent: Arc<Amount>,
pub received: Arc<Amount>,
}
pub struct Update(pub(crate) BdkUpdate);
#[derive(Clone, Debug)]
pub struct TxBuilder {
pub(crate) recipients: Vec<(BdkScriptBuf, BdkAmount)>,
pub(crate) utxos: Vec<OutPoint>,
pub(crate) unspendable: HashSet<OutPoint>,
pub(crate) change_policy: ChangeSpendPolicy,
pub(crate) manually_selected_only: bool,
pub(crate) fee_rate: Option<FeeRate>,
pub(crate) fee_absolute: Option<Arc<Amount>>,
pub(crate) drain_wallet: bool,
pub(crate) drain_to: Option<BdkScriptBuf>,
pub(crate) rbf: Option<RbfValue>,
// pub(crate) data: Vec<u8>,
}
impl TxBuilder {
pub(crate) fn new() -> Self {
TxBuilder {
recipients: Vec::new(),
utxos: Vec::new(),
unspendable: HashSet::new(),
change_policy: ChangeSpendPolicy::ChangeAllowed,
manually_selected_only: false,
fee_rate: None,
fee_absolute: None,
drain_wallet: false,
drain_to: None,
rbf: None,
// data: Vec::new(),
}
}
pub(crate) fn add_recipient(&self, script: &Script, amount: Arc<Amount>) -> Arc<Self> {
let mut recipients: Vec<(BdkScriptBuf, BdkAmount)> = self.recipients.clone();
recipients.append(&mut vec![(script.0.clone(), amount.0)]);
Arc::new(TxBuilder {
recipients,
..self.clone()
})
}
pub(crate) fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
let recipients = recipients
.iter()
.map(|script_amount| (script_amount.script.0.clone(), script_amount.amount.0)) //;
.collect();
Arc::new(TxBuilder {
recipients,
..self.clone()
})
}
pub(crate) fn add_unspendable(&self, unspendable: OutPoint) -> Arc<Self> {
let mut unspendable_hash_set = self.unspendable.clone();
unspendable_hash_set.insert(unspendable);
Arc::new(TxBuilder {
unspendable: unspendable_hash_set,
..self.clone()
})
}
pub(crate) fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
Arc::new(TxBuilder {
unspendable: unspendable.into_iter().collect(),
..self.clone()
})
}
pub(crate) fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
self.add_utxos(vec![outpoint])
}
pub(crate) fn add_utxos(&self, mut outpoints: Vec<OutPoint>) -> Arc<Self> {
let mut utxos = self.utxos.to_vec();
utxos.append(&mut outpoints);
Arc::new(TxBuilder {
utxos,
..self.clone()
})
}
pub(crate) fn change_policy(&self, change_policy: ChangeSpendPolicy) -> Arc<Self> {
Arc::new(TxBuilder {
change_policy,
..self.clone()
})
}
pub(crate) fn do_not_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::ChangeForbidden,
..self.clone()
})
}
pub(crate) fn only_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::OnlyChange,
..self.clone()
})
}
pub(crate) fn manually_selected_only(&self) -> Arc<Self> {
Arc::new(TxBuilder {
manually_selected_only: true,
..self.clone()
})
}
pub(crate) fn fee_rate(&self, fee_rate: &FeeRate) -> Arc<Self> {
Arc::new(TxBuilder {
fee_rate: Some(fee_rate.clone()),
..self.clone()
})
}
pub(crate) fn fee_absolute(&self, fee_amount: Arc<Amount>) -> Arc<Self> {
Arc::new(TxBuilder {
fee_absolute: Some(fee_amount),
..self.clone()
})
}
pub(crate) fn drain_wallet(&self) -> Arc<Self> {
Arc::new(TxBuilder {
drain_wallet: true,
..self.clone()
})
}
pub(crate) fn drain_to(&self, script: &Script) -> Arc<Self> {
Arc::new(TxBuilder {
drain_to: Some(script.0.clone()),
..self.clone()
})
}
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
Arc::new(TxBuilder {
rbf: Some(RbfValue::Default),
..self.clone()
})
}
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(TxBuilder {
rbf: Some(RbfValue::Value(nsequence)),
..self.clone()
})
}
pub(crate) fn finish(&self, wallet: &Arc<Wallet>) -> Result<Arc<Psbt>, CreateTxError> {
// TODO: I had to change the wallet here to be mutable. Why is that now required with the 1.0 API?
let mut wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_tx();
for (script, amount) in &self.recipients {
tx_builder.add_recipient(script.clone(), *amount);
}
tx_builder.change_policy(self.change_policy);
if !self.utxos.is_empty() {
let bdk_utxos: Vec<BdkOutPoint> = self.utxos.iter().map(BdkOutPoint::from).collect();
tx_builder
.add_utxos(&bdk_utxos)
.map_err(CreateTxError::from)?;
}
if !self.unspendable.is_empty() {
let bdk_unspendable: Vec<BdkOutPoint> =
self.unspendable.iter().map(BdkOutPoint::from).collect();
tx_builder.unspendable(bdk_unspendable);
}
if self.manually_selected_only {
tx_builder.manually_selected_only();
}
if let Some(fee_rate) = &self.fee_rate {
tx_builder.fee_rate(fee_rate.0);
}
if let Some(fee_amount) = &self.fee_absolute {
tx_builder.fee_absolute(fee_amount.0);
}
if self.drain_wallet {
tx_builder.drain_wallet();
}
if let Some(script) = &self.drain_to {
tx_builder.drain_to(script.clone());
}
if let Some(rbf) = &self.rbf {
match *rbf {
RbfValue::Default => {
tx_builder.enable_rbf();
}
RbfValue::Value(nsequence) => {
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
}
}
}
let psbt = tx_builder.finish().map_err(CreateTxError::from)?;
Ok(Arc::new(psbt.into()))
}
}
#[derive(Clone)]
pub(crate) struct BumpFeeTxBuilder {
pub(crate) txid: String,
pub(crate) fee_rate: Arc<FeeRate>,
pub(crate) rbf: Option<RbfValue>,
}
impl BumpFeeTxBuilder {
pub(crate) fn new(txid: String, fee_rate: Arc<FeeRate>) -> Self {
Self {
txid,
fee_rate,
rbf: None,
}
}
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
Arc::new(Self {
rbf: Some(RbfValue::Default),
..self.clone()
})
}
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(Self {
rbf: Some(RbfValue::Value(nsequence)),
..self.clone()
})
}
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<Arc<Psbt>, CreateTxError> {
let txid = Txid::from_str(self.txid.as_str()).map_err(|_| CreateTxError::UnknownUtxo {
outpoint: self.txid.clone(),
})?;
let mut wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_fee_bump(txid).map_err(CreateTxError::from)?;
tx_builder.fee_rate(self.fee_rate.0);
if let Some(rbf) = &self.rbf {
match *rbf {
RbfValue::Default => {
tx_builder.enable_rbf();
}
RbfValue::Value(nsequence) => {
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
}
}
}
let psbt: BdkPsbt = tx_builder.finish()?;
Ok(Arc::new(psbt.into()))
}
}
#[derive(Clone, Debug)]
pub enum RbfValue {
Default,
Value(u32),
}
// #[cfg(test)]
// mod test {
// use crate::database::DatabaseConfig;
// use crate::descriptor::Descriptor;
// use crate::keys::{DescriptorSecretKey, Mnemonic};
// use crate::wallet::{AddressIndex, TxBuilder, Wallet};
// use crate::Script;
// use assert_matches::assert_matches;
// use bdk::bitcoin::{Address, Network};
// // use bdk::wallet::get_funded_wallet;
// use bdk::KeychainKind;
// use std::str::FromStr;
// use std::sync::{Arc, Mutex};
//
// // #[test]
// // fn test_drain_wallet() {
// // let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
// // let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
// // let test_wallet = Wallet {
// // inner_mutex: Mutex::new(funded_wallet),
// // };
// // let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
// // let drain_to_script = crate::Address::new(drain_to_address)
// // .unwrap()
// // .script_pubkey();
// // let tx_builder = TxBuilder::new()
// // .drain_wallet()
// // .drain_to(drain_to_script.clone());
// // assert!(tx_builder.drain_wallet);
// // assert_eq!(tx_builder.drain_to, Some(drain_to_script.inner.clone()));
// //
// // let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
// // let psbt = tx_builder_result.psbt.inner.lock().unwrap().clone();
// // let tx_details = tx_builder_result.transaction_details;
// //
// // // confirm one input with 50,000 sats
// // assert_eq!(psbt.inputs.len(), 1);
// // let input_value = psbt
// // .inputs
// // .get(0)
// // .cloned()
// // .unwrap()
// // .non_witness_utxo
// // .unwrap()
// // .output
// // .get(0)
// // .unwrap()
// // .value;
// // assert_eq!(input_value, 50_000_u64);
// //
// // // confirm one output to correct address with all sats - fee
// // assert_eq!(psbt.outputs.len(), 1);
// // let output_address = Address::from_script(
// // &psbt
// // .unsigned_tx
// // .output
// // .get(0)
// // .cloned()
// // .unwrap()
// // .script_pubkey,
// // Network::Testnet,
// // )
// // .unwrap();
// // assert_eq!(
// // output_address,
// // Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap()
// // );
// // let output_value = psbt.unsigned_tx.output.get(0).cloned().unwrap().value;
// // assert_eq!(output_value, 49_890_u64); // input - fee
// //
// // assert_eq!(
// // tx_details.txid,
// // "312f1733badab22dc26b8dcbc83ba5629fb7b493af802e8abe07d865e49629c5"
// // );
// // assert_eq!(tx_details.received, 0);
// // assert_eq!(tx_details.sent, 50000);
// // assert!(tx_details.fee.is_some());
// // assert_eq!(tx_details.fee.unwrap(), 110);
// // assert!(tx_details.confirmation_time.is_none());
// // }
//
// #[test]
// fn test_peek_reset_address() {
// let test_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
// let descriptor = Descriptor::new(test_wpkh.to_string(), Network::Regtest).unwrap();
// let change_descriptor = Descriptor::new(
// test_wpkh.to_string().replace("/0/*", "/1/*"),
// Network::Regtest,
// )
// .unwrap();
//
// let wallet = Wallet::new(
// Arc::new(descriptor),
// Some(Arc::new(change_descriptor)),
// Network::Regtest,
// DatabaseConfig::Memory,
// )
// .unwrap();
//
// assert_eq!(
// wallet
// .get_address(AddressIndex::Peek { index: 2 })
// .unwrap()
// .address
// .as_string(),
// "bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
// );
//
// assert_eq!(
// wallet
// .get_address(AddressIndex::Peek { index: 1 })
// .unwrap()
// .address
// .as_string(),
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
// );
//
// // new index still 0
// assert_eq!(
// wallet
// .get_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
// );
//
// // new index now 1
// assert_eq!(
// wallet
// .get_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
// );
//
// // new index now 2
// assert_eq!(
// wallet
// .get_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
// );
//
// // peek index 1
// assert_eq!(
// wallet
// .get_address(AddressIndex::Peek { index: 1 })
// .unwrap()
// .address
// .as_string(),
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
// );
//
// // reset to index 0
// // assert_eq!(
// // wallet
// // .get_address(AddressIndex::Reset { index: 0 })
// // .unwrap()
// // .address
// // .as_string(),
// // "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
// // );
//
// // new index 1 again
// assert_eq!(
// wallet
// .get_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
// );
// }
//
// #[test]
// fn test_get_address() {
// let test_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
// let descriptor = Descriptor::new(test_wpkh.to_string(), Network::Regtest).unwrap();
// let change_descriptor = Descriptor::new(
// test_wpkh.to_string().replace("/0/*", "/1/*"),
// Network::Regtest,
// )
// .unwrap();
//
// let wallet = Wallet::new(
// Arc::new(descriptor),
// Some(Arc::new(change_descriptor)),
// Network::Regtest,
// DatabaseConfig::Memory,
// )
// .unwrap();
//
// assert_eq!(
// wallet
// .get_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
// );
//
// assert_eq!(
// wallet
// .get_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
// );
//
// assert_eq!(
// wallet
// .get_address(AddressIndex::LastUnused)
// .unwrap()
// .address
// .as_string(),
// "bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
// );
//
// assert_eq!(
// wallet
// .get_internal_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1qpmz73cyx00r4a5dea469j40ax6d6kqyd67nnpj"
// );
//
// assert_eq!(
// wallet
// .get_internal_address(AddressIndex::New)
// .unwrap()
// .address
// .as_string(),
// "bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
// );
//
// assert_eq!(
// wallet
// .get_internal_address(AddressIndex::LastUnused)
// .unwrap()
// .address
// .as_string(),
// "bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
// );
// }
//
// #[test]
// fn test_is_mine() {
// // is_mine should return true for addresses generated by the wallet
// let mnemonic: Mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
// let secret_key: DescriptorSecretKey =
// DescriptorSecretKey::new(Network::Testnet, Arc::new(mnemonic), None);
// let descriptor: Descriptor = Descriptor::new_bip84(
// Arc::new(secret_key),
// KeychainKind::External,
// Network::Testnet,
// );
// let wallet: Wallet = Wallet::new(
// Arc::new(descriptor),
// None,
// Network::Testnet,
// DatabaseConfig::Memory,
// )
// .unwrap();
//
// // let address = wallet.get_address(AddressIndex::New).unwrap();
// // let script: Arc<Script> = address.address.script_pubkey();
//
// // let is_mine_1: bool = wallet.is_mine(script).unwrap();
// // assert!(is_mine_1);
//
// // is_mine returns false when provided a script that is not in the wallet
// let other_wpkh = "wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)";
// let other_descriptor = Descriptor::new(other_wpkh.to_string(), Network::Testnet).unwrap();
//
// let other_wallet = Wallet::new(
// Arc::new(other_descriptor),
// None,
// Network::Testnet,
// DatabaseConfig::Memory,
// )
// .unwrap();
//
// let other_address = other_wallet.get_address(AddressIndex::New).unwrap();
// let other_script: Arc<Script> = other_address.address.script_pubkey();
// let is_mine_2: bool = wallet.is_mine(other_script).unwrap();
// assert_matches!(is_mine_2, false);
// }
// }

View File

@ -5,7 +5,7 @@ supported bindings languages.
To skip integration tests and only run unit tests use `cargo test --lib`.
To run all tests including integration tests use `CLASSPATH=./tests/jna/jna-5.8.0.jar cargo test`.
To run all tests including integration tests use `CLASSPATH=./tests/jna/jna-5.14.0.jar cargo test`.
Before running integration tests you must install the following development tools:

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

@ -4,6 +4,6 @@
*/
import Foundation
import bdk
import BitcoinDevKit
let network = Network.testnet

Binary file not shown.

Binary file not shown.

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 @@
[bindings.kotlin]
android = true
android_cleaner = true

View File

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

View File

@ -5,8 +5,6 @@ cdylib_name = "bdkffi"
[bindings.python]
cdylib_name = "bdkffi"
[bindings.ruby]
cdylib_name = "bdkffi"
[bindings.swift]
module_name = "BitcoinDevKit"
cdylib_name = "bdkffi"

View File

@ -1,5 +1,5 @@
# bdk-android
This project builds a .jar package for the JVM platform that provide Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository.
# bdk-jvm
This project builds a .jar package for the JVM platform that provides Kotlin language bindings for the [`bdk`] library. The Kotlin language bindings are created by the `bdk-ffi` project which is included in the root of this repository.
## How to Use
To use the Kotlin language bindings for [`bdk`] in your JVM project add the following to your gradle dependencies:
@ -13,24 +13,6 @@ dependencies {
}
```
You may then import and use the `org.bitcoindevkit` library in your Kotlin code. For example:
```kotlin
import org.bitcoindevkit.*
// ...
val externalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"
val databaseConfig = DatabaseConfig.Memory
val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
)
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
```
### Snapshot releases
To use a snapshot release, specify the snapshot repository url in the `repositories` block and use the snapshot version in the `dependencies` block:
```kotlin
@ -38,37 +20,44 @@ 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.
_Note that Kotlin version `1.9.23` or later is required to build the library._
1. Install JDK 17. For example, with SDKMAN!:
```shell
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk install java 17.0.2-tem
```
2. Install Rust (note that we are currently building using Rust 1.77.1):
```shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
rustup default 1.77.1
```
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
cd bdk-jvm
./gradlew publishToMavenLocal --exclude-task signMavenPublication
./gradlew publishToMavenLocal -P localBuild
```
Note that the commands assume you don't need the local libraries to be signed. If you do wish to sign them, simply set your `~/.gradle/gradle.properties` signing key values like so:
@ -77,16 +66,18 @@ signing.gnupg.keyName=<YOUR_GNUPG_ID>
signing.gnupg.passphrase=<YOUR_GNUPG_PASSPHRASE>
```
and use the `publishToMavenLocal` task without excluding the signing task:
and use the `publishToMavenLocal` task without the `localBuild` flag:
```shell
./gradlew publishToMavenLocal
```
## Known issues
## JNA dependency
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
```shell
class file for com.sun.jna.Pointer not found
```
The solution is to add JNA as a dependency like so:
```kotlin
dependencies {

View File

@ -1,4 +1,9 @@
plugins {
id("org.jetbrains.kotlin.jvm").version("1.9.23").apply(false)
id("org.gradle.java-library")
id("org.gradle.maven-publish")
id("org.gradle.signing")
id("org.bitcoindevkit.plugins.generate-jvm-bindings").apply(false)
id("io.github.gradle-nexus.publish-plugin") version "1.1.0"
}

View File

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

Binary file not shown.

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

31
bdk-jvm/gradlew vendored
View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -80,13 +80,11 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@ -133,22 +131,29 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@ -193,6 +198,10 @@ if "$cygwin" || "$msys" ; then
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
@ -205,6 +214,12 @@ set -- \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
if ! command -v xargs >/dev/null 2>&1
then
die "xargs is not available"
fi
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.

15
bdk-jvm/gradlew.bat vendored
View File

@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@ -25,7 +25,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal

23
bdk-jvm/justfile Normal file
View File

@ -0,0 +1,23 @@
default:
just --list
build:
./gradlew buildJvmLib
clean:
rm -rf ../bdk-ffi/target/
rm -rf ./build/
rm -rf ./lib/build/
rm -rf ./plugins/build/
publish-local:
./gradlew publishToMavenLocal -P localBuild
test:
./gradlew test
test-offline:
./gradlew test -P excludeConnectedTests
test-specific TEST:
./gradlew test --tests {{TEST}}

View File

@ -6,45 +6,64 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
val libraryVersion: String by project
plugins {
id("org.jetbrains.kotlin.jvm") version "1.6.10"
id("java-library")
id("maven-publish")
id("signing")
id("org.jetbrains.kotlin.jvm")
id("org.gradle.java-library")
id("org.gradle.maven-publish")
id("org.gradle.signing")
// Custom plugin to generate the native libs and bindings file
id("org.bitcoindevkit.plugins.generate-jvm-bindings")
}
repositories {
mavenCentral()
}
java {
sourceCompatibility = JavaVersion.VERSION_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 or signet.
// 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("**/LiveElectrumClientTest.class")
exclude("**/LiveMemoryWalletTest.class")
exclude("**/LiveTransactionTest.class")
exclude("**/LiveTxBuilderTest.class")
exclude("**/LiveWalletTest.class")
}
}
testing {
suites {
val test by getting(JvmTestSuite::class) {
useKotlinTest("1.9.23")
}
}
}
tasks.withType<Test> {
testLogging {
events(PASSED, SKIPPED, FAILED, STANDARD_OUT, STANDARD_ERROR)
exceptionFormat = FULL
showExceptions = true
showCauses = true
showStackTraces = true
showCauses = true
}
}
dependencies {
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
implementation("net.java.dev.jna:jna:5.8.0")
implementation("net.java.dev.jna:jna:5.14.0")
api("org.slf4j:slf4j-api:1.7.30")
testImplementation("junit:junit:4.13.2")
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2")
// testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.1")
// testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.1")
// testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2")
testImplementation("ch.qos.logback:logback-classic:1.2.3")
testImplementation("ch.qos.logback:logback-core:1.2.3")
}
@ -74,14 +93,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 {
@ -96,6 +110,10 @@ afterEvaluate {
}
signing {
if (project.hasProperty("localBuild")) {
isRequired = false
}
val signingKeyId: String? by project
val signingKey: String? by project
val signingPassword: String? by project
@ -107,4 +125,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,39 @@
package org.bitcoindevkit
import kotlin.test.Test
private const val SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
class LiveElectrumClientTest {
private val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
private val changeDescriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
Network.SIGNET
)
@Test
fun testSyncedBalance() {
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val electrumClient: ElectrumClient = ElectrumClient(SIGNET_ELECTRUM_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = electrumClient.fullScan(fullScanRequest, 10uL, 10uL, false)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
println("Transactions count: ${wallet.transactions().count()}")
val transactions = wallet.transactions().take(3)
for (tx in transactions) {
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
println("Transaction: ${tx.transaction.computeTxid()}")
println("Sent ${sentAndReceived.sent.toSat()}")
println("Received ${sentAndReceived.received.toSat()}")
}
}
}

View File

@ -0,0 +1,67 @@
package org.bitcoindevkit
import kotlin.test.Test
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
class LiveMemoryWalletTest {
private val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
private val changeDescriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
Network.SIGNET
)
@Test
fun testSyncedBalance() {
val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
println("Transactions count: ${wallet.transactions().count()}")
val transactions = wallet.transactions().take(3)
for (tx in transactions) {
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
println("Transaction: ${tx.transaction.computeTxid()}")
println("Sent ${sentAndReceived.sent.toSat()}")
println("Received ${sentAndReceived.received.toSat()}")
}
}
@Test
fun testScriptInspector() {
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val scriptInspector: FullScriptInspector = FullScriptInspector()
val fullScanRequest: FullScanRequest = wallet.startFullScan().inspectSpksForAllKeychains(scriptInspector)
val update = esploraClient.fullScan(fullScanRequest, 21uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
}
}
class FullScriptInspector: FullScanScriptInspector {
override fun inspect(keychain: KeychainKind, index: UInt, script: Script){
println("Inspecting index $index for keychain $keychain")
}
}

View File

@ -0,0 +1,47 @@
package org.bitcoindevkit
import kotlin.test.Test
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
class LiveTransactionTests {
private val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
private val changeDescriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
Network.SIGNET
)
@Test
fun testSyncedBalance() {
val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Wallet balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val transaction: Transaction = wallet.transactions().first().transaction
println("First transaction:")
println("Txid: ${transaction.computeTxid()}")
println("Version: ${transaction.version()}")
println("Total size: ${transaction.totalSize()}")
println("Vsize: ${transaction.vsize()}")
println("Weight: ${transaction.weight()}")
println("Coinbase transaction: ${transaction.isCoinbase()}")
println("Is explicitly RBF: ${transaction.isExplicitlyRbf()}")
println("Inputs: ${transaction.input()}")
println("Outputs: ${transaction.output()}")
}
}

View File

@ -0,0 +1,88 @@
package org.bitcoindevkit
import java.io.File
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertTrue
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
class LiveTxBuilderTest {
private val persistenceFilePath = run {
val currentDirectory = System.getProperty("user.dir")
"$currentDirectory/bdk_persistence.sqlite"
}
private val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
private val changeDescriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
Network.SIGNET
)
@AfterTest
fun cleanup() {
val file = File(persistenceFilePath)
if (file.exists()) {
file.delete()
}
}
@Test
fun testTxBuilder() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
val psbt: Psbt = TxBuilder()
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
.feeRate(FeeRate.fromSatPerVb(2uL))
.finish(wallet)
println(psbt.serialize())
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
}
@Test
fun complexTxBuilder() {
val wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val recipient1: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
val recipient2: Address = Address("tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6", Network.SIGNET)
val allRecipients: List<ScriptAmount> = listOf(
ScriptAmount(recipient1.scriptPubkey(), Amount.fromSat(4200uL)),
ScriptAmount(recipient2.scriptPubkey(), Amount.fromSat(4200uL)),
)
val psbt: Psbt = TxBuilder()
.setRecipients(allRecipients)
.feeRate(FeeRate.fromSatPerVb(4uL))
.changePolicy(ChangeSpendPolicy.CHANGE_FORBIDDEN)
.enableRbf()
.finish(wallet)
wallet.sign(psbt)
assertTrue(psbt.serialize().startsWith("cHNi"), "PSBT should start with 'cHNi'")
}
}

View File

@ -0,0 +1,94 @@
package org.bitcoindevkit
import java.io.File
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertTrue
private const val SIGNET_ESPLORA_URL = "http://signet.bitcoindevkit.net"
private const val TESTNET_ESPLORA_URL = "https://esplora.testnet.kuutamo.cloud"
class LiveWalletTest {
private val persistenceFilePath = run {
val currentDirectory = System.getProperty("user.dir")
"$currentDirectory/bdk_persistence.sqlite"
}
private val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
private val changeDescriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
Network.SIGNET
)
@AfterTest
fun cleanup() {
val file = File(persistenceFilePath)
if (file.exists()) {
file.delete()
}
}
@Test
fun testSyncedBalance() {
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient: EsploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
println("Transactions count: ${wallet.transactions().count()}")
val transactions = wallet.transactions().take(3)
for (tx in transactions) {
val sentAndReceived = wallet.sentAndReceived(tx.transaction)
println("Transaction: ${tx.transaction.computeTxid()}")
println("Sent ${sentAndReceived.sent}")
println("Received ${sentAndReceived.received}")
}
}
@Test
fun testBroadcastTransaction() {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.SIGNET)
val wallet: Wallet = Wallet(descriptor, changeDescriptor, Network.SIGNET)
val esploraClient = EsploraClient(SIGNET_ESPLORA_URL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.balance().total.toSat()}")
assert(wallet.balance().total.toSat() > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.SIGNET)
val psbt: Psbt = TxBuilder()
.addRecipient(recipient.scriptPubkey(), Amount.fromSat(4200uL))
.feeRate(FeeRate.fromSatPerVb(2uL))
.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.computeTxid()}")
val txFee: Amount = wallet.calculateFee(tx)
println("Tx fee is: ${txFee}")
val feeRate: FeeRate = wallet.calculateFeeRate(tx)
println("Tx fee rate is: ${feeRate.toSatPerVbCeil()} sat/vB")
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.toString()
)
}
}

View File

@ -0,0 +1,45 @@
package org.bitcoindevkit
import kotlin.test.Test
import kotlin.test.assertEquals
class OfflinePersistenceTest {
private val persistenceFilePath = run {
val currentDirectory = System.getProperty("user.dir")
val dbFileName = "pre_existing_wallet_persistence_test.sqlite"
"$currentDirectory/src/test/resources/$dbFileName"
}
private val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.SIGNET
)
private val changeDescriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
Network.SIGNET
)
@Test
fun testPersistence() {
val sqliteStore: SqliteStore = SqliteStore(persistenceFilePath)
val initialChangeSet: ChangeSet? = sqliteStore.read()
requireNotNull(initialChangeSet) { "ChangeSet should not be null after loading a valid database" }
val wallet: Wallet = Wallet.newOrLoad(
descriptor,
changeDescriptor,
initialChangeSet,
Network.SIGNET,
)
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
println("Address: $addressInfo")
assertEquals(
expected = 7u,
actual = addressInfo.index,
)
assertEquals(
expected = "tb1qan3lldunh37ma6c0afeywgjyjgnyc8uz975zl2",
actual = addressInfo.address.toString(),
)
}
}

View File

@ -0,0 +1,74 @@
package org.bitcoindevkit
import java.io.File
import kotlin.test.AfterTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
import kotlin.test.assertFalse
class OfflineWalletTest {
private val persistenceFilePath = run {
val currentDirectory = System.getProperty("user.dir")
"$currentDirectory/bdk_persistence.sqlite"
}
private val descriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
Network.TESTNET
)
private val changeDescriptor: Descriptor = Descriptor(
"wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)",
Network.TESTNET
)
@AfterTest
fun cleanup() {
val file = File(persistenceFilePath)
if (file.exists()) {
file.delete()
}
}
@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.toString().startsWith("tr"), "Bip86 Descriptor does not start with 'tr'")
}
@Test
fun testNewAddress() {
val wallet: Wallet = Wallet(
descriptor,
changeDescriptor,
Network.TESTNET
)
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")
assertFalse(addressInfo.address.isValidForNetwork(Network.REGTEST), "Address is valid for regtest network, but it shouldn't be")
assertFalse(addressInfo.address.isValidForNetwork(Network.BITCOIN), "Address is valid for bitcoin network, but it shouldn't be")
assertEquals(
expected = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
actual = addressInfo.address.toString()
)
}
@Test
fun testBalance() {
val wallet: Wallet = Wallet(
descriptor,
changeDescriptor,
Network.TESTNET
)
assertEquals(
expected = 0uL,
actual = wallet.balance().total.toSat()
)
}
}

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")

View File

@ -2,3 +2,15 @@ rootProject.name = "bdk-jvm"
include(":lib")
includeBuild("plugins")
pluginManagement {
repositories {
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
mavenCentral()
}
}

View File

@ -1,14 +0,0 @@
This software is licensed under [Apache 2.0](LICENSE-APACHE) or
[MIT](LICENSE-MIT), at your option.
Some files retain their own copyright notice, however, for full authorship
information, see version control history.
Except as otherwise noted in individual files, all files in this repository are
licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
http://opensource.org/licenses/MIT>, at your option.
You may not use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of this software or any files in this repository except in
accordance with one or both of these licenses.

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Some files were not shown because too many files have changed in this diff Show More