Merge branch 'mweb' of https://github.com/cake-tech/cake_wallet into mweb-bg-sync-2

This commit is contained in:
Matthew Fosse 2024-09-05 14:49:56 -07:00
commit a87a02891f
14 changed files with 180 additions and 90 deletions

View file

@ -113,15 +113,13 @@ jobs:
cd /opt/android/cake_wallet
git clone https://github.com/ltcmweb/mwebd
cd /opt/android/cake_wallet/mwebd
git reset --hard 49c42597ce5036fe1065200c3c056d0aba5f1a58
git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3
gomobile bind -target=android -androidapi 21 .
mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/
mv ./mwebd.aar $_
cd ..
rm -rf mwebd
- name: Generate KeyStore
run: |
cd /opt/android/cake_wallet/android/app

View file

@ -99,7 +99,7 @@ jobs:
go install golang.org/x/mobile/cmd/gomobile@latest
gomobile init
- name: Build mwebd TODO for linux!
- name: Build mwebd
run: |
# paths are reset after each step, so we need to set them again:
export PATH=$PATH:/usr/local/go/bin
@ -108,6 +108,7 @@ jobs:
cd /opt/android/cake_wallet
git clone https://github.com/ltcmweb/mwebd
cd /opt/android/cake_wallet/mwebd
git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3
gomobile bind -target=android -androidapi 21 .
mkdir -p /opt/android/cake_wallet/cw_mweb/android/libs/
mv ./mwebd.aar $_

View file

