feat: beginning isolate, add compute

This commit is contained in:
Rafael Saes 2023-11-15 18:01:18 -03:00
parent 06d9231f8d
commit 41f2e4ab33
3 changed files with 237 additions and 121 deletions

View file

@ -145,6 +145,10 @@ abstract class ElectrumWalletBase
@action
@override
Future<void> startSync() async {
compute(test, {
"scanPrivateKeyCompressed": walletAddresses.silentAddress!.scanPrivkey.toCompressedHex(),
"spendPubkeyCompressed": walletAddresses.silentAddress!.spendPubkey.toCompressedHex()
});
try {
syncStatus = AttemptingSyncStatus();
await walletAddresses.discoverAddresses();
@ -349,13 +353,11 @@ abstract class ElectrumWalletBase
generatedOutputs.forEach((recipientSilentAddress, generatedOutput) {
generatedOutput.forEach((output) {
final generatedPubkey = output.$1.toCompressedHex();
txb.addOutput(
bitcoin.ECPublic.fromHex(generatedPubkey)
.toTaprootAddress()
.toScriptPubKey()
.toBytes(),
amount);
final generatedPubkey = output.$1.toHex();
// TODO: pubkeyToOutputScript (?)
final point = bitcoin.ECPublic.fromHex(generatedPubkey).toTapPoint();
final p2tr = bitcoin.P2trAddress(program: point);
txb.addOutput(p2tr.toScriptPubKey().toBytes(), amount);
});
});
}
@ -525,69 +527,6 @@ abstract class ElectrumWalletBase
return null;
}
}).whereNotNull())));
final txid = "28a21e8b6373bf20928107f8f1f1ea5a98ada793f2676372d03466e874d4e762";
final uri = Uri(scheme: 'https', host: 'blockstream.info', path: '/testnet/api/tx/$txid');
// final uri = Uri(scheme: 'https', host: 'blockstream.info', path: 'testnet/api/block-height/0');
await http.get(uri).then((response) {
// print(response.body);
final obj = json.decode(response.body);
final scanPrivateKey = walletAddresses.silentAddress!.scanPrivkey;
final spendPublicKey = walletAddresses.silentAddress!.spendPubkey;
List<String> pubkeys = [];
List<bitcoin.Outpoint> outpoints = [];
obj["vin"].forEach((input) {
final witness = input["witness"] as List<dynamic>;
final pubkey = witness[1] as String;
pubkeys.add(pubkey);
outpoints.add(bitcoin.Outpoint(txid: input["txid"] as String, index: input["vout"] as int));
});
Uint8List sumOfInputPublicKeys =
bitcoin.getSumInputPubKeys(pubkeys).toCompressedHex().fromHex;
final outpointHash = bitcoin.SilentPayment.hashOutpoints(outpoints);
Map<String, bitcoin.Outpoint> outpointsByP2TRpubkey = {};
int i = 0;
obj['vout'].forEach((out) {
if (out["scriptpubkey_type"] == "v1_p2tr")
outpointsByP2TRpubkey[out['scriptpubkey_address'] as String] =
bitcoin.Outpoint(txid: txid, index: i, value: out["value"] as int);
i++;
});
final result = bitcoin.scanOutputs(
scanPrivateKey.toCompressedHex().fromHex,
spendPublicKey.toCompressedHex().fromHex,
sumOfInputPublicKeys,
outpointHash,
outpointsByP2TRpubkey.keys.toList());
// print(result);
result.forEach((key, value) {
final outpoint = outpointsByP2TRpubkey[key];
// if (outpoint != null) {
// unspentCoins.add(BitcoinUnspent(
// BitcoinAddressRecord(walletAddresses.silentAddress.toString(), index: 0),
// outpoint.txid,
// outpoint.value!,
// outpoint.n));
// final currentBalance = balance[currency];
// if (currentBalance != null) {
// balance[currency] = ElectrumBalance(
// confirmed: currentBalance.confirmed + outpoint.value!,
// unconfirmed: currentBalance.unconfirmed,
// frozen: currentBalance.frozen);
// } else {
// balance[currency] =
// ElectrumBalance(confirmed: outpoint.value!, unconfirmed: 0, frozen: 0);
// }
// }
});
});
unspentCoins = unspent.expand((e) => e).toList();
if (unspentCoinsInfo.isEmpty) {
@ -848,7 +787,7 @@ abstract class ElectrumWalletBase
}
// int _getHeightByDate(DateTime date) {
// final nodeHeight = monero_wallet.getNodeHeightSync();
// final currentHeight = await electrumClient.getCurrentBlockChainTip();
// final heightDistance = _getHeightDistance(date);
// if (nodeHeight <= 0) {
@ -868,10 +807,73 @@ abstract class ElectrumWalletBase
final currentHeight = await electrumClient.getCurrentBlockChainTip();
if (currentHeight == null) return;
// if (currentHeight <= 1) {
// final height = _getHeightByDate(walletInfo.date);
// monero_wallet.setRecoveringFromSeed(isRecovery: true);
// monero_wallet.setRefreshFromBlockHeight(height: height);
// }
if (currentHeight <= 1) {
// final height = _getHeightByDate(walletInfo.date);
// monero_wallet.setRecoveringFromSeed(isRecovery: true);
// monero_wallet.setRefreshFromBlockHeight(height: height);
}
}
}
void test(Map<String, String> addrs) async {
final txid = "920244856cba9d8421ea01f3930baec80642b2b908c0e83a7e6c918040d5656e";
final uri = Uri(scheme: 'https', host: 'blockstream.info', path: '/testnet/api/tx/$txid');
// final uri = Uri(scheme: 'https', host: 'blockstream.info', path: 'testnet/api/block-height/0');
await http.get(uri).then((response) {
// print(response.body);
final obj = json.decode(response.body);
List<String> pubkeys = [];
List<bitcoin.Outpoint> outpoints = [];
obj["vin"].forEach((input) {
final witness = input["witness"] as List<dynamic>;
final pubkey = witness[1] as String;
pubkeys.add(pubkey);
outpoints.add(bitcoin.Outpoint(txid: input["txid"] as String, index: input["vout"] as int));
});
Uint8List sumOfInputPublicKeys = bitcoin.getSumInputPubKeys(pubkeys).toCompressedHex().fromHex;
final outpointHash = bitcoin.SilentPayment.hashOutpoints(outpoints);
Map<String, bitcoin.Outpoint> outpointsByP2TRpubkey = {};
int i = 0;
obj['vout'].forEach((out) {
if (out["scriptpubkey_type"] == "v1_p2tr")
outpointsByP2TRpubkey[out['scriptpubkey_address'] as String] =
bitcoin.Outpoint(txid: txid, index: i, value: out["value"] as int);
i++;
});
final result = bitcoin.scanOutputs(
(addrs["scanPrivateKeyCompressed"] as String).fromHex,
(addrs["spendPubkeyCompressed"] as String).fromHex,
sumOfInputPublicKeys,
outpointHash,
outpointsByP2TRpubkey.keys.toList());
print(["RESULT:", result]);
result.forEach((key, value) {
final outpoint = outpointsByP2TRpubkey[key];
// if (outpoint != null) {
// unspentCoins.add(BitcoinUnspent(
// BitcoinAddressRecord(walletAddresses.silentAddress.toString(), index: 0),
// outpoint.txid,
// outpoint.value!,
// outpoint.n));
// final currentBalance = balance[currency];
// if (currentBalance != null) {
// balance[currency] = ElectrumBalance(
// confirmed: currentBalance.confirmed + outpoint.value!,
// unconfirmed: currentBalance.unconfirmed,
// frozen: currentBalance.frozen);
// } else {
// balance[currency] =
// ElectrumBalance(confirmed: outpoint.value!, unconfirmed: 0, frozen: 0);
// }
// }
});
});
}

