[#1106] Zashi-iOS audit Issue G

- storedWallet is no longer held in memory
- unit tests fixed
This commit is contained in:
Lukas Korba 2024-03-06 16:12:23 +01:00
parent 6970b6ff60
commit b5a71c7bed
5 changed files with 36 additions and 56 deletions

View File

@ -240,15 +240,8 @@ extension RootReducer {
/// When initialization succeeds user is taken to the home screen. /// When initialization succeeds user is taken to the home screen.
case .initialization(.initializeSDK(let walletMode)): case .initialization(.initializeSDK(let walletMode)):
do { do {
state.storedWallet = try walletStorage.exportWallet() let storedWallet = try walletStorage.exportWallet()
let birthday = storedWallet.birthday?.value() ?? zcashSDKEnvironment.latestCheckpoint
guard let storedWallet = state.storedWallet else {
state.appInitializationState = .failed
state.alert = AlertState.cantLoadSeedPhrase()
return .none
}
let birthday = state.storedWallet?.birthday?.value() ?? zcashSDKEnvironment.latestCheckpoint
try mnemonic.isValid(storedWallet.seedPhrase.value()) try mnemonic.isValid(storedWallet.seedPhrase.value())
let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase.value()) let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase.value())
@ -273,35 +266,34 @@ extension RootReducer {
return .send(.initialization(.registerForSynchronizersUpdate)) return .send(.initialization(.registerForSynchronizersUpdate))
case .initialization(.checkBackupPhraseValidation): case .initialization(.checkBackupPhraseValidation):
guard let storedWallet = state.storedWallet else { do {
state.appInitializationState = .failed let storedWallet = try walletStorage.exportWallet()
state.alert = AlertState.cantLoadSeedPhrase() var landingDestination = RootReducer.DestinationState.Destination.tabs
return .none
}
var landingDestination = RootReducer.DestinationState.Destination.tabs
if !storedWallet.hasUserPassedPhraseBackupTest {
let phraseWords = mnemonic.asWords(storedWallet.seedPhrase.value())
let recoveryPhrase = RecoveryPhrase(words: phraseWords.map { $0.redacted }) if !storedWallet.hasUserPassedPhraseBackupTest {
state.phraseDisplayState.phrase = recoveryPhrase let phraseWords = mnemonic.asWords(storedWallet.seedPhrase.value())
state.phraseDisplayState.birthday = storedWallet.birthday
if let value = storedWallet.birthday?.value() { let recoveryPhrase = RecoveryPhrase(words: phraseWords.map { $0.redacted })
let latestBlock = numberFormatter.string(NSDecimalNumber(value: value)) state.phraseDisplayState.phrase = recoveryPhrase
state.phraseDisplayState.birthdayValue = "\(String(describing: latestBlock ?? ""))" state.phraseDisplayState.birthday = storedWallet.birthday
if let value = storedWallet.birthday?.value() {
let latestBlock = numberFormatter.string(NSDecimalNumber(value: value))
state.phraseDisplayState.birthdayValue = "\(String(describing: latestBlock ?? ""))"
}
landingDestination = .phraseDisplay
} }
landingDestination = .phraseDisplay
state.appInitializationState = .initialized
return .run { [landingDestination] send in
try await mainQueue.sleep(for: .seconds(2.5))
await send(.destination(.updateDestination(landingDestination)))
}
.cancellable(id: CancelId.timer, cancelInFlight: true)
} catch {
return Effect.send(.initialization(.initializationFailed(error.toZcashError())))
} }
state.appInitializationState = .initialized
return .run { [landingDestination] send in
try await mainQueue.sleep(for: .seconds(2.5))
await send(.destination(.updateDestination(landingDestination)))
}
.cancellable(id: CancelId.timer, cancelInFlight: true)
case .initialization(.nukeWalletRequest): case .initialization(.nukeWalletRequest):
state.alert = AlertState.wipeRequest() state.alert = AlertState.wipeRequest()
return .none return .none

View File

@ -46,7 +46,6 @@ public struct RootReducer: Reducer {
public var phraseDisplayState: RecoveryPhraseDisplay.State public var phraseDisplayState: RecoveryPhraseDisplay.State
public var sandboxState: SandboxReducer.State public var sandboxState: SandboxReducer.State
public var splashAppeared = false public var splashAppeared = false
public var storedWallet: StoredWallet?
public var tabsState: TabsReducer.State public var tabsState: TabsReducer.State
public var walletConfig: WalletConfig public var walletConfig: WalletConfig
public var welcomeState: WelcomeReducer.State public var welcomeState: WelcomeReducer.State
@ -62,7 +61,6 @@ public struct RootReducer: Reducer {
onboardingState: OnboardingFlowReducer.State, onboardingState: OnboardingFlowReducer.State,
phraseDisplayState: RecoveryPhraseDisplay.State, phraseDisplayState: RecoveryPhraseDisplay.State,
sandboxState: SandboxReducer.State, sandboxState: SandboxReducer.State,
storedWallet: StoredWallet? = nil,
tabsState: TabsReducer.State, tabsState: TabsReducer.State,
walletConfig: WalletConfig, walletConfig: WalletConfig,
welcomeState: WelcomeReducer.State welcomeState: WelcomeReducer.State
@ -77,7 +75,6 @@ public struct RootReducer: Reducer {
self.onboardingState = onboardingState self.onboardingState = onboardingState
self.phraseDisplayState = phraseDisplayState self.phraseDisplayState = phraseDisplayState
self.sandboxState = sandboxState self.sandboxState = sandboxState
self.storedWallet = storedWallet
self.tabsState = tabsState self.tabsState = tabsState
self.walletConfig = walletConfig self.walletConfig = walletConfig
self.welcomeState = welcomeState self.welcomeState = welcomeState

View File

@ -75,9 +75,7 @@ class AppInitializationTests: XCTestCase {
await testQueue.advance(by: 3.00) await testQueue.advance(by: 3.00)
await store.receive(.initialization(.initializeSDK(.existingWallet))) { state in await store.receive(.initialization(.initializeSDK(.existingWallet)))
state.storedWallet = storedWallet
}
await store.receive(.initialization(.checkBackupPhraseValidation)) { state in await store.receive(.initialization(.checkBackupPhraseValidation)) { state in
state.appInitializationState = .initialized state.appInitializationState = .initialized
@ -151,9 +149,7 @@ class AppInitializationTests: XCTestCase {
await testQueue.advance(by: 3.00) await testQueue.advance(by: 3.00)
await store.receive(.initialization(.initializeSDK(.existingWallet))) { state in await store.receive(.initialization(.initializeSDK(.existingWallet)))
state.storedWallet = storedWallet
}
let recoveryPhrase = RecoveryPhrase(words: try store.dependencies.mnemonic.randomMnemonicWords().map { $0.redacted }) let recoveryPhrase = RecoveryPhrase(words: try store.dependencies.mnemonic.randomMnemonicWords().map { $0.redacted })

View File

@ -35,9 +35,7 @@ final class RestoreWalletTests: XCTestCase {
state.isRestoringWallet = true state.isRestoringWallet = true
} }
await store.receive(.initialization(.initializeSDK(.restoreWallet))) { state in await store.receive(.initialization(.initializeSDK(.restoreWallet)))
state.storedWallet = .placeholder
}
await store.receive(.initialization(.initializationSuccessfullyDone(nil))) await store.receive(.initialization(.initializationSuccessfullyDone(nil)))

View File

@ -165,11 +165,9 @@ class RootTests: XCTestCase {
state.alert = AlertState.initializationFailed(zcashError) state.alert = AlertState.initializationFailed(zcashError)
} }
await store.receive(.initialization(.checkBackupPhraseValidation)) { state in await store.receive(.initialization(.checkBackupPhraseValidation))
// failed is expected because environment is throwing errors
state.appInitializationState = .failed await store.receive(.initialization(.initializationFailed(zcashError)))
state.alert = AlertState.cantLoadSeedPhrase()
}
await store.finish() await store.finish()
} }
@ -191,16 +189,15 @@ class RootTests: XCTestCase {
await store.receive(.initialization(.initializeSDK(.existingWallet))) await store.receive(.initialization(.initializeSDK(.existingWallet)))
await store.receive(.initialization(.checkBackupPhraseValidation)) { state in await store.receive(.initialization(.checkBackupPhraseValidation))
// failed is expected because environment is throwing errors
state.appInitializationState = .failed
state.alert = AlertState.cantLoadSeedPhrase()
}
await store.receive(.initialization(.initializationFailed(zcashError))) { state in await store.receive(.initialization(.initializationFailed(zcashError))) { state in
state.appInitializationState = .failed
state.alert = AlertState.initializationFailed(zcashError) state.alert = AlertState.initializationFailed(zcashError)
} }
await store.receive(.initialization(.initializationFailed(zcashError)))
await store.finish() await store.finish()
} }