Compare commits

..

69 Commits

Author SHA1 Message Date
thunderbiscuit
016bba93d1 Update libraries to official release versions 2023-02-21 11:00:08 -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
ec71ef58be 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 ec71ef58be

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
8e54ada436 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 8e54ada436. 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
thunderbiscuit
5f7d8a9077 Fix typos in Kotlin API docs 2023-01-10 16:15:12 -05:00
thunderbiscuit
d77f5b84c3 Update Swift readme for 0.26.0 release 2023-01-09 21:36:19 -05:00
thunderbiscuit
6555170d28 Fix Python tests and examples for 0.26.0 release 2023-01-09 21:24:02 -05:00
thunderbiscuit
c763609b8b Update bdk-jvm readme for 0.26.0 release 2023-01-09 21:08:20 -05:00
thunderbiscuit
340fa19bb8 Update bdk-android tests and readme 2023-01-09 21:03:41 -05:00
thunderbiscuit
ff2734663e Update Kotlin API docs to reflect new descriptor templates 2023-01-09 20:48:44 -05:00
Steve Myers
f399b799e4 Add validate_domain option to ElectrumConfig 2023-01-09 14:41:49 -05:00
Steve Myers
cdde26a8ea Bump bdk-ff version to 0.26.0 and bdk version to 0.26 2023-01-03 09:07:38 -06:00
Shobit Beltangdy
34af88df6f Update Blockchain.broadcast doc to reflect no return type
This fixes #284
2022-12-18 14:32:29 -08:00
Shobit Beltangdy
12e9a18357 Update Wallet.getBalance doc to reflect Balance return type
This is a fix for issue #284
2022-12-17 09:11:56 -08:00
thunderbiscuit
bada9b82e0 Add assert_matches dev-dependency 2022-12-16 12:28:06 -05:00
thunderbiscuit
41f15fe80f Change descriptor parameter in wallet constructor to use Descriptor instead of String 2022-12-16 11:28:55 -05:00
thunderbiscuit
54953fa208 Add default string constructor for Descriptor 2022-12-16 11:28:50 -05:00
thunderbiscuit
b7ccf81c7a Add tests for templates 2022-12-16 11:28:45 -05:00
thunderbiscuit
3792a98426 Add BIP44/49/84 templates 2022-12-16 11:28:38 -05:00
thunderbiscuit
5dd79e9632 Add readme to api-docs directory 2022-12-16 11:26:25 -05:00
thunderbiscuit
9ae938ca8c Add JNA issue fix in documentation 2022-12-16 11:26:19 -05:00
thunderbiscuit
05aa7157df Clean up CI tests for bdk-android and bdk-jvm 2022-12-16 11:23:04 -05:00
thunderbiscuit
275bd94148 Add dependency on the Android plugin library building task for tests 2022-12-16 11:23:02 -05:00
thunderbiscuit
146cb039c3 Add dependency on the JVM plugin library building task for tests 2022-12-16 11:22:43 -05:00
Steve Myers
41fdadb09c Merge bitcoindevkit/bdk-ffi#125: Add RpcConfig, BlockchainConfig::Rpc, and Auth
4ed6e364e6 Add API docs for RPC blockchain config and auth (thunderbiscuit)
d0cd3b0f38 Add Auth, RpcSyncParams, and RpcConfig (Steve Myers)

Pull request description:

  Fixes #117. This adds the RPC blockchain config but I don't currently have a way to test it as part of the `bdk-kotlin` or `bdk-swift` automated testing, which would need to be able to spin up a local regtest bitcoind for the tests.

  For now this will only be manually tested.

ACKs for top commit:
  thunderbiscuit:
    ACK [4ed6e36](4ed6e364e6).

Tree-SHA512: 2f114753a683c32ec957b26e23918eb3c1de07073fd0293e06fb960f8226f09d4d9ebf89f8d04f9da5cd459619f224d732b81e21c2173afeccb8ce56cc558582
2022-12-16 10:10:06 -06:00
thunderbiscuit
4ed6e364e6 Add API docs for RPC blockchain config and auth 2022-12-16 10:36:32 -05:00
Steve Myers
d0cd3b0f38 Add Auth, RpcSyncParams, and RpcConfig 2022-12-13 15:10:28 -06:00
Steve Myers
f216417fd2 Update api-docs for TxBuilder.drain_to change 2022-12-07 15:34:32 -06:00
Steve Myers
7d1a4500ef Change TxBuilder.drain_to argument to Script instead of address String 2022-12-07 11:54:22 -06:00
thunderbiscuit
a408387bff Bump snapshot and dev libraries' versions 2022-12-07 11:59:40 -05:00
33 changed files with 2420 additions and 1584 deletions

View File

