Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP - Fix Websocket RPC with Safari. #878

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
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
28 changes: 28 additions & 0 deletions assets/ssl/private_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC90vMC8TGOoiEh
so5bAYdE8C1P+bt2deqs2oMgqZBF5+3gkvvp7kEij/x2nKKnBlJ/1a7lJXIjEdp8
LNuvCeTzy1pdHZf8MzMhVLWHYO8xoGAJHPVBpbE4KDrxXoSY01f15wasRcqBgUYx
pMB7QqDG49PMheyvg92KhMh73ZXZZWyjDA8UjfmbCkTZTn2rQTloTMP8tL50/lWW
y86dJOshke5Nb14xVLNruTKOPy05RwgbcMz7WKD02xkBv8s+i7i0yl7Tyh8g/V/7
8ILpeqFyMq4DXFLCC8wDrnjHoNc/ThO5IdW595lSSr7T018bZ1GfIx+jGV2yF1Hf
OvWYmBI9AgMBAAECggEAKilLvOxN7ciAc9/ZIwj+nrb61X/aHZhYsJ5TECLhTsmS
CfaTmdSHppHVCRJGxTLQCaEwOKidxoDNZpW/EIxxzB1vW226h3NmyKEsNZ632e2t
TvDd34AaEedLmjc8W8ZbS9yNnJ9euG96ZAXcE7SLiclqDkvzs1MmVoEiF3LyuMde
I6GwvSFsLtUzHW6fx8j8c0VnbXI7PzF4zevLnrDfHK2edatB0P0Wh2jl6xOZQo4o
WL0N4R05583cJYhuOJll14H+0G0sRqUeot+Fb9GbwBR0IGUuw0//3Qq2TbHi3OJq
rJ3GaAPeWCYzJjY/gPkIHhx5iaD8LmeXDQXQ9433OQKBgQDyIlsK4cO+jk+Eq+08
YuJrMxCtLYe22tMVjFqh/McMu2M3ATZZD6b06MGdmYJhTPTfRGmyw5VsYg+pzfE7
6zNk+3zMjUykar6n56qDe2DwAX9eOkIol8PPbN8K/lb0AQv5wRAvn8trIbchz6QV
8q1tKiyupe1BMGCerN116+OG5QKBgQDIsbxU3BQ+hMaR7hkNbd6r8a1RtMnM/Zsp
DDiULJnKeyaegz58Rj9+5k7q0gDCgutetMmmy869921JtXldPeeJ4U6w+Giin9CO
6sUVmwsC3m9cYWZUvAUFcqMRiRxK4NlZIKf2zbsAicJeDV8NcpB2+uK5SX2PUhQ8
qKKo2jMQeQKBgBOPLueMITHNcSL4eGt+WWfrchdrLCFbP93nvpKDRtkCchtJX8iF
+SijfLBsbBnMC6PVdOxZ3EIrEbTXy/rZHwezQPuNFnnZnZkWn7GLT+NTW6SS9DEv
QUQKOWI79W40f3EnZVVThS0cRhzXFxwmxFejJoTdJnMzozpuIF2cNn75AoGAQsxN
h90ca9ablPSvp/WauJYe/uPyiuUaIKZtqnvR0D//EaFTSd/DwIP9XlD5acRTfNkl
iPuq2zWgTXk2ZN920kCIhYSYpxAURY1EfbV2C8VnQVM10Rwne7G1Lt/4579A+FZT
MWU2Sa6QX+eJbJtmFAPbNJqOTQafr7TdNuAxQNkCgYA+RaFqihahSHygc7cdllvJ
8UjzcIF/71BE/9RPm3Cqo25fBkL4LQOPfWQewl8zTZ+CsQVdJnVKNMcM/G1sroJa
sJ2XgHJJ1zaI/rwaE5Jxf6lH4fL7uL2BDR7s3VLDs8BRWrcrgdNzb+UWhXJICnz5
bK3qk9vzRjMv+8g5zolPJQ==
-----END PRIVATE KEY-----
19 changes: 19 additions & 0 deletions assets/ssl/server.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDETCCAfkCFDuB9EvVQQhQQjxiaMFD6Ze3tpDbMA0GCSqGSIb3DQEBCwUAMEUx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjMxMjA2MDc0MTI2WhcNMjQxMjA1MDc0
MTI2WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE
CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC
AQ8AMIIBCgKCAQEAvdLzAvExjqIhIbKOWwGHRPAtT/m7dnXqrNqDIKmQReft4JL7
6e5BIo/8dpyipwZSf9Wu5SVyIxHafCzbrwnk88taXR2X/DMzIVS1h2DvMaBgCRz1
QaWxOCg68V6EmNNX9ecGrEXKgYFGMaTAe0KgxuPTzIXsr4PdioTIe92V2WVsowwP
FI35mwpE2U59q0E5aEzD/LS+dP5VlsvOnSTrIZHuTW9eMVSza7kyjj8tOUcIG3DM
+1ig9NsZAb/LPou4tMpe08ofIP1f+/CC6XqhcjKuA1xSwgvMA654x6DXP04TuSHV
ufeZUkq+09NfG2dRnyMfoxldshdR3zr1mJgSPQIDAQABMA0GCSqGSIb3DQEBCwUA
A4IBAQCBMvhMmAp1Y08XtNoaaEJv9KIaNkrbvbZzJlVbvrUDDAq0/DU4B6EA3V4U
8tFsfZpmqWpoMspINYT/EgHYK1idkh2TRyCA94XdSw4g3g0iuwoDJRnvTTkXbge9
BIac4jOmS44VvrzT9OCH7W8AB4P0vTM3Nssu3kIQz/VDSuv7Ypfvvl4O8XP9PTym
cjjrs83iomOcinONzM6MIvUHe+n8nODbsxLcqQZbZIn+jcKprUVZYT4Cbq1HeanP
QU71lxR6YmRARmuHYpN3VKTpkQKSC5BEXPfSuRzTFUsH8f0JDWxg4VV+oLiINrRL
8qrX+XeZPZHXkeqR61QUxCAUFkV7
-----END CERTIFICATE-----
222 changes: 139 additions & 83 deletions lib/infrastructure/rpc/websocket_server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ import 'package:aewallet/infrastructure/rpc/sign_transactions/command_handler.da
import 'package:aewallet/infrastructure/rpc/sub_account/command_handler.dart';
import 'package:aewallet/infrastructure/rpc/sub_current_account/command_handler.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'package:json_rpc_2/json_rpc_2.dart';
import 'package:nsd/nsd.dart';
import 'package:web_socket_channel/io.dart';

