Compare commits

...

130 Commits

Author SHA1 Message Date
Sudarsan Balaji
a68a8bee7d Merge pull request #75 from bitcoindevkit/update-readme-with-deployed-package-versions
Update README with latest published package information
2021-11-06 05:48:53 +05:30
Sudarsan Balaji
e250d4ae1f Update README with latest published package information 2021-11-06 05:48:27 +05:30
Sudarsan Balaji
3e0ae31890 Merge pull request #73 from bitcoindevkit/publish-a-package
Publish android and kotlin packages
2021-11-06 01:31:42 +05:30
Sudarsan Balaji
d197e17eaa Add some notes on consuming published packages 2021-11-06 01:30:36 +05:30
Sudarsan Balaji
63cbcb1aa3 Remove armv7 ABI target instead of i686 2021-11-06 00:45:03 +05:30
Sudarsan Balaji
be8b31684f Stop publishing on build 2021-11-06 00:23:06 +05:30
Sudarsan Balaji
862658ce96 Stop copying over i686 2021-11-06 00:22:58 +05:30
Sudarsan Balaji
6257911095 Ignore unnecessary files 2021-11-05 23:52:08 +05:30
Steve Myers
01da0137ef Merge pull request #70 from notmandatory/fix_build
Fix build.sh kotlin copy for android, also a rust fmt fix
2021-11-04 17:47:52 -07:00
Steve Myers
f86a9df594 Fix build.sh kotlin copy for android 2021-11-04 17:44:02 -07:00
Sudarsan Balaji
379cbe0b59 Merge pull request #69 from bitcoindevkit/return-transaction-details-on-broadcast
Return transaction details on broadcast
2021-11-05 01:19:04 +05:30
Sudarsan Balaji
4fd4a7ee6f Use From trait for conversion 2021-11-05 01:13:45 +05:30
Sudarsan Balaji
c6c4446092 Use From trait for conversion 2021-11-05 01:08:50 +05:30
Sudarsan Balaji
358cc35b60 Update Wallet::broadcast API 2021-11-05 00:45:40 +05:30
Sudarsan Balaji
c58a31f711 Return transaction on broadcast 2021-11-05 00:45:27 +05:30
Sudarsan Balaji
8a9e025e2f Simplify 2021-11-05 00:45:16 +05:30
Sudarsan Balaji
2ac26fa060 Add details to PSBT 2021-11-05 00:45:02 +05:30
Sudarsan Balaji
7e61659cb7 Add a way to convert TransactionDetails to Transaction 2021-11-05 00:44:40 +05:30
Sudarsan Balaji
947a5cb8e0 Allow cloning transaction 2021-11-05 00:43:26 +05:30
Sudarsan Balaji
f6b099aa76 Merge pull request #67 from bitcoindevkit/allow-passing-a-fee-rate-when-creating-a-transaction
Add optional fee rate to a transaction
2021-11-04 23:30:11 +05:30
Sudarsan Balaji
0467e12aae Add optional fee rate to a transaction 2021-11-04 23:29:22 +05:30
Sudarsan Balaji
94b07b9fb9 Merge pull request #65 from bitcoindevkit/allow-getting-last-unused-wallet-address
Allow getting last unused wallet address
2021-11-04 22:48:55 +05:30
Sudarsan Balaji
9ee31d97c7 Expose Wallet::getLastUnusedAddress 2021-11-04 22:45:00 +05:30
Sudarsan Balaji
2c30bdff56 Add a way to get last unused address 2021-11-04 22:44:38 +05:30
Sudarsan Balaji
97c59b4cad Merge pull request #52 from notmandatory/add-more-features-to-ios-example
Add more features to iOS example
2021-11-02 21:59:59 +05:30
Sudarsan Balaji
c5f18ca998 Ignore xc user data 2021-11-02 21:48:29 +05:30
Sudarsan Balaji
c79f1b6326 Ignore unnecessary files 2021-11-02 18:43:05 +05:30
Steve Myers
334bafcc7c Delete generated swift files, update lib name 2021-11-01 19:36:52 -07:00
Steve Myers
1148a2e6d7 Rename kotlin package to org.bitcoindevkit, rust lib to bdkffi 2021-11-01 19:07:27 -07:00
Sudarsan Balaji
80510381de Merge pull request #40 from notmandatory/add-documentation-for-swift-bindings
Add sections and more info to README
2021-10-30 00:29:24 +05:30
Sudarsan Balaji
a4370332df Fix heading levels 2021-10-30 00:28:26 +05:30
Sudarsan Balaji
693a75e6d1 Add sections and more info 2021-10-30 00:27:02 +05:30
Steve Myers
b6fa3539e4 Update README android setup 2021-10-28 15:34:17 -07:00
Steve Myers
5e7a42df07 Fix build.sh and test.sh help 2021-10-28 14:22:45 -07:00
Steve Myers
3b05c57794 Update bdk dependency to 0.13.0 2021-10-28 14:21:57 -07:00
Sudarsan Balaji
aabb09ccb8 Merge pull request #35 from notmandatory/add-swift-language-bindings
Add swift language bindings
2021-10-28 23:01:11 +05:30
Sudarsan Balaji
ab301f075b Update uniffi to 0.14.1
which supports callback interface in Swift
2021-10-28 23:00:23 +05:30
Sudarsan Balaji
9681f30e19 Revert "Remove callback interface"
This reverts commit 4ad9b89e25.
2021-10-28 01:51:16 +05:30
Sudarsan Balaji
ef73e38154 Add swiftmodule 2021-10-28 00:37:42 +05:30
Sudarsan Balaji
e98298090d Generate bindings for swift 2021-10-28 00:35:22 +05:30
Sudarsan Balaji
d1b24026e6 Copy kotlin libs only when building kotlin 2021-10-28 00:35:09 +05:30
Sudarsan Balaji
4ad9b89e25 Remove callback interface 2021-10-28 00:33:10 +05:30
Sudarsan Balaji
a8d9864825 Merge pull request #32 from notmandatory/fix-identifier-case-in-udl
Use snake_case for identifier
2021-10-21 23:43:28 +05:30
Sudarsan Balaji
71531b7abb Use snake_case for identifier 2021-10-21 23:42:38 +05:30
Sudarsan Balaji
a7a45a036c Merge pull request #31 from notmandatory/change-aar-outputs
Export source files with aar
2021-10-21 23:27:04 +05:30
Sudarsan Balaji
7e02dbc97a Export source files with aar 2021-10-21 23:26:34 +05:30
Sudarsan Balaji
1b30ce14a3 Merge pull request #30 from notmandatory/allow-creating-a-wallet-with-a-change-descriptor
Allow creating a wallet with a change descriptor
2021-10-21 14:54:21 +05:30
Sudarsan Balaji
e0937b73db Add change descriptor to Wallet 2021-10-21 14:50:52 +05:30
Sudarsan Balaji
a8b161569c Merge pull request #29 from notmandatory/allow-restoring-extended-key-from-mnemonic
Allow restoring extended key from mnemonic
2021-10-21 14:45:11 +05:30
Sudarsan Balaji
04eec7bf56 Allow restoring extended keys from mnemonic 2021-10-21 14:40:26 +05:30
Sudarsan Balaji
f2bbe668b1 Merge pull request #28 from notmandatory/allow-generating-key
Allow creating extended keys
2021-10-21 14:37:18 +05:30
Sudarsan Balaji
f1c2118b02 Allow generating extended keys 2021-10-21 14:35:40 +05:30
Sudarsan Balaji
f34e59e289 Merge pull request #21 from notmandatory/list-incomplete-transactions
List both confirmed and unconfirmed transactions
2021-10-18 17:35:14 +05:30
Sudarsan Balaji
892bfe868f List both confirmed and unconfirmed transactions 2021-10-18 15:48:30 +05:30
Steve Myers
fe251c12f3 Add android aar build and connected device test 2021-10-17 14:51:05 -07:00
Steve Myers
ee59dbe543 Remove and ignore generated code and binary libs 2021-10-17 14:51:02 -07:00
Sudarsan Balaji
2f83a9ed05 Merge pull request #18 from notmandatory/allow-listing-transactions
Allow listing confirmed transactions
2021-10-17 03:19:42 +05:30
Sudarsan Balaji
4a7d665f7c Return only confirmed transactions in Wallet::getTransactions 2021-10-17 02:36:43 +05:30
Sudarsan Balaji
25b8a8841d Allow listing confirmed transactions 2021-10-17 02:28:26 +05:30
Sudarsan Balaji
38b8589526 Merge pull request #14 from notmandatory/allow-signing-partially-signed-transactions
Allow signing partially signed transactions
2021-10-16 20:28:03 +05:30
Sudarsan Balaji
3693e99372 Share Wallet::getBalance and Wallet::sign 2021-10-16 20:25:58 +05:30
Sudarsan Balaji
d97a13d186 Fix formatting 2021-10-16 20:19:56 +05:30
Sudarsan Balaji
4e1c6bd62b Add sign and broadcast to wallet 2021-10-16 20:19:34 +05:30
Sudarsan Balaji
04eee046cb Merge pull request #13 from notmandatory/allow-creating-a-wallet-transaction
Allow creating partially signed bitcoin transactions
2021-10-16 16:44:49 +05:30
Sudarsan Balaji
69a676ba36 Allow creating partially signed bitcoin transactions 2021-10-16 16:42:35 +05:30
Sudarsan Balaji
a528a76c5d Merge pull request #12 from notmandatory/add-demo-application
Add demo application
2021-10-16 14:46:50 +05:30
Sudarsan Balaji
653773a638 Add demo application in kotlin 2021-10-16 14:45:32 +05:30
Sudarsan Balaji
857d99030b Merge pull request #11 from notmandatory/unify-offline-operations
Share OfflineWalletOperations
2021-10-16 14:22:37 +05:30
Sudarsan Balaji
2db59de659 Share OfflineWalletOperations 2021-10-16 14:19:29 +05:30
Steve Myers
c15993310d Reorganize bdk-kotlin into jvm sub-module 2021-10-14 22:05:21 -07:00
artfuldev
b2f2f9135d Add OnlineWallet::getBalance() 2021-10-15 03:40:33 +05:30
artfuldev
3a7e7baf51 Remove testdb before every test 2021-10-15 03:05:46 +05:30
artfuldev
d307b281d7 Remove unnecessary Mutex wrapper 2021-10-15 03:00:49 +05:30
artfuldev
41afeafcd3 Test a callback 2021-10-15 01:54:32 +05:30
artfuldev
47651f3681 Add OnlineWallet::getNetwork 2021-10-15 00:48:53 +05:30
artfuldev
9b7a9ded56 Add online wallet 2021-10-15 00:43:17 +05:30
Steve Myers
178fd6f010 Change order of Network param 2021-10-14 11:17:52 -07:00
Steve Myers
31710ac77b Add Network enum as wallet constructor param 2021-10-14 10:58:16 -07:00
Sudarsan Balaji
6d7939c88f Allow using configs for database 2021-10-14 04:23:17 +05:30
Sudarsan Balaji
598d08b3bc Use a thread-safe MemoryDatabase 2021-10-14 03:53:22 +05:30
Sudarsan Balaji
b6d6a0d092 Add name to authors 2021-10-14 00:15:25 +05:30
Sudarsan Balaji
6093a8750b Add JNA debug_load to gradle script 2021-10-14 00:06:05 +05:30
Sudarsan Balaji
279f304d5d Add a little bit of error handling 2021-10-14 00:05:50 +05:30
Sudarsan Balaji
64fbf60a6a Stop running gradle build
which also runs tests
2021-10-14 00:05:29 +05:30
Sudarsan Balaji
ce93e4e954 Ignore more files
mac-specific
local sled database
2021-10-14 00:05:04 +05:30
Sudarsan Balaji
cf56619157 Use settings from rust-fmt 2021-10-14 00:04:44 +05:30
Sudarsan Balaji
caff93c945 Add editorconfig 2021-10-13 13:42:55 +05:30
Steve Myers
01bfe5d10e Update README, build.sh and test.sh, rust fmt 2021-10-12 18:22:02 -07:00
Steve Myers
091c9994fa [WIP] reorganize and remove old stuff 2021-10-12 15:24:11 -07:00
Sudarsan Balaji
f30558d55c Ignore testdb 2021-10-13 03:06:49 +05:30
Sudarsan Balaji
4efbfc1e9c Add some more steps to run 2021-10-13 03:05:55 +05:30
Sudarsan Balaji
3491a4548d [WIP] kotlin tests work! 2021-10-13 03:02:49 +05:30
Sudarsan Balaji
0cc2f0a5be [WIP] Add generated and test files 2021-10-13 02:05:46 +05:30
Sudarsan Balaji
784f754cd9 [WIP] Add get new address API to Wallet 2021-10-13 01:45:22 +05:30
Steve Myers
8faba58cd4 wip compiles now 2021-10-12 11:53:11 -07:00
Steve Myers
0b265a7c26 WIP -- NOT WORKING 2021-10-12 10:27:33 -07:00
Steve Myers
f9e3bdfdb2 [wip] swift 2021-10-02 18:22:30 -07:00
Steve Myers
39e5efe5c0 Upgrade bdk dependency to 0.11 2021-09-28 17:03:25 -07:00
Steve Myers
342c4c4aa8 Remove local.properties and add to .gitignore 2021-07-07 10:42:42 -07:00
Steve Myers
bb9fdd35cf Update build.sh to install jvm darwin-x86-64 dylib 2021-07-05 14:25:09 -07:00
Steve Myers
a1aea54c53 Add Wallet.listTransactions() 2021-07-04 22:10:32 -07:00
Steve Myers
a33a09f2a3 Add Wallet.balance() 2021-07-04 15:54:23 -07:00
Steve Myers
6361b41f33 Return FfiResult errors as FfiError enum short values 2021-07-03 20:40:17 -07:00
Steve Myers
2abe7205cb Add FfiResultVoid type 2021-07-03 19:24:29 -07:00
Steve Myers
3e31e9aca3 Remove unneeded pointers from FfiResult types 2021-07-03 19:07:49 -07:00
Steve Myers
a056c0dd59 Reorganized code into wallet mod/package 2021-07-03 10:16:02 -07:00
Steve Myers
060e54a718 Refactor to return results by value, add wallet list_unspent and related types 2021-07-02 21:28:26 -07:00
Steve Myers
59fe24818a Add LibTest getTestDataDir to fix sled test on android 2021-06-26 18:55:49 -07:00
Steve Myers
91ae8dd537 Add kotlin BlockchainConfig and DatabaseConfig 2021-06-25 23:40:38 -07:00
Steve Myers
13e7217ffd Simplify Kotlin Wallet api 2021-06-24 15:00:00 -07:00
Steve Myers
af828efa93 Update build.sh to publish to local maven repo 2021-06-23 14:20:04 -07:00
Steve Myers
0f7a4aebf8 Fix Kotlin jar: native library path 2021-06-23 14:13:33 -07:00
Steve Myers
9f29eb0e86 Add classes to wrap LibJna native types 2021-06-22 11:53:09 -07:00
Steve Myers
3a5d4816ac Add jna lib to jvm jar resources 2021-06-21 15:08:39 -07:00
Steve Myers
2b40718875 Add kotlin test-fixtures module used by jvm and android 2021-06-20 20:26:13 -07:00
Steve Myers
36333b9fb7 Rename gradle modules to jvm and android 2021-06-20 18:48:48 -07:00
Steve Myers
f6c10da805 Return results as opaque structs from ffi calls 2021-06-20 15:48:06 -07:00
Steve Myers
76087b9aec Add kotlin/aar android device tests 2021-06-14 22:38:29 -07:00
Steve Myers
43ddf0a3b1 Execute bdk_ffi_test via valgrind 2021-06-14 14:18:16 -07:00
Steve Myers
5303de9593 Rename bdk-kotlin companion project, fix gradle warnings 2021-06-14 13:59:56 -07:00
Steve Myers
333f694d55 Remove unneeded test structs and functions, cleanup tests 2021-06-10 17:22:33 -07:00
Steve Myers
308d4af4f1 Fix kotlin wallet struct access via JNA opaque pointer 2021-06-10 13:41:46 -07:00
Steve Myers
2aad5d4428 Add missing kotlin files 2021-06-09 16:00:40 -07:00
Steve Myers
f31ebaa1e6 Test new, print, and free Config works 2021-06-08 18:15:20 -07:00
Steve Myers
43425c8875 Pass wallet by ref 2021-06-04 23:04:19 -07:00
Steve Myers
cb54405aed Fix bdk features 2021-06-04 23:01:28 -07:00
Steve Myers
49126a8943 Add strings example 2021-06-04 18:03:34 -07:00
Steve Myers
ec9d2ea284 Set safer-ffi version 2021-06-04 18:01:47 -07:00
Steve Myers
f07f0248ef Add build.sh 2021-06-04 18:00:55 -07:00
Steve Myers
d14ea9a059 Add WalletPtr 2021-06-04 11:07:51 -07:00
11 changed files with 873 additions and 63 deletions

