diff --git a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/receiver/BootReceiver.kt b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/receiver/BootReceiver.kt index cabe3a952e..598f68381f 100644 --- a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/receiver/BootReceiver.kt +++ b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/receiver/BootReceiver.kt @@ -30,7 +30,7 @@ class BootReceiver : BroadcastReceiver() { applicationScope.launch { if (settingsRepository.isAutoStartEnabled()) { if (tunnelManager.getState() != Tunnel.State.Down) return@launch - tunnelManager.start(true) + tunnelManager.start() } } } diff --git a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/android/tile/VpnQuickTile.kt b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/android/tile/VpnQuickTile.kt index f586e59d91..daf6cef71b 100644 --- a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/android/tile/VpnQuickTile.kt +++ b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/android/tile/VpnQuickTile.kt @@ -89,7 +89,7 @@ class VpnQuickTile : TileService(), LifecycleOwner { lifecycleScope.launch { when (tunnelManager.getState()) { Tunnel.State.Up -> tunnelManager.stop() - Tunnel.State.Down -> tunnelManager.start(true) + Tunnel.State.Down -> tunnelManager.start() else -> Unit } } diff --git a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/NymTunnelManager.kt b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/NymTunnelManager.kt index ecf0c20609..b651471747 100644 --- a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/NymTunnelManager.kt +++ b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/NymTunnelManager.kt @@ -26,7 +26,7 @@ import net.nymtech.nymvpn.util.extensions.toUserMessage import net.nymtech.vpn.backend.Backend import net.nymtech.vpn.backend.Tunnel import net.nymtech.vpn.model.BackendEvent -import net.nymtech.vpn.util.exceptions.NymVpnInitializeException +import net.nymtech.vpn.util.exceptions.BackendException import nym_vpn_lib.AccountLinks import nym_vpn_lib.AccountStateSummary import nym_vpn_lib.BandwidthEvent @@ -73,7 +73,7 @@ class NymTunnelManager @Inject constructor( } } - override suspend fun start(fromBackground: Boolean) { + override suspend fun start() { runCatching { // clear any error states emitBackendUiEvent(null) @@ -86,12 +86,12 @@ class NymTunnelManager @Inject constructor( backendEvent = ::onBackendEvent, credentialMode = settingsRepository.isCredentialMode(), ) - backend.get().start(tunnel, fromBackground, context.toUserAgent()) + backend.get().start(tunnel, context.toUserAgent()) }.onFailure { - if (it is NymVpnInitializeException) { + if (it is BackendException) { when (it) { - is NymVpnInitializeException.VpnAlreadyRunning -> Timber.w("Vpn already running") - is NymVpnInitializeException.VpnPermissionDenied -> launchVpnPermissionNotification() + is BackendException.VpnAlreadyRunning -> Timber.w("Vpn already running") + is BackendException.VpnPermissionDenied -> launchVpnPermissionNotification() } } else { Timber.e(it) diff --git a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/TunnelManager.kt b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/TunnelManager.kt index 8471c6df49..d3a5591ff7 100644 --- a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/TunnelManager.kt +++ b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/service/tunnel/TunnelManager.kt @@ -7,7 +7,7 @@ import nym_vpn_lib.AccountStateSummary interface TunnelManager { suspend fun stop() - suspend fun start(fromBackground: Boolean = true) + suspend fun start() suspend fun storeMnemonic(mnemonic: String) suspend fun isMnemonicStored(): Boolean suspend fun removeMnemonic() diff --git a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/ShortcutActivity.kt b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/ShortcutActivity.kt index e9dd254b2f..0ff74dc364 100644 --- a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/ShortcutActivity.kt +++ b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/ShortcutActivity.kt @@ -33,11 +33,11 @@ class ShortcutActivity : ComponentActivity() { when (intent.action) { ShortcutAction.START_MIXNET.name -> { settingsRepository.setVpnMode(Tunnel.Mode.FIVE_HOP_MIXNET) - tunnelManager.start(true) + tunnelManager.start() } ShortcutAction.START_WG.name -> { settingsRepository.setVpnMode(Tunnel.Mode.TWO_HOP_MIXNET) - tunnelManager.start(true) + tunnelManager.start() } ShortcutAction.STOP.name -> tunnelManager.stop() } diff --git a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/screens/main/MainViewModel.kt b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/screens/main/MainViewModel.kt index 7859ebebbd..ac06be95c4 100644 --- a/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/screens/main/MainViewModel.kt +++ b/nym-vpn-android/app/src/main/java/net/nymtech/nymvpn/ui/screens/main/MainViewModel.kt @@ -52,7 +52,7 @@ constructor( } fun onConnect() = viewModelScope.launch { - tunnelManager.start(false) + tunnelManager.start() } fun onDisconnect() = viewModelScope.launch { diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Backend.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Backend.kt index 0553d48384..bb4a811921 100644 --- a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Backend.kt +++ b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Backend.kt @@ -27,7 +27,7 @@ interface Backend { suspend fun removeMnemonic() - suspend fun start(tunnel: Tunnel, background: Boolean, userAgent: UserAgent) + suspend fun start(tunnel: Tunnel, userAgent: UserAgent) suspend fun stop() diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/NymBackend.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/NymBackend.kt index 150545d225..b79f034d3b 100644 --- a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/NymBackend.kt +++ b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/NymBackend.kt @@ -13,22 +13,22 @@ import com.getkeepsafe.relinker.ReLinker.LoadListener import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import net.nymtech.vpn.model.BackendEvent import net.nymtech.vpn.model.Country import net.nymtech.vpn.service.network.NetworkConnectivityService +import net.nymtech.vpn.service.network.NetworkService import net.nymtech.vpn.service.network.NetworkStatus -import net.nymtech.vpn.util.Action import net.nymtech.vpn.util.Constants import net.nymtech.vpn.util.Constants.LOG_LEVEL import net.nymtech.vpn.util.LifecycleVpnService import net.nymtech.vpn.util.NotificationManager import net.nymtech.vpn.util.SingletonHolder -import net.nymtech.vpn.util.exceptions.NymVpnInitializeException +import net.nymtech.vpn.util.exceptions.BackendException import net.nymtech.vpn.util.extensions.asTunnelState import net.nymtech.vpn.util.extensions.startServiceByClass +import net.nymtech.vpn.util.extensions.waitForTrue import nym_vpn_lib.AccountLinks import nym_vpn_lib.AccountStateSummary import nym_vpn_lib.AndroidTunProvider @@ -53,7 +53,6 @@ import nym_vpn_lib.waitForRegisterDevice import nym_vpn_lib.waitForUpdateAccount import nym_vpn_lib.waitForUpdateDevice import timber.log.Timber -import java.util.concurrent.TimeoutException import java.util.concurrent.atomic.AtomicBoolean import kotlin.properties.Delegates @@ -100,6 +99,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat override suspend fun init(environment: Tunnel.Environment, credentialMode: Boolean?) { return withContext(ioDispatcher) { runCatching { + Os.setenv("RUST_LOG", LOG_LEVEL, true) initLogger(null) initEnvironment(environment) nym_vpn_lib.configureLib(storagePath, credentialMode) @@ -110,12 +110,12 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat } } - private fun onNetworkStateChange(networkStatus: NetworkStatus) { + private fun onNetworkStatusChange(networkStatus: NetworkStatus) { this.networkStatus = networkStatus updateObservers() } - private fun addObserver(observer: ConnectivityObserver) { + private fun addConnectivityObserver(observer: ConnectivityObserver) { observers.add(observer) updateObservers() } @@ -136,26 +136,9 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat observers.remove(observer) } - suspend fun waitForInit() { - runCatching { - val startTime = System.currentTimeMillis() - val timeout = 5000L - while (System.currentTimeMillis() - startTime < timeout) { - if (initialized.get()) { - return - } - delay(10) - } - throw TimeoutException("Failed to initialize backend") - }.onFailure { - Timber.e(it) - } - } - private suspend fun initEnvironment(environment: Tunnel.Environment) { withContext(ioDispatcher) { runCatching { - Os.setenv("RUST_LOG", LOG_LEVEL, true) initEnvironment(environment.networkName()) }.onFailure { Timber.w("Failed to setup environment, defaulting to bundle mainnet") @@ -167,7 +150,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat @Throws(VpnException::class) override suspend fun getAccountSummary(): AccountStateSummary { return withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() nym_vpn_lib.getAccountState() } } @@ -175,7 +158,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat @Throws(VpnException::class) override suspend fun getAccountLinks(): AccountLinks { return withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() nym_vpn_lib.getAccountLinks(getCurrentLocaleCountryCode()) } } @@ -194,7 +177,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat override suspend fun storeMnemonic(mnemonic: String) { withContext(ioDispatcher) { try { - waitForInit() + initialized.waitForTrue() storeAccountMnemonic(mnemonic) waitForUpdateAccount() waitForUpdateDevice() @@ -209,14 +192,14 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat @Throws(VpnException::class) override suspend fun isMnemonicStored(): Boolean { return withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() isAccountMnemonicStored() } } override suspend fun getDeviceIdentity(): String { return withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() nym_vpn_lib.getDeviceIdentity() } } @@ -224,7 +207,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat @Throws(VpnException::class) override suspend fun removeMnemonic() { withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() forgetAccount() } } @@ -239,44 +222,45 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat override suspend fun getSystemMessages(): List { return withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() nym_vpn_lib.getSystemMessages() } } - @Throws(NymVpnInitializeException::class) - override suspend fun start(tunnel: Tunnel, background: Boolean, userAgent: UserAgent) { + override suspend fun start(tunnel: Tunnel, userAgent: UserAgent) { withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() val state = getState() - // TODO handle changes to tunnel while tunnel is up in future - if (state != Tunnel.State.Down) throw NymVpnInitializeException.VpnAlreadyRunning() + if (state != Tunnel.State.Down) throw BackendException.VpnAlreadyRunning() this@NymBackend.tunnel = tunnel onStateChange(Tunnel.State.InitializingClient) - if (android.net.VpnService.prepare(context) != null) throw NymVpnInitializeException.VpnPermissionDenied() - startVpn(tunnel, background, userAgent) + if (android.net.VpnService.prepare(context) != null) throw BackendException.VpnPermissionDenied() + startVpn(tunnel, userAgent) } } - private suspend fun startVpn(tunnel: Tunnel, background: Boolean, userAgent: UserAgent) { + private suspend fun startServices() { + if (!vpnService.isCompleted) context.startServiceByClass(VpnService::class.java) + if (!stateMachineService.isCompleted) context.startServiceByClass(StateMachineService::class.java) + val vpnService = vpnService.await() + val stateMachineService = stateMachineService.await() + vpnService.setOwner(this) + stateMachineService.setOwner(this) + } + + private suspend fun startVpn(tunnel: Tunnel, userAgent: UserAgent) { withContext(ioDispatcher) { - if (!initialized.get()) init(tunnel.environment, tunnel.credentialMode) - if (!vpnService.isCompleted) context.startServiceByClass(background, VpnService::class.java) - context.startServiceByClass(background, StateMachineService::class.java) - val vpnService = vpnService.await() - val stateMachineService = stateMachineService.await() - val backend = this@NymBackend - vpnService.setOwner(backend) - stateMachineService.setOwner(backend) + initialized.waitForTrue() + startServices() try { startVpn( VpnConfig( tunnel.entryPoint, tunnel.exitPoint, - isTwoHop(tunnel.mode), - vpnService, + tunnel.mode.isTwoHop(), + vpnService.await(), storagePath, - backend, + this@NymBackend, tunnel.credentialMode, null, userAgent, @@ -297,7 +281,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat @OptIn(ExperimentalCoroutinesApi::class) override suspend fun stop() { withContext(ioDispatcher) { - waitForInit() + initialized.waitForTrue() runCatching { stopVpn() vpnService.getCompleted().stopSelf() @@ -312,11 +296,6 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat return state } - private fun isTwoHop(mode: Tunnel.Mode): Boolean = when (mode) { - Tunnel.Mode.TWO_HOP_MIXNET -> true - else -> false - } - override fun onEvent(event: TunnelEvent) { when (event) { is TunnelEvent.MixnetState -> { @@ -336,6 +315,8 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat internal class StateMachineService : LifecycleService() { + private lateinit var networkService: NetworkService + private var owner: NymBackend? = null private var wakeLock: PowerManager.WakeLock? = null @@ -349,6 +330,8 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat } override fun onCreate() { + super.onCreate() + networkService = NetworkConnectivityService(this) stateMachineService.complete(this) val notificationManager = NotificationManager.getInstance(this) ServiceCompat.startForeground( @@ -357,14 +340,15 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat notificationManager.createStateMachineNotification(), SYSTEM_EXEMPT_SERVICE_TYPE_ID, ) - lifecycleScope.launch { - NetworkConnectivityService(this@StateMachineService).networkStatus.collect { - Timber.d("New network event: $it") - owner?.onNetworkStateChange(it) - } - } + startNetworkStatusMonitor() initWakeLock() - super.onCreate() + } + + private fun startNetworkStatusMonitor() = lifecycleScope.launch { + networkService.networkStatus.collect { + Timber.d("New network event: $it") + owner?.onNetworkStatusChange(it) + } } override fun onDestroy() { @@ -403,17 +387,13 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat private var startId by Delegates.notNull() private val notificationManager = NotificationManager.getInstance(this) - companion object { - private const val VPN_NOTIFICATION_ID = 222 - } - private val builder: Builder get() = Builder() override fun onCreate() { + super.onCreate() Timber.d("Vpn service created") vpnService.complete(this) - super.onCreate() } override fun onDestroy() { @@ -427,11 +407,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat this.startId = startId vpnService.complete(this) intent?.let { - if (it.action == Action.START_FOREGROUND.name) { - startForeground(startId, notificationManager.createVpnRunningNotification()) - } else { - notificationManager.notify(notificationManager.createVpnRunningNotification(), VPN_NOTIFICATION_ID) - } + startForeground(startId, notificationManager.createVpnRunningNotification()) } return super.onStartCommand(intent, flags, startId) } @@ -487,7 +463,7 @@ class NymBackend private constructor(val context: Context) : Backend, TunnelStat } override fun addConnectivityObserver(observer: ConnectivityObserver) { - owner?.addObserver(observer) + owner?.addConnectivityObserver(observer) } override fun removeConnectivityObserver(observer: ConnectivityObserver) { diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Tunnel.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Tunnel.kt index 9059500044..5d7927d860 100644 --- a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Tunnel.kt +++ b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/backend/Tunnel.kt @@ -49,6 +49,9 @@ interface Tunnel { enum class Mode { FIVE_HOP_MIXNET, TWO_HOP_MIXNET, + ; + + fun isTwoHop() = this == TWO_HOP_MIXNET } /** diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/Action.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/Action.kt deleted file mode 100644 index f8329049d3..0000000000 --- a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/Action.kt +++ /dev/null @@ -1,8 +0,0 @@ -package net.nymtech.vpn.util - -enum class Action { - START, - START_FOREGROUND, - STOP_FOREGROUND, - STOP, -} diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/exceptions/BackendException.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/exceptions/BackendException.kt new file mode 100644 index 0000000000..1c853683ea --- /dev/null +++ b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/exceptions/BackendException.kt @@ -0,0 +1,6 @@ +package net.nymtech.vpn.util.exceptions + +sealed class BackendException : Exception() { + class VpnAlreadyRunning : BackendException() + class VpnPermissionDenied : BackendException() +} diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/exceptions/NymVpnInitializeException.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/exceptions/NymVpnInitializeException.kt deleted file mode 100644 index b6b4765dad..0000000000 --- a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/exceptions/NymVpnInitializeException.kt +++ /dev/null @@ -1,6 +0,0 @@ -package net.nymtech.vpn.util.exceptions - -sealed class NymVpnInitializeException : Exception() { - class VpnAlreadyRunning : NymVpnInitializeException() - class VpnPermissionDenied : NymVpnInitializeException() -} diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/ContextExtensions.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/ContextExtensions.kt index dfead5fcd2..776c38baeb 100644 --- a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/ContextExtensions.kt +++ b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/ContextExtensions.kt @@ -4,15 +4,12 @@ import android.app.Service import android.content.Context import android.content.Intent import android.os.Build -import net.nymtech.vpn.util.Action import timber.log.Timber -fun Context.startServiceByClass(background: Boolean, cls: Class) { +fun Context.startServiceByClass(cls: Class) { runCatching { - val intent = Intent(this, cls).apply { - if (background) action = Action.START_FOREGROUND.name - } - if (background && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val intent = Intent(this, cls) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(intent) } else { startService(intent) diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/Extensions.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/Extensions.kt new file mode 100644 index 0000000000..a7853132c8 --- /dev/null +++ b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/Extensions.kt @@ -0,0 +1,16 @@ +package net.nymtech.vpn.util.extensions + +import kotlinx.coroutines.delay +import java.util.concurrent.TimeoutException +import java.util.concurrent.atomic.AtomicBoolean + +suspend fun AtomicBoolean.waitForTrue(timeout: Long = 5000L) { + val startTime = System.currentTimeMillis() + while (System.currentTimeMillis() - startTime < timeout) { + if (this.get()) { + return + } + delay(10) + } + throw TimeoutException() +} diff --git a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/LibExtensions.kt b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/LibExtensions.kt index ef004ec58a..be9f612032 100644 --- a/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/LibExtensions.kt +++ b/nym-vpn-android/core/src/main/java/net/nymtech/vpn/util/extensions/LibExtensions.kt @@ -1,41 +1,9 @@ package net.nymtech.vpn.util.extensions -import android.system.Os import net.nymtech.vpn.backend.Tunnel -import nym_vpn_lib.NetworkEnvironment import nym_vpn_lib.TunnelEvent import nym_vpn_lib.TunnelState -fun NetworkEnvironment.export() { - Os.setenv("NETWORK_NAME", nymNetwork.networkName, true) - - Os.setenv("BECH32_PREFIX", nymNetwork.chainDetails.bech32AccountPrefix, true) - Os.setenv("MIX_DENOM", nymNetwork.chainDetails.mixDenom.base, true) - Os.setenv("MIX_DENOM_DISPLAY", nymNetwork.chainDetails.mixDenom.display, true) - Os.setenv("STAKE_DENOM", nymNetwork.chainDetails.stakeDenom.base, true) - Os.setenv("STAKE_DENOM_DISPLAY", nymNetwork.chainDetails.stakeDenom.display, true) - Os.setenv("DENOMS_EXPONENT", nymNetwork.chainDetails.mixDenom.displayExponent.toString(), true) - - Os.setenv("MIXNET_CONTRACT_ADDRESS", nymNetwork.contracts.mixnetContractAddress, true) - Os.setenv("VESTING_CONTRACT_ADDRESS", nymNetwork.contracts.vestingContractAddress, true) - Os.setenv("GROUP_CONTRACT_ADDRESS", nymNetwork.contracts.groupContractAddress, true) - Os.setenv("ECASH_CONTRACT_ADDRESS", nymNetwork.contracts.ecashContractAddress, true) - Os.setenv("MULTISIG_CONTRACT_ADDRESS", nymNetwork.contracts.multisigContractAddress, true) - Os.setenv("COCONUT_DKG_CONTRACT_ADDRESS", nymNetwork.contracts.coconutDkgContractAddress, true) - - nymNetwork.endpoints.firstOrNull()?.let { - Os.setenv("NYXD", it.nyxdUrl, true) - it.apiUrl?.let { url -> - Os.setenv("NYM_API", url, true) - } - it.websocketUrl?.let { url -> - Os.setenv("NYXD_WS", url, true) - } - } - - Os.setenv("NYM_VPN_API", nymVpnNetwork.nymVpnApiUrl, true) -} - fun TunnelEvent.NewState.asTunnelState(): Tunnel.State { return when (this.v1) { is TunnelState.Connected -> Tunnel.State.Up