Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 106 additions & 6 deletions lib/services/coins/epiccash/epiccash_wallet.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import 'package:mutex/mutex.dart';
import 'package:path_provider/path_provider.dart';
import 'package:stack_wallet_backup/generate_password.dart';
import 'package:tuple/tuple.dart';
import 'package:websocket_universal/websocket_universal.dart';

const int MINIMUM_CONFIRMATIONS = 10;

Expand Down Expand Up @@ -476,16 +477,73 @@ class EpicCashWallet extends CoinServiceAPI {
return result!;
}

Future<bool> testEpicboxServer(String host, int port) async {
final websocketConnectionUri = 'wss://$host:$port';
const connectionOptions = SocketConnectionOptions(
pingIntervalMs: 3000,
timeoutConnectionMs: 4000,

/// see ping/pong messages in [logEventStream] stream
skipPingMessages: true,

/// Set this attribute to `true` if do not need any ping/pong
/// messages and ping measurement. Default is `false`
pingRestrictionForce: false,
);

final IMessageProcessor<String, String> textSocketProcessor =
SocketSimpleTextProcessor();
final textSocketHandler = IWebSocketHandler<String, String>.createClient(
websocketConnectionUri,
textSocketProcessor,
connectionOptions: connectionOptions,
);

// Listening to webSocket status changes
// textSocketHandler.socketHandlerStateStream.listen((stateEvent) {
// debugPrint('> status changed to ${stateEvent.status}');
// });

// Listening to server responses:
bool isConnected = true;
textSocketHandler.incomingMessagesStream.listen((inMsg) {
debugPrint('> webSocket got text message from server: "$inMsg" '
'[ping: ${textSocketHandler.pingDelayMs}]');
});

// Connecting to server:
final isTextSocketConnected = await textSocketHandler.connect();
if (!isTextSocketConnected) {
// ignore: avoid_print
debugPrint(
'Connection to [$websocketConnectionUri] failed for some reason!');
isConnected = false;
}
return isConnected;
}