29
.editorconfig Normal file
View File

@@ -0,0 +1,29 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.rs]
indent_size = 4
[*.kt]
indent_size = 4
[*.gradle]
indent_size = 4
[tests/**/*.rs]
charset = utf-8
end_of_line = unset
indent_size = unset
indent_style = unset
trim_trailing_whitespace = unset
insert_final_newline = unset

23
.gitignore vendored
View File

@@ -1,4 +1,19 @@
/target
*.h
/main
/Cargo.lock
target
build
Cargo.lock
/bindings/bdk-kotlin/local.properties
/bindings/bdk-swift
/bindings/bdk-swift.swiftdoc
/bindings/bdk-swift.swiftsourceinfo
.gradle
wallet_db
bdk_ffi_test
local.properties
*.log
*.dylib
*.so
.DS_Store
testdb
xcuserdata
.lsp
.clj-kondo

View File

@@ -1,16 +1,19 @@
[package]
name = "bdk_ffi"
name = "bdk-ffi"
version = "0.1.0"
authors = ["Steve Myers <steve@notmandatory.org>"]
authors = ["Steve Myers <steve@notmandatory.org>", "Sudarsan Balaji <sudarsan.balaji@artfuldev.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["staticlib"]
crate-type = ["staticlib", "cdylib"]
name = "bdkffi"
[dependencies]
bdk = { version = "^0.7", feature = ["all-keys"] }
safer-ffi = { version = "*", features = ["proc_macros"]}
bdk = { version = "0.13", features = ["all-keys", "use-esplora-ureq"] }
uniffi_macros = "0.14.1"
uniffi = "0.14.1"
thiserror = "1.0"
[features]
c-headers = ["safer-ffi/headers"]
[build-dependencies]
uniffi_build = "0.14.1"

112
README.md Normal file
View File

@@ -0,0 +1,112 @@
# Foreign language bindings for BDK (bdk-ffi)
This repository contains source code for generating foreign language bindings
for the rust library bdk for the Bitcoin Dev Kit (BDK) project.
## Supported target languages and platforms
| Language | Platform | Status |
| --- | --- | --- |
| Kotlin | JVM | WIP |
| Kotlin | Android | WIP |
| Swift | iOS | WIP |
## Getting Started (User)
If you just want to consume the language bindings:
### Kotlin (JVM)
Just add the dependency `org.bitcoindevkit:bdk-jvm:0.1.1`. The package is `org.bitcoindevkit.bdk`.
### Kotlin (Android)
Just add the dependency `org.bitcoindevkit:bdk-android:0.1.1`. The package is `org.bitcoindevkit.bdk`.
## Getting Started (Developer)
This project uses rust. A basic knowledge of the rust ecosystem is helpful.
### General
1. Install `uniffi-bindgen`
```sh
cargo install uniffi_bindgen
```
1. See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/) for more info
### Kotlin Bindings for JVM (OSX / Linux)
1. Install required targets
```sh
rustup target add x86_64-apple-darwin x86_64-unknown-linux-gnu
```
1. Build kotlin (JVM) bindings
```sh
./build.sh -k
```
1. Generated kotlin bindings are available at `/bindings/bdk-kotlin/`
1. A demo app is available at `/bindings/bdk-kotlin/demo/`. It uses stdin for
inputs and can be run from gradle.
```sh
cd bindings/bdk-kotlin
./gradlew :demo:run
```
### Kotlin bindings for Android
1. Install required targets
```sh
rustup target add x86_64-linux-android aarch64-linux-android
armv7-linux-androideabi i686-linux-android
```
1. Install Android SDK and Build-Tools for API level 30+
1. Setup `$ANDROID_NDK_HOME` and `$ANDROID_SDK_ROOT` path variables (which are
required by the build scripts)
1. Build kotlin (Android) bindings
```sh
./build.sh -a
```
2. A demo android app is available at [notmandatory/bdk-sample-app](https://github.com/notmandatory/bitcoindevkit-android-sample-app/tree/upgrade-to-bdk-ffi/)
### Swift bindings for iOS
1. Install the latest version of xcode, download and install the advanced tools.
1. Ensure Swift is installed
1. Install required targets
```sh
rustup target add aarch64-apple-ios x86_64-apple-ios
```
1. Build swift (iOS) bindings
```sh
./build.sh -s
```
1. Example iOS app can be found in `/examples/iOS` which can be run by xcode.
## Notes
### Adding new structs and functions
See the [UniFFI User Guide](https://mozilla.github.io/uniffi-rs/)
#### For pass by value objects
1. create new rust struct with only fields that are supported UniFFI types
1. update mapping `bdk.udl` file with new `dictionary`
#### For pass by reference values
1. create wrapper rust struct/impl with only fields that are `Sync + Send`
1. update mapping `bdk.udl` file with new `interface`
## Goals
1. Language bindings should feel idiomatic in target languages/platforms
1. Adding new targets should be easy
1. Getting up and running should be easy
1. Contributing should be easy
1. Get it right, then automate
## Thanks
This project is made possible thanks to the wonderful work on [mozilla/uniffi-rs](https://github.com/mozilla/uniffi-rs)

3
build.rs Normal file
View File

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

145
build.sh Executable file
View File

@@ -0,0 +1,145 @@
#!/usr/bin/env bash
set -eo pipefail
# functions
## help
help()
{
# Display Help
echo "Build bdk-ffi and related libraries."
echo
echo "Syntax: build [-a|h|k|s]"
echo "options:"
echo "-a Android."
echo "-h Print this Help."
echo "-k Kotlin."
echo "-s Swift."
echo
}
## rust
build_rust() {
echo "Build Rust library"
cargo fmt
cargo build
cargo test
}
## copy to bdk-bdk-kotlin
copy_lib_kotlin() {
echo -n "Copy "
case $OS in
"Darwin")
echo -n "darwin "
mkdir -p bindings/bdk-kotlin/jvm/src/main/resources/darwin-x86-64
cp target/debug/libbdkffi.dylib bindings/bdk-kotlin/jvm/src/main/resources/darwin-x86-64
;;
"Linux")
echo -n "linux "
mkdir -p bindings/bdk-kotlin/jvm/src/main/resources/linux-x86-64
cp target/debug/libbdkffi.so bindings/bdk-kotlin/jvm/src/main/resources/linux-x86-64
;;
esac
echo "libs to kotlin sub-project"
}
## bdk-bdk-kotlin jar
build_kotlin() {
copy_lib_kotlin
uniffi-bindgen generate src/bdk.udl --no-format --out-dir bindings/bdk-kotlin/jvm/src/main/kotlin --language kotlin
}
## bdk swift
build_swift() {
uniffi-bindgen generate src/bdk.udl --no-format --out-dir bindings/bdk-swift/ --language swift
swiftc -module-name bdk -emit-library -o libbdkffi.dylib -emit-module -emit-module-path ./bindings/bdk-swift/ -parse-as-library -L ./target/debug/ -lbdkffi -Xcc -fmodule-map-file=./bindings/bdk-swift/bdkFFI.modulemap ./bindings/bdk-swift/bdk.swift
TARGETDIR=target
RELDIR=debug
STATIC_LIB_NAME=libbdkffi.a
# We can't use cargo lipo because we can't link to universal libraries :(
# https://github.com/rust-lang/rust/issues/55235
LIBS_ARCHS=("x86_64" "arm64")
IOS_TRIPLES=("x86_64-apple-ios" "aarch64-apple-ios")
for i in "${!LIBS_ARCHS[@]}"; do
cargo build --target "${IOS_TRIPLES[${i}]}"
done
UNIVERSAL_BINARY=./${TARGETDIR}/ios/universal/${RELDIR}/${STATIC_LIB_NAME}
NEED_LIPO=
# if the universal binary doesnt exist, or if it's older than the static libs,
# we need to run `lipo` again.
if [[ ! -f "${UNIVERSAL_BINARY}" ]]; then
NEED_LIPO=1
elif [[ "$(stat -f "%m" "./${TARGETDIR}/x86_64-apple-ios/${RELDIR}/${STATIC_LIB_NAME}")" -gt "$(stat -f "%m" "${UNIVERSAL_BINARY}")" ]]; then
NEED_LIPO=1
elif [[ "$(stat -f "%m" "./${TARGETDIR}/aarch64-apple-ios/${RELDIR}/${STATIC_LIB_NAME}")" -gt "$(stat -f "%m" "${UNIVERSAL_BINARY}")" ]]; then
NEED_LIPO=1
fi
if [[ "${NEED_LIPO}" = "1" ]]; then
mkdir -p "${TARGETDIR}/ios/universal/${RELDIR}"
lipo -create -output "${UNIVERSAL_BINARY}" \
"${TARGETDIR}/x86_64-apple-ios/${RELDIR}/${STATIC_LIB_NAME}" \
"${TARGETDIR}/aarch64-apple-ios/${RELDIR}/${STATIC_LIB_NAME}"
fi
}
## rust android
build_android() {
build_kotlin
# If ANDROID_NDK_HOME is not set then set it to github actions default
[ -z "$ANDROID_NDK_HOME" ] && export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle
# Update this line accordingly if you are not building *from* darwin-x86_64 or linux-x86_64
export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/`uname | tr '[:upper:]' '[:lower:]'`-x86_64/bin
# Required for 'ring' dependency to cross-compile to Android platform, must be at least 21
export CFLAGS="-D__ANDROID_API__=21"
# IMPORTANT: make sure every target is not a substring of a different one. We check for them with grep later on
BUILD_TARGETS="${BUILD_TARGETS:-aarch64,x86_64,i686}"
mkdir -p bindings/bdk-kotlin/android/src/main/jniLibs/ bindings/bdk-kotlin/android/src/main/jniLibs/arm64-v8a bindings/bdk-kotlin/android/src/main/jniLibs/x86_64 bindings/bdk-kotlin/android/src/main/jniLibs/x86
if echo $BUILD_TARGETS | grep "aarch64"; then
CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER="aarch64-linux-android21-clang" CC="aarch64-linux-android21-clang" cargo build --target=aarch64-linux-android
cp target/aarch64-linux-android/debug/libbdkffi.so bindings/bdk-kotlin/android/src/main/jniLibs/arm64-v8a
fi
if echo $BUILD_TARGETS | grep "x86_64"; then
CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER="x86_64-linux-android21-clang" CC="x86_64-linux-android21-clang" cargo build --target=x86_64-linux-android
cp target/x86_64-linux-android/debug/libbdkffi.so bindings/bdk-kotlin/android/src/main/jniLibs/x86_64
fi
if echo $BUILD_TARGETS | grep "i686"; then
CARGO_TARGET_I686_LINUX_ANDROID_LINKER="i686-linux-android21-clang" CC="i686-linux-android21-clang" cargo build --target=i686-linux-android
cp target/i686-linux-android/debug/libbdkffi.so bindings/bdk-kotlin/android/src/main/jniLibs/x86
fi
# copy sources
cp -R bindings/bdk-kotlin/jvm/src/main/kotlin bindings/bdk-kotlin/android/src/main
# bdk-kotlin aar
(cd bindings/bdk-kotlin && ./gradlew :android:build)
}
OS=$(uname)
if [ "$1" == "-h" ]
then
help
else
build_rust
while [ -n "$1" ]; do # while loop starts
case "$1" in
-a) build_android ;;
-k) build_kotlin ;;
-s) build_swift ;;
-h) help ;;
*) echo "Option $1 not recognized" ;;
esac
shift
done
fi

