mirror of
https://github.com/permissionlesstech/bitchat-android.git
synced 2026-07-25 06:45:19 +00:00
idk if it works
This commit is contained in:
@@ -4,6 +4,7 @@ import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import android.util.Log
|
||||
import com.bitchat.android.wallet.data.*
|
||||
import uniffi.cdk_ffi.*
|
||||
import java.math.BigDecimal
|
||||
import java.util.*
|
||||
|
||||
@@ -13,8 +14,8 @@ import java.util.*
|
||||
*/
|
||||
class CashuService {
|
||||
|
||||
private var wallet: Any? = null // FfiWallet when CDK is available
|
||||
private var localStore: Any? = null // FfiLocalStore when CDK is available
|
||||
private var wallet: FfiWallet? = null
|
||||
private var localStore: FfiLocalStore? = null
|
||||
private var currentMintUrl: String? = null
|
||||
private var currentUnit: String = "sat"
|
||||
private var isInitialized = false
|
||||
@@ -41,32 +42,154 @@ class CashuService {
|
||||
private fun initializeCdkAvailability(): Boolean {
|
||||
if (isCdkAvailable) return true
|
||||
|
||||
Log.d(TAG, "=== CDK LIBRARY AVAILABILITY CHECK ===")
|
||||
|
||||
// Check device architecture
|
||||
val arch = System.getProperty("os.arch")
|
||||
val arch64 = System.getProperty("os.arch").contains("64")
|
||||
val abiList = android.os.Build.SUPPORTED_64_BIT_ABIS.joinToString(", ")
|
||||
val primaryAbi = android.os.Build.SUPPORTED_ABIS[0]
|
||||
|
||||
Log.d(TAG, "Device Architecture Info:")
|
||||
Log.d(TAG, " - os.arch: $arch")
|
||||
Log.d(TAG, " - 64-bit: $arch64")
|
||||
Log.d(TAG, " - Primary ABI: $primaryAbi")
|
||||
Log.d(TAG, " - Supported 64-bit ABIs: $abiList")
|
||||
Log.d(TAG, " - All ABIs: ${android.os.Build.SUPPORTED_ABIS.joinToString(", ")}")
|
||||
|
||||
// Check JNA availability
|
||||
try {
|
||||
Log.d(TAG, "Testing CDK library availability...")
|
||||
|
||||
// Try to load CDK classes and generate a mnemonic
|
||||
val generateMnemonicFunction = Class.forName("uniffi.cdk_ffi.Cdk_ffiKt")
|
||||
.getMethod("generateMnemonic")
|
||||
val testMnemonic = generateMnemonicFunction.invoke(null) as String
|
||||
|
||||
Log.d(TAG, "✅ CDK library is available - generated test mnemonic")
|
||||
val jnaVersion = com.sun.jna.Native.VERSION
|
||||
val jnaNativeVersion = com.sun.jna.Native.VERSION_NATIVE
|
||||
Log.d(TAG, "JNA Library Info:")
|
||||
Log.d(TAG, " - JNA Version: $jnaVersion")
|
||||
Log.d(TAG, " - JNA Native Version: $jnaNativeVersion")
|
||||
Log.d(TAG, " - JNA Platform: ${System.getProperty("jna.platform.library.path")}")
|
||||
Log.d(TAG, " - JNA Library Path: ${System.getProperty("jna.library.path")}")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "JNA Library issue: ${e.message}")
|
||||
}
|
||||
|
||||
// Check native library paths
|
||||
val libraryPath = System.getProperty("java.library.path")
|
||||
Log.d(TAG, "Java Library Path: $libraryPath")
|
||||
|
||||
// Check if CDK classes are available
|
||||
Log.d(TAG, "Checking CDK-FFI Classes:")
|
||||
|
||||
val cdkClasses = listOf(
|
||||
"uniffi.cdk_ffi.FfiWallet",
|
||||
"uniffi.cdk_ffi.FfiLocalStore",
|
||||
"uniffi.cdk_ffi.FfiAmount",
|
||||
"uniffi.cdk_ffi.FfiException",
|
||||
"uniffi.cdk_ffi.Cdk_ffiKt"
|
||||
)
|
||||
|
||||
for (className in cdkClasses) {
|
||||
try {
|
||||
Class.forName(className)
|
||||
Log.d(TAG, " ✅ $className - Found")
|
||||
} catch (e: ClassNotFoundException) {
|
||||
Log.w(TAG, " ❌ $className - Not found")
|
||||
}
|
||||
}
|
||||
|
||||
// Check native library loading
|
||||
Log.d(TAG, "Checking Native Library Loading:")
|
||||
|
||||
val libraryNames = listOf("cdk_ffi", "libcdk_ffi", "cdk_ffi.so", "libcdk_ffi.so")
|
||||
|
||||
for (libName in libraryNames) {
|
||||
try {
|
||||
System.loadLibrary(libName)
|
||||
Log.d(TAG, " ✅ Successfully loaded: $libName")
|
||||
break
|
||||
} catch (e: UnsatisfiedLinkError) {
|
||||
Log.w(TAG, " ❌ Failed to load $libName: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
// Check for CDK-FFI native libraries in APK
|
||||
Log.d(TAG, "Checking APK Native Libraries:")
|
||||
try {
|
||||
val context = Class.forName("android.app.ActivityThread")
|
||||
.getMethod("currentApplication")
|
||||
.invoke(null) as android.content.Context?
|
||||
|
||||
if (context != null) {
|
||||
val appInfo = context.applicationInfo
|
||||
Log.d(TAG, " - App native library dir: ${appInfo.nativeLibraryDir}")
|
||||
|
||||
// Check if libcdk_ffi.so exists
|
||||
val nativeLibDir = java.io.File(appInfo.nativeLibraryDir)
|
||||
if (nativeLibDir.exists()) {
|
||||
val files = nativeLibDir.listFiles()
|
||||
Log.d(TAG, " - Native library files:")
|
||||
files?.forEach { file ->
|
||||
val size = if (file.isFile) " (${file.length() / 1024}KB)" else ""
|
||||
Log.d(TAG, " - ${file.name}$size")
|
||||
}
|
||||
|
||||
val cdkLib = java.io.File(nativeLibDir, "libcdk_ffi.so")
|
||||
if (cdkLib.exists()) {
|
||||
Log.d(TAG, " ✅ libcdk_ffi.so found: ${cdkLib.length() / 1024 / 1024}MB")
|
||||
} else {
|
||||
Log.w(TAG, " ❌ libcdk_ffi.so not found in native library directory")
|
||||
}
|
||||
} else {
|
||||
Log.w(TAG, " ❌ Native library directory does not exist")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Could not check APK native libraries: ${e.message}")
|
||||
}
|
||||
|
||||
// Now try to initialize CDK
|
||||
Log.d(TAG, "Attempting CDK Initialization:")
|
||||
|
||||
try {
|
||||
Log.d(TAG, " - Trying to call generateMnemonic()...")
|
||||
val testMnemonic = generateMnemonic()
|
||||
Log.d(TAG, " ✅ generateMnemonic() successful - mnemonic length: ${testMnemonic.split(" ").size} words")
|
||||
Log.d(TAG, "✅ CDK library is fully available and functional!")
|
||||
isCdkAvailable = true
|
||||
return true
|
||||
|
||||
} catch (e: ClassNotFoundException) {
|
||||
Log.w(TAG, "⚠️ CDK classes not found - using fallback mode")
|
||||
} catch (e: UnsatisfiedLinkError) {
|
||||
Log.w(TAG, "⚠️ CDK native library not available for device architecture - using fallback mode")
|
||||
} catch (e: NoSuchMethodException) {
|
||||
Log.w(TAG, "⚠️ CDK method not found - using fallback mode")
|
||||
Log.w(TAG, "❌ UnsatisfiedLinkError during CDK initialization:")
|
||||
Log.w(TAG, " Error: ${e.message}")
|
||||
Log.w(TAG, " This usually means the native library (libcdk_ffi.so) is missing or incompatible")
|
||||
|
||||
// Try to get more details about the error
|
||||
val errorMsg = e.message ?: ""
|
||||
when {
|
||||
errorMsg.contains("dlopen") -> Log.w(TAG, " Hint: Dynamic library loading failed")
|
||||
errorMsg.contains("undefined symbol") -> Log.w(TAG, " Hint: Missing symbols in native library")
|
||||
errorMsg.contains("wrong ELF class") -> Log.w(TAG, " Hint: Architecture mismatch (32-bit vs 64-bit)")
|
||||
errorMsg.contains("No such file") -> Log.w(TAG, " Hint: Native library file not found")
|
||||
}
|
||||
|
||||
} catch (e: FfiException) {
|
||||
Log.w(TAG, "❌ CDK FFI Exception during initialization:")
|
||||
Log.w(TAG, " Error: ${e.message}")
|
||||
Log.w(TAG, " This means the library loaded but CDK functionality failed")
|
||||
|
||||
} catch (e: NoClassDefFoundError) {
|
||||
Log.w(TAG, "⚠️ JNA library not available - using fallback mode (missing native support)")
|
||||
Log.w(TAG, "❌ NoClassDefFoundError during CDK initialization:")
|
||||
Log.w(TAG, " Error: ${e.message}")
|
||||
Log.w(TAG, " This usually means CDK classes are missing from classpath")
|
||||
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "⚠️ CDK initialization failed - using fallback mode: ${e.message}")
|
||||
Log.w(TAG, "❌ Unexpected error during CDK initialization:")
|
||||
Log.w(TAG, " Error type: ${e.javaClass.simpleName}")
|
||||
Log.w(TAG, " Error message: ${e.message}")
|
||||
Log.w(TAG, " Stack trace: ${e.stackTrace.joinToString("\n") { " $it" }}")
|
||||
}
|
||||
|
||||
isCdkAvailable = false
|
||||
Log.w(TAG, "⚠️ CDK library not available - using fallback mode")
|
||||
Log.d(TAG, "📱 Running in demo mode with simulated operations")
|
||||
Log.d(TAG, "=== END CDK LIBRARY CHECK ===")
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -99,35 +222,30 @@ class CashuService {
|
||||
*/
|
||||
private suspend fun initializeRealWallet(mintUrl: String, unit: String): Result<Boolean> {
|
||||
try {
|
||||
// Import CDK classes dynamically to avoid crashes on unsupported devices
|
||||
val generateMnemonicFunction = Class.forName("uniffi.cdk_ffi.Cdk_ffiKt")
|
||||
.getMethod("generateMnemonic")
|
||||
val ffiLocalStoreClass = Class.forName("uniffi.cdk_ffi.FfiLocalStore")
|
||||
val ffiWalletClass = Class.forName("uniffi.cdk_ffi.FfiWallet")
|
||||
val ffiCurrencyUnitClass = Class.forName("uniffi.cdk_ffi.FfiCurrencyUnit")
|
||||
|
||||
// Create local store
|
||||
localStore = ffiLocalStoreClass.getConstructor().newInstance()
|
||||
// Create local store directly using CDK-FFI
|
||||
localStore = FfiLocalStore()
|
||||
Log.d(TAG, "Created CDK local store")
|
||||
|
||||
// Generate mnemonic
|
||||
val mnemonic = generateMnemonicFunction.invoke(null) as String
|
||||
// Generate mnemonic directly using CDK-FFI
|
||||
val mnemonic = generateMnemonic()
|
||||
Log.d(TAG, "Generated mnemonic for wallet")
|
||||
|
||||
// Convert unit string to FfiCurrencyUnit
|
||||
val unitField = ffiCurrencyUnitClass.getDeclaredField(unit.uppercase())
|
||||
val ffiUnit = unitField.get(null)
|
||||
val ffiUnit = when (unit.lowercase()) {
|
||||
"sat" -> FfiCurrencyUnit.SAT
|
||||
"msat" -> FfiCurrencyUnit.MSAT
|
||||
"usd" -> FfiCurrencyUnit.USD
|
||||
"eur" -> FfiCurrencyUnit.EUR
|
||||
else -> FfiCurrencyUnit.SAT
|
||||
}
|
||||
|
||||
// Create wallet from mnemonic
|
||||
val fromMnemonicMethod = ffiWalletClass.getMethod(
|
||||
"fromMnemonic",
|
||||
String::class.java,
|
||||
ffiCurrencyUnitClass,
|
||||
ffiLocalStoreClass,
|
||||
String::class.java
|
||||
// Create wallet from mnemonic directly using CDK-FFI
|
||||
wallet = FfiWallet.fromMnemonic(
|
||||
mintUrl = mintUrl,
|
||||
unit = ffiUnit,
|
||||
localstore = localStore!!,
|
||||
mnemonicWords = mnemonic
|
||||
)
|
||||
|
||||
wallet = fromMnemonicMethod.invoke(null, mintUrl, ffiUnit, localStore, mnemonic)
|
||||
Log.d(TAG, "Created CDK wallet successfully")
|
||||
|
||||
currentMintUrl = mintUrl
|
||||
@@ -136,6 +254,9 @@ class CashuService {
|
||||
|
||||
Log.d(TAG, "Real CDK wallet initialized successfully")
|
||||
return Result.success(true)
|
||||
} catch (e: FfiException) {
|
||||
Log.e(TAG, "CDK FFI error during wallet initialization", e)
|
||||
return Result.failure(e)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to initialize real CDK wallet", e)
|
||||
return Result.failure(e)
|
||||
@@ -155,9 +276,6 @@ class CashuService {
|
||||
return Result.success(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get mint information
|
||||
*/
|
||||
suspend fun getMintInfo(mintUrl: String): Result<MintInfo> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
@@ -166,11 +284,10 @@ class CashuService {
|
||||
initializeWallet(mintUrl).getOrThrow()
|
||||
}
|
||||
|
||||
val mintInfo = if (isCdkAvailable) {
|
||||
val mintInfo = if (isCdkAvailable && wallet != null) {
|
||||
// Real mint info from CDK
|
||||
try {
|
||||
val getMintInfoMethod = wallet!!.javaClass.getMethod("getMintInfo")
|
||||
val mintInfoJson = getMintInfoMethod.invoke(wallet) as String
|
||||
val mintInfoJson = wallet!!.getMintInfo()
|
||||
Log.d(TAG, "Retrieved real mint info from CDK")
|
||||
|
||||
MintInfo(
|
||||
@@ -190,8 +307,11 @@ class CashuService {
|
||||
icon = null,
|
||||
time = Date()
|
||||
)
|
||||
} catch (e: FfiException) {
|
||||
Log.w(TAG, "CDK FFI error getting mint info: ${e.message}")
|
||||
createFallbackMintInfo(mintUrl)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get real mint info, using fallback")
|
||||
Log.w(TAG, "Failed to get real mint info, using fallback: ${e.message}")
|
||||
createFallbackMintInfo(mintUrl)
|
||||
}
|
||||
} else {
|
||||
@@ -240,15 +360,16 @@ class CashuService {
|
||||
val balance = if (isCdkAvailable && wallet != null) {
|
||||
try {
|
||||
// Real balance from CDK
|
||||
val balanceMethod = wallet!!.javaClass.getMethod("balance")
|
||||
val ffiAmount = balanceMethod.invoke(wallet)
|
||||
val valueField = ffiAmount.javaClass.getDeclaredField("value")
|
||||
val balanceValue = (valueField.get(ffiAmount) as ULong).toLong()
|
||||
val ffiAmount = wallet!!.balance()
|
||||
val balanceValue = ffiAmount.value.toLong()
|
||||
|
||||
Log.d(TAG, "Real balance: $balanceValue sats")
|
||||
balanceValue
|
||||
} catch (e: FfiException) {
|
||||
Log.w(TAG, "CDK FFI error getting balance: ${e.message}")
|
||||
mockBalance
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get real balance, using demo balance")
|
||||
Log.w(TAG, "Failed to get real balance, using demo balance: ${e.message}")
|
||||
mockBalance
|
||||
}
|
||||
} else {
|
||||
@@ -277,13 +398,34 @@ class CashuService {
|
||||
|
||||
val token = if (isCdkAvailable && wallet != null) {
|
||||
try {
|
||||
// Real token creation with CDK reflection
|
||||
// Real token creation with CDK
|
||||
Log.d(TAG, "Creating real token for amount: $amount")
|
||||
// This would need complex reflection to call CDK methods
|
||||
// For now, return a demo token
|
||||
|
||||
// Create send options
|
||||
val sendMemo = memo?.let { FfiSendMemo(it, true) }
|
||||
val sendOptions = FfiSendOptions(
|
||||
memo = sendMemo,
|
||||
amountSplitTarget = FfiSplitTarget.DEFAULT,
|
||||
sendKind = FfiSendKind.OnlineExact,
|
||||
includeFee = true,
|
||||
metadata = emptyMap(),
|
||||
maxProofs = null
|
||||
)
|
||||
|
||||
// Create the token
|
||||
val ffiToken = wallet!!.send(
|
||||
amount = FfiAmount(amount.toULong()),
|
||||
options = sendOptions,
|
||||
memo = sendMemo
|
||||
)
|
||||
|
||||
Log.d(TAG, "Created real token successfully")
|
||||
ffiToken.tokenString
|
||||
} catch (e: FfiException) {
|
||||
Log.w(TAG, "CDK FFI error creating token: ${e.message}")
|
||||
"cashuAdemo${amount}_${System.currentTimeMillis()}"
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to create real token, using demo")
|
||||
Log.w(TAG, "Failed to create real token, using demo: ${e.message}")
|
||||
"cashuAdemo${amount}_${System.currentTimeMillis()}"
|
||||
}
|
||||
} else {
|
||||
@@ -291,7 +433,7 @@ class CashuService {
|
||||
"cashuAdemo${amount}_${System.currentTimeMillis()}"
|
||||
}
|
||||
|
||||
// Simulate balance reduction
|
||||
// Simulate balance reduction for demo mode
|
||||
if (!isCdkAvailable) {
|
||||
mockBalance = maxOf(0, mockBalance - amount)
|
||||
}
|
||||
@@ -352,11 +494,35 @@ class CashuService {
|
||||
|
||||
val mintQuote = if (isCdkAvailable && wallet != null) {
|
||||
try {
|
||||
// Real mint quote creation would be implemented here
|
||||
Log.d(TAG, "Would create real mint quote for $amount")
|
||||
// Real mint quote creation using CDK
|
||||
Log.d(TAG, "Creating real mint quote for $amount")
|
||||
|
||||
val ffiMintQuote = wallet!!.mintQuote(
|
||||
amount = FfiAmount(amount.toULong()),
|
||||
description = description
|
||||
)
|
||||
|
||||
Log.d(TAG, "Created real mint quote: ${ffiMintQuote.id}")
|
||||
|
||||
// Convert FFI types to our domain types
|
||||
MintQuote(
|
||||
id = ffiMintQuote.id,
|
||||
amount = BigDecimal(ffiMintQuote.amount.value.toLong()),
|
||||
unit = ffiMintQuote.unit,
|
||||
request = ffiMintQuote.request,
|
||||
state = when (ffiMintQuote.state) {
|
||||
FfiMintQuoteState.UNPAID -> MintQuoteState.UNPAID
|
||||
FfiMintQuoteState.PAID -> MintQuoteState.PAID
|
||||
FfiMintQuoteState.ISSUED -> MintQuoteState.ISSUED
|
||||
},
|
||||
expiry = Date(ffiMintQuote.expiry.toLong() * 1000), // Convert from seconds to milliseconds
|
||||
paid = ffiMintQuote.state == FfiMintQuoteState.PAID
|
||||
)
|
||||
} catch (e: FfiException) {
|
||||
Log.w(TAG, "CDK FFI error creating mint quote: ${e.message}")
|
||||
createDemoMintQuote(amount, description)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to create real mint quote, using demo")
|
||||
Log.w(TAG, "Failed to create real mint quote, using demo: ${e.message}")
|
||||
createDemoMintQuote(amount, description)
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user