@override
Future<String> confirmSend({required Map<String, dynamic> txData}) async {
try {
final wallet = await _secureStore.read(key: '${_walletId}_wallet');
final epicboxConfig = await getEpicBoxConfig();

final wallet = await _secureStore.read(key: '${_walletId}_wallet');

// TODO determine whether it is worth sending change to a change address.
dynamic message;

String receiverAddress = txData['addresss'] as String;

if (!receiverAddress.startsWith("http://") ||
!receiverAddress.startsWith("https://")) {
final decoded = json.decode(epicboxConfig);
bool isEpicboxConnected = await testEpicboxServer(
decoded["epicbox_domain"] as String,
decoded["epicbox_port"] as int);
if (!isEpicboxConnected) {
throw Exception("Failed to send TX : Unable to reach epicbox server");
}
}
await m.protect(() async {
if (receiverAddress.startsWith("http://") ||
receiverAddress.startsWith("https://")) {
Expand Down Expand Up @@ -1027,9 +1085,10 @@ class EpicCashWallet extends CoinServiceAPI {
return stringConfig;
}

Future<String> getEpicBoxConfig() async {
EpicBoxModel? _epicBox = DB.instance
.get<EpicBoxModel>(boxName: DB.boxNamePrimaryEpicBox, key: 'primary');
Future<String> getEpicBoxConfig({EpicBoxModel? epicBox}) async {
EpicBoxModel? _epicBox = epicBox ??
DB.instance.get<EpicBoxModel>(
boxName: DB.boxNamePrimaryEpicBox, key: 'primary');
Logging.instance.log(
"Read primary Epic Box config: ${jsonEncode(_epicBox)}",
level: LogLevel.Info);
Expand All @@ -1041,9 +1100,50 @@ class EpicCashWallet extends CoinServiceAPI {
_epicBox = DefaultEpicBoxes.defaultEpicBoxConfig;
}

//First check if the default box is up
final connected =
await testEpicboxServer(_epicBox.host, _epicBox.port as int);

if (!connected) {
//Default Epic Box is not connected, iterate through list of defaults
Logging.instance.log("Default Epic Box server not connected",
level: LogLevel.Warning);

//Get all available hosts
final allBoxes = DefaultEpicBoxes.all;
final List<EpicBoxModel> alternativeServers = [];

for (var i = 0; i < allBoxes.length; i++) {
if (allBoxes[i].name != _epicBox?.name) {
alternativeServers.add(allBoxes[i]);
}
}

bool altConnected = false;
int i = 1;
while (i < alternativeServers.length) {
while (altConnected == false) {
altConnected = await testEpicboxServer(
alternativeServers[i].host, alternativeServers[i].port as int);
if (altConnected == true) {
_epicBox = alternativeServers[i];
Logging.instance.log(
"Connected to alternate Epic Box server ${json.encode(_epicBox)}",
level: LogLevel.Info);
break;
}
}
i++;
}
if (!altConnected) {
Logging.instance
.log("No Epic Box server connected!", level: LogLevel.Error);
}
}

Map<String, dynamic> _config = {
'epicbox_domain': _epicBox.host,
'epicbox_port': _epicBox.port,
'epicbox_domain': _epicBox?.host,
'epicbox_port': _epicBox?.port,
'epicbox_protocol_unsecure': false,
'epicbox_address_index': 0,
};
Expand Down
66 changes: 45 additions & 21 deletions lib/utilities/test_epic_box_connection.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,54 @@
import 'dart:convert';

import 'package:epicpay/pages/settings_views/epicbox_settings_view/manage_epicbox_views/add_edit_epicbox_view.dart';
import 'package:epicpay/utilities/logger.dart';
import 'package:http/http.dart' as http;
import 'package:websocket_universal/websocket_universal.dart';

Future<bool> _testEpicBoxConnection(Uri uri) async {
Future<bool> _testEpicBoxConnection(String host, int port) async {
try {
final client = http.Client();
final response = await client.get(
uri,
headers: {'Content-Type': 'application/json'},
).timeout(const Duration(milliseconds: 2000),
onTimeout: () async => http.Response('Error', 408));
final websocketConnectionUri = 'wss://$host:$port';
const connectionOptions = SocketConnectionOptions(
pingIntervalMs: 3000,
timeoutConnectionMs: 4000,

final json = jsonDecode(response.body);
/// see ping/pong messages in [logEventStream] stream
skipPingMessages: true,

if (response.statusCode == 200 && json["node_version"] != null) {
return true;
} else {
return false;
/// Set this attribute to `true` if do not need any ping/pong
/// messages and ping measurement. Default is `false`
pingRestrictionForce: false,
);

final IMessageProcessor<String, String> textSocketProcessor =
SocketSimpleTextProcessor();
final textSocketHandler = IWebSocketHandler<String, String>.createClient(
websocketConnectionUri,
textSocketProcessor,
connectionOptions: connectionOptions,
);

// Listening to webSocket status changes
// textSocketHandler.socketHandlerStateStream.listen((stateEvent) {
// debugPrint('> status changed to ${stateEvent.status}');
// });

// Listening to server responses:
bool isConnected = true;
textSocketHandler.incomingMessagesStream.listen((inMsg) {
Logging.instance.log(
'> webSocket got text message from server: "$inMsg" '
'[ping: ${textSocketHandler.pingDelayMs}]',
level: LogLevel.Info);
});

// Connecting to server:
final isTextSocketConnected = await textSocketHandler.connect();
if (!isTextSocketConnected) {
// ignore: avoid_print
Logging.instance.log(
'Connection to [$websocketConnectionUri] failed for some reason!',
level: LogLevel.Info);
isConnected = false;
}
return isConnected;
} catch (e, s) {
Logging.instance.log("$e\n$s", level: LogLevel.Warning);
return false;
Expand All @@ -32,7 +61,6 @@ Future<EpicBoxFormData?> testEpicBoxConnection(EpicBoxFormData data) async {
if (data.host == null || data.port == null || data.useSSL == null) {
return null;
}
const String path_postfix = "/v1/version";

if (data.host!.startsWith("https://")) {
data.useSSL = true;
Expand All @@ -46,12 +74,8 @@ Future<EpicBoxFormData?> testEpicBoxConnection(EpicBoxFormData data) async {
}
}

Uri uri = Uri.parse(data.host! + path_postfix);

uri = uri.replace(port: data.port);

try {
if (await _testEpicBoxConnection(uri)) {
if (await _testEpicBoxConnection(data.host!, data.port!)) {
return data;
} else {
return null;
Expand Down
Loading