From 0e5fbfb567fa28422f9ba1a7bb3f57441feb77ff Mon Sep 17 00:00:00 2001 From: "B.Melnik" Date: Mon, 15 Nov 2021 13:17:41 +0300 Subject: [PATCH] refactor(Node): move node to new architecture Closes: #3805 --- src/app/boot/app_controller.nim | 9 +- src/app/modules/main/io_interface.nim | 2 + src/app/modules/main/module.nim | 15 +- .../modules/main/node_section/controller.nim | 101 +++++++++++++ .../node_section/controller_interface.nim | 44 ++++++ .../main/node_section/io_interface.nim | 12 ++ src/app/modules/main/node_section/module.nim | 105 +++++++++++++ .../module_access_interface.nim | 8 + .../module_base_interface.nim | 5 + .../module_controller_delegate_interface.nim | 16 ++ .../module_view_delegate_interface.nim | 35 +++++ src/app/modules/main/node_section/view.nim | 140 ++++++++++++++++++ ...module_node_section_delegate_interface.nim | 2 + src/app_service/service/node/async_tasks.nim | 25 ++++ src/app_service/service/node/service.nim | 99 +++++++++++++ .../service/node_configuration/service.nim | 10 +- .../node_configuration/service_interface.nim | 6 + src/app_service/service/settings/service.nim | 5 +- .../service/settings/service_interface.nim | 3 + ui/app/AppLayouts/Node/NodeLayout.qml | 60 ++++---- ui/app/AppLayouts/Node/stores/RootStore.qml | 5 +- ui/app/AppLayouts/Node/views/RateView.qml | 10 +- .../Profile/stores/ProfileSectionStore.qml | 6 +- 23 files changed, 677 insertions(+), 46 deletions(-) create mode 100644 src/app/modules/main/node_section/controller.nim create mode 100644 src/app/modules/main/node_section/controller_interface.nim create mode 100644 src/app/modules/main/node_section/io_interface.nim create mode 100644 src/app/modules/main/node_section/module.nim create mode 100644 src/app/modules/main/node_section/private_interfaces/module_access_interface.nim create mode 100644 src/app/modules/main/node_section/private_interfaces/module_base_interface.nim create mode 100644 src/app/modules/main/node_section/private_interfaces/module_controller_delegate_interface.nim create mode 100644 src/app/modules/main/node_section/private_interfaces/module_view_delegate_interface.nim create mode 100644 src/app/modules/main/node_section/view.nim create mode 100644 src/app/modules/main/private_interfaces/module_node_section_delegate_interface.nim create mode 100644 src/app_service/service/node/async_tasks.nim create mode 100644 src/app_service/service/node/service.nim diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index 593dfe4a79..441df2d361 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -17,6 +17,7 @@ import ../../app_service/service/bookmarks/service as bookmark_service import ../../app_service/service/dapp_permissions/service as dapp_permissions_service import ../../app_service/service/privacy/service as privacy_service import ../../app_service/service/provider/service as provider_service +import ../../app_service/service/node/service as node_service import ../../app_service/service/profile/service as profile_service import ../../app_service/service/settings/service as settings_service import ../../app_service/service/stickers/service as stickers_service @@ -39,6 +40,7 @@ import ../core/[main] type AppController* = ref object of RootObj statusFoundation: StatusFoundation + # Global localAppSettingsVariant: QVariant localAccountSettingsVariant: QVariant @@ -75,6 +77,7 @@ type savedAddressService: saved_address_service.Service devicesService: devices_service.Service mailserversService: mailservers_service.Service + nodeService: node_service.Service # Modules startupModule: startup_module.AccessInterface @@ -156,6 +159,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.devicesService = devices_service.newService(statusFoundation.status.events, result.settingsService) result.mailserversService = mailservers_service.newService(statusFoundation.status.events, statusFoundation.marathon, result.settingsService, result.nodeConfigurationService, statusFoundation.fleetConfiguration) + result.nodeService = node_service.newService(statusFoundation.status.events, statusFoundation.threadpool, + result.settingsService) # Modules result.startupModule = startup_module.newModule[AppController]( @@ -191,7 +196,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.savedAddressService, result.nodeConfigurationService, result.devicesService, - result.mailserversService + result.mailserversService, + result.nodeService ) # Do connections @@ -226,6 +232,7 @@ proc delete*(self: AppController) = self.dappPermissionsService.delete self.providerService.delete self.nodeConfigurationService.delete + self.nodeService.delete self.settingsService.delete self.stickersService.delete self.savedAddressService.delete diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 6ad38cd7ae..96564c587c 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -13,6 +13,8 @@ include ./private_interfaces/module_chat_section_delegate_interface include ./private_interfaces/module_app_search_delegate_interface include ./private_interfaces/module_browser_section_delegate_interface include ./private_interfaces/module_communities_section_delegate_interface +include ./private_interfaces/module_node_section_delegate_interface + # This way (using concepts) is used only for the modules managed by AppController type diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 1c201fe1d5..9de6158f1f 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -14,6 +14,7 @@ import app_search/module as app_search_module import stickers/module as stickers_module import activity_center/module as activity_center_module import communities/module as communities_module +import node_section/module as node_section_module import ../../../app_service/service/keychain/service as keychain_service import ../../../app_service/service/chat/service as chat_service @@ -36,6 +37,7 @@ import ../../../app_service/service/privacy/service as privacy_service import ../../../app_service/service/stickers/service as stickers_service import ../../../app_service/service/activity_center/service as activity_center_service import ../../../app_service/service/saved_address/service as saved_address_service +import ../../../app_service/service/node/service as node_service import ../../../app_service/service/node_configuration/service_interface as node_configuration_service import ../../../app_service/service/devices/service as devices_service import ../../../app_service/service/mailservers/service as mailservers_service @@ -59,6 +61,7 @@ type activityCenterModule: activity_center_module.AccessInterface communitiesModule: communities_module.AccessInterface appSearchModule: app_search_module.AccessInterface + nodeSectionModule: node_section_module.AccessInterface moduleLoaded: bool proc newModule*[T]( @@ -87,7 +90,8 @@ proc newModule*[T]( savedAddressService: saved_address_service.ServiceInterface, nodeConfigurationService: node_configuration_service.ServiceInterface, devicesService: devices_service.Service, - mailserversService: mailservers_service.Service + mailserversService: mailservers_service.Service, + nodeService: node_service.Service ): Module[T] = result = Module[T]() result.delegate = delegate @@ -125,6 +129,7 @@ proc newModule*[T]( result.communitiesModule = communities_module.newModule(result, events, communityService) result.appSearchModule = app_search_module.newModule(result, events, contactsService, chatService, communityService, messageService) + result.nodeSectionModule = node_section_module.newModule(result, events, settingsService, nodeService, nodeConfigurationService) method delete*[T](self: Module[T]) = self.chatSectionModule.delete @@ -138,6 +143,7 @@ method delete*[T](self: Module[T]) = self.walletSectionModule.delete self.browserSectionModule.delete self.appSearchModule.delete + self.nodeSectionModule.delete self.view.delete self.viewVariant.delete self.controller.delete @@ -316,6 +322,7 @@ method load*[T]( self.activityCenterModule.load() self.communitiesModule.load() self.appSearchModule.load() + self.nodeSectionModule.load() # Set active section on app start self.setActiveSection(activeSection) @@ -337,6 +344,9 @@ proc checkIfModuleDidLoad [T](self: Module[T]) = if(not self.browserSectionModule.isLoaded()): return + if(not self.nodeSectionModule.isLoaded()): + return + if(not self.profileSectionModule.isLoaded()): return @@ -382,6 +392,9 @@ method browserSectionDidLoad*[T](self: Module[T]) = proc profileSectionDidLoad*[T](self: Module[T]) = self.checkIfModuleDidLoad() +method nodeSectionDidLoad*[T](self: Module[T]) = + self.checkIfModuleDidLoad() + method viewDidLoad*[T](self: Module[T]) = self.checkIfModuleDidLoad() diff --git a/src/app/modules/main/node_section/controller.nim b/src/app/modules/main/node_section/controller.nim new file mode 100644 index 0000000000..20b847577c --- /dev/null +++ b/src/app/modules/main/node_section/controller.nim @@ -0,0 +1,101 @@ +import json, strutils +import controller_interface +import io_interface + +import ../../../../app_service/service/settings/service_interface as settings_service +import ../../../../app_service/service/node/service as node_service +import ../../../../app_service/service/node_configuration/service as node_configuration_service + +import eventemitter +import ../../../core/signals/types +import ../../../core/fleets/fleet_configuration + +export controller_interface + +type + Controller* = ref object of controller_interface.AccessInterface + delegate: io_interface.AccessInterface + events: EventEmitter + settingsService: settings_service.ServiceInterface + nodeService: node_service.Service + nodeConfigurationService: node_configuration_service.ServiceInterface + isWakuV2: bool + +proc newController*(delegate: io_interface.AccessInterface, + events: EventEmitter, + settingsService: settings_service.ServiceInterface, + nodeService: node_service.Service, + nodeConfigurationService: node_configuration_service.ServiceInterface + ): Controller = + result = Controller() + result.delegate = delegate + result.events = events + result.settingsService = settingsService + result.nodeService = nodeService + result.nodeConfigurationService = nodeConfigurationService + +proc delete*(self: Controller) = + discard + +proc setPeers(self: Controller, peers: seq[string]) = + self.nodeService.peerSummaryChange(peers) + self.delegate.setPeerSize(peers.len) + +method init*(self: Controller) = + self.isWakuV2 = self.nodeConfigurationService.getWakuVersion() == WAKU_VERSION_2 + + self.events.on(SignalType.Wallet.event) do(e:Args): + self.delegate.setLastMessage($WalletSignal(e).blockNumber) + + self.events.on(SignalType.DiscoverySummary.event) do(e:Args): + var data = DiscoverySummarySignal(e) + self.setPeers(data.enodes) + + self.events.on(SignalType.PeerStats.event) do(e:Args): + var data = PeerStatsSignal(e) + self.setPeers(data.peers) + + self.events.on(SignalType.Stats.event) do (e:Args): + self.delegate.setStats(StatsSignal(e).stats) + if not self.isWakuV2: self.delegate.fetchBitsSet() + + self.events.on(SignalType.ChroniclesLogs.event) do(e:Args): + self.delegate.log(ChroniclesLogsSignal(e).content) + + self.events.on(SIGNAL_BITS_SET_FETCHED) do (e:Args): + self.delegate.setBitsSet(self.nodeService.getBloomBitsSet()) + + self.setPeers(self.nodeService.fetchPeers()) + +method sendRPCMessageRaw*(self: Controller, inputJSON: string): string = + return self.nodeService.sendRPCMessageRaw(inputJSON); + +method setBloomFilterMode*(self: Controller, bloomFilterMode: bool): bool = + return self.nodeConfigurationService.setBloomFilterMode(bloomFilterMode) + +method setBloomLevel*(self: Controller, level: string): bool = + return self.nodeConfigurationService.setBloomLevel(level) + +method isV2LightMode*(self: Controller): bool = + return self.nodeConfigurationService.isV2LightMode() + +method isFullNode*(self: Controller): bool = + return self.nodeConfigurationService.isFullNode() + +method setV2LightMode*(self: Controller, enabled: bool): bool = + return self.nodeConfigurationService.setV2LightMode(enabled) + +method getWakuBloomFilterMode*(self: Controller): bool = + return self.settingsService.getWakuBloomFilterMode() + +method fetchBitsSet*(self: Controller) = + self.nodeService.fetchBitsSet() + +method getWakuVersion*(self: Controller): int = + var fleet = self.settingsService.getFleet() + let isWakuV2 = if fleet == WakuV2Prod or fleet == WakuV2Test: true else: false + if isWakuV2: return 2 + return 1 + +method getBloomLevel*(self: Controller): string = + return self.nodeConfigurationService.getBloomLevel() diff --git a/src/app/modules/main/node_section/controller_interface.nim b/src/app/modules/main/node_section/controller_interface.nim new file mode 100644 index 0000000000..cc5013feb6 --- /dev/null +++ b/src/app/modules/main/node_section/controller_interface.nim @@ -0,0 +1,44 @@ +import json + +type + AccessInterface* {.pure inheritable.} = ref object of RootObj + ## Abstract class for any input/interaction with this module. + +method delete*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method init*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method sendRPCMessageRaw*(self: AccessInterface, inputJSON: string): string {.base.} = + raise newException(ValueError, "No implementation available") + +method setBloomFilterMode*(self: AccessInterface, bloomFilterMode: bool): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method setBloomLevel*(self: AccessInterface, level: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getNodeConfig*(self: AccessInterface): JsonNode {.base.} = + raise newException(ValueError, "No implementation available") + +method setV2LightMode*(self: AccessInterface, enabled: bool): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getWakuBloomFilterMode*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method fetchBitsSet*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method isV2LightMode*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method isFullNode*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getWakuVersion*(self: AccessInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + +method getBloomLevel*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/node_section/io_interface.nim b/src/app/modules/main/node_section/io_interface.nim new file mode 100644 index 0000000000..0f1f5d01aa --- /dev/null +++ b/src/app/modules/main/node_section/io_interface.nim @@ -0,0 +1,12 @@ +# Defines how parent module accesses this module +include ./private_interfaces/module_base_interface +include ./private_interfaces/module_access_interface + +# Defines how this module view communicates with this module +include ./private_interfaces/module_view_delegate_interface + +# Defines how this controller communicates with this module +include ./private_interfaces/module_controller_delegate_interface + +# Defines how submodules of this module communicate with this module +# will be added if needed diff --git a/src/app/modules/main/node_section/module.nim b/src/app/modules/main/node_section/module.nim new file mode 100644 index 0000000000..a26bfa2c6b --- /dev/null +++ b/src/app/modules/main/node_section/module.nim @@ -0,0 +1,105 @@ +import NimQml, Tables, json + +import io_interface +import ../io_interface as delegate_interface +import view, controller + +import ../../../global/global_singleton + +import ../../../../app_service/service/settings/service_interface as settings_service +import ../../../../app_service/service/node/service as node_service +import ../../../../app_service/service/node_configuration/service as node_configuration_service + +import ../../../core/signals/types + +import eventemitter + +export io_interface + +type + Module* = ref object of io_interface.AccessInterface + delegate: delegate_interface.AccessInterface + controller: controller.AccessInterface + view: View + viewVariant: QVariant + moduleLoaded: bool + +proc newModule*(delegate: delegate_interface.AccessInterface, + events: EventEmitter, + settingsService: settings_service.ServiceInterface, + nodeService: node_service.Service, + nodeConfigurationService: node_configuration_service.ServiceInterface + ): Module = + result = Module() + result.delegate = delegate + result.view = view.newView(result) + result.viewVariant = newQVariant(result.view) + result.controller = controller.newController(result, events, settingsService, nodeService, nodeConfigurationService) + result.moduleLoaded = false + +method delete*(self: Module) = + self.view.delete + self.viewVariant.delete + self.controller.delete + +method load*(self: Module) = + singletonInstance.engine.setRootContextProperty("nodeModel", self.viewVariant) + self.controller.init() + self.view.load() + +method isLoaded*(self: Module): bool = + return self.moduleLoaded + +method viewDidLoad*(self: Module) = + self.moduleLoaded = true + self.delegate.nodeSectionDidLoad() + +method sendRPCMessageRaw*(self: Module, inputJSON: string): string = + return self.controller.sendRPCMessageRaw(inputJSON) + +method setBloomFilterMode*(self: Module, bloomFilterMode: bool) = + if(self.controller.setBloomFilterMode(bloomFilterMode)): + quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported + +method setBloomLevel*(self: Module, level: string) = + if(self.controller.setBloomLevel(level)): + quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported + +method setV2LightMode*(self: Module, enabled: bool) = + if(self.controller.setV2LightMode(enabled)): + quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported + +method getWakuBloomFilterMode*(self: Module): bool = + return self.controller.getWakuBloomFilterMode() + +method fetchBitsSet*(self: Module) = + self.controller.fetchBitsSet(); + +method isV2LightMode*(self: Module): bool = + return self.controller.isV2LightMode() + +method isFullNode*(self: Module): bool = + return self.controller.isFullNode() + +method getWakuVersion*(self: Module): int = + return self.controller.getWakuVersion() + +method getBloomLevel*(self: Module): string = + return self.controller.getBloomLevel() + + + +method setLastMessage*(self: Module, lastMessage: string) = + self.view.setLastMessage(lastMessage) + +method setStats*(self: Module, stats: Stats) = + self.view.setStats(stats) + +method log*(self: Module, logContent: string) = + self.view.log(logContent) + +method setPeerSize*(self: Module, peerSize: int) = + self.view.setPeerSize(peerSize) + +method setBitsSet*(self: Module, bitsSet: int) = + self.view.setBitsSet(bitsSet) \ No newline at end of file diff --git a/src/app/modules/main/node_section/private_interfaces/module_access_interface.nim b/src/app/modules/main/node_section/private_interfaces/module_access_interface.nim new file mode 100644 index 0000000000..d057a91427 --- /dev/null +++ b/src/app/modules/main/node_section/private_interfaces/module_access_interface.nim @@ -0,0 +1,8 @@ +method delete*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method load*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method isLoaded*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/node_section/private_interfaces/module_base_interface.nim b/src/app/modules/main/node_section/private_interfaces/module_base_interface.nim new file mode 100644 index 0000000000..64e14bbf51 --- /dev/null +++ b/src/app/modules/main/node_section/private_interfaces/module_base_interface.nim @@ -0,0 +1,5 @@ +type + AccessInterface* {.pure inheritable.} = ref object of RootObj + +# Since nim doesn't support using concepts in second level nested types we +# define delegate interfaces within access interface. \ No newline at end of file diff --git a/src/app/modules/main/node_section/private_interfaces/module_controller_delegate_interface.nim b/src/app/modules/main/node_section/private_interfaces/module_controller_delegate_interface.nim new file mode 100644 index 0000000000..c717207285 --- /dev/null +++ b/src/app/modules/main/node_section/private_interfaces/module_controller_delegate_interface.nim @@ -0,0 +1,16 @@ +import ../../../../core/signals/types + +method setPeerSize*(self: AccessInterface, peerSize: int) {.base.} = + raise newException(ValueError, "No implementation available") + +method setLastMessage*(self: AccessInterface, lastMessage: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method setStats*(self: AccessInterface, stats: Stats) {.base.} = + raise newException(ValueError, "No implementation available") + +method log*(self: AccessInterface, logContent: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method setBitsSet*(self: AccessInterface, bitsSet: int) {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/node_section/private_interfaces/module_view_delegate_interface.nim b/src/app/modules/main/node_section/private_interfaces/module_view_delegate_interface.nim new file mode 100644 index 0000000000..adcc3b05c2 --- /dev/null +++ b/src/app/modules/main/node_section/private_interfaces/module_view_delegate_interface.nim @@ -0,0 +1,35 @@ +import json +import status/types/[rpc_response] + +method viewDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method sendRPCMessageRaw*(self: AccessInterface, inputJSON: string): string {.base.} = + raise newException(ValueError, "No implementation available") + +method setBloomFilterMode*(self: AccessInterface, bloomFilterMode: bool){.base.} = + raise newException(ValueError, "No implementation available") + +method setBloomLevel*(self: AccessInterface, level: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method setV2LightMode*(self: AccessInterface, enabled: bool) {.base.} = + raise newException(ValueError, "No implementation available") + +method getWakuBloomFilterMode*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method fetchBitsSet*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method isV2LightMode*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method isFullNode*(self: AccessInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getWakuVersion*(self: AccessInterface): int {.base.} = + raise newException(ValueError, "No implementation available") + +method getBloomLevel*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app/modules/main/node_section/view.nim b/src/app/modules/main/node_section/view.nim new file mode 100644 index 0000000000..d87b8b1c14 --- /dev/null +++ b/src/app/modules/main/node_section/view.nim @@ -0,0 +1,140 @@ +import NimQml, chronicles, json, strutils +import io_interface +import ../../../core/signals/types + + +QtObject: + type + View* = ref object of QObject + delegate: io_interface.AccessInterface + callResult: string + lastMessage*: string + stats*: Stats + peerSize: int + bloomBitsSet: int + + proc delete*(self: View) = + self.QObject.delete + + proc newView*(delegate: io_interface.AccessInterface): View = + new(result, delete) + result.QObject.setup + result.delegate = delegate + result.callResult = "Use this tool to call JSONRPC methods" + result.lastMessage = "" + + proc load*(self: View) = + self.delegate.viewDidLoad() + + proc callResult*(self: View): string {.slot.} = + result = self.callResult + + proc callResultChanged*(self: View, callResult: string) {.signal.} + + proc setCallResult(self: View, callResult: string) = + if self.callResult == callResult: return + self.callResult = callResult + self.callResultChanged(callResult) + + QtProperty[string] callResult: + read = callResult + notify = callResultChanged + + proc onSend*(self: View, inputJSON: string) {.slot.} = + self.setCallResult(self.delegate.sendRPCMessageRaw(inputJSON)) + + proc onMessage*(self: View, message: string) {.slot.} = + self.setCallResult(message) + + proc receivedMessage*(self: View, lastMessage: string) {.signal.} + + proc setLastMessage*(self: View, lastMessage: string) = + self.lastMessage = lastMessage + self.receivedMessage(lastMessage) + + QtProperty[string] lastMessage: + read = lastMessage + notify = receivedMessage + + proc getWakuBloomFilterMode*(self: View): bool {.slot.} = + return self.delegate.getWakuBloomFilterMode() + + proc getBloomLevel*(self: View): string {.slot.} = + return self.delegate.getBloomLevel() + + QtProperty[bool] wakuBloomFilterMode: + read = getWakuBloomFilterMode + notify = initialized + + QtProperty[string] bloomLevel: + read = getBloomLevel + notify = initialized + + proc setWakuBloomFilterMode*(self: View, bloomFilterMode: bool) {.slot.} = + self.delegate.setBloomFilterMode(bloomFilterMode) + + proc wakuVersion*(self: View): int {.slot.} = + return self.delegate.getWakuVersion() + + proc setBloomLevel*(self: View, level: string) {.slot.} = + self.delegate.setBloomLevel(level) + + proc statsChanged*(self: View) {.signal.} + + proc setStats*(self: View, stats: Stats) = + self.stats = stats + self.statsChanged() + + proc fetchBitsSet*(self: View) = + self.delegate.fetchBitsSet() + + proc getBloomBitsSet(self: View): int {.slot.} = + self.bloomBitsSet + + proc bloomBitsSetChanged(self: View) {.signal.} + + proc setBitsSet*(self: View, bitsSet: int) = + self.bloomBitsSet = bitsSet + self.bloomBitsSetChanged(); + + QtProperty[int] bloomBits: + read = getBloomBitsSet + notify = bloomBitsSetChanged + + proc uploadRate*(self: View): string {.slot.} = $self.stats.uploadRate + + QtProperty[string] uploadRate: + read = uploadRate + notify = statsChanged + + proc downloadRate*(self: View): string {.slot.} = $self.stats.downloadRate + + QtProperty[string] downloadRate: + read = downloadRate + notify = statsChanged + + proc getPeerSize*(self: View): int {.slot.} = self.peerSize + + proc peerSizeChanged*(self: View, value: int) {.signal.} + + proc setPeerSize*(self: View, value: int) {.slot.} = + self.peerSize = value + self.peerSizeChanged(value) + + proc resetPeers*(self: View) {.slot.} = + self.setPeerSize(0) + + QtProperty[int] peerSize: + read = getPeerSize + notify = peerSizeChanged + + proc log*(self: View, logContent: string) {.signal.} + + proc getWakuV2LightClient(self: View): bool {.slot.} = self.delegate.isV2LightMode() + + QtProperty[bool] WakuV2LightClient: + read = getWakuV2LightClient + notify = initialized + + proc setWakuV2LightClient*(self: View, enabled: bool) {.slot.} = + self.delegate.setV2LightMode(enabled) diff --git a/src/app/modules/main/private_interfaces/module_node_section_delegate_interface.nim b/src/app/modules/main/private_interfaces/module_node_section_delegate_interface.nim new file mode 100644 index 0000000000..fa4d852411 --- /dev/null +++ b/src/app/modules/main/private_interfaces/module_node_section_delegate_interface.nim @@ -0,0 +1,2 @@ +method nodeSectionDidLoad*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") diff --git a/src/app_service/service/node/async_tasks.nim b/src/app_service/service/node/async_tasks.nim new file mode 100644 index 0000000000..44738a0228 --- /dev/null +++ b/src/app_service/service/node/async_tasks.nim @@ -0,0 +1,25 @@ +import status/statusgo_backend_new/node as status_node +import bitops, stew/byteutils, chronicles +include ../../../app/core/tasks/common + +type + BloomBitsSetTaskArg = ref object of QObjectTaskArg + bitsSet: int + +proc getBloomFilterBitsSet*(): int = + try: + let bloomFilter = status_node.getBloomFilter().result.getStr + var bitCount = 0; + for b in hexToSeqByte(bloomFilter): + bitCount += countSetBits(b) + return bitCount + except Exception as e: + error "error while getting BloomFilterBitSet: ", msg = e.msg + return 0; + +const bloomBitsSetTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = + let + arg = decode[BloomBitsSetTaskArg](argEncoded) + output = getBloomFilterBitsSet() + arg.finish(output) + \ No newline at end of file diff --git a/src/app_service/service/node/service.nim b/src/app_service/service/node/service.nim new file mode 100644 index 0000000000..dc56ef980e --- /dev/null +++ b/src/app_service/service/node/service.nim @@ -0,0 +1,99 @@ +import NimQml, chronicles, strutils, json, nimcrypto +import status/utils +import eventemitter + +import ../settings/service as settings_service + +import ../../../app/core/tasks/[qt, threadpool] +import ../../../app/core/fleets/fleet_configuration + +include async_tasks + +logScope: + topics = "node-service" + +# Signals which may be emitted by this service: +const SIGNAL_BITS_SET_FETCHED* = "SIGNAL_BITS_SET_FETCHED" +const SIGNAL_NETWORK_DISCONNECTED* = "SIGNAL_NETWORK_DISCONNECTED" +const SIGNAL_NETWORK_CONNECTED* = "SIGNAL_NETWORK_CONNECTED" + +type + BitsSet* = ref object of Args + bitsSet*: int + +QtObject: + type Service* = ref object of QObject + events*: EventEmitter + threadpool: ThreadPool + settingsService: settings_service.Service + bloomBitsSet: int + peers*: seq[string] + connected: bool + + proc delete*(self: Service) = + self.QObject.delete + + proc newService*(events: EventEmitter, threadpool: ThreadPool, settingsService: settings_service.Service): Service = + new(result, delete) + result.QObject.setup + result.events = events + result.threadpool = threadpool + result.settingsService = settingsService + result.peers = @[] + result.connected = false + + proc init*(self: Service) = + discard + + proc bloomFiltersBitsSet*(self: Service, slot: string) = + let arg = BloomBitsSetTaskArg( + tptr: cast[ByteAddress](bloomBitsSetTask), + vptr: cast[ByteAddress](self.vptr), + slot: slot + ) + self.threadpool.start(arg) + + proc fetchBitsSet*(self: Service) = + self.bloomFiltersBitsSet("bitsSet") + + proc getBloomBitsSet*(self: Service): int = + return self.bloomBitsSet; + + proc bitsSet*(self: Service, bitsSet: string) {.slot.} = + self.bloomBitsSet = parseInt(bitsSet) + self.events.emit(SIGNAL_BITS_SET_FETCHED, BitsSet(bitsSet: self.bloomBitsSet)) + + proc sendRPCMessageRaw*(self: Service, inputJSON: string): string = + return status_node.sendRPCMessageRaw(inputJSON) + + proc adminPeers*(): seq[string] = + let response = status_node.adminPeers().result + for jsonPeer in response: + result.add(jsonPeer["enode"].getStr) + + proc wakuV2Peers*(): seq[string] = + let response = status_node.wakuV2Peers().result + for (id, proto) in response.pairs: + if proto.len != 0: + result.add(id) + + proc fetchPeers*(self: Service): seq[string] = + var fleet = self.settingsService.getFleet() + let isWakuV2 = if fleet == WakuV2Prod or fleet == WakuV2Test: true else: false + if isWakuV2: + return wakuV2Peers() + else: + return adminPeers() + + proc peerSummaryChange*(self: Service, peers: seq[string]) = + if peers.len == 0 and self.connected: + self.connected = false + self.events.emit(SIGNAL_NETWORK_DISCONNECTED, Args()) + + if peers.len > 0 and not self.connected: + self.connected = true + self.events.emit(SIGNAL_NETWORK_CONNECTED, Args()) + + self.peers = peers + + proc peerCount*(self: Service): int = self.peers.len \ No newline at end of file diff --git a/src/app_service/service/node_configuration/service.nim b/src/app_service/service/node_configuration/service.nim index 31f6891816..3cb6e13c56 100644 --- a/src/app_service/service/node_configuration/service.nim +++ b/src/app_service/service/node_configuration/service.nim @@ -153,7 +153,7 @@ method setBloomLevel*(self: Service, bloomLevel: string): bool = method setFleet*(self: Service, fleet: string): bool = if(not self.settingsService.saveFleet(fleet)): error "error saving fleet ", methodName="setFleet" - return + return false let fleetType = parseEnum[Fleet](fleet) var newConfiguration = self.configuration @@ -185,4 +185,10 @@ method getDebugLevel*(self: Service): string = method setDebugLevel*(self: Service, logLevel: LogLevel): bool = var newConfiguration = self.configuration newConfiguration.LogLevel = $logLevel - return self.saveConfiguration(newConfiguration) \ No newline at end of file + return self.saveConfiguration(newConfiguration) + +method isV2LightMode*(self: Service): bool = + return self.configuration.WakuV2Config.LightClient + +method isFullNode*(self: Service): bool = + return self.configuration.WakuConfig.FullNode \ No newline at end of file diff --git a/src/app_service/service/node_configuration/service_interface.nim b/src/app_service/service/node_configuration/service_interface.nim index 019bbac96a..0e5801ac5c 100644 --- a/src/app_service/service/node_configuration/service_interface.nim +++ b/src/app_service/service/node_configuration/service_interface.nim @@ -51,4 +51,10 @@ method getDebugLevel*(self: ServiceInterface): string {.base.} = raise newException(ValueError, "No implementation available") method setDebugLevel*(self: ServiceInterface, logLevel: LogLevel): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method isV2LightMode*(self: ServiceInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method isFullNode*(self: ServiceInterface): bool {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/src/app_service/service/settings/service.nim b/src/app_service/service/settings/service.nim index 01b75ad5a5..165704739f 100644 --- a/src/app_service/service/settings/service.nim +++ b/src/app_service/service/settings/service.nim @@ -445,4 +445,7 @@ method saveAutoMessageEnabled*(self: Service, value: bool): bool = return false method autoMessageEnabled*(self: Service): bool = - return self.settings.autoMessageEnabled \ No newline at end of file + return self.settings.autoMessageEnabled + +method getWakuBloomFilterMode*(self: Service): bool = + return self.settings.wakuBloomFilterMode diff --git a/src/app_service/service/settings/service_interface.nim b/src/app_service/service/settings/service_interface.nim index af0f012301..a46b2afd79 100644 --- a/src/app_service/service/settings/service_interface.nim +++ b/src/app_service/service/settings/service_interface.nim @@ -255,4 +255,7 @@ method saveAutoMessageEnabled*(self: ServiceInterface, value: bool): bool {.base raise newException(ValueError, "No implementation available") method autoMessageEnabled*(self: ServiceInterface): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getWakuBloomFilterMode*(self: ServiceInterface): bool {.base.} = raise newException(ValueError, "No implementation available") \ No newline at end of file diff --git a/ui/app/AppLayouts/Node/NodeLayout.qml b/ui/app/AppLayouts/Node/NodeLayout.qml index 379badfe5f..681270ec34 100644 --- a/ui/app/AppLayouts/Node/NodeLayout.qml +++ b/ui/app/AppLayouts/Node/NodeLayout.qml @@ -18,14 +18,16 @@ Item { Layout.fillHeight: true Layout.fillWidth: true - property var store: RootStore {} + property RootStore store: RootStore {} ColumnLayout { id: rpcColumn spacing: 0 anchors.fill: parent - RateView {} + RateView { + store: root.store + } RowLayout { id: peerContainer2 @@ -44,7 +46,7 @@ Item { id: peerNumber color: Theme.palette.primaryColor1 // Not Refactored Yet -// text: root.store.nodeModelInst.peerSize + text: root.store.nodeModelInst.peerSize Layout.rightMargin: Style.current.padding Layout.leftMargin: Style.current.padding Layout.fillWidth: true @@ -68,8 +70,7 @@ Item { StatusBaseText { id: bloomPerc color: Theme.palette.primaryColor1 - // Not Refactored Yet -// text: ((root.store.nodeModelInst.bloomBits / 512) * 100).toFixed(2) + "%" + text: ((root.store.nodeModelInst.bloomBits / 512) * 100).toFixed(2) + "%" Layout.rightMargin: Style.current.padding Layout.leftMargin: Style.current.padding Layout.fillWidth: true @@ -122,27 +123,27 @@ Item { } // Not Refactored Yet -// Connections { -// target: root.store.nodeModelInst -// function onLog(logContent) { -// // TODO: this is ugly, but there's not even a design for this section -// if(logContent.indexOf("mailserver") > 0){ -// let lines = mailserverLogTxt.text.split("\n"); -// if (lines.length > 10){ -// lines.shift(); -// } -// lines.push(logContent.trim()) -// mailserverLogTxt.text = lines.join("\n") -// } else { -// let lines = logsTxt.text.split("\n"); -// if (lines.length > 5){ -// lines.shift(); -// } -// lines.push(logContent.trim()) -// logsTxt.text = lines.join("\n") -// } -// } -// } + Connections { + target: root.store.nodeModelInst + function onLog(logContent) { + // TODO: this is ugly, but there's not even a design for this section + if(logContent.indexOf("mailserver") > 0){ + let lines = mailserverLogTxt.text.split("\n"); + if (lines.length > 10){ + lines.shift(); + } + lines.push(logContent.trim()) + mailserverLogTxt.text = lines.join("\n") + } else { + let lines = logsTxt.text.split("\n"); + if (lines.length > 5){ + lines.shift(); + } + lines.push(logContent.trim()) + logsTxt.text = lines.join("\n") + } + } + } ColumnLayout { id: messageContainer @@ -161,7 +162,7 @@ Item { id: test color: Theme.palette.primaryColor1 // Not Refactored Yet -// text: root.store.nodeModelInst.lastMessage + text: root.store.nodeModelInst.lastMessage Layout.rightMargin: Style.current.padding Layout.leftMargin: Style.current.padding Layout.fillWidth: true @@ -177,7 +178,7 @@ Item { Layout.leftMargin: Style.current.padding // TODO: replace with StatusTextArea once it lives in StatusQ. // Not Refactored Yet -// TextArea { id: callResult; Layout.fillWidth: true; text: nodeModel.callResult; readOnly: true } + TextArea { id: callResult; Layout.fillWidth: true; text: root.store.nodeModelInst.callResult; readOnly: true } } RowLayout { @@ -213,8 +214,7 @@ Item { anchors.right: parent.right anchors.rightMargin: 16 onClicked: { - // Not Refactored Yet -// nodeModel.onSend(txtData.text) + root.store.onSend(txtData.text) txtData.text = "" } enabled: txtData.text !== "" diff --git a/ui/app/AppLayouts/Node/stores/RootStore.qml b/ui/app/AppLayouts/Node/stores/RootStore.qml index 5505868c0c..cb58072c1d 100644 --- a/ui/app/AppLayouts/Node/stores/RootStore.qml +++ b/ui/app/AppLayouts/Node/stores/RootStore.qml @@ -5,7 +5,7 @@ import utils 1.0 QtObject { id: root -// property var nodeModelInst: nodeModel + property var nodeModelInst: nodeModel // property var profileModelInst: profileModel function getMailserverName(activeMailServer) { @@ -15,8 +15,7 @@ QtObject { } function onSend(text) { - // Not Refactored Yet -// nodeModelInst.onSend(text) + nodeModelInst.onSend(text) } } diff --git a/ui/app/AppLayouts/Node/views/RateView.qml b/ui/app/AppLayouts/Node/views/RateView.qml index 94c337636e..1b333640a2 100644 --- a/ui/app/AppLayouts/Node/views/RateView.qml +++ b/ui/app/AppLayouts/Node/views/RateView.qml @@ -10,7 +10,11 @@ import shared 1.0 import shared.status 1.0 import shared.controls 1.0 +import "../stores" + Column { + property RootStore store + spacing: 0 StatusSectionHeadline { text: qsTr("Bandwidth") @@ -33,8 +37,7 @@ Column { // TODO: replace with StatusInput from StatusQ at some point Input { id: uploadRate - // Not Refactored Yet -// text: Math.round(parseInt(nodeModel.uploadRate, 10) / 1024 * 100) / 100 + text: Math.round(parseInt(store.nodeModelInst.uploadRate, 10) / 1024 * 100) / 100 width: parent.width readOnly: true customHeight: 44 @@ -64,8 +67,7 @@ Column { // TODO: replace with StatusInput from StatusQ at some point Input { id: downloadRate - // Not Refactored Yet -// text: Math.round(parseInt(nodeModel.downloadRate, 10) / 1024 * 100) / 100 + text: Math.round(parseInt(store.nodeModelInst.downloadRate, 10) / 1024 * 100) / 100 width: parent.width readOnly: true customHeight: 44 diff --git a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml index b815e14f70..020e4ca944 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml @@ -265,13 +265,11 @@ QtObject { } function setBloomLevel(mode) { - // Not Refactored Yet -// nodeModelInst.setBloomLevel(mode) + nodeModelInst.setBloomLevel(mode) } function setWakuV2LightClient(mode) { - // Not Refactored Yet -// nodeModelInst.setWakuV2LightClient(mode) + nodeModelInst.setWakuV2LightClient(mode) } function getCurrentVersion() {