Closes #654 - `SDKSynchronizerClient` is now regular TCA dependency like any other dependency that we have in the app.
This commit is contained in:
parent
69c1aa12a3
commit
5ce87b18a9
|
@ -185,7 +185,6 @@
|
|||
0D26AF4E299E8196005260EE /* BalanceBreakdownView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6713F5289BC58C00A6796F /* BalanceBreakdownView.swift */; };
|
||||
0D26AF4F299E8196005260EE /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9971A6427680DFE00A2DB75 /* SettingsView.swift */; };
|
||||
0D26AF50299E8196005260EE /* Strings.swift in Sources */ = {isa = PBXBuildFile; fileRef = F96B41EA273B50520021B49A /* Strings.swift */; };
|
||||
0D26AF51299E8196005260EE /* SDKSynchronizerMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */; };
|
||||
0D26AF52299E8196005260EE /* LogsHandlerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E612C7529880FC900D09B09 /* LogsHandlerTest.swift */; };
|
||||
0D26AF53299E8196005260EE /* TextFieldFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EDA07A127EDE1AE00D6F09B /* TextFieldFooter.swift */; };
|
||||
0D26AF54299E8196005260EE /* CrashReportingInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D26103B298C3E4800CC9DE9 /* CrashReportingInterface.swift */; };
|
||||
|
@ -502,7 +501,6 @@
|
|||
9EB863C92923C953003D0F8B /* UserPreferencesStorageMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */; };
|
||||
9EB863CB2923CA20003D0F8B /* SDKSynchronizerLive.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */; };
|
||||
9EB863CD2923CA28003D0F8B /* SDKSynchronizerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */; };
|
||||
9EB863D02923D3FC003D0F8B /* SDKSynchronizerMocks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */; };
|
||||
9EBDF947291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF946291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift */; };
|
||||
9EBDF949291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF948291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift */; };
|
||||
9EBDF94B291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EBDF94A291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift */; };
|
||||
|
@ -821,7 +819,6 @@
|
|||
9EB863C82923C953003D0F8B /* UserPreferencesStorageMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserPreferencesStorageMocks.swift; sourceTree = "<group>"; };
|
||||
9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerLive.swift; sourceTree = "<group>"; };
|
||||
9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerTest.swift; sourceTree = "<group>"; };
|
||||
9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDKSynchronizerMocks.swift; sourceTree = "<group>"; };
|
||||
9EBDF946291D75B2000A1A05 /* DiskSpaceCheckerInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerInterface.swift; sourceTree = "<group>"; };
|
||||
9EBDF948291D75BF000A1A05 /* DiskSpaceCheckerLiveKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerLiveKey.swift; sourceTree = "<group>"; };
|
||||
9EBDF94A291D75C7000A1A05 /* DiskSpaceCheckerTestKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskSpaceCheckerTestKey.swift; sourceTree = "<group>"; };
|
||||
|
@ -1980,7 +1977,6 @@
|
|||
9EAFEB872806E5AE00199FC9 /* SDKSynchronizerInterface.swift */,
|
||||
9EB863CA2923CA20003D0F8B /* SDKSynchronizerLive.swift */,
|
||||
9EB863CC2923CA28003D0F8B /* SDKSynchronizerTest.swift */,
|
||||
9EB863CE2923CA32003D0F8B /* SDKSynchronizerMocks.swift */,
|
||||
);
|
||||
path = SDKSynchronizer;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2782,7 +2778,6 @@
|
|||
0D26AF4E299E8196005260EE /* BalanceBreakdownView.swift in Sources */,
|
||||
0D26AF4F299E8196005260EE /* SettingsView.swift in Sources */,
|
||||
0D26AF50299E8196005260EE /* Strings.swift in Sources */,
|
||||
0D26AF51299E8196005260EE /* SDKSynchronizerMocks.swift in Sources */,
|
||||
0D26AF52299E8196005260EE /* LogsHandlerTest.swift in Sources */,
|
||||
0D26AF53299E8196005260EE /* TextFieldFooter.swift in Sources */,
|
||||
0D26AF54299E8196005260EE /* CrashReportingInterface.swift in Sources */,
|
||||
|
@ -3010,7 +3005,6 @@
|
|||
9E6713F8289BC58C00A6796F /* BalanceBreakdownView.swift in Sources */,
|
||||
F9971A6627680DFE00A2DB75 /* SettingsView.swift in Sources */,
|
||||
F96B41EB273B50520021B49A /* Strings.swift in Sources */,
|
||||
9EB863D02923D3FC003D0F8B /* SDKSynchronizerMocks.swift in Sources */,
|
||||
9E612C7629880FC900D09B09 /* LogsHandlerTest.swift in Sources */,
|
||||
2EDA07A227EDE1AE00D6F09B /* TextFieldFooter.swift in Sources */,
|
||||
0D26103C298C3E4800CC9DE9 /* CrashReportingInterface.swift in Sources */,
|
||||
|
|
|
@ -27,93 +27,57 @@ struct DatabaseFiles {
|
|||
self.fileManager = fileManager
|
||||
}
|
||||
|
||||
func documentsDirectory() throws -> URL {
|
||||
func documentsDirectory() -> URL {
|
||||
do {
|
||||
return try fileManager.url(.documentDirectory, .userDomainMask, nil, true)
|
||||
} catch {
|
||||
throw DatabaseFilesError.getDocumentsURL
|
||||
// This is not super clean but this is second best thing when the above call fails.
|
||||
return URL(fileURLWithPath: NSHomeDirectory()).appendingPathComponent("Documents")
|
||||
}
|
||||
}
|
||||
|
||||
func cacheDbURL(for network: ZcashNetwork) throws -> URL {
|
||||
do {
|
||||
return try documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)cache.db",
|
||||
isDirectory: false
|
||||
func cacheDbURL(for network: ZcashNetwork) -> URL {
|
||||
return documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)cache.db",
|
||||
isDirectory: false
|
||||
)
|
||||
}
|
||||
|
||||
func dataDbURL(for network: ZcashNetwork) -> URL {
|
||||
return documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)data.db",
|
||||
isDirectory: false
|
||||
)
|
||||
} catch {
|
||||
throw DatabaseFilesError.getCacheURL
|
||||
}
|
||||
}
|
||||
|
||||
func dataDbURL(for network: ZcashNetwork) throws -> URL {
|
||||
do {
|
||||
return try documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)data.db",
|
||||
isDirectory: false
|
||||
)
|
||||
} catch {
|
||||
throw DatabaseFilesError.getDataURL
|
||||
}
|
||||
func outputParamsURL(for network: ZcashNetwork) -> URL {
|
||||
return documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)sapling-output.params",
|
||||
isDirectory: false
|
||||
)
|
||||
}
|
||||
|
||||
func outputParamsURL(for network: ZcashNetwork) throws -> URL {
|
||||
do {
|
||||
return try documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)sapling-output.params",
|
||||
isDirectory: false
|
||||
)
|
||||
} catch {
|
||||
throw DatabaseFilesError.getOutputParamsURL
|
||||
}
|
||||
func pendingDbURL(for network: ZcashNetwork) -> URL {
|
||||
return documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)pending.db",
|
||||
isDirectory: false
|
||||
)
|
||||
}
|
||||
|
||||
func pendingDbURL(for network: ZcashNetwork) throws -> URL {
|
||||
do {
|
||||
return try documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)pending.db",
|
||||
isDirectory: false
|
||||
)
|
||||
} catch {
|
||||
throw DatabaseFilesError.getPendingURL
|
||||
}
|
||||
func spendParamsURL(for network: ZcashNetwork) -> URL {
|
||||
return documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)sapling-spend.params",
|
||||
isDirectory: false
|
||||
)
|
||||
}
|
||||
|
||||
func spendParamsURL(for network: ZcashNetwork) throws -> URL {
|
||||
do {
|
||||
return try documentsDirectory()
|
||||
.appendingPathComponent(
|
||||
"\(network.constants.defaultDbNamePrefix)sapling-spend.params",
|
||||
isDirectory: false
|
||||
)
|
||||
} catch {
|
||||
throw DatabaseFilesError.getSpendParamsURL
|
||||
}
|
||||
}
|
||||
|
||||
func areDbFilesPresent(for network: ZcashNetwork) throws -> Bool {
|
||||
do {
|
||||
let dataDbURL = try dataDbURL(for: network)
|
||||
return fileManager.fileExists(dataDbURL.path)
|
||||
} catch {
|
||||
throw DatabaseFilesError.filesPresentCheck
|
||||
}
|
||||
}
|
||||
|
||||
func nukeDbFiles(for network: ZcashNetwork) throws {
|
||||
do {
|
||||
let cacheDbURL = try cacheDbURL(for: network)
|
||||
let dataDbURL = try dataDbURL(for: network)
|
||||
let pendingDbURL = try pendingDbURL(for: network)
|
||||
try fileManager.removeItem(cacheDbURL)
|
||||
try fileManager.removeItem(dataDbURL)
|
||||
try fileManager.removeItem(pendingDbURL)
|
||||
} catch {
|
||||
throw DatabaseFilesError.nukeFiles
|
||||
}
|
||||
func areDbFilesPresent(for network: ZcashNetwork) -> Bool {
|
||||
let dataDbURL = dataDbURL(for: network)
|
||||
return fileManager.fileExists(dataDbURL.path)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,12 @@ extension DependencyValues {
|
|||
}
|
||||
|
||||
struct DatabaseFilesClient {
|
||||
let documentsDirectory: () throws -> URL
|
||||
let fsBlockDbRootFor: (ZcashNetwork) throws -> URL
|
||||
let cacheDbURLFor: (ZcashNetwork) throws -> URL
|
||||
let dataDbURLFor: (ZcashNetwork) throws -> URL
|
||||
let outputParamsURLFor: (ZcashNetwork) throws -> URL
|
||||
let pendingDbURLFor: (ZcashNetwork) throws -> URL
|
||||
let spendParamsURLFor: (ZcashNetwork) throws -> URL
|
||||
var areDbFilesPresentFor: (ZcashNetwork) throws -> Bool
|
||||
let nukeDbFilesFor: (ZcashNetwork) throws -> Void
|
||||
let documentsDirectory: () -> URL
|
||||
let fsBlockDbRootFor: (ZcashNetwork) -> URL
|
||||
let cacheDbURLFor: (ZcashNetwork) -> URL
|
||||
let dataDbURLFor: (ZcashNetwork) -> URL
|
||||
let outputParamsURLFor: (ZcashNetwork) -> URL
|
||||
let pendingDbURLFor: (ZcashNetwork) -> URL
|
||||
let spendParamsURLFor: (ZcashNetwork) -> URL
|
||||
var areDbFilesPresentFor: (ZcashNetwork) -> Bool
|
||||
}
|
||||
|
|
|
@ -14,33 +14,30 @@ extension DatabaseFilesClient: DependencyKey {
|
|||
static func live(databaseFiles: DatabaseFiles = DatabaseFiles(fileManager: .live)) -> Self {
|
||||
Self(
|
||||
documentsDirectory: {
|
||||
try databaseFiles.documentsDirectory()
|
||||
databaseFiles.documentsDirectory()
|
||||
},
|
||||
fsBlockDbRootFor: { network in
|
||||
try databaseFiles.documentsDirectory()
|
||||
databaseFiles.documentsDirectory()
|
||||
.appendingPathComponent(network.networkType.chainName)
|
||||
.appendingPathComponent(ZcashSDK.defaultFsCacheName, isDirectory: true)
|
||||
},
|
||||
cacheDbURLFor: { network in
|
||||
try databaseFiles.cacheDbURL(for: network)
|
||||
databaseFiles.cacheDbURL(for: network)
|
||||
},
|
||||
dataDbURLFor: { network in
|
||||
try databaseFiles.dataDbURL(for: network)
|
||||
databaseFiles.dataDbURL(for: network)
|
||||
},
|
||||
outputParamsURLFor: { network in
|
||||
try databaseFiles.outputParamsURL(for: network)
|
||||
databaseFiles.outputParamsURL(for: network)
|
||||
},
|
||||
pendingDbURLFor: { network in
|
||||
try databaseFiles.pendingDbURL(for: network)
|
||||
databaseFiles.pendingDbURL(for: network)
|
||||
},
|
||||
spendParamsURLFor: { network in
|
||||
try databaseFiles.spendParamsURL(for: network)
|
||||
databaseFiles.spendParamsURL(for: network)
|
||||
},
|
||||
areDbFilesPresentFor: { network in
|
||||
try databaseFiles.areDbFilesPresent(for: network)
|
||||
},
|
||||
nukeDbFilesFor: { network in
|
||||
try databaseFiles.nukeDbFiles(for: network)
|
||||
databaseFiles.areDbFilesPresent(for: network)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -18,8 +18,7 @@ extension DatabaseFilesClient: TestDependencyKey {
|
|||
outputParamsURLFor: XCTUnimplemented("\(Self.self).outputParamsURLFor", placeholder: .emptyURL),
|
||||
pendingDbURLFor: XCTUnimplemented("\(Self.self).pendingDbURLFor", placeholder: .emptyURL),
|
||||
spendParamsURLFor: XCTUnimplemented("\(Self.self).spendParamsURLFor", placeholder: .emptyURL),
|
||||
areDbFilesPresentFor: XCTUnimplemented("\(Self.self).areDbFilesPresentFor", placeholder: false),
|
||||
nukeDbFilesFor: XCTUnimplemented("\(Self.self).nukeDbFilesFor")
|
||||
areDbFilesPresentFor: XCTUnimplemented("\(Self.self).areDbFilesPresentFor", placeholder: false)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -38,7 +37,6 @@ extension DatabaseFilesClient {
|
|||
outputParamsURLFor: { _ in .emptyURL },
|
||||
pendingDbURLFor: { _ in .emptyURL },
|
||||
spendParamsURLFor: { _ in .emptyURL },
|
||||
areDbFilesPresentFor: { _ in false },
|
||||
nukeDbFilesFor: { _ in }
|
||||
areDbFilesPresentFor: { _ in false }
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@ import ZcashLightClientKit
|
|||
|
||||
extension DependencyValues {
|
||||
var sdkSynchronizer: SDKSynchronizerClient {
|
||||
get { self[SDKSynchronizerDependency.self] }
|
||||
set { self[SDKSynchronizerDependency.self] = newValue }
|
||||
get { self[SDKSynchronizerClient.self] }
|
||||
set { self[SDKSynchronizerClient.self] = newValue }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,57 +25,35 @@ enum SDKSynchronizerState: Equatable {
|
|||
case unknown
|
||||
}
|
||||
|
||||
enum SDKSynchronizerClientError: Error {
|
||||
case synchronizerNotInitialized
|
||||
}
|
||||
|
||||
protocol SDKSynchronizerClient {
|
||||
var notificationCenter: NotificationCenterClient { get }
|
||||
var synchronizer: SDKSynchronizer? { get }
|
||||
var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never> { get }
|
||||
var walletBirthday: BlockHeight? { get }
|
||||
var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState? { get }
|
||||
|
||||
func prepareWith(initializer: Initializer, seedBytes: [UInt8], viewingKey: UnifiedFullViewingKey, walletBirthday: BlockHeight) throws
|
||||
func start(retry: Bool) throws
|
||||
func stop()
|
||||
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?)
|
||||
func statusSnapshot() -> SyncStatusSnapshot
|
||||
func isSyncing() -> Bool
|
||||
func isInitialized() -> Bool
|
||||
|
||||
func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error>?
|
||||
|
||||
func getShieldedBalance() -> WalletBalance?
|
||||
func getTransparentBalance() -> WalletBalance?
|
||||
func getAllSentTransactions() -> EffectTask<[WalletEvent]>
|
||||
func getAllReceivedTransactions() -> EffectTask<[WalletEvent]>
|
||||
func getAllClearedTransactions() -> EffectTask<[WalletEvent]>
|
||||
func getAllPendingTransactions() -> EffectTask<[WalletEvent]>
|
||||
func getAllTransactions() -> EffectTask<[WalletEvent]>
|
||||
|
||||
func getUnifiedAddress(account: Int) -> UnifiedAddress?
|
||||
func getTransparentAddress(account: Int) -> TransparentAddress?
|
||||
func getSaplingAddress(accountIndex: Int) async -> SaplingAddress?
|
||||
|
||||
func sendTransaction(
|
||||
with spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to recipientAddress: Recipient,
|
||||
memo: Memo?
|
||||
) -> EffectTask<Result<TransactionState, NSError>>
|
||||
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> TransactionState
|
||||
|
||||
func wipe() -> AnyPublisher<Void, Error>?
|
||||
}
|
||||
|
||||
extension SDKSynchronizerClient {
|
||||
func start() throws {
|
||||
try start(retry: false)
|
||||
}
|
||||
struct SDKSynchronizerClient {
|
||||
let stateChangedStream: () -> CurrentValueSubject<SDKSynchronizerState, Never>
|
||||
let latestScannedSynchronizerState: () -> SDKSynchronizer.SynchronizerState?
|
||||
let latestScannedHeight: () -> BlockHeight
|
||||
|
||||
let prepareWith: ([UInt8], UnifiedFullViewingKey, BlockHeight) throws -> Void
|
||||
let start: (_ retry: Bool) throws -> Void
|
||||
let stop: () -> Void
|
||||
let statusSnapshot: () -> SyncStatusSnapshot
|
||||
let isSyncing: () -> Bool
|
||||
let isInitialized: () -> Bool
|
||||
|
||||
let rewind: (RewindPolicy) -> AnyPublisher<Void, Error>
|
||||
|
||||
let getShieldedBalance: () -> WalletBalance?
|
||||
let getTransparentBalance: () -> WalletBalance?
|
||||
let getAllSentTransactions: () -> EffectTask<[WalletEvent]>
|
||||
let getAllReceivedTransactions: () -> EffectTask<[WalletEvent]>
|
||||
let getAllClearedTransactions: () -> EffectTask<[WalletEvent]>
|
||||
let getAllPendingTransactions: () -> EffectTask<[WalletEvent]>
|
||||
let getAllTransactions: () -> EffectTask<[WalletEvent]>
|
||||
|
||||
let getUnifiedAddress: (_ account: Int) -> UnifiedAddress?
|
||||
let getTransparentAddress: (_ account: Int) -> TransparentAddress?
|
||||
let getSaplingAddress: (_ accountIndex: Int) async -> SaplingAddress?
|
||||
|
||||
let sendTransaction: (UnifiedSpendingKey, Zatoshi, Recipient, Memo?) -> EffectTask<Result<TransactionState, NSError>>
|
||||
|
||||
let shieldFunds: (UnifiedSpendingKey, Memo, Zatoshi) async throws -> TransactionState
|
||||
|
||||
let wipe: () -> AnyPublisher<Void, Error>?
|
||||
}
|
||||
|
|
|
@ -10,33 +10,42 @@ import Combine
|
|||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
enum SDKSynchronizerDependency: DependencyKey {
|
||||
static let liveValue: SDKSynchronizerClient = LiveSDKSynchronizerClient()
|
||||
}
|
||||
|
||||
class LiveSDKSynchronizerClient: SDKSynchronizerClient {
|
||||
private class SDKSynchronizerLiveWrapper {
|
||||
private var cancellables: [AnyCancellable] = []
|
||||
private(set) var synchronizer: SDKSynchronizer?
|
||||
// TODO: [#497] Since 0.17.0-beta SDKSynchronizer has `lastState` property which does exactly the same as `stateChanged`. Problem is that we have
|
||||
// synchronizer as optional. And now it would be complicated to handle the situation when `lastState` isn't always available. Let's handle this
|
||||
// in future.
|
||||
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
|
||||
private(set) var notificationCenter: NotificationCenterClient
|
||||
private(set) var walletBirthday: BlockHeight?
|
||||
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
|
||||
let synchronizer: SDKSynchronizer
|
||||
let stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
|
||||
var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
|
||||
|
||||
init(notificationCenter: NotificationCenterClient = .live) {
|
||||
self.notificationCenter = notificationCenter
|
||||
init(
|
||||
notificationCenter: NotificationCenterClient = .live,
|
||||
databaseFiles: DatabaseFilesClient = .liveValue,
|
||||
environment: ZcashSDKEnvironment = .liveValue
|
||||
) {
|
||||
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
|
||||
|
||||
let network = environment.network
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: databaseFiles.cacheDbURLFor(network),
|
||||
fsBlockDbRoot: databaseFiles.fsBlockDbRootFor(network),
|
||||
dataDbURL: databaseFiles.dataDbURLFor(network),
|
||||
pendingDbURL: databaseFiles.pendingDbURLFor(network),
|
||||
endpoint: environment.endpoint,
|
||||
network: network,
|
||||
spendParamsURL: databaseFiles.spendParamsURLFor(network),
|
||||
outputParamsURL: databaseFiles.outputParamsURLFor(network),
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
||||
loggerProxy: OSLogger(logLevel: .debug, category: LoggerConstants.sdkLogs)
|
||||
)
|
||||
|
||||
synchronizer = SDKSynchronizer(initializer: initializer)
|
||||
subscribeToNotifications(notificationCenter: notificationCenter)
|
||||
}
|
||||
|
||||
|
||||
deinit {
|
||||
synchronizer?.stop()
|
||||
synchronizer.stop()
|
||||
}
|
||||
|
||||
func prepareWith(initializer: Initializer, seedBytes: [UInt8], viewingKey: UnifiedFullViewingKey, walletBirthday: BlockHeight) throws {
|
||||
let synchronizer = SDKSynchronizer(initializer: initializer)
|
||||
|
||||
private func subscribeToNotifications(notificationCenter: NotificationCenterClient) {
|
||||
notificationCenter.publisherFor(.synchronizerStarted)?
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] notif in
|
||||
|
@ -62,197 +71,160 @@ class LiveSDKSynchronizerClient: SDKSynchronizerClient {
|
|||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] _ in self?.synchronizerStopped() }
|
||||
.store(in: &cancellables)
|
||||
|
||||
guard try synchronizer.prepare(with: seedBytes, viewingKeys: [viewingKey], walletBirthday: walletBirthday) == .success else {
|
||||
throw SynchronizerError.initFailed(message: "")
|
||||
}
|
||||
|
||||
self.synchronizer = synchronizer
|
||||
self.walletBirthday = initializer.walletBirthday
|
||||
}
|
||||
|
||||
func start(retry: Bool) throws {
|
||||
try synchronizer?.start(retry: retry)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
synchronizer?.stop()
|
||||
}
|
||||
|
||||
func isSyncing() -> Bool {
|
||||
latestScannedSynchronizerState?.syncStatus.isSyncing ?? false
|
||||
}
|
||||
|
||||
func isInitialized() -> Bool {
|
||||
synchronizer != nil
|
||||
}
|
||||
|
||||
func synchronizerStarted(_ synchronizerState: SDKSynchronizer.SynchronizerState?) {
|
||||
private func synchronizerStarted(_ synchronizerState: SDKSynchronizer.SynchronizerState?) {
|
||||
latestScannedSynchronizerState = synchronizerState
|
||||
stateChanged.send(.started)
|
||||
}
|
||||
|
||||
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) {
|
||||
private func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) {
|
||||
latestScannedSynchronizerState = synchronizerState
|
||||
stateChanged.send(.synced)
|
||||
}
|
||||
|
||||
func synchronizerProgressUpdated() {
|
||||
private func synchronizerProgressUpdated() {
|
||||
stateChanged.send(.progressUpdated)
|
||||
}
|
||||
|
||||
func synchronizerStopped() {
|
||||
private func synchronizerStopped() {
|
||||
stateChanged.send(.stopped)
|
||||
}
|
||||
}
|
||||
|
||||
func statusSnapshot() -> SyncStatusSnapshot {
|
||||
guard let synchronizer else {
|
||||
return .default
|
||||
}
|
||||
|
||||
return SyncStatusSnapshot.snapshotFor(state: synchronizer.status)
|
||||
}
|
||||
extension SDKSynchronizerClient: DependencyKey {
|
||||
static let liveValue: SDKSynchronizerClient = Self.live()
|
||||
|
||||
func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error>? {
|
||||
return synchronizer?.rewind(policy)
|
||||
}
|
||||
|
||||
func getShieldedBalance() -> WalletBalance? {
|
||||
latestScannedSynchronizerState?.shieldedBalance
|
||||
}
|
||||
static func live(
|
||||
notificationCenter: NotificationCenterClient = .live,
|
||||
databaseFiles: DatabaseFilesClient = .liveValue,
|
||||
environment: ZcashSDKEnvironment = .liveValue
|
||||
) -> Self {
|
||||
let sdkSynchronizerWrapper = SDKSynchronizerLiveWrapper(
|
||||
notificationCenter: notificationCenter,
|
||||
databaseFiles: databaseFiles,
|
||||
environment: environment
|
||||
)
|
||||
let synchronizer = sdkSynchronizerWrapper.synchronizer
|
||||
|
||||
func getTransparentBalance() -> WalletBalance? {
|
||||
latestScannedSynchronizerState?.transparentBalance
|
||||
}
|
||||
return SDKSynchronizerClient(
|
||||
stateChangedStream: { sdkSynchronizerWrapper.stateChanged },
|
||||
latestScannedSynchronizerState: { sdkSynchronizerWrapper.latestScannedSynchronizerState },
|
||||
latestScannedHeight: { synchronizer.latestScannedHeight },
|
||||
prepareWith: { seedBytes, viewingKey, walletBirtday in
|
||||
let result = try synchronizer.prepare(with: seedBytes, viewingKeys: [viewingKey], walletBirthday: walletBirtday)
|
||||
|
||||
func getAllSentTransactions() -> EffectTask<[WalletEvent]> {
|
||||
if let transactions = try? synchronizer?.allSentTransactions() {
|
||||
return EffectTask(value: transactions.map {
|
||||
let memos = try? synchronizer?.getMemos(for: $0)
|
||||
let transaction = TransactionState.init(transaction: $0, memos: memos)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
|
||||
func getAllReceivedTransactions() -> EffectTask<[WalletEvent]> {
|
||||
if let transactions = try? synchronizer?.allReceivedTransactions() {
|
||||
return EffectTask(value: transactions.map {
|
||||
let memos = try? synchronizer?.getMemos(for: $0)
|
||||
let transaction = TransactionState.init(transaction: $0, memos: memos)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
|
||||
func getAllClearedTransactions() -> EffectTask<[WalletEvent]> {
|
||||
if let transactions = try? synchronizer?.allClearedTransactions() {
|
||||
return EffectTask(value: transactions.map {
|
||||
let memos = try? synchronizer?.getMemos(for: $0)
|
||||
let transaction = TransactionState.init(transaction: $0, memos: memos)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
|
||||
func getAllPendingTransactions() -> EffectTask<[WalletEvent]> {
|
||||
if let transactions = try? synchronizer?.allPendingTransactions(),
|
||||
let syncedBlockHeight = synchronizer?.latestScannedHeight {
|
||||
return EffectTask(value: transactions.map {
|
||||
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
|
||||
func getAllTransactions() -> EffectTask<[WalletEvent]> {
|
||||
if let pendingTransactions = try? synchronizer?.allPendingTransactions(),
|
||||
let clearedTransactions = try? synchronizer?.allClearedTransactions(),
|
||||
let syncedBlockHeight = synchronizer?.latestScannedHeight {
|
||||
let clearedTxs: [WalletEvent] = clearedTransactions.map {
|
||||
let transaction = TransactionState.init(transaction: $0)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
let pendingTxs: [WalletEvent] = pendingTransactions.map {
|
||||
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
let cTxs = clearedTxs.filter { transaction in
|
||||
pendingTxs.first { pending in
|
||||
pending.id == transaction.id
|
||||
} == nil
|
||||
}
|
||||
|
||||
return .merge(
|
||||
EffectTask(value: cTxs),
|
||||
EffectTask(value: pendingTxs)
|
||||
)
|
||||
.flatMap(Publishers.Sequence.init(sequence:))
|
||||
.collect()
|
||||
.eraseToEffect()
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
|
||||
func getUnifiedAddress(account: Int) -> UnifiedAddress? {
|
||||
synchronizer?.getUnifiedAddress(accountIndex: account)
|
||||
}
|
||||
|
||||
func getTransparentAddress(account: Int) -> TransparentAddress? {
|
||||
synchronizer?.getTransparentAddress(accountIndex: account)
|
||||
}
|
||||
|
||||
func getSaplingAddress(accountIndex: Int) async -> SaplingAddress? {
|
||||
await synchronizer?.getSaplingAddress(accountIndex: accountIndex)
|
||||
}
|
||||
|
||||
func sendTransaction(
|
||||
with spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to recipientAddress: Recipient,
|
||||
memo: Memo?
|
||||
) -> EffectTask<Result<TransactionState, NSError>> {
|
||||
return .run { [weak self] send in
|
||||
do {
|
||||
guard let synchronizer = self?.synchronizer else {
|
||||
await send(.failure(SDKSynchronizerClientError.synchronizerNotInitialized as NSError))
|
||||
return
|
||||
if result != .success {
|
||||
throw SynchronizerError.initFailed(message: "")
|
||||
}
|
||||
},
|
||||
start: { retry in try synchronizer.start(retry: retry) },
|
||||
stop: { synchronizer.stop() },
|
||||
statusSnapshot: { SyncStatusSnapshot.snapshotFor(state: synchronizer.status) },
|
||||
isSyncing: { sdkSynchronizerWrapper.latestScannedSynchronizerState?.syncStatus.isSyncing ?? false },
|
||||
isInitialized: { synchronizer.status != .unprepared },
|
||||
rewind: { synchronizer.rewind($0) },
|
||||
getShieldedBalance: { sdkSynchronizerWrapper.latestScannedSynchronizerState?.shieldedBalance },
|
||||
getTransparentBalance: { sdkSynchronizerWrapper.latestScannedSynchronizerState?.transparentBalance },
|
||||
getAllSentTransactions: {
|
||||
if let transactions = try? synchronizer.allSentTransactions() {
|
||||
return EffectTask(value: transactions.map {
|
||||
let memos = try? synchronizer.getMemos(for: $0)
|
||||
let transaction = TransactionState.init(transaction: $0, memos: memos)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
let pendingTransaction = try await synchronizer.sendToAddress(
|
||||
return .none
|
||||
},
|
||||
getAllReceivedTransactions: {
|
||||
if let transactions = try? synchronizer.allReceivedTransactions() {
|
||||
return EffectTask(value: transactions.map {
|
||||
let memos = try? synchronizer.getMemos(for: $0)
|
||||
let transaction = TransactionState.init(transaction: $0, memos: memos)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
return .none
|
||||
},
|
||||
getAllClearedTransactions: {
|
||||
if let transactions = try? synchronizer.allClearedTransactions() {
|
||||
return EffectTask(value: transactions.map {
|
||||
let memos = try? synchronizer.getMemos(for: $0)
|
||||
let transaction = TransactionState.init(transaction: $0, memos: memos)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
return .none
|
||||
},
|
||||
getAllPendingTransactions: {
|
||||
if let transactions = try? synchronizer.allPendingTransactions() {
|
||||
return EffectTask(value: transactions.map {
|
||||
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: synchronizer.latestScannedHeight)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
})
|
||||
}
|
||||
|
||||
return .none
|
||||
},
|
||||
getAllTransactions: {
|
||||
if let pendingTransactions = try? synchronizer.allPendingTransactions(),
|
||||
let clearedTransactions = try? synchronizer.allClearedTransactions() {
|
||||
let clearedTxs: [WalletEvent] = clearedTransactions.map {
|
||||
let transaction = TransactionState.init(transaction: $0)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
let pendingTxs: [WalletEvent] = pendingTransactions.map {
|
||||
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: synchronizer.latestScannedHeight)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
let cTxs = clearedTxs.filter { transaction in
|
||||
pendingTxs.first { pending in
|
||||
pending.id == transaction.id
|
||||
} == nil
|
||||
}
|
||||
|
||||
return .merge(
|
||||
EffectTask(value: cTxs),
|
||||
EffectTask(value: pendingTxs)
|
||||
)
|
||||
.flatMap(Publishers.Sequence.init(sequence:))
|
||||
.collect()
|
||||
.eraseToEffect()
|
||||
}
|
||||
|
||||
return .none
|
||||
},
|
||||
getUnifiedAddress: { synchronizer.getUnifiedAddress(accountIndex: $0) },
|
||||
getTransparentAddress: { synchronizer.getTransparentAddress(accountIndex: $0) },
|
||||
getSaplingAddress: { await synchronizer.getSaplingAddress(accountIndex: $0) },
|
||||
sendTransaction: { spendingKey, amount, recipient, memo in
|
||||
return .run { send in
|
||||
do {
|
||||
let pendingTransaction = try await synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: amount,
|
||||
toAddress: recipient,
|
||||
memo: memo
|
||||
)
|
||||
|
||||
await send(.success(TransactionState(pendingTransaction: pendingTransaction)))
|
||||
} catch {
|
||||
await send(.failure(error as NSError))
|
||||
}
|
||||
}
|
||||
},
|
||||
shieldFunds: { spendingKey, memo, shieldingThreshold in
|
||||
let pendingTransaction = try await synchronizer.shieldFunds(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: zatoshi,
|
||||
toAddress: recipientAddress,
|
||||
memo: memo
|
||||
memo: memo,
|
||||
shieldingThreshold: shieldingThreshold
|
||||
)
|
||||
|
||||
await send(.success(TransactionState(pendingTransaction: pendingTransaction)))
|
||||
} catch {
|
||||
await send(.failure(error as NSError))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> TransactionState {
|
||||
guard let synchronizer = synchronizer else { throw SDKSynchronizerClientError.synchronizerNotInitialized }
|
||||
let pendingTransaction = try await synchronizer.shieldFunds(spendingKey: spendingKey, memo: memo, shieldingThreshold: shieldingThreshold)
|
||||
return TransactionState(pendingTransaction: pendingTransaction)
|
||||
}
|
||||
|
||||
func wipe() -> AnyPublisher<Void, Error>? {
|
||||
synchronizer?.wipe()
|
||||
return TransactionState(pendingTransaction: pendingTransaction)
|
||||
},
|
||||
wipe: { synchronizer.wipe() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,240 +0,0 @@
|
|||
//
|
||||
// SDKSynchronizerMocks.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 15.11.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
extension SDKSynchronizerDependency {
|
||||
static let mock: SDKSynchronizerClient = MockSDKSynchronizerClient()
|
||||
|
||||
static func mockWithSnapshot(_ snapshot: SyncStatusSnapshot) -> MockSDKSynchronizerClient {
|
||||
MockSDKSynchronizerClient(snapshot: snapshot)
|
||||
}
|
||||
}
|
||||
|
||||
class MockSDKSynchronizerClient: SDKSynchronizerClient {
|
||||
private var cancellables: [AnyCancellable] = []
|
||||
private var snapshot: SyncStatusSnapshot
|
||||
private(set) var notificationCenter: NotificationCenterClient
|
||||
private(set) var synchronizer: SDKSynchronizer?
|
||||
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
|
||||
private(set) var walletBirthday: BlockHeight?
|
||||
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
|
||||
|
||||
init(
|
||||
notificationCenter: NotificationCenterClient = .noOp,
|
||||
snapshot: SyncStatusSnapshot = .default
|
||||
) {
|
||||
self.notificationCenter = notificationCenter
|
||||
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
|
||||
self.snapshot = snapshot
|
||||
}
|
||||
|
||||
func prepareWith(initializer: Initializer, seedBytes: [UInt8], viewingKey: UnifiedFullViewingKey, walletBirthday: BlockHeight) throws { }
|
||||
|
||||
func start(retry: Bool) throws { }
|
||||
|
||||
func stop() { }
|
||||
|
||||
func isSyncing() -> Bool { false }
|
||||
|
||||
func isInitialized() -> Bool { false }
|
||||
|
||||
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) { }
|
||||
|
||||
func statusSnapshot() -> SyncStatusSnapshot { self.snapshot }
|
||||
|
||||
func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error>? { Empty<Void, Error>().eraseToAnyPublisher() }
|
||||
|
||||
func getShieldedBalance() -> WalletBalance? {
|
||||
WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000))
|
||||
}
|
||||
|
||||
func getTransparentBalance() -> WalletBalance? {
|
||||
WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000))
|
||||
}
|
||||
|
||||
func getAllSentTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func getAllReceivedTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func getAllClearedTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func getAllPendingTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(
|
||||
date: 1651039606,
|
||||
amount: Zatoshi(6),
|
||||
status: .paid(success: false),
|
||||
uuid: "ff66"
|
||||
),
|
||||
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
|
||||
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
|
||||
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.amount.amount > 5 ? .pending : $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func getAllTransactions() -> EffectTask<[WalletEvent]> {
|
||||
return .merge(
|
||||
getAllClearedTransactions(),
|
||||
getAllPendingTransactions()
|
||||
)
|
||||
.flatMap(Publishers.Sequence.init(sequence:))
|
||||
.collect()
|
||||
.eraseToEffect()
|
||||
}
|
||||
|
||||
func getUnifiedAddress(account: Int) -> UnifiedAddress? {
|
||||
// swiftlint:disable force_try
|
||||
try! UnifiedAddress(
|
||||
encoding: """
|
||||
utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyz\
|
||||
wtgnuc76h
|
||||
""",
|
||||
network: .testnet
|
||||
)
|
||||
}
|
||||
|
||||
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
|
||||
|
||||
func getSaplingAddress(accountIndex account: Int) -> SaplingAddress? {
|
||||
// swiftlint:disable:next force_try
|
||||
try! SaplingAddress(encoding: "ztestsapling1edm52k336nk70gxqxedd89slrrf5xwnnp5rt6gqnk0tgw4mynv6fcx42ym6x27yac5amvfvwypz", network: .testnet)
|
||||
}
|
||||
|
||||
func sendTransaction(
|
||||
with spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to recipientAddress: Recipient,
|
||||
memo: Memo?
|
||||
) -> EffectTask<Result<TransactionState, NSError>> {
|
||||
var memos: [Memo]? = []
|
||||
if let memo { memos?.append(memo) }
|
||||
|
||||
let transactionState = TransactionState(
|
||||
expiryHeight: 40,
|
||||
memos: memos,
|
||||
minedHeight: 50,
|
||||
shielded: true,
|
||||
zAddress: "tteafadlamnelkqe",
|
||||
fee: Zatoshi(10),
|
||||
id: "id",
|
||||
status: .paid(success: true),
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(10)
|
||||
)
|
||||
|
||||
return EffectTask(value: Result.success(transactionState))
|
||||
}
|
||||
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> TransactionState {
|
||||
return TransactionState(
|
||||
expiryHeight: 40,
|
||||
memos: [memo],
|
||||
minedHeight: 50,
|
||||
shielded: true,
|
||||
zAddress: "tteafadlamnelkqe",
|
||||
fee: Zatoshi(10),
|
||||
id: "id",
|
||||
status: .paid(success: true),
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(10)
|
||||
)
|
||||
}
|
||||
|
||||
func wipe() -> AnyPublisher<Void, Error>? { nil }
|
||||
}
|
|
@ -10,257 +10,317 @@ import ComposableArchitecture
|
|||
import Foundation
|
||||
import ZcashLightClientKit
|
||||
|
||||
extension SDKSynchronizerDependency: TestDependencyKey {
|
||||
static let testValue: SDKSynchronizerClient = NoopSDKSynchronizer()
|
||||
extension SDKSynchronizerClient: TestDependencyKey {
|
||||
static let testValue = Self(
|
||||
stateChangedStream: XCTUnimplemented("\(Self.self).stateChangedStream", placeholder: CurrentValueSubject(SDKSynchronizerState.unknown)),
|
||||
latestScannedSynchronizerState: XCTUnimplemented("\(Self.self).latestScannedSynchronizerState", placeholder: .zero),
|
||||
latestScannedHeight: XCTUnimplemented("\(Self.self).latestScannedHeight", placeholder: 0),
|
||||
prepareWith: XCTUnimplemented("\(Self.self).prepareWith"),
|
||||
start: XCTUnimplemented("\(Self.self).start"),
|
||||
stop: XCTUnimplemented("\(Self.self).stop"),
|
||||
statusSnapshot: XCTUnimplemented("\(Self.self).statusSnapshot", placeholder: SyncStatusSnapshot.snapshotFor(state: .unprepared)),
|
||||
isSyncing: XCTUnimplemented("\(Self.self).isSyncing", placeholder: false),
|
||||
isInitialized: XCTUnimplemented("\(Self.self).isInitialized", placeholder: false),
|
||||
rewind: XCTUnimplemented("\(Self.self).rewind", placeholder: Fail(error: "Error").eraseToAnyPublisher()),
|
||||
getShieldedBalance: XCTUnimplemented("\(Self.self).getShieldedBalance", placeholder: WalletBalance.zero),
|
||||
getTransparentBalance: XCTUnimplemented("\(Self.self).getTransparentBalance", placeholder: WalletBalance.zero),
|
||||
getAllSentTransactions: XCTUnimplemented("\(Self.self).getAllSentTransactions", placeholder: .none),
|
||||
getAllReceivedTransactions: XCTUnimplemented("\(Self.self).getAllReceivedTransactions", placeholder: .none),
|
||||
getAllClearedTransactions: XCTUnimplemented("\(Self.self).getAllClearedTransactions", placeholder: .none),
|
||||
getAllPendingTransactions: XCTUnimplemented("\(Self.self).getAllPendingTransactions", placeholder: .none),
|
||||
getAllTransactions: XCTUnimplemented("\(Self.self).getAllTransactions", placeholder: .none),
|
||||
getUnifiedAddress: XCTUnimplemented("\(Self.self).getUnifiedAddress", placeholder: nil),
|
||||
getTransparentAddress: XCTUnimplemented("\(Self.self).getTransparentAddress", placeholder: nil),
|
||||
getSaplingAddress: XCTUnimplemented("\(Self.self).getSaplingAddress", placeholder: nil),
|
||||
sendTransaction: XCTUnimplemented("\(Self.self).sendTransaction", placeholder: .none),
|
||||
shieldFunds: XCTUnimplemented("\(Self.self).shieldFunds", placeholder: .placeholder),
|
||||
wipe: XCTUnimplemented("\(Self.self).wipe")
|
||||
)
|
||||
}
|
||||
|
||||
class NoopSDKSynchronizer: SDKSynchronizerClient {
|
||||
private(set) var notificationCenter: NotificationCenterClient
|
||||
private(set) var synchronizer: SDKSynchronizer?
|
||||
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
|
||||
private(set) var walletBirthday: BlockHeight?
|
||||
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
|
||||
extension SDKSynchronizerClient {
|
||||
static let noOp = Self(
|
||||
stateChangedStream: { CurrentValueSubject<SDKSynchronizerState, Never>(.unknown) },
|
||||
latestScannedSynchronizerState: { nil },
|
||||
latestScannedHeight: { 0 },
|
||||
prepareWith: { _, _, _ in },
|
||||
start: { _ in },
|
||||
stop: { },
|
||||
statusSnapshot: { SyncStatusSnapshot.snapshotFor(state: .unprepared) },
|
||||
isSyncing: { false },
|
||||
isInitialized: { false },
|
||||
rewind: { _ in return Empty<Void, Error>().eraseToAnyPublisher() },
|
||||
getShieldedBalance: { .zero },
|
||||
getTransparentBalance: { .zero },
|
||||
getAllSentTransactions: { EffectTask(value: []) },
|
||||
getAllReceivedTransactions: { EffectTask(value: []) },
|
||||
getAllClearedTransactions: { EffectTask(value: []) },
|
||||
getAllPendingTransactions: { EffectTask(value: []) },
|
||||
getAllTransactions: { EffectTask(value: []) },
|
||||
getUnifiedAddress: { _ in return nil },
|
||||
getTransparentAddress: { _ in return nil },
|
||||
getSaplingAddress: { _ in return nil },
|
||||
sendTransaction: { _, _, _, _ in return EffectTask(value: Result.failure(SynchronizerError.criticalError as NSError)) },
|
||||
shieldFunds: { _, _, _ in return .placeholder },
|
||||
wipe: { Empty<Void, Error>().eraseToAnyPublisher() }
|
||||
)
|
||||
|
||||
init(notificationCenter: NotificationCenterClient = .noOp) {
|
||||
self.notificationCenter = notificationCenter
|
||||
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
|
||||
}
|
||||
|
||||
func prepareWith(initializer: Initializer, seedBytes: [UInt8], viewingKey: UnifiedFullViewingKey, walletBirthday: BlockHeight) throws { }
|
||||
|
||||
func start(retry: Bool) throws { }
|
||||
|
||||
func stop() { }
|
||||
|
||||
func isSyncing() -> Bool { false }
|
||||
|
||||
func isInitialized() -> Bool { false }
|
||||
|
||||
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) { }
|
||||
|
||||
func statusSnapshot() -> SyncStatusSnapshot { .default }
|
||||
|
||||
func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error>? { Empty<Void, Error>().eraseToAnyPublisher() }
|
||||
|
||||
func getShieldedBalance() -> WalletBalance? { nil }
|
||||
|
||||
func getTransparentBalance() -> WalletBalance? { nil }
|
||||
|
||||
func getAllSentTransactions() -> EffectTask<[WalletEvent]> { EffectTask(value: []) }
|
||||
|
||||
func getAllReceivedTransactions() -> EffectTask<[WalletEvent]> { EffectTask(value: []) }
|
||||
|
||||
func getAllClearedTransactions() -> EffectTask<[WalletEvent]> { EffectTask(value: []) }
|
||||
|
||||
func getAllPendingTransactions() -> EffectTask<[WalletEvent]> { EffectTask(value: []) }
|
||||
|
||||
func getAllTransactions() -> EffectTask<[WalletEvent]> { EffectTask(value: []) }
|
||||
|
||||
func getUnifiedAddress(account: Int) -> UnifiedAddress? { nil }
|
||||
|
||||
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
|
||||
|
||||
func getSaplingAddress(accountIndex account: Int) -> SaplingAddress? { nil }
|
||||
|
||||
func sendTransaction(
|
||||
with spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to recipientAddress: Recipient,
|
||||
memo: Memo?
|
||||
) -> EffectTask<Result<TransactionState, NSError>> {
|
||||
EffectTask(value: Result.failure(SynchronizerError.criticalError as NSError))
|
||||
}
|
||||
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> TransactionState {
|
||||
throw SynchronizerError.criticalError
|
||||
}
|
||||
|
||||
func updateStateChanged(_ newState: SDKSynchronizerState) {
|
||||
stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(newState)
|
||||
}
|
||||
|
||||
func wipe() -> AnyPublisher<Void, Error>? { nil }
|
||||
static let mock = Self.mocked()
|
||||
}
|
||||
|
||||
class TestSDKSynchronizerClient: SDKSynchronizerClient {
|
||||
private(set) var notificationCenter: NotificationCenterClient
|
||||
private(set) var synchronizer: SDKSynchronizer?
|
||||
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
|
||||
private(set) var walletBirthday: BlockHeight?
|
||||
private(set) var latestScannedSynchronizerState: SDKSynchronizer.SynchronizerState?
|
||||
extension SDKSynchronizerClient {
|
||||
static func mocked(
|
||||
stateChangedStream: @escaping () -> CurrentValueSubject<SDKSynchronizerState, Never> = {
|
||||
CurrentValueSubject<SDKSynchronizerState, Never>(.synced)
|
||||
},
|
||||
latestScannedSynchronizerState: @escaping () -> SDKSynchronizer.SynchronizerState? = { nil },
|
||||
latestScannedHeight: @escaping () -> BlockHeight = { 0 },
|
||||
prepareWith: @escaping ([UInt8], UnifiedFullViewingKey, BlockHeight) throws -> Void = { _, _, _ in },
|
||||
start: @escaping (_ retry: Bool) throws -> Void = { _ in },
|
||||
stop: @escaping () -> Void = { },
|
||||
statusSnapshot: @escaping () -> SyncStatusSnapshot = { SyncStatusSnapshot.snapshotFor(state: .unprepared) },
|
||||
isSyncing: @escaping () -> Bool = { false },
|
||||
isInitialized: @escaping () -> Bool = { false },
|
||||
rewind: @escaping (RewindPolicy) -> AnyPublisher<Void, Error> = { _ in return Empty<Void, Error>().eraseToAnyPublisher() },
|
||||
getShieldedBalance: @escaping () -> WalletBalance? = { WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000)) },
|
||||
getTransparentBalance: @escaping () -> WalletBalance? = { WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000)) },
|
||||
getAllSentTransactions: @escaping () -> EffectTask<[WalletEvent]> = {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
init(notificationCenter: NotificationCenterClient = .noOp) {
|
||||
self.notificationCenter = notificationCenter
|
||||
self.stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(.unknown)
|
||||
}
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
|
||||
}
|
||||
)
|
||||
},
|
||||
getAllReceivedTransactions: @escaping () -> EffectTask<[WalletEvent]> = {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
func prepareWith(initializer: Initializer, seedBytes: [UInt8], viewingKey: UnifiedFullViewingKey, walletBirthday: BlockHeight) throws { }
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
|
||||
}
|
||||
)
|
||||
},
|
||||
getAllClearedTransactions: @escaping () -> EffectTask<[WalletEvent]> = {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
func start(retry: Bool) throws { }
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
|
||||
}
|
||||
)
|
||||
},
|
||||
getAllPendingTransactions: @escaping () -> EffectTask<[WalletEvent]> = {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(
|
||||
date: 1651039606,
|
||||
amount: Zatoshi(6),
|
||||
status: .paid(success: false),
|
||||
uuid: "ff66"
|
||||
),
|
||||
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
|
||||
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
|
||||
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
|
||||
]
|
||||
|
||||
func stop() { }
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.amount.amount > 5 ? .pending : $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
)
|
||||
},
|
||||
getAllTransactions: @escaping () -> EffectTask<[WalletEvent]> = {
|
||||
let mockerCleared: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
func isSyncing() -> Bool { false }
|
||||
let clearedTransactionsEffect = EffectTask(
|
||||
value:
|
||||
mockerCleared.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
|
||||
}
|
||||
)
|
||||
|
||||
func isInitialized() -> Bool { false }
|
||||
let mockedPending: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(
|
||||
date: 1651039606,
|
||||
amount: Zatoshi(6),
|
||||
status: .paid(success: false),
|
||||
uuid: "ff66"
|
||||
),
|
||||
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
|
||||
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
|
||||
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
|
||||
]
|
||||
|
||||
func synchronizerSynced(_ synchronizerState: SDKSynchronizer.SynchronizerState?) { }
|
||||
let pendingTransactionsEffect = EffectTask(
|
||||
value:
|
||||
mockedPending.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.amount.amount > 5 ? .pending : $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
)
|
||||
|
||||
func statusSnapshot() -> SyncStatusSnapshot { .default }
|
||||
return .merge(
|
||||
clearedTransactionsEffect,
|
||||
pendingTransactionsEffect
|
||||
)
|
||||
.flatMap(Publishers.Sequence.init(sequence:))
|
||||
.collect()
|
||||
.eraseToEffect()
|
||||
},
|
||||
getUnifiedAddress: @escaping (_ account: Int) -> UnifiedAddress? = { _ in
|
||||
// swiftlint:disable force_try
|
||||
try! UnifiedAddress(
|
||||
encoding: """
|
||||
utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7d\
|
||||
wyzwtgnuc76h
|
||||
""",
|
||||
network: .testnet
|
||||
)
|
||||
},
|
||||
getTransparentAddress: @escaping (_ account: Int) -> TransparentAddress? = { _ in return nil },
|
||||
getSaplingAddress: @escaping (_ accountIndex: Int) async -> SaplingAddress? = { _ in
|
||||
// swiftlint:disable:next force_try
|
||||
try! SaplingAddress(
|
||||
encoding: "ztestsapling1edm52k336nk70gxqxedd89slrrf5xwnnp5rt6gqnk0tgw4mynv6fcx42ym6x27yac5amvfvwypz",
|
||||
network: .testnet
|
||||
)
|
||||
},
|
||||
sendTransaction:
|
||||
@escaping (UnifiedSpendingKey, Zatoshi, Recipient, Memo?) -> EffectTask<Result<TransactionState, NSError>> = { _, _, _, memo in
|
||||
var memos: [Memo]? = []
|
||||
if let memo { memos?.append(memo) }
|
||||
|
||||
func rewind(_ policy: RewindPolicy) -> AnyPublisher<Void, Error>? { nil }
|
||||
|
||||
func getShieldedBalance() -> WalletBalance? { nil }
|
||||
|
||||
func getTransparentBalance() -> WalletBalance? { nil }
|
||||
let transactionState = TransactionState(
|
||||
expiryHeight: 40,
|
||||
memos: memos,
|
||||
minedHeight: 50,
|
||||
shielded: true,
|
||||
zAddress: "tteafadlamnelkqe",
|
||||
fee: Zatoshi(10),
|
||||
id: "id",
|
||||
status: .paid(success: true),
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(10)
|
||||
)
|
||||
|
||||
func getAllSentTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
return EffectTask(value: Result.success(transactionState))
|
||||
},
|
||||
shieldFunds: @escaping (UnifiedSpendingKey, Memo, Zatoshi) async throws -> TransactionState = { _, memo, _ in
|
||||
return TransactionState(
|
||||
expiryHeight: 40,
|
||||
memos: [memo],
|
||||
minedHeight: 50,
|
||||
shielded: true,
|
||||
zAddress: "tteafadlamnelkqe",
|
||||
fee: Zatoshi(10),
|
||||
id: "id",
|
||||
status: .paid(success: true),
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(10)
|
||||
)
|
||||
},
|
||||
wipe: @escaping () -> AnyPublisher<Void, Error>? = { Fail(error: "Error").eraseToAnyPublisher() }
|
||||
) -> SDKSynchronizerClient {
|
||||
SDKSynchronizerClient(
|
||||
stateChangedStream: stateChangedStream,
|
||||
latestScannedSynchronizerState: latestScannedSynchronizerState,
|
||||
latestScannedHeight: latestScannedHeight,
|
||||
prepareWith: prepareWith,
|
||||
start: start,
|
||||
stop: stop,
|
||||
statusSnapshot: statusSnapshot,
|
||||
isSyncing: isSyncing,
|
||||
isInitialized: isInitialized,
|
||||
rewind: rewind,
|
||||
getShieldedBalance: getShieldedBalance,
|
||||
getTransparentBalance: getTransparentBalance,
|
||||
getAllSentTransactions: getAllSentTransactions,
|
||||
getAllReceivedTransactions: getAllReceivedTransactions,
|
||||
getAllClearedTransactions: getAllClearedTransactions,
|
||||
getAllPendingTransactions: getAllPendingTransactions,
|
||||
getAllTransactions: getAllTransactions,
|
||||
getUnifiedAddress: getUnifiedAddress,
|
||||
getTransparentAddress: getTransparentAddress,
|
||||
getSaplingAddress: getSaplingAddress,
|
||||
sendTransaction: sendTransaction,
|
||||
shieldFunds: shieldFunds,
|
||||
wipe: wipe
|
||||
)
|
||||
}
|
||||
|
||||
func getAllReceivedTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func getAllClearedTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
|
||||
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
|
||||
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
|
||||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func getAllPendingTransactions() -> EffectTask<[WalletEvent]> {
|
||||
let mocked: [TransactionStateMockHelper] = [
|
||||
TransactionStateMockHelper(
|
||||
date: 1651039606,
|
||||
amount: Zatoshi(6),
|
||||
status: .paid(success: false),
|
||||
uuid: "ff66"
|
||||
),
|
||||
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
|
||||
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
|
||||
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
|
||||
]
|
||||
|
||||
return EffectTask(
|
||||
value:
|
||||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.amount.amount > 5 ? .pending : $0.status,
|
||||
timestamp: $0.date,
|
||||
uuid: $0.uuid
|
||||
)
|
||||
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func getAllTransactions() -> EffectTask<[WalletEvent]> {
|
||||
return .merge(
|
||||
getAllClearedTransactions(),
|
||||
getAllPendingTransactions()
|
||||
)
|
||||
.flatMap(Publishers.Sequence.init(sequence:))
|
||||
.collect()
|
||||
.eraseToEffect()
|
||||
}
|
||||
|
||||
func getUnifiedAddress(account: Int) -> UnifiedAddress? { nil }
|
||||
|
||||
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
|
||||
|
||||
func getSaplingAddress(accountIndex account: Int) -> SaplingAddress? {
|
||||
// swiftlint:disable:next force_try
|
||||
try! SaplingAddress(encoding: "ztestsapling1edm52k336nk70gxqxedd89slrrf5xwnnp5rt6gqnk0tgw4mynv6fcx42ym6x27yac5amvfvwypz", network: .testnet)
|
||||
}
|
||||
|
||||
func sendTransaction(
|
||||
with spendingKey: UnifiedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to recipientAddress: Recipient,
|
||||
memo: Memo?
|
||||
) -> EffectTask<Result<TransactionState, NSError>> {
|
||||
return EffectTask(value: Result.failure(SynchronizerError.criticalError as NSError))
|
||||
}
|
||||
|
||||
func shieldFunds(
|
||||
spendingKey: UnifiedSpendingKey,
|
||||
memo: Memo,
|
||||
shieldingThreshold: Zatoshi
|
||||
) async throws -> TransactionState {
|
||||
throw SynchronizerError.criticalError
|
||||
}
|
||||
|
||||
func updateStateChanged(_ newState: SDKSynchronizerState) {
|
||||
stateChanged = CurrentValueSubject<SDKSynchronizerState, Never>(newState)
|
||||
}
|
||||
|
||||
func wipe() -> AnyPublisher<Void, Error>? { nil }
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ struct BalanceBreakdownReducer: ReducerProtocol {
|
|||
return .none
|
||||
|
||||
case .onAppear:
|
||||
return sdkSynchronizer.stateChanged
|
||||
return sdkSynchronizer.stateChangedStream()
|
||||
.map(BalanceBreakdownReducer.Action.synchronizerStateChanged)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: CancelId.self, cancelInFlight: true)
|
||||
|
@ -84,11 +84,7 @@ struct BalanceBreakdownReducer: ReducerProtocol {
|
|||
let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase.value())
|
||||
let spendingKey = try derivationTool.deriveSpendingKey(seedBytes, 0)
|
||||
|
||||
_ = try await sdkSynchronizer.shieldFunds(
|
||||
spendingKey: spendingKey,
|
||||
memo: Memo(string: ""),
|
||||
shieldingThreshold: state.autoShieldingThreshold
|
||||
)
|
||||
_ = try await sdkSynchronizer.shieldFunds(spendingKey, Memo(string: ""), state.autoShieldingThreshold)
|
||||
|
||||
await send(.shieldFundsSuccess)
|
||||
} catch {
|
||||
|
@ -121,17 +117,17 @@ struct BalanceBreakdownReducer: ReducerProtocol {
|
|||
return EffectTask(value: .updateSynchronizerStatus)
|
||||
|
||||
case .updateSynchronizerStatus:
|
||||
if let shieldedBalance = sdkSynchronizer.latestScannedSynchronizerState?.shieldedBalance {
|
||||
if let shieldedBalance = sdkSynchronizer.latestScannedSynchronizerState()?.shieldedBalance {
|
||||
state.shieldedBalance = shieldedBalance.redacted
|
||||
}
|
||||
if let transparentBalance = sdkSynchronizer.latestScannedSynchronizerState?.transparentBalance {
|
||||
if let transparentBalance = sdkSynchronizer.latestScannedSynchronizerState()?.transparentBalance {
|
||||
state.transparentBalance = transparentBalance.redacted
|
||||
}
|
||||
return EffectTask(value: .updateLatestBlock)
|
||||
|
||||
case .updateLatestBlock:
|
||||
guard
|
||||
let latestBlockNumber = sdkSynchronizer.latestScannedSynchronizerState?.latestScannedHeight,
|
||||
let latestBlockNumber = sdkSynchronizer.latestScannedSynchronizerState()?.latestScannedHeight,
|
||||
let latestBlock = numberFormatter.string(NSDecimalNumber(value: latestBlockNumber))
|
||||
else {
|
||||
state.latestBlock = L10n.General.unknown
|
||||
|
|
|
@ -112,7 +112,7 @@ struct HomeReducer: ReducerProtocol {
|
|||
state.requiredTransactionConfirmations = zcashSDKEnvironment.requiredTransactionConfirmations
|
||||
|
||||
if diskSpaceChecker.hasEnoughFreeSpaceForSync() {
|
||||
let syncEffect = sdkSynchronizer.stateChanged
|
||||
let syncEffect = sdkSynchronizer.stateChangedStream()
|
||||
.map(HomeReducer.Action.synchronizerStateChanged)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: CancelId.self, cancelInFlight: true)
|
||||
|
@ -138,7 +138,7 @@ struct HomeReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
state.synchronizerStatusSnapshot = snapshot
|
||||
if let shieldedBalance = sdkSynchronizer.latestScannedSynchronizerState?.shieldedBalance {
|
||||
if let shieldedBalance = sdkSynchronizer.latestScannedSynchronizerState()?.shieldedBalance {
|
||||
state.shieldedBalance = shieldedBalance.redacted
|
||||
}
|
||||
|
||||
|
@ -179,7 +179,7 @@ struct HomeReducer: ReducerProtocol {
|
|||
|
||||
case .retrySync:
|
||||
do {
|
||||
try sdkSynchronizer.start(retry: true)
|
||||
try sdkSynchronizer.start(true)
|
||||
} catch {
|
||||
state.alert = AlertState<HomeReducer.Action>(
|
||||
title: TextState(L10n.Home.SyncFailed.title),
|
||||
|
|
|
@ -43,7 +43,7 @@ struct ProfileReducer: ReducerProtocol {
|
|||
Reduce { state, action in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
state.addressDetailsState.uAddress = self.sdkSynchronizer.getUnifiedAddress(account: 0)
|
||||
state.addressDetailsState.uAddress = self.sdkSynchronizer.getUnifiedAddress(0)
|
||||
state.appBuild = appVersion.appBuild()
|
||||
state.appVersion = appVersion.appVersion()
|
||||
state.sdkVersion = zcashSDKEnvironment.sdkVersion
|
||||
|
|
|
@ -71,7 +71,7 @@ extension RootReducer {
|
|||
)
|
||||
} else {
|
||||
do {
|
||||
try sdkSynchronizer.start()
|
||||
try sdkSynchronizer.start(false)
|
||||
} catch {
|
||||
// TODO: [#221] Handle error more properly (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||
state.alert = AlertState(
|
||||
|
@ -107,18 +107,7 @@ extension RootReducer {
|
|||
}
|
||||
|
||||
private func rewind(policy: RewindPolicy, sourceAction: DebugAction) -> EffectPublisher<RootReducer.Action, Never> {
|
||||
guard let rewindPublisher = sdkSynchronizer.rewind(policy) else {
|
||||
return EffectTask(
|
||||
value: .debug(
|
||||
.rewindDone(
|
||||
L10n.Root.Debug.Error.Rewind.sdkSynchronizerNotInitialized,
|
||||
.debug(sourceAction)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
return rewindPublisher
|
||||
return sdkSynchronizer.rewind(policy)
|
||||
.replaceEmpty(with: Void())
|
||||
.map { _ in return RootReducer.Action.debug(.rewindDone(nil, .debug(sourceAction))) }
|
||||
.catch { error in
|
||||
|
|
|
@ -77,7 +77,7 @@ extension RootReducer {
|
|||
|
||||
case .destination(.deeplink(let url)):
|
||||
// get the latest synchronizer state
|
||||
let synchronizerStatus = sdkSynchronizer.stateChanged.value
|
||||
let synchronizerStatus = sdkSynchronizer.stateChangedStream().value
|
||||
|
||||
// process the deeplink only if app is initialized and synchronizer synced
|
||||
guard state.appInitializationState == .initialized && synchronizerStatus == .synced else {
|
||||
|
|
|
@ -130,23 +130,15 @@ extension RootReducer {
|
|||
return .none
|
||||
}
|
||||
|
||||
try mnemonic.isValid(storedWallet.seedPhrase.value())
|
||||
let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase.value())
|
||||
|
||||
let birthday = state.storedWallet?.birthday?.value() ?? zcashSDKEnvironment.latestCheckpoint
|
||||
|
||||
let initializer = try RootReducer.prepareInitializer(
|
||||
databaseFiles: databaseFiles,
|
||||
derivationTool: derivationTool,
|
||||
mnemonic: mnemonic,
|
||||
zcashSDKEnvironment: zcashSDKEnvironment
|
||||
)
|
||||
|
||||
try mnemonic.isValid(storedWallet.seedPhrase.value())
|
||||
let seedBytes = try mnemonic.toSeed(storedWallet.seedPhrase.value())
|
||||
let spendingKey = try derivationTool.deriveSpendingKey(seedBytes, 0)
|
||||
let viewingKey = try spendingKey.deriveFullViewingKey()
|
||||
|
||||
try sdkSynchronizer.prepareWith(initializer: initializer, seedBytes: seedBytes, viewingKey: viewingKey, walletBirthday: birthday)
|
||||
try sdkSynchronizer.start()
|
||||
try sdkSynchronizer.prepareWith(seedBytes, viewingKey, birthday)
|
||||
try sdkSynchronizer.start(false)
|
||||
return .none
|
||||
} catch {
|
||||
state.appInitializationState = .failed
|
||||
|
|
|
@ -105,7 +105,7 @@ extension RootReducer {
|
|||
var keysPresent = false
|
||||
do {
|
||||
keysPresent = try walletStorage.areKeysPresent()
|
||||
let databaseFilesPresent = try databaseFiles.areDbFilesPresentFor(
|
||||
let databaseFilesPresent = databaseFiles.areDbFilesPresentFor(
|
||||
zcashSDKEnvironment.network
|
||||
)
|
||||
|
||||
|
@ -119,19 +119,9 @@ extension RootReducer {
|
|||
case (true, true):
|
||||
return .initialized
|
||||
}
|
||||
} catch DatabaseFiles.DatabaseFilesError.filesPresentCheck {
|
||||
if keysPresent {
|
||||
return .filesMissing
|
||||
}
|
||||
} catch WalletStorage.WalletStorageError.uninitializedWallet {
|
||||
do {
|
||||
if try databaseFiles.areDbFilesPresentFor(
|
||||
zcashSDKEnvironment.network
|
||||
) {
|
||||
return .keysMissing
|
||||
}
|
||||
} catch {
|
||||
return .uninitialized
|
||||
if databaseFiles.areDbFilesPresentFor(zcashSDKEnvironment.network) {
|
||||
return .keysMissing
|
||||
}
|
||||
} catch {
|
||||
return .failed
|
||||
|
@ -139,34 +129,6 @@ extension RootReducer {
|
|||
|
||||
return .uninitialized
|
||||
}
|
||||
|
||||
static func prepareInitializer(
|
||||
databaseFiles: DatabaseFilesClient,
|
||||
derivationTool: DerivationToolClient,
|
||||
mnemonic: MnemonicClient,
|
||||
zcashSDKEnvironment: ZcashSDKEnvironment
|
||||
) throws -> Initializer {
|
||||
do {
|
||||
let network = zcashSDKEnvironment.network
|
||||
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: try databaseFiles.cacheDbURLFor(network),
|
||||
fsBlockDbRoot: try databaseFiles.fsBlockDbRootFor(network),
|
||||
dataDbURL: try databaseFiles.dataDbURLFor(network),
|
||||
pendingDbURL: try databaseFiles.pendingDbURLFor(network),
|
||||
endpoint: zcashSDKEnvironment.endpoint,
|
||||
network: zcashSDKEnvironment.network,
|
||||
spendParamsURL: try databaseFiles.spendParamsURLFor(network),
|
||||
outputParamsURL: try databaseFiles.outputParamsURLFor(network),
|
||||
saplingParamsSourceURL: SaplingParamsSourceURL.default,
|
||||
loggerProxy: OSLogger(logLevel: .debug, category: LoggerConstants.sdkLogs)
|
||||
)
|
||||
|
||||
return initializer
|
||||
} catch {
|
||||
throw SDKInitializationError.failed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
|
|
@ -162,15 +162,10 @@ struct SendFlowReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
let recipient = try Recipient(state.address, network: zcashSDKEnvironment.network.networkType)
|
||||
let sendTransActionEffect = sdkSynchronizer.sendTransaction(
|
||||
with: spendingKey,
|
||||
zatoshi: state.amount,
|
||||
to: recipient,
|
||||
memo: memo
|
||||
)
|
||||
.receive(on: mainQueue)
|
||||
.map(SendFlowReducer.Action.sendTransactionResult)
|
||||
.eraseToEffect()
|
||||
let sendTransActionEffect = sdkSynchronizer.sendTransaction(spendingKey, state.amount, recipient, memo)
|
||||
.receive(on: mainQueue)
|
||||
.map(SendFlowReducer.Action.sendTransactionResult)
|
||||
.eraseToEffect()
|
||||
|
||||
return .concatenate(
|
||||
EffectTask(value: .updateDestination(.inProgress)),
|
||||
|
@ -200,7 +195,7 @@ struct SendFlowReducer: ReducerProtocol {
|
|||
|
||||
case .onAppear:
|
||||
state.memoState.charLimit = zcashSDKEnvironment.memoCharLimit
|
||||
return sdkSynchronizer.stateChanged
|
||||
return sdkSynchronizer.stateChangedStream()
|
||||
.map(SendFlowReducer.Action.synchronizerStateChanged)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: SyncStatusUpdatesID.self, cancelInFlight: true)
|
||||
|
@ -209,7 +204,7 @@ struct SendFlowReducer: ReducerProtocol {
|
|||
return .cancel(id: SyncStatusUpdatesID.self)
|
||||
|
||||
case .synchronizerStateChanged(.synced):
|
||||
if let shieldedBalance = sdkSynchronizer.latestScannedSynchronizerState?.shieldedBalance {
|
||||
if let shieldedBalance = sdkSynchronizer.latestScannedSynchronizerState()?.shieldedBalance {
|
||||
state.shieldedBalance = shieldedBalance.redacted
|
||||
state.transactionAmountInputState.maxValue = shieldedBalance.total.amount.redacted
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ struct WalletEventsFlowReducer: ReducerProtocol {
|
|||
switch action {
|
||||
case .onAppear:
|
||||
state.requiredTransactionConfirmations = zcashSDKEnvironment.requiredTransactionConfirmations
|
||||
return sdkSynchronizer.stateChanged
|
||||
return sdkSynchronizer.stateChangedStream()
|
||||
.map(WalletEventsFlowReducer.Action.synchronizerStateChanged)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: CancelId.self, cancelInFlight: true)
|
||||
|
@ -57,9 +57,7 @@ struct WalletEventsFlowReducer: ReducerProtocol {
|
|||
return .cancel(id: CancelId.self)
|
||||
|
||||
case .synchronizerStateChanged(.synced):
|
||||
if let latestMinedHeight = sdkSynchronizer.synchronizer?.latestScannedHeight {
|
||||
state.latestMinedHeight = latestMinedHeight
|
||||
}
|
||||
state.latestMinedHeight = sdkSynchronizer.latestScannedHeight()
|
||||
return sdkSynchronizer.getAllTransactions()
|
||||
.receive(on: mainQueue)
|
||||
.map(WalletEventsFlowReducer.Action.updateWalletEvents)
|
||||
|
|
|
@ -352,12 +352,6 @@ internal enum L10n {
|
|||
}
|
||||
}
|
||||
}
|
||||
internal enum Error {
|
||||
internal enum Rewind {
|
||||
/// SDKSynchronizer not initilized. rewindPublisher is nil
|
||||
internal static let sdkSynchronizerNotInitialized = L10n.tr("Localizable", "root.debug.error.rewind.sdkSynchronizerNotInitialized", fallback: "SDKSynchronizer not initilized. rewindPublisher is nil")
|
||||
}
|
||||
}
|
||||
internal enum Option {
|
||||
/// Export logs
|
||||
internal static let exportLogs = L10n.tr("Localizable", "root.debug.option.exportLogs", fallback: "Export logs")
|
||||
|
|
|
@ -251,7 +251,6 @@
|
|||
"root.debug.alert.rewind.failed.message" = "Error: %@";
|
||||
"root.debug.alert.rewind.cantStartSync.title" = "Can't start sync process after rewind";
|
||||
"root.debug.alert.rewind.cantStartSync.message" = "Error: %@";
|
||||
"root.debug.error.rewind.sdkSynchronizerNotInitialized" = "SDKSynchronizer not initilized. rewindPublisher is nil";
|
||||
|
||||
// MARK: - Export logs
|
||||
"exportLogs.alert.failed.title" = "Error when exporting logs";
|
||||
|
|
|
@ -16,6 +16,8 @@ class BalanceBreakdownTests: XCTestCase {
|
|||
initialState: .placeholder,
|
||||
reducer: BalanceBreakdownReducer()
|
||||
)
|
||||
|
||||
store.dependencies.sdkSynchronizer = .noOp
|
||||
|
||||
store.send(.onAppear)
|
||||
|
||||
|
@ -35,7 +37,7 @@ class BalanceBreakdownTests: XCTestCase {
|
|||
reducer: BalanceBreakdownReducer()
|
||||
)
|
||||
|
||||
store.dependencies.sdkSynchronizer = MockSDKSynchronizerClient()
|
||||
store.dependencies.sdkSynchronizer = .mock
|
||||
store.dependencies.derivationTool = .liveValue
|
||||
store.dependencies.mnemonic = .mock
|
||||
store.dependencies.walletStorage.exportWallet = { .placeholder }
|
||||
|
@ -64,7 +66,7 @@ class BalanceBreakdownTests: XCTestCase {
|
|||
reducer: BalanceBreakdownReducer()
|
||||
)
|
||||
|
||||
store.dependencies.sdkSynchronizer = NoopSDKSynchronizer()
|
||||
store.dependencies.sdkSynchronizer = .mocked(shieldFunds: { _, _, _ in throw SynchronizerError.criticalError })
|
||||
store.dependencies.derivationTool = .liveValue
|
||||
store.dependencies.mnemonic = .mock
|
||||
store.dependencies.walletStorage.exportWallet = { .placeholder }
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// Created by Lukáš Korba on 16.06.2022.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import XCTest
|
||||
@testable import secant_testnet
|
||||
import ComposableArchitecture
|
||||
|
@ -87,9 +88,9 @@ class DeeplinkTests: XCTestCase {
|
|||
store.dependencies.deeplink = DeeplinkClient(
|
||||
resolveDeeplinkURL: { _, _ in Deeplink.Destination.home }
|
||||
)
|
||||
let synchronizer = NoopSDKSynchronizer()
|
||||
synchronizer.updateStateChanged(.synced)
|
||||
store.dependencies.sdkSynchronizer = synchronizer
|
||||
store.dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(
|
||||
stateChangedStream: { CurrentValueSubject<SDKSynchronizerState, Never>(.synced) }
|
||||
)
|
||||
store.dependencies.walletConfigProvider = .noOp
|
||||
|
||||
guard let url = URL(string: "zcash:///home") else {
|
||||
|
@ -116,9 +117,6 @@ class DeeplinkTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testDeeplinkRequest_Received_Send() async throws {
|
||||
let synchronizer = NoopSDKSynchronizer()
|
||||
synchronizer.updateStateChanged(.synced)
|
||||
|
||||
var appState = RootReducer.State.placeholder
|
||||
appState.destinationState.destination = .welcome
|
||||
appState.appInitializationState = .initialized
|
||||
|
@ -130,7 +128,9 @@ class DeeplinkTests: XCTestCase {
|
|||
dependencies.deeplink = DeeplinkClient(
|
||||
resolveDeeplinkURL: { _, _ in Deeplink.Destination.send(amount: 123_000_000, address: "address", memo: "some text") }
|
||||
)
|
||||
dependencies.sdkSynchronizer = synchronizer
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(
|
||||
stateChangedStream: { CurrentValueSubject<SDKSynchronizerState, Never>(.synced) }
|
||||
)
|
||||
}
|
||||
|
||||
guard let url = URL(string: "zcash:///home/send?address=address&memo=some%20text&amount=123000000") else {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// Created by Lukáš Korba on 02.06.2022.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import XCTest
|
||||
import ComposableArchitecture
|
||||
@testable import secant_testnet
|
||||
|
@ -16,6 +17,11 @@ class HomeTests: XCTestCase {
|
|||
initialState: .placeholder,
|
||||
reducer: HomeReducer()
|
||||
)
|
||||
|
||||
store.dependencies.sdkSynchronizer = .mocked(
|
||||
stateChangedStream: { CurrentValueSubject<SDKSynchronizerState, Never>(.progressUpdated) },
|
||||
statusSnapshot: { .default }
|
||||
)
|
||||
|
||||
store.send(.synchronizerStateChanged(.progressUpdated))
|
||||
|
||||
|
@ -32,7 +38,7 @@ class HomeTests: XCTestCase {
|
|||
reducer: HomeReducer()
|
||||
) { dependencies in
|
||||
dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(.default)
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { .default })
|
||||
}
|
||||
|
||||
store.send(.synchronizerStateChanged(.synced))
|
||||
|
@ -71,7 +77,7 @@ class HomeTests: XCTestCase {
|
|||
)
|
||||
|
||||
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
||||
store.dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(mockSnapshot)
|
||||
store.dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { mockSnapshot })
|
||||
|
||||
store.send(.synchronizerStateChanged(.progressUpdated))
|
||||
|
||||
|
@ -112,7 +118,7 @@ class HomeTests: XCTestCase {
|
|||
)
|
||||
|
||||
store.dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
||||
store.dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(mockSnapshot)
|
||||
store.dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { mockSnapshot })
|
||||
|
||||
store.send(.updateDestination(.send)) {
|
||||
$0.destination = .send
|
||||
|
@ -135,9 +141,13 @@ class HomeTests: XCTestCase {
|
|||
let store = TestStore(
|
||||
initialState: .placeholder,
|
||||
reducer: HomeReducer()
|
||||
) {
|
||||
$0.diskSpaceChecker = .mockEmptyDisk
|
||||
}
|
||||
)
|
||||
|
||||
store.dependencies.diskSpaceChecker = .mockEmptyDisk
|
||||
store.dependencies.sdkSynchronizer = .mocked(
|
||||
stateChangedStream: { CurrentValueSubject<SDKSynchronizerState, Never>(.unknown) },
|
||||
statusSnapshot: { .default }
|
||||
)
|
||||
|
||||
store.send(.onAppear) { state in
|
||||
state.requiredTransactionConfirmations = 10
|
||||
|
@ -181,15 +191,11 @@ class HomeTests: XCTestCase {
|
|||
state: .error(testError)
|
||||
)
|
||||
|
||||
let mockSynchronizer = MockSDKSynchronizerClient(
|
||||
snapshot: errorSnapshot
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: .placeholder,
|
||||
reducer: HomeReducer()
|
||||
) {
|
||||
$0.sdkSynchronizer = mockSynchronizer
|
||||
$0.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { errorSnapshot })
|
||||
}
|
||||
|
||||
store.send(.updateSynchronizerStatus) {
|
||||
|
|
|
@ -20,7 +20,7 @@ class ProfileTests: XCTestCase {
|
|||
reducer: ProfileReducer()
|
||||
) { dependencies in
|
||||
dependencies.appVersion = .mock
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(.default)
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { .default })
|
||||
}
|
||||
|
||||
let uAddress = try UnifiedAddress(
|
||||
|
|
|
@ -194,6 +194,7 @@ class RecoveryPhraseValidationFlowFeatureFlagTests: XCTestCase {
|
|||
store.dependencies.walletStorage.exportWallet = { .placeholder }
|
||||
store.dependencies.walletStorage.areKeysPresent = { true }
|
||||
store.dependencies.walletConfigProvider = .noOp
|
||||
store.dependencies.sdkSynchronizer = .noOp
|
||||
|
||||
// Root of the test, the app finished the launch process and triggers the checks and initializations.
|
||||
await store.send(.initialization(.appDelegate(.didFinishLaunching)))
|
||||
|
|
|
@ -101,6 +101,7 @@ class AppInitializationTests: XCTestCase {
|
|||
store.dependencies.walletStorage.exportWallet = { .placeholder }
|
||||
store.dependencies.walletStorage.areKeysPresent = { true }
|
||||
store.dependencies.walletConfigProvider = .noOp
|
||||
store.dependencies.sdkSynchronizer = .noOp
|
||||
|
||||
// Root of the test, the app finished the launch process and triggers the checks and initializations.
|
||||
await store.send(.initialization(.appDelegate(.didFinishLaunching)))
|
||||
|
|
|
@ -72,6 +72,7 @@ class DebugTests: XCTestCase {
|
|||
)
|
||||
|
||||
store.dependencies.mainQueue = .immediate
|
||||
store.dependencies.sdkSynchronizer = .noOp
|
||||
|
||||
await store.send(.debug(.quickRescan)) { state in
|
||||
state.destinationState.internalDestination = .home
|
||||
|
@ -100,6 +101,7 @@ class DebugTests: XCTestCase {
|
|||
)
|
||||
|
||||
store.dependencies.mainQueue = .immediate
|
||||
store.dependencies.sdkSynchronizer = .noOp
|
||||
|
||||
await store.send(.debug(.fullRescan)) { state in
|
||||
state.destinationState.internalDestination = .home
|
||||
|
|
|
@ -50,7 +50,7 @@ class SendTests: XCTestCase {
|
|||
dependencies.derivationTool = .liveValue
|
||||
dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
||||
dependencies.mnemonic = .liveValue
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(.default)
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { .default })
|
||||
dependencies.walletStorage = .noOp
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,7 @@ class SendTests: XCTestCase {
|
|||
dependencies.derivationTool = .liveValue
|
||||
dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
||||
dependencies.mnemonic = .liveValue
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(.default)
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { .default })
|
||||
dependencies.walletStorage = .noOp
|
||||
}
|
||||
|
||||
|
@ -190,6 +190,7 @@ class SendTests: XCTestCase {
|
|||
dependencies.mainQueue = testScheduler.eraseToAnyScheduler()
|
||||
dependencies.mnemonic = .liveValue
|
||||
dependencies.walletStorage = .noOp
|
||||
dependencies.sdkSynchronizer = .noOp
|
||||
}
|
||||
|
||||
// simulate the sending confirmation button to be pressed
|
||||
|
@ -629,6 +630,8 @@ class SendTests: XCTestCase {
|
|||
reducer: SendFlowReducer()
|
||||
)
|
||||
|
||||
store.dependencies.sdkSynchronizer = .noOp
|
||||
|
||||
store.send(.onAppear) { state in
|
||||
state.memoState.charLimit = 512
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class BalanceBreakdownSnapshotTests: XCTestCase {
|
|||
transparentBalance: WalletBalance(verified: Zatoshi(850_000_000), total: Zatoshi(850_000_000)).redacted
|
||||
),
|
||||
reducer: BalanceBreakdownReducer()
|
||||
.dependency(\.sdkSynchronizer, .noOp)
|
||||
)
|
||||
|
||||
addAttachments(BalanceBreakdownView(store: store))
|
||||
|
|
|
@ -49,6 +49,7 @@ class HomeSnapshotTests: XCTestCase {
|
|||
),
|
||||
reducer: HomeReducer()
|
||||
.dependency(\.diskSpaceChecker, .mockEmptyDisk)
|
||||
.dependency(\.sdkSynchronizer, .noOp)
|
||||
)
|
||||
|
||||
// landing home screen
|
||||
|
|
|
@ -16,7 +16,7 @@ class ProfileSnapshotTests: XCTestCase {
|
|||
initialState: .placeholder,
|
||||
reducer: ProfileReducer()
|
||||
.dependency(\.appVersion, .mock)
|
||||
.dependency(\.sdkSynchronizer, NoopSDKSynchronizer())
|
||||
.dependency(\.sdkSynchronizer, .noOp)
|
||||
)
|
||||
|
||||
ViewStore(store).send(.onAppear)
|
||||
|
|
|
@ -36,6 +36,7 @@ class TransactionSendingTests: XCTestCase {
|
|||
.dependency(\.mainQueue, DispatchQueue.main.eraseToAnyScheduler())
|
||||
.dependency(\.numberFormatter, .live())
|
||||
.dependency(\.walletStorage, .live())
|
||||
.dependency(\.sdkSynchronizer, .mock)
|
||||
)
|
||||
|
||||
ViewStore(store).send(.onAppear)
|
||||
|
|
|
@ -16,7 +16,7 @@ class SettingsSnapshotTests: XCTestCase {
|
|||
initialState: .placeholder,
|
||||
reducer: SettingsReducer()
|
||||
.dependency(\.localAuthentication, .mockAuthenticationFailed)
|
||||
.dependency(\.sdkSynchronizer, NoopSDKSynchronizer())
|
||||
.dependency(\.sdkSynchronizer, .noOp)
|
||||
.dependency(\.walletStorage, .noOp)
|
||||
.dependency(\.appVersion, .mock)
|
||||
)
|
||||
|
@ -29,7 +29,7 @@ class SettingsSnapshotTests: XCTestCase {
|
|||
initialState: .placeholder,
|
||||
reducer: SettingsReducer()
|
||||
.dependency(\.localAuthentication, .mockAuthenticationFailed)
|
||||
.dependency(\.sdkSynchronizer, NoopSDKSynchronizer())
|
||||
.dependency(\.sdkSynchronizer, .noOp)
|
||||
.dependency(\.walletStorage, .noOp)
|
||||
.dependency(\.appVersion, .liveValue)
|
||||
)
|
||||
|
|
|
@ -15,6 +15,8 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
let store = WalletEventsFlowStore(
|
||||
initialState: .placeHolder,
|
||||
reducer: WalletEventsFlowReducer()
|
||||
.dependency(\.sdkSynchronizer, .mock)
|
||||
.dependency(\.mainQueue, .immediate)
|
||||
)
|
||||
|
||||
// landing wallet events screen
|
||||
|
|
|
@ -9,81 +9,9 @@ import XCTest
|
|||
import ZcashLightClientKit
|
||||
@testable import secant_testnet
|
||||
|
||||
extension DatabaseFiles.DatabaseFilesError {
|
||||
var debugValue: String {
|
||||
switch self {
|
||||
case .getFsBlockDbRoot: return "getFsBlockDbRoot"
|
||||
case .getDocumentsURL: return "getDocumentsURL"
|
||||
case .getCacheURL: return "getCacheURL"
|
||||
case .getDataURL: return "getDataURL"
|
||||
case .getOutputParamsURL: return "getOutputParamsURL"
|
||||
case .getPendingURL: return "getPendingURL"
|
||||
case .getSpendParamsURL: return "getSpendParamsURL"
|
||||
case .nukeFiles: return "nukeFiles"
|
||||
case .filesPresentCheck: return "filesPresentCheck"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseFilesTests: XCTestCase {
|
||||
let network = ZcashNetworkBuilder.network(for: .testnet)
|
||||
|
||||
func testFailingDocumentsDirectory() throws {
|
||||
let mockedFileManager = FileManagerClient(
|
||||
url: { _, _, _, _ in throw "some error" },
|
||||
fileExists: { _ in return true },
|
||||
removeItem: { _ in }
|
||||
)
|
||||
|
||||
let dfInteractor = DatabaseFilesClient.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager))
|
||||
|
||||
do {
|
||||
_ = try dfInteractor.documentsDirectory()
|
||||
|
||||
XCTFail("DatabaseFiles: `testFailingDocumentsDirectory` expected to fail but passed with no error.")
|
||||
} catch {
|
||||
guard let error = error as? DatabaseFiles.DatabaseFilesError else {
|
||||
XCTFail("DatabaseFiles: the error is expected to be DatabaseFilesError but it's \(error).")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(
|
||||
error.debugValue,
|
||||
DatabaseFiles.DatabaseFilesError.getDocumentsURL.debugValue,
|
||||
"DatabaseFiles: error must be .getDocumentsURL but it's \(error)."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testFailingDataDbURL() throws {
|
||||
let mockedFileManager = FileManagerClient(
|
||||
url: { _, _, _, _ in throw "some error" },
|
||||
fileExists: { _ in return true },
|
||||
removeItem: { _ in }
|
||||
)
|
||||
|
||||
let dfInteractor = DatabaseFilesClient.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager))
|
||||
|
||||
do {
|
||||
_ = try dfInteractor.dataDbURLFor(network)
|
||||
|
||||
XCTFail("DatabaseFiles: `testFailingDataDbURL` expected to fail but passed with no error.")
|
||||
} catch {
|
||||
guard let error = error as? DatabaseFiles.DatabaseFilesError else {
|
||||
XCTFail("DatabaseFiles: the error is expected to be DatabaseFilesError but it's \(error).")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(
|
||||
error.debugValue,
|
||||
DatabaseFiles.DatabaseFilesError.getDataURL.debugValue,
|
||||
"DatabaseFiles: error must be .getDataURL but it's \(error)."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testDatabaseFilesPresent() throws {
|
||||
let mockedFileManager = FileManagerClient(
|
||||
url: { _, _, _, _ in .emptyURL },
|
||||
|
@ -92,14 +20,8 @@ class DatabaseFilesTests: XCTestCase {
|
|||
)
|
||||
|
||||
let dfInteractor = DatabaseFilesClient.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager))
|
||||
|
||||
do {
|
||||
let areFilesPresent = try dfInteractor.areDbFilesPresentFor(network)
|
||||
|
||||
XCTAssertTrue(areFilesPresent, "DatabaseFiles: `testDatabaseFilesPresent` is expected to be true but it's \(areFilesPresent)")
|
||||
} catch {
|
||||
XCTFail("DatabaseFiles: `testDatabaseFilesPresent` expected to fail but passed with no error.")
|
||||
}
|
||||
let areFilesPresent = dfInteractor.areDbFilesPresentFor(network)
|
||||
XCTAssertTrue(areFilesPresent, "DatabaseFiles: `testDatabaseFilesPresent` is expected to be true but it's \(areFilesPresent)")
|
||||
}
|
||||
|
||||
func testDatabaseFilesNotPresent() throws {
|
||||
|
@ -110,97 +32,7 @@ class DatabaseFilesTests: XCTestCase {
|
|||
)
|
||||
|
||||
let dfInteractor = DatabaseFilesClient.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager))
|
||||
|
||||
do {
|
||||
let areFilesPresent = try dfInteractor.areDbFilesPresentFor(network)
|
||||
|
||||
XCTAssertFalse(areFilesPresent, "DatabaseFiles: `testDatabaseFilesNotPresent` is expected to be false but it's \(areFilesPresent)")
|
||||
} catch {
|
||||
XCTFail("DatabaseFiles: `testDatabaseFilesPresent` expected to fail but passed with no error.")
|
||||
}
|
||||
}
|
||||
|
||||
func testDatabaseFilesPresentFailure() throws {
|
||||
let mockedFileManager = FileManagerClient(
|
||||
url: { _, _, _, _ in throw "some error" },
|
||||
fileExists: { _ in return true },
|
||||
removeItem: { _ in }
|
||||
)
|
||||
|
||||
let dfInteractor = DatabaseFilesClient.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager))
|
||||
|
||||
do {
|
||||
_ = try dfInteractor.areDbFilesPresentFor(network)
|
||||
|
||||
XCTFail("DatabaseFiles: `testDatabaseFilesPresentFailure` expected to fail but passed with no error.")
|
||||
} catch {
|
||||
guard let error = error as? DatabaseFiles.DatabaseFilesError else {
|
||||
XCTFail("DatabaseFiles: the error is expected to be DatabaseFilesError but it's \(error).")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(
|
||||
error.debugValue,
|
||||
DatabaseFiles.DatabaseFilesError.filesPresentCheck.debugValue,
|
||||
"DatabaseFiles: error must be .filesPresentCheck but it's \(error)."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testNukeFiles_RemoveFileFailure() throws {
|
||||
let mockedFileManager = FileManagerClient(
|
||||
url: { _, _, _, _ in .emptyURL },
|
||||
fileExists: { _ in return true },
|
||||
removeItem: { _ in throw "some error" }
|
||||
)
|
||||
|
||||
let dfInteractor = DatabaseFilesClient.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager))
|
||||
|
||||
do {
|
||||
_ = try dfInteractor.nukeDbFilesFor(network)
|
||||
|
||||
XCTFail("DatabaseFiles: `testNukeFiles_RemoveFileFailure` expected to fail but passed with no error.")
|
||||
} catch {
|
||||
guard let error = error as? DatabaseFiles.DatabaseFilesError else {
|
||||
XCTFail("DatabaseFiles: the error is expected to be DatabaseFilesError but it's \(error).")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(
|
||||
error.debugValue,
|
||||
DatabaseFiles.DatabaseFilesError.nukeFiles.debugValue,
|
||||
"DatabaseFiles: error must be .nukeFiles but it's \(error)."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testNukeFiles_URLFailure() throws {
|
||||
let mockedFileManager = FileManagerClient(
|
||||
url: { _, _, _, _ in throw "some error" },
|
||||
fileExists: { _ in return true },
|
||||
removeItem: { _ in }
|
||||
)
|
||||
|
||||
let dfInteractor = DatabaseFilesClient.live(databaseFiles: DatabaseFiles(fileManager: mockedFileManager))
|
||||
|
||||
do {
|
||||
_ = try dfInteractor.nukeDbFilesFor(network)
|
||||
|
||||
XCTFail("DatabaseFiles: `testNukeFiles_URLFailure` expected to fail but passed with no error.")
|
||||
} catch {
|
||||
guard let error = error as? DatabaseFiles.DatabaseFilesError else {
|
||||
XCTFail("DatabaseFiles: the error is expected to be DatabaseFilesError but it's \(error).")
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertEqual(
|
||||
error.debugValue,
|
||||
DatabaseFiles.DatabaseFilesError.nukeFiles.debugValue,
|
||||
"DatabaseFiles: error must be .nukeFiles but it's \(error)."
|
||||
)
|
||||
}
|
||||
let areFilesPresent = dfInteractor.areDbFilesPresentFor(network)
|
||||
XCTAssertFalse(areFilesPresent, "DatabaseFiles: `testDatabaseFilesNotPresent` is expected to be false but it's \(areFilesPresent)")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ class WalletEventsTests: XCTestCase {
|
|||
),
|
||||
reducer: WalletEventsFlowReducer()
|
||||
)
|
||||
|
||||
store.dependencies.sdkSynchronizer = .noOp
|
||||
|
||||
store.send(.onAppear) { state in
|
||||
state.requiredTransactionConfirmations = 10
|
||||
|
@ -78,10 +80,12 @@ class WalletEventsTests: XCTestCase {
|
|||
reducer: WalletEventsFlowReducer()
|
||||
) { dependencies in
|
||||
dependencies.mainQueue = Self.testScheduler.eraseToAnyScheduler()
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mockWithSnapshot(.default)
|
||||
dependencies.sdkSynchronizer = SDKSynchronizerClient.mocked(statusSnapshot: { .default })
|
||||
}
|
||||
|
||||
store.send(.synchronizerStateChanged(.synced))
|
||||
store.send(.synchronizerStateChanged(.synced)) { state in
|
||||
state.latestMinedHeight = 0
|
||||
}
|
||||
|
||||
Self.testScheduler.advance(by: 0.01)
|
||||
|
||||
|
|
Loading…
Reference in New Issue