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 <cyjan@mrcyjanek.net>

* 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 <cyjan@mrcyjanek.net>
This commit is contained in:
Konstantin Ullrich
2025-09-12 16:13:00 +03:00
committed by GitHub
co-authored by cyan
parent 52e778f275
commit 309ad1836e
20 changed files with 607 additions and 272 deletions
@@ -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
@@ -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
@@ -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));
-12
View File
@@ -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')
}
@@ -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<ByteArray>("scanSecret") ?: ByteArray(0)
// val spendPub: ByteArray = call.argument<ByteArray>("spendPub") ?: ByteArray(0)
// val index: Int = call.argument<Int>("index") ?: 0
// val res = Mwebd.address(scanSecret, spendPub, index)
// result.success(res)
} else if (call.method == "addresses") {
val scanSecret: ByteArray = call.argument<ByteArray>("scanSecret") ?: ByteArray(0)
val spendPub: ByteArray = call.argument<ByteArray>("spendPub") ?: ByteArray(0)
val fromIndex: Int = call.argument<Int>("fromIndex") ?: 0
val toIndex: Int = call.argument<Int>("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
}
}
+8
View File
@@ -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'
+50
View File
@@ -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
)
+97
View File
@@ -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=
+58
View File
@@ -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() {}
+1 -86
View File
@@ -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)
}
}
+10 -13
View File
@@ -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<void> 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<String?> 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<List<String>?> addresses(
Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) async {
static List<String>? 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;
}
-58
View File
@@ -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<int?> start(String dataDir, String nodeUri) async {
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
return null;
}
final result =
await methodChannel.invokeMethod<int>('start', {'dataDir': dataDir, 'nodeUri': nodeUri});
return result;
}
@override
Future<void> stop() async {
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
return;
}
await methodChannel.invokeMethod<void>('stop');
}
@override
Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) async {
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
return null;
}
final result = await methodChannel.invokeMethod<String>('address', {
'scanSecret': scanSecret,
'spendPub': spendPub,
'index': index,
});
return result;
}
@override
Future<String?> addresses(Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) async {
if (Platform.isLinux || Platform.isMacOS || Platform.isWindows) {
return null;
}
final result = await methodChannel.invokeMethod<String>('addresses', {
'scanSecret': scanSecret,
'spendPub': spendPub,
'fromIndex': fromIndex,
'toIndex': toIndex,
});
return result;
}
}
@@ -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<int?> start(String dataDir, String nodeUri) {
throw UnimplementedError('start() has not been implemented.');
}
Future<void> stop() {
throw UnimplementedError('stop() has not been implemented.');
}
Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) {
throw UnimplementedError('address(int) has not been implemented.');
}
Future<String?> addresses(Uint8List scanSecret, Uint8List spendPub, int fromIndex, int toIndex) {
throw UnimplementedError('addresses has not been implemented.');
}
}
+68
View File
@@ -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<Char>();
final dataDir_ = dataDir.toNativeUtf8().cast<Char>();
final nodeUri_ = nodeUri.toNativeUtf8().cast<Char>();
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<Uint8>(scanSecret.length);
for (int k = 0; k < scanSecret.length; k++) {
scanSecretPtr[k] = scanSecret[k];
}
final spendPubKeyPtr = malloc.allocate<Uint8>(spendPub.length);
for (int k = 0; k < spendPub.length; k++) {
spendPubKeyPtr[k] = spendPub[k];
}
final Pointer<Char> resultPtr = lib.Addresses(
scanSecretPtr.cast(),
scanSecret.length,
spendPubKeyPtr.cast(),
spendPub.length,
fromIndex,
toIndex,
);
final result = resultPtr.cast<Utf8>().toDartString();
malloc.free(scanSecretPtr);
malloc.free(spendPubKeyPtr);
malloc.free(resultPtr);
return result;
}
}
+2
View File
@@ -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
+2
View File
@@ -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):
+4
View File
@@ -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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
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 = "<group>"; };
/* End PBXFileReference section */
@@ -163,6 +166,7 @@
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
D7FC97362E4F66CF0077F194 /* Mwebd.xcframework */,
0C44A7182518EF4A00B570ED /* CakeWallet */,
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
+38 -9
View File
@@ -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 $_
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
+258 -11
View File
@@ -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
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" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>BuildMachineOSBuild</key>
<string>23F79</string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${framework_name}</string>
<key>CFBundleIdentifier</key>
<string>com.cakewallet.${framework_name}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${framework_name}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>${platform}</string>
</array>
<key>DTCompiler</key>
<string>com.apple.compilers.llvm.clang.1_0</string>
<key>DTPlatformName</key>
<string>${dtplatformname}</string>
<key>DTPlatformVersion</key>
<string>17.4</string>
<key>DTSDKBuild</key>
<string>21E213</string>
<key>DTSDKName</key>
<string>${dtsdkname}</string>
<key>DTSDKVersion</key>
<string>17.4</string>
<key>DTXcode</key>
<string>1530</string>
<key>DTXcodeBuild</key>
<string>15E204a</string>
<key>MinimumOSVersion</key>
<string>${min_os_version}</string>
<key>UIDeviceFamily</key>
<array>
<integer>1</integer>
<integer>2</integer>
</array>
</dict>
</plist>
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."