diff --git a/.gitignore b/.gitignore index 757510a..277a164 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,5 @@ testdb xcuserdata .lsp .clj-kondo +.idea +bdk.kt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..9c7f2ac --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "bdk-ffi"] + path = bdk-ffi + url = git@github.com:bitcoindevkit/bdk-ffi.git diff --git a/Cargo.toml b/Cargo.toml deleted file mode 100644 index bb01c73..0000000 --- a/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "bdk-ffi" -version = "0.1.0" -authors = ["Steve Myers ", "Sudarsan Balaji "] -edition = "2018" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[lib] -crate-type = ["staticlib", "cdylib"] -name = "bdkffi" - -[dependencies] -bdk = { version = "0.13", features = ["all-keys", "use-esplora-ureq"] } -uniffi_macros = "0.14.1" -uniffi = "0.14.1" -thiserror = "1.0" -anyhow = "=1.0.45" # remove after upgrading to next version of uniffi - -[build-dependencies] -uniffi_build = "0.14.1" diff --git a/bindings/bdk-kotlin/android/build.gradle b/android/build.gradle similarity index 100% rename from bindings/bdk-kotlin/android/build.gradle rename to android/build.gradle diff --git a/bindings/bdk-kotlin/android/proguard-rules.pro b/android/proguard-rules.pro similarity index 100% rename from bindings/bdk-kotlin/android/proguard-rules.pro rename to android/proguard-rules.pro diff --git a/bindings/bdk-kotlin/android/src/androidTest/assets/logback.xml b/android/src/androidTest/assets/logback.xml similarity index 100% rename from bindings/bdk-kotlin/android/src/androidTest/assets/logback.xml rename to android/src/androidTest/assets/logback.xml diff --git a/bindings/bdk-kotlin/android/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt b/android/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt similarity index 100% rename from bindings/bdk-kotlin/android/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt rename to android/src/androidTest/kotlin/org/bitcoindevkit/AndroidLibTest.kt diff --git a/bindings/bdk-kotlin/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml similarity index 100% rename from bindings/bdk-kotlin/android/src/main/AndroidManifest.xml rename to android/src/main/AndroidManifest.xml diff --git a/bdk-ffi b/bdk-ffi new file mode 160000 index 0000000..e4d53b5 --- /dev/null +++ b/bdk-ffi @@ -0,0 +1 @@ +Subproject commit e4d53b5e4b213e484bf4b76a4bf33884dd68f086 diff --git a/bindings/bdk-kotlin/.gitignore b/bindings/bdk-kotlin/.gitignore deleted file mode 100644 index f9467fc..0000000 --- a/bindings/bdk-kotlin/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -.idea -.gradle -local.properties -build -*.so -*.dylib -bdk.kt diff --git a/bindings/bdk-kotlin/build.gradle b/build.gradle similarity index 100% rename from bindings/bdk-kotlin/build.gradle rename to build.gradle diff --git a/build.rs b/build.rs deleted file mode 100644 index 153077f..0000000 --- a/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - uniffi_build::generate_scaffolding("src/bdk.udl").unwrap(); -} diff --git a/build.sh b/build.sh index 7c4b99a..4ab1677 100755 --- a/build.sh +++ b/build.sh @@ -1,107 +1,65 @@ #!/usr/bin/env bash set -eo pipefail -# functions +echo "Build and test bdk-ffi library for local platform (darwin or linux)" +pushd bdk-ffi -## 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 -} - -## rust -build_rust() { - echo "Build Rust library" - cargo fmt - cargo build --release - 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/release/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/release/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 -} - -## 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 --release --target=aarch64-linux-android - cp target/aarch64-linux-android/release/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 --release --target=x86_64-linux-android - cp target/x86_64-linux-android/release/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 --release --target=i686-linux-android - cp target/i686-linux-android/release/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) -} +cargo fmt +cargo build --release +cargo test OS=$(uname) +echo -n "Copy " +case $OS in + "Darwin") + echo -n "darwin " + mkdir -p ../jvm/src/main/resources/darwin-x86-64 + cp target/release/libbdkffi.dylib ../jvm/src/main/resources/darwin-x86-64 + ;; + "Linux") + echo -n "linux " + mkdir -p ../jvm/src/main/resources/linux-x86-64 + cp target/release/libbdkffi.so ../jvm/src/main/resources/linux-x86-64 + ;; +esac +echo "libs to jvm subproject" -if [ "$1" == "-h" ] -then - help -else - build_rust +echo "Generate kotlin bindings from bdk.udl to jvm subproject" +uniffi-bindgen generate src/bdk.udl --no-format --out-dir ../jvm/src/main/kotlin --language kotlin - while [ -n "$1" ]; do # while loop starts - case "$1" in - -a) build_android ;; - -k) build_kotlin ;; - -h) help ;; - *) echo "Option $1 not recognized" ;; - esac - shift - done +## android + +# 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 ../android/src/main/jniLibs/arm64-v8a ../android/src/main/jniLibs/x86_64 ../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 --release --target=aarch64-linux-android + cp target/aarch64-linux-android/release/libbdkffi.so ../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 --release --target=x86_64-linux-android + cp target/x86_64-linux-android/release/libbdkffi.so ../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 --release --target=i686-linux-android + cp target/i686-linux-android/release/libbdkffi.so ../android/src/main/jniLibs/x86 +fi + +popd + +# copy bdk-ffi kotlin binding sources from jvm to android +cp -R jvm/src/main/kotlin android/src/main + +# bdk-kotlin build jar and aar subprojects +./gradlew build diff --git a/bindings/bdk-kotlin/demo/build.gradle b/demo/build.gradle similarity index 100% rename from bindings/bdk-kotlin/demo/build.gradle rename to demo/build.gradle diff --git a/bindings/bdk-kotlin/demo/gradle/wrapper/gradle-wrapper.jar b/demo/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from bindings/bdk-kotlin/demo/gradle/wrapper/gradle-wrapper.jar rename to demo/gradle/wrapper/gradle-wrapper.jar diff --git a/bindings/bdk-kotlin/demo/gradle/wrapper/gradle-wrapper.properties b/demo/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from bindings/bdk-kotlin/demo/gradle/wrapper/gradle-wrapper.properties rename to demo/gradle/wrapper/gradle-wrapper.properties diff --git a/bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt b/demo/src/main/kotlin/Main.kt similarity index 100% rename from bindings/bdk-kotlin/demo/src/main/kotlin/Main.kt rename to demo/src/main/kotlin/Main.kt diff --git a/bindings/bdk-kotlin/gradle.properties b/gradle.properties similarity index 100% rename from bindings/bdk-kotlin/gradle.properties rename to gradle.properties diff --git a/bindings/bdk-kotlin/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from bindings/bdk-kotlin/gradle/wrapper/gradle-wrapper.jar rename to gradle/wrapper/gradle-wrapper.jar diff --git a/bindings/bdk-kotlin/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from bindings/bdk-kotlin/gradle/wrapper/gradle-wrapper.properties rename to gradle/wrapper/gradle-wrapper.properties diff --git a/bindings/bdk-kotlin/gradlew b/gradlew similarity index 100% rename from bindings/bdk-kotlin/gradlew rename to gradlew diff --git a/bindings/bdk-kotlin/gradlew.bat b/gradlew.bat similarity index 96% rename from bindings/bdk-kotlin/gradlew.bat rename to gradlew.bat index ac1b06f..107acd3 100644 --- a/bindings/bdk-kotlin/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,89 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/bindings/bdk-kotlin/jvm/build.gradle b/jvm/build.gradle similarity index 100% rename from bindings/bdk-kotlin/jvm/build.gradle rename to jvm/build.gradle diff --git a/bindings/bdk-kotlin/jvm/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt b/jvm/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt similarity index 100% rename from bindings/bdk-kotlin/jvm/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt rename to jvm/src/test/kotlin/org/bitcoindevkit/JvmLibTest.kt diff --git a/bindings/bdk-kotlin/settings.gradle b/settings.gradle similarity index 100% rename from bindings/bdk-kotlin/settings.gradle rename to settings.gradle diff --git a/src/bdk.udl b/src/bdk.udl deleted file mode 100644 index 0597368..0000000 --- a/src/bdk.udl +++ /dev/null @@ -1,167 +0,0 @@ -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 txid; -}; - -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 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 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", -}; diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index d88d7d6..0000000 --- a/src/lib.rs +++ /dev/null @@ -1,340 +0,0 @@ -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}; - -uniffi_macros::include_scaffolding!("bdk"); - -type BdkError = Error; - -pub enum DatabaseConfig { - Memory { junk: String }, - Sled { config: SledDbConfiguration }, -} - -pub struct ElectrumConfig { - pub url: String, - pub socks5: Option, - pub retry: u8, - pub timeout: Option, - pub stop_gap: u64, -} - -pub struct EsploraConfig { - pub base_url: String, - pub proxy: Option, - pub timeout_read: u64, - pub timeout_write: u64, - pub stop_gap: u64, -} - -pub enum BlockchainConfig { - Electrum { config: ElectrumConfig }, - Esplora { config: EsploraConfig }, -} - -trait WalletHolder { - fn get_wallet(&self) -> MutexGuard>; -} - -struct OfflineWallet { - wallet: Mutex>, -} - -impl WalletHolder<()> for OfflineWallet { - fn get_wallet(&self) -> MutexGuard> { - self.wallet.lock().unwrap() - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Default)] -pub struct TransactionDetails { - pub fees: Option, - pub received: u64, - pub sent: u64, - pub txid: 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, - txid: 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: WalletHolder { - 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 { - 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, 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 { - 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>, -} - -pub trait BdkProgress: Send + Sync { - fn update(&self, progress: f32, message: Option); -} - -struct BdkProgressHolder { - progress_update: Box, -} - -impl Progress for BdkProgressHolder { - fn update(&self, progress: f32, message: Option) -> Result<(), Error> { - self.progress_update.update(progress, message); - Ok(()) - } -} - -struct PartiallySignedBitcoinTransaction { - internal: Mutex, - details: bdk::TransactionDetails, -} - -impl PartiallySignedBitcoinTransaction { - fn new( - online_wallet: &OnlineWallet, - recipient: String, - amount: u64, - fee_rate: Option, // satoshis per vbyte - ) -> Result { - 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, - network: Network, - database_config: DatabaseConfig, - blockchain_config: BlockchainConfig, - ) -> Result { - 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, - max_address_param: Option, - ) -> 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 { - let tx = psbt.internal.lock().unwrap().clone().extract_tx(); - self.get_wallet().broadcast(tx)?; - Ok(Transaction::from(&psbt.details)) - } -} - -impl WalletHolder for OnlineWallet { - fn get_wallet(&self) -> MutexGuard> { - self.wallet.lock().unwrap() - } -} - -impl OfflineWalletOperations for OnlineWallet {} - -pub struct ExtendedKeyInfo { - mnemonic: String, - xprv: String, - fingerprint: String, -} - -fn generate_extended_key( - network: Network, - mnemonic_type: MnemonicType, - password: Option, -) -> Result { - 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(), - }) -} - -fn restore_extended_key( - network: Network, - mnemonic: String, - password: Option, -) -> Result { - 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(), - }) -} - -uniffi::deps::static_assertions::assert_impl_all!(OfflineWallet: Sync, Send); -uniffi::deps::static_assertions::assert_impl_all!(OnlineWallet: Sync, Send); diff --git a/test.sh b/test.sh deleted file mode 100755 index 288947c..0000000 --- a/test.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/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 diff --git a/uniffi.toml b/uniffi.toml deleted file mode 100644 index 767e032..0000000 --- a/uniffi.toml +++ /dev/null @@ -1,12 +0,0 @@ -[bindings.kotlin] -package_name = "org.bitcoindevkit" -cdylib_name = "bdkffi" - -[bindings.python] -cdylib_name = "bdkffi" - -[bindings.ruby] -cdylib_name = "bdkffi" - -[bindings.swift] -cdylib_name = "bdkffi"