Return FfiResult errors as FfiError enum short values

This commit is contained in:
Steve Myers 2021-07-03 20:40:08 -07:00
parent 8443265142
commit adadcbc982
14 changed files with 281 additions and 144 deletions

View File

@ -1,6 +1,7 @@
package org.bitcoindevkit.bdk
enum class JnaError {
enum class FfiError {
None,
InvalidU32Bytes,
Generic,
ScriptDoesntHaveAddressForm,

View File

@ -0,0 +1,14 @@
package org.bitcoindevkit.bdk
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class FfiException(val err: FfiError) : Exception() {
private val log: Logger = LoggerFactory.getLogger(FfiException::class.java)
init {
log.error("JnaError: [{}] {}",err.ordinal, err.name)
}
internal constructor(err: Short) : this(FfiError.values()[err.toInt()])
}

View File

@ -1,3 +0,0 @@
package org.bitcoindevkit.bdk
class JnaException internal constructor(val err: JnaError) : Exception()

View File

@ -8,7 +8,7 @@ interface LibJna : Library {
//
// char * ok;
//
// char * err;
// FfiError_t err;
//
//} FfiResult_char_ptr_t;
open class FfiResult_char_ptr_t : Structure() {
@ -19,7 +19,7 @@ interface LibJna : Library {
var ok: String = ""
@JvmField
var err: String = ""
var err: Short = 0
override fun getFieldOrder() = listOf("ok", "err")
}
@ -30,7 +30,7 @@ interface LibJna : Library {
// typedef struct {
//
// char * err;
// FfiError_t err;
//
//} FfiResultVoid_t;
open class FfiResultVoid_t : Structure() {
@ -38,7 +38,7 @@ interface LibJna : Library {
class ByReference : FfiResultVoid_t(), Structure.ByReference
@JvmField
var err: String = ""
var err: Short = 0
override fun getFieldOrder() = listOf("err")
}
@ -89,7 +89,7 @@ interface LibJna : Library {
//
// OpaqueWallet_t * ok;
//
// char * err;
// FfiError_t err;
//
// } FfiResult_OpaqueWallet_ptr_t;
open class FfiResult_OpaqueWallet_ptr_t : Structure() {
@ -100,7 +100,7 @@ interface LibJna : Library {
var ok: OpaqueWallet_t = OpaqueWallet_t()
@JvmField
var err: String = ""
var err: Short = 0
override fun getFieldOrder() = listOf("ok", "err")
}
@ -215,7 +215,7 @@ interface LibJna : Library {
//
// Vec_LocalUtxo_t ok;
//
// char * err;
// FfiError_t err;
//
// } FfiResult_Vec_LocalUtxo_t;
open class FfiResultVec_LocalUtxo_t : Structure() {
@ -227,7 +227,7 @@ interface LibJna : Library {
var ok: Vec_LocalUtxo_t = Vec_LocalUtxo_t()
@JvmField
var err: String = ""
var err: Short = 0
override fun getFieldOrder() = listOf("ok", "err")
}

View File

@ -1,7 +1,6 @@
package org.bitcoindevkit.bdk.types
import org.bitcoindevkit.bdk.JnaError
import org.bitcoindevkit.bdk.JnaException
import org.bitcoindevkit.bdk.FfiException
import org.bitcoindevkit.bdk.LibBase
import org.bitcoindevkit.bdk.LibJna
import org.slf4j.Logger
@ -16,9 +15,8 @@ class StringResult constructor(private val ffiResultCharPtrT: LibJna.FfiResult_c
val err = ffiResultCharPtrT.err
val ok = ffiResultCharPtrT.ok
when {
err.isNotEmpty() -> {
log.error("JnaError: $err")
throw JnaException(JnaError.valueOf(err))
err > 0 -> {
throw FfiException(err)
}
else -> {
return ok

View File

@ -1,7 +1,6 @@
package org.bitcoindevkit.bdk.types
import org.bitcoindevkit.bdk.JnaError
import org.bitcoindevkit.bdk.JnaException
import org.bitcoindevkit.bdk.FfiException
import org.bitcoindevkit.bdk.LibBase
import org.bitcoindevkit.bdk.LibJna
import org.slf4j.Logger
@ -16,9 +15,8 @@ class VoidResult constructor(private val ffiResultVoidT: LibJna.FfiResultVoid_t.
val err = ffiResultVoidT.err
when {
err.isNotEmpty() -> {
log.error("JnaError: $err")
throw JnaException(JnaError.valueOf(err))
err > 0 -> {
throw FfiException(err)
}
else -> {
return

View File

@ -1,7 +1,6 @@
package org.bitcoindevkit.bdk.wallet
import org.bitcoindevkit.bdk.JnaError
import org.bitcoindevkit.bdk.JnaException
import org.bitcoindevkit.bdk.FfiException
import org.bitcoindevkit.bdk.LibBase
import org.bitcoindevkit.bdk.LibJna
import org.slf4j.Logger
@ -16,9 +15,8 @@ class VecLocalUtxoResult(private val ffiResultVecLocalUtxoT: LibJna.FfiResultVec
val err = ffiResultVecLocalUtxoT.err
val ok = ffiResultVecLocalUtxoT.ok
when {
err .isNotEmpty() -> {
log.error("JnaError: $err")
throw JnaException(JnaError.valueOf(err))
err > 0 -> {
throw FfiException(err)
}
else -> {
val first = ok.ptr!!

View File

@ -1,7 +1,6 @@
package org.bitcoindevkit.bdk.wallet
import org.bitcoindevkit.bdk.JnaError
import org.bitcoindevkit.bdk.JnaException
import org.bitcoindevkit.bdk.FfiException
import org.bitcoindevkit.bdk.LibBase
import org.bitcoindevkit.bdk.LibJna
import org.slf4j.Logger
@ -16,9 +15,8 @@ class WalletResult constructor(private val ffiResultOpaqueWalletPtrT: LibJna.Ffi
val err = ffiResultOpaqueWalletPtrT.err
val ok = ffiResultOpaqueWalletPtrT.ok
when {
err.isNotEmpty() -> {
log.error("JnaError: $err")
throw JnaException(JnaError.valueOf(err))
err > 0 -> {
throw FfiException(err)
}
else -> {
return ok

View File

@ -31,10 +31,10 @@ abstract class LibTest : LibBase() {
@Test
fun walletResultError() {
val jnaException = assertThrows(JnaException::class.java) {
val jnaException = assertThrows(FfiException::class.java) {
Wallet("bad", "bad", blockchainConfig, databaseConfig)
}
assertEquals(jnaException.err, JnaError.Descriptor)
assertEquals(jnaException.err, FfiError.Descriptor)
}
// @Test

View File

@ -14,8 +14,136 @@
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
/** \remark Has the same ABI as `uint16_t` **/
#ifdef DOXYGEN
typedef enum FfiError
#else
typedef uint16_t FfiError_t; enum
#endif
{
/** . */
FFI_ERROR_NONE,
/** . */
FFI_ERROR_INVALID_U32_BYTES,
/** . */
FFI_ERROR_GENERIC,
/** . */
FFI_ERROR_SCRIPT_DOESNT_HAVE_ADDRESS_FORM,
/** . */
FFI_ERROR_SINGLE_RECIPIENT_MULTIPLE_OUTPUTS,
/** . */
FFI_ERROR_SINGLE_RECIPIENT_NO_INPUTS,
/** . */
FFI_ERROR_NO_RECIPIENTS,
/** . */
FFI_ERROR_NO_UTXOS_SELECTED,
/** . */
FFI_ERROR_OUTPUT_BELOW_DUST_LIMIT,
/** . */
FFI_ERROR_INSUFFICIENT_FUNDS,
/** . */
FFI_ERROR_BN_B_TOTAL_TRIES_EXCEEDED,
/** . */
FFI_ERROR_BN_B_NO_EXACT_MATCH,
/** . */
FFI_ERROR_UNKNOWN_UTXO,
/** . */
FFI_ERROR_TRANSACTION_NOT_FOUND,
/** . */
FFI_ERROR_TRANSACTION_CONFIRMED,
/** . */
FFI_ERROR_IRREPLACEABLE_TRANSACTION,
/** . */
FFI_ERROR_FEE_RATE_TOO_LOW,
/** . */
FFI_ERROR_FEE_TOO_LOW,
/** . */
FFI_ERROR_MISSING_KEY_ORIGIN,
/** . */
FFI_ERROR_KEY,
/** . */
FFI_ERROR_CHECKSUM_MISMATCH,
/** . */
FFI_ERROR_SPENDING_POLICY_REQUIRED,
/** . */
FFI_ERROR_INVALID_POLICY_PATH_ERROR,
/** . */
FFI_ERROR_SIGNER,
/** . */
FFI_ERROR_INVALID_PROGRESS_VALUE,
/** . */
FFI_ERROR_PROGRESS_UPDATE_ERROR,
/** . */
FFI_ERROR_INVALID_OUTPOINT,
/** . */
FFI_ERROR_DESCRIPTOR,
/** . */
FFI_ERROR_ADDRESS_VALIDATOR,
/** . */
FFI_ERROR_ENCODE,
/** . */
FFI_ERROR_MINISCRIPT,
/** . */
FFI_ERROR_BIP32,
/** . */
FFI_ERROR_SECP256K1,
/** . */
FFI_ERROR_JSON,
/** . */
FFI_ERROR_HEX,
/** . */
FFI_ERROR_PSBT,
/** . */
FFI_ERROR_ELECTRUM,
/** . */
FFI_ERROR_SLED,
}
#ifdef DOXYGEN
FfiError_t
#endif
;
typedef struct {
char * ok;
FfiError_t err;
} FfiResult_char_ptr_t;
void free_string_result (
FfiResult_char_ptr_t string_result);
typedef struct {
FfiError_t err;
} FfiResultVoid_t;
void free_void_result (
FfiResultVoid_t void_result);
/** \brief
* Free a Rust-allocated string
*/
void free_string (
char * string);
typedef struct BlockchainConfig BlockchainConfig_t;
BlockchainConfig_t * new_electrum_config (
char const * url,
char const * socks5,
int16_t retry,
int16_t timeout);
void free_blockchain_config (
BlockchainConfig_t * blockchain_config);
typedef struct DatabaseConfig DatabaseConfig_t;
typedef struct OpaqueWallet OpaqueWallet_t;
@ -24,7 +152,7 @@ typedef struct {
OpaqueWallet_t * ok;
char * err;
FfiError_t err;
} FfiResult_OpaqueWallet_ptr_t;
@ -37,30 +165,12 @@ FfiResult_OpaqueWallet_ptr_t new_wallet_result (
void free_wallet_result (
FfiResult_OpaqueWallet_ptr_t wallet_result);
typedef struct {
char * err;
} FfiResultVoid_t;
FfiResultVoid_t sync_wallet (
OpaqueWallet_t const * opaque_wallet);
typedef struct {
char * ok;
char * err;
} FfiResult_char_ptr_t;
FfiResult_char_ptr_t new_address (
OpaqueWallet_t const * opaque_wallet);
#include <stddef.h>
#include <stdint.h>
typedef struct {
char * txid;
@ -104,7 +214,7 @@ typedef struct {
Vec_LocalUtxo_t ok;
char * err;
FfiError_t err;
} FfiResult_Vec_LocalUtxo_t;
@ -114,15 +224,6 @@ FfiResult_Vec_LocalUtxo_t list_unspent (
void free_unspent_result (
FfiResult_Vec_LocalUtxo_t unspent_result);
BlockchainConfig_t * new_electrum_config (
char const * url,
char const * socks5,
int16_t retry,
int16_t timeout);
void free_blockchain_config (
BlockchainConfig_t * blockchain_config);
DatabaseConfig_t * new_memory_config (void);
DatabaseConfig_t * new_sled_config (
@ -132,18 +233,6 @@ DatabaseConfig_t * new_sled_config (
void free_database_config (
DatabaseConfig_t * database_config);
void free_string_result (
FfiResult_char_ptr_t string_result);
void free_void_result (
FfiResultVoid_t void_result);
/** \brief
* Free a Rust-allocated string
*/
void free_string (
char * string);
#ifdef __cplusplus
} /* extern "C" */

View File

@ -14,16 +14,11 @@ int main (int argc, char const * const argv[])
// new wallet with bad descriptor
FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result("bad","bad",bc_config,db_config);
assert(strlen(wallet_result.err) > 0);
assert(wallet_result.err == FFI_ERROR_DESCRIPTOR);
assert(wallet_result.ok == NULL);
free_blockchain_config(bc_config);
free_database_config(db_config);
char *wallet_err = wallet_result.err;
assert(wallet_err != NULL);
assert( 0 == strcmp(wallet_err,"Descriptor") );
// printf("wallet err: %s\n", wallet_err);
free_database_config(db_config);
free_wallet_result(wallet_result);
}
@ -38,8 +33,8 @@ int main (int argc, char const * const argv[])
// new wallet
FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,bc_config,db_config);
// printf("wallet_result.err = %ld\n", strlen(wallet_result.err));
assert(strlen(wallet_result.err) == 0);
// printf("wallet_result.err = %d\n", wallet_result.err));
assert(wallet_result.err == FFI_ERROR_NONE);
assert(wallet_result.ok != NULL);
free_blockchain_config(bc_config);
@ -49,20 +44,20 @@ int main (int argc, char const * const argv[])
// sync wallet
FfiResultVoid_t sync_result = sync_wallet(wallet);
assert(strlen(sync_result.err) == 0);
assert(sync_result.err == FFI_ERROR_NONE);
free_void_result(sync_result);
// new address
FfiResult_char_ptr_t address1_result = new_address(wallet);
assert(address1_result.ok != NULL);
assert(strlen(address1_result.err) == 0);
assert(address1_result.err == FFI_ERROR_NONE);
// printf("address1 = %s\n", *address1_result.ok);
assert( 0 == strcmp(address1_result.ok,"tb1qgkhp034fyxeta00h0nne9tzfm0vsxq4prduzxp"));
free_string_result(address1_result);
FfiResult_char_ptr_t address2_result = new_address(wallet);
assert(address2_result.ok != NULL);
assert(strlen(address2_result.err) == 0);
assert(address2_result.err == FFI_ERROR_NONE);
// printf("address2 = %s\n", *address2_result.ok);
assert( 0 == strcmp(address2_result.ok,"tb1qd6u9q327sru2ljvwzdtfrdg36sapax7udz97wf"));
free_string_result(address2_result);
@ -87,7 +82,7 @@ int main (int argc, char const * const argv[])
// new wallet
FfiResult_OpaqueWallet_ptr_t wallet_result = new_wallet_result(desc,change,bc_config,db_config);
assert(strlen(wallet_result.err) == 0);
assert(wallet_result.err == FFI_ERROR_NONE);
assert(wallet_result.ok != NULL);
free_blockchain_config(bc_config);
@ -97,13 +92,13 @@ int main (int argc, char const * const argv[])
// sync wallet
FfiResultVoid_t sync_result = sync_wallet(wallet);
assert(strlen(sync_result.err) == 0);
assert(sync_result.err == FFI_ERROR_NONE);
free_void_result(sync_result);
// list unspent
FfiResult_Vec_LocalUtxo_t unspent_result = list_unspent(wallet);
assert(unspent_result.ok.len == 7);
assert(strlen(unspent_result.err) == 0);
assert(unspent_result.err == FFI_ERROR_NONE);
LocalUtxo_t * unspent_ptr = unspent_result.ok.ptr;
for (int i = 0; i < unspent_result.ok.len; i++) {

View File

@ -1,46 +1,94 @@
use ::safer_ffi::prelude::*;
use bdk::Error;
pub fn get_name(error: &bdk::Error) -> String {
match error {
Error::InvalidU32Bytes(_) => "InvalidU32Bytes",
Error::Generic(_) => "Generic",
Error::ScriptDoesntHaveAddressForm => "ScriptDoesntHaveAddressForm",
Error::SingleRecipientMultipleOutputs => "SingleRecipientMultipleOutputs",
Error::SingleRecipientNoInputs => "SingleRecipientNoInputs",
Error::NoRecipients => "NoRecipients",
Error::NoUtxosSelected => "NoUtxosSelected",
Error::OutputBelowDustLimit(_) => "OutputBelowDustLimit",
Error::InsufficientFunds { .. } => "InsufficientFunds",
Error::BnBTotalTriesExceeded => "BnBTotalTriesExceeded",
Error::BnBNoExactMatch => "BnBNoExactMatch",
Error::UnknownUtxo => "UnknownUtxo",
Error::TransactionNotFound => "TransactionNotFound",
Error::TransactionConfirmed => "TransactionConfirmed",
Error::IrreplaceableTransaction => "IrreplaceableTransaction",
Error::FeeRateTooLow { .. } => "FeeRateTooLow",
Error::FeeTooLow { .. } => "FeeTooLow",
Error::MissingKeyOrigin(_) => "MissingKeyOrigin",
Error::Key(_) => "Key",
Error::ChecksumMismatch => "ChecksumMismatch",
Error::SpendingPolicyRequired(_) => "SpendingPolicyRequired",
Error::InvalidPolicyPathError(_) => "InvalidPolicyPathError",
Error::Signer(_) => "Signer",
Error::InvalidProgressValue(_) => "InvalidProgressValue",
Error::ProgressUpdateError => "ProgressUpdateError",
Error::InvalidOutpoint(_) => "InvalidOutpoint",
Error::Descriptor(_) => "Descriptor",
Error::AddressValidator(_) => "AddressValidator",
Error::Encode(_) => "Encode",
Error::Miniscript(_) => "Miniscript",
Error::Bip32(_) => "Bip32",
Error::Secp256k1(_) => "Secp256k1",
Error::Json(_) => "Json",
Error::Hex(_) => "Hex",
Error::Psbt(_) => "Psbt",
Error::Electrum(_) => "Electrum",
// Error::Esplora(_) => "Esplora",
// Error::CompactFilters(_) => "CompactFilters",
Error::Sled(_) => "Sled",
}
.to_string()
#[derive_ReprC]
#[repr(u16)]
#[derive(Debug)]
pub enum FfiError {
None,
InvalidU32Bytes,
Generic,
ScriptDoesntHaveAddressForm,
SingleRecipientMultipleOutputs,
SingleRecipientNoInputs,
NoRecipients,
NoUtxosSelected,
OutputBelowDustLimit,
InsufficientFunds,
BnBTotalTriesExceeded,
BnBNoExactMatch,
UnknownUtxo,
TransactionNotFound,
TransactionConfirmed,
IrreplaceableTransaction,
FeeRateTooLow,
FeeTooLow,
MissingKeyOrigin,
Key,
ChecksumMismatch,
SpendingPolicyRequired,
InvalidPolicyPathError,
Signer,
InvalidProgressValue,
ProgressUpdateError,
InvalidOutpoint,
Descriptor,
AddressValidator,
Encode,
Miniscript,
Bip32,
Secp256k1,
Json,
Hex,
Psbt,
Electrum,
// Esplora,
// CompactFilters,
Sled,
}
impl From<&bdk::Error> for FfiError {
fn from(error: &bdk::Error) -> Self {
match error {
Error::InvalidU32Bytes(_) => FfiError::InvalidU32Bytes,
Error::Generic(_) => FfiError::Generic,
Error::ScriptDoesntHaveAddressForm => FfiError::ScriptDoesntHaveAddressForm,
Error::SingleRecipientMultipleOutputs => FfiError::SingleRecipientMultipleOutputs,
Error::SingleRecipientNoInputs => FfiError::SingleRecipientNoInputs,
Error::NoRecipients => FfiError::NoRecipients,
Error::NoUtxosSelected => FfiError::NoUtxosSelected,
Error::OutputBelowDustLimit(_) => FfiError::OutputBelowDustLimit,
Error::InsufficientFunds { .. } => FfiError::InsufficientFunds,
Error::BnBTotalTriesExceeded => FfiError::BnBTotalTriesExceeded,
Error::BnBNoExactMatch => FfiError::BnBNoExactMatch,
Error::UnknownUtxo => FfiError::UnknownUtxo,
Error::TransactionNotFound => FfiError::TransactionNotFound,
Error::TransactionConfirmed => FfiError::TransactionConfirmed,
Error::IrreplaceableTransaction => FfiError::IrreplaceableTransaction,
Error::FeeRateTooLow { .. } => FfiError::FeeRateTooLow,
Error::FeeTooLow { .. } => FfiError::FeeTooLow,
Error::MissingKeyOrigin(_) => FfiError::MissingKeyOrigin,
Error::Key(_) => FfiError::Key,
Error::ChecksumMismatch => FfiError::ChecksumMismatch,
Error::SpendingPolicyRequired(_) => FfiError::SpendingPolicyRequired,
Error::InvalidPolicyPathError(_) => FfiError::InvalidPolicyPathError,
Error::Signer(_) => FfiError::Signer,
Error::InvalidProgressValue(_) => FfiError::InvalidProgressValue,
Error::ProgressUpdateError => FfiError::ProgressUpdateError,
Error::InvalidOutpoint(_) => FfiError::InvalidOutpoint,
Error::Descriptor(_) => FfiError::Descriptor,
Error::AddressValidator(_) => FfiError::AddressValidator,
Error::Encode(_) => FfiError::Encode,
Error::Miniscript(_) => FfiError::Miniscript,
Error::Bip32(_) => FfiError::Bip32,
Error::Secp256k1(_) => FfiError::Secp256k1,
Error::Json(_) => FfiError::Json,
Error::Hex(_) => FfiError::Hex,
Error::Psbt(_) => FfiError::Psbt,
Error::Electrum(_) => FfiError::Electrum,
// Error::Esplora(_) => JniError::Esplora,
// Error::CompactFilters(_) => JniError::CompactFilters,
Error::Sled(_) => FfiError::Sled,
}
}
}

View File

@ -1,3 +1,4 @@
use crate::error::FfiError;
use ::safer_ffi::prelude::*;
use safer_ffi::char_p::char_p_boxed;
@ -6,14 +7,14 @@ use safer_ffi::char_p::char_p_boxed;
#[derive(Debug)]
pub struct FfiResult<T> {
pub ok: T,
pub err: char_p_boxed,
pub err: FfiError,
}
#[derive_ReprC]
#[repr(C)]
#[derive(Debug)]
pub struct FfiResultVoid {
pub err: char_p_boxed,
pub err: FfiError,
}
#[ffi_export]

View File

@ -12,7 +12,7 @@ use safer_ffi::char_p::{char_p_boxed, char_p_ref};
use blockchain::BlockchainConfig;
use database::DatabaseConfig;
use crate::error::get_name;
use crate::error::FfiError;
use crate::types::{FfiResult, FfiResultVoid};
use std::ffi::CString;
@ -43,11 +43,11 @@ fn new_wallet_result(
match wallet_result {
Ok(w) => FfiResult {
ok: Some(Box::new(OpaqueWallet { raw: w })),
err: char_p_boxed::from(CString::default()),
err: FfiError::None,
},
Err(e) => FfiResult {
ok: None,
err: char_p_boxed::try_from(get_name(&e)).unwrap(),
err: FfiError::from(&e),
},
}
}
@ -81,10 +81,10 @@ fn sync_wallet(opaque_wallet: &OpaqueWallet) -> FfiResultVoid {
let int_result = opaque_wallet.raw.sync(log_progress(), Some(100));
match int_result {
Ok(_v) => FfiResultVoid {
err: char_p_boxed::from(CString::default()),
err: FfiError::None,
},
Err(e) => FfiResultVoid {
err: char_p_boxed::try_from(get_name(&e)).unwrap(),
err: FfiError::from(&e),
},
}
}
@ -96,11 +96,11 @@ fn new_address(opaque_wallet: &OpaqueWallet) -> FfiResult<char_p_boxed> {
match string_result {
Ok(a) => FfiResult {
ok: char_p_boxed::try_from(a).unwrap(),
err: char_p_boxed::from(CString::default()),
err: FfiError::None,
},
Err(e) => FfiResult {
ok: char_p_boxed::from(CString::default()),
err: char_p_boxed::try_from(get_name(&e)).unwrap(),
err: FfiError::from(&e),
},
}
}
@ -115,11 +115,11 @@ fn list_unspent(opaque_wallet: &OpaqueWallet) -> FfiResult<repr_c::Vec<LocalUtxo
let ve: Vec<LocalUtxo> = v.iter().map(|lu| LocalUtxo::from(lu)).collect();
repr_c::Vec::from(ve)
},
err: char_p_boxed::from(CString::default()),
err: FfiError::None,
},
Err(e) => FfiResult {
ok: repr_c::Vec::EMPTY,
err: char_p_boxed::try_from(get_name(&e)).unwrap(),
err: FfiError::from(&e),
},
}
}