@@ -90,7 +90,7 @@ jobs:
- uses: actions/upload-artifact@v2
with:
name: bdkpython-macos-${{ matrix.python }}
path: dist/*.whl
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-windows-wheel:
name: 'Build windows wheel'
@@ -123,4 +123,4 @@ jobs:
- uses: actions/upload-artifact@v2
with:
name: bdkpython-win-${{ matrix.python }}
path: dist/*.whl
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl

View File

@@ -82,7 +82,7 @@ jobs:
- uses: actions/upload-artifact@v2
with:
name: bdkpython-macos-${{ matrix.python }}
path: dist/*.whl
path: /Users/runner/work/bdk-ffi/bdk-ffi/bdk-python/dist/*.whl
build-windows-wheel:
name: 'Build windows wheel'
@@ -115,7 +115,7 @@ jobs:
- uses: actions/upload-artifact@v2
with:
name: bdkpython-win-${{ matrix.python }}
path: dist/*.whl
path: D:\a\bdk-ffi\bdk-ffi\bdk-python\dist\*.whl
publish-pypi:
name: 'Publish on PyPI'

View File

@@ -46,11 +46,6 @@ jobs:
- name: Install rust android targets
run: rustup target add x86_64-linux-android aarch64-linux-android armv7-linux-androideabi
- name: Build bdk-android library
run: |
cd bdk-android
./gradlew buildAndroidLib
- name: Run Android tests
run: |
cd bdk-android

View File

@@ -31,11 +31,6 @@ jobs:
distribution: temurin
java-version: 11
- name: Build bdk-jvm library
run: |
cd bdk-jvm
./gradlew buildJvmLib
- name: Run JVM tests
run: |
cd bdk-jvm

3
.gitignore vendored
View File

@@ -31,3 +31,6 @@ bdkFFI.h
BitcoinDevKit.swift
bdk.swift
.build
# Python related
__pycache__

421
Cargo.lock generated
View File

@@ -2,6 +2,12 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.7.6"
@@ -24,9 +30,9 @@ dependencies = [
[[package]]
name = "anyhow"
version = "1.0.66"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800"
[[package]]
name = "askama"
@@ -74,10 +80,16 @@ dependencies = [
]
[[package]]
name = "async-trait"
version = "0.1.59"
name = "assert_matches"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31e6e93155431f3931513b243d371981bb2770112b370c82745a1d19d2f99364"
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
[[package]]
name = "async-trait"
version = "0.1.64"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2"
dependencies = [
"proc-macro2",
"quote",
@@ -108,16 +120,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "bdk"
version = "0.25.0"
name = "base64-compat"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e7eb54c6288eca1b698e6e33dd82ebe6c08a93ec1a96bb6926ddceed22c703"
checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7"
dependencies = [
"byteorder",
]
[[package]]
name = "bdk"
version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51c878ac60a45c41523ff790df555ccb1fbfd634a667220104dcae3e64d7ed9f"
dependencies = [
"ahash",
"async-trait",
"bdk-macros",
"bip39",
"bitcoin",
"bitcoincore-rpc",
"electrum-client",
"esplora-client",
"getrandom",
@@ -134,8 +156,9 @@ dependencies = [
[[package]]
name = "bdk-ffi"
version = "0.25.0"
version = "0.27.0"
dependencies = [
"assert_matches",
"bdk",
"uniffi",
"uniffi_build",
@@ -218,6 +241,30 @@ dependencies = [
"serde",
]
[[package]]
name = "bitcoincore-rpc"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0261b2bb7617e0c91b452a837bbd1291fd34ad6990cb8e3ffc28239cc045b5ca"
dependencies = [
"bitcoincore-rpc-json",
"jsonrpc",
"log",
"serde",
"serde_json",
]
[[package]]
name = "bitcoincore-rpc-json"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c231bea28e314879c5aef240f6052e8a72a369e3c9f9b20d9bfbb33ad18029b2"
dependencies = [
"bitcoin",
"serde",
"serde_json",
]
[[package]]
name = "bitflags"
version = "1.3.2"
@@ -226,9 +273,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.11.1"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "byteorder"
@@ -238,15 +285,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
version = "1.3.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
[[package]]
name = "camino"
version = "1.1.1"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e"
checksum = "c77df041dc383319cc661b428b6961a005db4d6808d5e12536931b1ca9556055"
dependencies = [
"serde",
]
@@ -275,9 +322,9 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.77"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9f73505338f7d905b19d18738976aae232eb46b8efc15554ffc56deb5d9ebe4"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
@@ -285,12 +332,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chunked_transfer"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e"
[[package]]
name = "clap"
version = "2.34.0"
@@ -329,7 +370,7 @@ version = "3.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65"
dependencies = [
"heck 0.4.0",
"heck 0.4.1",
"proc-macro-error",
"proc-macro2",
"quote",
@@ -386,19 +427,19 @@ dependencies = [
"byteorder",
"libc",
"log",
"rustls 0.20.7",
"rustls",
"serde",
"serde_json",
"webpki 0.22.0",
"webpki-roots 0.22.5",
"webpki",
"webpki-roots",
"winapi",
]
[[package]]
name = "esplora-client"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b94400573de17e346ab91ec08ad13d37e871e400b9930d4cb2b6b02253e760e"
checksum = "38bbba572d03ca4d628b653f01e60ba6947c67df65ee8910c79daaf252897924"
dependencies = [
"bitcoin",
"log",
@@ -418,6 +459,16 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "flate2"
version = "1.0.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "form_urlencoded"
version = "1.1.0"
@@ -476,26 +527,20 @@ dependencies = [
[[package]]
name = "hashbrown"
version = "0.11.2"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashlink"
version = "0.7.0"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
checksum = "69fe1fcf8b4278d860ad0548329f892a3631fb63f82574df68275f34cdbe0ffa"
dependencies = [
"hashbrown 0.11.2",
"hashbrown",
]
[[package]]
@@ -509,9 +554,9 @@ dependencies = [
[[package]]
name = "heck"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hermit-abi"
@@ -540,7 +585,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
"hashbrown",
]
[[package]]
@@ -554,19 +599,31 @@ dependencies = [
[[package]]
name = "itoa"
version = "1.0.4"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440"
[[package]]
name = "js-sys"
version = "0.3.60"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "jsonrpc"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f8423b78fc94d12ef1a4a9d13c348c9a78766dda0cc18817adf0faf77e670c8"
dependencies = [
"base64-compat",
"serde",
"serde_derive",
"serde_json",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@@ -575,15 +632,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.138"
version = "0.2.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db6d7e329c562c5dfab7a46a2afabc8b987ab9a4834c9d1ca04dc54c1546cef8"
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
[[package]]
name = "libsqlite3-sys"
version = "0.24.2"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "898745e570c7d0453cc1fbc4a701eb6c662ed54e8fec8b7d14be137ebeeb9d14"
checksum = "29f835d03d717946d28b1d1ed632eb6f0e24a299388ee623d0c23118d3e8a7fa"
dependencies = [
"cc",
"pkg-config",
@@ -611,9 +668,9 @@ dependencies = [
[[package]]
name = "matches"
version = "0.1.9"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5"
[[package]]
name = "maybe-uninit"
@@ -660,19 +717,28 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniscript"
version = "8.0.0"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f4975078076f0b7b914a3044ad7432d2a7fcec38edb855afdc672e24ca35b69"
checksum = "123a10aae81d0712ecc09b780f6f0ae0b0f506a5c4c912974725760d59ba073e"
dependencies = [
"bitcoin",
"serde",
]
[[package]]
name = "nom"
version = "7.1.1"
name = "miniz_oxide"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36"
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
dependencies = [
"adler",
]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr",
"minimal-lexical",
@@ -680,9 +746,9 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.16.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "os_str_bytes"
@@ -703,9 +769,9 @@ dependencies = [
[[package]]
name = "parking_lot_core"
version = "0.8.5"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"
dependencies = [
"cfg-if",
"instant",
@@ -717,9 +783,9 @@ dependencies = [
[[package]]
name = "paste"
version = "1.0.9"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1de2e551fb905ac83f73f7aedf2f0cb4a0da7e35efa24a202a936269f1f18e1"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "percent-encoding"
@@ -777,18 +843,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.47"
version = "1.0.51"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
@@ -855,49 +921,35 @@ dependencies = [
[[package]]
name = "rusqlite"
version = "0.27.0"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85127183a999f7db96d1a976a309eebbfb6ea3b0b400ddd8340190129de6eb7a"
checksum = "01e213bc3ecb39ac32e81e51ebe31fd888a940515173e3a18a35f8c6e896422a"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"memchr",
"smallvec 1.10.0",
]
[[package]]
name = "rustls"
version = "0.19.1"
version = "0.20.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35edb675feee39aec9c99fa5ff985081995a06d594114ae14cbe797ad7b7a6d7"
dependencies = [
"base64",
"log",
"ring",
"sct 0.6.1",
"webpki 0.21.4",
]
[[package]]
name = "rustls"
version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "539a2bfe908f471bfa933876bd1eb6a19cf2176d375f82ef7f99530a40e48c2c"
checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f"
dependencies = [
"log",
"ring",
"sct 0.7.0",
"webpki 0.22.0",
"sct",
"webpki",
]
[[package]]
name = "ryu"
version = "1.0.11"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde"
[[package]]
name = "scopeguard"
@@ -925,16 +977,6 @@ dependencies = [
"syn",
]
[[package]]
name = "sct"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "sct"
version = "0.7.0"
@@ -947,9 +989,9 @@ dependencies = [
[[package]]
name = "secp256k1"
version = "0.24.1"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff55dc09d460954e9ef2fa8a7ced735a964be9981fd50e870b2b3b0705e14964"
checksum = "6b1629c9c557ef9b293568b338dddfc8208c98a18c59d722a9d53f859d9c9b62"
dependencies = [
"bitcoin_hashes 0.11.0",
"rand",
@@ -968,27 +1010,27 @@ dependencies = [
[[package]]
name = "semver"
version = "1.0.14"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4"
checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a"
dependencies = [
"serde",
]
[[package]]
name = "serde"
version = "1.0.149"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "256b9932320c590e707b94576e3cc1f7c9024d0ee6612dfbcf1cb106cbe8e055"
checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.149"
version = "1.0.152"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4eae9b04cbffdfd550eb462ed33bc6a1b68c935127d008b27444d08380f94e4"
checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e"
dependencies = [
"proc-macro2",
"quote",
@@ -997,15 +1039,21 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.89"
version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "siphasher"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
[[package]]
name = "sled"
version = "0.34.7"
@@ -1098,9 +1146,9 @@ dependencies = [
[[package]]
name = "syn"
version = "1.0.105"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
@@ -1109,9 +1157,9 @@ dependencies = [
[[package]]
name = "termcolor"
version = "1.1.3"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
@@ -1133,9 +1181,9 @@ checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "tokio"
version = "1.23.0"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46"
checksum = "c8e00990ebabbe4c14c08aca901caed183ecd5c09562a12c824bb53d3c3fd3af"
dependencies = [
"autocfg",
"pin-project-lite",
@@ -1156,9 +1204,9 @@ dependencies = [
[[package]]
name = "toml"
version = "0.5.9"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
@@ -1174,15 +1222,15 @@ dependencies = [
[[package]]
name = "unicode-bidi"
version = "0.3.8"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58"
[[package]]
name = "unicode-ident"
version = "1.0.5"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "unicode-normalization"
@@ -1195,9 +1243,9 @@ dependencies = [
[[package]]
name = "unicode-segmentation"
version = "1.10.0"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fdbf052a0783de01e944a6ce7a8cb939e295b1e7be835a1112c3b9a7f047a5a"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
@@ -1207,9 +1255,9 @@ checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "uniffi"
version = "0.21.0"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f54af5ada67d1173457a99a7bb44a7917f63e7466764cb4714865c7a6678b830"
checksum = "b983553c0d1ad73547c65fa0c399aa800bee4a70ad330198e1c7a523212da5ee"
dependencies = [
"anyhow",
"bytes",
@@ -1225,9 +1273,9 @@ dependencies = [
[[package]]
name = "uniffi_bindgen"
version = "0.21.0"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12cc4af3c0180c7e86c4a3acf2b6587af04ba2567b1e948993df10f421796621"
checksum = "5d46080a4840abccf7c0cce21931dae53215cbd7dd969b5e63c486235ce91a2a"
dependencies = [
"anyhow",
"askama",
@@ -1236,7 +1284,7 @@ dependencies = [
"clap 3.2.23",
"fs-err",
"goblin",
"heck 0.4.0",
"heck 0.4.1",
"once_cell",
"paste",
"serde",
@@ -1248,9 +1296,9 @@ dependencies = [
[[package]]
name = "uniffi_build"
version = "0.21.0"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "510287c368a9386eb731ebe824a6fc6c82a105e20d020af1aa20519c1c1561db"
checksum = "d035e50433ee3d52ab0dcdcf3b9b26f2cc2ec39294b3c07d95865a518709455f"
dependencies = [
"anyhow",
"camino",
@@ -1258,10 +1306,20 @@ dependencies = [
]
[[package]]
name = "uniffi_macros"
version = "0.21.0"
name = "uniffi_checksum_derive"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c8604503caa2cbcf271578dc51ca236d40e3b22e1514ffa2e638e2c39f6ad10"
checksum = "78b6e16d46caf942016997af8bbdf4b163bf8ae3deb0b667d9643de7b7ffd4c9"
dependencies = [
"quote",
"syn",
]
[[package]]
name = "uniffi_macros"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96a574677566f83ea8458dac1dd7792fd63e7c3f9dbcd865f0e8d6f8057b127"
dependencies = [
"bincode",
"camino",
@@ -1278,11 +1336,13 @@ dependencies = [
[[package]]
name = "uniffi_meta"
version = "0.21.0"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9417cc653937681436b93838d8c5f97a5b8c58697813982ee8810bd1dc3b57"
checksum = "729835442da829c9b6f7c111c76cf87b2498e129101203bec94f0c39a3296a38"
dependencies = [
"serde",
"siphasher",
"uniffi_checksum_derive",
]
[[package]]
@@ -1293,21 +1353,21 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]]
name = "ureq"
version = "2.2.0"
version = "2.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3131cd6cb18488da91da1d10ed31e966f453c06b65bf010d35638456976a3fd7"
checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d"
dependencies = [
"base64",
"chunked_transfer",
"flate2",
"log",
"once_cell",
"rustls 0.19.1",
"rustls",
"serde",
"serde_json",
"socks",
"url",
"webpki 0.21.4",
"webpki-roots 0.21.1",
"webpki",
"webpki-roots",
]
[[package]]
@@ -1347,9 +1407,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -1357,9 +1417,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
@@ -1372,9 +1432,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -1382,9 +1442,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
@@ -1395,30 +1455,20 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
version = "0.3.60"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki"
version = "0.22.0"
@@ -1431,20 +1481,11 @@ dependencies = [
[[package]]
name = "webpki-roots"
version = "0.21.1"
version = "0.22.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aabe153544e473b775453675851ecc86863d2a81d786d741f6b76778f2a48940"
checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87"
dependencies = [
"webpki 0.21.4",
]
[[package]]
name = "webpki-roots"
version = "0.22.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "368bfe657969fb01238bb756d351dcade285e0f6fcbd36dcb23359a5169975be"
dependencies = [
"webpki 0.22.0",
"webpki",
]
[[package]]
@@ -1504,42 +1545,42 @@ dependencies = [
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"

6
api-docs/README.md Normal file
View File

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

@@ -35,21 +35,41 @@ data class AddressInfo (
/**
* 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`.
* If youre unsure which one to use, use `AddressIndex.New`.
*
* @sample org.bitcoindevkit.addressIndexSample
*/
enum class AddressIndex {
sealed class AddressIndex {
/** Return a new address after incrementing the current descriptor index. */
NEW,
object New: AddressIndex(),
/** Return the address for the current descriptor index if it has not been used in a received transaction.
/**
* 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,
object LastUnused: AddressIndex(),
/**
* Return the address for a specific descriptor index. Does not change the current descriptor
* index used by [AddressIndex.New] and [AddressIndex.LastUsed].
* Use with caution, if an index is given that is less than the current descriptor index
* then the returned address may have already been used.
*/
data class Peek(val index: UInt): AddressIndex(),
/**
* Return the address for a specific descriptor index and reset the current descriptor index
* used by [AddressIndex.New] and [AddressIndex.LastUsed] to this value.
* Use with caution, if an index is given that is less than the current descriptor index
* then the returned address and subsequent addresses returned by calls to [AddressIndex.New]
* and [AddressIndex.LastUsed] may have already been used. Also if the index is reset to a
* value earlier than the [Blockchain] stopGap (default is 20) then a
* larger stopGap should be used to monitor for all possibly used addresses.
*/
data class Reset(val index: UInt): AddressIndex()
}
/**
@@ -120,15 +140,17 @@ data class SledDbConfiguration(
* @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 (
data class ElectrumConfig(
var url: String,
var socks5: String?,
var retry: UByte,
var timeout: UByte?,
var stopGap: ULong
var stopGap: ULong,
var validateDomain: Boolean
)
/**
@@ -142,7 +164,7 @@ data class ElectrumConfig (
*
* @sample org.bitcoindevkit.esploraBlockchainConfigSample
*/
data class EsploraConfig (
data class EsploraConfig(
var baseUrl: String,
var proxy: String?,
var concurrency: UByte?,
@@ -150,6 +172,56 @@ data class EsploraConfig (
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.
*
@@ -161,6 +233,9 @@ sealed class BlockchainConfig {
/** Esplora client. */
data class Esplora(val config: EsploraConfig) : BlockchainConfig()
/** Bitcoin Core RPC client. */
data class Rpc(val config: RpcConfig) : BlockchainConfig()
}
/**
@@ -193,7 +268,10 @@ class Blockchain(
config: BlockchainConfig
) {
/** Broadcast a transaction. */
fun broadcast(psbt: PartiallySignedBitcoinTransaction): String {}
fun broadcast(transaction: Transaction) {}
/** Estimate the fee rate required to confirm a transaction in a given target of blocks. */
fun estimateFee(target: ULong): FeeRate {}
/** Get the current height of the blockchain. */
fun getHeight(): UInt {}
@@ -202,6 +280,18 @@ class Blockchain(
fun getBlockHash(height: UInt): String {}
}
/**
* A bitcoin transaction.
*
* @constructor Build a new Bitcoin Transaction.
*
* @param transactionBytes The transaction bytes, bitcoin consensus encoded.
*/
class Transaction(transactionBytes: List<UByte>) {
/** Return the transaction bytes, bitcoin consensus encoded. */
fun serialize(): List<UByte> {}
}
/**
* A partially signed bitcoin transaction.
*
@@ -209,21 +299,21 @@ class Blockchain(
*
* @param psbtBase64 The PSBT in base64 format.
*/
class PartiallySignedBitcoinTransaction(psbtBase64: String) {
class PartiallySignedTransaction(psbtBase64: String) {
/** Return the PSBT in string format, using a base64 encoding. */
fun serialize(): String {}
/** Get the txid of the PSBT. */
fun txid(): String {}
/** Return the transaction as bytes. */
fun extractTx(): List<UByte>
/** Extract the transaction. */
fun extractTx(): Transaction {}
/**
* Combines this PartiallySignedTransaction with another PSBT as described by BIP 174.
* In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
*/
fun combine(other: PartiallySignedBitcoinTransaction): PartiallySignedBitcoinTransaction
fun combine(other: PartiallySignedTransaction): PartiallySignedTransaction
}
/**
@@ -295,15 +385,15 @@ data class BlockTime (
* @constructor Create a BDK wallet.
*
* @param descriptor The main (or "external") descriptor.
* @param changeDescriptor The change (or "internal") descriptor.
* @param changeDescriptor? The change (or "internal") descriptor.
* @param network The network to act on.
* @param databaseConfig The database configuration.
*
* @sample org.bitcoindevkit.walletSample
*/
class Wallet(
descriptor: String,
changeDescriptor: String,
descriptor: Descriptor,
changeDescriptor: Descriptor?,
network: Network,
databaseConfig: DatabaseConfig,
) {
@@ -314,11 +404,20 @@ class Wallet(
*/
fun getAddress(addressIndex: AddressIndex): AddressInfo {}
/** Return the balance, meaning the sum of this wallets unspent outputs values. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
fun getBalance(): ULong {}
/**
* Return a derived address using the internal (change) descriptor.
* If the wallet doesn't have an internal descriptor it will use the external descriptor.
* See [AddressIndex] for available address index selection strategies. If none of the keys
* in the descriptor are derivable (i.e. does not end with /\*) then the same address will always
* be returned for any [AddressIndex].
*/
fun getInternalAddress(addressIndex: AddressIndex): AddressInfo {}
/** Return the wallet's balance, across different categories. See [Balance] for the categories. Note that this method only operates on the internal database, which first needs to be [Wallet.sync] manually. */
fun getBalance(): Balance {}
/** Sign a transaction with all the wallets signers. */
fun sign(psbt: PartiallySignedBitcoinTransaction): Boolean {}
fun sign(psbt: PartiallySignedTransaction): Boolean {}
/** Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually. */
fun listTransactions(): List<TransactionDetails> {}
@@ -410,7 +509,7 @@ class TxBuilder() {
* [drainWallet] to spend all of them. When bumping the fees of a transaction made with this option,
* you probably want to use [BumpFeeTxBuilder.allowShrinking] to allow this output to be reduced to pay for the extra fees.
*/
fun drainTo(address: String): TxBuilder {}
fun drainTo(script: Script): TxBuilder {}
/** Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`. */
fun enableRbf(): TxBuilder {}
@@ -495,7 +594,7 @@ class DescriptorSecretKey(network: Network, mnemonic: Mnemonic, password: String
/** Return the public version of the descriptor. */
fun asPublic(): DescriptorPublicKey {}
/* Return the raw private key as bytes. */
/** Return the raw private key as bytes. */
fun secretBytes(): List<UByte>
/** Return the private descriptor as a string. */
@@ -523,6 +622,62 @@ class DescriptorPublicKey(network: Network, mnemonic: String, password: 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.
*/
@@ -553,7 +708,7 @@ enum class WordCount {
* @sample org.bitcoindevkit.txBuilderResultSample2
*/
data class TxBuilderResult (
var psbt: PartiallySignedBitcoinTransaction,
var psbt: PartiallySignedTransaction,
var transactionDetails: TransactionDetails
)
@@ -568,7 +723,7 @@ class Script(rawOutputScript: List<UByte>)
* @param address The address in string format.
*/
class Address(address: String) {
/* Return the ScriptPubKey. */
/** Return the ScriptPubKey. */
fun scriptPubkey(): Script
}

View File

@@ -72,8 +72,8 @@ fun addressIndexSample() {
databaseConfig = DatabaseConfig.Memory
)
fun getLastUnusedAddress(): AddressInfo {
return wallet.getAddress(AddressIndex.LAST_UNUSED)
fun peekAddress100(): AddressInfo {
return wallet.getAddress(AddressIndex.Peek(100u))
}
}
@@ -85,11 +85,7 @@ fun addressInfoSample() {
databaseConfig = DatabaseConfig.Memory
)
fun getLastUnusedAddress(): AddressInfo {
return wallet.getAddress(AddressIndex.NEW)
}
val newAddress: AddressInfo = getLastUnusedAddress()
val newAddress: AddressInfo = wallet.getAddress(AddressIndex.New)
println("New address at index ${newAddress.index} is ${newAddress.address}")
}
@@ -107,7 +103,16 @@ fun blockchainSample() {
val blockchain: Blockchain = Blockchain(blockchainConfig)
blockchain.broadcast(signedPsbt)
val feeRate: FeeRate = blockchain.estimateFee(3u)
val (psbt, txDetails) = TxBuilder()
.addRecipient(faucetAddress.scriptPubkey(), 1000uL)
.feeRate(feeRate.asSatPerVb())
.finish(wallet)
wallet.sign(psbt)
blockchain.broadcast(psbt)
}
@@ -243,3 +248,34 @@ fun mnemonicSample() {
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

@@ -25,10 +25,10 @@ val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEm
val databaseConfig = DatabaseConfig.Memory
val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u)
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
)
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
```
### Snapshot releases
@@ -95,5 +95,22 @@ and use the `publishToMavenLocal` task without excluding the signing task:
./gradlew publishToMavenLocal
```
## Known issues
### JNA dependency
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
```shell
class file for com.sun.jna.Pointer not found
```
The solution is to add JNA as a dependency like so:
```kotlin
dependencies {
// ...
implementation("net.java.dev.jna:jna:5.12.1")
}
```
### x86 emulators
For some older versions of macOS, Android Studio will recommend users install the x86 version of the emulator by default. This will not work with the bdk-android library, as we do not support 32-bit architectures. Make sure you install an x86_64 emulator to work with bdk-android.
[`bdk`]: https://github.com/bitcoindevkit/bdk
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi

View File

@@ -2,4 +2,4 @@ org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
android.enableJetifier=true
kotlin.code.style=official
libraryVersion=0.25.0
libraryVersion=0.27.1

View File

@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
// library version is defined in gradle.properties
val libraryVersion: String by project
@@ -107,3 +109,9 @@ signing {
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
sign(publishing.publications)
}
// This task dependency ensures that we build the bindings
// binaries before running the tests
tasks.withType<KotlinCompile> {
dependsOn("buildAndroidLib")
}

View File

@@ -36,8 +36,7 @@ class AndroidLibTest {
}
}
private val descriptor =
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
private val databaseConfig = DatabaseConfig.Memory
@@ -47,14 +46,15 @@ class AndroidLibTest {
null,
5u,
null,
100u
100u,
true,
)
)
@Test
fun memoryWalletNewAddress() {
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val address = wallet.getAddress(AddressIndex.NEW).address
val address = wallet.getAddress(AddressIndex.New).address
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
}

View File

@@ -1,6 +1,6 @@
[package]
name = "bdk-ffi"
version = "0.25.0"
version = "0.27.0"
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
edition = "2018"
license = "MIT OR Apache-2.0"
@@ -10,10 +10,12 @@ crate-type = ["staticlib", "cdylib"]
name = "bdkffi"
[dependencies]
bdk = { version = "0.25", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled"] }
bdk = { version = "0.27.1", features = ["all-keys", "use-esplora-ureq", "sqlite-bundled", "rpc"] }
uniffi_macros = { version = "0.21.0", features = ["builtin-bindgen"] }
uniffi = { version = "0.21.0", features = ["builtin-bindgen"] }
[build-dependencies]
uniffi_build = { version = "0.21.0", features = ["builtin-bindgen"] }
[dev-dependencies]
assert_matches = "1.5.0"

View File

@@ -45,6 +45,7 @@ enum BdkError {
"Esplora",
"Sled",
"Rusqlite",
"Rpc",
};
dictionary AddressInfo {
@@ -52,9 +53,12 @@ dictionary AddressInfo {
string address;
};
enum AddressIndex {
"New",
"LastUnused",
[Enum]
interface AddressIndex {
New();
LastUnused();
Peek(u32 index);
Reset(u32 index);
};
enum Network {
@@ -116,6 +120,7 @@ dictionary ElectrumConfig {
u8 retry;
u8? timeout;
u64 stop_gap;
boolean validate_domain;
};
dictionary EsploraConfig {
@@ -126,10 +131,33 @@ dictionary EsploraConfig {
u64? timeout;
};
[Enum]
interface Auth {
None();
UserPass(string username, string password);
Cookie(string file);
};
dictionary RpcSyncParams {
u64 start_script_count;
u64 start_time;
boolean force_start_time;
u64 poll_rate_sec;
};
dictionary RpcConfig {
string url;
Auth auth;
Network network;
string wallet_name;
RpcSyncParams? sync_params;
};
[Enum]
interface BlockchainConfig {
Electrum(ElectrumConfig config);
Esplora(EsploraConfig config);
Rpc(RpcConfig config);
};
interface Blockchain {
@@ -137,7 +165,10 @@ interface Blockchain {
constructor(BlockchainConfig config);
[Throws=BdkError]
void broadcast([ByRef] PartiallySignedTransaction psbt);
void broadcast([ByRef] Transaction transaction);
[Throws=BdkError]
FeeRate estimate_fee(u64 target);
[Throws=BdkError]
u32 get_height();
@@ -179,11 +210,14 @@ dictionary ScriptAmount {
interface Wallet {
[Throws=BdkError]
constructor(string descriptor, string? change_descriptor, Network network, DatabaseConfig database_config);
constructor(Descriptor descriptor, Descriptor? change_descriptor, Network network, DatabaseConfig database_config);
[Throws=BdkError]
AddressInfo get_address(AddressIndex address_index);
[Throws=BdkError]
AddressInfo get_internal_address(AddressIndex address_index);
[Throws=BdkError]
Balance get_balance();
@@ -209,6 +243,13 @@ interface FeeRate {
float as_sat_per_vb();
};
interface Transaction {
[Throws=BdkError]
constructor(sequence<u8> transaction_bytes);
sequence<u8> serialize();
};
interface PartiallySignedTransaction {
[Throws=BdkError]
constructor(string psbt_base64);
@@ -217,7 +258,7 @@ interface PartiallySignedTransaction {
string txid();
sequence<u8> extract_tx();
Transaction extract_tx();
[Throws=BdkError]
PartiallySignedTransaction combine(PartiallySignedTransaction other);
@@ -257,7 +298,7 @@ interface TxBuilder {
TxBuilder drain_wallet();
TxBuilder drain_to(string address);
TxBuilder drain_to(Script script);
TxBuilder enable_rbf();
@@ -333,6 +374,33 @@ interface DescriptorPublicKey {
string as_string();
};
interface Descriptor {
[Throws=BdkError]
constructor(string descriptor, Network network);
[Name=new_bip44]
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
[Name=new_bip44_public]
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
[Name=new_bip49]
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
[Name=new_bip49_public]
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
[Name=new_bip84]
constructor(DescriptorSecretKey secret_key, KeychainKind keychain, Network network);
[Name=new_bip84_public]
constructor(DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);
string as_string();
string as_string_private();
};
interface Address {
[Throws=BdkError]
constructor(string address);

201
bdk-ffi/src/blockchain.rs Normal file
View File

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

14
bdk-ffi/src/database.rs Normal file
View File

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

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

@@ -0,0 +1,334 @@
use crate::{BdkError, DescriptorPublicKey, DescriptorSecretKey};
use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::Fingerprint;
use bdk::bitcoin::Network;
use bdk::descriptor::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
use bdk::keys::{
DescriptorPublicKey as BdkDescriptorPublicKey, DescriptorSecretKey as BdkDescriptorSecretKey,
};
use bdk::template::{
Bip44, Bip44Public, Bip49, Bip49Public, Bip84, Bip84Public, DescriptorTemplate,
};
use bdk::KeychainKind;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
#[derive(Debug)]
pub(crate) struct Descriptor {
pub(crate) extended_descriptor: ExtendedDescriptor,
pub(crate) key_map: KeyMap,
}
impl Descriptor {
pub(crate) fn new(descriptor: String, network: Network) -> Result<Self, BdkError> {
let secp = Secp256k1::new();
let (extended_descriptor, key_map) = descriptor.into_wallet_descriptor(&secp, network)?;
Ok(Self {
extended_descriptor,
key_map,
})
}
pub(crate) fn new_bip44(
secret_key: Arc<DescriptorSecretKey>,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
match derivable_key.deref() {
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::Single(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip44_public(
public_key: Arc<DescriptorPublicKey>,
fingerprint: String,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
match derivable_key.deref() {
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::Single(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip49(
secret_key: Arc<DescriptorSecretKey>,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
match derivable_key.deref() {
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::Single(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip49_public(
public_key: Arc<DescriptorPublicKey>,
fingerprint: String,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
match derivable_key.deref() {
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::Single(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip84(
secret_key: Arc<DescriptorSecretKey>,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let derivable_key = secret_key.descriptor_secret_key_mutex.lock().unwrap();
match derivable_key.deref() {
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::Single(_) => {
unreachable!()
}
}
}
pub(crate) fn new_bip84_public(
public_key: Arc<DescriptorPublicKey>,
fingerprint: String,
keychain_kind: KeychainKind,
network: Network,
) -> Self {
let fingerprint = Fingerprint::from_str(fingerprint.as_str()).unwrap();
let derivable_key = public_key.descriptor_public_key_mutex.lock().unwrap();
match derivable_key.deref() {
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::Single(_) => {
unreachable!()
}
}
}
pub(crate) fn as_string_private(&self) -> String {
let descriptor = &self.extended_descriptor;
let key_map = &self.key_map;
descriptor.to_string_with_secret(key_map)
}
pub(crate) fn as_string(&self) -> String {
self.extended_descriptor.to_string()
}
}
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
// crate.
#[cfg(test)]
mod test {
use crate::database::DatabaseConfig;
use crate::*;
use assert_matches::assert_matches;
use bdk::descriptor::DescriptorError::Key;
use bdk::keys::KeyError::InvalidNetwork;
fn get_descriptor_secret_key() -> DescriptorSecretKey {
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
DescriptorSecretKey::new(Network::Testnet, Arc::new(mnemonic), None)
}
#[test]
fn test_descriptor_templates() {
let master: Arc<DescriptorSecretKey> = Arc::new(get_descriptor_secret_key());
println!("Master: {:?}", master.as_string());
// tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h
let handmade_public_44 = master
.derive(Arc::new(
DerivationPath::new("m/44h/1h/0h".to_string()).unwrap(),
))
.unwrap()
.as_public();
println!("Public 44: {}", handmade_public_44.as_string());
// Public 44: [d1d04177/44'/1'/0']tpubDCoPjomfTqh1e7o1WgGpQtARWtkueXQAepTeNpWiitS3Sdv8RKJ1yvTrGHcwjDXp2SKyMrTEca4LoN7gEUiGCWboyWe2rz99Kf4jK4m2Zmx/*
let handmade_public_49 = master
.derive(Arc::new(
DerivationPath::new("m/49h/1h/0h".to_string()).unwrap(),
))
.unwrap()
.as_public();
println!("Public 49: {}", handmade_public_49.as_string());
// Public 49: [d1d04177/49'/1'/0']tpubDC65ZRvk1NDddHrVAUAZrUPJ772QXzooNYmPywYF9tMyNLYKf5wpKE7ZJvK9kvfG3FV7rCsHBNXy1LVKW95jrmC7c7z4hq7a27aD2sRrAhR/*
let handmade_public_84 = master
.derive(Arc::new(
DerivationPath::new("m/84h/1h/0h".to_string()).unwrap(),
))
.unwrap()
.as_public();
println!("Public 84: {}", handmade_public_84.as_string());
// Public 84: [d1d04177/84'/1'/0']tpubDDNxbq17egjFk2edjv8oLnzxk52zny9aAYNv9CMqTzA4mQDiQq818sEkNe9Gzmd4QU8558zftqbfoVBDQorG3E4Wq26tB2JeE4KUoahLkx6/*
let template_private_44 =
Descriptor::new_bip44(master.clone(), KeychainKind::External, Network::Testnet);
let template_private_49 =
Descriptor::new_bip49(master.clone(), KeychainKind::External, Network::Testnet);
let template_private_84 =
Descriptor::new_bip84(master, KeychainKind::External, Network::Testnet);
// the extended public keys are the same when creating them manually as they are with the templates
println!("Template 49: {}", template_private_49.as_string());
println!("Template 44: {}", template_private_44.as_string());
println!("Template 84: {}", template_private_84.as_string());
// for the public versions of the templates these are incorrect, bug report and fix in bitcoindevkit/bdk#817 and bitcoindevkit/bdk#818
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,
);
println!("Template public 49: {}", template_public_49.as_string());
println!("Template public 44: {}", template_public_44.as_string());
println!("Template public 84: {}", template_public_84.as_string());
// when using a public key, both as_string and as_string_private return the same string
assert_eq!(
template_public_44.as_string_private(),
template_public_44.as_string()
);
assert_eq!(
template_public_49.as_string_private(),
template_public_49.as_string()
);
assert_eq!(
template_public_84.as_string_private(),
template_public_84.as_string()
);
// when using as_string on a private key, we get the same result as when using it on a public key
assert_eq!(
template_private_44.as_string(),
template_public_44.as_string()
);
assert_eq!(
template_private_49.as_string(),
template_public_49.as_string()
);
assert_eq!(
template_private_84.as_string(),
template_public_84.as_string()
);
}
#[test]
fn test_descriptor_from_string() {
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet);
let descriptor2 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Bitcoin);
// Creating a Descriptor using an extended key that doesn't match the network provided will throw and InvalidNetwork Error
assert!(descriptor1.is_ok());
assert_matches!(
descriptor2.unwrap_err(),
bdk::Error::Descriptor(Key(InvalidNetwork))
)
}
#[test]
fn test_wallet_from_descriptor() {
let descriptor1 = Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap();
let wallet1 = Wallet::new(
Arc::new(Descriptor::new("wpkh(tprv8hwWMmPE4BVNxGdVt3HhEERZhondQvodUY7Ajyseyhudr4WabJqWKWLr4Wi2r26CDaNCQhhxEftEaNzz7dPGhWuKFU4VULesmhEfZYyBXdE/0/*)".to_string(), Network::Testnet).unwrap()),
None,
Network::Testnet,
DatabaseConfig::Memory
);
let wallet2 = Wallet::new(
Arc::new(descriptor1),
None,
Network::Bitcoin,
DatabaseConfig::Memory,
);
// Creating a wallet using a Descriptor with an extended key that doesn't match the network provided in the wallet constructor will throw and InvalidNetwork Error
assert!(wallet1.is_ok());
assert_matches!(
wallet2.unwrap_err(),
bdk::Error::Descriptor(Key(InvalidNetwork))
)
}
}

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

@@ -0,0 +1,376 @@
use crate::BdkError;
use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::DerivationPath as BdkDerivationPath;
use bdk::bitcoin::Network;
use bdk::descriptor::DescriptorXKey;
use bdk::keys::bip39::{Language, Mnemonic as BdkMnemonic, WordCount};
use bdk::keys::{
DerivableKey, DescriptorPublicKey as BdkDescriptorPublicKey,
DescriptorSecretKey as BdkDescriptorSecretKey, ExtendedKey, GeneratableKey, GeneratedKey,
};
use bdk::miniscript::BareCtx;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
/// Mnemonic phrases are a human-readable version of the private keys.
/// Supported number of words are 12, 15, 18, 21 and 24.
pub(crate) struct Mnemonic {
internal: BdkMnemonic,
}
impl Mnemonic {
/// Generates Mnemonic with a random entropy
pub(crate) fn new(word_count: WordCount) -> Self {
let generated_key: GeneratedKey<_, BareCtx> =
BdkMnemonic::generate((word_count, Language::English)).unwrap();
let mnemonic = BdkMnemonic::parse_in(Language::English, generated_key.to_string()).unwrap();
Mnemonic { internal: mnemonic }
}
/// Parse a Mnemonic with given string
pub(crate) fn from_string(mnemonic: String) -> Result<Self, BdkError> {
BdkMnemonic::from_str(&mnemonic)
.map(|m| Mnemonic { internal: m })
.map_err(|e| BdkError::Generic(e.to_string()))
}
/// Create a new Mnemonic in the specified language from the given entropy.
/// Entropy must be a multiple of 32 bits (4 bytes) and 128-256 bits in length.
pub(crate) fn from_entropy(entropy: Vec<u8>) -> Result<Self, BdkError> {
BdkMnemonic::from_entropy(entropy.as_slice())
.map(|m| Mnemonic { internal: m })
.map_err(|e| BdkError::Generic(e.to_string()))
}
/// Returns Mnemonic as string
pub(crate) fn as_string(&self) -> String {
self.internal.to_string()
}
}
pub(crate) struct DerivationPath {
derivation_path_mutex: Mutex<BdkDerivationPath>,
}
impl DerivationPath {
pub(crate) fn new(path: String) -> Result<Self, BdkError> {
BdkDerivationPath::from_str(&path)
.map(|x| DerivationPath {
derivation_path_mutex: Mutex::new(x),
})
.map_err(|e| BdkError::Generic(e.to_string()))
}
}
#[derive(Debug)]
pub(crate) struct DescriptorSecretKey {
pub(crate) descriptor_secret_key_mutex: Mutex<BdkDescriptorSecretKey>,
}
impl DescriptorSecretKey {
pub(crate) fn new(network: Network, mnemonic: Arc<Mnemonic>, password: Option<String>) -> Self {
let mnemonic = mnemonic.internal.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: bdk::descriptor::Wildcard::Unhardened,
});
Self {
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
}
}
pub(crate) fn from_string(private_key: String) -> Result<Self, BdkError> {
let descriptor_secret_key = BdkDescriptorSecretKey::from_str(private_key.as_str())
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(Self {
descriptor_secret_key_mutex: Mutex::new(descriptor_secret_key),
})
}
pub(crate) fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
let secp = Secp256k1::new();
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
match descriptor_secret_key.deref() {
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
let derived_xprv = descriptor_x_key.xkey.derive_priv(&secp, &path)?;
let key_source = match descriptor_x_key.origin.clone() {
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
None => (descriptor_x_key.xkey.fingerprint(&secp), path),
};
let derived_descriptor_secret_key = BdkDescriptorSecretKey::XPrv(DescriptorXKey {
origin: Some(key_source),
xkey: derived_xprv,
derivation_path: BdkDerivationPath::default(),
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self {
descriptor_secret_key_mutex: Mutex::new(derived_descriptor_secret_key),
}))
}
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
"Cannot derive from a single key".to_string(),
)),
}
}
pub(crate) fn extend(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
match descriptor_secret_key.deref() {
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 {
descriptor_secret_key_mutex: Mutex::new(extended_descriptor_secret_key),
}))
}
BdkDescriptorSecretKey::Single(_) => Err(BdkError::Generic(
"Cannot extend from a single key".to_string(),
)),
}
}
pub(crate) fn as_public(&self) -> Arc<DescriptorPublicKey> {
let secp = Secp256k1::new();
let descriptor_public_key = self
.descriptor_secret_key_mutex
.lock()
.unwrap()
.to_public(&secp)
.unwrap();
Arc::new(DescriptorPublicKey {
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
})
}
/// Get the private key as bytes.
pub(crate) fn secret_bytes(&self) -> Vec<u8> {
let descriptor_secret_key = self.descriptor_secret_key_mutex.lock().unwrap();
let secret_bytes: Vec<u8> = match descriptor_secret_key.deref() {
BdkDescriptorSecretKey::XPrv(descriptor_x_key) => {
descriptor_x_key.xkey.private_key.secret_bytes().to_vec()
}
BdkDescriptorSecretKey::Single(_) => {
unreachable!()
}
};
secret_bytes
}
pub(crate) fn as_string(&self) -> String {
self.descriptor_secret_key_mutex.lock().unwrap().to_string()
}
}
#[derive(Debug)]
pub(crate) struct DescriptorPublicKey {
pub(crate) descriptor_public_key_mutex: Mutex<BdkDescriptorPublicKey>,
}
impl DescriptorPublicKey {
pub(crate) fn from_string(public_key: String) -> Result<Self, BdkError> {
let descriptor_public_key = BdkDescriptorPublicKey::from_str(public_key.as_str())
.map_err(|e| BdkError::Generic(e.to_string()))?;
Ok(Self {
descriptor_public_key_mutex: Mutex::new(descriptor_public_key),
})
}
pub(crate) fn derive(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
let secp = Secp256k1::new();
let descriptor_public_key = self.descriptor_public_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
match descriptor_public_key.deref() {
BdkDescriptorPublicKey::XPub(descriptor_x_key) => {
let derived_xpub = descriptor_x_key.xkey.derive_pub(&secp, &path)?;
let key_source = match descriptor_x_key.origin.clone() {
Some((fingerprint, origin_path)) => (fingerprint, origin_path.extend(path)),
None => (descriptor_x_key.xkey.fingerprint(), path),
};
let derived_descriptor_public_key = BdkDescriptorPublicKey::XPub(DescriptorXKey {
origin: Some(key_source),
xkey: derived_xpub,
derivation_path: BdkDerivationPath::default(),
wildcard: descriptor_x_key.wildcard,
});
Ok(Arc::new(Self {
descriptor_public_key_mutex: Mutex::new(derived_descriptor_public_key),
}))
}
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
"Cannot derive from a single key".to_string(),
)),
}
}
pub(crate) fn extend(&self, path: Arc<DerivationPath>) -> Result<Arc<Self>, BdkError> {
let descriptor_public_key = self.descriptor_public_key_mutex.lock().unwrap();
let path = path.derivation_path_mutex.lock().unwrap().deref().clone();
match descriptor_public_key.deref() {
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 {
descriptor_public_key_mutex: Mutex::new(extended_descriptor_public_key),
}))
}
BdkDescriptorPublicKey::Single(_) => Err(BdkError::Generic(
"Cannot extend from a single key".to_string(),
)),
}
}
pub(crate) fn as_string(&self) -> String {
self.descriptor_public_key_mutex.lock().unwrap().to_string()
}
}
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
// crate.
#[cfg(test)]
mod test {
use crate::keys::{DerivationPath, DescriptorPublicKey, DescriptorSecretKey, Mnemonic};
use crate::BdkError;
use bdk::bitcoin::hashes::hex::ToHex;
use bdk::bitcoin::Network;
use std::sync::Arc;
fn get_descriptor_secret_key() -> DescriptorSecretKey {
let mnemonic = Mnemonic::from_string("chaos fabric time speed sponsor all flat solution wisdom trophy crack object robot pave observe combine where aware bench orient secret primary cable detect".to_string()).unwrap();
DescriptorSecretKey::new(Network::Testnet, Arc::new(mnemonic), None)
}
fn derive_dsk(
key: &DescriptorSecretKey,
path: &str,
) -> Result<Arc<DescriptorSecretKey>, BdkError> {
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
key.derive(path)
}
fn extend_dsk(
key: &DescriptorSecretKey,
path: &str,
) -> Result<Arc<DescriptorSecretKey>, BdkError> {
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
key.extend(path)
}
fn derive_dpk(
key: &DescriptorPublicKey,
path: &str,
) -> Result<Arc<DescriptorPublicKey>, BdkError> {
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
key.derive(path)
}
fn extend_dpk(
key: &DescriptorPublicKey,
path: &str,
) -> Result<Arc<DescriptorPublicKey>, BdkError> {
let path = Arc::new(DerivationPath::new(path.to_string()).unwrap());
key.extend(path)
}
#[test]
fn test_generate_descriptor_secret_key() {
let master_dsk = get_descriptor_secret_key();
assert_eq!(master_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/*");
assert_eq!(master_dsk.as_public().as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/*");
}
#[test]
fn test_derive_self() {
let master_dsk = get_descriptor_secret_key();
let 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_descriptor_secret_key();
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_descriptor_secret_key();
let extended_dsk: &DescriptorSecretKey = &extend_dsk(&master_dsk, "m/0").unwrap();
assert_eq!(extended_dsk.as_string(), "tprv8ZgxMBicQKsPdWuqM1t1CDRvQtQuBPyfL6GbhQwtxDKgUAVPbxmj71pRA8raTqLrec5LyTs5TqCxdABcZr77bt2KyWA5bizJHnC4g4ysm4h/0/*");
let master_dpk: &DescriptorPublicKey = &master_dsk.as_public();
let extended_dpk: &DescriptorPublicKey = &extend_dpk(master_dpk, "m/0").unwrap();
assert_eq!(extended_dpk.as_string(), "tpubD6NzVbkrYhZ4WywdEfYbbd62yuvqLjAZuPsNyvzCNV85JekAEMbKHWSHLF9h3j45SxewXDcLv328B1SEZrxg4iwGfmdt1pDFjZiTkGiFqGa/0/*");
let wif = "L2wTu6hQrnDMiFNWA5na6jB12ErGQqtXwqpSL7aWquJaZG8Ai3ch";
let extended_key = DescriptorSecretKey::from_string(wif.to_string()).unwrap();
let result = extended_key.derive(Arc::new(DerivationPath::new("m/0".to_string()).unwrap()));
dbg!(&result);
assert!(result.is_err());
}
#[test]
fn test_from_str_descriptor_secret_key() {
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_descriptor_secret_key() {
let master_dsk = get_descriptor_secret_key();
// 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_descriptor_secret_key().as_public();
let derived_dpk = &derive_dpk(&master_dpk, "m/84h/1h/0h");
assert!(derived_dpk.is_err());
}
#[test]
fn test_retrieve_master_secret_key() {
let master_dpk = get_descriptor_secret_key();
let master_private_key = master_dpk.secret_bytes().to_hex();
assert_eq!(
master_private_key,
"e93315d6ce401eb4db803a56232f0ed3e69b053774e6047df54f1bd00e5ea936"
)
}
}

File diff suppressed because it is too large Load Diff

111
bdk-ffi/src/psbt.rs Normal file
View File

@@ -0,0 +1,111 @@
use bdk::bitcoin::hashes::hex::ToHex;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction as BdkPartiallySignedTransaction;
use bdk::psbt::PsbtUtils;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use crate::{BdkError, FeeRate, Transaction};
#[derive(Debug)]
pub(crate) struct PartiallySignedTransaction {
pub(crate) internal: Mutex<BdkPartiallySignedTransaction>,
}
impl PartiallySignedTransaction {
pub(crate) fn new(psbt_base64: String) -> Result<Self, BdkError> {
let psbt: BdkPartiallySignedTransaction =
BdkPartiallySignedTransaction::from_str(&psbt_base64)?;
Ok(PartiallySignedTransaction {
internal: Mutex::new(psbt),
})
}
pub(crate) fn serialize(&self) -> String {
let psbt = self.internal.lock().unwrap().clone();
psbt.to_string()
}
pub(crate) fn txid(&self) -> String {
let tx = self.internal.lock().unwrap().clone().extract_tx();
let txid = tx.txid();
txid.to_hex()
}
/// Return the transaction.
pub(crate) fn extract_tx(&self) -> Arc<Transaction> {
let tx = self.internal.lock().unwrap().clone().extract_tx();
Arc::new(Transaction { internal: tx })
}
/// Combines this PartiallySignedTransaction with other PSBT as described by BIP 174.
///
/// In accordance with BIP 174 this function is commutative i.e., `A.combine(B) == B.combine(A)`
pub(crate) fn combine(
&self,
other: Arc<PartiallySignedTransaction>,
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
let other_psbt = other.internal.lock().unwrap().clone();
let mut original_psbt = self.internal.lock().unwrap().clone();
original_psbt.combine(other_psbt)?;
Ok(Arc::new(PartiallySignedTransaction {
internal: Mutex::new(original_psbt),
}))
}
/// The total transaction fee amount, sum of input amounts minus sum of output amounts, in Sats.
/// If the PSBT is missing a TxOut for an input returns None.
pub(crate) fn fee_amount(&self) -> Option<u64> {
self.internal.lock().unwrap().fee_amount()
}
/// The transaction's fee rate. This value will only be accurate if calculated AFTER the
/// `PartiallySignedTransaction` is finalized and all witness/signature data is added to the
/// transaction.
/// If the PSBT is missing a TxOut for an input returns None.
pub(crate) fn fee_rate(&self) -> Option<Arc<FeeRate>> {
self.internal.lock().unwrap().fee_rate().map(Arc::new)
}
}
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
// crate.
#[cfg(test)]
mod test {
use crate::wallet::{TxBuilder, Wallet};
use bdk::wallet::get_funded_wallet;
use std::sync::Mutex;
#[test]
fn test_psbt_fee() {
let test_wpkh = "wpkh(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW)";
let (funded_wallet, _, _) = get_funded_wallet(test_wpkh);
let test_wallet = Wallet {
wallet_mutex: Mutex::new(funded_wallet),
};
let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
let drain_to_script = crate::Address::new(drain_to_address)
.unwrap()
.script_pubkey();
let tx_builder = TxBuilder::new()
.fee_rate(2.0)
.drain_wallet()
.drain_to(drain_to_script.clone());
//dbg!(&tx_builder);
assert!(tx_builder.drain_wallet);
assert_eq!(tx_builder.drain_to, Some(drain_to_script.script.clone()));
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
assert!(tx_builder_result.psbt.fee_rate().is_some());
assert_eq!(
tx_builder_result.psbt.fee_rate().unwrap().as_sat_per_vb(),
2.682927
);
assert!(tx_builder_result.psbt.fee_amount().is_some());
assert_eq!(tx_builder_result.psbt.fee_amount().unwrap(), 220);
}
}

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

@@ -0,0 +1,696 @@
use bdk::bitcoin::blockdata::script::Script as BdkScript;
use bdk::bitcoin::{Address as BdkAddress, Network, OutPoint as BdkOutPoint, Sequence, Txid};
use bdk::database::any::AnyDatabase;
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
use bdk::wallet::tx_builder::ChangeSpendPolicy;
use bdk::{FeeRate, SignOptions, SyncOptions as BdkSyncOptions, Wallet as BdkWallet};
use std::collections::HashSet;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::{Arc, Mutex, MutexGuard};
use crate::blockchain::Blockchain;
use crate::database::DatabaseConfig;
use crate::descriptor::Descriptor;
use crate::psbt::PartiallySignedTransaction;
use crate::{
AddressIndex, AddressInfo, Balance, BdkError, LocalUtxo, NetworkLocalUtxo, OutPoint, Progress,
ProgressHolder, RbfValue, Script, ScriptAmount, TransactionDetails, TxBuilderResult,
};
#[derive(Debug)]
pub(crate) struct Wallet {
pub(crate) wallet_mutex: Mutex<BdkWallet<AnyDatabase>>,
}
/// A Bitcoin wallet.
/// The Wallet acts as a way of coherently interfacing with output descriptors and related transactions. Its main components are:
/// 1. Output descriptors from which it can derive addresses.
/// 2. A Database where it tracks transactions and utxos related to the descriptors.
/// 3. Signers that can contribute signatures to addresses instantiated from the descriptors.
impl Wallet {
pub(crate) fn new(
descriptor: Arc<Descriptor>,
change_descriptor: Option<Arc<Descriptor>>,
network: Network,
database_config: DatabaseConfig,
) -> Result<Self, BdkError> {
let any_database_config = match database_config {
DatabaseConfig::Memory => AnyDatabaseConfig::Memory(()),
DatabaseConfig::Sled { config } => AnyDatabaseConfig::Sled(config),
DatabaseConfig::Sqlite { config } => AnyDatabaseConfig::Sqlite(config),
};
let database = AnyDatabase::from_config(&any_database_config)?;
let descriptor: String = descriptor.as_string_private();
let change_descriptor: Option<String> = change_descriptor.map(|d| d.as_string_private());
let wallet_mutex = Mutex::new(BdkWallet::new(
&descriptor,
change_descriptor.as_ref(),
network,
database,
)?);
Ok(Wallet { wallet_mutex })
}
pub(crate) fn get_wallet(&self) -> MutexGuard<BdkWallet<AnyDatabase>> {
self.wallet_mutex.lock().expect("wallet")
}
/// Get the Bitcoin network the wallet is using.
pub(crate) fn network(&self) -> Network {
self.get_wallet().network()
}
/// Sync the internal database with the blockchain.
pub(crate) fn sync(
&self,
blockchain: &Blockchain,
progress: Option<Box<dyn Progress>>,
) -> Result<(), BdkError> {
let bdk_sync_opts = BdkSyncOptions {
progress: progress.map(|p| {
Box::new(ProgressHolder { progress: p })
as Box<(dyn bdk::blockchain::Progress + 'static)>
}),
};
let blockchain = blockchain.get_blockchain();
self.get_wallet().sync(blockchain.deref(), bdk_sync_opts)
}
/// Return a derived address using the external descriptor, see AddressIndex for available address index selection
/// strategies. If none of the keys in the descriptor are derivable (i.e. the descriptor does not end with a * character)
/// then the same address will always be returned for any AddressIndex.
pub(crate) fn get_address(&self, address_index: AddressIndex) -> Result<AddressInfo, BdkError> {
self.get_wallet()
.get_address(address_index.into())
.map(AddressInfo::from)
}
/// Return a derived address using the internal (change) descriptor.
///
/// If the wallet doesn't have an internal descriptor it will use the external descriptor.
///
/// see [`AddressIndex`] for available address index selection strategies. If none of the keys
/// in the descriptor are derivable (i.e. does not end with /*) then the same address will always
/// be returned for any [`AddressIndex`].
pub(crate) fn get_internal_address(
&self,
address_index: AddressIndex,
) -> Result<AddressInfo, BdkError> {
self.get_wallet()
.get_internal_address(address_index.into())
.map(AddressInfo::from)
}
/// Return the balance, meaning the sum of this wallets unspent outputs values. Note that this method only operates
/// on the internal database, which first needs to be Wallet.sync manually.
pub(crate) fn get_balance(&self) -> Result<Balance, BdkError> {
self.get_wallet().get_balance().map(|b| b.into())
}
/// Sign a transaction with all the wallets signers.
pub(crate) fn sign(&self, psbt: &PartiallySignedTransaction) -> Result<bool, BdkError> {
let mut psbt = psbt.internal.lock().unwrap();
self.get_wallet().sign(&mut psbt, SignOptions::default())
}
/// Return the list of transactions made and received by the wallet. Note that this method only operate on the internal database, which first needs to be [Wallet.sync] manually.
pub(crate) fn list_transactions(&self) -> Result<Vec<TransactionDetails>, BdkError> {
let transaction_details = self.get_wallet().list_transactions(true)?;
Ok(transaction_details
.iter()
.map(TransactionDetails::from)
.collect())
}
/// Return the list of unspent outputs of this wallet. Note that this method only operates on the internal database,
/// which first needs to be Wallet.sync manually.
pub(crate) fn list_unspent(&self) -> Result<Vec<LocalUtxo>, BdkError> {
let unspents = self.get_wallet().list_unspent()?;
Ok(unspents
.iter()
.map(|u| LocalUtxo::from_utxo(u, self.network()))
.collect())
}
}
/// A transaction builder.
/// After creating the TxBuilder, you set options on it until finally calling finish to consume the builder and generate the transaction.
/// Each method on the TxBuilder returns an instance of a new TxBuilder with the option set/added.
#[derive(Clone, Debug)]
pub(crate) struct TxBuilder {
pub(crate) recipients: Vec<(BdkScript, u64)>,
pub(crate) utxos: Vec<OutPoint>,
pub(crate) unspendable: HashSet<OutPoint>,
pub(crate) change_policy: ChangeSpendPolicy,
pub(crate) manually_selected_only: bool,
pub(crate) fee_rate: Option<f32>,
pub(crate) fee_absolute: Option<u64>,
pub(crate) drain_wallet: bool,
pub(crate) drain_to: Option<BdkScript>,
pub(crate) rbf: Option<RbfValue>,
pub(crate) data: Vec<u8>,
}
impl TxBuilder {
pub(crate) fn new() -> Self {
TxBuilder {
recipients: Vec::new(),
utxos: Vec::new(),
unspendable: HashSet::new(),
change_policy: ChangeSpendPolicy::ChangeAllowed,
manually_selected_only: false,
fee_rate: None,
fee_absolute: None,
drain_wallet: false,
drain_to: None,
rbf: None,
data: Vec::new(),
}
}
/// Add a recipient to the internal list.
pub(crate) fn add_recipient(&self, script: Arc<Script>, amount: u64) -> Arc<Self> {
let mut recipients: Vec<(BdkScript, u64)> = self.recipients.clone();
recipients.append(&mut vec![(script.script.clone(), amount)]);
Arc::new(TxBuilder {
recipients,
..self.clone()
})
}
pub(crate) fn set_recipients(&self, recipients: Vec<ScriptAmount>) -> Arc<Self> {
let recipients = recipients
.iter()
.map(|script_amount| (script_amount.script.script.clone(), script_amount.amount))
.collect();
Arc::new(TxBuilder {
recipients,
..self.clone()
})
}
/// 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.
pub(crate) fn add_unspendable(&self, unspendable: OutPoint) -> Arc<Self> {
let mut unspendable_hash_set = self.unspendable.clone();
unspendable_hash_set.insert(unspendable);
Arc::new(TxBuilder {
unspendable: unspendable_hash_set,
..self.clone()
})
}
/// Add an outpoint to the internal list of UTXOs that must be spent. These have priority over the "unspendable"
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
pub(crate) fn add_utxo(&self, outpoint: OutPoint) -> Arc<Self> {
self.add_utxos(vec![outpoint])
}
/// Add the list of outpoints to the internal list of UTXOs that must be spent. If an error occurs while adding
/// any of the UTXOs then none of them are added and the error is returned. These have priority over the "unspendable"
/// utxos, meaning that if a utxo is present both in the "utxos" and the "unspendable" list, it will be spent.
pub(crate) fn add_utxos(&self, mut outpoints: Vec<OutPoint>) -> Arc<Self> {
let mut utxos = self.utxos.to_vec();
utxos.append(&mut outpoints);
Arc::new(TxBuilder {
utxos,
..self.clone()
})
}
/// Do not spend change outputs. This effectively adds all the change outputs to the "unspendable" list. See TxBuilder.unspendable.
pub(crate) fn do_not_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::ChangeForbidden,
..self.clone()
})
}
/// Only spend utxos added by [add_utxo]. The wallet will not add additional utxos to the transaction even if they are
/// needed to make the transaction valid.
pub(crate) fn manually_selected_only(&self) -> Arc<Self> {
Arc::new(TxBuilder {
manually_selected_only: true,
..self.clone()
})
}
/// Only spend change outputs. This effectively adds all the non-change outputs to the "unspendable" list. See TxBuilder.unspendable.
pub(crate) fn only_spend_change(&self) -> Arc<Self> {
Arc::new(TxBuilder {
change_policy: ChangeSpendPolicy::OnlyChange,
..self.clone()
})
}
/// 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.
pub(crate) fn unspendable(&self, unspendable: Vec<OutPoint>) -> Arc<Self> {
Arc::new(TxBuilder {
unspendable: unspendable.into_iter().collect(),
..self.clone()
})
}
/// Set a custom fee rate.
pub(crate) fn fee_rate(&self, sat_per_vb: f32) -> Arc<Self> {
Arc::new(TxBuilder {
fee_rate: Some(sat_per_vb),
..self.clone()
})
}
/// Set an absolute fee.
pub(crate) fn fee_absolute(&self, fee_amount: u64) -> Arc<Self> {
Arc::new(TxBuilder {
fee_absolute: Some(fee_amount),
..self.clone()
})
}
/// Spend all the available inputs. This respects filters like TxBuilder.unspendable and the change policy.
pub(crate) fn drain_wallet(&self) -> Arc<Self> {
Arc::new(TxBuilder {
drain_wallet: true,
..self.clone()
})
}
/// Sets the address to drain excess coins to. Usually, when there are excess coins they are sent to a change address
/// generated by the wallet. This option replaces the usual change address with an arbitrary ScriptPubKey of your choosing.
/// Just as with a change output, if the drain output is not needed (the excess coins are too small) it will not be included
/// in the resulting transaction. The only difference is that it is valid to use drain_to without setting any ordinary recipients
/// with add_recipient (but it is perfectly fine to add recipients as well). If you choose not to set any recipients, you should
/// either provide the utxos that the transaction should spend via add_utxos, or set drain_wallet to spend all of them.
/// When bumping the fees of a transaction made with this option, you probably want to use BumpFeeTxBuilder.allow_shrinking
/// to allow this output to be reduced to pay for the extra fees.
pub(crate) fn drain_to(&self, script: Arc<Script>) -> Arc<Self> {
Arc::new(TxBuilder {
drain_to: Some(script.script.clone()),
..self.clone()
})
}
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
Arc::new(TxBuilder {
rbf: Some(RbfValue::Default),
..self.clone()
})
}
/// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
/// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(TxBuilder {
rbf: Some(RbfValue::Value(nsequence)),
..self.clone()
})
}
/// Add data as an output using OP_RETURN.
pub(crate) fn add_data(&self, data: Vec<u8>) -> Arc<Self> {
Arc::new(TxBuilder {
data,
..self.clone()
})
}
/// Finish building the transaction. Returns the BIP174 PSBT.
pub(crate) fn finish(&self, wallet: &Wallet) -> Result<TxBuilderResult, BdkError> {
let wallet = wallet.get_wallet();
let mut tx_builder = wallet.build_tx();
for (script, amount) in &self.recipients {
tx_builder.add_recipient(script.clone(), *amount);
}
tx_builder.change_policy(self.change_policy);
if !self.utxos.is_empty() {
let bdk_utxos: Vec<BdkOutPoint> = self.utxos.iter().map(BdkOutPoint::from).collect();
let utxos: &[BdkOutPoint] = &bdk_utxos;
tx_builder.add_utxos(utxos)?;
}
if !self.unspendable.is_empty() {
let bdk_unspendable: Vec<BdkOutPoint> =
self.unspendable.iter().map(BdkOutPoint::from).collect();
tx_builder.unspendable(bdk_unspendable);
}
if self.manually_selected_only {
tx_builder.manually_selected_only();
}
if let Some(sat_per_vb) = self.fee_rate {
tx_builder.fee_rate(FeeRate::from_sat_per_vb(sat_per_vb));
}
if let Some(fee_amount) = self.fee_absolute {
tx_builder.fee_absolute(fee_amount);
}
if self.drain_wallet {
tx_builder.drain_wallet();
}
if let Some(script) = &self.drain_to {
tx_builder.drain_to(script.clone());
}
if let Some(rbf) = &self.rbf {
match *rbf {
RbfValue::Default => {
tx_builder.enable_rbf();
}
RbfValue::Value(nsequence) => {
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
}
}
}
if !&self.data.is_empty() {
tx_builder.add_data(self.data.as_slice());
}
tx_builder
.finish()
.map(|(psbt, tx_details)| TxBuilderResult {
psbt: Arc::new(PartiallySignedTransaction {
internal: Mutex::new(psbt),
}),
transaction_details: TransactionDetails::from(&tx_details),
})
}
}
/// The BumpFeeTxBuilder is used to bump the fee on a transaction that has been broadcast and has its RBF flag set to true.
#[derive(Clone)]
pub(crate) struct BumpFeeTxBuilder {
pub(crate) txid: String,
pub(crate) fee_rate: f32,
pub(crate) allow_shrinking: Option<String>,
pub(crate) rbf: Option<RbfValue>,
}
impl BumpFeeTxBuilder {
pub(crate) fn new(txid: String, fee_rate: f32) -> Self {
Self {
txid,
fee_rate,
allow_shrinking: None,
rbf: None,
}
}
/// Explicitly tells the wallet that it is allowed to reduce the amount of the output matching this script_pubkey
/// in order to bump the transaction fee. Without specifying this the wallet will attempt to find a change output to
/// shrink instead. Note that the output may shrink to below the dust limit and therefore be removed. If it is preserved
/// then it is currently not guaranteed to be in the same position as it was originally. Returns an error if script_pubkey
/// cant be found among the recipients of the transaction we are bumping.
pub(crate) fn allow_shrinking(&self, address: String) -> Arc<Self> {
Arc::new(Self {
allow_shrinking: Some(address),
..self.clone()
})
}
/// Enable signaling RBF. This will use the default `nsequence` value of `0xFFFFFFFD`.
pub(crate) fn enable_rbf(&self) -> Arc<Self> {
Arc::new(Self {
rbf: Some(RbfValue::Default),
..self.clone()
})
}
/// Enable signaling RBF with a specific nSequence value. This can cause conflicts if the wallet's descriptors contain an
/// "older" (OP_CSV) operator and the given `nsequence` is lower than the CSV value. If the `nsequence` is higher than `0xFFFFFFFD`
/// an error will be thrown, since it would not be a valid nSequence to signal RBF.
pub(crate) fn enable_rbf_with_sequence(&self, nsequence: u32) -> Arc<Self> {
Arc::new(Self {
rbf: Some(RbfValue::Value(nsequence)),
..self.clone()
})
}
/// Finish building the transaction. Returns the BIP174 PSBT.
pub(crate) fn finish(
&self,
wallet: &Wallet,
) -> Result<Arc<PartiallySignedTransaction>, BdkError> {
let wallet = wallet.get_wallet();
let txid = Txid::from_str(self.txid.as_str())?;
let mut tx_builder = wallet.build_fee_bump(txid)?;
tx_builder.fee_rate(FeeRate::from_sat_per_vb(self.fee_rate));
if let Some(allow_shrinking) = &self.allow_shrinking {
let address = BdkAddress::from_str(allow_shrinking)
.map_err(|e| BdkError::Generic(e.to_string()))?;
let script = address.script_pubkey();
tx_builder.allow_shrinking(script)?;
}
if let Some(rbf) = &self.rbf {
match *rbf {
RbfValue::Default => {
tx_builder.enable_rbf();
}
RbfValue::Value(nsequence) => {
tx_builder.enable_rbf_with_sequence(Sequence(nsequence));
}
}
}
tx_builder
.finish()
.map(|(psbt, _)| PartiallySignedTransaction {
internal: Mutex::new(psbt),
})
.map(Arc::new)
}
}
// The goal of these tests to to ensure `bdk-ffi` intermediate code correctly calls `bdk` APIs.
// These tests should not be used to verify `bdk` behavior that is already tested in the `bdk`
// crate.
#[cfg(test)]
mod test {
use crate::database::DatabaseConfig;
use crate::descriptor::Descriptor;
use crate::wallet::{AddressIndex, TxBuilder, Wallet};
use bdk::bitcoin::{Address, Network};
use bdk::wallet::get_funded_wallet;
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 {
wallet_mutex: Mutex::new(funded_wallet),
};
let drain_to_address = "tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt".to_string();
let drain_to_script = crate::Address::new(drain_to_address)
.unwrap()
.script_pubkey();
let tx_builder = TxBuilder::new()
.drain_wallet()
.drain_to(drain_to_script.clone());
assert!(tx_builder.drain_wallet);
assert_eq!(tx_builder.drain_to, Some(drain_to_script.script.clone()));
let tx_builder_result = tx_builder.finish(&test_wallet).unwrap();
let psbt = tx_builder_result.psbt.internal.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,
"bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
);
assert_eq!(
wallet
.get_address(AddressIndex::Peek { index: 1 })
.unwrap()
.address,
"bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
);
// new index still 0
assert_eq!(
wallet
.get_address(crate::AddressIndex::New)
.unwrap()
.address,
"bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
);
// new index now 1
assert_eq!(
wallet
.get_address(crate::AddressIndex::New)
.unwrap()
.address,
"bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
);
// new index now 2
assert_eq!(
wallet
.get_address(crate::AddressIndex::New)
.unwrap()
.address,
"bcrt1q5g0mq6dkmwzvxscqwgc932jhgcxuqqkjv09tkj"
);
// peek index 1
assert_eq!(
wallet
.get_address(AddressIndex::Peek { index: 1 })
.unwrap()
.address,
"bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
);
// reset to index 0
assert_eq!(
wallet
.get_address(AddressIndex::Reset { index: 0 })
.unwrap()
.address,
"bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
);
// new index 1 again
assert_eq!(
wallet.get_address(AddressIndex::New).unwrap().address,
"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(crate::AddressIndex::New)
.unwrap()
.address,
"bcrt1qqjn9gky9mkrm3c28e5e87t5akd3twg6xezp0tv"
);
assert_eq!(
wallet
.get_address(crate::AddressIndex::New)
.unwrap()
.address,
"bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
);
assert_eq!(
wallet
.get_address(crate::AddressIndex::LastUnused)
.unwrap()
.address,
"bcrt1q0xs7dau8af22rspp4klya4f7lhggcnqfun2y3a"
);
assert_eq!(
wallet
.get_internal_address(crate::AddressIndex::New)
.unwrap()
.address,
"bcrt1qpmz73cyx00r4a5dea469j40ax6d6kqyd67nnpj"
);
assert_eq!(
wallet
.get_internal_address(crate::AddressIndex::New)
.unwrap()
.address,
"bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
);
assert_eq!(
wallet
.get_internal_address(crate::AddressIndex::LastUnused)
.unwrap()
.address,
"bcrt1qaux734vuhykww9632v8cmdnk7z2mw5lsf74v6k"
);
}
}

View File

@@ -25,10 +25,10 @@ val internalDescriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEm
val databaseConfig = DatabaseConfig.Memory
val blockchainConfig = BlockchainConfig.Electrum(
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u)
ElectrumConfig("ssl://electrum.blockstream.info:60002", null, 5u, null, 10u, true)
)
val wallet = Wallet(externalDescriptor, internalDescriptor, Network.TESTNET, databaseConfig, blockchainConfig)
val newAddress = wallet.getAddress(AddressIndex.LAST_UNUSED)
val newAddress = wallet.getAddress(AddressIndex.LastUnused)
```
### Snapshot releases
@@ -82,5 +82,19 @@ and use the `publishToMavenLocal` task without excluding the signing task:
./gradlew publishToMavenLocal
```
## Known issues
## JNA dependency
Depending on the JVM version you use, you might not have the JNA dependency on your classpath. The exception thrown will be
```shell
class file for com.sun.jna.Pointer not found
```
The solution is to add JNA as a dependency like so:
```kotlin
dependencies {
// ...
implementation("net.java.dev.jna:jna:5.12.1")
}
```
[`bdk`]: https://github.com/bitcoindevkit/bdk
[`bdk-ffi`]: https://github.com/bitcoindevkit/bdk-ffi

View File

@@ -1,4 +1,4 @@
org.gradle.jvmargs=-Xmx1536m
android.enableJetifier=true
kotlin.code.style=official
libraryVersion=0.25.0
libraryVersion=0.27.1

View File

@@ -1,5 +1,6 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat.*
import org.gradle.api.tasks.testing.logging.TestLogEvent.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
// library version is defined in gradle.properties
val libraryVersion: String by project
@@ -101,3 +102,9 @@ signing {
useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword)
sign(publishing.publications)
}
// This task dependency ensures that we build the bindings
// binaries before running the tests
tasks.withType<KotlinCompile> {
dependsOn("buildJvmLib")
}

View File

@@ -28,8 +28,7 @@ class JvmLibTest {
}
}
private val descriptor =
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
private val descriptor = Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", Network.TESTNET)
private val databaseConfig = DatabaseConfig.Memory
@@ -39,14 +38,15 @@ class JvmLibTest {
null,
5u,
null,
100u
100u,
true,
)
)
@Test
fun memoryWalletNewAddress() {
val wallet = Wallet(descriptor, null, Network.TESTNET, databaseConfig)
val address = wallet.getAddress(AddressIndex.NEW).address
val address = wallet.getAddress(AddressIndex.New).address
assertEquals("tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", address)
}

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.

View File

@@ -1,17 +0,0 @@
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -18,7 +18,7 @@ pip install bdkpython
import bdkpython as bdk
descriptor = "wpkh(tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/84h/0h/0h/0/*)"
descriptor = bdk.Descriptor("wpkh(tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy/84h/0h/0h/0/*)", bdk.Network.TESTNET)
db_config = bdk.DatabaseConfig.MEMORY()
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
bdk.ElectrumConfig(
@@ -26,7 +26,8 @@ blockchain_config = bdk.BlockchainConfig.ELECTRUM(
None,
5,
None,
100
100,
True,
)
)
blockchain = bdk.Blockchain(blockchain_config)
@@ -39,7 +40,7 @@ wallet = bdk.Wallet(
)
# print new receive address
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED)
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
address = address_info.address
index = address_info.index
print(f"New BIP84 testnet address: {address} at index {index}")
@@ -59,7 +60,7 @@ rust_ext = RustExtension(
setup(
name='bdkpython',
version='0.25.0',
version='0.27.1',
description="The Python language bindings for the Bitcoin Development Kit",
long_description=LONG_DESCRIPTION,
long_description_content_type='text/markdown',
@@ -67,7 +68,7 @@ setup(
zip_safe=False,
packages=['bdkpython'],
package_dir={'bdkpython': './src/bdkpython'},
url="https://github.com/bitcoindevkit/bdk-python",
url="https://github.com/bitcoindevkit/bdk-ffi",
author="Alekos Filini <alekos.filini@gmail.com>, Steve Myers <steve@notmandatory.org>",
license="MIT or Apache 2.0",
)

View File

@@ -1,7 +1,8 @@
import bdkpython as bdk
import unittest
descriptor = "wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)"
descriptor = bdk.Descriptor("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)", bdk.Network.TESTNET)
db_config = bdk.DatabaseConfig.MEMORY()
blockchain_config = bdk.BlockchainConfig.ELECTRUM(
bdk.ElectrumConfig(
@@ -9,7 +10,8 @@ blockchain_config = bdk.BlockchainConfig.ELECTRUM(
None,
5,
None,
100
100,
True,
)
)
blockchain = bdk.Blockchain(blockchain_config)
@@ -24,7 +26,7 @@ class TestSimpleBip84Wallet(unittest.TestCase):
network=bdk.Network.TESTNET,
database_config=db_config
)
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED)
address_info = wallet.get_address(bdk.AddressIndex.LAST_UNUSED())
address = address_info.address
# print(f"New address is {address}")
assert address == "tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e", f"Wrong address {address}, should be tb1qzg4mckdh50nwdm9hkzq06528rsu73hjxxzem3e"

View File

@@ -5,14 +5,14 @@ This project builds a Swift package that provides [Swift] language bindings for
Supported target platforms are:
- MacOS, X86_64 and M1 (aarch64)
- macOS, X86_64 and M1 (aarch64)
- iOS, iPhones (aarch64)
- iOS simulator, X86_64 and M1 (aarch64)
## How to Use
To use the Swift language bindings for [`bdk`] in your [Xcode] iOS or MacOS project add
the github repository https://github.com/bitcoindevkit/bdk-swift and select one of the
To use the Swift language bindings for [`bdk`] in your [Xcode] iOS or macOS project add
the GitHub repository https://github.com/bitcoindevkit/bdk-swift and select one of the
release versions. You may then import and use the `BitcoinDevKit` library in your Swift
code. For example:
@@ -40,11 +40,11 @@ swift test
## How to Build and Publish
If you are a maintainer of this project or want to build and publish this project to your
own Github repository use the following steps:
own GitHub repository use the following steps:
1. If it doesn't already exist, create a new `release/0.MINOR` branch from the `master` branch.
2. Add a tag `v0.MINOR.PATCH`.
3. Run the `publish-spm` workflow on Github from the `bdk-swift` repo for version `0.MINOR.PATCH`.
3. Run the `publish-spm` workflow on GitHub from the `bdk-swift` repo for version `0.MINOR.PATCH`.
[Swift]: https://developer.apple.com/swift/
[Xcode]: https://developer.apple.com/documentation/Xcode

View File

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