import 'dart:async'; import 'dart:convert'; import 'dart:ffi'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_zano/api/model/get_wallet_info_result.dart'; import 'package:cw_zano/api/model/get_wallet_status_result.dart'; import 'package:cw_zano/api/model/zano_wallet_keys.dart'; import 'package:cw_zano/zano_balance.dart'; import 'package:cw_zano/zano_wallet.dart'; import 'package:ffi/ffi.dart'; import 'package:cw_zano/api/structs/ut8_box.dart'; import 'package:cw_zano/api/convert_utf8_to_string.dart'; import 'package:cw_zano/api/signatures.dart'; import 'package:cw_zano/api/types.dart'; import 'package:cw_zano/api/zano_api.dart'; import 'package:cw_zano/api/calls.dart' as calls; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:mobx/mobx.dart' as mobx; int _boolToInt(bool value) => value ? 1 : 0; final getFileNameNative = zanoApi.lookup>('get_filename').asFunction(); /*final getSeedNative = zanoApi.lookup>('seed').asFunction();*/ final getAddressNative = zanoApi.lookup>('get_address').asFunction(); final getFullBalanceNative = zanoApi .lookup>('get_full_balance') .asFunction(); final getUnlockedBalanceNative = zanoApi .lookup>('get_unlocked_balance') .asFunction(); /**final getCurrentHeightNative = zanoApi .lookup>('get_current_height') .asFunction();*/ // final getNodeHeightNative = zanoApi // .lookup>('get_node_height') // .asFunction(); final isConnectedNative = zanoApi.lookup>('is_connected').asFunction(); final setupNodeNative = zanoApi.lookup>('setup_node').asFunction(); // final startRefreshNative = zanoApi // .lookup>('start_refresh') // .asFunction(); final connecToNodeNative = zanoApi.lookup>('connect_to_node').asFunction(); final setRefreshFromBlockHeightNative = zanoApi .lookup>('set_refresh_from_block_height') .asFunction(); // final setRecoveringFromSeedNative = zanoApi // .lookup>('set_recovering_from_seed') // .asFunction(); final storeNative = zanoApi.lookup>('store').asFunction(); final setPasswordNative = zanoApi.lookup>('set_password').asFunction(); /**final setListenerNative = zanoApi .lookup>('set_listener') .asFunction();*/ final getSyncingHeightNative = zanoApi .lookup>('get_syncing_height') .asFunction(); final isNeededToRefreshNative = zanoApi .lookup>('is_needed_to_refresh') .asFunction(); // final isNewTransactionExistNative = zanoApi // .lookup>('is_new_transaction_exist') // .asFunction(); final getSecretViewKeyNative = zanoApi.lookup>('secret_view_key').asFunction(); final getPublicViewKeyNative = zanoApi.lookup>('public_view_key').asFunction(); final getSecretSpendKeyNative = zanoApi .lookup>('secret_spend_key') .asFunction(); final getPublicSpendKeyNative = zanoApi .lookup>('public_spend_key') .asFunction(); final closeCurrentWalletNative = zanoApi .lookup>('close_current_wallet') .asFunction(); final onStartupNative = zanoApi.lookup>('on_startup').asFunction(); final rescanBlockchainAsyncNative = zanoApi .lookup>('rescan_blockchain') .asFunction(); // final setTrustedDaemonNative = zanoApi // .lookup>('set_trusted_daemon') // .asFunction(); final trustedDaemonNative = zanoApi.lookup>('trusted_daemon').asFunction(); int getSyncingHeight() => getSyncingHeightNative(); bool isNeededToRefresh() => isNeededToRefreshNative() != 0; //bool isNewTransactionExist() => isNewTransactionExistNative() != 0; String getFilename() => convertUTF8ToString(pointer: getFileNameNative()); /**String getSeed() => convertUTF8ToString(pointer: getSeedNative());*/ String getAddress({int accountIndex = 0, int addressIndex = 0}) => convertUTF8ToString(pointer: getAddressNative(accountIndex, addressIndex)); int getFullBalance({int accountIndex = 0}) => getFullBalanceNative(accountIndex); int getUnlockedBalance({int accountIndex = 0}) => getUnlockedBalanceNative(accountIndex); int getCurrentHeight(int hWallet) { final json = calls.getWalletStatus(hWallet); final walletStatus = GetWalletStatusResult.fromJson(jsonDecode(json) as Map); return walletStatus.currentWalletHeight; } int getNodeHeightSync(int hWallet) { final json = calls.getWalletStatus(hWallet); final walletStatus = GetWalletStatusResult.fromJson(jsonDecode(json) as Map); return walletStatus.currentDaemonHeight; } // int getWalletInfo(int hWallet) { // final json = calls.getWalletInfo(hWallet); // final walletInfo = GetWalletInfoResult.fromJson(jsonDecode(json) as Map); // zanoSeed = walletInfo.wiExtended.seed; // zanoKeys = ZanoWalletKeys( // privateSpendKey: walletInfo.wiExtended.spendPrivateKey, // privateViewKey: walletInfo.wiExtended.viewPrivateKey, // publicSpendKey: walletInfo.wiExtended.spendPublicKey, // publicViewKey: walletInfo.wiExtended.viewPublicKey, // ); // return 0; // } // int getTxFee(int priority) { // return calls.getCurrentTxFee(priority); // } bool isConnectedSync() => isConnectedNative() != 0; bool setupNodeSync({ required String address, String? login, String? password, bool useSSL = false, bool isLightWallet = false, /*String? socksProxyAddress*/ }) { final addressPointer = address.toNativeUtf8(); Pointer? loginPointer; Pointer? socksProxyAddressPointer; Pointer? passwordPointer; if (login != null) { loginPointer = login.toNativeUtf8(); } if (password != null) { passwordPointer = password.toNativeUtf8(); } /*if (socksProxyAddress != null) { socksProxyAddressPointer = socksProxyAddress.toNativeUtf8(); }*/ final errorMessagePointer = ''.toNativeUtf8(); debugPrint( "setup_node address $address login $login password $password useSSL $useSSL isLightWallet $isLightWallet"); // TODO: here can be ZERO! upd: no final isSetupNode = setupNodeNative( addressPointer, loginPointer, passwordPointer, _boolToInt(useSSL), _boolToInt(isLightWallet), /*socksProxyAddressPointer,*/ errorMessagePointer) != 0; debugPrint("setup_node result $isSetupNode"); calloc.free(addressPointer); if (loginPointer != null) { calloc.free(loginPointer); } if (passwordPointer != null) { calloc.free(passwordPointer); } // TODO: fix it /**if (!isSetupNode) { throw SetupWalletException( message: convertUTF8ToString(pointer: errorMessagePointer)); }*/ return isSetupNode; } //void startRefreshSync() => startRefreshNative(); Future connectToNode() async => connecToNodeNative() != 0; void setRefreshFromBlockHeight({required int height}) => setRefreshFromBlockHeightNative(height); // void setRecoveringFromSeed({required bool isRecovery}) => // setRecoveringFromSeedNative(_boolToInt(isRecovery)); void storeSync(int hWallet) { calls.store(hWallet); // TODO: fixit /*final pathPointer = ''.toNativeUtf8(); storeNative(pathPointer); calloc.free(pathPointer);*/ } void setPasswordSync(String password) { final passwordPointer = password.toNativeUtf8(); final errorMessagePointer = calloc(); final changed = setPasswordNative(passwordPointer, errorMessagePointer) != 0; calloc.free(passwordPointer); if (!changed) { final message = errorMessagePointer.ref.getValue(); calloc.free(errorMessagePointer); throw Exception(message); } calloc.free(errorMessagePointer); } void closeCurrentWallet() => closeCurrentWalletNative(); String getSecretViewKey() => convertUTF8ToString(pointer: getSecretViewKeyNative()); String getPublicViewKey() => convertUTF8ToString(pointer: getPublicViewKeyNative()); String getSecretSpendKey() => convertUTF8ToString(pointer: getSecretSpendKeyNative()); String getPublicSpendKey() => convertUTF8ToString(pointer: getPublicSpendKeyNative()); class SyncListener { SyncListener(this.onNewBlock, this.onNewTransaction) : _cachedBlockchainHeight = 0, _lastKnownBlockHeight = 0, _initialSyncHeight = 0; void Function(int, int, double) onNewBlock; void Function() onNewTransaction; Timer? _updateSyncInfoTimer; int _cachedBlockchainHeight; int _lastKnownBlockHeight; int _initialSyncHeight; Future getNodeHeightOrUpdate(int hWallet, int baseHeight) async { if (_cachedBlockchainHeight < baseHeight || _cachedBlockchainHeight == 0) { _cachedBlockchainHeight = await compute(getNodeHeightSync, hWallet); } return _cachedBlockchainHeight; } void start(ZanoWalletBase wallet, int hWallet) async { _cachedBlockchainHeight = 0; _lastKnownBlockHeight = 0; _initialSyncHeight = 0; _updateSyncInfoTimer ??= Timer.periodic(Duration(milliseconds: 1200), (_) async { /**if (isNewTransactionExist()) { onNewTransaction?.call(); }*/ ///var syncHeight = getSyncingHeight(); var syncHeight = getCurrentHeight(hWallet); if (syncHeight <= 0) { syncHeight = getCurrentHeight(hWallet); } //getWalletInfo(hWallet); final json = calls.getWalletInfo(hWallet); final result = GetWalletInfoResult.fromJson(jsonDecode(json) as Map); wallet.seed = result.wiExtended.seed; wallet.keys = ZanoWalletKeys( privateSpendKey: result.wiExtended.spendPrivateKey, privateViewKey: result.wiExtended.viewPrivateKey, publicSpendKey: result.wiExtended.spendPublicKey, publicViewKey: result.wiExtended.viewPublicKey, ); final balance = result.wi.balances.first; wallet.assetId = balance.assetInfo.assetId; wallet.balance = mobx.ObservableMap.of( {CryptoCurrency.zano: ZanoBalance(total: balance.total, unlocked: balance.unlocked)}); ///getTxFee(hWallet); if (_initialSyncHeight <= 0) { _initialSyncHeight = syncHeight; } final bchHeight = await getNodeHeightOrUpdate(hWallet, syncHeight); if (_lastKnownBlockHeight == syncHeight || syncHeight == null) { return; } _lastKnownBlockHeight = syncHeight; final track = bchHeight - _initialSyncHeight; final diff = track - (bchHeight - syncHeight); final ptc = diff <= 0 ? 0.0 : diff / track; final left = bchHeight - syncHeight; if (syncHeight < 0 || left < 0) { return; } // 1. Actual new height; 2. Blocks left to finish; 3. Progress in percents; onNewBlock?.call(syncHeight, left, ptc); }); } void stop() => _updateSyncInfoTimer?.cancel(); } SyncListener setListeners( void Function(int, int, double) onNewBlock, void Function() onNewTransaction) { final listener = SyncListener(onNewBlock, onNewTransaction); /**setListenerNative();*/ return listener; } void onStartup() => onStartupNative(); void _storeSync(int hWallet) => storeSync(hWallet); bool _setupNodeSync(Map args) { final address = args['address'] as String; final login = (args['login'] ?? '') as String; final password = (args['password'] ?? '') as String; final useSSL = args['useSSL'] as bool; final isLightWallet = args['isLightWallet'] as bool; /*final socksProxyAddress = (args['socksProxyAddress'] ?? '') as String;*/ return setupNodeSync( address: address, login: login, password: password, useSSL: useSSL, isLightWallet: isLightWallet, /*socksProxyAddress: socksProxyAddress*/ ); } bool _isConnected(Object _) => isConnectedSync(); //int _getNodeHeight(Object _) => getNodeHeightSync(); //void startRefresh() => startRefreshSync(); Future setupNode( {required String address, String? login, String? password, bool useSSL = false, /*String? socksProxyAddress,*/ bool isLightWallet = false}) => compute, bool>(_setupNodeSync, { 'address': address, 'login': login, 'password': password, 'useSSL': useSSL, 'isLightWallet': isLightWallet, //'socksProxyAddress': socksProxyAddress }); Future store(int hWallet) => compute(_storeSync, 0); Future isConnected() => compute(_isConnected, 0); //Future getNodeHeight() => compute(_getNodeHeight, 0); void rescanBlockchainAsync() => rescanBlockchainAsyncNative(); // Future setTrustedDaemon(bool trusted) async => // setTrustedDaemonNative(_boolToInt(trusted)); Future trustedDaemon() async => trustedDaemonNative() != 0;