Skip to content

Commit

Permalink
Merge pull request #1325 from radixdlt/refactor/ABW-4157-remove-profi…
Browse files Browse the repository at this point in the history
…le-security-state-usage

Remove direct usages of Unsecured security state
  • Loading branch information
micbakos-rdx authored Feb 11, 2025
2 parents 825c7cc + eddb096 commit eea2486
Show file tree
Hide file tree
Showing 25 changed files with 124 additions and 550 deletions.
16 changes: 0 additions & 16 deletions app/src/main/java/com/babylon/wallet/android/WalletApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.babylon.wallet.android

import android.content.Intent
import android.provider.Settings
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Text
Expand Down Expand Up @@ -42,7 +41,6 @@ import com.babylon.wallet.android.presentation.navigation.NavigationHost
import com.babylon.wallet.android.presentation.navigation.PriorityRoutes
import com.babylon.wallet.android.presentation.rootdetection.ROUTE_ROOT_DETECTION
import com.babylon.wallet.android.presentation.transaction.transactionReview
import com.babylon.wallet.android.presentation.ui.composables.BDFSErrorDialog
import com.babylon.wallet.android.presentation.ui.composables.BasicPromptAlertDialog
import com.babylon.wallet.android.presentation.ui.composables.FullScreen
import com.babylon.wallet.android.presentation.ui.composables.LocalDevBannerState
Expand Down Expand Up @@ -165,20 +163,6 @@ fun WalletApp(
dismissText = null
)
}
val olympiaErrorState = state.olympiaErrorState
if (olympiaErrorState != null) {
BackHandler {}
BDFSErrorDialog(
finish = {
if (!olympiaErrorState.isCountdownActive) {
mainViewModel.clearOlympiaError()
}
},
title = stringResource(id = R.string.homePage_profileOlympiaError_title),
message = stringResource(id = R.string.homePage_profileOlympiaError_subtitle),
state = olympiaErrorState
)
}
state.dappRequestFailure?.let {
BasicPromptAlertDialog(
finish = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,10 @@ import com.radixdlt.sargon.extensions.indexInGlobalKeySpace
import com.radixdlt.sargon.extensions.indexInLocalKeySpace
import com.radixdlt.sargon.extensions.init
import com.radixdlt.sargon.extensions.lastPathComponent
import com.radixdlt.sargon.extensions.scheme
import com.radixdlt.sargon.extensions.unsecuredControllingFactorInstance
import rdx.works.core.sargon.asIdentifiable
import rdx.works.core.sargon.currentGateway
import rdx.works.core.sargon.derivationPathEntityIndex
import rdx.works.core.sargon.derivationPathScheme
import rdx.works.core.sargon.factorSourceId
import rdx.works.core.sargon.toFactorSourceId
import rdx.works.profile.domain.GetProfileUseCase
import javax.inject.Inject
Expand Down Expand Up @@ -49,10 +48,12 @@ class ResolveDerivationPathsForRecoveryScanUseCase @Inject constructor(
network
?.accounts
?.filter { account ->
account.factorSourceId == factorSourceId && account.derivationPathScheme == derivationPathScheme
val transactionSigning = account.unsecuredControllingFactorInstance ?: return@filter false
transactionSigning.factorSourceId == factorSourceId.value &&
transactionSigning.publicKey.derivationPath.scheme == derivationPathScheme
}
?.map { account ->
account.derivationPathEntityIndex.indexInLocalKeySpace
?.mapNotNull { account ->
account.unsecuredControllingFactorInstance?.publicKey?.derivationPath?.lastPathComponent?.indexInLocalKeySpace
}
?.toSet().orEmpty()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import com.radixdlt.sargon.FactorSourceKind
import com.radixdlt.sargon.extensions.asGeneral
import com.radixdlt.sargon.extensions.hex
import com.radixdlt.sargon.extensions.string
import com.radixdlt.sargon.extensions.unsecuredControllingFactorInstance
import rdx.works.core.UUIDGenerator
import rdx.works.core.sargon.activeAccountOnCurrentNetwork
import rdx.works.core.sargon.factorSourceById
import rdx.works.core.sargon.transactionSigningFactorInstance
import rdx.works.profile.domain.GetProfileUseCase
import javax.inject.Inject

Expand All @@ -27,21 +27,23 @@ class VerifyAddressOnLedgerUseCase @Inject constructor(
withAddress = address
) ?: return Result.failure(Exception("No account with address: $address"))

val factorInstance = account.securityState.transactionSigningFactorInstance
if (factorInstance.factorSourceId.kind != FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET) {
return Result.failure(Exception("Account $address is not a Ledger backed account"))
}
val ledgerControlledFactorInstance = account.unsecuredControllingFactorInstance?.takeIf {
it.factorSourceId.kind == FactorSourceKind.LEDGER_HQ_HARDWARE_WALLET
} ?: return Result.failure(Exception("Account $address is not a Ledger backed account"))

val ledgerFactorSource = (profile.factorSourceById(factorInstance.factorSourceId.asGeneral()) as? FactorSource.Ledger)
?: return Result.failure(Exception("Ledger factor source not found for $factorInstance"))
val ledgerFactorSource = profile.factorSourceById(
id = ledgerControlledFactorInstance.factorSourceId.asGeneral()
) as? FactorSource.Ledger ?: return Result.failure(
Exception("Ledger factor source not found for ${ledgerControlledFactorInstance.factorSourceId}")
)

return ledgerMessenger.deriveAndDisplayAddressRequest(
interactionId = UUIDGenerator.uuid().toString(),
keyParameters = LedgerInteractionRequest.KeyParameters.from(factorInstance.publicKey.derivationPath),
keyParameters = LedgerInteractionRequest.KeyParameters.from(ledgerControlledFactorInstance.publicKey.derivationPath),
ledgerDevice = LedgerInteractionRequest.LedgerDevice.from(ledgerFactorSource)
).fold(
onSuccess = { response ->
if (response.derivedAddress.derivedKey.publicKeyHex != factorInstance.publicKey.publicKey.hex) {
if (response.derivedAddress.derivedKey.publicKeyHex != ledgerControlledFactorInstance.publicKey.publicKey.hex) {
return Result.failure(Exception("Public key does not match the derived pub key from Ledger"))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import rdx.works.core.domain.cloudbackup.BackupWarning
import rdx.works.core.preferences.PreferencesManager
import rdx.works.core.sargon.allEntitiesOnCurrentNetwork
import rdx.works.core.sargon.factorSourceById
import rdx.works.core.sargon.factorSourceId
import rdx.works.core.sargon.isDeleted
import rdx.works.profile.data.repository.CheckKeystoreIntegrityUseCase
import rdx.works.profile.data.repository.MnemonicRepository
Expand Down Expand Up @@ -48,9 +47,9 @@ class GetEntitiesWithSecurityPromptUseCase @Inject constructor(
backedUpFactorSourceIds: Set<FactorSourceId.Hash>,
backupState: BackupState
): EntityWithSecurityPrompt? {
val factorSourceId = entity.securityState.factorSourceId as? FactorSourceId.Hash ?: return null
val factorSource = getProfileUseCase().factorSourceById(factorSourceId) as? FactorSource.Device ?: return null
val unsecuredFactorSourceId = entity.unsecuredControllingFactorInstance?.factorSourceId?.asGeneral() ?: return null

val factorSource = getProfileUseCase().factorSourceById(unsecuredFactorSourceId) as? FactorSource.Device ?: return null
val prompts = mutableSetOf<SecurityPromptType>().apply {
backupState.backupWarning?.let { backupWarning ->
when (backupWarning) {
Expand All @@ -64,7 +63,7 @@ class GetEntitiesWithSecurityPromptUseCase @Inject constructor(

if (!mnemonicRepository.mnemonicExist(factorSource.value.id.asGeneral())) {
add(SecurityPromptType.RECOVERY_REQUIRED)
} else if (!backedUpFactorSourceIds.contains(factorSourceId)) {
} else if (!backedUpFactorSourceIds.contains(unsecuredFactorSourceId)) {
add(SecurityPromptType.WRITE_DOWN_SEED_PHRASE)
}
}.toSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import rdx.works.core.domain.cloudbackup.BackupState
import rdx.works.core.sargon.factorSourceId
import rdx.works.core.sargon.isNotHidden
import rdx.works.profile.domain.backup.GetBackupStateUseCase
import timber.log.Timber
Expand Down Expand Up @@ -70,14 +69,14 @@ class GetSecurityProblemsUseCase @Inject constructor(
.filter { entityWithSecurityPrompt ->
entityWithSecurityPrompt.prompts.contains(SecurityPromptType.WRITE_DOWN_SEED_PHRASE)
}
val factorSourceIdsNeedBackup = entitiesNeedBackup.map { entityWithSecurityPrompt ->
entityWithSecurityPrompt.entity.securityState.factorSourceId
val factorSourceIdsNeedBackup = entitiesNeedBackup.mapNotNull { entityWithSecurityPrompt ->
entityWithSecurityPrompt.entity.unsecuredControllingFactorInstance?.factorSourceId
}.toSet()
// not hidden and hidden accounts that need to write down seed phrase
val (activeAccountAddressesNeedBackup, hiddenAccountAddressesNeedBackup) = entitiesNeedBackup
.filter { entityWithSecurityPrompt ->
entityWithSecurityPrompt.entity is ProfileEntity.AccountEntity &&
factorSourceIdsNeedBackup.contains(entityWithSecurityPrompt.entity.securityState.factorSourceId)
entityWithSecurityPrompt.entity.unsecuredControllingFactorInstance?.factorSourceId in factorSourceIdsNeedBackup
}
.map { entityWithSecurityPrompt -> entityWithSecurityPrompt.entity as ProfileEntity.AccountEntity }
.partition { accountEntity -> accountEntity.isNotHidden() }
Expand All @@ -88,7 +87,7 @@ class GetSecurityProblemsUseCase @Inject constructor(
val (activePersonaAddressesNeedBackup, hiddenPersonaAddressesNeedBackup) = entitiesNeedBackup
.filter { entityWithSecurityPrompt ->
entityWithSecurityPrompt.entity is ProfileEntity.PersonaEntity &&
factorSourceIdsNeedBackup.contains(entityWithSecurityPrompt.entity.securityState.factorSourceId)
entityWithSecurityPrompt.entity.unsecuredControllingFactorInstance?.factorSourceId in factorSourceIdsNeedBackup
}
.map { entityWithSecurityPrompt -> entityWithSecurityPrompt.entity as ProfileEntity.PersonaEntity }
.partition { personaEntity -> personaEntity.isNotHidden() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import com.radixdlt.sargon.AccountAddress
import com.radixdlt.sargon.Address
import com.radixdlt.sargon.AddressFormat
import com.radixdlt.sargon.extensions.formatted
import com.radixdlt.sargon.extensions.isUnsecuredLedgerControlled
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toPersistentList
Expand All @@ -25,7 +26,6 @@ import rdx.works.core.domain.resources.Resource
import rdx.works.core.mapWhen
import rdx.works.core.sargon.activeAccountsOnCurrentNetwork
import rdx.works.core.sargon.activePersonasOnCurrentNetwork
import rdx.works.core.sargon.isLedgerAccount
import rdx.works.profile.domain.GetProfileUseCase
import timber.log.Timber
import javax.inject.Inject
Expand Down Expand Up @@ -78,7 +78,7 @@ class AddressDetailsDialogViewModel @Inject constructor(
if (actionableAddress is ActionableAddress.Address && actionableAddress.address is Address.Account) {
val account = getProfileUseCase().activeAccountsOnCurrentNetwork.find { it.address == actionableAddress.address.v1 }

if (account?.isLedgerAccount == true) {
if (account?.isUnsecuredLedgerControlled == true) {
_state.update { state ->
state.copy(
sections = state.sections.toMutableList().apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.babylon.wallet.android.presentation.accessfactorsources.AccessFactorS
import com.radixdlt.sargon.AuthorizationPurpose
import com.radixdlt.sargon.AuthorizationResponse
import com.radixdlt.sargon.CommonException
import com.radixdlt.sargon.FactorSource
import com.radixdlt.sargon.HostInteractor
import com.radixdlt.sargon.KeyDerivationRequest
import com.radixdlt.sargon.KeyDerivationResponse
Expand All @@ -16,6 +17,7 @@ import com.radixdlt.sargon.SignRequestOfTransactionIntent
import com.radixdlt.sargon.SignResponseOfAuthIntentHash
import com.radixdlt.sargon.SignResponseOfSubintentHash
import com.radixdlt.sargon.SignResponseOfTransactionIntentHash
import com.radixdlt.sargon.SpotCheckResponse
import com.radixdlt.sargon.os.signing.into
import com.radixdlt.sargon.os.signing.intoSargon
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -151,6 +153,10 @@ class WalletInteractor(
)
}

override suspend fun spotCheck(factorSource: FactorSource, allowSkip: Boolean): SpotCheckResponse {
throw CommonException.HostInteractionAborted()
}

companion object {

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,12 @@ import com.radixdlt.sargon.NetworkId
import com.radixdlt.sargon.Persona
import com.radixdlt.sargon.ProfileState
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import rdx.works.core.preferences.PreferencesManager
import rdx.works.core.sargon.currentGateway
Expand All @@ -41,7 +38,6 @@ import rdx.works.core.sargon.isAdvancedLockEnabled
import rdx.works.profile.cloudbackup.domain.CloudBackupErrorStream
import rdx.works.profile.cloudbackup.model.BackupServiceException.ClaimedByAnotherDevice
import rdx.works.profile.data.repository.CheckKeystoreIntegrityUseCase
import rdx.works.profile.domain.CheckEntitiesCreatedWithOlympiaUseCase
import rdx.works.profile.domain.GetProfileUseCase
import timber.log.Timber
import javax.inject.Inject
Expand All @@ -54,16 +50,13 @@ class MainViewModel @Inject constructor(
private val deviceCapabilityHelper: DeviceCapabilityHelper,
private val preferencesManager: PreferencesManager,
private val checkKeystoreIntegrityUseCase: CheckKeystoreIntegrityUseCase,
private val checkEntitiesCreatedWithOlympiaUseCase: CheckEntitiesCreatedWithOlympiaUseCase,
private val observeAccountsAndSyncWithConnectorExtensionUseCase: ObserveAccountsAndSyncWithConnectorExtensionUseCase,
private val cloudBackupErrorStream: CloudBackupErrorStream,
private val processDeepLinkUseCase: ProcessDeepLinkUseCase,
private val appLockStateProvider: AppLockStateProvider,
private val incomingRequestsDelegate: IncomingRequestsDelegate
) : StateViewModel<MainViewModel.State>(), OneOffEventHandler<MainViewModel.Event> by OneOffEventHandlerImpl() {

private var countdownJob: Job? = null

val observeP2PLinks
get() = incomingRequestsDelegate.observeP2PLinks

Expand Down Expand Up @@ -207,10 +200,6 @@ class MainViewModel @Inject constructor(
_state.update { it.copy(dappRequestFailure = null) }
}

fun clearOlympiaError() {
_state.update { it.copy(olympiaErrorState = null) }
}

fun onAppToForeground() {
val isDeviceSecure = deviceCapabilityHelper.isDeviceSecure
_state.update { state ->
Expand All @@ -224,45 +213,13 @@ class MainViewModel @Inject constructor(
private fun runForegroundChecks() {
viewModelScope.launch {
checkKeystoreIntegrityUseCase()
if (_state.value.isDeviceSecure) {
val checkResult = checkEntitiesCreatedWithOlympiaUseCase()
if (checkResult.isAnyEntityCreatedWithOlympia) {
_state.update { state ->
state.copy(
olympiaErrorState = OlympiaErrorState(
affectedAccounts = checkResult.affectedAccounts,
affectedPersonas = checkResult.affectedPersonas
)
)
}
countdownJob?.cancel()
countdownJob = startOlympiaErrorCountdown()
return@launch
}
}
}
}

private fun startOlympiaErrorCountdown(): Job {
return viewModelScope.launch {
while (isActive && state.value.olympiaErrorState?.isCountdownActive == true) {
delay(TICK_MS)
_state.update { state ->
state.copy(
olympiaErrorState = state.olympiaErrorState?.copy(
secondsLeft = state.olympiaErrorState.secondsLeft - 1
)
)
}
}
}
}

data class State(
val initialAppState: AppState = AppState.Loading,
val showDeviceRootedWarning: Boolean = false,
val dappRequestFailure: UiMessage.ErrorMessage? = null,
val olympiaErrorState: OlympiaErrorState? = null,
val claimedByAnotherDeviceError: ClaimedByAnotherDevice? = null,
val showMobileConnectWarning: Boolean = false,
val isAdvancedLockEnabled: Boolean = false,
Expand All @@ -285,10 +242,6 @@ class MainViewModel @Inject constructor(
sealed class Event : OneOffEvent {
data class IncomingRequestEvent(val request: DappToWalletInteraction) : Event()
}

companion object {
private const val TICK_MS = 1000L
}
}

sealed interface AppState {
Expand Down
Loading

0 comments on commit eea2486

Please sign in to comment.