From 309ad1836edf48edc2b16842350c74e4bdd05faa Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Fri, 12 Sep 2025 15:13:00 +0200 Subject: [PATCH] use ffi for mweb (#2456) * chore: add ffi and ffigen dependencies to pubspec.yaml * refactor: replace method channel with FFI implementation for MWeb module * fix: correct `cd` command in build_mwebd.sh script * chore: add go.sum file with dependency checksums * fix: ensure ANDROID_NDK_VERSION is set before building and dynamically update NDK_BIN path * fix: update NDK_BIN path to dynamically detect platform in build_mwebd.sh script * refactor: remove unused mwebd dependencies and clean up CwMwebPlugin code * Update scripts/android/build_mwebd.sh Co-authored-by: cyan * refactor: replace iOS MoneroWallet framework with Mwebd.xcframework and clean up plugin implementation [skip-ci] * fix: mwebd ffi ios * refactor: update MWebFfi class to use non-nullable `lib` and improve address debug logging * chore: remove unused iOS/Android build scripts for mwebd * refactor: replace mweb.so with libmweb.so, use sync methods in MWebFfi, and update iOS/Android frameworks/scripts accordingly * chore: unify mwebd build process in workflows, remove unused gomobile init command --------- Co-authored-by: cyan --- .github/workflows/pr_test_build_android.yml | 1 - .github/workflows/pr_test_build_linux.yml | 9 + cw_bitcoin/lib/litecoin_wallet_addresses.dart | 4 +- cw_mweb/android/build.gradle | 12 - cw_mweb/android/src/main/jniLibs/x86/.gitkeep | 0 .../com/cakewallet/mweb/CwMwebPlugin.kt | 37 +-- cw_mweb/ffigen_config.yaml | 8 + cw_mweb/go/go.mod | 50 ++++ cw_mweb/go/go.sum | 97 +++++++ cw_mweb/go/mweb.go | 58 ++++ cw_mweb/ios/Classes/CwMwebPlugin.swift | 87 +----- cw_mweb/lib/cw_mweb.dart | 23 +- cw_mweb/lib/cw_mweb_method_channel.dart | 58 ---- cw_mweb/lib/cw_mweb_platform_interface.dart | 43 --- cw_mweb/lib/mweb_ffi.dart | 68 +++++ cw_mweb/pubspec.yaml | 2 + ios/Podfile.lock | 2 + ios/Runner.xcodeproj/project.pbxproj | 4 + scripts/android/build_mwebd.sh | 47 ++- scripts/ios/build_mwebd.sh | 269 +++++++++++++++++- 20 files changed, 607 insertions(+), 272 deletions(-) create mode 100644 cw_mweb/android/src/main/jniLibs/x86/.gitkeep create mode 100644 cw_mweb/ffigen_config.yaml create mode 100644 cw_mweb/go/go.mod create mode 100644 cw_mweb/go/go.sum create mode 100644 cw_mweb/go/mweb.go delete mode 100644 cw_mweb/lib/cw_mweb_method_channel.dart delete mode 100644 cw_mweb/lib/cw_mweb_platform_interface.dart create mode 100644 cw_mweb/lib/mweb_ffi.dart diff --git a/.github/workflows/pr_test_build_android.yml b/.github/workflows/pr_test_build_android.yml index 135752105..3122965d0 100644 --- a/.github/workflows/pr_test_build_android.yml +++ b/.github/workflows/pr_test_build_android.yml @@ -254,7 +254,6 @@ jobs: export MWEBD_HASH=$(cat scripts/android/build_mwebd.sh | grep 'git reset --hard' | xargs | awk '{ print $4 }') echo MWEBD_HASH=$MWEBD_HASH >> /etc/environment pushd scripts/android - gomobile init; ./build_mwebd.sh --dont-install popd diff --git a/.github/workflows/pr_test_build_linux.yml b/.github/workflows/pr_test_build_linux.yml index 0a6370184..329c4275e 100644 --- a/.github/workflows/pr_test_build_linux.yml +++ b/.github/workflows/pr_test_build_linux.yml @@ -232,6 +232,15 @@ jobs: run: | flutter pub get + - name: Build mwebd + run: | + set -x -e + export MWEBD_HASH=$(cat scripts/android/build_mwebd.sh | grep 'git reset --hard' | xargs | awk '{ print $4 }') + echo MWEBD_HASH=$MWEBD_HASH >> /etc/environment + pushd scripts/android + ./build_mwebd.sh --dont-install + popd + - name: Build generated code run: | ./model_generator.sh async diff --git a/cw_bitcoin/lib/litecoin_wallet_addresses.dart b/cw_bitcoin/lib/litecoin_wallet_addresses.dart index bbb987766..76228c16d 100644 --- a/cw_bitcoin/lib/litecoin_wallet_addresses.dart +++ b/cw_bitcoin/lib/litecoin_wallet_addresses.dart @@ -6,7 +6,6 @@ import 'package:bitcoin_base/bitcoin_base.dart'; import 'package:blockchain_utils/blockchain_utils.dart'; import 'package:cw_bitcoin/bitcoin_address_record.dart'; import 'package:cw_bitcoin/bitcoin_unspent.dart'; -import 'package:cw_bitcoin/electrum_wallet.dart'; import 'package:cw_bitcoin/utils.dart'; import 'package:cw_bitcoin/electrum_wallet_addresses.dart'; import 'package:cw_core/unspent_coin_type.dart'; @@ -84,8 +83,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with generating = true; try { while (mwebAddrs.length <= (index + 1)) { - final addresses = - await CwMweb.addresses(scan, spend, mwebAddrs.length, mwebAddrs.length + 50); + final addresses = CwMweb.addresses(scan, spend, mwebAddrs.length, mwebAddrs.length + 50); printV("generated up to index ${mwebAddrs.length}"); // sleep for a bit to avoid making the main thread unresponsive: await Future.delayed(Duration(milliseconds: 200)); diff --git a/cw_mweb/android/build.gradle b/cw_mweb/android/build.gradle index 0d17a47ec..cb36aa1f0 100644 --- a/cw_mweb/android/build.gradle +++ b/cw_mweb/android/build.gradle @@ -21,14 +21,6 @@ allprojects { } } -rootProject.allprojects { - repositories { - flatDir { - dirs project(':cw_mweb').file('libs') - } - } -} - apply plugin: 'com.android.library' apply plugin: 'kotlin-android' @@ -74,7 +66,3 @@ android { } } } - -dependencies { - implementation (name: 'mwebd', ext: 'aar') -} diff --git a/cw_mweb/android/src/main/jniLibs/x86/.gitkeep b/cw_mweb/android/src/main/jniLibs/x86/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt index 3ab24ee12..f90759d31 100644 --- a/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt +++ b/cw_mweb/android/src/main/kotlin/com/cakewallet/mweb/CwMwebPlugin.kt @@ -8,9 +8,6 @@ import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import mwebd.Mwebd -import mwebd.Server - /** CwMwebPlugin */ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { /// The MethodChannel that will the communication between Flutter and native Android @@ -18,8 +15,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { /// This local reference serves to register the plugin with the Flutter Engine and unregister it /// when the Flutter Engine is detached from the Activity private lateinit var channel : MethodChannel - private var server: Server? = null - private var port: Long? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "cw_mweb") @@ -27,40 +22,10 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler { } override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { - if (call.method == "start") { - server?.stop() - val dataDir = call.argument("dataDir") ?: "" - val nodeUri = call.argument("nodeUri") ?: "" - server = server ?: Mwebd.newServer("", dataDir, nodeUri) - port = server?.start(0) - result.success(port) - } else if (call.method == "stop") { - server?.stop() - server = null - port = null - result.success(null) - } else if (call.method == "address") { - // val scanSecret: ByteArray = call.argument("scanSecret") ?: ByteArray(0) - // val spendPub: ByteArray = call.argument("spendPub") ?: ByteArray(0) - // val index: Int = call.argument("index") ?: 0 - // val res = Mwebd.address(scanSecret, spendPub, index) - // result.success(res) - } else if (call.method == "addresses") { - val scanSecret: ByteArray = call.argument("scanSecret") ?: ByteArray(0) - val spendPub: ByteArray = call.argument("spendPub") ?: ByteArray(0) - val fromIndex: Int = call.argument("fromIndex") ?: 0 - val toIndex: Int = call.argument("toIndex") ?: 0 - val res = Mwebd.addresses(scanSecret, spendPub, fromIndex, toIndex) - result.success(res) - } else { - result.notImplemented() - } + result.notImplemented() } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel.setMethodCallHandler(null) - server?.stop() - server = null - port = null } } diff --git a/cw_mweb/ffigen_config.yaml b/cw_mweb/ffigen_config.yaml new file mode 100644 index 000000000..2dbe85c4a --- /dev/null +++ b/cw_mweb/ffigen_config.yaml @@ -0,0 +1,8 @@ +# yaml-language-server: $schema=../../ffigen.schema.json + +name: MWebFlutter +description: Bindings to `go/mweb.h`. +output: 'lib/generated_bindings.g.dart' +headers: + entry-points: + - 'android/src/main/jniLibs/arm64-v8a/libmweb.h' diff --git a/cw_mweb/go/go.mod b/cw_mweb/go/go.mod new file mode 100644 index 000000000..36d1e464c --- /dev/null +++ b/cw_mweb/go/go.mod @@ -0,0 +1,50 @@ +module github.com/cake-tech/cake_wallet/cw_mweb/go + +go 1.24.2 + +require ( + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/aead/siphash v1.0.1 // indirect + github.com/alitto/pond/v2 v2.2.0 // indirect + github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f // indirect + github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd // indirect + github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/deckarep/golang-set/v2 v2.6.0 // indirect + github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/decred/dcrd/lru v1.1.2 // indirect + github.com/ethereum/go-ethereum v1.14.8 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/gorilla/websocket v1.4.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect + github.com/holiman/uint256 v1.3.1 // indirect + github.com/kkdai/bstream v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/ltcmweb/coinswapd v0.1.0 // indirect + github.com/ltcmweb/ltcd v0.25.6 // indirect + github.com/ltcmweb/ltcd/btcec/v2 v2.3.3 // indirect + github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3 // indirect + github.com/ltcmweb/mwebd v0.1.8 // indirect + github.com/ltcmweb/neutrino v0.17.4 // indirect + github.com/ltcmweb/neutrino/cache v1.1.0 // indirect + github.com/ltcmweb/secp256k1 v0.1.1 // indirect + github.com/ltcsuite/lnd/queue v1.1.0 // indirect + github.com/ltcsuite/lnd/ticker v1.1.0 // indirect + github.com/ltcsuite/ltcwallet/walletdb v1.3.5 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + go.etcd.io/bbolt v1.3.10 // indirect + golang.org/x/crypto v0.33.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 // indirect + google.golang.org/grpc v1.63.2 // indirect + google.golang.org/protobuf v1.34.2 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + lukechampine.com/blake3 v1.3.0 // indirect +) diff --git a/cw_mweb/go/go.sum b/cw_mweb/go/go.sum new file mode 100644 index 000000000..c973d854f --- /dev/null +++ b/cw_mweb/go/go.sum @@ -0,0 +1,97 @@ +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alitto/pond/v2 v2.2.0 h1:hX3B1Lu4b5PjSHR+IWNRDKD0Jfw2ew8V25J7Vu5j7RM= +github.com/alitto/pond/v2 v2.2.0/go.mod h1:xkjYEgQ05RSpWdfSd1nM3OVv7TBhLdy7rMp3+2Nq+yE= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f h1:bAs4lUbRJpnnkd9VhRV3jjAVU7DJVjMaK+IsvSeZvFo= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd h1:R/opQEbFEy9JGkIguV40SvRY1uliPX8ifOvi6ICsFCw= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= +github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/lru v1.1.2 h1:KdCzlkxppuoIDGEvCGah1fZRicrDH36IipvlB1ROkFY= +github.com/decred/dcrd/lru v1.1.2/go.mod h1:gEdCVgXs1/YoBvFWt7Scgknbhwik3FgVSzlnCcXL2N8= +github.com/ethereum/go-ethereum v1.14.8 h1:NgOWvXS+lauK+zFukEvi85UmmsS/OkV0N23UZ1VTIig= +github.com/ethereum/go-ethereum v1.14.8/go.mod h1:TJhyuDq0JDppAkFXgqjwpdlQApywnu/m10kFPxh8vvs= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= +github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= +github.com/holiman/uint256 v1.3.1 h1:JfTzmih28bittyHM8z360dCjIA9dbPIBlcTI6lmctQs= +github.com/holiman/uint256 v1.3.1/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/kkdai/bstream v1.0.0 h1:Se5gHwgp2VT2uHfDrkbbgbgEvV9cimLELwrPJctSjg8= +github.com/kkdai/bstream v1.0.0/go.mod h1:FDnDOHt5Yx4p3FaHcioFT0QjDOtgUpvjeZqAs+NVZZA= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/ltcmweb/coinswapd v0.1.0 h1:M6eoz4g7fDpsQeNA8wWbBh6PY1xykrihyLU44SgljeI= +github.com/ltcmweb/coinswapd v0.1.0/go.mod h1:CY5cVSympaLrMO8fHWXO+e/iuPLs2L0U9daMCRTstEM= +github.com/ltcmweb/ltcd v0.25.6 h1:96aC38ZEyNVztylrBBqYnVZJ6cuF7IUvL2knhbMxs3Y= +github.com/ltcmweb/ltcd v0.25.6/go.mod h1:jQbvPfnT4bBXJKRwU5SdD9ZUopwmSvhngdjeopv2cIU= +github.com/ltcmweb/ltcd/btcec/v2 v2.3.3 h1:gJc1ljDPCBtwBKFcC4SW44BFoJqoZzkeagndMYdqKKE= +github.com/ltcmweb/ltcd/btcec/v2 v2.3.3/go.mod h1:NRr2WtpiSiSO29TkdZhbGNRA/Q16DV2eNbORf+M2ykI= +github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3 h1:CAPyzHI3bCFRrrVHZkDUR2i3Awj6l/aqAZIEi0E/nfM= +github.com/ltcmweb/ltcd/chaincfg/chainhash v1.0.3/go.mod h1:zB+HhI2IbIwTGpdAhdzpm1GVX4ShcWTgN42+ar9HXrg= +github.com/ltcmweb/mwebd v0.1.8 h1:5TJgrM76H3s6kWUJ6CsU1L+DaafcAZ1edDlkyJ+GWt0= +github.com/ltcmweb/mwebd v0.1.8/go.mod h1:KPf+lXEvEMuG0NaHume0imWMb9Cvr15SNPujZO6EAv4= +github.com/ltcmweb/neutrino v0.17.4 h1:ZsguBvicTM5CtZDsKg+OBFuPUCKjfEJ3lRSGXPjF2oM= +github.com/ltcmweb/neutrino v0.17.4/go.mod h1:KktZ+McIwHuTHe0K8JK9Wv4qgv8orkA21a0ZOU4SwJ4= +github.com/ltcmweb/neutrino/cache v1.1.0 h1:C0Qn2p8ogcskRPfrBKPUayjn3m/CUJqI7otopNScvhk= +github.com/ltcmweb/neutrino/cache v1.1.0/go.mod h1:HddBIjnWaEvrDSnpMIBKkxCJaoRMlg2DfVdNFM0y+ME= +github.com/ltcmweb/secp256k1 v0.1.1 h1:hsGpnFcZ5zmjSxg7AswZp8ygo5NiY06OYWJk97hFizI= +github.com/ltcmweb/secp256k1 v0.1.1/go.mod h1:Kdj2WHIAc0Y1apPtVY7nVxImRWiRTQue2vs1gt1VoEc= +github.com/ltcsuite/lnd/queue v1.1.0 h1:/aVgox4Lz74xBU8BSw5HDau7hHl2irJs5M9u1SPQ2E0= +github.com/ltcsuite/lnd/queue v1.1.0/go.mod h1:DJrxK2gPC2FjJAVYxPOcnY2CplI3rhL2PEq7IexlTWQ= +github.com/ltcsuite/lnd/ticker v1.0.1/go.mod h1:WZKpekfDVAVv7Gsrr0GAWC/U1XURfGesFg9sQYJbeL4= +github.com/ltcsuite/lnd/ticker v1.1.0 h1:3zYM/JlKNqq+DotW8jiEdHVeY5Yl7n7cUb2bTof1yXM= +github.com/ltcsuite/lnd/ticker v1.1.0/go.mod h1:K2qQ3EPe8enztYvn/VAbLafCPy13XHMuCRPsySoAbt8= +github.com/ltcsuite/ltcwallet/walletdb v1.3.5 h1:WymVw0FBQ8KJgH7B88ujRqBOJ9R0en9K9urpJW4atAE= +github.com/ltcsuite/ltcwallet/walletdb v1.3.5/go.mod h1:29SBzxA55wNxY3ctFw6t5PgsULwf3NMwg2MiGQgtrJE= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +go.etcd.io/bbolt v1.3.5-0.20200615073812-232d8fc87f50/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0= +go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434 h1:umK/Ey0QEzurTNlsV3R+MfxHAb78HCEX/IkuR+zH4WQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240509183442-62759503f434/go.mod h1:I7Y+G38R2bu5j1aLzfFmQfTcU/WnFuqDwLZAbvKTKpM= +google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM= +google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/cw_mweb/go/mweb.go b/cw_mweb/go/mweb.go new file mode 100644 index 000000000..0fc2d3a9a --- /dev/null +++ b/cw_mweb/go/mweb.go @@ -0,0 +1,58 @@ +package main + +import ( + "C" + "unsafe" + + "github.com/ltcmweb/mwebd" +) + +var server *mwebd.Server + +//export StartServer +func StartServer(chain *C.char, dataDir *C.char, nodeUri *C.char) C.int { + if server != nil { + server.Stop() + } + goChain := C.GoString(chain) + goDataDir := C.GoString(dataDir) + goNodeUri := C.GoString(nodeUri) + + var err error + server, err = mwebd.NewServer(goChain, goDataDir, goNodeUri) + if err != nil { + return 0 + } + + start, err := server.Start(0) + if err != nil { + return 0 + } + + return C.int(start) +} + +//export StopServer +func StopServer() { + if server == nil { + return + } + server.Stop() + server = nil +} + +//export Addresses +func Addresses(scanSecret *C.char, scanSecretLen C.int, spendPubKey *C.char, spendPubKeyLen C.int, i C.int, j C.int) *C.char { + // Convert C pointers to Go byte slices + goScanSecret := C.GoBytes(unsafe.Pointer(scanSecret), scanSecretLen) + goSpendPubKey := C.GoBytes(unsafe.Pointer(spendPubKey), spendPubKeyLen) + + // Call the original Go function + addr := mwebd.Addresses(goScanSecret, goSpendPubKey, int32(i), int32(j)) + + // Convert the resulting Go string to a C string + // The C code must free this memory + return C.CString(addr) +} + +func main() {} diff --git a/cw_mweb/ios/Classes/CwMwebPlugin.swift b/cw_mweb/ios/Classes/CwMwebPlugin.swift index 55b380829..aa3297dfc 100644 --- a/cw_mweb/ios/Classes/CwMwebPlugin.swift +++ b/cw_mweb/ios/Classes/CwMwebPlugin.swift @@ -1,6 +1,5 @@ import Flutter import UIKit -import Mwebd public class CwMwebPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { @@ -9,92 +8,8 @@ public static func register(with registrar: FlutterPluginRegistrar) { registrar.addMethodCallDelegate(instance, channel: channel) } - private static var server: MwebdServer? - private static var port: Int = 0 - private static var dataDir: String? - private static var nodeUri: String? public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - switch call.method { - case "getPlatformVersion": - result("iOS " + UIDevice.current.systemVersion) - break - case "start": - stopServer() - let args = call.arguments as? [String: String] - let dataDir = args?["dataDir"] - let nodeUri = args?["nodeUri"] - CwMwebPlugin.dataDir = dataDir - CwMwebPlugin.nodeUri = nodeUri - startServer(result: result) - break - case "stop": - stopServer() - result(nil) - break - // case "address": - // let args = call.arguments as! [String: Any] - // let scanSecret = args["scanSecret"] as! FlutterStandardTypedData - // let spendPub = args["spendPub"] as! FlutterStandardTypedData - // let index = args["index"] as! Int32 - - // let scanSecretData = scanSecret.data - // let spendPubData = spendPub.data - // result(MwebdAddress(scanSecretData, spendPubData, index)) - // break - case "addresses": - let args = call.arguments as! [String: Any] - let scanSecret = args["scanSecret"] as! FlutterStandardTypedData - let spendPub = args["spendPub"] as! FlutterStandardTypedData - let fromIndex = args["fromIndex"] as! Int32 - let toIndex = args["toIndex"] as! Int32 - - let scanSecretData = scanSecret.data - let spendPubData = spendPub.data - result(MwebdAddresses(scanSecretData, spendPubData, fromIndex, toIndex)) - break - default: - result(FlutterMethodNotImplemented) - break - } - } - - private func startServer(result: @escaping FlutterResult) { - if CwMwebPlugin.server == nil { - var error: NSError? - CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, CwMwebPlugin.nodeUri, &error) - - if let server = CwMwebPlugin.server { - do { - print("Starting server...") - try server.start(0, ret0_: &CwMwebPlugin.port) - print("Server started successfully on port: \(CwMwebPlugin.port)") - result(CwMwebPlugin.port) - } catch let startError as NSError { - print("Server Start Error: \(startError.localizedDescription)") - result(FlutterError(code: "Server Start Error", message: startError.localizedDescription, details: nil)) - } - } else if let error = error { - print("Server Creation Error: \(error.localizedDescription)") - result(FlutterError(code: "Server Creation Error", message: error.localizedDescription, details: nil)) - } else { - print("Unknown Error: Failed to create server") - result(FlutterError(code: "Unknown Error", message: "Failed to create server", details: nil)) - } - } else { - print("Server already running on port: \(CwMwebPlugin.port)") - result(CwMwebPlugin.port) - } - } - - private func stopServer() { - print("Stopping server") - CwMwebPlugin.server?.stop() - CwMwebPlugin.server = nil - CwMwebPlugin.port = 0 - } - - deinit { - stopServer() + result(FlutterMethodNotImplemented) } } diff --git a/cw_mweb/lib/cw_mweb.dart b/cw_mweb/lib/cw_mweb.dart index 594197018..bcd182714 100644 --- a/cw_mweb/lib/cw_mweb.dart +++ b/cw_mweb/lib/cw_mweb.dart @@ -5,9 +5,9 @@ import 'dart:io'; import 'dart:typed_data'; import 'package:cw_core/utils/print_verbose.dart'; +import 'package:cw_mweb/mweb_ffi.dart'; import 'package:grpc/grpc.dart'; import 'package:path_provider/path_provider.dart'; -import 'cw_mweb_platform_interface.dart'; import 'mwebd.pbgrpc.dart'; class CwMweb { @@ -58,7 +58,7 @@ class CwMweb { String debugLogPath = "${appDir.path}/logs/debug.log"; readFileWithTimer(debugLogPath); - _port = await CwMwebPlatform.instance.start(appDir.path, nodeUriOverride ?? ltcNodeUri); + _port = MWebFfi.instance.start(appDir.path, nodeUriOverride ?? ltcNodeUri); if (_port == null || _port == 0) { throw Exception("Failed to start server"); } @@ -109,7 +109,7 @@ class CwMweb { static Future stop() async { try { - await CwMwebPlatform.instance.stop(); + MWebFfi.instance.stop(); await cleanup(); } on GrpcError catch (e) { printV('Caught grpc error: ${e.message}'); @@ -118,11 +118,9 @@ class CwMweb { } } - static Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { + static String? address(Uint8List scanSecret, Uint8List spendPub, int index) { try { - return (await CwMwebPlatform.instance.addresses(scanSecret, spendPub, index, index + 1)) - ?.split(',') - .first; + return MWebFfi.instance.addresses(scanSecret, spendPub, index, index + 1).split(',').first; } on GrpcError catch (e) { printV('Caught grpc error: ${e.message}'); } catch (e) { @@ -131,15 +129,14 @@ class CwMweb { return null; } - static Future?> addresses( - Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) async { + static List? addresses( + Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) { try { - return (await CwMwebPlatform.instance.addresses(scanSecret, spendPub, fromIndex, toIndex)) - ?.split(','); + return MWebFfi.instance.addresses(scanSecret, spendPub, fromIndex, toIndex).split(','); } on GrpcError catch (e) { - log('Caught grpc error: ${e.message}'); + printV('Caught grpc error: ${e.message}'); } catch (e) { - log("Error getting addresses: $e"); + printV("Error getting addresses: $e"); } return null; } diff --git a/cw_mweb/lib/cw_mweb_method_channel.dart b/cw_mweb/lib/cw_mweb_method_channel.dart deleted file mode 100644 index 1aeb1939e..000000000 --- a/cw_mweb/lib/cw_mweb_method_channel.dart +++ /dev/null @@ -1,58 +0,0 @@ -import 'dart:io' show Platform; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; - -import 'cw_mweb_platform_interface.dart'; - -/// An implementation of [CwMwebPlatform] that uses method channels. -class MethodChannelCwMweb extends CwMwebPlatform { - /// The method channel used to interact with the native platform. - @visibleForTesting - final methodChannel = const MethodChannel('cw_mweb'); - - @override - Future start(String dataDir, String nodeUri) async { - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) { - return null; - } - final result = - await methodChannel.invokeMethod('start', {'dataDir': dataDir, 'nodeUri': nodeUri}); - return result; - } - - @override - Future stop() async { - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) { - return; - } - await methodChannel.invokeMethod('stop'); - } - - @override - Future address(Uint8List scanSecret, Uint8List spendPub, int index) async { - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) { - return null; - } - final result = await methodChannel.invokeMethod('address', { - 'scanSecret': scanSecret, - 'spendPub': spendPub, - 'index': index, - }); - return result; - } - - @override - Future addresses(Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) async { - if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) { - return null; - } - final result = await methodChannel.invokeMethod('addresses', { - 'scanSecret': scanSecret, - 'spendPub': spendPub, - 'fromIndex': fromIndex, - 'toIndex': toIndex, - }); - return result; - } -} diff --git a/cw_mweb/lib/cw_mweb_platform_interface.dart b/cw_mweb/lib/cw_mweb_platform_interface.dart deleted file mode 100644 index 6ec22bf56..000000000 --- a/cw_mweb/lib/cw_mweb_platform_interface.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:typed_data'; - -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; - -import 'cw_mweb_method_channel.dart'; - -abstract class CwMwebPlatform extends PlatformInterface { - /// Constructs a CwMwebPlatform. - CwMwebPlatform() : super(token: _token); - - static final Object _token = Object(); - - static CwMwebPlatform _instance = MethodChannelCwMweb(); - - /// The default instance of [CwMwebPlatform] to use. - /// - /// Defaults to [MethodChannelCwMweb]. - static CwMwebPlatform get instance => _instance; - - /// Platform-specific implementations should set this with their own - /// platform-specific class that extends [CwMwebPlatform] when - /// they register themselves. - static set instance(CwMwebPlatform instance) { - PlatformInterface.verifyToken(instance, _token); - _instance = instance; - } - - Future start(String dataDir, String nodeUri) { - throw UnimplementedError('start() has not been implemented.'); - } - - Future stop() { - throw UnimplementedError('stop() has not been implemented.'); - } - - Future address(Uint8List scanSecret, Uint8List spendPub, int index) { - throw UnimplementedError('address(int) has not been implemented.'); - } - - Future addresses(Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) { - throw UnimplementedError('addresses has not been implemented.'); - } -} diff --git a/cw_mweb/lib/mweb_ffi.dart b/cw_mweb/lib/mweb_ffi.dart new file mode 100644 index 000000000..14f49ba91 --- /dev/null +++ b/cw_mweb/lib/mweb_ffi.dart @@ -0,0 +1,68 @@ +import 'dart:ffi'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:cw_mweb/generated_bindings.g.dart'; +import 'package:ffi/ffi.dart'; + +String libPath = (() { + if (Platform.isWindows) return 'mweb.dll'; + if (Platform.isMacOS) return 'mweb.dylib'; + if (Platform.isIOS) return 'Mwebd.framework/Mwebd'; + if (Platform.isAndroid) return 'libmweb.so'; + return 'libmweb.so'; +})(); + +class MWebFfi { + late final MWebFlutter lib; + + MWebFfi() : lib = MWebFlutter(DynamicLibrary.open(libPath)); + + static MWebFfi instance = MWebFfi(); + + int start(String dataDir, String nodeUri) { + final chain = "".toNativeUtf8().cast(); + final dataDir_ = dataDir.toNativeUtf8().cast(); + final nodeUri_ = nodeUri.toNativeUtf8().cast(); + + final port = lib.StartServer(chain, dataDir_, nodeUri_); + + calloc.free(chain); + calloc.free(dataDir_); + calloc.free(nodeUri_); + + return port; + } + + void stop() => lib.StopServer(); + + String addresses( + Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) { + final scanSecretPtr = malloc.allocate(scanSecret.length); + for (int k = 0; k < scanSecret.length; k++) { + scanSecretPtr[k] = scanSecret[k]; + } + + final spendPubKeyPtr = malloc.allocate(spendPub.length); + for (int k = 0; k < spendPub.length; k++) { + spendPubKeyPtr[k] = spendPub[k]; + } + + final Pointer resultPtr = lib.Addresses( + scanSecretPtr.cast(), + scanSecret.length, + spendPubKeyPtr.cast(), + spendPub.length, + fromIndex, + toIndex, + ); + + final result = resultPtr.cast().toDartString(); + + malloc.free(scanSecretPtr); + malloc.free(spendPubKeyPtr); + malloc.free(resultPtr); + + return result; + } +} diff --git a/cw_mweb/pubspec.yaml b/cw_mweb/pubspec.yaml index 2a8f6b59d..300bbd590 100644 --- a/cw_mweb/pubspec.yaml +++ b/cw_mweb/pubspec.yaml @@ -15,11 +15,13 @@ dependencies: plugin_platform_interface: ^2.0.2 cw_core: path: ../cw_core + ffi: ^2.1.0 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^2.0.0 + ffigen: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c926a3d65..6de4825de 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -69,6 +69,8 @@ PODS: - Flutter - image_picker_ios (0.0.1): - Flutter + - image_picker_ios (0.0.1): + - Flutter - in_app_review (2.0.0): - Flutter - integration_test (0.0.1): diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 794305883..fcfffb4fd 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ CFEFC24F82F78FE747DF1D22 /* LnurlPayInfo.swift in Resources */ = {isa = PBXBuildFile; fileRef = 58C22CBD8C22B9D6023D59F8 /* LnurlPayInfo.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; D0D7A0D4E13F31C4E02E235B /* ReceivePayment.swift in Resources */ = {isa = PBXBuildFile; fileRef = 91C524F800843E0A3F17E004 /* ReceivePayment.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; D3AD73A327249AFE8F016A51 /* BreezSDK.swift in Resources */ = {isa = PBXBuildFile; fileRef = ABD6FCBB0F4244B090459128 /* BreezSDK.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; + D7FC97372E4F66CF0077F194 /* Mwebd.xcframework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D7FC97362E4F66CF0077F194 /* Mwebd.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; F5EE19868D6F10D814BF73AD /* SDKNotificationService.swift in Resources */ = {isa = PBXBuildFile; fileRef = 41102141140E57B1DC27FBA1 /* SDKNotificationService.swift */; settings = {ASSET_TAGS = (BreezSDK, ); }; }; /* End PBXBuildFile section */ @@ -43,6 +44,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + D7FC97372E4F66CF0077F194 /* Mwebd.xcframework in CopyFiles */, CE918BF82D533ECE007F186E /* MoneroWallet.xcframework in CopyFiles */, CE918BFA2D533ED4007F186E /* WowneroWallet.xcframework in CopyFiles */, CE918BFC2D533ED8007F186E /* ZanoWallet.xcframework in CopyFiles */, @@ -90,6 +92,7 @@ CE918BFB2D533ED8007F186E /* ZanoWallet.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; path = ZanoWallet.xcframework; sourceTree = ""; }; D139E30AEB36740C21C00A9E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; D7CD6B6020744E8FA471915D /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D7FC97362E4F66CF0077F194 /* Mwebd.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = Mwebd.xcframework; path = ../cw_mweb/go/Mwebd.xcframework; sourceTree = ""; }; DCEA540E3586164FB47AD13E /* LnurlPayInvoice.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = LnurlPayInvoice.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/Task/LnurlPayInvoice.swift"; sourceTree = ""; }; F42258C3697CFE3C8C8D1933 /* ServiceLogger.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServiceLogger.swift; path = "../.symlinks/plugins/breez_sdk/ios/bindings-swift/Sources/BreezSDK/ServiceLogger.swift"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -163,6 +166,7 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + D7FC97362E4F66CF0077F194 /* Mwebd.xcframework */, 0C44A7182518EF4A00B570ED /* CakeWallet */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, diff --git a/scripts/android/build_mwebd.sh b/scripts/android/build_mwebd.sh index 3e9c5199d..e7edc4fbb 100755 --- a/scripts/android/build_mwebd.sh +++ b/scripts/android/build_mwebd.sh @@ -6,14 +6,43 @@ else sudo rm -rf /usr/local/go && sudo tar -C /usr/local -xzf go1.24.1.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin export PATH=$PATH:~/go/bin - go install golang.org/x/mobile/cmd/gomobile@latest - gomobile init fi -# build mwebd: -git clone https://github.com/ltcmweb/mwebd -cd mwebd -git reset --hard f75cb9edef07d03360ee2d33e6edae4243a890b1 -gomobile bind -target=android -androidapi 21 . -mkdir -p ../../../cw_mweb/android/libs/ -cp ./mwebd.aar $_ \ No newline at end of file +cd ../../cw_mweb/go + +if [[ "x$ANDROID_NDK_VERSION" == "x" ]]; +then + echo "ANDROID_NDK_VERSION is missing, please declare it before building" + echo "You have these versions installed on your system currently:" + ls ${ANDROID_HOME}/ndk/ | cat | awk '{ print "- " $1 }' + echo "echo > ~/.zprofile" + echo "echo 'export ANDROID_NDK_VERSION=..... > ~/.zprofile" + exit 1 +fi + +export ANDROID_OUT=../android/src/main/jniLibs +export ANDROID_SDK="${HOME}/Library/Android/sdk" +export NDK_BIN="${ANDROID_HOME}/ndk/${ANDROID_NDK_VERSION}/toolchains/llvm/prebuilt/$(uname | tr '[:upper:]' '[:lower:]')-x86_64/bin" + +# Compile for x86_64 architecture and place the binary file in the android/src/main/jniLibs/x86_64 folder +CGO_ENABLED=1 \ +GOOS=android \ +GOARCH=amd64 \ +CC=${NDK_BIN}/x86_64-linux-android21-clang \ +go build -buildmode=c-shared -o ${ANDROID_OUT}/x86_64/libmweb.so . + +# Compile for arm64 architecture and place the binary file in the android/src/main/jniLibs/arm64-v8a folder +CGO_ENABLED=1 \ +GOOS=android \ +GOARCH=arm64 \ +CC=${NDK_BIN}/aarch64-linux-android21-clang \ +go build -buildmode=c-shared -o ${ANDROID_OUT}/arm64-v8a/libmweb.so . + +# Compile for armv7a architecture and place the binary file in the android/src/main/jniLibs/armeabi-v7a folder +CGO_ENABLED=1 \ +GOOS=android \ +GOARCH=arm \ +CC=${NDK_BIN}/armv7a-linux-androideabi21-clang \ +go build -buildmode=c-shared -o ${ANDROID_OUT}/armeabi-v7a/libmweb.so . +cd ../ +dart run ffigen --config ffigen_config.yaml diff --git a/scripts/ios/build_mwebd.sh b/scripts/ios/build_mwebd.sh index 456d43bb8..55401cc34 100755 --- a/scripts/ios/build_mwebd.sh +++ b/scripts/ios/build_mwebd.sh @@ -1,20 +1,267 @@ #!/bin/bash + +set -e -x + if [[ "$1" == "--dont-install" ]]; then echo "Skipping Go installation as per --dont-install flag" else # install go > 1.24: brew install go export PATH=$PATH:~/go/bin - go install golang.org/x/mobile/cmd/gomobile@latest - gomobile init fi -# build mwebd: -git clone https://github.com/ltcmweb/mwebd -cd mwebd -git reset --hard f75cb9edef07d03360ee2d33e6edae4243a890b1 -gomobile bind -target=ios . -mv -fn ./Mwebd.xcframework ../../../cw_mweb/ios/ -# cleanup: -cd .. -rm -rf mwebd \ No newline at end of file +BASE_DIR="$(pwd)" +MWEB_GO_DIR="${BASE_DIR}/../../cw_mweb/go" +IOS_OUTPUT_DIR="${BASE_DIR}/../../cw_mweb/ios" +TMP_DIR="${BASE_DIR}/tmp_mweb_frameworks" +FRAMEWORK_NAME="Mwebd" + +rm -rf "$TMP_DIR" +mkdir -p "$TMP_DIR" + +mkdir -p "$IOS_OUTPUT_DIR" + +write_info_plist() { + local framework_bundle="$1" + local framework_name="$2" + local target="$3" + local arch="$4" + local plist_path="${framework_bundle}/Info.plist" + + local platform min_os_version dtplatformname dtsdkname + + if [[ "$target" == "ios-simulator" ]]; then + platform="iPhoneSimulator" + dtplatformname="iphonesimulator" + dtsdkname="iphonesimulator17.4" + min_os_version="12.0" + elif [[ "$target" == "ios" ]]; then + platform="iPhoneOS" + dtplatformname="iphoneos" + dtsdkname="iphoneos17.4" + min_os_version="12.0" + else + echo "Unknown target: $target" + exit 1 + fi + + cat > "$plist_path" < + + + + BuildMachineOSBuild + 23F79 + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${framework_name} + CFBundleIdentifier + com.cakewallet.${framework_name} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${framework_name} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + CFBundleSupportedPlatforms + + ${platform} + + DTCompiler + com.apple.compilers.llvm.clang.1_0 + DTPlatformName + ${dtplatformname} + DTPlatformVersion + 17.4 + DTSDKBuild + 21E213 + DTSDKName + ${dtsdkname} + DTSDKVersion + 17.4 + DTXcode + 1530 + DTXcodeBuild + 15E204a + MinimumOSVersion + ${min_os_version} + UIDeviceFamily + + 1 + 2 + + + +EOF +} + +build_go_library() { + local goos="$1" + local goarch="$2" + local cc_target="$3" + local sdk="$4" + local output_file="$5" + + echo "Building Go library for ${goos}/${goarch} with target ${cc_target}..." + + cd "$MWEB_GO_DIR" + + export GOOS="$goos" + export GOARCH="$goarch" + export CGO_ENABLED=1 + export CGO_CFLAGS="-fembed-bitcode" + + if [[ "$sdk" == "iphoneos" ]]; then + export CC="$(xcrun -f clang) -target $cc_target -mios-version-min=12 --sysroot $(xcrun --sdk iphoneos --show-sdk-path)" + else + export CC="$(xcrun -f clang) -target $cc_target -mios-simulator-version-min=12 --sysroot $(xcrun --sdk iphonesimulator --show-sdk-path)" + fi + + go build -buildmode=c-archive -trimpath -o "$output_file" mweb.go + + echo "Built: $output_file" + cd - > /dev/null +} + +create_framework() { + local archive_path="$1" + local framework_name="$2" + local target="$3" + local out_dir="$4" + local arch="$5" + + echo "Creating ${framework_name}.framework for target ${target} (${arch}) in ${out_dir}..." + + local framework_bundle="${out_dir}/${framework_name}.framework" + + rm -rf "$framework_bundle" + mkdir -p "$framework_bundle" + + if [[ ! -f "$archive_path" ]]; then + echo "Error: Input archive not found: $archive_path" + exit 1 + fi + + local temp_dir="${TMP_DIR}/temp_${arch}" + mkdir -p "$temp_dir" + + pushd "$temp_dir" + ar x "$archive_path" + + if [[ "$target" == "ios" ]]; then + xcrun -sdk iphoneos clang -dynamiclib -arch "${arch}" -mios-version-min=12 \ + -install_name "@rpath/${framework_name}.framework/${framework_name}" \ + -framework CoreFoundation -framework Security -lresolv \ + -o "${framework_bundle}/${framework_name}" ./*.o + else + xcrun -sdk iphonesimulator clang -dynamiclib -arch "${arch}" -mios-simulator-version-min=12 \ + -install_name "@rpath/${framework_name}.framework/${framework_name}" \ + -framework CoreFoundation -framework Security -lresolv \ + -o "${framework_bundle}/${framework_name}" ./*.o + fi + popd + + echo "Created binary: ${framework_bundle}/${framework_name}" + + write_info_plist "$framework_bundle" "$framework_name" "$target" "$arch" + + mkdir -p "${framework_bundle}/Headers" + local header_file="${archive_path%.a}.h" + if [[ -f "$header_file" ]]; then + cp "$header_file" "${framework_bundle}/Headers/${framework_name}.h" + echo "Copied header: ${framework_bundle}/Headers/${framework_name}.h" + fi + + echo "Framework created: ${framework_bundle}" +} + +create_xcframework() { + local framework_name="$1" + local xcframework_output="$2" + shift 2 + local frameworks=("$@") + + echo "Creating ${xcframework_output} by bundling:" + for fw in "${frameworks[@]}"; do + echo " Framework: ${fw}" + done + + local xcodebuild_args=() + for fw in "${frameworks[@]}"; do + xcodebuild_args+=("-framework" "$fw") + done + + rm -rf "$xcframework_output" + xcodebuild -create-xcframework "${xcodebuild_args[@]}" -output "$xcframework_output" + + echo "Created XCFramework: ${xcframework_output}" +} + +echo "Building MWEB XCFramework for iOS..." + +IOS_DEVICE_OUT="${TMP_DIR}/ios_device" +IOS_SIMULATOR_ARM64_OUT="${TMP_DIR}/ios_simulator_arm64" +IOS_SIMULATOR_X86_64_OUT="${TMP_DIR}/ios_simulator_x86_64" + +mkdir -p "$IOS_DEVICE_OUT" "$IOS_SIMULATOR_ARM64_OUT" "$IOS_SIMULATOR_X86_64_OUT" + +IOS_DEVICE_ARCHIVE="${TMP_DIR}/${FRAMEWORK_NAME}_device.a" +IOS_SIMULATOR_ARM64_ARCHIVE="${TMP_DIR}/${FRAMEWORK_NAME}_sim_arm64.a" +IOS_SIMULATOR_X86_64_ARCHIVE="${TMP_DIR}/${FRAMEWORK_NAME}_sim_x86_64.a" + +build_go_library "ios" "arm64" "aarch64-apple-ios" "iphoneos" "$IOS_DEVICE_ARCHIVE" +build_go_library "ios" "arm64" "aarch64-apple-ios-simulator" "iphonesimulator" "$IOS_SIMULATOR_ARM64_ARCHIVE" +build_go_library "ios" "amd64" "x86_64-apple-ios-simulator" "iphonesimulator" "$IOS_SIMULATOR_X86_64_ARCHIVE" + +create_framework "$IOS_DEVICE_ARCHIVE" "$FRAMEWORK_NAME" "ios" "$IOS_DEVICE_OUT" "arm64" +create_framework "$IOS_SIMULATOR_ARM64_ARCHIVE" "$FRAMEWORK_NAME" "ios-simulator" "$IOS_SIMULATOR_ARM64_OUT" "arm64" +create_framework "$IOS_SIMULATOR_X86_64_ARCHIVE" "$FRAMEWORK_NAME" "ios-simulator" "$IOS_SIMULATOR_X86_64_OUT" "x86_64" + +IOS_SIMULATOR_UNIVERSAL_OUT="${TMP_DIR}/ios_simulator_universal" +mkdir -p "$IOS_SIMULATOR_UNIVERSAL_OUT" + +SIMULATOR_UNIVERSAL_FRAMEWORK="${IOS_SIMULATOR_UNIVERSAL_OUT}/${FRAMEWORK_NAME}.framework" +mkdir -p "$SIMULATOR_UNIVERSAL_FRAMEWORK" + +lipo -create \ + "${IOS_SIMULATOR_ARM64_OUT}/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" \ + "${IOS_SIMULATOR_X86_64_OUT}/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" \ + -output "${SIMULATOR_UNIVERSAL_FRAMEWORK}/${FRAMEWORK_NAME}" + +echo "Created universal simulator binary: ${SIMULATOR_UNIVERSAL_FRAMEWORK}/${FRAMEWORK_NAME}" + +cp -r "${IOS_SIMULATOR_ARM64_OUT}/${FRAMEWORK_NAME}.framework/Info.plist" "${SIMULATOR_UNIVERSAL_FRAMEWORK}/" +cp -r "${IOS_SIMULATOR_ARM64_OUT}/${FRAMEWORK_NAME}.framework/Headers" "${SIMULATOR_UNIVERSAL_FRAMEWORK}/" + +IOS_DEVICE_FRAMEWORK="${IOS_DEVICE_OUT}/${FRAMEWORK_NAME}.framework" +IOS_SIMULATOR_FRAMEWORK="${SIMULATOR_UNIVERSAL_FRAMEWORK}" + +IOS_XCFRAMEWORK="${IOS_OUTPUT_DIR}/${FRAMEWORK_NAME}.xcframework" +create_xcframework "$FRAMEWORK_NAME" "$IOS_XCFRAMEWORK" "$IOS_DEVICE_FRAMEWORK" "$IOS_SIMULATOR_FRAMEWORK" + +echo "" +echo "XCFramework created successfully:" +echo " iOS XCFramework: ${IOS_XCFRAMEWORK}" +echo "" + +FFIGEN_CONFIG="${BASE_DIR}/../../cw_mweb/ffigen_config.yaml" +if [[ -f "$FFIGEN_CONFIG" ]]; then + echo "Updating ffigen configuration..." + sed -i.bak "s|android/src/main/jniLibs/arm64-v8a/mweb.h|ios/${FRAMEWORK_NAME}.xcframework/ios-arm64/${FRAMEWORK_NAME}.framework/Headers/${FRAMEWORK_NAME}.h|g" "$FFIGEN_CONFIG" + mv "$FFIGEN_CONFIG.bak" "$FFIGEN_CONFIG" + echo "Updated ffigen config to use XCFramework header" +fi + +cd "${BASE_DIR}/../../cw_mweb" +echo "Generating Dart FFI bindings..." +dart run ffigen --config ffigen_config.yaml + +echo "Build completed successfully!" + +rm -rf "$TMP_DIR" +echo "Temporary files cleaned up." \ No newline at end of file