refactor(fleet-configuration): fleet configuration added to `StatusFoundation`

This commit is contained in:
Sale Djenic 2021-11-23 14:44:33 +01:00
parent 7a9784b854
commit bf651c093e
13 changed files with 363 additions and 121 deletions

View File

@ -28,14 +28,14 @@ import ../modules/main/module as main_module
import ../global/local_account_settings import ../global/local_account_settings
import ../global/global_singleton import ../global/global_singleton
import ../core/[main]
################################################# #################################################
# This will be removed later once we move to c++ and handle there async things # This will be removed later once we move to c++ and handle there async things
# and improved some services, like EventsService which should implement # and improved some services, like EventsService which should implement
# provider/subscriber principe, similar we should have SettingsService. # provider/subscriber principe, similar we should have SettingsService.
import ../../constants import ../../constants
import ../core/[main]
import eventemitter import eventemitter
import status/[fleet]
import ../profile/core as profile import ../profile/core as profile
import ../chat/core as chat import ../chat/core as chat
import ../wallet/v1/core as wallet import ../wallet/v1/core as wallet
@ -165,7 +165,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.osNotificationService = os_notification_service.newService(statusFoundation.status.events) result.osNotificationService = os_notification_service.newService(statusFoundation.status.events)
result.keychainService = keychain_service.newService(statusFoundation.status.events) result.keychainService = keychain_service.newService(statusFoundation.status.events)
result.settingsService = settings_service.newService() result.settingsService = settings_service.newService()
result.accountsService = accounts_service.newService() result.accountsService = accounts_service.newService(statusFoundation.fleetConfiguration)
result.contactsService = contacts_service.newService(statusFoundation.status.events, statusFoundation.threadpool) result.contactsService = contacts_service.newService(statusFoundation.status.events, statusFoundation.threadpool)
result.chatService = chat_service.newService(result.contactsService) result.chatService = chat_service.newService(result.contactsService)
result.communityService = community_service.newService(result.chatService) result.communityService = community_service.newService(result.chatService)
@ -191,7 +191,6 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.startupModule = startup_module.newModule[AppController]( result.startupModule = startup_module.newModule[AppController](
result, result,
statusFoundation.status.events, statusFoundation.status.events,
statusFoundation.status.fleet,
result.keychainService, result.keychainService,
result.accountsService result.accountsService
) )

View File

@ -0,0 +1,67 @@
import json, typetraits, tables, sequtils
type
Fleet* {.pure.} = enum
Prod = "eth.prod",
Staging = "eth.staging",
Test = "eth.test",
WakuV2Prod = "wakuv2.prod"
WakuV2Test = "wakuv2.test"
GoWakuTest = "go-waku.test"
FleetNodes* {.pure.} = enum
Bootnodes = "boot",
Mailservers = "mail",
Rendezvous = "rendezvous",
Whisper = "whisper",
Waku = "waku"
LibP2P = "libp2p"
Websocket = "websocket"
Meta* = object
hostname*: string
timestamp*: uint64
Conf* = Table[string, Table[string, Table[string, string]]]
type
FleetConfiguration* = ref object
fleet: Conf
meta: Meta
## Forward declaration
proc extractConfig(self: FleetConfiguration, jsonString: string)
proc newFleetConfiguration*(jsonString: string): FleetConfiguration =
result = FleetConfiguration()
result.extractConfig(jsonString)
proc delete*(self: FleetConfiguration) =
discard
proc extractConfig(self: FleetConfiguration, jsonString: string) =
let fleetJson = jsonString.parseJSON
self.meta.hostname = fleetJson["meta"]["hostname"].getStr
self.meta.timestamp = fleetJson["meta"]["timestamp"].getBiggestInt.uint64
self.fleet = initTable[string, Table[string, Table[string, string]]]()
for fleet in fleetJson["fleets"].keys():
self.fleet[fleet] = initTable[string, Table[string, string]]()
for nodes in fleetJson["fleets"][fleet].keys():
self.fleet[fleet][nodes] = initTable[string, string]()
for server in fleetJson["fleets"][fleet][nodes].keys():
self.fleet[fleet][nodes][server] = fleetJson["fleets"][fleet][nodes][server].getStr
proc getNodes*(self: FleetConfiguration, fleet: Fleet, nodeType: FleetNodes = FleetNodes.Bootnodes): seq[string] =
if not self.fleet[$fleet].hasKey($nodeType): return
result = toSeq(self.fleet[$fleet][$nodeType].values)
proc getMailservers*(self: FleetConfiguration, fleet: Fleet, isWakuV2: bool): Table[string, string] =
# TODO: If using wakuV2, this assumes that Waku nodes in fleet.status.json are also store nodes.
# Maybe it make senses to add a "waku-store" section in case we want to have separate node types?
# Discuss with @iurimatias, @cammellos and Vac team
let fleetKey = if isWakuV2: $FleetNodes.Waku else: $FleetNodes.Mailservers
if not self.fleet[$fleet].hasKey(fleetKey) :
result = initTable[string,string]()
return
result = self.fleet[$fleet][fleetKey]

View File