/// A [Peer] composition which handles subscription requests
Expand Down Expand Up @@ -101,9 +103,12 @@ class ArchethicWebsocketRPCServer {
ArchethicWebsocketRPCServer();

static const logName = 'RPC Server';
static const host = '127.0.0.1';
static const name = 'archethic_wallet';
static const host = 'localhost';
static const port = 12345;

Registration? mDnsRegistration;

static bool get isPlatformCompatible {
return !kIsWeb &&
(Platform.isLinux || Platform.isMacOS || Platform.isWindows);
Expand All @@ -114,6 +119,34 @@ class ArchethicWebsocketRPCServer {

bool get isRunning => _openedSockets.isNotEmpty || _runningHttpServer != null;

Future<List<int>> _loadBundleIntList(String asset) async {
final byteData = await rootBundle.load(asset);
return byteData.buffer.asInt8List();
}

Future<SecurityContext> _setupServerContext() async {
final serverContext = SecurityContext()
..useCertificateChainBytes(
await _loadBundleIntList('assets/ssl/server.crt'),
)
..usePrivateKeyBytes(
await _loadBundleIntList('assets/ssl/private_key.pem'),
);
return serverContext;
}

Future<void> mDnsRegister() async {
mDnsRegistration = await register(
const Service(name: name, type: '_http._tcp', port: port),
);
}

Future<void> mDnsUnRegister() async {
if (mDnsRegistration == null) return;
await unregister(mDnsRegistration!);
mDnsRegistration = null;
}

Future<void> run() async {
runZonedGuarded(
() async {
Expand All @@ -122,89 +155,109 @@ class ArchethicWebsocketRPCServer {
return;
}

log('Starting at ws://$host:$port', name: logName);
final httpServer = await HttpServer.bind(
host,
port,
shared: true,
);
log('Starting at wss://$host:$port', name: logName);

try {
final httpServer = await HttpServer.bindSecure(
host,
port,
await _setupServerContext(),
shared: true,
);
// final httpServer = await HttpServer.bind(
// host,
// port,
// shared: true,
// );
httpServer.listen((HttpRequest request) async {
log('Received request', name: logName);

final socket = await WebSocketTransformer.upgrade(request);
final channel = IOWebSocketChannel(socket);

final peerServer = _SubscribablePeer(Peer(channel.cast<String>()))
..registerMethod(
'sendTransaction',
(params) => _handle(RPCSendTransactionCommandHandler(), params),
)
..registerMethod(
'getEndpoint',
(params) => _handle(RPCGetEndpointCommandHandler(), params),
)
..registerMethod(
'refreshCurrentAccount',
(params) =>
_handle(RPCRefreshCurrentAccountCommandHandler(), params),
)
..registerMethod(
'getCurrentAccount',
(params) =>
_handle(RPCGetCurrentAccountCommandHandler(), params),
)
..registerMethod(
'getAccounts',
(params) => _handle(RPCGetAccountsCommandHandler(), params),
)
..registerSubscriptionMethod(
'subscribeAccount',
(params) => _handleSubscription(
RPCSubscribeAccountCommandHandler(),
params,
),
)
..registerUnsubscriptionMethod(
'unsubscribeAccount',
)
..registerSubscriptionMethod(
'subscribeCurrentAccount',
(params) => _handleSubscription(
RPCSubscribeCurrentAccountCommandHandler(),
params,
),
)
..registerUnsubscriptionMethod(
'unsubscribeCurrentAccount',
)
..registerMethod(
'addService',
(params) => _handle(RPCAddServiceCommandHandler(), params),
)
..registerMethod(
'getServicesFromKeychain',
(params) =>
_handle(RPCGetServicesFromKeychainCommandHandler(), params),
)
..registerMethod(
'keychainDeriveKeypair',
(params) =>
_handle(RPCKeychainDeriveKeypairCommandHandler(), params),
)
..registerMethod(
'keychainDeriveAddress',
(params) =>
_handle(RPCKeychainDeriveAddressCommandHandler(), params),
)
..registerMethod(
'signTransactions',
(params) =>
_handle(RPCSignTransactionsCommandHandler(), params),
);

_openedSockets.add(peerServer);
await peerServer.listen();
});
_runningHttpServer = httpServer;
} catch (error, stack) {
log(
'WebSocket server failed',
error: error,
stackTrace: stack,
name: logName,
);
}

httpServer.listen((HttpRequest request) async {
log('Received request', name: logName);

final socket = await WebSocketTransformer.upgrade(request);
final channel = IOWebSocketChannel(socket);

final peerServer = _SubscribablePeer(Peer(channel.cast<String>()))
..registerMethod(
'sendTransaction',
(params) => _handle(RPCSendTransactionCommandHandler(), params),
)
..registerMethod(
'getEndpoint',
(params) => _handle(RPCGetEndpointCommandHandler(), params),
)
..registerMethod(
'refreshCurrentAccount',
(params) =>
_handle(RPCRefreshCurrentAccountCommandHandler(), params),
)
..registerMethod(
'getCurrentAccount',
(params) => _handle(RPCGetCurrentAccountCommandHandler(), params),
)
..registerMethod(
'getAccounts',
(params) => _handle(RPCGetAccountsCommandHandler(), params),
)
..registerSubscriptionMethod(
'subscribeAccount',
(params) => _handleSubscription(
RPCSubscribeAccountCommandHandler(),
params,
),
)
..registerUnsubscriptionMethod(
'unsubscribeAccount',
)
..registerSubscriptionMethod(
'subscribeCurrentAccount',
(params) => _handleSubscription(
RPCSubscribeCurrentAccountCommandHandler(),
params,
),
)
..registerUnsubscriptionMethod(
'unsubscribeCurrentAccount',
)
..registerMethod(
'addService',
(params) => _handle(RPCAddServiceCommandHandler(), params),
)
..registerMethod(
'getServicesFromKeychain',
(params) =>
_handle(RPCGetServicesFromKeychainCommandHandler(), params),
)
..registerMethod(
'keychainDeriveKeypair',
(params) =>
_handle(RPCKeychainDeriveKeypairCommandHandler(), params),
)
..registerMethod(
'keychainDeriveAddress',
(params) =>
_handle(RPCKeychainDeriveAddressCommandHandler(), params),
)
..registerMethod(
'signTransactions',
(params) => _handle(RPCSignTransactionsCommandHandler(), params),
);

_openedSockets.add(peerServer);
await peerServer.listen();
});
_runningHttpServer = httpServer;
log('mDns registration', name: logName);
await mDnsRegister();
},
(error, stack) {
log(
Expand Down Expand Up @@ -235,6 +288,9 @@ class ArchethicWebsocketRPCServer {
await _runningHttpServer?.close();
_runningHttpServer = null;
log('Server stopped at ws://$host:$port', name: logName);

log('mDns unregistration', name: logName);
await mDnsUnRegister();
},
(error, stack) {
log(
Expand Down
2 changes: 2 additions & 0 deletions macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import firebase_core
import firebase_messaging
import flutter_local_notifications
import flutter_secure_storage_macos
import nsd_macos
import package_info_plus
import path_provider_foundation
import pdfx
Expand All @@ -29,6 +30,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
NsdMacosPlugin.register(with: registry.registrar(forPlugin: "NsdMacosPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
PdfxPlugin.register(with: registry.registrar(forPlugin: "PdfxPlugin"))
Expand Down
5 changes: 5 additions & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ dependencies:
# Get Mime Types, Extensions, Compressible for Dart and Flutter. Inspired from IANA Database
mime_dart: ^3.0.0

# mDNS registration
nsd: ^2.3.1

# Flutter plugin for accessing the NFC features on Android and iOS.
nfc_manager: ^3.3.0

Expand Down Expand Up @@ -311,6 +314,8 @@ flutter:

assets:
- assets/ssl/isrg-root-x1.pem
- assets/ssl/private_key.pem
- assets/ssl/server.crt
- assets/icons/
- assets/icons/currency/
- assets/icons/languages/
Expand Down
3 changes: 3 additions & 0 deletions windows/flutter/generated_plugin_registrant.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
#include <local_auth_windows/local_auth_plugin.h>
#include <nsd_windows/nsd_windows_plugin_c_api.h>
#include <pdfx/pdfx_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <share_plus/share_plus_windows_plugin_c_api.h>
Expand All @@ -28,6 +29,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
LocalAuthPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
NsdWindowsPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("NsdWindowsPluginCApi"));
PdfxPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("PdfxPlugin"));
ScreenRetrieverPluginRegisterWithRegistrar(
Expand Down
1 change: 1 addition & 0 deletions windows/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
firebase_core
flutter_secure_storage_windows
local_auth_windows
nsd_windows
pdfx
screen_retriever
share_plus
Expand Down