This commit is contained in:
Matthew Fosse 2024-09-04 15:50:56 -07:00
parent 5ec0e264ce
commit 7b5c36e5a7
7 changed files with 185 additions and 128 deletions

View file

@ -1,4 +1,6 @@
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/utils.dart';
import 'package:cw_bitcoin/electrum_wallet_addresses.dart';
@ -7,88 +9,91 @@ 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';
// import 'dart:typed_data';
// import 'package:bech32/bech32.dart';
// import 'package:r_crypto/r_crypto.dart';
part 'litecoin_wallet_addresses.g.dart';
// class Keychain {
// // ECPrivate scan;
// // ECPrivate? spend;
// ECPrivate scan;
// ECPrivate? spend;
// ECPublic? spendPubKey;
// Keychain({required this.scan, this.spend, this.spendPubKey}) {
// if (this.spend != null) {
// spendPubKey = this.spend!.getPublic();
// }
// }
// static const HashTagAddress = 'A';
class Keychain {
final ECPrivate scan;
ECPrivate? spend;
ECPublic? spendPubKey;
// ECPrivate mi(int index) {
// final input = BytesBuilder();
// // Write HashTagAddress to the input
// input.addByte(HashTagAddress.codeUnitAt(0));
Keychain({required this.scan, this.spend, this.spendPubKey}) {
if (this.spend != null) {
spendPubKey = this.spend!.getPublic();
}
}
// // Write index to the input in little endian
// final indexBytes = Uint8List(4);
// final byteData = ByteData.view(indexBytes.buffer);
// byteData.setUint32(0, index, Endian.little);
// input.add(indexBytes);
// // Write scan to the input
// input.add(scan.prive.raw);
static const HashTagAddress = 'A';
// // Hash the input using Blake3 with a length of 32 bytes
// final hash = rHash.hashString(HashType.blake3(length: 32), input.toString());
ECPrivate mi(int index) {
final input = BytesBuilder();
// // Return the hash digest
// var res = Uint8List.fromList(hash);
// return ECPrivate.fromBytes(res);
// }
// Write HashTagAddress to the input
input.addByte(HashTagAddress.codeUnitAt(0));
// Keychain address(int index) {
// Write index to the input in little endian
final indexBytes = Uint8List(4);
final byteData = ByteData.view(indexBytes.buffer);
byteData.setUint32(0, index, Endian.little);
input.add(indexBytes);
// final miPub = this.mi(index).getPublic();
// final Bi = spendPubKey!.pubkeyAdd(miPub);
// // final Ai = Bi.pubkeyMult(ECPublic.fromBytes(scan.toBytes()));
// final Ai = Bi.tweakMul(scan.toBigInt());
// Write scan to the input
input.add(scan.prive.raw);
// // final miPubKey = ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16);
// // final Bi = spendPubKey + miPubKey;
// // return Uint8List.fromList(Ai.getEncoded(compressed: true) + Bi.getEncoded(compressed: true));
// final AiPriv = ECPrivate.fromBytes(Ai.toBytes());
// final BiPriv = ECPrivate.fromBytes(Bi.toBytes());
// Hash the input using Blake3 with a length of 32 bytes
final hash = rHash.hashString(HashType.blake3(length: 32), input.toString());
// return Keychain(scan: AiPriv, spend: BiPriv);
// }
// Return the hash digest
var res = Uint8List.fromList(hash);
return ECPrivate.fromBytes(res);
}
// String addressString(int index) {
// final address = this.address(index);
// List<int> bytes = [];
// bytes.addAll(address.scan.toBytes());
// bytes.addAll(address.spend!.toBytes());
// return encodeMwebAddress(bytes);
// }
Keychain address(int index) {
// // Uint8List spendKey(int index) {
// // final mi = this.mi(index);
// // final spendKey = spend + ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16);
// // return spendKey.getEncoded(compressed: true);
// // }
final miPub = this.mi(index).getPublic();
final Bi = spendPubKey!.pubkeyAdd(miPub);
final Ai = Bi.pubkeyMult(ECPublic.fromBytes(scan.toBytes()));
// final miPubKey = ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16);
// final Bi = spendPubKey + miPubKey;
// return Uint8List.fromList(Ai.getEncoded(compressed: true) + Bi.getEncoded(compressed: true));
final AiPriv = ECPrivate.fromBytes(Ai.toBytes());
final BiPriv = ECPrivate.fromBytes(Bi.toBytes());
// String encodeMwebAddress(List<int> scriptPubKey) {
// return bech32.encode(Bech32("ltcmweb", scriptPubKey));
// }
// }
return Keychain(scan: AiPriv, spend: BiPriv);
}
String addressString(int index) {
final address = this.address(index);
List<int> bytes = [];
bytes.addAll(address.scan.toBytes());
bytes.addAll(address.spend!.toBytes());
return encodeMwebAddress(bytes);
}
// Uint8List spendKey(int index) {
// final mi = this.mi(index);
// final spendKey = spend + ECCurve_secp256k1().G * BigInt.parse(hex.encode(mi), radix: 16);
// return spendKey.getEncoded(compressed: true);
// }
String encodeMwebAddress(List<int> scriptPubKey) {
return bech32.encode(Bech32("ltcmweb", scriptPubKey));
}
String encodeMwebAddress(List<int> scriptPubKey) {
return bech32.encode(Bech32("ltcmweb1", scriptPubKey), 250);
}
class LitecoinWalletAddresses = LitecoinWalletAddressesBase with _$LitecoinWalletAddresses;
abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with Store {
LitecoinWalletAddressesBase(
WalletInfo walletInfo, {
@ -119,7 +124,6 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
List<String> mwebAddrs = [];
List<String> oldMwebAddrs = [];
Future<void> topUpMweb(int index) async {
final stub = await CwMweb.stub();
while (oldMwebAddrs.length - index < 1000) {
@ -135,16 +139,19 @@ abstract class LitecoinWalletAddressesBase extends ElectrumWalletAddresses with
}
}
Keychain k = Keychain(scan: ECPrivate.fromBytes(scanSecret), spendPubKey: ECPublic.fromBytes(spendPubkey),);
// Keychain k = Keychain(scan: ECPrivate.fromBytes(scanSecret), spendPubKey: ECPublic.fromBytes(spendPubkey),);
for (int i = 0; i < 10; i++) {
final address = k.addressString(i + 1000);
// final address = k.addressString(i + 1000);
final addressHex =
await CwMweb.address(hex.encode(scanSecret), hex.encode(spendPubkey), index);
// print(addressHex);
// print(hex.decode(addressHex!).length);
// return;
final address = encodeMwebAddress(hex.decode(addressHex!));
mwebAddrs.add(address);
}
print("old function: ${oldMwebAddrs.first} new function!: ${mwebAddrs.first}");
}
@override

View file

@ -40,6 +40,12 @@ class CwMwebPlugin: FlutterPlugin, MethodCallHandler {
server = null
port = null
result.success(null)
} else if (call.method == "address") {
val scanSecret: String = call.argument<String>("scanSecret") ?: ""
val spendPub: String = call.argument<String>("spendPub") ?: ""
val index: Int = call.argument<Int>("index") ?: 0
val res = Mwebd.addressIndex(scanSecret, spendPub)
result.success(res)
} else {
result.notImplemented()
}

View file

@ -3,70 +3,90 @@ 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: String]
let scanSecret = args?["scanSecret"]
let spendPub = args?["spendPub"]
let index = args?["index"]
result(address(scanSecret, spendPub, 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
}
private func address(_ scanSecret: String?, _ spendPub: String?, _ index: Int?) -> String? {
guard let scanSecret = scanSecret, let spendPub = spendPub, let index = index else {
print("Invalid arguments for address function")
return nil
}
return MwebdAddressIndex(scanSecret, spendPub, UInt32(index))
}
deinit {
stopServer()
}
}

View file

@ -53,6 +53,15 @@ class CwMweb {
await cleanup();
}
static Future<String?> address(String scanSecret, String 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(String scanSecret, String spendPub, int index) async {
final result = await methodChannel.invokeMethod<String>('address', {
'scanSecret': scanSecret,
'spendPub': spendPub,
'index': index,
});
return result;
}
}

View file

@ -30,4 +30,8 @@ abstract class CwMwebPlatform extends PlatformInterface {
Future<void> stop() {
throw UnimplementedError('stop() has not been implemented.');
}
Future<String?> address(String scanSecret, String spendPub, int index) {
throw UnimplementedError('address(int) has not been implemented.');
}
}

View file

@ -136,6 +136,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"