@ -2,6 +2,7 @@ import NimQml, chronicles, task_runner
import ../../constants import ../../constants
import status/status as status_lib_status import status/status as status_lib_status
import import
./fleets/fleet_configuration,
./tasks/marathon, ./tasks/marathon,
./tasks/marathon/mailserver/controller, ./tasks/marathon/mailserver/controller,
./tasks/marathon/mailserver/worker, ./tasks/marathon/mailserver/worker,
@ -9,10 +10,11 @@ import
./signals/signals_manager ./signals/signals_manager
export status_lib_status export status_lib_status
export marathon, task_runner, signals_manager export marathon, task_runner, signals_manager, fleet_configuration
type StatusFoundation* = ref object type StatusFoundation* = ref object
status*: Status # in one point of time this should be completely removed status*: Status # in one point of time this should be completely removed
fleetConfiguration*: FleetConfiguration
threadpool*: ThreadPool threadpool*: ThreadPool
marathon*: Marathon marathon*: Marathon
signalsManager*: SignalsManager signalsManager*: SignalsManager
@ -22,9 +24,10 @@ type StatusFoundation* = ref object
proc newStatusFoundation*(fleetConfig: string): StatusFoundation = proc newStatusFoundation*(fleetConfig: string): StatusFoundation =
result = StatusFoundation() result = StatusFoundation()
result.status = newStatusInstance(fleetConfig) result.status = newStatusInstance()
result.status.initNode(STATUSGODIR, KEYSTOREDIR) result.status.initNode(STATUSGODIR, KEYSTOREDIR)
result.fleetConfiguration = newFleetConfiguration(fleetConfig)
result.mailserverController = newMailserverController(result.status.events) result.mailserverController = newMailserverController(result.status.events)
result.mailserverWorker = newMailserverWorker(cast[ByteAddress](result.mailserverController.vptr)) result.mailserverWorker = newMailserverWorker(cast[ByteAddress](result.mailserverController.vptr))
result.threadpool = newThreadPool() result.threadpool = newThreadPool()
@ -34,6 +37,9 @@ proc newStatusFoundation*(fleetConfig: string): StatusFoundation =
proc delete*(self: StatusFoundation) = proc delete*(self: StatusFoundation) =
self.threadpool.teardown() self.threadpool.teardown()
self.marathon.teardown() self.marathon.teardown()
self.mailserverWorker.teardown()
self.mailserverController.delete()
self.fleetConfiguration.delete()
self.signalsManager.delete() self.signalsManager.delete()
self.status.reset() self.status.reset()

View File

