Remove non-kotlin related files, add bdk-ffi as submodule, update build.sh

This commit is contained in:
Steve Myers 2021-12-13 22:14:16 -08:00
parent 09ce971708
commit 0f42ba7590
No known key found for this signature in database
GPG Key ID: 8105A46B22C2D051
29 changed files with 151 additions and 780 deletions

2
.gitignore vendored
View File

@ -14,3 +14,5 @@ testdb
xcuserdata xcuserdata
.lsp .lsp
.clj-kondo .clj-kondo
.idea
bdk.kt

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "bdk-ffi"]
path = bdk-ffi
url = git@github.com:bitcoindevkit/bdk-ffi.git

View File

@ -1,20 +0,0 @@
[package]
name = "bdk-ffi"
version = "0.1.0"
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", "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"

1
bdk-ffi Submodule

@ -0,0 +1 @@
Subproject commit e4d53b5e4b213e484bf4b76a4bf33884dd68f086

View File

@ -1,7 +0,0 @@
.idea
.gradle
local.properties
build
*.so
*.dylib
bdk.kt

View File

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

154
build.sh
View File

@ -1,107 +1,65 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -eo pipefail set -eo pipefail
# functions echo "Build and test bdk-ffi library for local platform (darwin or linux)"
pushd bdk-ffi
## help cargo fmt
help() cargo build --release
{ cargo test
# 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)
}
OS=$(uname) 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" ] echo "Generate kotlin bindings from bdk.udl to jvm subproject"
then uniffi-bindgen generate src/bdk.udl --no-format --out-dir ../jvm/src/main/kotlin --language kotlin
help
else
build_rust
while [ -n "$1" ]; do # while loop starts ## android
case "$1" in
-a) build_android ;; # If ANDROID_NDK_HOME is not set then set it to github actions default
-k) build_kotlin ;; [ -z "$ANDROID_NDK_HOME" ] && export ANDROID_NDK_HOME=$ANDROID_HOME/ndk-bundle
-h) help ;;
*) echo "Option $1 not recognized" ;; # Update this line accordingly if you are not building *from* darwin-x86_64 or linux-x86_64
esac export PATH=$PATH:$ANDROID_NDK_HOME/toolchains/llvm/prebuilt/`uname | tr '[:upper:]' '[:lower:]'`-x86_64/bin
shift
done # 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 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

View File

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

View File

@ -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<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,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<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 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<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(),
})
}
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(),
})
}
uniffi::deps::static_assertions::assert_impl_all!(OfflineWallet: Sync, Send);
uniffi::deps::static_assertions::assert_impl_all!(OnlineWallet: Sync, Send);

44
test.sh
View File

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

View File

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