13
main.c
View File

@@ -1,13 +0,0 @@
#include <stdlib.h>
#include "bdk_ffi.h"
int main (int argc, char const * const argv[])
{
Point_t * a = new_point(84,45);
Point_t * b = new_point(0.0,39.0);
Point_t * m = mid_point(a, b);
print_point(m);
print_point(NULL);
return EXIT_SUCCESS;
}

167
src/bdk.udl Normal file
View File

@@ -0,0 +1,167 @@
namespace bdk {
[Throws=BdkError]
ExtendedKeyInfo generate_extended_key(Network network, MnemonicType mnemonic_type, string? password);
[Throws=BdkError]
ExtendedKeyInfo restore_extended_key(Network network, string mnemonic, string? password);
};
[Error]
enum BdkError {
"InvalidU32Bytes",
"Generic",
"ScriptDoesntHaveAddressForm",
"NoRecipients",
"NoUtxosSelected",
"OutputBelowDustLimit",
"InsufficientFunds",
"BnBTotalTriesExceeded",
"BnBNoExactMatch",
"UnknownUtxo",
"TransactionNotFound",
"TransactionConfirmed",
"IrreplaceableTransaction",
"FeeRateTooLow",
"FeeTooLow",
"FeeRateUnavailable",
"MissingKeyOrigin",
"Key",
"ChecksumMismatch",
"SpendingPolicyRequired",
"InvalidPolicyPathError",
"Signer",
"InvalidNetwork",
"InvalidProgressValue",
"ProgressUpdateError",
"InvalidOutpoint",
"Descriptor",
"AddressValidator",
"Encode",
"Miniscript",
"Bip32",
"Secp256k1",
"Json",
"Hex",
"Psbt",
"PsbtParse",
"Electrum",
"Esplora",
"Sled",
};
enum Network {
"Bitcoin",
"Testnet",
"Signet",
"Regtest",
};
dictionary SledDbConfiguration {
string path;
string tree_name;
};
[Enum]
interface DatabaseConfig {
Memory(string junk);
Sled(SledDbConfiguration config);
};
dictionary TransactionDetails {
u64? fees;
u64 received;
u64 sent;
string id;
};
dictionary Confirmation {
u32 height;
u64 timestamp;
};
[Enum]
interface Transaction {
Unconfirmed(TransactionDetails details);
Confirmed(TransactionDetails details, Confirmation confirmation);
};
interface OfflineWallet {
[Throws=BdkError]
constructor(string descriptor, Network network, DatabaseConfig database_config);
// OfflineWalletOperations
string get_new_address();
string get_last_unused_address();
[Throws=BdkError]
u64 get_balance();
[Throws=BdkError]
void sign([ByRef] PartiallySignedBitcoinTransaction psbt);
[Throws=BdkError]
sequence<Transaction> get_transactions();
};
dictionary ElectrumConfig {
string url;
string? socks5;
u8 retry;
u8? timeout;
u64 stop_gap;
};
dictionary EsploraConfig {
string base_url;
string? proxy;
u64 timeout_read;
u64 timeout_write;
u64 stop_gap;
};
[Enum]
interface BlockchainConfig {
Electrum(ElectrumConfig config);
Esplora(EsploraConfig config);
};
callback interface BdkProgress {
void update(f32 progress, string? message);
};
interface OnlineWallet {
[Throws=BdkError]
constructor(string descriptor, string? change_descriptor, Network network, DatabaseConfig database_config, BlockchainConfig blockchain_config);
// OfflineWalletOperations
string get_new_address();
string get_last_unused_address();
[Throws=BdkError]
u64 get_balance();
[Throws=BdkError]
void sign([ByRef] PartiallySignedBitcoinTransaction psbt);
[Throws=BdkError]
sequence<Transaction> get_transactions();
// OnlineWalletInterface
Network get_network();
[Throws=BdkError]
void sync(BdkProgress progress_update, u32? max_address_param);
[Throws=BdkError]
Transaction broadcast([ByRef] PartiallySignedBitcoinTransaction psbt);
};
interface PartiallySignedBitcoinTransaction {
[Throws=BdkError]
constructor([ByRef] OnlineWallet wallet, string recipient, u64 amount, float? fee_rate);
};
dictionary ExtendedKeyInfo {
string mnemonic;
string xprv;
string fingerprint;
};
enum MnemonicType {
"Words12",
"Words15",
"Words18",
"Words21",
"Words24",
};