@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:isolate';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:cw_bitcoin/litecoin_wallet_addresses.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:cw_core/encryption_file_utils.dart';
import 'package:blockchain_utils/blockchain_utils.dart';
@ -795,7 +796,10 @@ abstract class ElectrumWalletBase
throw BitcoinTransactionWrongBalanceException();
}
final changeAddress = await walletAddresses.getChangeAddress();
final changeAddress = await walletAddresses.getChangeAddress(
outputs: outputs,
utxoDetails: utxoDetails,
);
final address = addressTypeFromStr(changeAddress, network);
outputs.add(BitcoinOutput(
address: address,
@ -2061,7 +2065,8 @@ abstract class ElectrumWalletBase
_isTryingToConnect = true;
Timer(Duration(seconds: 10), () {
if (this.syncStatus is NotConnectedSyncStatus || this.syncStatus is LostConnectionSyncStatus) {
if (this.syncStatus is NotConnectedSyncStatus ||
this.syncStatus is LostConnectionSyncStatus) {
this.electrumClient.connectToUri(
node!.uri,
useSSL: node!.useSSL ?? false,
@ -2387,6 +2392,8 @@ class PublicKeyWithDerivationPath {
}
BitcoinBaseAddress addressTypeFromStr(String address, BasedUtxoNetwork network) {
// print("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
// print(network);
if (network is BitcoinCashNetwork) {
if (!address.startsWith("bitcoincash:") &&
(address.startsWith("q") || address.startsWith("p"))) {

View file

@ -1,6 +1,7 @@
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/electrum_wallet.dart';
import 'package:cw_core/wallet_addresses.dart';
import 'package:cw_core/wallet_info.dart';
import 'package:cw_core/wallet_type.dart';
@ -239,7 +240,7 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store {
}
@action
Future<String> getChangeAddress() async {
Future<String> getChangeAddress({List<BitcoinOutput>? outputs, UtxoDetails? utxoDetails}) async {
updateChangeAddresses();
if (changeAddresses.isEmpty) {

View file

@ -1,6 +1,10 @@
import 'package:convert/convert.dart';
import 'dart:typed_data';
import 'package:bech32/bech32.dart';
import 'package:bitcoin_base/bitcoin_base.dart';
import 'package:blockchain_utils/bech32/bech32_base.dart';
import 'package:blockchain_utils/blockchain_utils.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/wallet_info.dart';
@ -8,8 +12,16 @@ import 'package:cw_mweb/cw_mweb.dart';
import 'package:cw_mweb/mwebd.pb.dart';
import 'package:mobx/mobx.dart';
// import 'dart:typed_data';
// import 'package:bech32/bech32.dart';
// import 'package:r_crypto/r_crypto.dart';
part 'litecoin_wallet_addresses.g.dart';
String encodeMwebAddress(List<int> scriptPubKey) {
return bech32.encode(Bech32("ltcmweb1", scriptPubKey), 250);
}
class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses;
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
@ -42,18 +54,15 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
List<String> mwebAddrs = [];
Future<void> topUpMweb(int index) async {
final stub = await CwMweb.stub();
// generate up to index + 1000 addresses:
while (mwebAddrs.length - index < 1000) {
final length = mwebAddrs.length;
final resp = await stub.addresses(AddressRequest(
fromIndex: length,
toIndex: index + 1000,
scanSecret: scanSecret,
spendPubkey: spendPubkey,
));
if (mwebAddrs.length == length) {
mwebAddrs.addAll(resp.address);
}
final address = await CwMweb.address(
Uint8List.fromList(scanSecret),
Uint8List.fromList(spendPubkey),
length,
);
mwebAddrs.add(address!);
}
}
@ -63,7 +72,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) {
if (addressType == SegwitAddresType.mweb && mwebEnabled) {
if (addressType == SegwitAddresType.mweb) {
topUpMweb(index);
return hd == sideHd ? mwebAddrs[0] : mwebAddrs[index + 1];
}
@ -76,11 +85,7 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
required Bip32Slip10Secp256k1 hd,
BitcoinAddressType? addressType,
}) async {
// if mweb isn't enabled we'll just return the regular address type which does effectively nothing
// sort of a hack but easier than trying to pull the mweb setting into the electrum_wallet_addresses initialization code
// (we want to avoid initializing the mweb.stub() if it's not enabled or we'd be starting the whole server for no reason and it's slow)
// TODO: find a way to do address generation without starting the whole mweb server
if (addressType == SegwitAddresType.mweb && mwebEnabled) {
if (addressType == SegwitAddresType.mweb) {
await topUpMweb(index);
}
return getAddress(index: index, hd: hd, addressType: addressType);
@ -88,11 +93,38 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
@action
@override
Future<String> getChangeAddress() async {
Future<String> getChangeAddress({List<BitcoinOutput>? outputs, UtxoDetails? utxoDetails}) async {
// use regular change address on peg in, otherwise use mweb for change address:
if (outputs != null && utxoDetails != null) {
// check if this is a PEGIN:
bool outputsToMweb = false;
bool comesFromMweb = false;
for (var i = 0; i < outputs.length; i++) {
// TODO: probably not the best way to tell if this is an mweb address
// (but it doesn't contain the "mweb" text at this stage)
if (outputs[i].address.toAddress(network).length > 110) {
outputsToMweb = true;
}
}
utxoDetails.availableInputs.forEach((element) {
if (element.address.contains("mweb")) {
comesFromMweb = true;
}
});
bool isPegIn = !comesFromMweb && outputsToMweb;
if (isPegIn && mwebEnabled) {
return super.getChangeAddress();
}
}
if (mwebEnabled) {
await topUpMweb(0);
return mwebAddrs[0];
}
return super.getChangeAddress();
}
}

View file

@ -41,6 +41,9 @@ dependencies:
git:
url: https://github.com/rafael-xmr/sp_scanner
ref: sp_v4.0.0
bech32:
git:
url: https://github.com/cake-tech/bech32.git
dev_dependencies:
flutter_test:
@ -62,6 +65,7 @@ dependency_overrides:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v6
pointycastle: 3.7.4
ffi: 2.1.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

View file

@ -30,8 +30,6 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
if (call.method == "start") {
server?.stop()
val dataDir = call.argument("dataDir") ?: ""
// server = server ?: Mwebd.newServer("", dataDir, "")
// port = port ?: server?.start(0)
server = server ?: Mwebd.newServer("", dataDir, "")
port = server?.start(0)
result.success(port)
@ -40,6 +38,12 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
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 {
result.notImplemented()
}

View file

@ -3,70 +3,84 @@ import UIKit
import Mwebd
public class CwMwebPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "cw_mweb", binaryMessenger: registrar.messenger())
let instance = CwMwebPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "cw_mweb", binaryMessenger: registrar.messenger())
let instance = CwMwebPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
private static var server: MwebdServer?
private static var port: Int = 0
private static var dataDir: String?
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
switch call.method {
case "getPlatformVersion":
result("iOS " + UIDevice.current.systemVersion)
case "start":
stopServer()
let args = call.arguments as? [String: String]
let dataDir = args?["dataDir"]
CwMwebPlugin.dataDir = dataDir
startServer(result: result)
case "stop":
stopServer()
result(nil)
default:
result(FlutterMethodNotImplemented)
}
}
private static var server: MwebdServer?
private static var port: Int = 0
private static var dataDir: String?
private func startServer(result: @escaping FlutterResult) {
if CwMwebPlugin.server == nil {
var error: NSError?
CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, "", &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)
}
}
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"]
CwMwebPlugin.dataDir = dataDir
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
default:
result(FlutterMethodNotImplemented)
break
}
}
private func stopServer() {
print("Stopping server")
CwMwebPlugin.server?.stop()
CwMwebPlugin.server = nil
CwMwebPlugin.port = 0
}
private func startServer(result: @escaping FlutterResult) {
if CwMwebPlugin.server == nil {
var error: NSError?
CwMwebPlugin.server = MwebdNewServer("", CwMwebPlugin.dataDir, "", &error)
deinit {
stopServer()
}
}
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()
}
}

View file

@ -1,4 +1,5 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:grpc/grpc.dart';
import 'package:path_provider/path_provider.dart';
@ -53,6 +54,15 @@ class CwMweb {
await cleanup();
}
static Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) async {
// try {
// return (await CwMwebPlatform.instance.address(scan, spendPub, index))!;
// } catch (e) {
// print("error generating address!: $e");
// }
return CwMwebPlatform.instance.address(scanSecret, spendPub, index);
}
static Future<void> cleanup() async {
await _clientChannel?.terminate();
_rpcClient = null;

View file

@ -19,4 +19,14 @@ class MethodChannelCwMweb extends CwMwebPlatform {
Future<void> stop() async {
await methodChannel.invokeMethod<void>('stop');
}
@override
Future<String?> address(Uint8List scanSecret, Uint8List spendPub, int index) async {
final result = await methodChannel.invokeMethod<String>('address', {
'scanSecret': scanSecret,
'spendPub': spendPub,
'index': index,
});
return result;
}
}

View file

@ -1,3 +1,5 @@
import 'dart:typed_data';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'cw_mweb_method_channel.dart';
@ -30,4 +32,8 @@ abstract class CwMwebPlatform extends PlatformInterface {
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.');
}
}

View file

@ -138,6 +138,7 @@ dependency_overrides:
git:
url: https://github.com/cake-tech/bitcoin_base
ref: cake-update-v6
ffi: 2.1.0
flutter_icons:
image_path: "assets/images/app_logo.png"

View file

@ -8,6 +8,7 @@ gomobile init
# build mwebd:
git clone https://github.com/ltcmweb/mwebd
cd mwebd
git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3
gomobile bind -target=android -androidapi 21 .
mkdir -p ../../../cw_mweb/android/libs/
mv ./mwebd.aar $_

View file

@ -7,6 +7,7 @@ gomobile init
# build mwebd:
git clone https://github.com/ltcmweb/mwebd
cd mwebd
git reset --hard 7f31c84eeb2e954f2c5f385b39db3b8e3b6389e3
gomobile bind -target=ios .
mv -fn ./Mwebd.xcframework ../../../ios/
# cleanup: