[#1024] KeysMissing when restored to a new iPhone

- validation flow done for the 2 out of 3 flows
- 1. when the restore path is selected and the seed doesn't match the DB
- 2. when create new wallet is selected
- (missing) 3. seed is validated (waiting on API)

[#1024] KeysMissing when restored to a new iPhone

- preparations for SDK 2.1.0

[#1024] KeysMissing when restored to a new iPhone

- Seed validation API used and integrated
- There's an error in rust that must be resolved first

[#1024] KeysMissing when restored to a new iPhone

- rebased

[#1024] KeysMissing when restored to a new iPhone

- updated to use latest SDK's isSeedRelevantToAnyDerivedAccount

[#1024] KeysMissing when restored to a new iPhone

- unstable SDK branch test

[#1024] KeysMissing when restored to a new iPhone

- version bump

[#1024] KeysMissing when restored to a new iPhone

- bugfix
- packae redirected to ECCs repo

[#1024] KeysMissing when restored to a new iPhone

- tests fixed
This commit is contained in:
Lukas Korba 2024-03-12 15:34:00 +01:00
parent 2e51a9d839
commit 3e6afc69a8
19 changed files with 224 additions and 102 deletions

View File

@ -9,6 +9,8 @@ directly impact users rather than highlighting other crucial architectural updat
### Added
- Proposal API integrated with error handling for multi-transaction Proposals.
- Privacy info manifest.
- Orchard support.
- Seed validation for case when Zashi is migrated to another device.
### Fixed
- White area above the keyboard has been removed.

View File

@ -68,7 +68,7 @@ let package = Package(
.package(url: "https://github.com/pointfreeco/swift-case-paths", from: "1.1.0"),
.package(url: "https://github.com/pointfreeco/swift-url-routing", from: "0.6.0"),
.package(url: "https://github.com/zcash-hackworks/MnemonicSwift", from: "2.2.4"),
.package(url: "https://github.com/zcash/ZcashLightClientKit", from: "2.0.11"),
.package(url: "https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk", from: "2.1.0"),
.package(url: "https://github.com/firebase/firebase-ios-sdk", from: "10.17.0")
],
targets: [
@ -80,7 +80,7 @@ let package = Package(
"UIComponents",
"Utils",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/AddressDetails"
),
@ -116,7 +116,7 @@ let package = Package(
"WalletStorage",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/BalanceBreakdown"
),
@ -126,7 +126,7 @@ let package = Package(
"Generated",
"Utils",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Dependencies/BalanceFormatter"
),
@ -151,7 +151,7 @@ let package = Package(
"FileManager",
"Utils",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Dependencies/DatabaseFiles"
),
@ -168,7 +168,7 @@ let package = Package(
"DerivationTool",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "URLRouting", package: "swift-url-routing"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Dependencies/Deeplink"
),
@ -177,7 +177,7 @@ let package = Package(
dependencies: [
"Utils",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Dependencies/DerivationTool"
),
@ -195,7 +195,7 @@ let package = Package(
"LogsHandler",
"Utils",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/ExportLogs"
),
@ -235,7 +235,7 @@ let package = Package(
"TransactionList",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/Home"
),
@ -249,7 +249,7 @@ let package = Package(
"WalletStorage",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/ImportWallet"
),
@ -303,7 +303,7 @@ let package = Package(
"SecurityWarning",
"UIComponents",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/OnboardingFlow"
),
@ -352,7 +352,7 @@ let package = Package(
"Utils",
"WalletStorage",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/RecoveryPhraseDisplay"
),
@ -401,7 +401,7 @@ let package = Package(
"Welcome",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/Root"
),
@ -413,7 +413,7 @@ let package = Package(
"SendFlow",
"TransactionList",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/Sandbox"
),
@ -427,7 +427,7 @@ let package = Package(
"Utils",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/Scan"
),
@ -438,7 +438,7 @@ let package = Package(
"Models",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Dependencies/SDKSynchronizer"
),
@ -483,7 +483,7 @@ let package = Package(
"WalletStorage",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/SendFlow"
),
@ -496,7 +496,7 @@ let package = Package(
"UserDefaults",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/ServerSetup"
),
@ -515,7 +515,7 @@ let package = Package(
"SupportDataGenerator",
"UIComponents",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/Settings"
),
@ -560,7 +560,7 @@ let package = Package(
"Settings",
"UIComponents",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/Tabs"
),
@ -576,7 +576,7 @@ let package = Package(
"Utils",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Features/TransactionList"
),
@ -587,6 +587,7 @@ let package = Package(
"DerivationTool",
"Generated",
"NumberFormatter",
"SupportDataGenerator",
"Utils",
"ZcashSDKEnvironment"
],
@ -597,7 +598,7 @@ let package = Package(
dependencies: [
"DerivationTool",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Dependencies/URIParser"
),
@ -619,7 +620,7 @@ let package = Package(
.target(
name: "Utils",
dependencies: [
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
.product(name: "CasePaths", package: "swift-case-paths"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
],
@ -642,7 +643,7 @@ let package = Package(
"MnemonicClient",
"Models",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk")
],
path: "Sources/Dependencies/WalletStorage"
),
@ -660,7 +661,7 @@ let package = Package(
name: "ZcashSDKEnvironment",
dependencies: [
"UserDefaults",
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit"),
.product(name: "ZcashLightClientKit", package: "zcash-swift-wallet-sdk"),
.product(name: "ComposableArchitecture", package: "swift-composable-architecture")
],
path: "Sources/Dependencies/ZcashSDKEnvironment"

View File

@ -54,4 +54,6 @@ public struct SDKSynchronizerClient {
public var proposeTransfer: (Int, Recipient, Zatoshi, Memo?) async throws -> Proposal
public var createProposedTransactions: (Proposal, UnifiedSpendingKey) async throws -> CreateProposedTransactionsResult
public var proposeShielding: (Int, Zatoshi, Memo, TransparentAddress?) async throws -> Proposal?
public var isSeedRelevantToAnyDerivedAccount: ([UInt8]) async throws -> Bool
}

View File

@ -173,6 +173,9 @@ extension SDKSynchronizerClient: DependencyKey {
memo: memo,
transparentReceiver: transparentReceiver
)
},
isSeedRelevantToAnyDerivedAccount: { seed in
try await synchronizer.isSeedRelevantToAnyDerivedAccount(seed: seed)
}
)
}

View File

@ -33,7 +33,8 @@ extension SDKSynchronizerClient: TestDependencyKey {
switchToEndpoint: XCTUnimplemented("\(Self.self).switchToEndpoint"),
proposeTransfer: XCTUnimplemented("\(Self.self).proposeTransfer", placeholder: .testOnlyFakeProposal(totalFee: 0)),
createProposedTransactions: XCTUnimplemented("\(Self.self).createProposedTransactions", placeholder: .success),
proposeShielding: XCTUnimplemented("\(Self.self).proposeShielding", placeholder: nil)
proposeShielding: XCTUnimplemented("\(Self.self).proposeShielding", placeholder: nil),
isSeedRelevantToAnyDerivedAccount: XCTUnimplemented("\(Self.self).isSeedRelevantToAnyDerivedAccount")
)
}
@ -58,7 +59,8 @@ extension SDKSynchronizerClient {
switchToEndpoint: { _ in },
proposeTransfer: { _, _, _, _ in .testOnlyFakeProposal(totalFee: 0) },
createProposedTransactions: { _, _ in .success },
proposeShielding: { _, _, _, _ in nil }
proposeShielding: { _, _, _, _ in nil },
isSeedRelevantToAnyDerivedAccount: { _ in false }
)
public static let mock = Self.mocked()
@ -184,7 +186,8 @@ extension SDKSynchronizerClient {
createProposedTransactions:
@escaping (Proposal, UnifiedSpendingKey) async throws -> CreateProposedTransactionsResult = { _, _ in .success },
proposeShielding:
@escaping (Int, Zatoshi, Memo, TransparentAddress?) async throws -> Proposal? = { _, _, _, _ in nil }
@escaping (Int, Zatoshi, Memo, TransparentAddress?) async throws -> Proposal? = { _, _, _, _ in nil },
isSeedRelevantToAnyDerivedAccount: @escaping ([UInt8]) async throws -> Bool = { _ in false }
) -> SDKSynchronizerClient {
SDKSynchronizerClient(
stateStream: stateStream,
@ -206,7 +209,8 @@ extension SDKSynchronizerClient {
switchToEndpoint: switchToEndpoint,
proposeTransfer: proposeTransfer,
createProposedTransactions: createProposedTransactions,
proposeShielding: proposeShielding
proposeShielding: proposeShielding,
isSeedRelevantToAnyDerivedAccount: isSeedRelevantToAnyDerivedAccount
)
}
}

View File

@ -210,11 +210,12 @@ public struct BalanceBreakdownReducer: Reducer {
case .synchronizerStateChanged(let latestState):
let accountBalance = latestState.data.accountBalance?.data
state.shieldedBalance = accountBalance?.saplingBalance.spendableValue ?? .zero
state.totalBalance = accountBalance?.saplingBalance.total() ?? .zero
state.shieldedBalance = (accountBalance?.saplingBalance.spendableValue ?? .zero) + (accountBalance?.orchardBalance.spendableValue ?? .zero)
state.totalBalance = (accountBalance?.saplingBalance.total() ?? .zero) + (accountBalance?.orchardBalance.total() ?? .zero)
state.transparentBalance = accountBalance?.unshielded ?? .zero
state.changePending = accountBalance?.saplingBalance.changePendingConfirmation ?? .zero
state.pendingTransactions = accountBalance?.saplingBalance.valuePendingSpendability ?? .zero
state.changePending = (accountBalance?.saplingBalance.changePendingConfirmation ?? .zero) + (accountBalance?.orchardBalance.changePendingConfirmation ?? .zero)
state.pendingTransactions = (accountBalance?.saplingBalance.valuePendingSpendability ?? .zero) + (accountBalance?.orchardBalance.valuePendingSpendability ?? .zero)
return .none
case .syncProgress:

View File

@ -70,6 +70,7 @@ public struct BalanceBreakdownView: View {
)
)
.padding(.top, viewStore.isRestoringWallet ? 0 : 40)
.padding(.bottom, 25)
.navigationLinkEmpty(
isActive: viewStore.bindingForPartialProposalError,
destination: {

View File

@ -196,8 +196,9 @@ public struct HomeReducer: Reducer {
state.synchronizerStatusSnapshot = snapshot
let accountBalance = latestState.data.accountBalance?.data
state.shieldedBalance = accountBalance?.saplingBalance.spendableValue ?? .zero
state.totalBalance = accountBalance?.saplingBalance.total() ?? .zero
state.shieldedBalance = (accountBalance?.saplingBalance.spendableValue ?? .zero) + (accountBalance?.orchardBalance.spendableValue ?? .zero)
state.totalBalance = (accountBalance?.saplingBalance.total() ?? .zero) + (accountBalance?.orchardBalance.total() ?? .zero)
switch snapshot.syncStatus {
case .error(let error):

View File

@ -72,10 +72,11 @@ public struct ImportWalletReducer: Reducer {
public enum Action: Equatable {
case alert(PresentationAction<Action>)
case birthdayInputChanged(RedactableString)
case restoreWallet
case importPrivateOrViewingKey
case initializeSDK
case nextPressed
case onAppear
case restoreWallet
case seedPhraseInputChanged(RedactableString)
case successfullyRecovered
case updateDestination(ImportWalletReducer.State.Destination?)
@ -130,6 +131,9 @@ public struct ImportWalletReducer: Reducer {
case .alert:
return .none
case .nextPressed:
return .none
case .restoreWallet:
do {
// validate the seed

View File

@ -97,7 +97,7 @@ public struct ImportWalletView: View {
}
Button(L10n.General.next.uppercased()) {
viewStore.send(.updateDestination(.birthday))
viewStore.send(.nextPressed)
}
.zcashStyle()
.frame(width: 236)

View File

@ -28,6 +28,8 @@ extension RootReducer {
case nukeWallet
case nukeWalletRequest
case respondToWalletInitializationState(InitializationState)
case restoreExistingWallet
case seedValidationResult(Bool)
case synchronizerStartFailed(ZcashError)
case registerForSynchronizersUpdate
case retryStart
@ -39,6 +41,7 @@ extension RootReducer {
Reduce { state, action in
switch action {
case .initialization(.appDelegate(.didFinishLaunching)):
//walletStorage.nukeWallet()
state.appStartState = .didFinishLaunching
// TODO: [#704], trigger the review request logic when approved by the team,
// https://github.com/Electric-Coin-Company/zashi-ios/issues/704
@ -203,11 +206,7 @@ extension RootReducer {
return .none
case .keysMissing:
state.appInitializationState = .keysMissing
// TODO: [#1024] This is the case when this wallet migrated to another device
// https://github.com/Electric-Coin-Company/zashi-ios/issues/1024
// Temporary alert view until #1024 is implemented
state.alert = AlertState.tmpMigrationToBeDeveloped()
return .none
return .send(.destination(.updateDestination(.onboarding)))
case .initialized, .filesMissing:
if walletState == .filesMissing {
state.appInitializationState = .filesMissing
@ -242,7 +241,6 @@ extension RootReducer {
do {
let storedWallet = try walletStorage.exportWallet()
let birthday = storedWallet.birthday?.value() ?? zcashSDKEnvironment.latestCheckpoint
try mnemonic.isValid(storedWallet.seedPhrase.value())
let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase.value())
@ -312,10 +310,32 @@ extension RootReducer {
.cancellable(id: SynchronizerCancelId, cancelInFlight: true)
case .nukeWalletSucceeded:
if state.appInitializationState != .keysMissing {
state = .initial
}
state.splashAppeared = true
walletStorage.nukeWallet()
try? readTransactionsStorage.nukeWallet()
if state.appInitializationState == .keysMissing && state.onboardingState.destination == .importExistingWallet {
state.appInitializationState = .uninitialized
return .concatenate(
.cancel(id: SynchronizerCancelId),
.run { send in
await userStoredPreferences.removeAll()
},
Effect.send(.onboarding(.importWallet(.updateDestination(.birthday))))
)
} else if state.appInitializationState == .keysMissing && state.onboardingState.destination == .createNewWallet {
state.appInitializationState = .uninitialized
return .concatenate(
.cancel(id: SynchronizerCancelId),
.run { send in
await userStoredPreferences.removeAll()
},
Effect.send(.onboarding(.securityWarning(.createNewWallet)))
)
} else {
return .concatenate(
.cancel(id: SynchronizerCancelId),
.run { send in
@ -323,6 +343,7 @@ extension RootReducer {
},
Effect.send(.initialization(.checkWalletInitialization))
)
}
case .nukeWalletFailed:
let backDestination: Effect<RootReducer.Action>
@ -332,10 +353,15 @@ extension RootReducer {
backDestination = Effect.send(.destination(.updateDestination(state.destinationState.destination)))
}
state.alert = AlertState.wipeFailed()
if state.appInitializationState == .keysMissing {
return .cancel(id: SynchronizerCancelId)
} else {
return .concatenate(
.cancel(id: SynchronizerCancelId),
backDestination
)
}
case .phraseDisplay(.finishedPressed):
do {
@ -352,6 +378,38 @@ extension RootReducer {
Effect.send(.destination(.updateDestination(.startup)))
)
case .onboarding(.securityWarning(.confirmTapped)):
if state.appInitializationState == .keysMissing {
state.alert = AlertState.existingWallet()
return .none
} else {
return .send(.onboarding(.securityWarning(.createNewWallet)))
}
case .initialization(.restoreExistingWallet):
return .run { send in
await send(.onboarding(.updateDestination(nil)))
try await mainQueue.sleep(for: .seconds(1))
await send(.onboarding(.importExistingWallet))
}
case .onboarding(.importWallet(.nextPressed)):
if state.appInitializationState == .keysMissing {
let seedPhrase = state.onboardingState.importWalletState.importedSeedPhrase.data
return .run { send in
do {
let seedBytes = try mnemonic.toSeed(seedPhrase)
let result = try await sdkSynchronizer.isSeedRelevantToAnyDerivedAccount(seedBytes)
await send(.initialization(.seedValidationResult(result)))
} catch {
await send(.initialization(.seedValidationResult(false)))
}
}
} else {
state.onboardingState.importWalletState.destination = .birthday
return .none
}
case .onboarding(.importWallet(.successfullyRecovered)):
state.alert = AlertState.successfullyRecovered()
return Effect.send(.destination(.updateDestination(.tabs)))
@ -365,6 +423,14 @@ extension RootReducer {
}
)
case .initialization(.seedValidationResult(let validSeed)):
if validSeed {
return .send(.onboarding(.importWallet(.restoreWallet)))
} else {
state.alert = AlertState.differentSeed()
}
return .none
case .initialization(.configureCrashReporter):
crashReporter.configure(
!userStoredPreferences.isUserOptedOutOfCrashReporting()

View File

@ -297,11 +297,33 @@ extension AlertState where Action == RootReducer.Action {
}
}
public static func tmpMigrationToBeDeveloped() -> AlertState {
public static func differentSeed() -> AlertState {
AlertState {
TextState("Automatic migration to be developed soon")
TextState("Warning")
} actions: {
ButtonState(role: .cancel, action: .alert(.dismiss)) {
TextState("Try Again")
}
ButtonState(role: .destructive, action: .initialization(.nukeWallet)) {
TextState("Continue")
}
} message: {
TextState("This copy of Zashi has been migrated from another device. Your funds are safe provided that you have the seed phrase. This issue will be addressed soon; until then, delete Zashi and reinstall it, providing the seed phrase to restore your wallet.")
TextState("This recovery phrase doesn't match the Zashi database backup saved on this device. If you proceed, you will lose access to this database backup and if you try to restore later, some information may be lost.")
}
}
public static func existingWallet() -> AlertState {
AlertState {
TextState("Warning")
} actions: {
ButtonState(role: .cancel, action: .initialization(.restoreExistingWallet)) {
TextState("Restore")
}
ButtonState(role: .destructive, action: .initialization(.nukeWallet)) {
TextState("Continue")
}
} message: {
TextState("We identified a Zashi database backup on this device. If you create a new wallet, you will lose access to this database backup and if you try to restore later, some information may be lost.")
}
}
}

View File

@ -48,6 +48,7 @@ public struct SecurityWarning {
case alert(PresentationAction<Action>)
case binding(BindingAction<SecurityWarning.State>)
case confirmTapped
case createNewWallet
case newWalletCreated
case onAppear
case recoveryPhraseDisplay(RecoveryPhraseDisplay.Action)
@ -85,6 +86,9 @@ public struct SecurityWarning {
return .none
case .confirmTapped:
return .none
case .createNewWallet:
do {
// get the random english mnemonic
let newRandomPhrase = try mnemonic.randomMnemonic()

View File

@ -320,8 +320,10 @@ public struct SendFlowReducer: Reducer {
return .none
case .synchronizerStateChanged(let latestState):
state.spendableBalance = latestState.data.accountBalance?.data?.saplingBalance.spendableValue ?? .zero
state.totalBalance = latestState.data.accountBalance?.data?.saplingBalance.total() ?? .zero
let latestAccountBalance = latestState.data.accountBalance?.data
state.spendableBalance = (latestAccountBalance?.saplingBalance.spendableValue ?? .zero) + (latestAccountBalance?.orchardBalance.spendableValue ?? .zero)
state.totalBalance = (latestAccountBalance?.saplingBalance.total() ?? .zero) + (latestAccountBalance?.orchardBalance.total() ?? .zero)
state.transactionAmountInputState.maxValue = state.spendableBalance.amount.redacted
return .none

View File

@ -8,6 +8,7 @@
import Foundation
import ZcashLightClientKit
import Generated
import Utils
public struct SyncStatusSnapshot: Equatable {
public let message: String
@ -27,7 +28,7 @@ public struct SyncStatusSnapshot: Equatable {
return SyncStatusSnapshot(state, L10n.Sync.Message.unprepared)
case .error(let error):
return SyncStatusSnapshot(state, L10n.Sync.Message.error(error.toZcashError().message))
return SyncStatusSnapshot(state, L10n.Sync.Message.error(error.toZcashError().detailedMessage))
case .stopped:
return SyncStatusSnapshot(state, L10n.Sync.Message.stopped)

View File

@ -1204,7 +1204,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
@ -1234,7 +1234,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_BITCODE = NO;
@ -1539,7 +1539,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 2;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
@ -1552,7 +1552,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = "co.electriccoin.secant-mainnet";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -1569,7 +1569,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_BITCODE = NO;
@ -1581,7 +1581,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = "co.electriccoin.secant-mainnet";
PRODUCT_NAME = "$(TARGET_NAME)";
@ -1684,7 +1684,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 4;
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_BITCODE = NO;
@ -1759,7 +1759,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 3;
CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"secant/Preview Content\"";
DEVELOPMENT_TEAM = RLPRR8CPQG;
ENABLE_BITCODE = NO;
@ -1771,7 +1771,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
OTHER_SWIFT_FLAGS = "";
PRODUCT_BUNDLE_IDENTIFIER = "co.electriccoin.secant-mainnet";
PRODUCT_NAME = "$(TARGET_NAME)";

View File

@ -374,17 +374,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state" : {
"revision" : "7c801be1f445402a433b32835a50d832e8a50437",
"version" : "0.6.0"
"revision" : "c7e5158edf5e62af15492d30237163b78af35ce9",
"version" : "0.7.1"
}
},
{
"identity" : "zcashlightclientkit",
"identity" : "zcash-swift-wallet-sdk",
"kind" : "remoteSourceControl",
"location" : "https://github.com/zcash/ZcashLightClientKit",
"location" : "https://github.com/Electric-Coin-Company/zcash-swift-wallet-sdk",
"state" : {
"revision" : "6c9b7a91d6b9ec4f3b8cb699a558eac1178ba837",
"version" : "2.0.11"
"revision" : "8909f237225676be70dfeaa103b77139481dfd86",
"version" : "2.1.0"
}
}
],

View File

@ -203,7 +203,11 @@ class AppInitializationTests: XCTestCase {
await store.receive(.initialization(.respondToWalletInitializationState(.keysMissing))) { state in
state.appInitializationState = .keysMissing
state.alert = AlertState.tmpMigrationToBeDeveloped()
}
await store.receive(.destination(.updateDestination(.onboarding))) { state in
state.destinationState.internalDestination = .onboarding
state.destinationState.previousDestination = .welcome
}
await store.finish()

View File

@ -133,7 +133,11 @@ class RootTests: XCTestCase {
await store.send(.initialization(.respondToWalletInitializationState(.keysMissing))) { state in
state.appInitializationState = .keysMissing
state.alert = AlertState.tmpMigrationToBeDeveloped()
}
await store.receive(.destination(.updateDestination(.onboarding))) { state in
state.destinationState.internalDestination = .onboarding
state.destinationState.previousDestination = .welcome
}
await store.finish()