@ -1,46 +1,237 @@
import import
chronos, chronicles algorithm, chronos, chronicles, json, math, os, random, sequtils, sets,
tables, strutils
from times import cpuTime
import import
status/statusgo_backend_new/mailservers as status_mailservers, status/statusgo_backend/settings as status_settings,
status/fleet status/statusgo_backend/chat as status_chat,
status/statusgo_backend/mailservers as status_mailservers,
status/statusgo_backend/core as status_core,
status/fleet,
./events as mailserver_events
logScope: logScope:
topics = "mailserver model" topics = "mailserver model"
################################################################################
## ##
## NOTE: MailserverModel runs on a separate (long-running) thread ##
## ##
## How do mailservers work ? ##
## ##
## - We send a request to the mailserver, we are only interested in the ##
## messages since `last-request` up to the last seven days ##
## and the last 24 hours for topics that were just joined ##
## - The mailserver doesn't directly respond to the request and ##
## instead we start receiving messages in the filters for the requested ##
## topics. ##
## - If the mailserver was not ready when we tried for instance to request ##
## the history of a topic after joining a chat, the request will be done ##
## as soon as the mailserver becomes available ##
## ##
################################################################################
type type
MailserverModel* = ref object MailserverModel* = ref object
mailservers*: seq[string]
events*: MailserverEvents
nodes*: Table[string, MailserverStatus]
activeMailserver*: string
lastConnectionAttempt*: float
## At this moment we cannot remove FleetModel from `status-lib` easily since the following error occurs:
## /desktop-app/src/app/core/tasks/marathon/mailserver/worker.nim(120, 37) template/generic instantiation of `async` from here
## /desktop-app/vendor/status-lib/vendor/nim-task-runner/vendor/nim-chronos/chronos/asyncmacro2.nim(210, 31) Error: 'worker' is not GC-safe as it calls 'init'
##
## But at some point in future we will spend more time and figure out what's the issue.
fleet*: FleetModel
wakuVersion*: int
MailserverStatus* = enum
Unknown = -1,
Disconnected = 0,
Connecting = 1
Connected = 2,
proc peerIdFromMultiAddress(nodeAddr: string): string =
let multiAddressParts = nodeAddr.split("/")
return multiAddressParts[multiAddressParts.len - 1]
proc cmpMailserverReply(x, y: (string, int)): int =
if x[1] > y[1]: 1
elif x[1] == y[1]: 0
else: -1
proc poolSize(fleetSize: int): int = ceil(fleetSize / 4).int
proc newMailserverModel*(vptr: ByteAddress): MailserverModel = proc newMailserverModel*(vptr: ByteAddress): MailserverModel =
result = MailserverModel() result = MailserverModel()
result.events = newMailserverEvents(vptr)
result.nodes = initTable[string, MailserverStatus]()
result.activeMailserver = ""
proc disconnectActiveMailserver(self: MailserverModel) = proc init*(self: MailserverModel) =
try: trace "MailserverModel::init()"
warn "Disconnecting active mailserver due to error" let fleets =
discard status_mailservers.disconnectActiveMailserver() if defined(windows) and defined(production):
except Exception as e: "/../resources/fleets.json"
error "error: ", errDescription=e.msg else:
"/../fleets.json"
self.wakuVersion = status_settings.getWakuVersion()
let fleetConfig = readFile(joinPath(getAppDir(), fleets))
self.fleet = newFleetModel(fleetConfig)
self.wakuVersion = status_settings.getWakuVersion()
let fleet = parseEnum[Fleet](status_settings.getFleet())
self.mailservers = toSeq(self.fleet.config.getMailservers(fleet, self.wakuVersion == 2).values)
for mailserver in status_settings.getMailservers().getElems():
self.mailservers.add(mailserver["address"].getStr())
proc getActiveMailserver*(self: MailserverModel): string = self.activeMailserver
proc isActiveMailserverAvailable*(self: MailserverModel): bool =
if not self.nodes.hasKey(self.activeMailserver):
result = false
else:
result = self.nodes[self.activeMailserver] == MailserverStatus.Connected
proc connect(self: MailserverModel, nodeAddr: string) =
debug "Connecting to mailserver", nodeAddr
var connected = false
# TODO: this should come from settings
var knownMailservers = initHashSet[string]()
for m in self.mailservers:
knownMailservers.incl m
if not knownMailservers.contains(nodeAddr):
warn "Mailserver not known", nodeAddr
return
self.activeMailserver = if self.wakuVersion == 2: peerIdFromMultiAddress(nodeAddr) else: nodeAddr
self.events.emit("mailserver:changed", MailserverArgs(peer: nodeAddr))
# Adding a peer and marking it as connected can't be executed sync in WakuV1, because
# There's a delay between requesting a peer being added, and a signal being
# received after the peer was added. So we first set the peer status as
# Connecting and once a peerConnected signal is received, we mark it as
# Connected
if self.nodes.hasKey(self.activeMailserver) and self.nodes[self.activeMailserver] == MailserverStatus.Connected:
connected = true
else:
# Attempt to connect to mailserver by adding it as a peer
if self.wakuVersion == 2:
if status_core.dialPeer(nodeAddr): # WakuV2 dial is sync (should it be async?)
discard status_mailservers.setMailserver(self.activeMailserver)
self.nodes[self.activeMailserver] = MailserverStatus.Connected
connected = true
else:
status_mailservers.update(nodeAddr)
self.nodes[nodeAddr] = MailserverStatus.Connecting
self.lastConnectionAttempt = cpuTime()
if connected:
info "Mailserver available"
self.events.emit("mailserverAvailable", MailserverArgs())
proc peerSummaryChange*(self: MailserverModel, peers: seq[string]) =
# When a node is added as a peer, or disconnected
# a DiscoverySummary signal is emitted. In here we
# change the status of the nodes the app is connected to
# Connected / Disconnected and emit peerConnected / peerDisconnected
# events.
var mailserverAvailable = false
for knownPeer in self.nodes.keys:
if not peers.contains(knownPeer) and (self.nodes[knownPeer] == MailserverStatus.Connected or (self.nodes[knownPeer] == MailserverStatus.Connecting and (cpuTime() - self.lastConnectionAttempt) > 8)):
info "Peer disconnected", peer=knownPeer
self.nodes[knownPeer] = MailserverStatus.Disconnected
self.events.emit("peerDisconnected", MailserverArgs(peer: knownPeer))
if self.activeMailserver == knownPeer:
warn "Active mailserver disconnected!", peer = knownPeer
self.activeMailserver = ""
for peer in peers:
if self.nodes.hasKey(peer) and (self.nodes[peer] == MailserverStatus.Connected): continue
info "Peer connected", peer
self.nodes[peer] = MailserverStatus.Connected
self.events.emit("peerConnected", MailserverArgs(peer: peer))
if peer == self.activeMailserver:
if self.nodes.hasKey(self.activeMailserver):
if self.activeMailserver == peer:
mailserverAvailable = true
if mailserverAvailable:
info "Mailserver available"
self.events.emit("mailserverAvailable", MailserverArgs())
proc requestMessages*(self: MailserverModel) = proc requestMessages*(self: MailserverModel) =
try: info "Requesting messages from", mailserver=self.activeMailserver
info "Requesting message history"
discard status_mailservers.requestAllHistoricMessages() discard status_mailservers.requestAllHistoricMessages()
except Exception as e:
error "error: ", errDescription=e.msg proc requestStoreMessages*(self: MailserverModel, topics: seq[string], fromValue: int64 = 0, toValue: int64 = 0, force: bool = false) =
self.disconnectActiveMailserver() info "Requesting messages from", mailserver=self.activeMailserver
let generatedSymKey = status_chat.generateSymKeyFromPassword()
status_mailservers.requestStoreMessages(topics, generatedSymKey, self.activeMailserver, 1000, fromValue, toValue, force)
proc requestMoreMessages*(self: MailserverModel, chatId: string) = proc requestMoreMessages*(self: MailserverModel, chatId: string) =
try: info "Requesting more messages from", mailserver=self.activeMailserver, chatId=chatId
info "Requesting more messages for", chatId=chatId
discard status_mailservers.syncChatFromSyncedFrom(chatId) discard status_mailservers.syncChatFromSyncedFrom(chatId)
except Exception as e:
error "error: ", errDescription=e.msg
self.disconnectActiveMailserver()
proc fillGaps*(self: MailserverModel, chatId: string, messageIds: seq[string]) = proc fillGaps*(self: MailserverModel, chatId: string, messageIds: seq[string]) =
try: info "Requesting fill gaps from", mailserver=self.activeMailserver, chatId=chatId
info "Requesting fill gaps from", chatId=chatId
discard status_mailservers.fillGaps(chatId, messageIds) discard status_mailservers.fillGaps(chatId, messageIds)
except Exception as e:
error "error: ", errDescription=e.msg proc findNewMailserver(self: MailserverModel) =
self.disconnectActiveMailserver() warn "Finding a new mailserver...", wakuVersion=self.wakuVersion
let mailserversReply = parseJson(status_mailservers.ping(self.mailservers, 500, self.wakuVersion == 2))["result"]
var availableMailservers:seq[(string, int)] = @[]
for reply in mailserversReply:
if(reply["error"].kind != JNull): continue # The results with error are ignored
availableMailservers.add((reply["address"].getStr, reply["rttMs"].getInt))
availableMailservers.sort(cmpMailserverReply)
# No mailservers where returned... do nothing.
if availableMailservers.len == 0:
warn "No mailservers available"
return
# Picks a random mailserver amongs the ones with the lowest latency
# The pool size is 1/4 of the mailservers were pinged successfully
randomize()
let mailServer = availableMailservers[rand(poolSize(availableMailservers.len - 1))][0]
self.connect(mailserver)
proc cycleMailservers(self: MailserverModel) =
warn "Automatically switching mailserver"
if self.activeMailserver != "":
info "Disconnecting active mailserver", peer=self.activeMailserver
self.nodes[self.activeMailserver] = MailserverStatus.Disconnected
if self.wakuVersion == 2:
dropPeerByID(self.activeMailserver)
else:
removePeer(self.activeMailserver)
self.activeMailserver = ""
self.findNewMailserver()
proc checkConnection*(self: MailserverModel) {.async.} =
while true:
info "Verifying mailserver connection state..."
let pinnedMailserver = status_settings.getPinnedMailserver()
if self.wakuVersion == 1 and pinnedMailserver != "" and self.activeMailserver != pinnedMailserver:
# connect to current mailserver from the settings
self.mailservers.add(pinnedMailserver)
self.connect(pinnedMailserver)
else:
# or setup a random mailserver:
if not self.isActiveMailserverAvailable:
# TODO: have a timeout for reconnection before changing to a different server
self.cycleMailservers()
await sleepAsync(10.seconds)