View File

@@ -1,47 +1,340 @@
use ::safer_ffi::prelude::*;
use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{Address, Network};
use bdk::blockchain::any::{AnyBlockchain, AnyBlockchainConfig};
use bdk::blockchain::Progress;
use bdk::blockchain::{
electrum::ElectrumBlockchainConfig, esplora::EsploraBlockchainConfig, ConfigurableBlockchain,
};
use bdk::database::any::{AnyDatabase, SledDbConfiguration};
use bdk::database::{AnyDatabaseConfig, ConfigurableDatabase};
use bdk::keys::bip39::{Language, Mnemonic, MnemonicType};
use bdk::keys::{DerivableKey, ExtendedKey, GeneratableKey, GeneratedKey};
use bdk::miniscript::BareCtx;
use bdk::wallet::AddressIndex;
use bdk::{ConfirmationTime, Error, FeeRate, SignOptions, Wallet};
use std::convert::TryFrom;
use std::str::FromStr;
use std::sync::{Mutex, MutexGuard};
/// A `struct` usable from both Rust and C
#[derive_ReprC]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct Point {
x: f64,
y: f64,
uniffi_macros::include_scaffolding!("bdk");
type BdkError = Error;
pub enum DatabaseConfig {
Memory { junk: String },
Sled { config: SledDbConfiguration },
}
/* Export a Rust function to the C world. */
/// Returns the middle point of `[a, b]`.
#[ffi_export]
fn mid_point(a: Option<repr_c::Box<Point>>, b: Option<repr_c::Box<Point>>) -> repr_c::Box<Point> {
let a = a.unwrap();
let b = b.unwrap();
repr_c::Box::new(Point {
x: (a.x + b.x) / 2.,
y: (a.y + b.y) / 2.,
pub struct ElectrumConfig {
pub url: String,
pub socks5: Option<String>,
pub retry: u8,
pub timeout: Option<u8>,
pub stop_gap: u64,
}
pub struct EsploraConfig {
pub base_url: String,
pub proxy: Option<String>,
pub timeout_read: u64,
pub timeout_write: u64,
pub stop_gap: u64,
}
pub enum BlockchainConfig {
Electrum { config: ElectrumConfig },
Esplora { config: EsploraConfig },
}
trait WalletHolder<B> {
fn get_wallet(&self) -> MutexGuard<Wallet<B, AnyDatabase>>;
}
struct OfflineWallet {
wallet: Mutex<Wallet<(), AnyDatabase>>,
}
impl WalletHolder<()> for OfflineWallet {
fn get_wallet(&self) -> MutexGuard<Wallet<(), AnyDatabase>> {
self.wallet.lock().unwrap()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct TransactionDetails {
pub fees: Option<u64>,
pub received: u64,
pub sent: u64,
pub id: String,
}
type Confirmation = ConfirmationTime;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Transaction {
Unconfirmed {
details: TransactionDetails,
},
Confirmed {
details: TransactionDetails,
confirmation: Confirmation,
},
}
impl From<&bdk::TransactionDetails> for TransactionDetails {
fn from(x: &bdk::TransactionDetails) -> TransactionDetails {
TransactionDetails {
fees: x.fee,
id: x.txid.to_string(),
received: x.received,
sent: x.sent,
}
}
}
impl From<&bdk::TransactionDetails> for Transaction {
fn from(x: &bdk::TransactionDetails) -> Transaction {
match x.confirmation_time.clone() {
Some(confirmation) => Transaction::Confirmed {
details: TransactionDetails::from(x),
confirmation,
},
None => Transaction::Unconfirmed {
details: TransactionDetails::from(x),
},
}
}
}
trait OfflineWalletOperations<B>: WalletHolder<B> {
fn get_new_address(&self) -> String {
self.get_wallet()
.get_address(AddressIndex::New)
.unwrap()
.address
.to_string()
}
fn get_last_unused_address(&self) -> String {
self.get_wallet()
.get_address(AddressIndex::LastUnused)
.unwrap()
.address
.to_string()
}
fn get_balance(&self) -> Result<u64, Error> {
self.get_wallet().get_balance()
}
fn sign<'a>(&self, psbt: &'a PartiallySignedBitcoinTransaction) -> Result<(), Error> {
let mut psbt = psbt.internal.lock().unwrap();
let finalized = self.get_wallet().sign(&mut psbt, SignOptions::default())?;
match finalized {
true => Ok(()),
false => Err(BdkError::Generic(format!(
"transaction signing not finalized {:?}",
psbt
))),
}
}
fn get_transactions(&self) -> Result<Vec<Transaction>, Error> {
let transactions = self.get_wallet().list_transactions(true)?;
Ok(transactions.iter().map(Transaction::from).collect())
}
}
impl OfflineWallet {
fn new(
descriptor: String,
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),
};
let database = AnyDatabase::from_config(&any_database_config)?;
let wallet = Mutex::new(Wallet::new_offline(&descriptor, None, network, database)?);
Ok(OfflineWallet { wallet })
}
}
impl OfflineWalletOperations<()> for OfflineWallet {}
struct OnlineWallet {
wallet: Mutex<Wallet<AnyBlockchain, AnyDatabase>>,
}
pub trait BdkProgress: Send + Sync {
fn update(&self, progress: f32, message: Option<String>);
}
struct BdkProgressHolder {
progress_update: Box<dyn BdkProgress>,
}
impl Progress for BdkProgressHolder {
fn update(&self, progress: f32, message: Option<String>) -> Result<(), Error> {
self.progress_update.update(progress, message);
Ok(())
}
}
struct PartiallySignedBitcoinTransaction {
internal: Mutex<PartiallySignedTransaction>,
details: bdk::TransactionDetails,
}
impl PartiallySignedBitcoinTransaction {
fn new(
online_wallet: &OnlineWallet,
recipient: String,
amount: u64,
fee_rate: Option<f32>, // satoshis per vbyte
) -> Result<Self, Error> {
let wallet = online_wallet.get_wallet();
match Address::from_str(&recipient) {
Ok(address) => {
let (psbt, details) = {
let mut builder = wallet.build_tx();
builder.add_recipient(address.script_pubkey(), amount);
if let Some(sat_per_vb) = fee_rate {
builder.fee_rate(FeeRate::from_sat_per_vb(sat_per_vb));
}
builder.finish()?
};
Ok(PartiallySignedBitcoinTransaction {
internal: Mutex::new(psbt),
details,
})
}
Err(..) => Err(BdkError::Generic(
"failed to read wallet address".to_string(),
)),
}
}
}
impl OnlineWallet {
fn new(
descriptor: String,
change_descriptor: Option<String>,
network: Network,
database_config: DatabaseConfig,
blockchain_config: BlockchainConfig,
) -> Result<Self, BdkError> {
let any_database_config = match database_config {
DatabaseConfig::Memory { .. } => AnyDatabaseConfig::Memory(()),
DatabaseConfig::Sled { config } => AnyDatabaseConfig::Sled(config),
};
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(),
})
}
BlockchainConfig::Esplora { config } => {
AnyBlockchainConfig::Esplora(EsploraBlockchainConfig {
base_url: config.base_url,
proxy: config.proxy,
timeout_read: config.timeout_read,
timeout_write: config.timeout_write,
stop_gap: usize::try_from(config.stop_gap).unwrap(),
})
}
};
let database = AnyDatabase::from_config(&any_database_config)?;
let blockchain = AnyBlockchain::from_config(&any_blockchain_config)?;
let wallet = Mutex::new(Wallet::new(
&descriptor,
change_descriptor.to_owned().as_ref(),
network,
database,
blockchain,
)?);
Ok(OnlineWallet { wallet })
}
fn get_network(&self) -> Network {
self.wallet.lock().unwrap().network()
}
fn sync(
&self,
progress_update: Box<dyn BdkProgress>,
max_address_param: Option<u32>,
) -> Result<(), BdkError> {
progress_update.update(21.0, Some("message".to_string()));
self.wallet
.lock()
.unwrap()
.sync(BdkProgressHolder { progress_update }, max_address_param)
}
fn broadcast<'a>(
&self,
psbt: &'a PartiallySignedBitcoinTransaction,
) -> Result<Transaction, Error> {
let tx = psbt.internal.lock().unwrap().clone().extract_tx();
self.get_wallet().broadcast(tx)?;
Ok(Transaction::from(&psbt.details))
}
}
impl WalletHolder<AnyBlockchain> for OnlineWallet {
fn get_wallet(&self) -> MutexGuard<Wallet<AnyBlockchain, AnyDatabase>> {
self.wallet.lock().unwrap()
}
}
impl OfflineWalletOperations<AnyBlockchain> for OnlineWallet {}
pub struct ExtendedKeyInfo {
mnemonic: String,
xprv: String,
fingerprint: String,
}
fn generate_extended_key(
network: Network,
mnemonic_type: MnemonicType,
password: Option<String>,
) -> Result<ExtendedKeyInfo, Error> {
let mnemonic: GeneratedKey<_, BareCtx> =
Mnemonic::generate((mnemonic_type, Language::English)).unwrap();
let mnemonic = mnemonic.into_key();
let xkey: ExtendedKey = (mnemonic.clone(), password).into_extended_key()?;
let xprv = xkey.into_xprv(network).unwrap();
let fingerprint = xprv.fingerprint(&Secp256k1::new());
Ok(ExtendedKeyInfo {
mnemonic: mnemonic.to_string(),
xprv: xprv.to_string(),
fingerprint: fingerprint.to_string(),
})
}
/// Pretty-prints a point using Rust's formatting logic.
#[ffi_export]
fn print_point(point: Option<repr_c::Box<Point>>) {
println!("{:?}", point);
fn restore_extended_key(
network: Network,
mnemonic: String,
password: Option<String>,
) -> Result<ExtendedKeyInfo, Error> {
let mnemonic = Mnemonic::from_phrase(mnemonic.as_ref(), Language::English).unwrap();
let xkey: ExtendedKey = (mnemonic.clone(), password).into_extended_key()?;
let xprv = xkey.into_xprv(network).unwrap();
let fingerprint = xprv.fingerprint(&Secp256k1::new());
Ok(ExtendedKeyInfo {
mnemonic: mnemonic.to_string(),
xprv: xprv.to_string(),
fingerprint: fingerprint.to_string(),
})
}
#[ffi_export]
fn new_point(x: f64, y: f64) -> repr_c::Box<Point> {
repr_c::Box::new(Point { x, y })
}
#[ffi_export]
fn free_point(point: Option<repr_c::Box<Point>>) {
drop(point)
}
/// The following test function is necessary for the header generation.
#[::safer_ffi::cfg_headers]
#[test]
fn generate_headers() -> ::std::io::Result<()> {
::safer_ffi::headers::builder()
.to_file("bdk_ffi.h")?
.generate()
}
uniffi::deps::static_assertions::assert_impl_all!(OfflineWallet: Sync, Send);
uniffi::deps::static_assertions::assert_impl_all!(OnlineWallet: Sync, Send);

44
test.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/usr/bin/env bash
set -eo pipefail
# functions
## help
help()
{
# Display Help
echo "Test bdk-uniffi and related libraries."
echo
echo "Syntax: build [-a|h|k]"
echo "options:"
echo "-a Android connected device tests."
echo "-h Print this Help."
echo "-k Kotlin tests."
echo
}
test_kotlin() {
(cd bindings/bdk-kotlin && ./gradlew :jvm:test -Djna.debug_load=true)
}
test_android() {
(cd bindings/bdk-kotlin && ./gradlew :android:connectedDebugAndroidTest)
}
if [ $1 = "-h" ]
then
help
else
cargo test
# optional tests
while [ -n "$1" ]; do # while loop starts
case "$1" in
-a) test_android ;;
-h) help ;;
-k) test_kotlin ;;
*) echo "Option $1 not recognized" ;;
esac
shift
done
fi

12
uniffi.toml Normal file
View File

@@ -0,0 +1,12 @@
[bindings.kotlin]
package_name = "org.bitcoindevkit"
cdylib_name = "bdkffi"
[bindings.python]
cdylib_name = "bdkffi"
[bindings.ruby]
cdylib_name = "bdkffi"
[bindings.swift]
cdylib_name = "bdkffi"