View file

@ -44,11 +44,9 @@ packages:
bech32:
dependency: transitive
description:
path: "."
ref: "cake-0.2.2"
resolved-ref: "05755063b593aa6cca0a4820a318e0ce17de6192"
url: "https://github.com/cake-tech/bech32.git"
source: git
path: "/home/rafael/Storage/Repositories/bech32"
relative: false
source: path
version: "0.2.2"
bip32:
dependency: transitive
@ -82,6 +80,13 @@ packages:
relative: false
source: path
version: "2.0.2"
blockchain_utils:
dependency: transitive
description:
path: "/home/rafael/Storage/Repositories/blockchain_utils"
relative: false
source: path
version: "1.0.3"
boolean_selector:
dependency: transitive
description:
@ -253,10 +258,9 @@ packages:
dart_bech32:
dependency: transitive
description:
name: dart_bech32
sha256: "0e1dc1ff39c9669c9ffeafd5d675104918f7b50799692491badfea7e1fb40888"
url: "https://pub.dev"
source: hosted
path: "/home/rafael/Storage/Repositories/dart-bech32"
relative: false
source: path
version: "2.0.0"
dart_style:
dependency: transitive
@ -803,5 +807,5 @@ packages:
source: hosted
version: "3.1.2"
sdks:
dart: ">=3.0.0 <4.0.0"
dart: ">=3.0.6 <4.0.0"
flutter: ">=3.7.0"