View File

@ -11,7 +11,6 @@ import ../../../app_service/service/keychain/service as keychain_service
import ../../../app_service/service/accounts/service_interface as accounts_service import ../../../app_service/service/accounts/service_interface as accounts_service
import eventemitter import eventemitter
import status/[fleet]
export io_interface export io_interface
@ -26,7 +25,6 @@ type
proc newModule*[T](delegate: T, proc newModule*[T](delegate: T,
events: EventEmitter, events: EventEmitter,
fleet: FleetModel,
keychainService: keychain_service.Service, keychainService: keychain_service.Service,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Module[T] = Module[T] =
@ -37,8 +35,7 @@ proc newModule*[T](delegate: T,
result.controller = controller.newController(result, events, accountsService) result.controller = controller.newController(result, events, accountsService)
# Submodules # Submodules
result.onboardingModule = onboarding_module.newModule(result, events, fleet, result.onboardingModule = onboarding_module.newModule(result, events, accountsService)
accountsService)
result.loginModule = login_module.newModule(result, events, keychainService, result.loginModule = login_module.newModule(result, events, keychainService,
accountsService) accountsService)

View File

@ -18,19 +18,16 @@ type
ref object of controller_interface.AccessInterface ref object of controller_interface.AccessInterface
delegate: io_interface.AccessInterface delegate: io_interface.AccessInterface
events: EventEmitter events: EventEmitter
fleet: FleetModel
accountsService: accounts_service.ServiceInterface accountsService: accounts_service.ServiceInterface
selectedAccountId: string selectedAccountId: string
proc newController*(delegate: io_interface.AccessInterface, proc newController*(delegate: io_interface.AccessInterface,
events: EventEmitter, events: EventEmitter,
fleet: FleetModel,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Controller = Controller =
result = Controller() result = Controller()
result.delegate = delegate result.delegate = delegate
result.events = events result.events = events
result.fleet = fleet
result.accountsService = accountsService result.accountsService = accountsService
method delete*(self: Controller) = method delete*(self: Controller) =
@ -53,8 +50,7 @@ method setSelectedAccountByIndex*(self: Controller, index: int) =
self.selectedAccountId = accounts[index].id self.selectedAccountId = accounts[index].id
method storeSelectedAccountAndLogin*(self: Controller, password: string) = method storeSelectedAccountAndLogin*(self: Controller, password: string) =
if(not self.accountsService.setupAccount(self.fleet.config, if(not self.accountsService.setupAccount(self.selectedAccountId, password)):
self.selectedAccountId, password)):
self.delegate.setupAccountError() self.delegate.setupAccountError()
method validateMnemonic*(self: Controller, mnemonic: string): string = method validateMnemonic*(self: Controller, mnemonic: string): string =

View File

@ -10,22 +10,19 @@ method delete*(self: AccessInterface) {.base.} =
method init*(self: AccessInterface) {.base.} = method init*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getGeneratedAccounts*(self: AccessInterface): method getGeneratedAccounts*(self: AccessInterface): seq[GeneratedAccountDto] {.base.} =
seq[GeneratedAccountDto] {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setSelectedAccountByIndex*(self: AccessInterface, index: int) {.base.} = method setSelectedAccountByIndex*(self: AccessInterface, index: int) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method storeSelectedAccountAndLogin*(self: AccessInterface, password: string) method storeSelectedAccountAndLogin*(self: AccessInterface, password: string) {.base.} =
{.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getImportedAccount*(self: AccessInterface): GeneratedAccountDto {.base.} = method getImportedAccount*(self: AccessInterface): GeneratedAccountDto {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method validateMnemonic*(self: AccessInterface, mnemonic: string): method validateMnemonic*(self: AccessInterface, mnemonic: string): string {.base.} =
string {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method importMnemonic*(self: AccessInterface, mnemonic: string) {.base.} = method importMnemonic*(self: AccessInterface, mnemonic: string) {.base.} =

View File

@ -7,7 +7,6 @@ import ../../../global/global_singleton
import ../../../../app_service/service/accounts/service_interface as accounts_service import ../../../../app_service/service/accounts/service_interface as accounts_service
import eventemitter import eventemitter
import status/[fleet]
export io_interface export io_interface
@ -19,17 +18,14 @@ type
controller: controller.AccessInterface controller: controller.AccessInterface
moduleLoaded: bool moduleLoaded: bool
proc newModule*(delegate: delegate_interface.AccessInterface, proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter,
events: EventEmitter,
fleet: FleetModel,
accountsService: accounts_service.ServiceInterface): accountsService: accounts_service.ServiceInterface):
Module = Module =
result = Module() result = Module()
result.delegate = delegate result.delegate = delegate
result.view = view.newView(result) result.view = view.newView(result)
result.viewVariant = newQVariant(result.view) result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, events, fleet, result.controller = controller.newController(result, events, accountsService)
accountsService)
result.moduleLoaded = false result.moduleLoaded = false
method delete*(self: Module) = method delete*(self: Module) =

View File

@ -1,4 +1,4 @@
import NimQml, json, tables import NimQml, json
import json_serialization import json_serialization
import status/[status, settings] import status/[status, settings]
import status/contacts as status_contacts import status/contacts as status_contacts
@ -63,9 +63,11 @@ proc init*(self: ProfileController, account: Account) =
self.view.ens.init() self.view.ens.init()
self.view.initialized() self.view.initialized()
for name, endpoint in self.status.fleet.config.getMailservers(self.status.settings.getFleet(), self.status.settings.getWakuVersion() == 2).pairs(): # Delete this once it's refactored.
let mailserver = MailServer(name: name, endpoint: endpoint) #
self.view.mailservers.add(mailserver) # for name, endpoint in self.status.fleet.config.getMailservers(self.status.settings.getFleet(), self.status.settings.getWakuVersion() == 2).pairs():
# let mailserver = MailServer(name: name, endpoint: endpoint)
# self.view.mailservers.add(mailserver)
for mailserver in self.status.settings.getMailservers().getElems(): for mailserver in self.status.settings.getMailservers().getElems():
let mailserver = MailServer(name: mailserver["name"].getStr(), endpoint: mailserver["address"].getStr()) let mailserver = MailServer(name: mailserver["name"].getStr(), endpoint: mailserver["address"].getStr())

View File

@ -24,20 +24,23 @@ QtObject:
self.fleetChanged($self.status.settings.getFleet()) self.fleetChanged($self.status.settings.getFleet())
proc setFleet*(self: Fleets, newFleet: string) {.slot.} = proc setFleet*(self: Fleets, newFleet: string) {.slot.} =
let fleet = parseEnum[Fleet](newFleet) discard
let statusGoResult = self.status.settings.setFleet(self.status.fleet.config, fleet) # Delete this once it's refactored.
if statusGoResult.error != "": #
error "Error saving updated node config", msg=statusGoResult.error # let fleet = parseEnum[Fleet](newFleet)
# let statusGoResult = self.status.settings.setFleet(self.status.fleet.config, fleet)
# if statusGoResult.error != "":
# error "Error saving updated node config", msg=statusGoResult.error
let isWakuV2 = if fleet == WakuV2Prod or fleet == WakuV2Test: true else: false # let isWakuV2 = if fleet == WakuV2Prod or fleet == WakuV2Test: true else: false
# Updating waku version because it makes no sense for some fleets to run under wakuv1 or v2 config # # Updating waku version because it makes no sense for some fleets to run under wakuv1 or v2 config
if isWakuV2: # if isWakuV2:
self.status.settings.setWakuVersion(2) # self.status.settings.setWakuVersion(2)
else: # else:
self.status.settings.setWakuVersion(1) # self.status.settings.setWakuVersion(1)
self.fleetChanged(newFleet) # self.fleetChanged(newFleet)
quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported # quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported
proc getFleet*(self: Fleets): string {.slot.} = $self.status.settings.getFleet() proc getFleet*(self: Fleets): string {.slot.} = $self.status.settings.getFleet()

View File

@ -40,7 +40,7 @@ QtObject:
proc setNetworkAndPersist*(self: NetworkView, network: string) {.slot.} = proc setNetworkAndPersist*(self: NetworkView, network: string) {.slot.} =
self.network = network self.network = network
self.networkChanged() self.networkChanged()
self.status.accounts.changeNetwork(self.status.fleet.config, network) ############################### self.status.accounts.changeNetwork(network) ###############################
quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported quit(QuitSuccess) # quits the app TODO: change this to logout instead when supported
QtProperty[QVariant] current: QtProperty[QVariant] current:

View File

@ -4,9 +4,10 @@ import json_serialization, chronicles
import service_interface import service_interface
import ./dto/accounts import ./dto/accounts
import ./dto/generated_accounts import ./dto/generated_accounts
import status/statusgo_backend_new/accounts as status_go import status/statusgo_backend_new/accounts as status_account
import status/statusgo_backend_new/general as status_go_general import status/statusgo_backend_new/general as status_general
import ../../../app/core/fleets/fleet_configuration
import ../../common/[account_constants, utils, string_utils] import ../../common/[account_constants, utils, string_utils]
import ../../../constants as main_constants import ../../../constants as main_constants
export service_interface export service_interface
@ -18,6 +19,7 @@ const PATHS = @[PATH_WALLET_ROOT, PATH_EIP_1581, PATH_WHISPER, PATH_DEFAULT_WALL
type type
Service* = ref object of ServiceInterface Service* = ref object of ServiceInterface
fleetConfiguration: FleetConfiguration
generatedAccounts: seq[GeneratedAccountDto] generatedAccounts: seq[GeneratedAccountDto]
loggedInAccount: AccountDto loggedInAccount: AccountDto
importedAccount: GeneratedAccountDto importedAccount: GeneratedAccountDto
@ -26,8 +28,9 @@ type
method delete*(self: Service) = method delete*(self: Service) =
discard discard
proc newService*(): Service = proc newService*(fleetConfiguration: FleetConfiguration): Service =
result = Service() result = Service()
result.fleetConfiguration = fleetConfiguration
result.isFirstTimeAccountLogin = false result.isFirstTimeAccountLogin = false
method getLoggedInAccount*(self: Service): AccountDto = method getLoggedInAccount*(self: Service): AccountDto =
@ -40,11 +43,11 @@ method isFirstTimeAccountLogin*(self: Service): bool =
return self.isFirstTimeAccountLogin return self.isFirstTimeAccountLogin
method generateAlias*(self: Service, publicKey: string): string = method generateAlias*(self: Service, publicKey: string): string =
return status_go.generateAlias(publicKey).result.getStr return status_account.generateAlias(publicKey).result.getStr
method init*(self: Service) = method init*(self: Service) =
try: try:
let response = status_go.generateAddresses(PATHS) let response = status_account.generateAddresses(PATHS)
self.generatedAccounts = map(response.result.getElems(), self.generatedAccounts = map(response.result.getElems(),
proc(x: JsonNode): GeneratedAccountDto = toGeneratedAccountDto(x)) proc(x: JsonNode): GeneratedAccountDto = toGeneratedAccountDto(x))
@ -52,8 +55,7 @@ method init*(self: Service) =
for account in self.generatedAccounts.mitems: for account in self.generatedAccounts.mitems:
account.alias = self.generateAlias(account.derivedAccounts.whisper.publicKey) account.alias = self.generateAlias(account.derivedAccounts.whisper.publicKey)
let responseIdenticon = status_go.generateIdenticon( let responseIdenticon = status_account.generateIdenticon(account.derivedAccounts.whisper.publicKey)
account.derivedAccounts.whisper.publicKey)
account.identicon = responseIdenticon.result.getStr account.identicon = responseIdenticon.result.getStr
except Exception as e: except Exception as e:
@ -67,7 +69,7 @@ method clear*(self: Service) =
method validateMnemonic*(self: Service, mnemonic: string): string = method validateMnemonic*(self: Service, mnemonic: string): string =
try: try:
let response = status_go_general.validateMnemonic(mnemonic) let response = status_general.validateMnemonic(mnemonic)
var error = "response doesn't contain \"error\"" var error = "response doesn't contain \"error\""
if(response.result.contains("error")): if(response.result.contains("error")):
@ -88,10 +90,9 @@ method generatedAccounts*(self: Service): seq[GeneratedAccountDto] =
method openedAccounts*(self: Service): seq[AccountDto] = method openedAccounts*(self: Service): seq[AccountDto] =
try: try:
let response = status_go.openedAccounts(main_constants.STATUSGODIR) let response = status_account.openedAccounts(main_constants.STATUSGODIR)
let accounts = map(response.result.getElems(), let accounts = map(response.result.getElems(), proc(x: JsonNode): AccountDto = toAccountDto(x))
proc(x: JsonNode): AccountDto = toAccountDto(x))
return accounts return accounts
@ -101,7 +102,7 @@ method openedAccounts*(self: Service): seq[AccountDto] =
proc storeDerivedAccounts(self: Service, accountId, hashedPassword: string, proc storeDerivedAccounts(self: Service, accountId, hashedPassword: string,
paths: seq[string]): DerivedAccounts = paths: seq[string]): DerivedAccounts =
try: try:
let response = status_go.storeDerivedAccounts(accountId, hashedPassword, paths) let response = status_account.storeDerivedAccounts(accountId, hashedPassword, paths)
result = toDerivedAccounts(response.result) result = toDerivedAccounts(response.result)
except Exception as e: except Exception as e:
@ -110,8 +111,7 @@ proc storeDerivedAccounts(self: Service, accountId, hashedPassword: string,
proc saveAccountAndLogin(self: Service, hashedPassword: string, account, proc saveAccountAndLogin(self: Service, hashedPassword: string, account,
subaccounts, settings, config: JsonNode): AccountDto = subaccounts, settings, config: JsonNode): AccountDto =
try: try:
let response = status_go.saveAccountAndLogin(hashedPassword, account, let response = status_account.saveAccountAndLogin(hashedPassword, account, subaccounts, settings, config)
subaccounts, settings, config)
var error = "response doesn't contain \"error\"" var error = "response doesn't contain \"error\""
if(response.result.contains("error")): if(response.result.contains("error")):
@ -213,8 +213,7 @@ proc getAccountSettings(self: Service, accountId: string,
if(self.importedAccount.id == accountId): if(self.importedAccount.id == accountId):
return self.prepareAccountSettingsJsonObject(self.importedAccount, installationId) return self.prepareAccountSettingsJsonObject(self.importedAccount, installationId)
proc getDefaultNodeConfig*(self: Service, fleetConfig: FleetConfig, proc getDefaultNodeConfig*(self: Service, installationId: string): JsonNode =
installationId: string): JsonNode =
let networkConfig = getNetworkConfig(DEFAULT_NETWORK_NAME) let networkConfig = getNetworkConfig(DEFAULT_NETWORK_NAME)
let upstreamUrl = networkConfig["config"]["UpstreamConfig"]["URL"] let upstreamUrl = networkConfig["config"]["UpstreamConfig"]["URL"]
let fleet = Fleet.PROD let fleet = Fleet.PROD
@ -223,10 +222,10 @@ proc getDefaultNodeConfig*(self: Service, fleetConfig: FleetConfig,
newDataDir.removeSuffix("_rpc") newDataDir.removeSuffix("_rpc")
result = NODE_CONFIG.copy() result = NODE_CONFIG.copy()
result["ClusterConfig"]["Fleet"] = newJString($fleet) result["ClusterConfig"]["Fleet"] = newJString($fleet)
result["ClusterConfig"]["BootNodes"] = %* fleetConfig.getNodes(fleet, FleetNodes.Bootnodes) result["ClusterConfig"]["BootNodes"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Bootnodes)
result["ClusterConfig"]["TrustedMailServers"] = %* fleetConfig.getNodes(fleet, FleetNodes.Mailservers) result["ClusterConfig"]["TrustedMailServers"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Mailservers)
result["ClusterConfig"]["StaticNodes"] = %* fleetConfig.getNodes(fleet, FleetNodes.Whisper) result["ClusterConfig"]["StaticNodes"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Whisper)
result["ClusterConfig"]["RendezvousNodes"] = %* fleetConfig.getNodes(fleet, FleetNodes.Rendezvous) result["ClusterConfig"]["RendezvousNodes"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Rendezvous)
result["NetworkId"] = networkConfig["config"]["NetworkId"] result["NetworkId"] = networkConfig["config"]["NetworkId"]
result["DataDir"] = newDataDir.newJString() result["DataDir"] = newDataDir.newJString()
result["UpstreamConfig"]["Enabled"] = networkConfig["config"]["UpstreamConfig"]["Enabled"] result["UpstreamConfig"]["Enabled"] = networkConfig["config"]["UpstreamConfig"]["Enabled"]
@ -235,22 +234,21 @@ proc getDefaultNodeConfig*(self: Service, fleetConfig: FleetConfig,
# TODO: fleet.status.im should have different sections depending on the node type # TODO: fleet.status.im should have different sections depending on the node type
# or maybe it's not necessary because a node has the identify protocol # or maybe it's not necessary because a node has the identify protocol
result["ClusterConfig"]["RelayNodes"] = %* fleetConfig.getNodes(fleet, FleetNodes.Waku) result["ClusterConfig"]["RelayNodes"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Waku)
result["ClusterConfig"]["StoreNodes"] = %* fleetConfig.getNodes(fleet, FleetNodes.Waku) result["ClusterConfig"]["StoreNodes"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Waku)
result["ClusterConfig"]["FilterNodes"] = %* fleetConfig.getNodes(fleet, FleetNodes.Waku) result["ClusterConfig"]["FilterNodes"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Waku)
result["ClusterConfig"]["LightpushNodes"] = %* fleetConfig.getNodes(fleet, FleetNodes.Waku) result["ClusterConfig"]["LightpushNodes"] = %* self.fleetConfiguration.getNodes(fleet, FleetNodes.Waku)
# TODO: commented since it's not necessary (we do the connections thru C bindings). Enable it thru an option once status-nodes are able to be configured in desktop # TODO: commented since it's not necessary (we do the connections thru C bindings). Enable it thru an option once status-nodes are able to be configured in desktop
# result["ListenAddr"] = if existsEnv("STATUS_PORT"): newJString("0.0.0.0:" & $getEnv("STATUS_PORT")) else: newJString("0.0.0.0:30305") # result["ListenAddr"] = if existsEnv("STATUS_PORT"): newJString("0.0.0.0:" & $getEnv("STATUS_PORT")) else: newJString("0.0.0.0:30305")
method setupAccount*(self: Service, fleetConfig: FleetConfig, accountId, method setupAccount*(self: Service, accountId, password: string): bool =
password: string): bool =
try: try:
let installationId = $genUUID() let installationId = $genUUID()
let accountDataJson = self.getAccountDataForAccountId(accountId) let accountDataJson = self.getAccountDataForAccountId(accountId)
let subaccountDataJson = self.getSubaccountDataForAccountId(accountId) let subaccountDataJson = self.getSubaccountDataForAccountId(accountId)
let settingsJson = self.getAccountSettings(accountId, installationId) let settingsJson = self.getAccountSettings(accountId, installationId)
let nodeConfigJson = self.getDefaultNodeConfig(fleetConfig, installationId) let nodeConfigJson = self.getDefaultNodeConfig(installationId)
if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
nodeConfigJson.isNil): nodeConfigJson.isNil):
@ -261,8 +259,8 @@ method setupAccount*(self: Service, fleetConfig: FleetConfig, accountId,
let hashedPassword = hashString(password) let hashedPassword = hashString(password)
discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS) discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS)
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson, subaccountDataJson, settingsJson,
accountDataJson, subaccountDataJson, settingsJson, nodeConfigJson) nodeConfigJson)
return self.getLoggedInAccount.isValid() return self.getLoggedInAccount.isValid()
@ -272,16 +270,15 @@ method setupAccount*(self: Service, fleetConfig: FleetConfig, accountId,
method importMnemonic*(self: Service, mnemonic: string): bool = method importMnemonic*(self: Service, mnemonic: string): bool =
try: try:
let response = status_go.multiAccountImportMnemonic(mnemonic) let response = status_account.multiAccountImportMnemonic(mnemonic)
self.importedAccount = toGeneratedAccountDto(response.result) self.importedAccount = toGeneratedAccountDto(response.result)
let responseDerived = status_go.deriveAccounts(self.importedAccount.id, PATHS) let responseDerived = status_account.deriveAccounts(self.importedAccount.id, PATHS)
self.importedAccount.derivedAccounts = toDerivedAccounts(responseDerived.result) self.importedAccount.derivedAccounts = toDerivedAccounts(responseDerived.result)
self.importedAccount.alias= self.generateAlias(self.importedAccount.derivedAccounts.whisper.publicKey) self.importedAccount.alias= self.generateAlias(self.importedAccount.derivedAccounts.whisper.publicKey)
let responseIdenticon = status_go.generateIdenticon( let responseIdenticon = status_account.generateIdenticon(self.importedAccount.derivedAccounts.whisper.publicKey)
self.importedAccount.derivedAccounts.whisper.publicKey)
self.importedAccount.identicon = responseIdenticon.result.getStr self.importedAccount.identicon = responseIdenticon.result.getStr
return self.importedAccount.isValid() return self.importedAccount.isValid()
@ -301,7 +298,7 @@ method login*(self: Service, account: AccountDto, password: string): string =
elif(img.imgType == "large"): elif(img.imgType == "large"):
largeImage = img.uri largeImage = img.uri
let response = status_go.login(account.name, account.keyUid, hashedPassword, account.identicon, thumbnailImage, let response = status_account.login(account.name, account.keyUid, hashedPassword, account.identicon, thumbnailImage,
largeImage) largeImage)
var error = "response doesn't contain \"error\"" var error = "response doesn't contain \"error\""

View File

@ -1,11 +1,8 @@
import ./dto/accounts as dto_accounts import ./dto/accounts as dto_accounts
import ./dto/generated_accounts as dto_generated_accounts import ./dto/generated_accounts as dto_generated_accounts
import status/fleet as status_lib_fleet
export dto_accounts export dto_accounts
export dto_generated_accounts export dto_generated_accounts
export status_lib_fleet
type type
ServiceInterface* {.pure inheritable.} = ref object of RootObj ServiceInterface* {.pure inheritable.} = ref object of RootObj
@ -17,37 +14,31 @@ method delete*(self: ServiceInterface) {.base.} =
method init*(self: ServiceInterface) {.base.} = method init*(self: ServiceInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method openedAccounts*(self: ServiceInterface): method openedAccounts*(self: ServiceInterface): seq[AccountDto] {.base.} =
seq[AccountDto] {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method generatedAccounts*(self: ServiceInterface): method generatedAccounts*(self: ServiceInterface): seq[GeneratedAccountDto] {.base.} =
seq[GeneratedAccountDto] {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setupAccount*(self: ServiceInterface, fleetConfig: FleetConfig, method setupAccount*(self: ServiceInterface, accountId, password: string): bool {.base.} =
accountId, password: string): bool {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getLoggedInAccount*(self: ServiceInterface): AccountDto {.base.} = method getLoggedInAccount*(self: ServiceInterface): AccountDto {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method getImportedAccount*(self: ServiceInterface): GeneratedAccountDto method getImportedAccount*(self: ServiceInterface): GeneratedAccountDto {.base.} =
{.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method isFirstTimeAccountLogin*(self: ServiceInterface): bool {.base.} = method isFirstTimeAccountLogin*(self: ServiceInterface): bool {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method validateMnemonic*(self: ServiceInterface, mnemonic: string): method validateMnemonic*(self: ServiceInterface, mnemonic: string): string {.base.} =
string {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method importMnemonic*(self: ServiceInterface, mnemonic: string): bool {.base.} = method importMnemonic*(self: ServiceInterface, mnemonic: string): bool {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method login*(self: ServiceInterface, account: AccountDto, password: string): method login*(self: ServiceInterface, account: AccountDto, password: string): string {.base.} =
string {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method clear*(self: ServiceInterface) {.base.} = method clear*(self: ServiceInterface) {.base.} =