diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 4ca762c12..16b036344 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -13,6 +13,13 @@ jobs: KEY_PASS: test@cake_wallet steps: + - name: Free Up GitHub Actions Ubuntu Runner Disk Space + run: | + sudo rm -rf /usr/share/dotnet + sudo rm -rf /opt/ghc + sudo rm -rf "/usr/local/share/boost" + sudo rm -rf "$AGENT_TOOLSDIRECTORY" + - uses: actions/checkout@v2 - uses: actions/setup-java@v1 with: diff --git a/android/app/build.gradle b/android/app/build.gradle index 00cef6393..946c53697 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -45,8 +45,8 @@ android { defaultConfig { applicationId appProperties['id'] - minSdkVersion 21 - targetSdkVersion 31 + minSdkVersion 24 + targetSdkVersion 33 versionCode flutterVersionCode.toInteger() versionName flutterVersionName testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" diff --git a/cw_core/lib/node.dart b/cw_core/lib/node.dart index 7df25d6a1..0848e8d94 100644 --- a/cw_core/lib/node.dart +++ b/cw_core/lib/node.dart @@ -18,6 +18,7 @@ class Node extends HiveObject with Keyable { this.password, this.useSSL, this.trusted = false, + this.socksProxyAddress, String? uri, WalletType? type,}) { if (uri != null) { @@ -33,7 +34,8 @@ class Node extends HiveObject with Keyable { login = map['login'] as String?, password = map['password'] as String?, useSSL = map['useSSL'] as bool?, - trusted = map['trusted'] as bool? ?? false; + trusted = map['trusted'] as bool? ?? false, + socksProxyAddress = map['socksProxyPort'] as String?; static const typeId = 1; static const boxName = 'Nodes'; @@ -56,8 +58,13 @@ class Node extends HiveObject with Keyable { @HiveField(5, defaultValue: false) bool trusted; + @HiveField(6) + String? socksProxyAddress; + bool get isSSL => useSSL ?? false; + bool get useSocksProxy => socksProxyAddress == null ? false : socksProxyAddress!.isNotEmpty; + Uri get uri { switch (type) { case WalletType.monero: @@ -81,7 +88,8 @@ class Node extends HiveObject with Keyable { other.password == password && other.typeRaw == typeRaw && other.useSSL == useSSL && - other.trusted == trusted); + other.trusted == trusted && + other.socksProxyAddress == socksProxyAddress); @override int get hashCode => @@ -90,7 +98,8 @@ class Node extends HiveObject with Keyable { password.hashCode ^ typeRaw.hashCode ^ useSSL.hashCode ^ - trusted.hashCode; + trusted.hashCode ^ + socksProxyAddress.hashCode; @override dynamic get keyIndex { @@ -108,7 +117,7 @@ class Node extends HiveObject with Keyable { try { switch (type) { case WalletType.monero: - return requestMoneroNode(); + return useSocksProxy ? requestNodeWithProxy(socksProxyAddress ?? '') : requestMoneroNode(); case WalletType.bitcoin: return requestElectrumServer(); case WalletType.litecoin: @@ -159,6 +168,22 @@ class Node extends HiveObject with Keyable { } } + Future requestNodeWithProxy(String proxy) async { + + if (proxy.isEmpty || !proxy.contains(':')) { + return false; + } + final proxyAddress = proxy.split(':')[0]; + final proxyPort = int.parse(proxy.split(':')[1]); + try { + final socket = await Socket.connect(proxyAddress, proxyPort, timeout: Duration(seconds: 5)); + socket.destroy(); + return true; + } catch (_) { + return false; + } + } + Future requestElectrumServer() async { try { await SecureSocket.connect(uri.host, uri.port, diff --git a/cw_haven/lib/api/signatures.dart b/cw_haven/lib/api/signatures.dart index 31c911edc..ae95b62dd 100644 --- a/cw_haven/lib/api/signatures.dart +++ b/cw_haven/lib/api/signatures.dart @@ -39,7 +39,7 @@ typedef get_node_height = Int64 Function(); typedef is_connected = Int8 Function(); typedef setup_node = Int8 Function( - Pointer, Pointer?, Pointer?, Int8, Int8, Pointer); + Pointer, Pointer?, Pointer?, Int8, Int8, Pointer?, Pointer); typedef start_refresh = Void Function(); diff --git a/cw_haven/lib/api/types.dart b/cw_haven/lib/api/types.dart index de9ff74a0..8c9dfdab2 100644 --- a/cw_haven/lib/api/types.dart +++ b/cw_haven/lib/api/types.dart @@ -39,7 +39,7 @@ typedef GetNodeHeight = int Function(); typedef IsConnected = int Function(); typedef SetupNode = int Function( - Pointer, Pointer?, Pointer?, int, int, Pointer); + Pointer, Pointer?, Pointer?, int, int, Pointer?, Pointer); typedef StartRefresh = void Function(); diff --git a/cw_haven/lib/api/wallet.dart b/cw_haven/lib/api/wallet.dart index bdf6a1af7..e6b75c0cc 100644 --- a/cw_haven/lib/api/wallet.dart +++ b/cw_haven/lib/api/wallet.dart @@ -154,9 +154,11 @@ bool setupNodeSync( String? login, String? password, bool useSSL = false, - bool isLightWallet = false}) { + bool isLightWallet = false, + String? socksProxyAddress}) { final addressPointer = address.toNativeUtf8(); Pointer? loginPointer; + Pointer? socksProxyAddressPointer; Pointer? passwordPointer; if (login != null) { @@ -167,6 +169,10 @@ bool setupNodeSync( passwordPointer = password.toNativeUtf8(); } + if (socksProxyAddress != null) { + socksProxyAddressPointer = socksProxyAddress.toNativeUtf8(); + } + final errorMessagePointer = ''.toNativeUtf8(); final isSetupNode = setupNodeNative( addressPointer, @@ -174,6 +180,7 @@ bool setupNodeSync( passwordPointer, _boolToInt(useSSL), _boolToInt(isLightWallet), + socksProxyAddressPointer, errorMessagePointer) != 0; @@ -323,13 +330,15 @@ bool _setupNodeSync(Map args) { 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); + isLightWallet: isLightWallet, + socksProxyAddress: socksProxyAddress); } bool _isConnected(Object _) => isConnectedSync(); @@ -343,13 +352,15 @@ Future setupNode( String? login, String? password, bool useSSL = false, + String? socksProxyAddress, bool isLightWallet = false}) => compute, void>(_setupNodeSync, { 'address': address, 'login': login, 'password': password, 'useSSL': useSSL, - 'isLightWallet': isLightWallet + 'isLightWallet': isLightWallet, + 'socksProxyAddress': socksProxyAddress }); Future store() => compute(_storeSync, 0); diff --git a/cw_haven/lib/haven_wallet.dart b/cw_haven/lib/haven_wallet.dart index ecd2a4b73..a38c4721c 100644 --- a/cw_haven/lib/haven_wallet.dart +++ b/cw_haven/lib/haven_wallet.dart @@ -125,7 +125,8 @@ abstract class HavenWalletBase extends WalletBase #include #include +#include #include #include #include "thread" @@ -405,13 +406,14 @@ extern "C" return is_connected; } - bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error) + bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error) { nice(19); Monero::Wallet *wallet = get_current_wallet(); std::string _login = ""; std::string _password = ""; + std::string _socksProxyAddress = ""; if (login != nullptr) { @@ -423,7 +425,12 @@ extern "C" _password = std::string(password); } - bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet); + if (socksProxyAddress != nullptr) + { + _socksProxyAddress = std::string(socksProxyAddress); + } + + bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress); if (!inited) { diff --git a/cw_monero/lib/api/signatures.dart b/cw_monero/lib/api/signatures.dart index 0b14fd557..82bc7801e 100644 --- a/cw_monero/lib/api/signatures.dart +++ b/cw_monero/lib/api/signatures.dart @@ -35,7 +35,7 @@ typedef get_node_height = Int64 Function(); typedef is_connected = Int8 Function(); typedef setup_node = Int8 Function( - Pointer, Pointer?, Pointer?, Int8, Int8, Pointer); + Pointer, Pointer?, Pointer?, Int8, Int8, Pointer?, Pointer); typedef start_refresh = Void Function(); diff --git a/cw_monero/lib/api/types.dart b/cw_monero/lib/api/types.dart index c5918c12a..051f317c9 100644 --- a/cw_monero/lib/api/types.dart +++ b/cw_monero/lib/api/types.dart @@ -35,7 +35,7 @@ typedef GetNodeHeight = int Function(); typedef IsConnected = int Function(); typedef SetupNode = int Function( - Pointer, Pointer?, Pointer?, int, int, Pointer); + Pointer, Pointer?, Pointer?, int, int, Pointer?, Pointer); typedef StartRefresh = void Function(); diff --git a/cw_monero/lib/api/wallet.dart b/cw_monero/lib/api/wallet.dart index 7ddbf29dc..1680918e5 100644 --- a/cw_monero/lib/api/wallet.dart +++ b/cw_monero/lib/api/wallet.dart @@ -158,9 +158,11 @@ bool setupNodeSync( String? login, String? password, bool useSSL = false, - bool isLightWallet = false}) { + bool isLightWallet = false, + String? socksProxyAddress}) { final addressPointer = address.toNativeUtf8(); Pointer? loginPointer; + Pointer? socksProxyAddressPointer; Pointer? passwordPointer; if (login != null) { @@ -171,6 +173,10 @@ bool setupNodeSync( passwordPointer = password.toNativeUtf8(); } + if (socksProxyAddress != null) { + socksProxyAddressPointer = socksProxyAddress.toNativeUtf8(); + } + final errorMessagePointer = ''.toNativeUtf8(); final isSetupNode = setupNodeNative( addressPointer, @@ -178,6 +184,7 @@ bool setupNodeSync( passwordPointer, _boolToInt(useSSL), _boolToInt(isLightWallet), + socksProxyAddressPointer, errorMessagePointer) != 0; @@ -328,13 +335,15 @@ bool _setupNodeSync(Map args) { 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); + isLightWallet: isLightWallet, + socksProxyAddress: socksProxyAddress); } bool _isConnected(Object _) => isConnectedSync(); @@ -348,13 +357,15 @@ Future setupNode( String? login, String? password, bool useSSL = false, + String? socksProxyAddress, bool isLightWallet = false}) => compute, void>(_setupNodeSync, { 'address': address, 'login': login , 'password': password, 'useSSL': useSSL, - 'isLightWallet': isLightWallet + 'isLightWallet': isLightWallet, + 'socksProxyAddress': socksProxyAddress }); Future store() => compute(_storeSync, 0); diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 7592001b8..104b3ebe8 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -138,7 +138,8 @@ abstract class MoneroWalletBase extends WalletBase{ 'apiKey': _apiKey, 'defaultCrypto': _normalizeCryptoCurrency, - 'wallets': '${_wallet.currency.title}:${_wallet.walletAddresses.address}', + 'networkWallets': '${networkName}:${_wallet.walletAddresses.address}', 'supportSell': "false", 'supportSwap': "false", 'primaryColor': primaryColor, diff --git a/lib/core/socks_proxy_node_address_validator.dart b/lib/core/socks_proxy_node_address_validator.dart new file mode 100644 index 000000000..eb1f78f1d --- /dev/null +++ b/lib/core/socks_proxy_node_address_validator.dart @@ -0,0 +1,10 @@ +import 'package:cake_wallet/core/validator.dart'; +import 'package:cake_wallet/generated/i18n.dart'; + +class SocksProxyNodeAddressValidator extends TextValidator { + SocksProxyNodeAddressValidator() + : super( + errorMessage: S.current.error_text_node_proxy_address, + pattern: + '^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}:[0-9]+\$'); +} diff --git a/lib/entities/openalias_record.dart b/lib/entities/openalias_record.dart index 842a711fe..6d8b759f5 100644 --- a/lib/entities/openalias_record.dart +++ b/lib/entities/openalias_record.dart @@ -37,7 +37,7 @@ class OpenaliasRecord { required String ticker, required List txtRecord, }) { - String address = formattedName; + String address = ''; String name = formattedName; String note = ''; diff --git a/lib/entities/parsed_address.dart b/lib/entities/parsed_address.dart index a73b44e33..67caebcb5 100644 --- a/lib/entities/parsed_address.dart +++ b/lib/entities/parsed_address.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/entities/openalias_record.dart'; import 'package:cake_wallet/entities/yat_record.dart'; -enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter } +enum ParseFrom { unstoppableDomains, openAlias, yatRecord, fio, notParsed, twitter, contact } class ParsedAddress { ParsedAddress({ @@ -40,13 +40,17 @@ class ParsedAddress { ); } - factory ParsedAddress.fetchOpenAliasAddress({required OpenaliasRecord record, required String name}){ - return ParsedAddress( - addresses: [record.address], - name: record.name, - description: record.description, - parseFrom: ParseFrom.openAlias, - ); + factory ParsedAddress.fetchOpenAliasAddress( + {required OpenaliasRecord record, required String name}) { + if (record.address.isEmpty) { + return ParsedAddress(addresses: [name]); + } + return ParsedAddress( + addresses: [record.address], + name: record.name, + description: record.description, + parseFrom: ParseFrom.openAlias, + ); } factory ParsedAddress.fetchFioAddress({required String address, required String name}){ @@ -65,6 +69,14 @@ class ParsedAddress { ); } + factory ParsedAddress.fetchContactAddress({required String address, required String name}){ + return ParsedAddress( + addresses: [address], + name: name, + parseFrom: ParseFrom.contact, + ); + } + final List addresses; final String name; final String description; diff --git a/lib/entities/template.dart b/lib/entities/template.dart index c26e3a501..8224ecdd8 100644 --- a/lib/entities/template.dart +++ b/lib/entities/template.dart @@ -4,14 +4,15 @@ part 'template.g.dart'; @HiveType(typeId: Template.typeId) class Template extends HiveObject { - Template({ - required this.nameRaw, - required this.isCurrencySelectedRaw, - required this.addressRaw, - required this.cryptoCurrencyRaw, - required this.amountRaw, - required this.fiatCurrencyRaw, - required this.amountFiatRaw}); + Template( + {required this.nameRaw, + required this.isCurrencySelectedRaw, + required this.addressRaw, + required this.cryptoCurrencyRaw, + required this.amountRaw, + required this.fiatCurrencyRaw, + required this.amountFiatRaw, + this.additionalRecipientsRaw}); static const typeId = 6; static const boxName = 'Template'; @@ -37,6 +38,9 @@ class Template extends HiveObject { @HiveField(6) String? amountFiatRaw; + @HiveField(7) + List