View file

@ -16,6 +16,7 @@ import 'package:cake_wallet/main.dart';
import 'package:cake_wallet/di.dart';
const moneroSyncTaskKey = "com.fotolockr.cakewallet.monero_sync_task";
const bitcoinSilentPaymentsSyncTaskKey = "com.fotolockr.cakewallet.bitcoin_sync_task";
@pragma('vm:entry-point')
void callbackDispatcher() {
@ -68,6 +69,62 @@ void callbackDispatcher() {
return Future.error("No Monero wallet found");
}
for (int i = 0;; i++) {
await Future<void>.delayed(const Duration(seconds: 1));
if (wallet?.syncStatus.progress() == 1.0) {
break;
}
if (i > 600) {
return Future.error("Synchronization Timed out");
}
}
break;
case bitcoinSilentPaymentsSyncTaskKey:
/// The work manager runs on a separate isolate from the main flutter isolate.
/// thus we initialize app configs first; hive, getIt, etc...
await initializeAppConfigs();
final walletLoadingService = getIt.get<WalletLoadingService>();
final node = getIt.get<SettingsStore>().getCurrentNode(WalletType.bitcoin);
final typeRaw = getIt.get<SharedPreferences>().getInt(PreferencesKey.currentWalletType);
WalletBase? wallet;
if (inputData!['sync_all'] as bool) {
// /// get all Monero wallets of the user and sync them
// final List<WalletListItem> moneroWallets = getIt
// .get<WalletListViewModel>()
// .wallets
// .where((element) => element.type == WalletType.monero)
// .toList();
// for (int i = 0; i < moneroWallets.length; i++) {
// wallet = await walletLoadingService.load(WalletType.monero, moneroWallets[i].name);
// await wallet.connectToNode(node: node);
// await wallet.startSync();
// }
} else {
/// if the user chose to sync only active wallet
/// if the current wallet is monero; sync it only
if (typeRaw == WalletType.bitcoin.index) {
final name =
getIt.get<SharedPreferences>().getString(PreferencesKey.currentWalletName);
wallet = await walletLoadingService.load(WalletType.bitcoin, name!);
await wallet.startSync();
}
}
if (wallet?.syncStatus.progress() == null) {
return Future.error("No Bitcoin wallet with silent payments enabled found");
}
for (int i = 0;; i++) {
await Future<void>.delayed(const Duration(seconds: 1));
if (wallet?.syncStatus.progress() == 1.0) {
@ -98,55 +155,108 @@ class BackgroundTasks {
.any((element) => element.type == WalletType.monero);
/// if its not android nor ios, or the user has no monero wallets; exit
if (!DeviceInfo.instance.isMobile || !hasMonero) {
return;
}
if (DeviceInfo.instance.isMobile && hasMonero) {
final settingsStore = getIt.get<SettingsStore>();
final settingsStore = getIt.get<SettingsStore>();
final SyncMode syncMode = settingsStore.currentSyncMode;
final bool syncAll = settingsStore.currentSyncAll;
final SyncMode syncMode = settingsStore.currentSyncMode;
final bool syncAll = settingsStore.currentSyncAll;
if (syncMode.type == SyncType.disabled) {
cancelSyncTask();
return;
}
if (syncMode.type == SyncType.disabled) {
cancelSyncTask();
return;
}
await Workmanager().initialize(
callbackDispatcher,
isInDebugMode: kDebugMode,
);
await Workmanager().initialize(
callbackDispatcher,
isInDebugMode: kDebugMode,
);
final inputData = <String, dynamic>{"sync_all": syncAll};
final constraints = Constraints(
networkType:
syncMode.type == SyncType.unobtrusive ? NetworkType.unmetered : NetworkType.connected,
requiresBatteryNotLow: syncMode.type == SyncType.unobtrusive,
requiresCharging: syncMode.type == SyncType.unobtrusive,
requiresDeviceIdle: syncMode.type == SyncType.unobtrusive,
);
final inputData = <String, dynamic>{"sync_all": syncAll};
final constraints = Constraints(
networkType:
syncMode.type == SyncType.unobtrusive ? NetworkType.unmetered : NetworkType.connected,
requiresBatteryNotLow: syncMode.type == SyncType.unobtrusive,
requiresCharging: syncMode.type == SyncType.unobtrusive,
requiresDeviceIdle: syncMode.type == SyncType.unobtrusive,
);
if (Platform.isIOS) {
await Workmanager().registerOneOffTask(
moneroSyncTaskKey,
moneroSyncTaskKey,
initialDelay: syncMode.frequency,
existingWorkPolicy: ExistingWorkPolicy.replace,
inputData: inputData,
constraints: constraints,
);
return;
}
if (Platform.isIOS) {
await Workmanager().registerOneOffTask(
await Workmanager().registerPeriodicTask(
moneroSyncTaskKey,
moneroSyncTaskKey,
initialDelay: syncMode.frequency,
existingWorkPolicy: ExistingWorkPolicy.replace,
frequency: syncMode.frequency,
existingWorkPolicy: changeExisting ? ExistingWorkPolicy.replace : ExistingWorkPolicy.keep,
inputData: inputData,
constraints: constraints,
);
return;
}
await Workmanager().registerPeriodicTask(
moneroSyncTaskKey,
moneroSyncTaskKey,
initialDelay: syncMode.frequency,
frequency: syncMode.frequency,
existingWorkPolicy: changeExisting ? ExistingWorkPolicy.replace : ExistingWorkPolicy.keep,
inputData: inputData,
constraints: constraints,
);
bool hasBitcoin = getIt
.get<WalletListViewModel>()
.wallets
.any((element) => element.type == WalletType.bitcoin);
/// if its not android nor ios, or the user has no monero wallets; exit
// if (hasBitcoin) {
if (false) {
final settingsStore = getIt.get<SettingsStore>();
final SyncMode syncMode = settingsStore.currentSyncMode;
final bool syncAll = settingsStore.currentSyncAll;
if (syncMode.type == SyncType.disabled) {
cancelSyncTask();
return;
}
await Workmanager().initialize(
callbackDispatcher,
isInDebugMode: kDebugMode,
);
final inputData = <String, dynamic>{"sync_all": syncAll};
final constraints = Constraints(
networkType:
syncMode.type == SyncType.unobtrusive ? NetworkType.unmetered : NetworkType.connected,
requiresBatteryNotLow: syncMode.type == SyncType.unobtrusive,
requiresCharging: syncMode.type == SyncType.unobtrusive,
requiresDeviceIdle: syncMode.type == SyncType.unobtrusive,
);
if (Platform.isIOS) {
await Workmanager().registerOneOffTask(
bitcoinSilentPaymentsSyncTaskKey,
bitcoinSilentPaymentsSyncTaskKey,
initialDelay: syncMode.frequency,
existingWorkPolicy: ExistingWorkPolicy.replace,
inputData: inputData,
constraints: constraints,
);
return;
}
await Workmanager().registerPeriodicTask(
bitcoinSilentPaymentsSyncTaskKey,
bitcoinSilentPaymentsSyncTaskKey,
initialDelay: syncMode.frequency,
frequency: syncMode.frequency,
existingWorkPolicy: changeExisting ? ExistingWorkPolicy.replace : ExistingWorkPolicy.keep,
inputData: inputData,
constraints: constraints,
);
}
} catch (error, stackTrace) {
print(error);
print(stackTrace);