[#361] Introduce ZcashError (#1005)

This PR introduces `ZcashError` enum. This enum represents any error
that can be thrown inside of the SDK and outside of the SDK. Also
`ZcashError` is used in `LightWalletGRPCService` and handled in
`CompactBlockProcessor` as example.

Why enum? First I tried this with some structure which contained code,
message and underlyingError. Problem was when some specific place in the
SDK would like to attach some additional data to error. I didn't want to
add some generic dictionary to store anything with the error.

So I used enum to identify error. Each member can have specified amount
of associated values. So specific error can bring some context data. And
it's type safe.

Each error has also a code. Relationship between errors and codes is
1:1. It may looks bit redundant but it's important. The client app now
can choose how to process errors. Either identify those by the error
itself or by code.

Definition or errors and codes is in `ZcashErrorDefinition`. And then
`ZcashError` and `ZcashErrorCode` are generated by Sourcery. Thanks to
this it is easier to maintain the final code. And it gives us ability to
generate any kind of documentation for the errors and codes. I created
simple example of this in this PR. And it doesn't make the SDK
completely dependent on the Sourcery. Final structures aren't super
complicated and can be written manually if needed.

[#923] Update error handling in DatabaseStorageManager.swift

- cleanup of DatabaseStorageManager, not used anymore
- ZcashError for SimpleConnectionProvider

Revert "[#923] Update error handling in DatabaseStorageManager.swift"

This reverts commit 94e028d31a.

Fix script which generates ZcashError

[#922] Update error handling in DatabaseMigrationManager.swift

Closes #922

[#923] Update error handling in DatabaseStorageManager.swift

- The DatabaseStorageManager is not used so I deleted it
- SimpleConnectionProvider's errors updated

Fix tests

[#955] Update error handling in SaplingParameterDownloader

Closes #955

[#936] Update error handling in NotesDao

Closes #936

[#935] Update error handling in BlockDao

Closes #935

[#931] InternalSyncProgress.computeNextState doesn't need to throw

Closes #931

[#950] Update error handling in rust backend

Closes #949
Closes #950

[#941] Update error handling in AccountEntity

Closes #941

[#928] Update error handling in FSCompactBlockRepository

Closes #928

[#925] Update error handling in BlockDownloaderService (#974)

- BlockDownloaderService errors updated

[#924] Update error handling in BlockDownloader (#973)

- BlockDownloaderStream nextBlock updated

[#942] Update error handling in TransactionEntity (#977)

- ZcashTransaction init errors converted to the ZcashError

[#939] Update error handling in TransactionDao (#976)

- TransactionRepositoryError removed and replaced with ZcashError
- all TransactionDAO errors converted to ZcashError

[#943] Update error handling in ZcashCompactBlock

Closes #943

[#945] Update error handling in Memo

Closes #945

[#934] Update error handling in Checkpoint

Closes #944
Closes #934

[#938] Update error handling in PendingTransactionDao

Closes #938

[#926] Update error handling in BlockEnhancer

- WIP, switching to transaction repository to unblock this ticket

[#926] Update error handling in BlockEnhancer

- enhancer's errors done

[#926] Update error handling in BlockEnhancer (#994)

- cleanup

[#952] Update error handling in DerivationTool

Closes #952

[#932] Update error handling in BlockValidator

Closes #932

[#940] Update error handling in UnspentTransactionOutputDao

Closes #940

[#946] Update error handling in WalletTypes

Closes #946

[#954] Update error handling in WalletTransactionEncoder

Closes #954

[#953] Update error handling in PersistentTransactionManager

Closes #953

[#956] Update error handling in Initializer

Closes #956

[#947] Update error handling in Zatoshi (#996)

- Zatoshi's errors converted to ZcashError

[#927] Update error handling in UTXOFetcher (#997)

- UTXOFetcher resolved

Use let instead of var where applicable

In the SDK `var` was used on places where `let` would be sufficient. And
default strategy in Swift should use use `let` until mutability is
required. So all these places are changed. No logic is changed.

[#933] Update error handling in CompactBlockProcessor

- CompactBlockProcessor's errors refactored to ZcashError

[#933] Update error handling in CompactBlockProcessor #999

- comments addressed

[#951] Update error handling in SDKSynchronizer

- SDKSynchronizer's errors refactored to ZcashError

[#951] Update error handling in SDKSynchronizer (#1002)

- comments resolved

Make DerivationTool constructor public

DerivationTool.init should be public. This was removed by accident when
adopting async.

[#361] Add changelog
This commit is contained in:
Michal Fousek 2023-04-24 23:15:20 +02:00 committed by GitHub
parent ed87069c03
commit 724d410fad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
86 changed files with 3610 additions and 1772 deletions

View File

@ -16,6 +16,8 @@ excluded:
- build/
- docs/
- Tests/TestUtils/Sourcery/GeneratedMocks/AutoMockable.generated.swift
- Sources/ZcashLightClientKit/Error/ZcashError.swift
- Sources/ZcashLightClientKit/Error/ZcashErrorCode.swift
disabled_rules:
- notification_center_detachment

View File

@ -27,6 +27,14 @@ so that there is not a discontinuity in the cached block range that could cause
discontinuity error on libzcashlc when calling `scan_blocks`. This will have a setback of
at most 100 blocks that would have to be re-downloaded when resuming sync.
### [#361] Redesign errors inside the SDK
Now the SDK uses only one error type - `ZcashError`. Each method that throws now throws only `ZcashError`.
Each publisher (or stream) that can emit error now emitts only `ZcashError`.
Each symbol in `ZcashError` enum represents one error. Each error is used only in one place
inside the SDK. Each error has assigned unique error code (`ZcashErrorCode`) which can be used in logs.
### [#959] and [#914] Value of outbound transactions does not match user intended tx input
This change switches to a new (future) version of the rust crates that will get rid of the sent and received transactions Views in favor of a v_transaction view that will do better accounting of outgoing and incoming funds. Additionally it will support an outputs view for seeing the inner details of transactions enabling the SDKs tell the users the precise movement of value that a tx causes in its multiple possible ways according to the protocol.

View File

@ -18,10 +18,10 @@ enum DemoAppConfig {
let seed: [UInt8]
}
static var host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co"
static var port: Int = 9067
static var defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
static var defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
static let host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co"
static let port: Int = 9067
static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor
""")

View File

@ -37,7 +37,7 @@ URL to the `cacheDb` location to migrate the internal state of the
`CompactBlockProcessor` and delete that database.
````Swift
convenience public init (
convenience public init(
cacheDbURL: URL?,
fsBlockDbRoot: URL,
dataDbURL: URL,

View File

@ -28,7 +28,8 @@ let package = Package(
],
exclude: [
"Modules/Service/GRPC/ProtoBuf/proto/compact_formats.proto",
"Modules/Service/GRPC/ProtoBuf/proto/service.proto"
"Modules/Service/GRPC/ProtoBuf/proto/service.proto",
"Error/Sourcery/"
],
resources: [
.copy("Resources/checkpoints")

View File

@ -12,27 +12,6 @@ import Combine
public typealias RefreshedUTXOs = (inserted: [UnspentTransactionOutputEntity], skipped: [UnspentTransactionOutputEntity])
/**
Errors thrown by CompactBlock Processor
*/
public enum CompactBlockProcessorError: Error {
case invalidConfiguration
case missingDbPath(path: String)
case dataDbInitFailed(path: String)
case connectionError(underlyingError: Error)
case grpcError(statusCode: Int, message: String)
case connectionTimeout
case generalError(message: String)
case maxAttemptsReached(attempts: Int)
case unspecifiedError(underlyingError: Error)
case criticalError
case invalidAccount
case wrongConsensusBranchId(expectedLocally: ConsensusBranchID, found: ConsensusBranchID)
case networkMismatch(expected: NetworkType, found: NetworkType)
case saplingActivationMismatch(expected: BlockHeight, found: BlockHeight)
case unknown
}
public enum CompactBlockProgress {
case syncing(_ progress: BlockProgress)
case enhance(_ progress: EnhancementProgress)
@ -79,10 +58,10 @@ public enum CompactBlockProgress {
}
public struct EnhancementProgress: Equatable {
public var totalTransactions: Int
public var enhancedTransactions: Int
public var lastFoundTransaction: ZcashTransaction.Overview?
public var range: CompactBlockRange
public let totalTransactions: Int
public let enhancedTransactions: Int
public let lastFoundTransaction: ZcashTransaction.Overview?
public let range: CompactBlockRange
public init(totalTransactions: Int, enhancedTransactions: Int, lastFoundTransaction: ZcashTransaction.Overview?, range: CompactBlockRange) {
self.totalTransactions = totalTransactions
@ -115,7 +94,7 @@ actor CompactBlockProcessor {
enum Event {
/// Event sent when the CompactBlockProcessor presented an error.
case failed (CompactBlockProcessorError)
case failed (Error)
/// Event sent when the CompactBlockProcessor has finished syncing the blockchain to latest height
case finished (_ lastScannedHeight: BlockHeight, _ foundBlocks: Bool)
@ -156,27 +135,27 @@ actor CompactBlockProcessor {
struct Configuration {
let alias: ZcashSynchronizerAlias
let saplingParamsSourceURL: SaplingParamsSourceURL
public var fsBlockCacheRoot: URL
public var dataDb: URL
public var spendParamsURL: URL
public var outputParamsURL: URL
public var downloadBatchSize = ZcashSDK.DefaultDownloadBatch
public var scanningBatchSize = ZcashSDK.DefaultScanningBatch
public var retries = ZcashSDK.defaultRetries
public var maxBackoffInterval = ZcashSDK.defaultMaxBackOffInterval
public var maxReorgSize = ZcashSDK.maxReorgSize
public var rewindDistance = ZcashSDK.defaultRewindDistance
let fsBlockCacheRoot: URL
let dataDb: URL
let spendParamsURL: URL
let outputParamsURL: URL
let downloadBatchSize: Int
let scanningBatchSize: Int
let retries: Int
let maxBackoffInterval: TimeInterval
let maxReorgSize = ZcashSDK.maxReorgSize
let rewindDistance: Int
let walletBirthdayProvider: () -> BlockHeight
public var walletBirthday: BlockHeight { walletBirthdayProvider() }
public private(set) var downloadBufferSize: Int = 10
private(set) var network: ZcashNetwork
private(set) var saplingActivation: BlockHeight
private(set) var cacheDbURL: URL?
var walletBirthday: BlockHeight { walletBirthdayProvider() }
let downloadBufferSize: Int = 10
let network: ZcashNetwork
let saplingActivation: BlockHeight
let cacheDbURL: URL?
var blockPollInterval: TimeInterval {
TimeInterval.random(in: ZcashSDK.defaultPollInterval / 2 ... ZcashSDK.defaultPollInterval * 1.5)
}
init (
init(
alias: ZcashSynchronizerAlias,
cacheDbURL: URL? = nil,
fsBlockCacheRoot: URL,
@ -184,10 +163,11 @@ actor CompactBlockProcessor {
spendParamsURL: URL,
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
downloadBatchSize: Int,
retries: Int,
maxBackoffInterval: TimeInterval,
rewindDistance: Int,
downloadBatchSize: Int = ZcashSDK.DefaultDownloadBatch,
retries: Int = ZcashSDK.defaultRetries,
maxBackoffInterval: TimeInterval = ZcashSDK.defaultMaxBackOffInterval,
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
scanningBatchSize: Int = ZcashSDK.DefaultScanningBatch,
walletBirthdayProvider: @escaping () -> BlockHeight,
saplingActivation: BlockHeight,
network: ZcashNetwork
@ -203,6 +183,7 @@ actor CompactBlockProcessor {
self.retries = retries
self.maxBackoffInterval = maxBackoffInterval
self.rewindDistance = rewindDistance
self.scanningBatchSize = scanningBatchSize
self.walletBirthdayProvider = walletBirthdayProvider
self.saplingActivation = saplingActivation
self.cacheDbURL = cacheDbURL
@ -216,6 +197,11 @@ actor CompactBlockProcessor {
spendParamsURL: URL,
outputParamsURL: URL,
saplingParamsSourceURL: SaplingParamsSourceURL,
downloadBatchSize: Int = ZcashSDK.DefaultDownloadBatch,
retries: Int = ZcashSDK.defaultRetries,
maxBackoffInterval: TimeInterval = ZcashSDK.defaultMaxBackOffInterval,
rewindDistance: Int = ZcashSDK.defaultRewindDistance,
scanningBatchSize: Int = ZcashSDK.DefaultScanningBatch,
walletBirthdayProvider: @escaping () -> BlockHeight,
network: ZcashNetwork
) {
@ -229,6 +215,11 @@ actor CompactBlockProcessor {
self.saplingActivation = network.constants.saplingActivationHeight
self.network = network
self.cacheDbURL = nil
self.downloadBatchSize = downloadBatchSize
self.retries = retries
self.maxBackoffInterval = maxBackoffInterval
self.rewindDistance = rewindDistance
self.scanningBatchSize = scanningBatchSize
assert(downloadBatchSize >= scanningBatchSize)
}
@ -313,11 +304,11 @@ actor CompactBlockProcessor {
let saplingParametersHandler: SaplingParametersHandler
private let latestBlocksDataProvider: LatestBlocksDataProvider
var service: LightWalletService
var storage: CompactBlockRepository
var transactionRepository: TransactionRepository
var accountRepository: AccountRepository
var rustBackend: ZcashRustBackendWelding
let service: LightWalletService
let storage: CompactBlockRepository
let transactionRepository: TransactionRepository
let accountRepository: AccountRepository
let rustBackend: ZcashRustBackendWelding
private var retryAttempts: Int = 0
private var backoffTimer: Timer?
private var lastChainValidationFailure: BlockHeight?
@ -511,29 +502,26 @@ actor CompactBlockProcessor {
) async throws {
// check network types
guard let remoteNetworkType = NetworkType.forChainName(info.chainName) else {
throw CompactBlockProcessorError.generalError(
message: "Chain name does not match. Expected either 'test' or 'main' but received '\(info.chainName)'." +
"this is probably an API or programming error"
)
throw ZcashError.compactBlockProcessorChainName(info.chainName)
}
guard remoteNetworkType == localNetwork.networkType else {
throw CompactBlockProcessorError.networkMismatch(expected: localNetwork.networkType, found: remoteNetworkType)
throw ZcashError.compactBlockProcessorNetworkMismatch(localNetwork.networkType, remoteNetworkType)
}
guard saplingActivation == info.saplingActivationHeight else {
throw CompactBlockProcessorError.saplingActivationMismatch(expected: saplingActivation, found: BlockHeight(info.saplingActivationHeight))
throw ZcashError.compactBlockProcessorSaplingActivationMismatch(saplingActivation, BlockHeight(info.saplingActivationHeight))
}
// check branch id
let localBranch = try rustBackend.consensusBranchIdFor(height: Int32(info.blockHeight))
guard let remoteBranchID = ConsensusBranchID.fromString(info.consensusBranchID) else {
throw CompactBlockProcessorError.generalError(message: "Consensus BranchIDs don't match this is probably an API or programming error")
throw ZcashError.compactBlockProcessorConsensusBranchID
}
guard remoteBranchID == localBranch else {
throw CompactBlockProcessorError.wrongConsensusBranchId(expectedLocally: localBranch, found: remoteBranchID)
throw ZcashError.compactBlockProcessorWrongConsensusBranchId(localBranch, remoteBranchID)
}
}
@ -555,16 +543,16 @@ actor CompactBlockProcessor {
case .error(let error):
// max attempts have been reached
logger.info("max retry attempts reached with error: \(error)")
await notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts))
await notifyError(ZcashError.compactBlockProcessorMaxAttemptsReached(self.maxAttempts))
await updateState(.stopped)
case .stopped:
// max attempts have been reached
logger.info("max retry attempts reached")
await notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts))
await notifyError(ZcashError.compactBlockProcessorMaxAttemptsReached(self.maxAttempts))
case .synced:
// max attempts have been reached
logger.warn("max retry attempts reached on synced state, this indicates malfunction")
await notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.maxAttempts))
await notifyError(ZcashError.compactBlockProcessorMaxAttemptsReached(self.maxAttempts))
case .syncing, .enhancing, .fetching, .handlingSaplingFiles:
logger.debug("Warning: compact block processor was started while busy!!!!")
afterSyncHooksManager.insert(hook: .anotherSync)
@ -708,8 +696,6 @@ actor CompactBlockProcessor {
localNetwork: self.config.network,
rustBackend: self.rustBackend
)
} catch let error as LightWalletServiceError {
await self.severeFailure(error.mapToProcessorError())
} catch {
await self.severeFailure(error)
}
@ -799,8 +785,8 @@ actor CompactBlockProcessor {
await updateState(.stopped)
await handleAfterSyncHooks()
} else {
if case BlockValidatorError.validationFailed(let height) = error {
await validationFailed(at: height)
if case let ZcashError.rustValidateCombinedChainInvalidChain(height) = error {
await validationFailed(at: BlockHeight(height))
} else {
logger.error("processing failed with error: \(error)")
await fail(error)
@ -859,23 +845,8 @@ actor CompactBlockProcessor {
try await blockValidator.validate()
} catch {
await ifTaskIsNotCanceledClearCompactBlockCache()
guard let validationError = error as? BlockValidatorError else {
logger.error("Block validation failed with generic error: \(error)")
throw error
}
switch validationError {
case .validationFailed:
throw error
case .failedWithError(let genericError):
throw genericError
case .failedWithUnknownError:
logger.error("validation failed without a specific error")
throw CompactBlockProcessorError.generalError(message: "validation failed without a specific error")
}
logger.error("Block validation failed with error: \(error)")
throw error
}
do {
@ -983,23 +954,13 @@ actor CompactBlockProcessor {
await self.setTimer()
}
func mapError(_ error: Error) -> CompactBlockProcessorError {
if let processorError = error as? CompactBlockProcessorError {
return processorError
}
if let lwdError = error as? LightWalletServiceError {
return lwdError.mapToProcessorError()
}
return .unspecifiedError(underlyingError: error)
}
private func validateConfiguration() throws {
guard FileManager.default.isReadableFile(atPath: config.fsBlockCacheRoot.absoluteString) else {
throw CompactBlockProcessorError.missingDbPath(path: config.fsBlockCacheRoot.absoluteString)
throw ZcashError.compactBlockProcessorMissingDbPath(config.fsBlockCacheRoot.absoluteString)
}
guard FileManager.default.isReadableFile(atPath: config.dataDb.absoluteString) else {
throw CompactBlockProcessorError.missingDbPath(path: config.dataDb.absoluteString)
throw ZcashError.compactBlockProcessorMissingDbPath(config.dataDb.absoluteString)
}
}
@ -1124,7 +1085,7 @@ actor CompactBlockProcessor {
)
await self.start()
} else if await self.maxAttemptsReached {
await self.fail(CompactBlockProcessorError.maxAttemptsReached(attempts: self.config.retries))
await self.fail(ZcashError.compactBlockProcessorMaxAttemptsReached(self.config.retries))
}
}
}
@ -1161,35 +1122,11 @@ actor CompactBlockProcessor {
}
private func notifyError(_ err: Error) async {
await send(event: .failed(mapError(err)))
await send(event: .failed(err))
}
// TODO: [#713] encapsulate service errors better, https://github.com/zcash/ZcashLightClientKit/issues/713
}
extension LightWalletServiceError {
func mapToProcessorError() -> CompactBlockProcessorError {
switch self {
case let .failed(statusCode, message):
return CompactBlockProcessorError.grpcError(statusCode: statusCode, message: message)
case .invalidBlock:
return CompactBlockProcessorError.generalError(message: "\(self)")
case .generalError(let message):
return CompactBlockProcessorError.generalError(message: message)
case .sentFailed(let error):
return CompactBlockProcessorError.connectionError(underlyingError: error)
case .genericError(let error):
return CompactBlockProcessorError.unspecifiedError(underlyingError: error)
case .timeOut:
return CompactBlockProcessorError.connectionTimeout
case .criticalError:
return CompactBlockProcessorError.criticalError
case .userCancelled:
return CompactBlockProcessorError.connectionTimeout
case .unknown:
return CompactBlockProcessorError.unspecifiedError(underlyingError: self)
}
}
}
extension CompactBlockProcessor.State: Equatable {
public static func == (lhs: CompactBlockProcessor.State, rhs: CompactBlockProcessor.State) -> Bool {
switch (lhs, rhs) {
@ -1222,7 +1159,7 @@ extension CompactBlockProcessor {
func getTransparentBalance(accountIndex: Int) async throws -> WalletBalance {
guard accountIndex >= 0 else {
throw CompactBlockProcessorError.invalidAccount
throw ZcashError.compactBlockProcessorInvalidAccount
}
return WalletBalance(
@ -1252,7 +1189,7 @@ extension CompactBlockProcessor {
}
return await storeUTXOs(utxos, in: dataDb)
} catch {
throw mapError(error)
throw error
}
}
@ -1279,68 +1216,6 @@ extension CompactBlockProcessor {
}
}
extension CompactBlockProcessorError: LocalizedError {
/// A localized message describing what error occurred.
public var errorDescription: String? {
switch self {
case .dataDbInitFailed(let path):
return "Data Db file couldn't be initialized at path: \(path)"
case .connectionError(let underlyingError):
return "There's a problem with the Network Connection. Underlying error: \(underlyingError.localizedDescription)"
case .connectionTimeout:
return "Network connection timeout"
case .criticalError:
return "Critical Error"
case .generalError(let message):
return "Error Processing Blocks - \(message)"
case let .grpcError(statusCode, message):
return "Error on gRPC - Status Code: \(statusCode) - Message: \(message)"
case .invalidAccount:
return "Invalid Account"
case .invalidConfiguration:
return "CompactBlockProcessor was started with an Invalid Configuration"
case .maxAttemptsReached(let attempts):
return "Compact Block failed \(attempts) times and reached the maximum amount of retries it was set up to do"
case .missingDbPath(let path):
return "CompactBlockProcessor was set up with path \(path) but that location couldn't be reached"
case let .networkMismatch(expected, found):
return """
A server was reached, but it's targeting the wrong network Type. App Expected \(expected) but found \(found). Make sure you are pointing \
to the right server
"""
case let .saplingActivationMismatch(expected, found):
return """
A server was reached, it's showing a different sapling activation. App expected sapling activation height to be \(expected) but instead \
it found \(found). Are you sure you are pointing to the right server?
"""
case .unspecifiedError(let underlyingError):
return "Unspecified error caused by this underlying error: \(underlyingError)"
case let .wrongConsensusBranchId(expectedLocally, found):
return """
The remote server you are connecting to is publishing a different branch ID \(found) than the one your App is expecting to \
be (\(expectedLocally)). This could be caused by your App being out of date or the server you are connecting you being either on a \
different network or out of date after a network upgrade.
"""
case .unknown: return "Unknown error occured."
}
}
/// A localized message describing the reason for the failure.
public var failureReason: String? {
self.localizedDescription
}
/// A localized message describing how one might recover from the failure.
public var recoverySuggestion: String? {
self.localizedDescription
}
/// A localized message providing "help" text if the user requests help.
public var helpAnchor: String? {
self.localizedDescription
}
}
extension CompactBlockProcessor {
enum NextState: Equatable {
case finishProcessing(height: BlockHeight)
@ -1399,7 +1274,7 @@ extension CompactBlockProcessor {
await latestBlocksDataProvider.updateScannedData()
await latestBlocksDataProvider.updateBlockData()
return try await internalSyncProgress.computeNextState(
return await internalSyncProgress.computeNextState(
latestBlockHeight: latestBlocksDataProvider.latestBlockHeight,
latestScannedHeight: latestBlocksDataProvider.latestScannedHeight,
walletBirthday: config.walletBirthday
@ -1424,7 +1299,7 @@ extension CompactBlockProcessor {
/// Deletes the SQLite cacheDb and attempts to initialize the fsBlockDbRoot
/// - parameter legacyCacheDbURL: the URL where the cache Db used to be stored.
/// - Throws `InitializerError.fsCacheInitFailedSameURL` when the given URL
/// - Throws: `InitializerError.fsCacheInitFailedSameURL` when the given URL
/// is the same URL than the one provided as `self.fsBlockDbRoot` assuming that's a
/// programming error being the `legacyCacheDbURL` a sqlite database file and not a
/// directory. Also throws errors from initializing the fsBlockDbRoot.
@ -1432,7 +1307,7 @@ extension CompactBlockProcessor {
/// - Note: Errors from deleting the `legacyCacheDbURL` won't be throwns.
func migrateCacheDb(_ legacyCacheDbURL: URL) async throws {
guard legacyCacheDbURL != config.fsBlockCacheRoot else {
throw CacheDbMigrationError.fsCacheMigrationFailedSameURL
throw ZcashError.compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL
}
// Instance with alias `default` is same as instance before the Alias was introduced. So it makes sense that only this instance handles
@ -1453,26 +1328,18 @@ extension CompactBlockProcessor {
// if there's a readable file at the provided URL, delete it.
try FileManager.default.removeItem(at: legacyCacheDbURL)
} catch {
throw CacheDbMigrationError.failedToDeleteLegacyDb(error)
throw ZcashError.compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb(error)
}
// create the storage
do {
try await self.storage.create()
} catch {
throw CacheDbMigrationError.failedToInitFsBlockDb(error)
}
try await self.storage.create()
// The database has been deleted, so we have adjust the internal state of the
// `CompactBlockProcessor` so that it doesn't rely on download heights set
// by a previous processing cycle.
do {
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
} catch {
throw CacheDbMigrationError.failedToSetDownloadHeight(error)
}
let lastScannedHeight = try await transactionRepository.lastScannedHeight()
await internalSyncProgress.set(lastScannedHeight, .latestDownloadedBlockHeight)
}
func wipeLegacyCacheDbIfNeeded() {

View File

@ -18,8 +18,8 @@ class MigrationManager {
static let nextPendingDbMigration = PendingDbMigration.v2
var pendingDb: ConnectionProvider
var network: NetworkType
let pendingDb: ConnectionProvider
let network: NetworkType
let logger: Logger
init(
@ -38,9 +38,19 @@ class MigrationManager {
}
private extension MigrationManager {
/// - Throws:
/// - `dbMigrationGenericFailure` when can't read current version of the pending DB.
/// - `dbMigrationInvalidVersion` when unknown version is read from the current pending DB.
/// - `dbMigrationV1` when migration to version 1 fails.
/// - `dbMigrationV2` when migration to version 2 fails.
func migratePendingDb() throws {
// getUserVersion returns a default value of zero for an unmigrated database.
let currentPendingDbVersion = try pendingDb.connection().getUserVersion()
let currentPendingDbVersion: Int32
do {
currentPendingDbVersion = try pendingDb.connection().getUserVersion()
} catch {
throw ZcashError.dbMigrationGenericFailure(error)
}
logger.debug(
"Attempting to perform migration for pending Db - currentVersion: \(currentPendingDbVersion)." +
@ -58,7 +68,7 @@ private extension MigrationManager {
// unreachable due to the bound on the loop.
break
case nil:
throw DatabaseStorageError.migrationFailedWithMessage(message: "Invalid migration version: \(version).")
throw ZcashError.dbMigrationInvalidVersion
}
}
}
@ -83,69 +93,77 @@ private extension MigrationManager {
createdTable.column(PendingTransactionSQLDAO.TableColumns.fee)
}
try pendingDb.connection().transaction(.immediate) {
try pendingDb.connection().execute(statement)
try pendingDb.connection().setUserVersion(PendingDbMigration.v1.rawValue)
do {
try pendingDb.connection().transaction(.immediate) {
try pendingDb.connection().execute(statement)
try pendingDb.connection().setUserVersion(PendingDbMigration.v1.rawValue)
}
} catch {
throw ZcashError.dbMigrationV1(error)
}
}
func migratePendingDbV2() throws {
try pendingDb.connection().transaction(.immediate) {
let statement =
"""
ALTER TABLE pending_transactions RENAME TO pending_transactions_old;
do {
try pendingDb.connection().transaction(.immediate) {
let statement =
"""
ALTER TABLE pending_transactions RENAME TO pending_transactions_old;
CREATE TABLE pending_transactions(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
to_address TEXT,
to_internal INTEGER,
account_index INTEGER NOT NULL,
mined_height INTEGER,
expiry_height INTEGER,
cancelled INTEGER,
encode_attempts INTEGER DEFAULT (0),
error_message TEXT,
error_code INTEGER,
submit_attempts INTEGER DEFAULT (0),
create_time REAL,
txid BLOB,
value INTEGER NOT NULL,
raw BLOB,
memo BLOB,
fee INTEGER
);
CREATE TABLE pending_transactions(
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
to_address TEXT,
to_internal INTEGER,
account_index INTEGER NOT NULL,
mined_height INTEGER,
expiry_height INTEGER,
cancelled INTEGER,
encode_attempts INTEGER DEFAULT (0),
error_message TEXT,
error_code INTEGER,
submit_attempts INTEGER DEFAULT (0),
create_time REAL,
txid BLOB,
value INTEGER NOT NULL,
raw BLOB,
memo BLOB,
fee INTEGER
);
INSERT INTO pending_transactions
SELECT
id,
to_address,
NULL,
account_index,
mined_height,
expiry_height,
cancelled,
encode_attempts,
error_message,
error_code,
submit_attempts,
create_time,
txid,
value,
raw,
memo,
NULL
FROM pending_transactions_old;
INSERT INTO pending_transactions
SELECT
id,
to_address,
NULL,
account_index,
mined_height,
expiry_height,
cancelled,
encode_attempts,
error_message,
error_code,
submit_attempts,
create_time,
txid,
value,
raw,
memo,
NULL
FROM pending_transactions_old;
DROP TABLE pending_transactions_old
"""
DROP TABLE pending_transactions_old
"""
try pendingDb.connection().execute(statement)
try pendingDb.connection().setUserVersion(PendingDbMigration.v2.rawValue)
try pendingDb.connection().execute(statement)
try pendingDb.connection().setUserVersion(PendingDbMigration.v2.rawValue)
}
} catch {
throw ZcashError.dbMigrationV2(error)
}
}
}
extension Connection {
private extension Connection {
func getUserVersion() throws -> Int32 {
guard let version = try scalar("PRAGMA user_version") as? Int64 else {
return 0

View File

@ -16,8 +16,5 @@ enum DatabaseStorageError: Error {
case updateFailed
case malformedEntity(fields: [String]?)
case transactionFailed(underlyingError: Error)
case invalidMigrationVersion(version: Int32)
case latestBlockNotFound
case migrationFailed(underlyingError: Error)
case migrationFailedWithMessage(message: String)
}

View File

@ -1,75 +0,0 @@
//
// SQLDatabase.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 10/13/19.
// Copyright © 2019 Electric Coin Company. All rights reserved.
//
import Foundation
import SQLite
class DatabaseStorageManager {
private var readOnly: [URL: Connection] = [:]
private var readWrite: [URL: Connection] = [:]
init() {}
func connection(at url: URL, readOnly: Bool = false) throws -> Connection {
readOnly ? try readOnlyConnection(at: url) : try readWriteConnection(at: url)
}
private func readOnlyConnection(at url: URL) throws -> Connection {
if let readOnlyConnection = readOnly[url] {
return readOnlyConnection
}
let readOnlyConnection = try Connection.customConection(at: url)
readOnly[url] = readOnlyConnection
return readOnlyConnection
}
private func readWriteConnection(at url: URL) throws -> Connection {
if let readWriteConnection = readWrite[url] {
return readWriteConnection
}
let readWriteConnection = try Connection.customConection(at: url)
readWrite[url] = readWriteConnection
return readWriteConnection
}
}
private extension Connection {
static func customConection(at url: URL, readonly: Bool = false) throws -> Connection {
let conn = try Connection(url.absoluteString, readonly: readonly)
try conn.run("PRAGMA journal_mode = TRUNCATE;")
return conn
}
}
class SimpleConnectionProvider: ConnectionProvider {
var path: String
var readonly: Bool
var db: Connection?
init(path: String, readonly: Bool = false) {
self.path = path
self.readonly = readonly
}
func connection() throws -> Connection {
guard let conn = db else {
let conn = try Connection(path, readonly: readonly)
self.db = conn
return conn
}
return conn
}
func close() {
self.db = nil
}
}

View File

@ -0,0 +1,39 @@
//
// SimpleConnectionProvider.swift
// ZcashLightClientKit
//
// Created by Francisco Gindre on 10/13/19.
// Copyright © 2019 Electric Coin Company. All rights reserved.
//
import Foundation
import SQLite
class SimpleConnectionProvider: ConnectionProvider {
let path: String
let readonly: Bool
var db: Connection?
init(path: String, readonly: Bool = false) {
self.path = path
self.readonly = readonly
}
/// throws ZcashError.simpleConnectionProvider
func connection() throws -> Connection {
guard let conn = db else {
do {
let conn = try Connection(path, readonly: readonly)
self.db = conn
return conn
} catch {
throw ZcashError.simpleConnectionProvider(error)
}
}
return conn
}
func close() {
self.db = nil
}
}

View File

@ -61,7 +61,7 @@ data; although, by default the SDK uses gRPC and SQL.
- Property storage: responsible for persisting the compact blocks that are received
*/
class BlockDownloaderServiceImpl {
var lightwalletService: LightWalletService
let lightwalletService: LightWalletService
let storage: CompactBlockRepository
init(service: LightWalletService, storage: CompactBlockRepository) {
@ -99,7 +99,7 @@ extension BlockDownloaderServiceImpl: BlockDownloaderService {
}
try await self.storage.write(blocks: compactBlocks)
} catch {
throw error
throw ZcashError.blockDownloaderServiceDownloadBlockRange(error)
}
}

View File

@ -7,13 +7,6 @@
import Foundation
enum BlockEnhancerError: Error {
case noRawData(message: String)
case unknownError
case decryptError(error: Error)
case txIdNotFound(txId: Data)
}
protocol BlockEnhancer {
func enhance(at range: CompactBlockRange, didEnhance: (EnhancementProgress) async -> Void) async throws -> [ZcashTransaction.Overview]
}
@ -35,38 +28,16 @@ struct BlockEnhancerImpl {
let block = String(describing: transaction.minedHeight)
logger.debug("Decrypting and storing transaction id: \(transactionID) block: \(block)")
do {
try await rustBackend.decryptAndStoreTransaction(
txBytes: fetchedTransaction.raw.bytes,
minedHeight: Int32(fetchedTransaction.minedHeight)
)
} catch {
throw BlockEnhancerError.decryptError(error: error)
}
try await rustBackend.decryptAndStoreTransaction(
txBytes: fetchedTransaction.raw.bytes,
minedHeight: Int32(fetchedTransaction.minedHeight)
)
let confirmedTx: ZcashTransaction.Overview
do {
confirmedTx = try await transactionRepository.find(rawID: fetchedTransaction.rawID)
} catch {
if let err = error as? TransactionRepositoryError, case .notFound = err {
throw BlockEnhancerError.txIdNotFound(txId: fetchedTransaction.rawID)
} else {
throw error
}
}
return confirmedTx
return try await transactionRepository.find(rawID: fetchedTransaction.rawID)
}
}
extension BlockEnhancerImpl: BlockEnhancer {
enum EnhancementError: Error {
case noRawData(message: String)
case unknownError
case decryptError(error: Error)
case txIdNotFound(txId: Data)
}
func enhance(at range: CompactBlockRange, didEnhance: (EnhancementProgress) async -> Void) async throws -> [ZcashTransaction.Overview] {
try Task.checkCancellation()

View File

@ -48,8 +48,12 @@ extension UTXOFetcherImpl: UTXOFetcher {
startHeight: await config.walletBirthdayProvider()
)
for try await transaction in stream {
utxos.append(transaction)
do {
for try await transaction in stream {
utxos.append(transaction)
}
} catch {
throw ZcashError.unspentTransactionFetcherStream(error)
}
var refreshed: [UnspentTransactionOutputEntity] = []

View File

@ -49,14 +49,18 @@ class FSCompactBlockRepository {
extension FSCompactBlockRepository: CompactBlockRepository {
func create() async throws {
if !fileManager.fileExists(atPath: blocksDirectory.path) {
try fileManager.createDirectory(at: blocksDirectory, withIntermediateDirectories: true)
do {
try fileManager.createDirectory(at: blocksDirectory, withIntermediateDirectories: true)
} catch {
throw ZcashError.blockRepositoryCreateBlocksCacheDirectory(blocksDirectory)
}
}
do {
try await self.metadataStore.initFsBlockDbRoot()
} catch {
logger.error("Blocks metadata store init failed with error: \(error)")
throw CompactBlockRepositoryError.failedToInitializeCache
throw error
}
}
@ -74,7 +78,11 @@ extension FSCompactBlockRepository: CompactBlockRepository {
if self.blockExistsInCache(block) {
// remove if needed
try self.fileManager.removeItem(at: blockURL)
do {
try self.fileManager.removeItem(at: blockURL)
} catch {
throw ZcashError.blockRepositoryRemoveExistingBlock(error)
}
}
// store atomically
@ -82,7 +90,7 @@ extension FSCompactBlockRepository: CompactBlockRepository {
try self.fileWriter.writeToURL(block.data, blockURL)
} catch {
logger.error("Failed to write block: \(block.height) to path: \(blockURL.path) with error: \(error)")
throw CompactBlockRepositoryError.failedToWriteBlock(block)
throw ZcashError.blockRepositoryWriteBlock(block)
}
savedBlocks.append(block)
@ -116,13 +124,21 @@ extension FSCompactBlockRepository: CompactBlockRepository {
else { return }
for item in deleteList {
try self.fileManager.removeItem(at: item)
do {
try self.fileManager.removeItem(at: item)
} catch {
throw ZcashError.blockRepositoryRemoveBlockAfterRewind(item)
}
}
}
func clear() async throws {
if self.fileManager.fileExists(atPath: self.fsBlockDbRoot.path) {
try self.fileManager.removeItem(at: self.fsBlockDbRoot)
do {
try self.fileManager.removeItem(at: self.fsBlockDbRoot)
} catch {
throw ZcashError.blockRepositoryRemoveBlocksCacheDirectory(fsBlockDbRoot)
}
}
try await create()
}
@ -175,7 +191,7 @@ extension FSCompactBlockRepository {
// then return their URLs
let deleteList = try sortedCachedContents.filter({ url in
guard let filename = try url.resourceValues(forKeys: [.nameKey]).name else {
throw CompactBlockRepositoryError.malformedCacheEntry("failed to get url \(url) from cache")
throw ZcashError.blockRepositoryGetFilename(url)
}
return try filename.filterGreaterThan(toHeight, with: blockDescriptor)
@ -201,7 +217,7 @@ extension FSCompactBlockRepository {
// MARK: Associated and Helper types
struct FSBlockFileWriter {
var writeToURL: (Data, URL) throws -> Void
let writeToURL: (Data, URL) throws -> Void
}
extension FSBlockFileWriter {
@ -211,10 +227,10 @@ extension FSBlockFileWriter {
}
struct FSMetadataStore {
var saveBlocksMeta: ([ZcashCompactBlock]) async throws -> Void
var rewindToHeight: (BlockHeight) async throws -> Void
var initFsBlockDbRoot: () async throws -> Void
var latestHeight: () async -> BlockHeight
let saveBlocksMeta: ([ZcashCompactBlock]) async throws -> Void
let rewindToHeight: (BlockHeight) async throws -> Void
let initFsBlockDbRoot: () async throws -> Void
let latestHeight: () async -> BlockHeight
}
extension FSMetadataStore {
@ -227,11 +243,7 @@ extension FSMetadataStore {
logger: logger
)
} rewindToHeight: { height in
do {
try await rustBackend.rewindCacheToHeight(height: Int32(height))
} catch {
throw CompactBlockRepositoryError.failedToRewind(height)
}
try await rustBackend.rewindCacheToHeight(height: Int32(height))
} initFsBlockDbRoot: {
try await rustBackend.initBlockMetadataDb()
} latestHeight: {
@ -243,7 +255,7 @@ extension FSMetadataStore {
extension FSMetadataStore {
/// saves blocks to the FsBlockDb metadata database.
/// - Parameter blocks: Array of `ZcashCompactBlock` to save
/// - Throws `CompactBlockRepositoryError.failedToWriteMetadata` if the
/// - Throws: `CompactBlockRepositoryError.failedToWriteMetadata` if the
/// operation fails. the underlying error is logged through `LoggerProxy`
/// - Note: This shouldn't be called in parallel by many threads or workers. Won't do anything if `blocks` is empty
static func saveBlocksMeta(
@ -258,15 +270,15 @@ extension FSMetadataStore {
try await rustBackend.writeBlocksMetadata(blocks: blocks)
} catch {
logger.error("Failed to write metadata with error: \(error)")
throw CompactBlockRepositoryError.failedToWriteMetadata
throw error
}
}
}
struct ZcashCompactBlockDescriptor {
var height: (String) -> BlockHeight?
var describe: (ZcashCompactBlock) -> String
var compare: (String, String) -> Int?
let height: (String) -> BlockHeight?
let describe: (ZcashCompactBlock) -> String
let compare: (String, String) -> Int?
}
extension ZcashCompactBlockDescriptor {
@ -310,14 +322,18 @@ class SortedDirectoryContentProvider: SortedDirectoryListing {
/// sorting provided by `sorting` property of this provider.
/// - Parameter url: url to list the contents from. It must be a directory or the call will fail.
/// - Returns an array with the contained files or an empty one if the directory is empty
/// - Throws rethrows any errors from the underlying `FileManager`
/// - Throws: rethrows any errors from the underlying `FileManager`
func listContents(of url: URL) throws -> [URL] {
try fileManager.contentsOfDirectory(
at: url,
includingPropertiesForKeys: [.nameKey, .isDirectoryKey],
options: recursive ? [] : .skipsSubdirectoryDescendants
)
.sorted(by: sorting)
do {
return try fileManager.contentsOfDirectory(
at: url,
includingPropertiesForKeys: [.nameKey, .isDirectoryKey],
options: recursive ? [] : .skipsSubdirectoryDescendants
)
.sorted(by: sorting)
} catch {
throw ZcashError.blockRepositoryReadDirectoryContent(url)
}
}
}
@ -329,13 +345,17 @@ protocol SortedDirectoryListing {
extension URL {
/// asumes that URLs are from the same directory for efficiency reasons
static let areInIncreasingOrderByFilename: (URL, URL) throws -> Bool = { lhs, rhs in
guard
let lhsName = try lhs.resourceValues(forKeys: [.nameKey, .isDirectoryKey]).name,
let rhsName = try rhs.resourceValues(forKeys: [.nameKey, .isDirectoryKey]).name else {
throw URLError(URLError.badURL)
guard let lhsName = try lhs.resourceValues(forKeys: [.nameKey, .isDirectoryKey]).name else {
throw ZcashError.blockRepositoryGetFilenameAndIsDirectory(lhs)
}
guard let strcmp = FSCompactBlockRepository.filenameComparison(lhsName, rhsName) else { throw URLError(URLError.badURL) }
guard let rhsName = try rhs.resourceValues(forKeys: [.nameKey, .isDirectoryKey]).name else {
throw ZcashError.blockRepositoryGetFilenameAndIsDirectory(rhs)
}
guard let strcmp = FSCompactBlockRepository.filenameComparison(lhsName, rhsName) else {
throw ZcashError.blockRepositoryGetFilenameAndIsDirectory(lhs)
}
return strcmp < 0
}
@ -343,11 +363,15 @@ extension URL {
extension FileManager: SortedDirectoryListing {
func listContents(of url: URL) throws -> [URL] {
try contentsOfDirectory(
at: url,
includingPropertiesForKeys: [.nameKey, .isDirectoryKey],
options: .skipsSubdirectoryDescendants
)
do {
return try contentsOfDirectory(
at: url,
includingPropertiesForKeys: [.nameKey, .isDirectoryKey],
options: .skipsSubdirectoryDescendants
)
} catch {
throw ZcashError.blockRepositoryReadDirectoryContent(url)
}
}
}
@ -357,11 +381,11 @@ extension String {
/// - Parameter descriptor: The block descriptor that corresponds to the filename
/// convention of the FsBlockDb
/// - Returns if the height from this filename is greater that the one received by parameter
/// - Throws `CompactBlockRepositoryError.malformedCacheEntry` if this String
/// - Throws: `CompactBlockRepositoryError.malformedCacheEntry` if this String
/// can't be parsed by the given `ZcashCompactBlockDescriptor`
func filterGreaterThan(_ height: BlockHeight, with descriptor: ZcashCompactBlockDescriptor) throws -> Bool {
guard let blockHeight = descriptor.height(self) else {
throw CompactBlockRepositoryError.malformedCacheEntry("couldn't retrieve filename from file \(self)")
throw ZcashError.blockRepositoryParseHeightFromFilename(self)
}
return blockHeight > height

View File

@ -115,7 +115,7 @@ actor InternalSyncProgress {
latestBlockHeight: BlockHeight,
latestScannedHeight: BlockHeight,
walletBirthday: BlockHeight
) throws -> CompactBlockProcessor.NextState {
) -> CompactBlockProcessor.NextState {
logger.debug("""
Init numbers:
latestBlockHeight: \(latestBlockHeight)

View File

@ -8,12 +8,6 @@
import Foundation
enum BlockValidatorError: Error {
case validationFailed(height: BlockHeight)
case failedWithError(_ error: Error)
case failedWithUnknownError
}
protocol BlockValidator {
/// Validate all the downloaded blocks that haven't been yet validated.
func validate() async throws
@ -26,6 +20,10 @@ struct BlockValidatorImpl {
}
extension BlockValidatorImpl: BlockValidator {
/// - Throws:
/// - `rustValidateCombinedChainValidationFailed` if there was an error during validation unrelated to chain validity.
/// - `rustValidateCombinedChainInvalidChain(upperBound)` if the combined chain is invalid. `upperBound` is the height of the highest invalid
/// block(on the assumption that the highest block in the cache database is correct).
func validate() async throws {
try Task.checkCancellation()
@ -36,14 +34,7 @@ extension BlockValidatorImpl: BlockValidator {
logger.debug("validateChainFinished")
} catch {
pushProgressReport(startTime: startTime, finishTime: Date())
switch error {
case let RustWeldingError.invalidChain(upperBound):
throw BlockValidatorError.validationFailed(height: BlockHeight(upperBound))
default:
throw BlockValidatorError.failedWithError(error)
}
throw error
}
}

View File

@ -52,10 +52,16 @@ extension Checkpoint {
return try? checkpoint(at: url)
}
/// - Throws:
/// - `checkpointCantLoadFromDisk` if can't load JSON with checkpoint from disk.
private static func checkpoint(at url: URL) throws -> Checkpoint {
let data = try Data(contentsOf: url)
let checkpoint = try JSONDecoder().decode(Checkpoint.self, from: data)
return checkpoint
do {
let data = try Data(contentsOf: url)
let checkpoint = try JSONDecoder().decode(Checkpoint.self, from: data)
return checkpoint
} catch {
throw ZcashError.checkpointCantLoadFromDisk(error)
}
}
}
@ -66,7 +72,7 @@ struct BundleCheckpointURLProvider {
extension BundleCheckpointURLProvider {
/// Attempts to resolve the platform by checking `#if os(macOS)` build corresponds to a MacOS target
/// `#else` branch of that condition will assume iOS is the target platform
static var `default` = BundleCheckpointURLProvider { networkType in
static let `default` = BundleCheckpointURLProvider { networkType in
#if os(macOS)
Self.macOS.url(networkType)
#else
@ -74,7 +80,7 @@ extension BundleCheckpointURLProvider {
#endif
}
static var iOS = BundleCheckpointURLProvider(url: { networkType in
static let iOS = BundleCheckpointURLProvider(url: { networkType in
switch networkType {
case .mainnet:
return Checkpoint.mainnetCheckpointDirectory
@ -88,7 +94,7 @@ extension BundleCheckpointURLProvider {
/// if not found it will return `WalletBirthday.mainnetCheckpointDirectory` or
/// `WalletBirthday.testnetCheckpointDirectory`. This responds to tests
/// failing on MacOS target because the checkpoint resources would fail.
static var macOS = BundleCheckpointURLProvider(url: { networkType in
static let macOS = BundleCheckpointURLProvider(url: { networkType in
switch networkType {
case .mainnet:
return Bundle.module.url(

View File

@ -11,7 +11,8 @@ extension Checkpoint {
height: 419_200,
hash: "00000000025a57200d898ac7f21e26bf29028bbe96ec46e05b2c17cc9db9e4f3",
time: 1540779337,
saplingTree: "000000"
saplingTree: "000000",
orchardTree: nil
)
static let mainnetCheckpointDirectory = Bundle.module.bundleURL.appendingPathComponent("checkpoints/mainnet/")

View File

@ -12,7 +12,8 @@ extension Checkpoint {
height: 280000,
hash: "000420e7fcc3a49d729479fb0b560dd7b8617b178a08e9e389620a9d1dd6361a",
time: 1535262293,
saplingTree: "000000"
saplingTree: "000000",
orchardTree: nil
)
static let testnetCheckpointDirectory = Bundle.module.bundleURL.appendingPathComponent("checkpoints/testnet/")

View File

@ -62,13 +62,13 @@ public enum ZcashNetworkBuilder {
}
class ZcashTestnet: ZcashNetwork {
var networkType: NetworkType = .testnet
var constants: NetworkConstants.Type = ZcashSDKTestnetConstants.self
let networkType: NetworkType = .testnet
let constants: NetworkConstants.Type = ZcashSDKTestnetConstants.self
}
class ZcashMainnet: ZcashNetwork {
var networkType: NetworkType = .mainnet
var constants: NetworkConstants.Type = ZcashSDKMainnetConstants.self
let networkType: NetworkType = .mainnet
let constants: NetworkConstants.Type = ZcashSDKMainnetConstants.self
}
/**
@ -76,60 +76,60 @@ Constants of ZcashLightClientKit. this constants don't
*/
public enum ZcashSDK {
/// The number of zatoshi that equal 1 ZEC.
public static var zatoshiPerZEC: BlockHeight = 100_000_000
public static let zatoshiPerZEC: BlockHeight = 100_000_000
/// The theoretical maximum number of blocks in a reorg, due to other bottlenecks in the protocol design.
public static var maxReorgSize = 100
public static let maxReorgSize = 100
/// The amount of blocks ahead of the current height where new transactions are set to expire. This value is controlled
/// by the rust backend but it is helpful to know what it is set to and should be kept in sync.
public static var expiryOffset = 20
public static let expiryOffset = 20
// MARK: Defaults
/// Default size of batches of blocks to request from the compact block service. Which was used both for scanning and downloading.
/// consider basing your code assumptions on `DefaultDownloadBatch` and `DefaultScanningBatch` instead.
@available(*, deprecated, message: "this value is being deprecated in favor of `DefaultDownloadBatch` and `DefaultScanningBatch`")
public static var DefaultBatchSize = 100
public static let DefaultBatchSize = 100
/// Default batch size for downloading blocks for the compact block processor. Be careful with this number. This amount of blocks is held in
/// memory at some point of the sync process.
/// This values can't be smaller than `DefaultScanningBatch`. Otherwise bad things will happen.
public static var DefaultDownloadBatch = 100
public static let DefaultDownloadBatch = 100
/// Default batch size for scanning blocks for the compact block processor
public static var DefaultScanningBatch = 100
public static let DefaultScanningBatch = 100
/// Default amount of time, in in seconds, to poll for new blocks. Typically, this should be about half the average
/// block time.
public static var defaultPollInterval: TimeInterval = 20
public static let defaultPollInterval: TimeInterval = 20
/// Default attempts at retrying.
public static var defaultRetries: Int = 5
public static let defaultRetries: Int = 5
/// The default maximum amount of time to wait during retry backoff intervals. Failed loops will never wait longer than
/// this before retrying.
public static var defaultMaxBackOffInterval: TimeInterval = 600
public static let defaultMaxBackOffInterval: TimeInterval = 600
/// Default number of blocks to rewind when a chain reorg is detected. This should be large enough to recover from the
/// reorg but smaller than the theoretical max reorg size of 100.
public static var defaultRewindDistance: Int = 10
public static let defaultRewindDistance: Int = 10
/// The number of blocks to allow before considering our data to be stale. This usually helps with what to do when
/// returning from the background and is exposed via the Synchronizer's isStale function.
public static var defaultStaleTolerance: Int = 10
public static let defaultStaleTolerance: Int = 10
/// Default Name for LibRustZcash data.db
public static var defaultDataDbName = "data.db"
public static let defaultDataDbName = "data.db"
/// Default Name for Compact Block file system based db
public static var defaultFsCacheName = "fs_cache"
public static let defaultFsCacheName = "fs_cache"
/// Default Name for Compact Block caches db
public static var defaultCacheDbName = "caches.db"
public static let defaultCacheDbName = "caches.db"
/// Default name for pending transactions db
public static var defaultPendingDbName = "pending.db"
public static let defaultPendingDbName = "pending.db"
/// The Url that is used by default in zcashd.
/// We'll want to make this externally configurable, rather than baking it into the SDK but
@ -186,42 +186,42 @@ public extension NetworkConstants {
public enum ZcashSDKMainnetConstants: NetworkConstants {
/// The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks
/// prior to this height, at all.
public static var saplingActivationHeight: BlockHeight = 419_200
public static let saplingActivationHeight: BlockHeight = 419_200
/// Default Name for LibRustZcash data.db
public static var defaultDataDbName = "data.db"
public static let defaultDataDbName = "data.db"
public static var defaultFsBlockDbRootName = "fs_cache"
public static let defaultFsBlockDbRootName = "fs_cache"
/// Default Name for Compact Block caches db
public static var defaultCacheDbName = "caches.db"
public static let defaultCacheDbName = "caches.db"
/// Default name for pending transactions db
public static var defaultPendingDbName = "pending.db"
public static let defaultPendingDbName = "pending.db"
public static var defaultDbNamePrefix = "ZcashSdk_mainnet_"
public static let defaultDbNamePrefix = "ZcashSdk_mainnet_"
public static var feeChangeHeight: BlockHeight = 1_077_550
public static let feeChangeHeight: BlockHeight = 1_077_550
}
public enum ZcashSDKTestnetConstants: NetworkConstants {
/// The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks
/// prior to this height, at all.
public static var saplingActivationHeight: BlockHeight = 280_000
public static let saplingActivationHeight: BlockHeight = 280_000
/// Default Name for LibRustZcash data.db
public static var defaultDataDbName = "data.db"
public static let defaultDataDbName = "data.db"
/// Default Name for Compact Block caches db
public static var defaultCacheDbName = "caches.db"
public static let defaultCacheDbName = "caches.db"
public static var defaultFsBlockDbRootName = "fs_cache"
public static let defaultFsBlockDbRootName = "fs_cache"
/// Default name for pending transactions db
public static var defaultPendingDbName = "pending.db"
public static let defaultPendingDbName = "pending.db"
public static var defaultDbNamePrefix = "ZcashSdk_testnet_"
public static let defaultDbNamePrefix = "ZcashSdk_testnet_"
/// Estimated height where wallets are supposed to change the fee
public static var feeChangeHeight: BlockHeight = 1_028_500
public static let feeChangeHeight: BlockHeight = 1_028_500
}

View File

@ -24,48 +24,84 @@ struct Block: Codable {
}
enum TableStructure {
static var height = Expression<Int>(Block.CodingKeys.height.rawValue)
static var hash = Expression<Blob>(Block.CodingKeys.hash.rawValue)
static var time = Expression<Int>(Block.CodingKeys.time.rawValue)
static var saplingTree = Expression<Blob>(Block.CodingKeys.saplingTree.rawValue)
static let height = Expression<Int>(Block.CodingKeys.height.rawValue)
static let hash = Expression<Blob>(Block.CodingKeys.hash.rawValue)
static let time = Expression<Int>(Block.CodingKeys.time.rawValue)
static let saplingTree = Expression<Blob>(Block.CodingKeys.saplingTree.rawValue)
}
var height: BlockHeight
var hash: Data
var time: Int
var saplingTree: Data
let height: BlockHeight
let hash: Data
let time: Int
let saplingTree: Data
static var table = Table("blocks")
static let table = Table("blocks")
}
class BlockSQLDAO: BlockDao {
var dbProvider: ConnectionProvider
var table: Table
var height = Expression<Int>("height")
let dbProvider: ConnectionProvider
let table: Table
let height = Expression<Int>("height")
init(dbProvider: ConnectionProvider) {
self.dbProvider = dbProvider
self.table = Table("Blocks")
}
/// - Throws:
/// - `blockDAOCantDecode` if block data loaded from DB can't be decoded to `Block` object.
/// - `blockDAOBlock` if sqlite query to load block metadata failed.
func block(at height: BlockHeight) throws -> Block? {
try dbProvider
.connection()
.prepare(Block.table.filter(Block.TableStructure.height == height).limit(1))
.map({ try $0.decode() })
.first
do {
return try dbProvider
.connection()
.prepare(Block.table.filter(Block.TableStructure.height == height).limit(1))
.map {
do {
return try $0.decode()
} catch {
throw ZcashError.blockDAOCantDecode(error)
}
}
.first
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.blockDAOBlock(error)
}
}
}
/// - Throws: `blockDAOLatestBlockHeight` if sqlite to fetch height fails.
func latestBlockHeight() throws -> BlockHeight {
try dbProvider.connection().scalar(table.select(height.max)) ?? BlockHeight.empty()
do {
return try dbProvider.connection().scalar(table.select(height.max)) ?? BlockHeight.empty()
} catch {
throw ZcashError.blockDAOLatestBlockHeight(error)
}
}
func latestBlock() throws -> Block? {
try dbProvider
.connection()
.prepare(Block.table.order(height.desc).limit(1))
.map({ try $0.decode() })
.first
do {
return try dbProvider
.connection()
.prepare(Block.table.order(height.desc).limit(1))
.map {
do {
return try $0.decode()
} catch {
throw ZcashError.blockDAOLatestBlockCantDecode(error)
}
}
.first
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.blockDAOLatestBlock(error)
}
}
}
}

View File

@ -23,33 +23,41 @@ struct ReceivedNote: ReceivedNoteEntity, Codable {
case spent
case tx
}
var id: Int
var diversifier: Data
var rcm: Data
var nf: Data
var isChange: Bool
var transactionId: Int
var outputIndex: Int
var account: Int
var value: Int
var memo: Data?
var spent: Int?
var tx: Int
let id: Int
let diversifier: Data
let rcm: Data
let nf: Data
let isChange: Bool
let transactionId: Int
let outputIndex: Int
let account: Int
let value: Int
let memo: Data?
let spent: Int?
let tx: Int
}
class ReceivedNotesSQLDAO: ReceivedNoteRepository {
let table = Table("received_notes")
var dbProvider: ConnectionProvider
let dbProvider: ConnectionProvider
init(dbProvider: ConnectionProvider) {
self.dbProvider = dbProvider
}
/// Throws `notesDAOReceivedCount` if sqlite query fetching count fails.
func count() throws -> Int {
try dbProvider.connection().scalar(table.count)
do {
return try dbProvider.connection().scalar(table.count)
} catch {
throw ZcashError.notesDAOReceivedCount(error)
}
}
/// - Throws:
/// - `notesDAOReceivedCantDecode` if fetched note data from the db can't be decoded to the `ReceivedNote` object.
/// - `notesDAOReceivedNote` if sqlite query fetching note data fails.
func receivedNote(byRawTransactionId: Data) throws -> ReceivedNoteEntity? {
let transactions = Table("transactions")
let idTx = Expression<Int>("id_tx")
@ -63,13 +71,25 @@ class ReceivedNotesSQLDAO: ReceivedNoteRepository {
)
.where(transactions[txid] == Blob(bytes: byRawTransactionId.bytes))
.limit(1)
return try dbProvider.connection()
.prepare(joinStatement)
.map { row -> ReceivedNote in
try row.decode()
do {
return try dbProvider.connection()
.prepare(joinStatement)
.map { row -> ReceivedNote in
do {
return try row.decode()
} catch {
throw ZcashError.notesDAOReceivedCantDecode(error)
}
}
.first
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.notesDAOReceivedNote(error)
}
.first
}
}
}
@ -86,30 +106,38 @@ struct SentNote: SentNoteEntity, Codable {
case memo
}
var id: Int
var transactionId: Int
var outputPool: Int
var outputIndex: Int
var fromAccount: Int
var toAddress: String?
var toAccount: Int?
var value: Int
var memo: Data?
let id: Int
let transactionId: Int
let outputPool: Int
let outputIndex: Int
let fromAccount: Int
let toAddress: String?
let toAccount: Int?
let value: Int
let memo: Data?
}
class SentNotesSQLDAO: SentNotesRepository {
let table = Table("sent_notes")
var dbProvider: ConnectionProvider
let dbProvider: ConnectionProvider
init(dbProvider: ConnectionProvider) {
self.dbProvider = dbProvider
}
/// - Throws: `notesDAOSentCount` if sqlite query fetching count fails.
func count() throws -> Int {
try dbProvider.connection().scalar(table.count)
do {
return try dbProvider.connection().scalar(table.count)
} catch {
throw ZcashError.notesDAOSentCount(error)
}
}
/// - Throws:
/// - `notesDAOSentCantDecode` if fetched note data from the db can't be decoded to the `SentNote` object.
/// - `notesDAOSentNote` if sqlite query fetching note data fails.
func sentNote(byRawTransactionId: Data) throws -> SentNoteEntity? {
let transactions = Table("transactions")
let idTx = Expression<Int>("id_tx")
@ -124,12 +152,24 @@ class SentNotesSQLDAO: SentNotesRepository {
.where(transactions[txid] == Blob(bytes: byRawTransactionId.bytes))
.limit(1)
return try dbProvider.connection()
.prepare(joinStatement)
.map { row -> SentNote in
try row.decode()
do {
return try dbProvider.connection()
.prepare(joinStatement)
.map { row -> SentNote in
do {
return try row.decode()
} catch {
throw ZcashError.notesDAOSentCantDecode(error)
}
}
.first
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.notesDAOSentNote(error)
}
.first
}
}
func getRecipients(for id: Int) -> [TransactionRecipient] {

View File

@ -8,9 +8,9 @@
import Foundation
class PagedTransactionDAO: PaginatedTransactionRepository {
var pageSize: Int
var transactionRepository: TransactionRepository
var kind: TransactionKind
let pageSize: Int
let transactionRepository: TransactionRepository
let kind: TransactionKind
var pageCount: Int {
get async {

View File

@ -29,8 +29,8 @@ struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
case fee
}
var recipient: PendingTransactionRecipient
var accountIndex: Int
let recipient: PendingTransactionRecipient
let accountIndex: Int
var minedHeight: BlockHeight
var expiryHeight: BlockHeight
var cancelled: Int
@ -38,13 +38,13 @@ struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
var submitAttempts: Int
var errorMessage: String?
var errorCode: Int?
var createTime: TimeInterval
let createTime: TimeInterval
var raw: Data?
var id: Int?
var value: Zatoshi
var memo: Data?
let value: Zatoshi
let memo: Data?
var rawTransactionId: Data?
var fee: Zatoshi?
let fee: Zatoshi?
static func from(entity: PendingTransactionEntity) -> PendingTransaction {
PendingTransaction(
@ -102,46 +102,61 @@ struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
self.rawTransactionId = rawTransactionId
self.fee = fee
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let toAddress: String? = try container.decodeIfPresent(String.self, forKey: .toAddress)
let toInternalAccount: Int? = try container.decodeIfPresent(Int.self, forKey: .toInternalAccount)
switch (toAddress, toInternalAccount) {
case let (.some(address), nil):
guard let recipient = Recipient.forEncodedAddress(encoded: address) else {
throw DatabaseStorageError.malformedEntity(fields: ["toAddress"])
/// - Throws:
/// - `pendingTransactionDecodeInvalidData` if some of the fields contain invalid data.
/// - `pendingTransactionCantDecode` if decoding fails.
init(from decoder: Decoder) throws {
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
let toAddress: String? = try container.decodeIfPresent(String.self, forKey: .toAddress)
let toInternalAccount: Int? = try container.decodeIfPresent(Int.self, forKey: .toInternalAccount)
switch (toAddress, toInternalAccount) {
case let (.some(address), nil):
guard let recipient = Recipient.forEncodedAddress(encoded: address) else {
throw ZcashError.pendingTransactionDecodeInvalidData(["toAddress"])
}
self.recipient = .address(recipient.0)
case let (nil, .some(accountId)):
self.recipient = .internalAccount(UInt32(accountId))
default:
throw ZcashError.pendingTransactionDecodeInvalidData(["toAddress", "toInternalAccount"])
}
self.accountIndex = try container.decode(Int.self, forKey: .accountIndex)
self.minedHeight = try container.decode(BlockHeight.self, forKey: .minedHeight)
self.expiryHeight = try container.decode(BlockHeight.self, forKey: .expiryHeight)
self.cancelled = try container.decode(Int.self, forKey: .cancelled)
self.encodeAttempts = try container.decode(Int.self, forKey: .encodeAttempts)
self.submitAttempts = try container.decode(Int.self, forKey: .submitAttempts)
self.errorMessage = try container.decodeIfPresent(String.self, forKey: .errorMessage)
self.errorCode = try container.decodeIfPresent(Int.self, forKey: .errorCode)
self.createTime = try container.decode(TimeInterval.self, forKey: .createTime)
self.raw = try container.decodeIfPresent(Data.self, forKey: .raw)
self.id = try container.decodeIfPresent(Int.self, forKey: .id)
let zatoshiValue = try container.decode(Int64.self, forKey: .value)
self.value = Zatoshi(zatoshiValue)
self.memo = try container.decodeIfPresent(Data.self, forKey: .memo)
self.rawTransactionId = try container.decodeIfPresent(Data.self, forKey: .rawTransactionId)
if let feeValue = try container.decodeIfPresent(Int64.self, forKey: .fee) {
self.fee = Zatoshi(feeValue)
} else {
self.fee = nil
}
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.pendingTransactionCantDecode(error)
}
self.recipient = .address(recipient.0)
case let (nil, .some(accountId)):
self.recipient = .internalAccount(UInt32(accountId))
default:
throw DatabaseStorageError.malformedEntity(fields: ["toAddress", "toInternalAccount"])
}
self.accountIndex = try container.decode(Int.self, forKey: .accountIndex)
self.minedHeight = try container.decode(BlockHeight.self, forKey: .minedHeight)
self.expiryHeight = try container.decode(BlockHeight.self, forKey: .expiryHeight)
self.cancelled = try container.decode(Int.self, forKey: .cancelled)
self.encodeAttempts = try container.decode(Int.self, forKey: .encodeAttempts)
self.submitAttempts = try container.decode(Int.self, forKey: .submitAttempts)
self.errorMessage = try container.decodeIfPresent(String.self, forKey: .errorMessage)
self.errorCode = try container.decodeIfPresent(Int.self, forKey: .errorCode)
self.createTime = try container.decode(TimeInterval.self, forKey: .createTime)
self.raw = try container.decodeIfPresent(Data.self, forKey: .raw)
self.id = try container.decodeIfPresent(Int.self, forKey: .id)
let zatoshiValue = try container.decode(Int64.self, forKey: .value)
self.value = Zatoshi(zatoshiValue)
self.memo = try container.decodeIfPresent(Data.self, forKey: .memo)
self.rawTransactionId = try container.decodeIfPresent(Data.self, forKey: .rawTransactionId)
if let feeValue = try container.decodeIfPresent(Int64.self, forKey: .fee) {
self.fee = Zatoshi(feeValue)
}
}
/// - Throws:
/// - `pendingTransactionCantEncode` if encoding fails.
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
@ -154,23 +169,27 @@ struct PendingTransaction: PendingTransactionEntity, Decodable, Encodable {
accountId = Int(acct)
}
try container.encodeIfPresent(toAddress, forKey: .toAddress)
try container.encodeIfPresent(accountId, forKey: .toInternalAccount)
try container.encode(self.accountIndex, forKey: .accountIndex)
try container.encode(self.minedHeight, forKey: .minedHeight)
try container.encode(self.expiryHeight, forKey: .expiryHeight)
try container.encode(self.cancelled, forKey: .cancelled)
try container.encode(self.encodeAttempts, forKey: .encodeAttempts)
try container.encode(self.submitAttempts, forKey: .submitAttempts)
try container.encodeIfPresent(self.errorMessage, forKey: .errorMessage)
try container.encodeIfPresent(self.errorCode, forKey: .errorCode)
try container.encode(self.createTime, forKey: .createTime)
try container.encodeIfPresent(self.raw, forKey: .raw)
try container.encodeIfPresent(self.id, forKey: .id)
try container.encode(self.value.amount, forKey: .value)
try container.encodeIfPresent(self.memo, forKey: .memo)
try container.encodeIfPresent(self.rawTransactionId, forKey: .rawTransactionId)
try container.encodeIfPresent(self.fee?.amount, forKey: .fee)
do {
try container.encodeIfPresent(toAddress, forKey: .toAddress)
try container.encodeIfPresent(accountId, forKey: .toInternalAccount)
try container.encode(self.accountIndex, forKey: .accountIndex)
try container.encode(self.minedHeight, forKey: .minedHeight)
try container.encode(self.expiryHeight, forKey: .expiryHeight)
try container.encode(self.cancelled, forKey: .cancelled)
try container.encode(self.encodeAttempts, forKey: .encodeAttempts)
try container.encode(self.submitAttempts, forKey: .submitAttempts)
try container.encodeIfPresent(self.errorMessage, forKey: .errorMessage)
try container.encodeIfPresent(self.errorCode, forKey: .errorCode)
try container.encode(self.createTime, forKey: .createTime)
try container.encodeIfPresent(self.raw, forKey: .raw)
try container.encodeIfPresent(self.id, forKey: .id)
try container.encode(self.value.amount, forKey: .value)
try container.encodeIfPresent(self.memo, forKey: .memo)
try container.encodeIfPresent(self.rawTransactionId, forKey: .rawTransactionId)
try container.encodeIfPresent(self.fee?.amount, forKey: .fee)
} catch {
throw ZcashError.pendingTransactionCantEncode(error)
}
}
func isSameTransactionId<T>(other: T) -> Bool where T: RawIdentifiable {
@ -209,28 +228,28 @@ extension PendingTransaction {
class PendingTransactionSQLDAO: PendingTransactionRepository {
enum TableColumns {
static var toAddress = Expression<String?>("to_address")
static var toInternalAccount = Expression<Int?>("to_internal")
static var accountIndex = Expression<Int>("account_index")
static var minedHeight = Expression<Int?>("mined_height")
static var expiryHeight = Expression<Int?>("expiry_height")
static var cancelled = Expression<Int?>("cancelled")
static var encodeAttempts = Expression<Int?>("encode_attempts")
static var errorMessage = Expression<String?>("error_message")
static var submitAttempts = Expression<Int?>("submit_attempts")
static var errorCode = Expression<Int?>("error_code")
static var createTime = Expression<TimeInterval?>("create_time")
static var raw = Expression<Blob?>("raw")
static var id = Expression<Int>("id")
static var value = Expression<Zatoshi>("value")
static var memo = Expression<Blob?>("memo")
static var rawTransactionId = Expression<Blob?>("txid")
static var fee = Expression<Zatoshi?>("fee")
static let toAddress = Expression<String?>("to_address")
static let toInternalAccount = Expression<Int?>("to_internal")
static let accountIndex = Expression<Int>("account_index")
static let minedHeight = Expression<Int?>("mined_height")
static let expiryHeight = Expression<Int?>("expiry_height")
static let cancelled = Expression<Int?>("cancelled")
static let encodeAttempts = Expression<Int?>("encode_attempts")
static let errorMessage = Expression<String?>("error_message")
static let submitAttempts = Expression<Int?>("submit_attempts")
static let errorCode = Expression<Int?>("error_code")
static let createTime = Expression<TimeInterval?>("create_time")
static let raw = Expression<Blob?>("raw")
static let id = Expression<Int>("id")
static let value = Expression<Zatoshi>("value")
static let memo = Expression<Blob?>("memo")
static let rawTransactionId = Expression<Blob?>("txid")
static let fee = Expression<Zatoshi?>("fee")
}
static let table = Table("pending_transactions")
var dbProvider: ConnectionProvider
let dbProvider: ConnectionProvider
let logger: Logger
init(dbProvider: ConnectionProvider, logger: Logger) {
@ -241,77 +260,119 @@ class PendingTransactionSQLDAO: PendingTransactionRepository {
func closeDBConnection() {
dbProvider.close()
}
/// - Throws:
/// - `pendingTransactionDAOCreate` if sqlite fails.
func create(_ transaction: PendingTransactionEntity) throws -> Int {
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
return try Int(dbProvider.connection().run(Self.table.insert(pendingTx)))
do {
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
return try Int(dbProvider.connection().run(Self.table.insert(pendingTx)))
} catch {
throw ZcashError.pendingTransactionDAOCreate(error)
}
}
/// - Throws:
/// - `pendingTransactionDAOUpdateMissingID` if `transaction` (or entity created from `transaction`) is missing id.
/// - `pendingTransactionDAOUpdate` if sqlite fails
func update(_ transaction: PendingTransactionEntity) throws {
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
guard let id = pendingTx.id else {
throw DatabaseStorageError.malformedEntity(fields: ["id"])
throw ZcashError.pendingTransactionDAOUpdateMissingID
}
let updatedRows = try dbProvider.connection().run(Self.table.filter(TableColumns.id == id).update(pendingTx))
if updatedRows == 0 {
logger.error("attempted to update pending transactions but no rows were updated")
do {
let updatedRows = try dbProvider.connection().run(Self.table.filter(TableColumns.id == id).update(pendingTx))
if updatedRows == 0 {
logger.error("attempted to update pending transactions but no rows were updated")
}
} catch {
throw ZcashError.pendingTransactionDAOUpdate(error)
}
}
/// - Throws:
/// - `pendingTransactionDAODeleteMissingID` if `transaction` (or entity created from `transaction`) is missing id.
/// - `pendingTransactionDAODelete` if sqlite fails
func delete(_ transaction: PendingTransactionEntity) throws {
guard let id = transaction.id else {
throw DatabaseStorageError.malformedEntity(fields: ["id"])
throw ZcashError.pendingTransactionDAODeleteMissingID
}
do {
try dbProvider.connection().run(Self.table.filter(TableColumns.id == id).delete())
} catch {
throw DatabaseStorageError.updateFailed
throw ZcashError.pendingTransactionDAODelete(error)
}
}
/// - Throws:
/// - `pendingTransactionDAOCancelMissingID` if `transaction` (or entity created from `transaction`) is missing id.
/// - `pendingTransactionDAOCancel` if sqlite fails
func cancel(_ transaction: PendingTransactionEntity) throws {
var pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
pendingTx.cancelled = 1
guard let txId = pendingTx.id else {
throw DatabaseStorageError.malformedEntity(fields: ["id"])
throw ZcashError.pendingTransactionDAOCancelMissingID
}
try dbProvider.connection().run(Self.table.filter(TableColumns.id == txId).update(pendingTx))
}
func find(by id: Int) throws -> PendingTransactionEntity? {
guard let row = try dbProvider.connection().pluck(Self.table.filter(TableColumns.id == id).limit(1)) else {
return nil
}
do {
try dbProvider.connection().run(Self.table.filter(TableColumns.id == txId).update(pendingTx))
} catch {
throw ZcashError.pendingTransactionDAOCancel(error)
}
}
/// - Throws:
/// - `pendingTransactionDAOFind` if sqlite fails
func find(by id: Int) throws -> PendingTransactionEntity? {
do {
guard let row = try dbProvider.connection().pluck(Self.table.filter(TableColumns.id == id).limit(1)) else {
return nil
}
let pendingTx: PendingTransaction = try row.decode()
return pendingTx
} catch {
throw DatabaseStorageError.operationFailed
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.pendingTransactionDAOFind(error)
}
}
}
/// - Throws:
/// - `pendingTransactionDAOGetAll` if sqlite fails
func getAll() throws -> [PendingTransactionEntity] {
let allTxs: [PendingTransaction] = try dbProvider.connection().prepare(Self.table).map { row in
try row.decode()
do {
let allTxs: [PendingTransaction] = try dbProvider.connection().prepare(Self.table).map { row in
try row.decode()
}
return allTxs
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.pendingTransactionDAOGetAll(error)
}
}
return allTxs
}
/// - Throws:
/// - `pendingTransactionDAOApplyMinedHeight` if sqlite fails
func applyMinedHeight(_ height: BlockHeight, id: Int) throws {
let transaction = Self.table.filter(TableColumns.id == id)
let updatedRows = try dbProvider.connection()
.run(transaction.update([TableColumns.minedHeight <- height]))
if updatedRows == 0 {
logger.error("attempted to update a row but none was updated")
do {
let transaction = Self.table.filter(TableColumns.id == id)
let updatedRows = try dbProvider.connection()
.run(transaction.update([TableColumns.minedHeight <- height]))
if updatedRows == 0 {
logger.error("attempted to update a row but none was updated")
}
} catch {
throw ZcashError.pendingTransactionDAOApplyMinedHeight(error)
}
}
}

View File

@ -14,11 +14,11 @@ class TransactionSQLDAO: TransactionRepository {
static let memo = Expression<Blob>("memo")
}
var dbProvider: ConnectionProvider
var transactions = Table("transactions")
let dbProvider: ConnectionProvider
let transactions = Table("transactions")
private var blockDao: BlockSQLDAO
private var sentNotesRepository: SentNotesRepository
private let blockDao: BlockSQLDAO
private let sentNotesRepository: SentNotesRepository
private let transactionsView = View("v_transactions")
private let receivedNotesTable = Table("received_notes")
private let sentNotesTable = Table("sent_notes")
@ -58,11 +58,19 @@ class TransactionSQLDAO: TransactionRepository {
}
func countAll() async throws -> Int {
try connection().scalar(transactions.count)
do {
return try connection().scalar(transactions.count)
} catch {
throw ZcashError.transactionRepositoryCountAll(error)
}
}
func countUnmined() async throws -> Int {
try connection().scalar(transactions.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
do {
return try connection().scalar(transactions.filter(ZcashTransaction.Overview.Column.minedHeight == nil).count)
} catch {
throw ZcashError.transactionRepositoryCountUnmined(error)
}
}
func find(id: Int) async throws -> ZcashTransaction.Overview {
@ -107,7 +115,7 @@ class TransactionSQLDAO: TransactionRepository {
guard
let transactionIndex = transaction.index,
let transactionBlockTime = transaction.blockTime
else { throw TransactionRepositoryError.transactionMissingRequiredFields }
else { throw ZcashError.transactionRepositoryTransactionMissingRequiredFields }
let query = transactionsView
.order(
@ -167,30 +175,42 @@ class TransactionSQLDAO: TransactionRepository {
let query = table
.filter(NotesTableStructure.transactionID == transactionID)
let memos = try connection().prepare(query).compactMap { row in
do {
let rawMemo = try row.get(NotesTableStructure.memo)
return try Memo(bytes: rawMemo.bytes)
} catch {
return nil
do {
let memos = try connection().prepare(query).compactMap { row in
do {
let rawMemo = try row.get(NotesTableStructure.memo)
return try Memo(bytes: rawMemo.bytes)
} catch {
return nil
}
}
return memos
} catch {
throw ZcashError.transactionRepositoryFindMemos(error)
}
return memos
}
private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> Entity {
let entities: [Entity] = try execute(query, createEntity: createEntity)
guard let entity = entities.first else { throw TransactionRepositoryError.notFound }
guard let entity = entities.first else { throw ZcashError.transactionRepositoryEntityNotFound }
return entity
}
private func execute<Entity>(_ query: View, createEntity: (Row) throws -> Entity) throws -> [Entity] {
let entities = try connection()
.prepare(query)
.map(createEntity)
return entities
do {
let entities = try connection()
.prepare(query)
.map(createEntity)
return entities
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.transactionRepositoryQueryExecute(error)
}
}
}
}

View File

@ -19,14 +19,14 @@ struct UTXO: Decodable, Encodable {
case spentInTx = "spent_in_tx"
}
var id: Int?
var address: String
let id: Int?
let address: String
var prevoutTxId: Data
var prevoutIndex: Int
var script: Data
var valueZat: Int
var height: Int
var spentInTx: Int?
let script: Data
let valueZat: Int
let height: Int
let spentInTx: Int?
}
extension UTXO: UnspentTransactionOutputEntity {
@ -69,29 +69,30 @@ extension UnspentTransactionOutputEntity {
import SQLite
class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
enum TableColumns {
static var id = Expression<Int>("id_utxo")
static var address = Expression<String>("address")
static var txid = Expression<Blob>("prevout_txid")
static var index = Expression<Int>("prevout_idx")
static var script = Expression<Blob>("script")
static var valueZat = Expression<Int>("value_zat")
static var height = Expression<Int>("height")
static var spentInTx = Expression<Int?>("spent_in_tx")
static let id = Expression<Int>("id_utxo")
static let address = Expression<String>("address")
static let txid = Expression<Blob>("prevout_txid")
static let index = Expression<Int>("prevout_idx")
static let script = Expression<Blob>("script")
static let valueZat = Expression<Int>("value_zat")
static let height = Expression<Int>("height")
static let spentInTx = Expression<Int?>("spent_in_tx")
}
let table = Table("utxos")
var dbProvider: ConnectionProvider
let dbProvider: ConnectionProvider
init (dbProvider: ConnectionProvider) {
init(dbProvider: ConnectionProvider) {
self.dbProvider = dbProvider
}
/// - Throws: `unspentTransactionOutputDAOCreateTable` if creation table fails.
func initialise() async throws {
try await createTableIfNeeded()
}
func createTableIfNeeded() async throws {
private func createTableIfNeeded() async throws {
let stringStatement =
"""
CREATE TABLE IF NOT EXISTS utxos (
@ -107,10 +108,14 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
CONSTRAINT tx_outpoint UNIQUE (prevout_txid, prevout_idx)
)
"""
try dbProvider.connection().run(stringStatement)
do {
try dbProvider.connection().run(stringStatement)
} catch {
throw ZcashError.unspentTransactionOutputDAOCreateTable(error)
}
}
/// - Throws: `unspentTransactionOutputDAOStore` if sqlite query fails.
func store(utxos: [UnspentTransactionOutputEntity]) async throws {
do {
let db = try dbProvider.connection()
@ -120,44 +125,57 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
}
}
} catch {
throw DatabaseStorageError.transactionFailed(underlyingError: error)
throw ZcashError.unspentTransactionOutputDAOStore(error)
}
}
/// - Throws: `unspentTransactionOutputDAOClearAll` if sqlite query fails.
func clearAll(address: String?) async throws {
if let tAddr = address {
do {
do {
if let tAddr = address {
try dbProvider.connection().run(table.filter(TableColumns.address == tAddr).delete())
} catch {
throw DatabaseStorageError.operationFailed
}
} else {
do {
} else {
try dbProvider.connection().run(table.delete())
} catch {
throw DatabaseStorageError.operationFailed
}
} catch {
throw ZcashError.unspentTransactionOutputDAOClearAll(error)
}
}
/// - Throws:
/// - `unspentTransactionOutputDAOClearAll` if the data fetched from the DB can't be decoded to `UTXO` object.
/// - `unspentTransactionOutputDAOGetAll` if sqlite query fails.
func getAll(address: String?) async throws -> [UnspentTransactionOutputEntity] {
do {
if let tAddress = address {
let allTxs: [UTXO] = try dbProvider.connection()
.prepare(table.filter(TableColumns.address == tAddress))
.map { row in
do {
return try row.decode()
} catch {
throw ZcashError.unspentTransactionOutputDAOGetAllCantDecode(error)
}
}
return allTxs
} else {
let allTxs: [UTXO] = try dbProvider.connection()
.prepare(table)
.map { row in
try row.decode()
}
return allTxs
}
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.unspentTransactionOutputDAOGetAll(error)
}
}
}
func getAll(address: String?) async throws -> [UnspentTransactionOutputEntity] {
if let tAddress = address {
let allTxs: [UTXO] = try dbProvider.connection()
.prepare(table.filter(TableColumns.address == tAddress))
.map { row in
try row.decode()
}
return allTxs
} else {
let allTxs: [UTXO] = try dbProvider.connection()
.prepare(table)
.map { row in
try row.decode()
}
return allTxs
}
}
/// - Throws: `unspentTransactionOutputDAOBalance` if sqlite query fails.
func balance(address: String, latestHeight: BlockHeight) async throws -> WalletBalance {
do {
let verified = try dbProvider.connection().scalar(
@ -175,7 +193,7 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
total: Zatoshi(Int64(total))
)
} catch {
throw DatabaseStorageError.operationFailed
throw ZcashError.unspentTransactionOutputDAOBalance(error)
}
}
}

View File

@ -6,10 +6,11 @@
//
import Foundation
import SQLite
protocol AccountEntity {
var account: Int { get set }
var ufvk: String { get set }
var account: Int { get }
var ufvk: String { get }
}
struct Account: AccountEntity, Encodable, Decodable {
@ -18,8 +19,8 @@ struct Account: AccountEntity, Encodable, Decodable {
case ufvk
}
var account: Int
var ufvk: String
let account: Int
let ufvk: String
}
extension Account: Hashable {
@ -44,8 +45,6 @@ protocol AccountRepository {
func update(_ account: AccountEntity) throws
}
import SQLite
class AccountSQDAO: AccountRepository {
enum TableColums {
static let account = Expression<Int>("account")
@ -54,41 +53,86 @@ class AccountSQDAO: AccountRepository {
let table = Table("accounts")
var dbProvider: ConnectionProvider
let dbProvider: ConnectionProvider
let logger: Logger
init (dbProvider: ConnectionProvider, logger: Logger) {
init(dbProvider: ConnectionProvider, logger: Logger) {
self.dbProvider = dbProvider
self.logger = logger
}
/// - Throws:
/// - `accountDAOGetAllCantDecode` if account data fetched from the db can't be decoded to the `Account` object.
/// - `accountDAOGetAll` if sqlite query fetching account data failed.
func getAll() throws -> [AccountEntity] {
let allAccounts: [Account] = try dbProvider.connection().prepare(table).map({ row in
try row.decode()
})
return allAccounts
do {
return try dbProvider.connection()
.prepare(table)
.map { row -> Account in
do {
return try row.decode()
} catch {
throw ZcashError.accountDAOGetAllCantDecode(error)
}
}
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.accountDAOGetAll(error)
}
}
}
/// - Throws:
/// - `accountDAOFindByCantDecode` if account data fetched from the db can't be decoded to the `Account` object.
/// - `accountDAOFindBy` if sqlite query fetching account data failed.
func findBy(account: Int) throws -> AccountEntity? {
let query = table.filter(TableColums.account == account).limit(1)
return try dbProvider.connection().prepare(query).map({ try $0.decode() as Account }).first
do {
return try dbProvider.connection()
.prepare(query)
.map {
do {
return try $0.decode() as Account
} catch {
throw ZcashError.accountDAOFindByCantDecode(error)
}
}
.first
} catch {
if let error = error as? ZcashError {
throw error
} else {
throw ZcashError.accountDAOFindBy(error)
}
}
}
/// - Throws:
/// - `accountDAOUpdate` if sqlite query updating account failed.
/// - `accountDAOUpdatedZeroRows` if sqlite query updating account pass but it affects 0 rows.
func update(_ account: AccountEntity) throws {
guard let acc = account as? Account else {
throw DatabaseStorageError.updateFailed
throw ZcashError.accountDAOUpdateInvalidAccount
}
let updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc))
let updatedRows: Int
do {
updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc))
} catch {
throw ZcashError.accountDAOUpdate(error)
}
if updatedRows == 0 {
logger.error("attempted to update pending transactions but no rows were updated")
throw DatabaseStorageError.updateFailed
throw ZcashError.accountDAOUpdatedZeroRows
}
}
}
class CachingAccountDao: AccountRepository {
var dao: AccountRepository
let dao: AccountRepository
lazy var cache: [Int: AccountEntity] = {
var accountCache: [Int: AccountEntity] = [:]
guard let all = try? dao.getAll() else {

View File

@ -8,9 +8,9 @@
import Foundation
public struct BlockProgress: Equatable {
public var startHeight: BlockHeight
public var targetHeight: BlockHeight
public var progressHeight: BlockHeight
public let startHeight: BlockHeight
public let targetHeight: BlockHeight
public let progressHeight: BlockHeight
public var progress: Float {
let overall = self.targetHeight - self.startHeight

View File

@ -8,8 +8,8 @@
import Foundation
struct EncodedTransaction {
var transactionId: Data
var raw: Data?
let transactionId: Data
let raw: Data?
}
extension EncodedTransaction: Hashable {

View File

@ -26,14 +26,14 @@ public protocol PendingTransactionEntity: RawIdentifiable {
/**
value in zatoshi
*/
var value: Zatoshi { get set }
var value: Zatoshi { get }
/**
data containing the memo if any
*/
var memo: Data? { get set }
var memo: Data? { get }
var fee: Zatoshi? { get set }
var fee: Zatoshi? { get }
var raw: Data? { get set }
@ -61,7 +61,7 @@ public protocol PendingTransactionEntity: RawIdentifiable {
/**
value is 1 if the transaction was cancelled
*/
var cancelled: Int { get }
var cancelled: Int { get set }
/**
how many times this transaction encoding was attempted

View File

@ -8,15 +8,15 @@
import Foundation
protocol ReceivedNoteEntity {
var id: Int { get set }
var transactionId: Int { get set }
var outputIndex: Int { get set }
var account: Int { get set }
var value: Int { get set }
var memo: Data? { get set }
var spent: Int? { get set }
var diversifier: Data { get set }
var rcm: Data { get set }
var nf: Data { get set }
var isChange: Bool { get set }
var id: Int { get }
var transactionId: Int { get }
var outputIndex: Int { get }
var account: Int { get }
var value: Int { get }
var memo: Data? { get }
var spent: Int? { get }
var diversifier: Data { get }
var rcm: Data { get }
var nf: Data { get }
var isChange: Bool { get }
}

View File

@ -8,14 +8,14 @@
import Foundation
protocol SentNoteEntity {
var id: Int { get set }
var transactionId: Int { get set }
var outputIndex: Int { get set }
var fromAccount: Int { get set }
var toAddress: String? { get set }
var toAccount: Int? { get set }
var value: Int { get set }
var memo: Data? { get set }
var id: Int { get }
var transactionId: Int { get }
var outputIndex: Int { get }
var fromAccount: Int { get }
var toAddress: String? { get }
var toAccount: Int? { get }
var value: Int { get }
var memo: Data? { get }
}
extension SentNoteEntity {

View File

@ -84,34 +84,38 @@ extension ZcashTransaction.Overview {
}
init(row: Row) throws {
self.accountId = try row.get(Column.accountId)
self.expiryHeight = try row.get(Column.expiryHeight)
self.id = try row.get(Column.id)
self.index = try row.get(Column.index)
self.hasChange = try row.get(Column.hasChange)
self.memoCount = try row.get(Column.memoCount)
self.minedHeight = try row.get(Column.minedHeight)
self.rawID = Data(blob: try row.get(Column.rawID))
self.receivedNoteCount = try row.get(Column.receivedNoteCount)
self.sentNoteCount = try row.get(Column.sentNoteCount)
self.value = Zatoshi(try row.get(Column.value))
self.isExpiredUmined = try row.get(Column.expiredUnmined)
if let blockTime = try row.get(Column.blockTime) {
self.blockTime = TimeInterval(blockTime)
} else {
self.blockTime = nil
}
do {
self.accountId = try row.get(Column.accountId)
self.expiryHeight = try row.get(Column.expiryHeight)
self.id = try row.get(Column.id)
self.index = try row.get(Column.index)
self.hasChange = try row.get(Column.hasChange)
self.memoCount = try row.get(Column.memoCount)
self.minedHeight = try row.get(Column.minedHeight)
self.rawID = Data(blob: try row.get(Column.rawID))
self.receivedNoteCount = try row.get(Column.receivedNoteCount)
self.sentNoteCount = try row.get(Column.sentNoteCount)
self.value = Zatoshi(try row.get(Column.value))
self.isExpiredUmined = try row.get(Column.expiredUnmined)
if let blockTime = try row.get(Column.blockTime) {
self.blockTime = TimeInterval(blockTime)
} else {
self.blockTime = nil
}
if let fee = try row.get(Column.fee) {
self.fee = Zatoshi(fee)
} else {
self.fee = nil
}
if let fee = try row.get(Column.fee) {
self.fee = Zatoshi(fee)
} else {
self.fee = nil
}
if let raw = try row.get(Column.raw) {
self.raw = Data(blob: raw)
} else {
self.raw = nil
if let raw = try row.get(Column.raw) {
self.raw = Data(blob: raw)
} else {
self.raw = nil
}
} catch {
throw ZcashError.zcashTransactionOverviewInit(error)
}
}

View File

@ -9,10 +9,10 @@ import Foundation
public protocol UnspentTransactionOutputEntity {
// TODO: [#714] Remove address field?, https://github.com/zcash/ZcashLightClientKit/issues/714
var address: String { get set }
var txid: Data { get set }
var index: Int { get set }
var script: Data { get set }
var valueZat: Int { get set }
var height: Int { get set }
var address: String { get }
var txid: Data { get }
var index: Int { get }
var script: Data { get }
var valueZat: Int { get }
var height: Int { get }
}

View File

@ -11,10 +11,6 @@ import Foundation
public typealias BlockHeight = Int
public typealias CompactBlockRange = ClosedRange<BlockHeight>
enum ZcashCompactBlockError: Error {
case unreadableBlock(compactBlock: CompactBlock)
}
extension BlockHeight {
static func empty() -> BlockHeight {
BlockHeight(-1)
@ -26,16 +22,16 @@ A Zcash compact block to store on cache DB
*/
public struct ZcashCompactBlock {
struct Meta {
var hash: Data
var time: UInt32
var saplingOutputs: UInt32
var orchardOutputs: UInt32
let hash: Data
let time: UInt32
let saplingOutputs: UInt32
let orchardOutputs: UInt32
}
public var height: BlockHeight
public var data: Data
public let height: BlockHeight
public let data: Data
var meta: Meta
let meta: Meta
}
extension ZcashCompactBlock: Encodable {
@ -46,9 +42,13 @@ extension ZcashCompactBlock: Encodable {
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(data, forKey: .data)
try container.encode(height, forKey: .height)
do {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(data, forKey: .data)
try container.encode(height, forKey: .height)
} catch {
throw ZcashError.compactBlockEncode(error)
}
}
}

View File

@ -0,0 +1,40 @@
/*
!!!!! To edit this file go to ZcashErrorCodeDefinition first and udate/add codes. Then run generateErrorCode.sh script to regenerate this file.
By design each error should be used only at one place in the app. Thanks to that it is possible to identify exact line in the code from which the
error originates. And it can help with debugging.
*/
import Foundation
{% for type in types.enums where type.name == "ZcashErrorDefinition" %}
public enum ZcashError: Equatable, Error {
{% for case in type.cases %}
{% for docLine in case.documentation %}
/// {{ docLine }}
{% endfor %}
/// {{ case.annotations["code"] }}
case {{ case.name }}{% if case.associatedValues.count > 0 %}({% for value in case.associatedValues %}_ {{ value.externalName }}: {{ value.typeName }}{% if not forloop.last%}, {% endif %}{% endfor %}){% endif %}
{% endfor %}
public var message: String {
switch self {
{% for case in type.cases %}
case .{{ case.name }}: return "{{ case.documentation[0] }}"
{% endfor %}
}
}
public var code: ZcashErrorCode {
switch self {
{% for case in type.cases %}
case .{{ case.name }}: return .{{ case.name}}
{% endfor %}
}
}
public static func == (lhs: ZcashError, rhs: ZcashError) -> Bool {
return lhs.code == rhs.code
}
}
{% endfor %}

View File

@ -0,0 +1,15 @@
/*
!!!!! To edit this file go to ZcashErrorCodeDefinition first and udate/add codes. Then run generateErrorCode.sh script to regenerate this file.
By design each error code should be used only at one place in the app. Thanks to that it is possible to identify exact line in the code from which the
error originates. And it can help with debugging.
*/
{% for type in types.enums where type.name == "ZcashErrorDefinition" %}
public enum ZcashErrorCode: String {
{% for case in type.cases %}
/// {{ case.documentation[0] }}
case {{ case.name }} = "{{ case.annotations["code"] }}"
{% endfor %}
}
{% endfor %}

View File

@ -0,0 +1,34 @@
#!/bin/zsh
scriptDir=${0:a:h}
cd "${scriptDir}"
sourcery_version=2.0.2
if which sourcery >/dev/null; then
if [[ $(sourcery --version) != $sourcery_version ]]; then
echo "warning: Compatible sourcer version not installed. Install sourcer $sourcery_version. Currently installed version is $(sourcer --version)"
exit 1
fi
sourcery \
--disableCache \
--parseDocumentation \
--verbose \
--sources ./ \
--sources ../ \
--templates ZcashErrorCode.stencil \
--output ../ZcashErrorCode.swift
sourcery \
--disableCache \
--parseDocumentation \
--verbose \
--sources ./ \
--sources ../ \
--templates ZcashError.stencil \
--output ../ZcashError.swift
else
echo "warning: sourcery not installed"
fi

View File

@ -0,0 +1,965 @@
// Generated using Sourcery 2.0.2 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
/*
!!!!! To edit this file go to ZcashErrorCodeDefinition first and udate/add codes. Then run generateErrorCode.sh script to regenerate this file.
By design each error should be used only at one place in the app. Thanks to that it is possible to identify exact line in the code from which the
error originates. And it can help with debugging.
*/
import Foundation
public enum ZcashError: Equatable, Error {
/// Updating of paths in `Initilizer` according to alias failed.
/// ZINIT0001
case initializerCantUpdateURLWithAlias(_ url: URL)
/// Alias used to create this instance of the `SDKSynchronizer` is already used by other instance.
/// ZINIT0002
case initializerAliasAlreadyInUse(_ alias: ZcashSynchronizerAlias)
/// Unknown GRPC Service error
/// ZSRVC0001
case serviceUnknownError(_ error: Error)
/// LightWalletService.getInfo failed.
/// ZSRVC0002
case serviceGetInfoFailed(_ error: LightWalletServiceError)
/// LightWalletService.latestBlock failed.
/// ZSRVC0003
case serviceLatestBlockFailed(_ error: LightWalletServiceError)
/// LightWalletService.latestBlockHeight failed.
/// ZSRVC0004
case serviceLatestBlockHeightFailed(_ error: LightWalletServiceError)
/// LightWalletService.blockRange failed.
/// ZSRVC0005
case serviceBlockRangeFailed(_ error: LightWalletServiceError)
/// LightWalletService.submit failed.
/// ZSRVC0006
case serviceSubmitFailed(_ error: LightWalletServiceError)
/// LightWalletService.fetchTransaction failed.
/// ZSRVC0007
case serviceFetchTransactionFailed(_ error: LightWalletServiceError)
/// LightWalletService.fetchUTXOs failed.
/// ZSRVC0008
case serviceFetchUTXOsFailed(_ error: LightWalletServiceError)
/// LightWalletService.blockStream failed.
/// ZSRVC0000
case serviceBlockStreamFailed(_ error: LightWalletServiceError)
/// Migration of the pending DB failed because of unspecific reason.
/// ZDBMG0001
case dbMigrationGenericFailure(_ error: Error)
/// Migration of the pending DB failed because unknown version of the existing database.
/// ZDBMG00002
case dbMigrationInvalidVersion
/// Migration of the pending DB to version 1 failed.
/// ZDBMG00003
case dbMigrationV1(_ dbError: Error)
/// Migration of the pending DB to version 2 failed.
/// ZDBMG00004
case dbMigrationV2(_ dbError: Error)
/// SimpleConnectionProvider init of Connection failed.
/// ZSCPC0001
case simpleConnectionProvider(_ error: Error)
/// Downloaded file with sapling spending parameters isn't valid.
/// ZSAPP0001
case saplingParamsInvalidSpendParams
/// Downloaded file with sapling output parameters isn't valid.
/// ZSAPP0002
case saplingParamsInvalidOutputParams
/// Failed to download sapling parameters file
/// - `error` is download error.
/// - `downloadURL` is URL from which was file downloaded.
/// ZSAPP0003
case saplingParamsDownload(_ error: Error, _ downloadURL: URL)
/// Failed to move sapling parameters file to final destination after download.
/// - `error` is move error.
/// - `downloadURL` is URL from which was file downloaded.
/// - `destination` is filesystem URL pointing to location where downloaded file should be moved.
/// ZSAPP0004
case saplingParamsCantMoveDownloadedFile(_ error: Error, _ downloadURL: URL, _ destination: URL)
/// SQLite query failed when fetching received notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0001
case notesDAOReceivedCount(_ sqliteError: Error)
/// SQLite query failed when fetching received notes from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0002
case notesDAOReceivedNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
/// ZNDAO0003
case notesDAOReceivedCantDecode(_ error: Error)
/// SQLite query failed when fetching sent notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0004
case notesDAOSentCount(_ sqliteError: Error)
/// SQLite query failed when fetching sent notes from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZNDAO0005
case notesDAOSentNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
/// ZNDAO0006
case notesDAOSentCantDecode(_ error: Error)
/// SQLite query failed when fetching block information from database.
/// - `sqliteError` is error produced by SQLite library.
/// ZBDAO0001
case blockDAOBlock(_ sqliteError: Error)
/// Fetched block information from DB but can't decode them.
/// - `error` is decoding error.
/// ZBDAO0002
case blockDAOCantDecode(_ error: Error)
/// SQLite query failed when fetching height of the latest block from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZBDAO0003
case blockDAOLatestBlockHeight(_ sqliteError: Error)
/// SQLite query failed when fetching the latest block from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZBDAO0004
case blockDAOLatestBlock(_ sqliteError: Error)
/// Fetched latesxt block information from DB but can't decode them.
/// - `error` is decoding error.
/// ZBDAO0005
case blockDAOLatestBlockCantDecode(_ error: Error)
/// Error from rust layer when calling ZcashRustBackend.createAccount
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0001
case rustCreateAccount(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.createToAddress
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0002
case rustCreateToAddress(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0003
case rustDecryptAndStoreTransaction(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getBalance
/// - `account` is account passed to ZcashRustBackend.getBalance.
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0004
case rustGetBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getCurrentAddress
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0005
case rustGetCurrentAddress(_ rustError: String)
/// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress
/// ZRUST0006
case rustGetCurrentAddressInvalidAddress
/// Error from rust layer when calling ZcashRustBackend.getNearestRewindHeight
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0007
case rustGetNearestRewindHeight(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getNextAvailableAddress
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0008
case rustGetNextAvailableAddress(_ rustError: String)
/// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getNextAvailableAddress
/// ZRUST0009
case rustGetNextAvailableAddressInvalidAddress
/// account parameter is lower than 0 when calling ZcashRustBackend.getTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getTransparentBalance.
/// ZRUST0010
case rustGetTransparentBalanceNegativeAccount(_ account: Int)
/// Error from rust layer when calling ZcashRustBackend.getTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getTransparentBalance.
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0011
case rustGetTransparentBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getVerifiedBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedBalance.
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0012
case rustGetVerifiedBalance(_ account: Int, _ rustError: String)
/// account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedTransparentBalance.
/// ZRUST0013
case rustGetVerifiedTransparentBalanceNegativeAccount(_ account: Int)
/// Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedTransparentBalance.
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0014
case rustGetVerifiedTransparentBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.initDataDb
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0015
case rustInitDataDb(_ rustError: String)
/// Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method contains null bytes before end
/// ZRUST0016
case rustInitAccountsTableViewingKeyCotainsNullBytes
/// Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method isn't valid
/// ZRUST0017
case rustInitAccountsTableViewingKeyIsInvalid
/// Error from rust layer when calling ZcashRustBackend.initAccountsTable
/// ZRUST0018
case rustInitAccountsTableDataDbNotEmpty
/// Error from rust layer when calling ZcashRustBackend.initAccountsTable
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0019
case rustInitAccountsTable(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.initBlockMetadataDb
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0020
case rustInitBlockMetadataDb(_ rustError: String)
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.writeBlocksMetadata
/// ZRUST0021
case rustWriteBlocksMetadataAllocationProblem
/// Error from rust layer when calling ZcashRustBackend.writeBlocksMetadata
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0022
case rustWriteBlocksMetadata(_ rustError: String)
/// hash passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end
/// ZRUST0023
case rustInitBlocksTableHashContainsNullBytes
/// saplingTree passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end
/// ZRUST0024
case rustInitBlocksTableSaplingTreeContainsNullBytes
/// Error from rust layer when calling ZcashRustBackend.initBlocksTable
/// ZRUST0025
case rustInitBlocksTableDataDbNotEmpty
/// Error from rust layer when calling ZcashRustBackend.initBlocksTable
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0026
case rustInitBlocksTable(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.listTransparentReceivers
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0027
case rustListTransparentReceivers(_ rustError: String)
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.listTransparentReceivers
/// ZRUST0028
case rustListTransparentReceiversInvalidAddress
/// Error from rust layer when calling ZcashRustBackend.putUnspentTransparentOutput
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0029
case rustPutUnspentTransparentOutput(_ rustError: String)
/// Error unrelated to chain validity from rust layer when calling ZcashRustBackend.validateCombinedChain
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0030
case rustValidateCombinedChainValidationFailed(_ rustError: String)
/// Error from rust layer which means that combined chain isn't valid.
/// - `upperBound` is the height of the highest invalid block (on the assumption that the highest block in the cache database is correct).
/// ZRUST0031
case rustValidateCombinedChainInvalidChain(_ upperBound: Int32)
/// Error from rust layer when calling ZcashRustBackend.rewindToHeight
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0032
case rustRewindToHeight(_ height: Int32, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.rewindCacheToHeight
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0033
case rustRewindCacheToHeight(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.scanBlocks
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0034
case rustScanBlocks(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.shieldFunds
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0035
case rustShieldFunds(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.consensusBranchIdFor
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0036
case rustNoConsensusBranchId(_ height: Int32)
/// address passed to the ZcashRustBackend.receiverTypecodesOnUnifiedAddress method contains null bytes before end
/// - `address` is address passed to ZcashRustBackend.receiverTypecodesOnUnifiedAddress.
/// ZRUST0037
case rustReceiverTypecodesOnUnifiedAddressContainsNullBytes(_ address: String)
/// Error from rust layer when calling ZcashRustBackend.receiverTypecodesOnUnifiedAddress
/// ZRUST0038
case rustRustReceiverTypecodesOnUnifiedAddressMalformed
/// Error from rust layer when calling ZcashRustBackend.deriveUnifiedSpendingKey
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0039
case rustDeriveUnifiedSpendingKey(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.deriveUnifiedFullViewingKey
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0040
case rustDeriveUnifiedFullViewingKey(_ rustError: String)
/// Viewing key derived by rust layer is invalid when calling ZcashRustBackend.deriveUnifiedFullViewingKey
/// ZRUST0041
case rustDeriveUnifiedFullViewingKeyInvalidDerivedKey
/// Error from rust layer when calling ZcashRustBackend.getSaplingReceiver
/// - `address` is address passed to ZcashRustBackend.getSaplingReceiver.
/// ZRUST0042
case rustGetSaplingReceiverInvalidAddress(_ address: UnifiedAddress)
/// Sapling receiver generated by rust layer is invalid when calling ZcashRustBackend.getSaplingReceiver
/// ZRUST0043
case rustGetSaplingReceiverInvalidReceiver
/// Error from rust layer when calling ZcashRustBackend.getTransparentReceiver
/// - `address` is address passed to ZcashRustBackend.getTransparentReceiver.
/// ZRUST0044
case rustGetTransparentReceiverInvalidAddress(_ address: UnifiedAddress)
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver
/// ZRUST0045
case rustGetTransparentReceiverInvalidReceiver
/// SQLite query failed when fetching all accounts from the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZADAO0001
case accountDAOGetAll(_ sqliteError: Error)
/// Fetched accounts from SQLite but can't decode them.
/// - `error` is decoding error.
/// ZADAO0002
case accountDAOGetAllCantDecode(_ error: Error)
/// SQLite query failed when seaching for accounts in the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZADAO0003
case accountDAOFindBy(_ sqliteError: Error)
/// Fetched accounts from SQLite but can't decode them.
/// - `error` is decoding error.
/// ZADAO0004
case accountDAOFindByCantDecode(_ error: Error)
/// Object passed to update() method conforms to `AccountEntity` protocol but isn't exactly `Account` type.
/// ZADAO0005
case accountDAOUpdateInvalidAccount
/// SQLite query failed when updating account in the database.
/// - `sqliteError` is error produced by SQLite library.
/// ZADAO0006
case accountDAOUpdate(_ sqliteError: Error)
/// Update of the account updated 0 rows in the database. One row should be updated.
/// ZADAO0007
case accountDAOUpdatedZeroRows
/// Failed to write block to disk.
/// ZBLRP00001
case blockRepositoryWriteBlock(_ block: ZcashCompactBlock)
/// Failed to get filename for the block from file URL.
/// ZBLRP0002
case blockRepositoryGetFilename(_ url: URL)
/// Failed to parse block height from filename.
/// ZBLRP0003
case blockRepositoryParseHeightFromFilename(_ filename: String)
/// Failed to remove existing block from disk.
/// ZBLRP0004
case blockRepositoryRemoveExistingBlock(_ error: Error)
/// Failed to get filename and information if url points to directory from file URL.
/// ZBLRP0005
case blockRepositoryGetFilenameAndIsDirectory(_ url: URL)
/// Failed to create blocks cache directory.
/// ZBLRP0006
case blockRepositoryCreateBlocksCacheDirectory(_ url: URL)
/// Failed to read content of directory.
/// ZBLRP0007
case blockRepositoryReadDirectoryContent(_ url: URL)
/// Failed to remove block from disk after rewind operation.
/// ZBLRP0008
case blockRepositoryRemoveBlockAfterRewind(_ url: URL)
/// Failed to remove blocks cache directory while clearing storage.
/// ZBLRP0009
case blockRepositoryRemoveBlocksCacheDirectory(_ url: URL)
/// Stream downloading the given block range failed.
/// ZBDSEO0001
case blockDownloaderServiceDownloadBlockRange(_ error: Error)
/// Initialization of `ZcashTransaction.Overview` failed.
/// ZTEZT0001
case zcashTransactionOverviewInit(_ error: Error)
/// Initialization of `ZcashTransaction.Received` failed.
/// ZTEZT0002
case zcashTransactionReceivedInit(_ error: Error)
/// Initialization of `ZcashTransaction.Sent` failed.
/// ZTEZT0003
case zcashTransactionSentInit(_ error: Error)
/// Entity not found in the database, result of `createEntity` execution.
/// ZTREE0001
case transactionRepositoryEntityNotFound
/// `Find` call is missing fields, required fields are transaction `index` and `blockTime`.
/// ZTREE0002
case transactionRepositoryTransactionMissingRequiredFields
/// Counting all transactions failed.
/// ZTREE0003
case transactionRepositoryCountAll(_ error: Error)
/// Counting all unmined transactions failed.
/// ZTREE0004
case transactionRepositoryCountUnmined(_ error: Error)
/// Execution of a query failed.
/// ZTREE0005
case transactionRepositoryQueryExecute(_ error: Error)
/// Finding memos in the database failed.
/// ZTREE0006
case transactionRepositoryFindMemos(_ error: Error)
/// Can't encode `ZcashCompactBlock` object.
/// ZCMPB0001
case compactBlockEncode(_ error: Error)
/// Invalid UTF-8 Bytes where detected when attempting to create a MemoText.
/// ZMEMO0001
case memoTextInvalidUTF8
/// Trailing null-bytes were found when attempting to create a MemoText.
/// ZMEMO0002
case memoTextInputEndsWithNullBytes
/// The resulting bytes provided are too long to be stored as a MemoText.
/// ZMEMO0003
case memoTextInputTooLong(_ length: Int)
/// The resulting bytes provided are too long to be stored as a MemoBytes.
/// ZMEMO0004
case memoBytesInputTooLong(_ length: Int)
/// Invalid UTF-8 Bytes where detected when attempting to convert MemoBytes to Memo.
/// ZMEMO0005
case memoBytesInvalidUTF8
/// Failed to load JSON with checkpoint from disk.
/// ZCHKP0001
case checkpointCantLoadFromDisk(_ error: Error)
/// Failed to decode `Checkpoint` object.
/// ZCHKP0002
case checkpointDecode(_ error: Error)
/// Decoding of `PendingTransaction` failed because of specific invalid data.
/// - `field` is list of fields names that contain invalid data.
/// ZPETR0001
case pendingTransactionDecodeInvalidData(_ fields: [String])
/// Can't decode `PendingTransaction`.
/// - `error` is error which described why decoding failed.
/// ZPETR0002
case pendingTransactionCantDecode(_ error: Error)
/// Can't encode `PendingTransaction`.
/// - `error` is error which described why encoding failed.
/// ZPETR0003
case pendingTransactionCantEncode(_ error: Error)
/// SQLite query failed when creating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0004
case pendingTransactionDAOCreate(_ sqliteError: Error)
/// Pending transaction which should be updated is missing ID.
/// ZPETR0005
case pendingTransactionDAOUpdateMissingID
/// SQLite query failed when updating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0006
case pendingTransactionDAOUpdate(_ sqliteError: Error)
/// Pending transaction which should be deleted is missing ID.
/// ZPETR0007
case pendingTransactionDAODeleteMissingID
/// SQLite query failed when deleting pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0008
case pendingTransactionDAODelete(_ sqliteError: Error)
/// Pending transaction which should be canceled is missing ID.
/// ZPETR0009
case pendingTransactionDAOCancelMissingID
/// SQLite query failed when canceling pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0010
case pendingTransactionDAOCancel(_ sqliteError: Error)
/// SQLite query failed when seaching for pending transaction.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0011
case pendingTransactionDAOFind(_ sqliteError: Error)
/// SQLite query failed when getting pending transactions.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0012
case pendingTransactionDAOGetAll(_ sqliteError: Error)
/// SQLite query failed when applying mined height.
/// - `sqliteError` is error produced by SQLite library.
/// ZPETR0013
case pendingTransactionDAOApplyMinedHeight(_ sqliteError: Error)
/// Invalid account when trying to derive spending key
/// ZDRVT0001
case derivationToolSpendingKeyInvalidAccount
/// Creation of the table for unspent transaction output failed.
/// - `sqliteError` is error produced by SQLite library.
/// ZUTOD0001
case unspentTransactionOutputDAOCreateTable(_ sqliteError: Error)
/// SQLite query failed when storing unspent transaction output.
/// - `sqliteError` is error produced by SQLite library.
/// ZUTOD0002
case unspentTransactionOutputDAOStore(_ sqliteError: Error)
/// SQLite query failed when removing all the unspent transation outputs.
/// - `sqliteError` is error produced by SQLite library.
/// ZUTOD0003
case unspentTransactionOutputDAOClearAll(_ sqliteError: Error)
/// Fetched information about unspent transaction output from the DB but it can't be decoded to `UTXO` object.
/// - `error` decoding error.
/// ZUTOD0004
case unspentTransactionOutputDAOGetAllCantDecode(_ error: Error)
/// SQLite query failed when getting all the unspent transation outputs.
/// - `sqliteError` is error produced by SQLite library.
/// ZUTOD0005
case unspentTransactionOutputDAOGetAll(_ sqliteError: Error)
/// SQLite query failed when getting balance.
/// - `sqliteError` is error produced by SQLite library.
/// ZUTOD0006
case unspentTransactionOutputDAOBalance(_ sqliteError: Error)
/// Can't create `SaplingExtendedSpendingKey` because input is invalid.
/// ZWLTP0001
case spendingKeyInvalidInput
/// Can't create `UnifiedFullViewingKey` because input is invalid.
/// ZWLTP0002
case unifiedFullViewingKeyInvalidInput
/// Can't create `SaplingExtendedFullViewingKey` because input is invalid.
/// ZWLTP0003
case extetendedFullViewingKeyInvalidInput
/// Can't create `TransparentAddress` because input is invalid.
/// ZWLTP0004
case transparentAddressInvalidInput
/// Can't create `SaplingAddress` because input is invalid.
/// ZWLTP0005
case saplingAddressInvalidInput
/// Can't create `UnifiedAddress` because input is invalid.
/// ZWLTP0006
case unifiedAddressInvalidInput
/// Can't create `Recipient` because input is invalid.
/// ZWLTP0007
case recipientInvalidInput
/// WalletTransactionEncoder wants to create transaction but files with sapling parameters are not present on disk.
/// ZWLTE0001
case walletTransEncoderCreateTransactionMissingSaplingParams
/// WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk.
/// ZWLTE0002
case walletTransEncoderShieldFundsMissingSaplingParams
/// PersistentTransactionsManager cant create transaction.
/// ZPTRM0001
case persistentTransManagerCantCreateTransaction(_ recipient: PendingTransactionRecipient, _ account: Int, _ zatoshi: Zatoshi)
/// PersistentTransactionsManager cant get to address from pending transaction.
/// ZPTRM0002
case persistentTransManagerEncodeUknownToAddress(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id.
/// ZPTRM0003
case persistentTransManagerSubmitTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored.
/// ZPTRM0004
case persistentTransManagerSubmitTransactionNotFound(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is canceled.
/// ZPTRM0005
case persistentTransManagerSubmitTransactionCanceled(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data.
/// ZPTRM0006
case persistentTransManagerSubmitTransactionRawDataMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but submit API call failed.
/// ZPTRM0007
case persistentTransManagerSubmitFailed(_ entity: PendingTransactionEntity, _ serviceErrorCode: Int)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored.
/// ZPTRM0008
case persistentTransManagerApplyMinedHeightTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored.
/// ZPTRM0009
case persistentTransManagerApplyMinedHeightTransactionNotFound(_ entity: PendingTransactionEntity)
/// Initiatilzation fo `Zatoshi` from a decoder failed.
/// ZTSHO0001
case zatoshiDecode(_ error: Error)
/// Encode of `Zatoshi` failed.
/// ZTSHO0002
case zatoshiEncode(_ error: Error)
/// Awaiting transactions from the stream failed.
/// ZUTXO0001
case unspentTransactionFetcherStream(_ error: Error)
/// CompactBlockProcessor was started with an invalid configuration.
/// ZCBPEO0001
case compactBlockProcessorInvalidConfiguration
/// CompactBlockProcessor was set up with path but that location couldn't be reached.
/// ZCBPEO0002
case compactBlockProcessorMissingDbPath(_ path: String)
/// Data Db file couldn't be initialized at path.
/// ZCBPEO0003
case compactBlockProcessorDataDbInitFailed(_ path: String)
/// There's a problem with the network connection.
/// ZCBPEO0004
case compactBlockProcessorConnection(_ underlyingError: Error)
/// Error on gRPC happened.
/// ZCBPEO0005
case compactBlockProcessorGrpcError(_ statusCode: Int, _ message: String)
/// Network connection timeout.
/// ZCBPEO0006
case compactBlockProcessorConnectionTimeout
/// Compact Block failed and reached the maximum amount of retries it was set up to do.
/// ZCBPEO0007
case compactBlockProcessorMaxAttemptsReached(_ attempts: Int)
/// Unspecified error occured.
/// ZCBPEO0008
case compactBlockProcessorUnspecified(_ underlyingError: Error)
/// Critical error occured.
/// ZCBPEO0009
case compactBlockProcessorCritical
/// Invalid Account.
/// ZCBPEO0010
case compactBlockProcessorInvalidAccount
/// The remote server you are connecting to is publishing a different branch ID than the one your App is expecting This could be caused by your App being out of date or the server you are connecting you being either on a different network or out of date after a network upgrade.
/// ZCBPEO0011
case compactBlockProcessorWrongConsensusBranchId(_ expectedLocally: ConsensusBranchID, _ found: ConsensusBranchID)
/// A server was reached, but it's targeting the wrong network Type. Make sure you are pointing to the right server.
/// ZCBPEO0012
case compactBlockProcessorNetworkMismatch(_ expected: NetworkType, _ found: NetworkType)
/// A server was reached, it's showing a different sapling activation. Are you sure you are pointing to the right server?
/// ZCBPEO0013
case compactBlockProcessorSaplingActivationMismatch(_ expected: BlockHeight, _ found: BlockHeight)
/// when the given URL is the same URL than the one provided as `self.fsBlockDbRoot` assuming that's a programming error being the `legacyCacheDbURL` a sqlite database file and not a directory
/// ZCBPEO0014
case compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL
/// Deletion of readable file at the provided URL failed.
/// ZCBPEO0015
case compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb(_ error: Error)
/// Chain name does not match. Expected either 'test' or 'main'. This is probably an API or programming error.
/// ZCBPEO0016
case compactBlockProcessorChainName(_ name: String)
/// Consensus BranchIDs don't match this is probably an API or programming error.
/// ZCBPEO0017
case compactBlockProcessorConsensusBranchID
/// The synchronizer is unprepared.
/// ZSYNCO0001
case synchronizerNotPrepared
/// Memos can't be sent to transparent addresses.
/// ZSYNCO0002
case synchronizerSendMemoToTransparentAddress
/// There is not enough transparent funds to cover fee for the shielding.
/// ZSYNCO0003
case synchronizerShieldFundsInsuficientTransparentFunds
/// LatestUTXOs for the address failed, invalid t-address.
/// ZSYNCO0004
case synchronizerLatestUTXOsInvalidTAddress
/// Rewind failed, unknown archor height
/// ZSYNCO0005
case synchronizerRewindUnknownArchorHeight
public var message: String {
switch self {
case .initializerCantUpdateURLWithAlias: return "Updating of paths in `Initilizer` according to alias failed."
case .initializerAliasAlreadyInUse: return "Alias used to create this instance of the `SDKSynchronizer` is already used by other instance."
case .serviceUnknownError: return "Unknown GRPC Service error"
case .serviceGetInfoFailed: return "LightWalletService.getInfo failed."
case .serviceLatestBlockFailed: return "LightWalletService.latestBlock failed."
case .serviceLatestBlockHeightFailed: return "LightWalletService.latestBlockHeight failed."
case .serviceBlockRangeFailed: return "LightWalletService.blockRange failed."
case .serviceSubmitFailed: return "LightWalletService.submit failed."
case .serviceFetchTransactionFailed: return "LightWalletService.fetchTransaction failed."
case .serviceFetchUTXOsFailed: return "LightWalletService.fetchUTXOs failed."
case .serviceBlockStreamFailed: return "LightWalletService.blockStream failed."
case .dbMigrationGenericFailure: return "Migration of the pending DB failed because of unspecific reason."
case .dbMigrationInvalidVersion: return "Migration of the pending DB failed because unknown version of the existing database."
case .dbMigrationV1: return "Migration of the pending DB to version 1 failed."
case .dbMigrationV2: return "Migration of the pending DB to version 2 failed."
case .simpleConnectionProvider: return "SimpleConnectionProvider init of Connection failed."
case .saplingParamsInvalidSpendParams: return "Downloaded file with sapling spending parameters isn't valid."
case .saplingParamsInvalidOutputParams: return "Downloaded file with sapling output parameters isn't valid."
case .saplingParamsDownload: return "Failed to download sapling parameters file"
case .saplingParamsCantMoveDownloadedFile: return "Failed to move sapling parameters file to final destination after download."
case .notesDAOReceivedCount: return "SQLite query failed when fetching received notes count from the database."
case .notesDAOReceivedNote: return "SQLite query failed when fetching received notes from the database."
case .notesDAOReceivedCantDecode: return "Fetched note from the SQLite but can't decode that."
case .notesDAOSentCount: return "SQLite query failed when fetching sent notes count from the database."
case .notesDAOSentNote: return "SQLite query failed when fetching sent notes from the database."
case .notesDAOSentCantDecode: return "Fetched note from the SQLite but can't decode that."
case .blockDAOBlock: return "SQLite query failed when fetching block information from database."
case .blockDAOCantDecode: return "Fetched block information from DB but can't decode them."
case .blockDAOLatestBlockHeight: return "SQLite query failed when fetching height of the latest block from the database."
case .blockDAOLatestBlock: return "SQLite query failed when fetching the latest block from the database."
case .blockDAOLatestBlockCantDecode: return "Fetched latesxt block information from DB but can't decode them."
case .rustCreateAccount: return "Error from rust layer when calling ZcashRustBackend.createAccount"
case .rustCreateToAddress: return "Error from rust layer when calling ZcashRustBackend.createToAddress"
case .rustDecryptAndStoreTransaction: return "Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction"
case .rustGetBalance: return "Error from rust layer when calling ZcashRustBackend.getBalance"
case .rustGetCurrentAddress: return "Error from rust layer when calling ZcashRustBackend.getCurrentAddress"
case .rustGetCurrentAddressInvalidAddress: return "Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress"
case .rustGetNearestRewindHeight: return "Error from rust layer when calling ZcashRustBackend.getNearestRewindHeight"
case .rustGetNextAvailableAddress: return "Error from rust layer when calling ZcashRustBackend.getNextAvailableAddress"
case .rustGetNextAvailableAddressInvalidAddress: return "Unified address generated by rust layer is invalid when calling ZcashRustBackend.getNextAvailableAddress"
case .rustGetTransparentBalanceNegativeAccount: return "account parameter is lower than 0 when calling ZcashRustBackend.getTransparentBalance"
case .rustGetTransparentBalance: return "Error from rust layer when calling ZcashRustBackend.getTransparentBalance"
case .rustGetVerifiedBalance: return "Error from rust layer when calling ZcashRustBackend.getVerifiedBalance"
case .rustGetVerifiedTransparentBalanceNegativeAccount: return "account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance"
case .rustGetVerifiedTransparentBalance: return "Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance"
case .rustInitDataDb: return "Error from rust layer when calling ZcashRustBackend.initDataDb"
case .rustInitAccountsTableViewingKeyCotainsNullBytes: return "Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method contains null bytes before end"
case .rustInitAccountsTableViewingKeyIsInvalid: return "Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method isn't valid"
case .rustInitAccountsTableDataDbNotEmpty: return "Error from rust layer when calling ZcashRustBackend.initAccountsTable"
case .rustInitAccountsTable: return "Error from rust layer when calling ZcashRustBackend.initAccountsTable"
case .rustInitBlockMetadataDb: return "Error from rust layer when calling ZcashRustBackend.initBlockMetadataDb"
case .rustWriteBlocksMetadataAllocationProblem: return "Unable to allocate memory required to write blocks when calling ZcashRustBackend.writeBlocksMetadata"
case .rustWriteBlocksMetadata: return "Error from rust layer when calling ZcashRustBackend.writeBlocksMetadata"
case .rustInitBlocksTableHashContainsNullBytes: return "hash passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end"
case .rustInitBlocksTableSaplingTreeContainsNullBytes: return "saplingTree passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end"
case .rustInitBlocksTableDataDbNotEmpty: return "Error from rust layer when calling ZcashRustBackend.initBlocksTable"
case .rustInitBlocksTable: return "Error from rust layer when calling ZcashRustBackend.initBlocksTable"
case .rustListTransparentReceivers: return "Error from rust layer when calling ZcashRustBackend.listTransparentReceivers"
case .rustListTransparentReceiversInvalidAddress: return "Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.listTransparentReceivers"
case .rustPutUnspentTransparentOutput: return "Error from rust layer when calling ZcashRustBackend.putUnspentTransparentOutput"
case .rustValidateCombinedChainValidationFailed: return "Error unrelated to chain validity from rust layer when calling ZcashRustBackend.validateCombinedChain"
case .rustValidateCombinedChainInvalidChain: return "Error from rust layer which means that combined chain isn't valid."
case .rustRewindToHeight: return "Error from rust layer when calling ZcashRustBackend.rewindToHeight"
case .rustRewindCacheToHeight: return "Error from rust layer when calling ZcashRustBackend.rewindCacheToHeight"
case .rustScanBlocks: return "Error from rust layer when calling ZcashRustBackend.scanBlocks"
case .rustShieldFunds: return "Error from rust layer when calling ZcashRustBackend.shieldFunds"
case .rustNoConsensusBranchId: return "Error from rust layer when calling ZcashRustBackend.consensusBranchIdFor"
case .rustReceiverTypecodesOnUnifiedAddressContainsNullBytes: return "address passed to the ZcashRustBackend.receiverTypecodesOnUnifiedAddress method contains null bytes before end"
case .rustRustReceiverTypecodesOnUnifiedAddressMalformed: return "Error from rust layer when calling ZcashRustBackend.receiverTypecodesOnUnifiedAddress"
case .rustDeriveUnifiedSpendingKey: return "Error from rust layer when calling ZcashRustBackend.deriveUnifiedSpendingKey"
case .rustDeriveUnifiedFullViewingKey: return "Error from rust layer when calling ZcashRustBackend.deriveUnifiedFullViewingKey"
case .rustDeriveUnifiedFullViewingKeyInvalidDerivedKey: return "Viewing key derived by rust layer is invalid when calling ZcashRustBackend.deriveUnifiedFullViewingKey"
case .rustGetSaplingReceiverInvalidAddress: return "Error from rust layer when calling ZcashRustBackend.getSaplingReceiver"
case .rustGetSaplingReceiverInvalidReceiver: return "Sapling receiver generated by rust layer is invalid when calling ZcashRustBackend.getSaplingReceiver"
case .rustGetTransparentReceiverInvalidAddress: return "Error from rust layer when calling ZcashRustBackend.getTransparentReceiver"
case .rustGetTransparentReceiverInvalidReceiver: return "Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver"
case .accountDAOGetAll: return "SQLite query failed when fetching all accounts from the database."
case .accountDAOGetAllCantDecode: return "Fetched accounts from SQLite but can't decode them."
case .accountDAOFindBy: return "SQLite query failed when seaching for accounts in the database."
case .accountDAOFindByCantDecode: return "Fetched accounts from SQLite but can't decode them."
case .accountDAOUpdateInvalidAccount: return "Object passed to update() method conforms to `AccountEntity` protocol but isn't exactly `Account` type."
case .accountDAOUpdate: return "SQLite query failed when updating account in the database."
case .accountDAOUpdatedZeroRows: return "Update of the account updated 0 rows in the database. One row should be updated."
case .blockRepositoryWriteBlock: return "Failed to write block to disk."
case .blockRepositoryGetFilename: return "Failed to get filename for the block from file URL."
case .blockRepositoryParseHeightFromFilename: return "Failed to parse block height from filename."
case .blockRepositoryRemoveExistingBlock: return "Failed to remove existing block from disk."
case .blockRepositoryGetFilenameAndIsDirectory: return "Failed to get filename and information if url points to directory from file URL."
case .blockRepositoryCreateBlocksCacheDirectory: return "Failed to create blocks cache directory."
case .blockRepositoryReadDirectoryContent: return "Failed to read content of directory."
case .blockRepositoryRemoveBlockAfterRewind: return "Failed to remove block from disk after rewind operation."
case .blockRepositoryRemoveBlocksCacheDirectory: return "Failed to remove blocks cache directory while clearing storage."
case .blockDownloaderServiceDownloadBlockRange: return "Stream downloading the given block range failed."
case .zcashTransactionOverviewInit: return "Initialization of `ZcashTransaction.Overview` failed."
case .zcashTransactionReceivedInit: return "Initialization of `ZcashTransaction.Received` failed."
case .zcashTransactionSentInit: return "Initialization of `ZcashTransaction.Sent` failed."
case .transactionRepositoryEntityNotFound: return "Entity not found in the database, result of `createEntity` execution."
case .transactionRepositoryTransactionMissingRequiredFields: return "`Find` call is missing fields, required fields are transaction `index` and `blockTime`."
case .transactionRepositoryCountAll: return "Counting all transactions failed."
case .transactionRepositoryCountUnmined: return "Counting all unmined transactions failed."
case .transactionRepositoryQueryExecute: return "Execution of a query failed."
case .transactionRepositoryFindMemos: return "Finding memos in the database failed."
case .compactBlockEncode: return "Can't encode `ZcashCompactBlock` object."
case .memoTextInvalidUTF8: return "Invalid UTF-8 Bytes where detected when attempting to create a MemoText."
case .memoTextInputEndsWithNullBytes: return "Trailing null-bytes were found when attempting to create a MemoText."
case .memoTextInputTooLong: return "The resulting bytes provided are too long to be stored as a MemoText."
case .memoBytesInputTooLong: return "The resulting bytes provided are too long to be stored as a MemoBytes."
case .memoBytesInvalidUTF8: return "Invalid UTF-8 Bytes where detected when attempting to convert MemoBytes to Memo."
case .checkpointCantLoadFromDisk: return "Failed to load JSON with checkpoint from disk."
case .checkpointDecode: return "Failed to decode `Checkpoint` object."
case .pendingTransactionDecodeInvalidData: return "Decoding of `PendingTransaction` failed because of specific invalid data."
case .pendingTransactionCantDecode: return "Can't decode `PendingTransaction`."
case .pendingTransactionCantEncode: return "Can't encode `PendingTransaction`."
case .pendingTransactionDAOCreate: return "SQLite query failed when creating pending transaction."
case .pendingTransactionDAOUpdateMissingID: return "Pending transaction which should be updated is missing ID."
case .pendingTransactionDAOUpdate: return "SQLite query failed when updating pending transaction."
case .pendingTransactionDAODeleteMissingID: return "Pending transaction which should be deleted is missing ID."
case .pendingTransactionDAODelete: return "SQLite query failed when deleting pending transaction."
case .pendingTransactionDAOCancelMissingID: return "Pending transaction which should be canceled is missing ID."
case .pendingTransactionDAOCancel: return "SQLite query failed when canceling pending transaction."
case .pendingTransactionDAOFind: return "SQLite query failed when seaching for pending transaction."
case .pendingTransactionDAOGetAll: return "SQLite query failed when getting pending transactions."
case .pendingTransactionDAOApplyMinedHeight: return "SQLite query failed when applying mined height."
case .derivationToolSpendingKeyInvalidAccount: return "Invalid account when trying to derive spending key"
case .unspentTransactionOutputDAOCreateTable: return "Creation of the table for unspent transaction output failed."
case .unspentTransactionOutputDAOStore: return "SQLite query failed when storing unspent transaction output."
case .unspentTransactionOutputDAOClearAll: return "SQLite query failed when removing all the unspent transation outputs."
case .unspentTransactionOutputDAOGetAllCantDecode: return "Fetched information about unspent transaction output from the DB but it can't be decoded to `UTXO` object."
case .unspentTransactionOutputDAOGetAll: return "SQLite query failed when getting all the unspent transation outputs."
case .unspentTransactionOutputDAOBalance: return "SQLite query failed when getting balance."
case .spendingKeyInvalidInput: return "Can't create `SaplingExtendedSpendingKey` because input is invalid."
case .unifiedFullViewingKeyInvalidInput: return "Can't create `UnifiedFullViewingKey` because input is invalid."
case .extetendedFullViewingKeyInvalidInput: return "Can't create `SaplingExtendedFullViewingKey` because input is invalid."
case .transparentAddressInvalidInput: return "Can't create `TransparentAddress` because input is invalid."
case .saplingAddressInvalidInput: return "Can't create `SaplingAddress` because input is invalid."
case .unifiedAddressInvalidInput: return "Can't create `UnifiedAddress` because input is invalid."
case .recipientInvalidInput: return "Can't create `Recipient` because input is invalid."
case .walletTransEncoderCreateTransactionMissingSaplingParams: return "WalletTransactionEncoder wants to create transaction but files with sapling parameters are not present on disk."
case .walletTransEncoderShieldFundsMissingSaplingParams: return "WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk."
case .persistentTransManagerCantCreateTransaction: return "PersistentTransactionsManager cant create transaction."
case .persistentTransManagerEncodeUknownToAddress: return "PersistentTransactionsManager cant get to address from pending transaction."
case .persistentTransManagerSubmitTransactionIDMissing: return "PersistentTransactionsManager wants to submit pending transaction but transaction is missing id."
case .persistentTransManagerSubmitTransactionNotFound: return "PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored."
case .persistentTransManagerSubmitTransactionCanceled: return "PersistentTransactionsManager wants to submit pending transaction but transaction is canceled."
case .persistentTransManagerSubmitTransactionRawDataMissing: return "PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data."
case .persistentTransManagerSubmitFailed: return "PersistentTransactionsManager wants to submit pending transaction but submit API call failed."
case .persistentTransManagerApplyMinedHeightTransactionIDMissing: return "PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored."
case .persistentTransManagerApplyMinedHeightTransactionNotFound: return "PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored."
case .zatoshiDecode: return "Initiatilzation fo `Zatoshi` from a decoder failed."
case .zatoshiEncode: return "Encode of `Zatoshi` failed."
case .unspentTransactionFetcherStream: return "Awaiting transactions from the stream failed."
case .compactBlockProcessorInvalidConfiguration: return "CompactBlockProcessor was started with an invalid configuration."
case .compactBlockProcessorMissingDbPath: return "CompactBlockProcessor was set up with path but that location couldn't be reached."
case .compactBlockProcessorDataDbInitFailed: return "Data Db file couldn't be initialized at path."
case .compactBlockProcessorConnection: return "There's a problem with the network connection."
case .compactBlockProcessorGrpcError: return "Error on gRPC happened."
case .compactBlockProcessorConnectionTimeout: return "Network connection timeout."
case .compactBlockProcessorMaxAttemptsReached: return "Compact Block failed and reached the maximum amount of retries it was set up to do."
case .compactBlockProcessorUnspecified: return "Unspecified error occured."
case .compactBlockProcessorCritical: return "Critical error occured."
case .compactBlockProcessorInvalidAccount: return "Invalid Account."
case .compactBlockProcessorWrongConsensusBranchId: return "The remote server you are connecting to is publishing a different branch ID than the one your App is expecting This could be caused by your App being out of date or the server you are connecting you being either on a different network or out of date after a network upgrade."
case .compactBlockProcessorNetworkMismatch: return "A server was reached, but it's targeting the wrong network Type. Make sure you are pointing to the right server."
case .compactBlockProcessorSaplingActivationMismatch: return "A server was reached, it's showing a different sapling activation. Are you sure you are pointing to the right server?"
case .compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL: return "when the given URL is the same URL than the one provided as `self.fsBlockDbRoot` assuming that's a programming error being the `legacyCacheDbURL` a sqlite database file and not a directory"
case .compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb: return "Deletion of readable file at the provided URL failed."
case .compactBlockProcessorChainName: return "Chain name does not match. Expected either 'test' or 'main'. This is probably an API or programming error."
case .compactBlockProcessorConsensusBranchID: return "Consensus BranchIDs don't match this is probably an API or programming error."
case .synchronizerNotPrepared: return "The synchronizer is unprepared."
case .synchronizerSendMemoToTransparentAddress: return "Memos can't be sent to transparent addresses."
case .synchronizerShieldFundsInsuficientTransparentFunds: return "There is not enough transparent funds to cover fee for the shielding."
case .synchronizerLatestUTXOsInvalidTAddress: return "LatestUTXOs for the address failed, invalid t-address."
case .synchronizerRewindUnknownArchorHeight: return "Rewind failed, unknown archor height"
}
}
public var code: ZcashErrorCode {
switch self {
case .initializerCantUpdateURLWithAlias: return .initializerCantUpdateURLWithAlias
case .initializerAliasAlreadyInUse: return .initializerAliasAlreadyInUse
case .serviceUnknownError: return .serviceUnknownError
case .serviceGetInfoFailed: return .serviceGetInfoFailed
case .serviceLatestBlockFailed: return .serviceLatestBlockFailed
case .serviceLatestBlockHeightFailed: return .serviceLatestBlockHeightFailed
case .serviceBlockRangeFailed: return .serviceBlockRangeFailed
case .serviceSubmitFailed: return .serviceSubmitFailed
case .serviceFetchTransactionFailed: return .serviceFetchTransactionFailed
case .serviceFetchUTXOsFailed: return .serviceFetchUTXOsFailed
case .serviceBlockStreamFailed: return .serviceBlockStreamFailed
case .dbMigrationGenericFailure: return .dbMigrationGenericFailure
case .dbMigrationInvalidVersion: return .dbMigrationInvalidVersion
case .dbMigrationV1: return .dbMigrationV1
case .dbMigrationV2: return .dbMigrationV2
case .simpleConnectionProvider: return .simpleConnectionProvider
case .saplingParamsInvalidSpendParams: return .saplingParamsInvalidSpendParams
case .saplingParamsInvalidOutputParams: return .saplingParamsInvalidOutputParams
case .saplingParamsDownload: return .saplingParamsDownload
case .saplingParamsCantMoveDownloadedFile: return .saplingParamsCantMoveDownloadedFile
case .notesDAOReceivedCount: return .notesDAOReceivedCount
case .notesDAOReceivedNote: return .notesDAOReceivedNote
case .notesDAOReceivedCantDecode: return .notesDAOReceivedCantDecode
case .notesDAOSentCount: return .notesDAOSentCount
case .notesDAOSentNote: return .notesDAOSentNote
case .notesDAOSentCantDecode: return .notesDAOSentCantDecode
case .blockDAOBlock: return .blockDAOBlock
case .blockDAOCantDecode: return .blockDAOCantDecode
case .blockDAOLatestBlockHeight: return .blockDAOLatestBlockHeight
case .blockDAOLatestBlock: return .blockDAOLatestBlock
case .blockDAOLatestBlockCantDecode: return .blockDAOLatestBlockCantDecode
case .rustCreateAccount: return .rustCreateAccount
case .rustCreateToAddress: return .rustCreateToAddress
case .rustDecryptAndStoreTransaction: return .rustDecryptAndStoreTransaction
case .rustGetBalance: return .rustGetBalance
case .rustGetCurrentAddress: return .rustGetCurrentAddress
case .rustGetCurrentAddressInvalidAddress: return .rustGetCurrentAddressInvalidAddress
case .rustGetNearestRewindHeight: return .rustGetNearestRewindHeight
case .rustGetNextAvailableAddress: return .rustGetNextAvailableAddress
case .rustGetNextAvailableAddressInvalidAddress: return .rustGetNextAvailableAddressInvalidAddress
case .rustGetTransparentBalanceNegativeAccount: return .rustGetTransparentBalanceNegativeAccount
case .rustGetTransparentBalance: return .rustGetTransparentBalance
case .rustGetVerifiedBalance: return .rustGetVerifiedBalance
case .rustGetVerifiedTransparentBalanceNegativeAccount: return .rustGetVerifiedTransparentBalanceNegativeAccount
case .rustGetVerifiedTransparentBalance: return .rustGetVerifiedTransparentBalance
case .rustInitDataDb: return .rustInitDataDb
case .rustInitAccountsTableViewingKeyCotainsNullBytes: return .rustInitAccountsTableViewingKeyCotainsNullBytes
case .rustInitAccountsTableViewingKeyIsInvalid: return .rustInitAccountsTableViewingKeyIsInvalid
case .rustInitAccountsTableDataDbNotEmpty: return .rustInitAccountsTableDataDbNotEmpty
case .rustInitAccountsTable: return .rustInitAccountsTable
case .rustInitBlockMetadataDb: return .rustInitBlockMetadataDb
case .rustWriteBlocksMetadataAllocationProblem: return .rustWriteBlocksMetadataAllocationProblem
case .rustWriteBlocksMetadata: return .rustWriteBlocksMetadata
case .rustInitBlocksTableHashContainsNullBytes: return .rustInitBlocksTableHashContainsNullBytes
case .rustInitBlocksTableSaplingTreeContainsNullBytes: return .rustInitBlocksTableSaplingTreeContainsNullBytes
case .rustInitBlocksTableDataDbNotEmpty: return .rustInitBlocksTableDataDbNotEmpty
case .rustInitBlocksTable: return .rustInitBlocksTable
case .rustListTransparentReceivers: return .rustListTransparentReceivers
case .rustListTransparentReceiversInvalidAddress: return .rustListTransparentReceiversInvalidAddress
case .rustPutUnspentTransparentOutput: return .rustPutUnspentTransparentOutput
case .rustValidateCombinedChainValidationFailed: return .rustValidateCombinedChainValidationFailed
case .rustValidateCombinedChainInvalidChain: return .rustValidateCombinedChainInvalidChain
case .rustRewindToHeight: return .rustRewindToHeight
case .rustRewindCacheToHeight: return .rustRewindCacheToHeight
case .rustScanBlocks: return .rustScanBlocks
case .rustShieldFunds: return .rustShieldFunds
case .rustNoConsensusBranchId: return .rustNoConsensusBranchId
case .rustReceiverTypecodesOnUnifiedAddressContainsNullBytes: return .rustReceiverTypecodesOnUnifiedAddressContainsNullBytes
case .rustRustReceiverTypecodesOnUnifiedAddressMalformed: return .rustRustReceiverTypecodesOnUnifiedAddressMalformed
case .rustDeriveUnifiedSpendingKey: return .rustDeriveUnifiedSpendingKey
case .rustDeriveUnifiedFullViewingKey: return .rustDeriveUnifiedFullViewingKey
case .rustDeriveUnifiedFullViewingKeyInvalidDerivedKey: return .rustDeriveUnifiedFullViewingKeyInvalidDerivedKey
case .rustGetSaplingReceiverInvalidAddress: return .rustGetSaplingReceiverInvalidAddress
case .rustGetSaplingReceiverInvalidReceiver: return .rustGetSaplingReceiverInvalidReceiver
case .rustGetTransparentReceiverInvalidAddress: return .rustGetTransparentReceiverInvalidAddress
case .rustGetTransparentReceiverInvalidReceiver: return .rustGetTransparentReceiverInvalidReceiver
case .accountDAOGetAll: return .accountDAOGetAll
case .accountDAOGetAllCantDecode: return .accountDAOGetAllCantDecode
case .accountDAOFindBy: return .accountDAOFindBy
case .accountDAOFindByCantDecode: return .accountDAOFindByCantDecode
case .accountDAOUpdateInvalidAccount: return .accountDAOUpdateInvalidAccount
case .accountDAOUpdate: return .accountDAOUpdate
case .accountDAOUpdatedZeroRows: return .accountDAOUpdatedZeroRows
case .blockRepositoryWriteBlock: return .blockRepositoryWriteBlock
case .blockRepositoryGetFilename: return .blockRepositoryGetFilename
case .blockRepositoryParseHeightFromFilename: return .blockRepositoryParseHeightFromFilename
case .blockRepositoryRemoveExistingBlock: return .blockRepositoryRemoveExistingBlock
case .blockRepositoryGetFilenameAndIsDirectory: return .blockRepositoryGetFilenameAndIsDirectory
case .blockRepositoryCreateBlocksCacheDirectory: return .blockRepositoryCreateBlocksCacheDirectory
case .blockRepositoryReadDirectoryContent: return .blockRepositoryReadDirectoryContent
case .blockRepositoryRemoveBlockAfterRewind: return .blockRepositoryRemoveBlockAfterRewind
case .blockRepositoryRemoveBlocksCacheDirectory: return .blockRepositoryRemoveBlocksCacheDirectory
case .blockDownloaderServiceDownloadBlockRange: return .blockDownloaderServiceDownloadBlockRange
case .zcashTransactionOverviewInit: return .zcashTransactionOverviewInit
case .zcashTransactionReceivedInit: return .zcashTransactionReceivedInit
case .zcashTransactionSentInit: return .zcashTransactionSentInit
case .transactionRepositoryEntityNotFound: return .transactionRepositoryEntityNotFound
case .transactionRepositoryTransactionMissingRequiredFields: return .transactionRepositoryTransactionMissingRequiredFields
case .transactionRepositoryCountAll: return .transactionRepositoryCountAll
case .transactionRepositoryCountUnmined: return .transactionRepositoryCountUnmined
case .transactionRepositoryQueryExecute: return .transactionRepositoryQueryExecute
case .transactionRepositoryFindMemos: return .transactionRepositoryFindMemos
case .compactBlockEncode: return .compactBlockEncode
case .memoTextInvalidUTF8: return .memoTextInvalidUTF8
case .memoTextInputEndsWithNullBytes: return .memoTextInputEndsWithNullBytes
case .memoTextInputTooLong: return .memoTextInputTooLong
case .memoBytesInputTooLong: return .memoBytesInputTooLong
case .memoBytesInvalidUTF8: return .memoBytesInvalidUTF8
case .checkpointCantLoadFromDisk: return .checkpointCantLoadFromDisk
case .checkpointDecode: return .checkpointDecode
case .pendingTransactionDecodeInvalidData: return .pendingTransactionDecodeInvalidData
case .pendingTransactionCantDecode: return .pendingTransactionCantDecode
case .pendingTransactionCantEncode: return .pendingTransactionCantEncode
case .pendingTransactionDAOCreate: return .pendingTransactionDAOCreate
case .pendingTransactionDAOUpdateMissingID: return .pendingTransactionDAOUpdateMissingID
case .pendingTransactionDAOUpdate: return .pendingTransactionDAOUpdate
case .pendingTransactionDAODeleteMissingID: return .pendingTransactionDAODeleteMissingID
case .pendingTransactionDAODelete: return .pendingTransactionDAODelete
case .pendingTransactionDAOCancelMissingID: return .pendingTransactionDAOCancelMissingID
case .pendingTransactionDAOCancel: return .pendingTransactionDAOCancel
case .pendingTransactionDAOFind: return .pendingTransactionDAOFind
case .pendingTransactionDAOGetAll: return .pendingTransactionDAOGetAll
case .pendingTransactionDAOApplyMinedHeight: return .pendingTransactionDAOApplyMinedHeight
case .derivationToolSpendingKeyInvalidAccount: return .derivationToolSpendingKeyInvalidAccount
case .unspentTransactionOutputDAOCreateTable: return .unspentTransactionOutputDAOCreateTable
case .unspentTransactionOutputDAOStore: return .unspentTransactionOutputDAOStore
case .unspentTransactionOutputDAOClearAll: return .unspentTransactionOutputDAOClearAll
case .unspentTransactionOutputDAOGetAllCantDecode: return .unspentTransactionOutputDAOGetAllCantDecode
case .unspentTransactionOutputDAOGetAll: return .unspentTransactionOutputDAOGetAll
case .unspentTransactionOutputDAOBalance: return .unspentTransactionOutputDAOBalance
case .spendingKeyInvalidInput: return .spendingKeyInvalidInput
case .unifiedFullViewingKeyInvalidInput: return .unifiedFullViewingKeyInvalidInput
case .extetendedFullViewingKeyInvalidInput: return .extetendedFullViewingKeyInvalidInput
case .transparentAddressInvalidInput: return .transparentAddressInvalidInput
case .saplingAddressInvalidInput: return .saplingAddressInvalidInput
case .unifiedAddressInvalidInput: return .unifiedAddressInvalidInput
case .recipientInvalidInput: return .recipientInvalidInput
case .walletTransEncoderCreateTransactionMissingSaplingParams: return .walletTransEncoderCreateTransactionMissingSaplingParams
case .walletTransEncoderShieldFundsMissingSaplingParams: return .walletTransEncoderShieldFundsMissingSaplingParams
case .persistentTransManagerCantCreateTransaction: return .persistentTransManagerCantCreateTransaction
case .persistentTransManagerEncodeUknownToAddress: return .persistentTransManagerEncodeUknownToAddress
case .persistentTransManagerSubmitTransactionIDMissing: return .persistentTransManagerSubmitTransactionIDMissing
case .persistentTransManagerSubmitTransactionNotFound: return .persistentTransManagerSubmitTransactionNotFound
case .persistentTransManagerSubmitTransactionCanceled: return .persistentTransManagerSubmitTransactionCanceled
case .persistentTransManagerSubmitTransactionRawDataMissing: return .persistentTransManagerSubmitTransactionRawDataMissing
case .persistentTransManagerSubmitFailed: return .persistentTransManagerSubmitFailed
case .persistentTransManagerApplyMinedHeightTransactionIDMissing: return .persistentTransManagerApplyMinedHeightTransactionIDMissing
case .persistentTransManagerApplyMinedHeightTransactionNotFound: return .persistentTransManagerApplyMinedHeightTransactionNotFound
case .zatoshiDecode: return .zatoshiDecode
case .zatoshiEncode: return .zatoshiEncode
case .unspentTransactionFetcherStream: return .unspentTransactionFetcherStream
case .compactBlockProcessorInvalidConfiguration: return .compactBlockProcessorInvalidConfiguration
case .compactBlockProcessorMissingDbPath: return .compactBlockProcessorMissingDbPath
case .compactBlockProcessorDataDbInitFailed: return .compactBlockProcessorDataDbInitFailed
case .compactBlockProcessorConnection: return .compactBlockProcessorConnection
case .compactBlockProcessorGrpcError: return .compactBlockProcessorGrpcError
case .compactBlockProcessorConnectionTimeout: return .compactBlockProcessorConnectionTimeout
case .compactBlockProcessorMaxAttemptsReached: return .compactBlockProcessorMaxAttemptsReached
case .compactBlockProcessorUnspecified: return .compactBlockProcessorUnspecified
case .compactBlockProcessorCritical: return .compactBlockProcessorCritical
case .compactBlockProcessorInvalidAccount: return .compactBlockProcessorInvalidAccount
case .compactBlockProcessorWrongConsensusBranchId: return .compactBlockProcessorWrongConsensusBranchId
case .compactBlockProcessorNetworkMismatch: return .compactBlockProcessorNetworkMismatch
case .compactBlockProcessorSaplingActivationMismatch: return .compactBlockProcessorSaplingActivationMismatch
case .compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL: return .compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL
case .compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb: return .compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb
case .compactBlockProcessorChainName: return .compactBlockProcessorChainName
case .compactBlockProcessorConsensusBranchID: return .compactBlockProcessorConsensusBranchID
case .synchronizerNotPrepared: return .synchronizerNotPrepared
case .synchronizerSendMemoToTransparentAddress: return .synchronizerSendMemoToTransparentAddress
case .synchronizerShieldFundsInsuficientTransparentFunds: return .synchronizerShieldFundsInsuficientTransparentFunds
case .synchronizerLatestUTXOsInvalidTAddress: return .synchronizerLatestUTXOsInvalidTAddress
case .synchronizerRewindUnknownArchorHeight: return .synchronizerRewindUnknownArchorHeight
}
}
public static func == (lhs: ZcashError, rhs: ZcashError) -> Bool {
return lhs.code == rhs.code
}
}

View File

@ -0,0 +1,358 @@
// Generated using Sourcery 2.0.2 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
/*
!!!!! To edit this file go to ZcashErrorCodeDefinition first and udate/add codes. Then run generateErrorCode.sh script to regenerate this file.
By design each error code should be used only at one place in the app. Thanks to that it is possible to identify exact line in the code from which the
error originates. And it can help with debugging.
*/
public enum ZcashErrorCode: String {
/// Updating of paths in `Initilizer` according to alias failed.
case initializerCantUpdateURLWithAlias = "ZINIT0001"
/// Alias used to create this instance of the `SDKSynchronizer` is already used by other instance.
case initializerAliasAlreadyInUse = "ZINIT0002"
/// Unknown GRPC Service error
case serviceUnknownError = "ZSRVC0001"
/// LightWalletService.getInfo failed.
case serviceGetInfoFailed = "ZSRVC0002"
/// LightWalletService.latestBlock failed.
case serviceLatestBlockFailed = "ZSRVC0003"
/// LightWalletService.latestBlockHeight failed.
case serviceLatestBlockHeightFailed = "ZSRVC0004"
/// LightWalletService.blockRange failed.
case serviceBlockRangeFailed = "ZSRVC0005"
/// LightWalletService.submit failed.
case serviceSubmitFailed = "ZSRVC0006"
/// LightWalletService.fetchTransaction failed.
case serviceFetchTransactionFailed = "ZSRVC0007"
/// LightWalletService.fetchUTXOs failed.
case serviceFetchUTXOsFailed = "ZSRVC0008"
/// LightWalletService.blockStream failed.
case serviceBlockStreamFailed = "ZSRVC0000"
/// Migration of the pending DB failed because of unspecific reason.
case dbMigrationGenericFailure = "ZDBMG0001"
/// Migration of the pending DB failed because unknown version of the existing database.
case dbMigrationInvalidVersion = "ZDBMG00002"
/// Migration of the pending DB to version 1 failed.
case dbMigrationV1 = "ZDBMG00003"
/// Migration of the pending DB to version 2 failed.
case dbMigrationV2 = "ZDBMG00004"
/// SimpleConnectionProvider init of Connection failed.
case simpleConnectionProvider = "ZSCPC0001"
/// Downloaded file with sapling spending parameters isn't valid.
case saplingParamsInvalidSpendParams = "ZSAPP0001"
/// Downloaded file with sapling output parameters isn't valid.
case saplingParamsInvalidOutputParams = "ZSAPP0002"
/// Failed to download sapling parameters file
case saplingParamsDownload = "ZSAPP0003"
/// Failed to move sapling parameters file to final destination after download.
case saplingParamsCantMoveDownloadedFile = "ZSAPP0004"
/// SQLite query failed when fetching received notes count from the database.
case notesDAOReceivedCount = "ZNDAO0001"
/// SQLite query failed when fetching received notes from the database.
case notesDAOReceivedNote = "ZNDAO0002"
/// Fetched note from the SQLite but can't decode that.
case notesDAOReceivedCantDecode = "ZNDAO0003"
/// SQLite query failed when fetching sent notes count from the database.
case notesDAOSentCount = "ZNDAO0004"
/// SQLite query failed when fetching sent notes from the database.
case notesDAOSentNote = "ZNDAO0005"
/// Fetched note from the SQLite but can't decode that.
case notesDAOSentCantDecode = "ZNDAO0006"
/// SQLite query failed when fetching block information from database.
case blockDAOBlock = "ZBDAO0001"
/// Fetched block information from DB but can't decode them.
case blockDAOCantDecode = "ZBDAO0002"
/// SQLite query failed when fetching height of the latest block from the database.
case blockDAOLatestBlockHeight = "ZBDAO0003"
/// SQLite query failed when fetching the latest block from the database.
case blockDAOLatestBlock = "ZBDAO0004"
/// Fetched latesxt block information from DB but can't decode them.
case blockDAOLatestBlockCantDecode = "ZBDAO0005"
/// Error from rust layer when calling ZcashRustBackend.createAccount
case rustCreateAccount = "ZRUST0001"
/// Error from rust layer when calling ZcashRustBackend.createToAddress
case rustCreateToAddress = "ZRUST0002"
/// Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction
case rustDecryptAndStoreTransaction = "ZRUST0003"
/// Error from rust layer when calling ZcashRustBackend.getBalance
case rustGetBalance = "ZRUST0004"
/// Error from rust layer when calling ZcashRustBackend.getCurrentAddress
case rustGetCurrentAddress = "ZRUST0005"
/// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress
case rustGetCurrentAddressInvalidAddress = "ZRUST0006"
/// Error from rust layer when calling ZcashRustBackend.getNearestRewindHeight
case rustGetNearestRewindHeight = "ZRUST0007"
/// Error from rust layer when calling ZcashRustBackend.getNextAvailableAddress
case rustGetNextAvailableAddress = "ZRUST0008"
/// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getNextAvailableAddress
case rustGetNextAvailableAddressInvalidAddress = "ZRUST0009"
/// account parameter is lower than 0 when calling ZcashRustBackend.getTransparentBalance
case rustGetTransparentBalanceNegativeAccount = "ZRUST0010"
/// Error from rust layer when calling ZcashRustBackend.getTransparentBalance
case rustGetTransparentBalance = "ZRUST0011"
/// Error from rust layer when calling ZcashRustBackend.getVerifiedBalance
case rustGetVerifiedBalance = "ZRUST0012"
/// account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance
case rustGetVerifiedTransparentBalanceNegativeAccount = "ZRUST0013"
/// Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance
case rustGetVerifiedTransparentBalance = "ZRUST0014"
/// Error from rust layer when calling ZcashRustBackend.initDataDb
case rustInitDataDb = "ZRUST0015"
/// Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method contains null bytes before end
case rustInitAccountsTableViewingKeyCotainsNullBytes = "ZRUST0016"
/// Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method isn't valid
case rustInitAccountsTableViewingKeyIsInvalid = "ZRUST0017"
/// Error from rust layer when calling ZcashRustBackend.initAccountsTable
case rustInitAccountsTableDataDbNotEmpty = "ZRUST0018"
/// Error from rust layer when calling ZcashRustBackend.initAccountsTable
case rustInitAccountsTable = "ZRUST0019"
/// Error from rust layer when calling ZcashRustBackend.initBlockMetadataDb
case rustInitBlockMetadataDb = "ZRUST0020"
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.writeBlocksMetadata
case rustWriteBlocksMetadataAllocationProblem = "ZRUST0021"
/// Error from rust layer when calling ZcashRustBackend.writeBlocksMetadata
case rustWriteBlocksMetadata = "ZRUST0022"
/// hash passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end
case rustInitBlocksTableHashContainsNullBytes = "ZRUST0023"
/// saplingTree passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end
case rustInitBlocksTableSaplingTreeContainsNullBytes = "ZRUST0024"
/// Error from rust layer when calling ZcashRustBackend.initBlocksTable
case rustInitBlocksTableDataDbNotEmpty = "ZRUST0025"
/// Error from rust layer when calling ZcashRustBackend.initBlocksTable
case rustInitBlocksTable = "ZRUST0026"
/// Error from rust layer when calling ZcashRustBackend.listTransparentReceivers
case rustListTransparentReceivers = "ZRUST0027"
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.listTransparentReceivers
case rustListTransparentReceiversInvalidAddress = "ZRUST0028"
/// Error from rust layer when calling ZcashRustBackend.putUnspentTransparentOutput
case rustPutUnspentTransparentOutput = "ZRUST0029"
/// Error unrelated to chain validity from rust layer when calling ZcashRustBackend.validateCombinedChain
case rustValidateCombinedChainValidationFailed = "ZRUST0030"
/// Error from rust layer which means that combined chain isn't valid.
case rustValidateCombinedChainInvalidChain = "ZRUST0031"
/// Error from rust layer when calling ZcashRustBackend.rewindToHeight
case rustRewindToHeight = "ZRUST0032"
/// Error from rust layer when calling ZcashRustBackend.rewindCacheToHeight
case rustRewindCacheToHeight = "ZRUST0033"
/// Error from rust layer when calling ZcashRustBackend.scanBlocks
case rustScanBlocks = "ZRUST0034"
/// Error from rust layer when calling ZcashRustBackend.shieldFunds
case rustShieldFunds = "ZRUST0035"
/// Error from rust layer when calling ZcashRustBackend.consensusBranchIdFor
case rustNoConsensusBranchId = "ZRUST0036"
/// address passed to the ZcashRustBackend.receiverTypecodesOnUnifiedAddress method contains null bytes before end
case rustReceiverTypecodesOnUnifiedAddressContainsNullBytes = "ZRUST0037"
/// Error from rust layer when calling ZcashRustBackend.receiverTypecodesOnUnifiedAddress
case rustRustReceiverTypecodesOnUnifiedAddressMalformed = "ZRUST0038"
/// Error from rust layer when calling ZcashRustBackend.deriveUnifiedSpendingKey
case rustDeriveUnifiedSpendingKey = "ZRUST0039"
/// Error from rust layer when calling ZcashRustBackend.deriveUnifiedFullViewingKey
case rustDeriveUnifiedFullViewingKey = "ZRUST0040"
/// Viewing key derived by rust layer is invalid when calling ZcashRustBackend.deriveUnifiedFullViewingKey
case rustDeriveUnifiedFullViewingKeyInvalidDerivedKey = "ZRUST0041"
/// Error from rust layer when calling ZcashRustBackend.getSaplingReceiver
case rustGetSaplingReceiverInvalidAddress = "ZRUST0042"
/// Sapling receiver generated by rust layer is invalid when calling ZcashRustBackend.getSaplingReceiver
case rustGetSaplingReceiverInvalidReceiver = "ZRUST0043"
/// Error from rust layer when calling ZcashRustBackend.getTransparentReceiver
case rustGetTransparentReceiverInvalidAddress = "ZRUST0044"
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver
case rustGetTransparentReceiverInvalidReceiver = "ZRUST0045"
/// SQLite query failed when fetching all accounts from the database.
case accountDAOGetAll = "ZADAO0001"
/// Fetched accounts from SQLite but can't decode them.
case accountDAOGetAllCantDecode = "ZADAO0002"
/// SQLite query failed when seaching for accounts in the database.
case accountDAOFindBy = "ZADAO0003"
/// Fetched accounts from SQLite but can't decode them.
case accountDAOFindByCantDecode = "ZADAO0004"
/// Object passed to update() method conforms to `AccountEntity` protocol but isn't exactly `Account` type.
case accountDAOUpdateInvalidAccount = "ZADAO0005"
/// SQLite query failed when updating account in the database.
case accountDAOUpdate = "ZADAO0006"
/// Update of the account updated 0 rows in the database. One row should be updated.
case accountDAOUpdatedZeroRows = "ZADAO0007"
/// Failed to write block to disk.
case blockRepositoryWriteBlock = "ZBLRP00001"
/// Failed to get filename for the block from file URL.
case blockRepositoryGetFilename = "ZBLRP0002"
/// Failed to parse block height from filename.
case blockRepositoryParseHeightFromFilename = "ZBLRP0003"
/// Failed to remove existing block from disk.
case blockRepositoryRemoveExistingBlock = "ZBLRP0004"
/// Failed to get filename and information if url points to directory from file URL.
case blockRepositoryGetFilenameAndIsDirectory = "ZBLRP0005"
/// Failed to create blocks cache directory.
case blockRepositoryCreateBlocksCacheDirectory = "ZBLRP0006"
/// Failed to read content of directory.
case blockRepositoryReadDirectoryContent = "ZBLRP0007"
/// Failed to remove block from disk after rewind operation.
case blockRepositoryRemoveBlockAfterRewind = "ZBLRP0008"
/// Failed to remove blocks cache directory while clearing storage.
case blockRepositoryRemoveBlocksCacheDirectory = "ZBLRP0009"
/// Stream downloading the given block range failed.
case blockDownloaderServiceDownloadBlockRange = "ZBDSEO0001"
/// Initialization of `ZcashTransaction.Overview` failed.
case zcashTransactionOverviewInit = "ZTEZT0001"
/// Initialization of `ZcashTransaction.Received` failed.
case zcashTransactionReceivedInit = "ZTEZT0002"
/// Initialization of `ZcashTransaction.Sent` failed.
case zcashTransactionSentInit = "ZTEZT0003"
/// Entity not found in the database, result of `createEntity` execution.
case transactionRepositoryEntityNotFound = "ZTREE0001"
/// `Find` call is missing fields, required fields are transaction `index` and `blockTime`.
case transactionRepositoryTransactionMissingRequiredFields = "ZTREE0002"
/// Counting all transactions failed.
case transactionRepositoryCountAll = "ZTREE0003"
/// Counting all unmined transactions failed.
case transactionRepositoryCountUnmined = "ZTREE0004"
/// Execution of a query failed.
case transactionRepositoryQueryExecute = "ZTREE0005"
/// Finding memos in the database failed.
case transactionRepositoryFindMemos = "ZTREE0006"
/// Can't encode `ZcashCompactBlock` object.
case compactBlockEncode = "ZCMPB0001"
/// Invalid UTF-8 Bytes where detected when attempting to create a MemoText.
case memoTextInvalidUTF8 = "ZMEMO0001"
/// Trailing null-bytes were found when attempting to create a MemoText.
case memoTextInputEndsWithNullBytes = "ZMEMO0002"
/// The resulting bytes provided are too long to be stored as a MemoText.
case memoTextInputTooLong = "ZMEMO0003"
/// The resulting bytes provided are too long to be stored as a MemoBytes.
case memoBytesInputTooLong = "ZMEMO0004"
/// Invalid UTF-8 Bytes where detected when attempting to convert MemoBytes to Memo.
case memoBytesInvalidUTF8 = "ZMEMO0005"
/// Failed to load JSON with checkpoint from disk.
case checkpointCantLoadFromDisk = "ZCHKP0001"
/// Failed to decode `Checkpoint` object.
case checkpointDecode = "ZCHKP0002"
/// Decoding of `PendingTransaction` failed because of specific invalid data.
case pendingTransactionDecodeInvalidData = "ZPETR0001"
/// Can't decode `PendingTransaction`.
case pendingTransactionCantDecode = "ZPETR0002"
/// Can't encode `PendingTransaction`.
case pendingTransactionCantEncode = "ZPETR0003"
/// SQLite query failed when creating pending transaction.
case pendingTransactionDAOCreate = "ZPETR0004"
/// Pending transaction which should be updated is missing ID.
case pendingTransactionDAOUpdateMissingID = "ZPETR0005"
/// SQLite query failed when updating pending transaction.
case pendingTransactionDAOUpdate = "ZPETR0006"
/// Pending transaction which should be deleted is missing ID.
case pendingTransactionDAODeleteMissingID = "ZPETR0007"
/// SQLite query failed when deleting pending transaction.
case pendingTransactionDAODelete = "ZPETR0008"
/// Pending transaction which should be canceled is missing ID.
case pendingTransactionDAOCancelMissingID = "ZPETR0009"
/// SQLite query failed when canceling pending transaction.
case pendingTransactionDAOCancel = "ZPETR0010"
/// SQLite query failed when seaching for pending transaction.
case pendingTransactionDAOFind = "ZPETR0011"
/// SQLite query failed when getting pending transactions.
case pendingTransactionDAOGetAll = "ZPETR0012"
/// SQLite query failed when applying mined height.
case pendingTransactionDAOApplyMinedHeight = "ZPETR0013"
/// Invalid account when trying to derive spending key
case derivationToolSpendingKeyInvalidAccount = "ZDRVT0001"
/// Creation of the table for unspent transaction output failed.
case unspentTransactionOutputDAOCreateTable = "ZUTOD0001"
/// SQLite query failed when storing unspent transaction output.
case unspentTransactionOutputDAOStore = "ZUTOD0002"
/// SQLite query failed when removing all the unspent transation outputs.
case unspentTransactionOutputDAOClearAll = "ZUTOD0003"
/// Fetched information about unspent transaction output from the DB but it can't be decoded to `UTXO` object.
case unspentTransactionOutputDAOGetAllCantDecode = "ZUTOD0004"
/// SQLite query failed when getting all the unspent transation outputs.
case unspentTransactionOutputDAOGetAll = "ZUTOD0005"
/// SQLite query failed when getting balance.
case unspentTransactionOutputDAOBalance = "ZUTOD0006"
/// Can't create `SaplingExtendedSpendingKey` because input is invalid.
case spendingKeyInvalidInput = "ZWLTP0001"
/// Can't create `UnifiedFullViewingKey` because input is invalid.
case unifiedFullViewingKeyInvalidInput = "ZWLTP0002"
/// Can't create `SaplingExtendedFullViewingKey` because input is invalid.
case extetendedFullViewingKeyInvalidInput = "ZWLTP0003"
/// Can't create `TransparentAddress` because input is invalid.
case transparentAddressInvalidInput = "ZWLTP0004"
/// Can't create `SaplingAddress` because input is invalid.
case saplingAddressInvalidInput = "ZWLTP0005"
/// Can't create `UnifiedAddress` because input is invalid.
case unifiedAddressInvalidInput = "ZWLTP0006"
/// Can't create `Recipient` because input is invalid.
case recipientInvalidInput = "ZWLTP0007"
/// WalletTransactionEncoder wants to create transaction but files with sapling parameters are not present on disk.
case walletTransEncoderCreateTransactionMissingSaplingParams = "ZWLTE0001"
/// WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk.
case walletTransEncoderShieldFundsMissingSaplingParams = "ZWLTE0002"
/// PersistentTransactionsManager cant create transaction.
case persistentTransManagerCantCreateTransaction = "ZPTRM0001"
/// PersistentTransactionsManager cant get to address from pending transaction.
case persistentTransManagerEncodeUknownToAddress = "ZPTRM0002"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id.
case persistentTransManagerSubmitTransactionIDMissing = "ZPTRM0003"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored.
case persistentTransManagerSubmitTransactionNotFound = "ZPTRM0004"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is canceled.
case persistentTransManagerSubmitTransactionCanceled = "ZPTRM0005"
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data.
case persistentTransManagerSubmitTransactionRawDataMissing = "ZPTRM0006"
/// PersistentTransactionsManager wants to submit pending transaction but submit API call failed.
case persistentTransManagerSubmitFailed = "ZPTRM0007"
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored.
case persistentTransManagerApplyMinedHeightTransactionIDMissing = "ZPTRM0008"
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored.
case persistentTransManagerApplyMinedHeightTransactionNotFound = "ZPTRM0009"
/// Initiatilzation fo `Zatoshi` from a decoder failed.
case zatoshiDecode = "ZTSHO0001"
/// Encode of `Zatoshi` failed.
case zatoshiEncode = "ZTSHO0002"
/// Awaiting transactions from the stream failed.
case unspentTransactionFetcherStream = "ZUTXO0001"
/// CompactBlockProcessor was started with an invalid configuration.
case compactBlockProcessorInvalidConfiguration = "ZCBPEO0001"
/// CompactBlockProcessor was set up with path but that location couldn't be reached.
case compactBlockProcessorMissingDbPath = "ZCBPEO0002"
/// Data Db file couldn't be initialized at path.
case compactBlockProcessorDataDbInitFailed = "ZCBPEO0003"
/// There's a problem with the network connection.
case compactBlockProcessorConnection = "ZCBPEO0004"
/// Error on gRPC happened.
case compactBlockProcessorGrpcError = "ZCBPEO0005"
/// Network connection timeout.
case compactBlockProcessorConnectionTimeout = "ZCBPEO0006"
/// Compact Block failed and reached the maximum amount of retries it was set up to do.
case compactBlockProcessorMaxAttemptsReached = "ZCBPEO0007"
/// Unspecified error occured.
case compactBlockProcessorUnspecified = "ZCBPEO0008"
/// Critical error occured.
case compactBlockProcessorCritical = "ZCBPEO0009"
/// Invalid Account.
case compactBlockProcessorInvalidAccount = "ZCBPEO0010"
/// The remote server you are connecting to is publishing a different branch ID than the one your App is expecting This could be caused by your App being out of date or the server you are connecting you being either on a different network or out of date after a network upgrade.
case compactBlockProcessorWrongConsensusBranchId = "ZCBPEO0011"
/// A server was reached, but it's targeting the wrong network Type. Make sure you are pointing to the right server.
case compactBlockProcessorNetworkMismatch = "ZCBPEO0012"
/// A server was reached, it's showing a different sapling activation. Are you sure you are pointing to the right server?
case compactBlockProcessorSaplingActivationMismatch = "ZCBPEO0013"
/// when the given URL is the same URL than the one provided as `self.fsBlockDbRoot` assuming that's a programming error being the `legacyCacheDbURL` a sqlite database file and not a directory
case compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL = "ZCBPEO0014"
/// Deletion of readable file at the provided URL failed.
case compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb = "ZCBPEO0015"
/// Chain name does not match. Expected either 'test' or 'main'. This is probably an API or programming error.
case compactBlockProcessorChainName = "ZCBPEO0016"
/// Consensus BranchIDs don't match this is probably an API or programming error.
case compactBlockProcessorConsensusBranchID = "ZCBPEO0017"
/// The synchronizer is unprepared.
case synchronizerNotPrepared = "ZSYNCO0001"
/// Memos can't be sent to transparent addresses.
case synchronizerSendMemoToTransparentAddress = "ZSYNCO0002"
/// There is not enough transparent funds to cover fee for the shielding.
case synchronizerShieldFundsInsuficientTransparentFunds = "ZSYNCO0003"
/// LatestUTXOs for the address failed, invalid t-address.
case synchronizerLatestUTXOsInvalidTAddress = "ZSYNCO0004"
/// Rewind failed, unknown archor height
case synchronizerRewindUnknownArchorHeight = "ZSYNCO0005"
}

View File

@ -0,0 +1,695 @@
//
// ZcashErrorDefinition.swift
//
//
// Created by Michal Fousek on 10.04.2023.
//
import Foundation
// swiftlint:disable identifier_name
/*
This enum won't every be directly used in the code. It is just definition of errors and it used as source for Sourcery. And the files used in the
code are then generated. Check `ZcashError` and `ZcashErrorCode`.
Please pay attention how each error is defined here. Important part is to raw code for each error. And it's important to use /// for the
documentation and only // for the sourcery command.
First line of documentation for each error will be used in automatically generated `message` property.
Error code should always start with `Z` letter. Then there should be 0-4 letters that marks in which part of the SDK is the code used. And then 4
numbers. This is suggestion to keep some order when it comes to error codes. Each code must be unique. `ZcashErrorCode` enum is generated from these
codes. So if the code isn't unique generated code won't compile.
*/
enum ZcashErrorDefinition {
// MARK: - Initializer
/// Updating of paths in `Initilizer` according to alias failed.
// sourcery: code="ZINIT0001"
case initializerCantUpdateURLWithAlias(_ url: URL)
/// Alias used to create this instance of the `SDKSynchronizer` is already used by other instance.
// sourcery: code="ZINIT0002"
case initializerAliasAlreadyInUse(_ alias: ZcashSynchronizerAlias)
// MARK: - LightWalletService
/// Unknown GRPC Service error
// sourcery: code="ZSRVC0001"
case serviceUnknownError(_ error: Error)
/// LightWalletService.getInfo failed.
// sourcery: code="ZSRVC0002"
case serviceGetInfoFailed(_ error: LightWalletServiceError)
/// LightWalletService.latestBlock failed.
// sourcery: code="ZSRVC0003"
case serviceLatestBlockFailed(_ error: LightWalletServiceError)
/// LightWalletService.latestBlockHeight failed.
// sourcery: code="ZSRVC0004"
case serviceLatestBlockHeightFailed(_ error: LightWalletServiceError)
/// LightWalletService.blockRange failed.
// sourcery: code="ZSRVC0005"
case serviceBlockRangeFailed(_ error: LightWalletServiceError)
/// LightWalletService.submit failed.
// sourcery: code="ZSRVC0006"
case serviceSubmitFailed(_ error: LightWalletServiceError)
/// LightWalletService.fetchTransaction failed.
// sourcery: code="ZSRVC0007"
case serviceFetchTransactionFailed(_ error: LightWalletServiceError)
/// LightWalletService.fetchUTXOs failed.
// sourcery: code="ZSRVC0008"
case serviceFetchUTXOsFailed(_ error: LightWalletServiceError)
/// LightWalletService.blockStream failed.
// sourcery: code="ZSRVC0000"
case serviceBlockStreamFailed(_ error: LightWalletServiceError)
// MARK: - DB Migration
/// Migration of the pending DB failed because of unspecific reason.
// sourcery: code="ZDBMG0001"
case dbMigrationGenericFailure(_ error: Error)
/// Migration of the pending DB failed because unknown version of the existing database.
// sourcery: code="ZDBMG00002"
case dbMigrationInvalidVersion
/// Migration of the pending DB to version 1 failed.
// sourcery: code="ZDBMG00003"
case dbMigrationV1(_ dbError: Error)
/// Migration of the pending DB to version 2 failed.
// sourcery: code="ZDBMG00004"
case dbMigrationV2(_ dbError: Error)
// MARK: SQLite connection
/// SimpleConnectionProvider init of Connection failed.
// sourcery: code="ZSCPC0001"
case simpleConnectionProvider(_ error: Error)
// MARK: - Sapling parameters download
/// Downloaded file with sapling spending parameters isn't valid.
// sourcery: code="ZSAPP0001"
case saplingParamsInvalidSpendParams
/// Downloaded file with sapling output parameters isn't valid.
// sourcery: code="ZSAPP0002"
case saplingParamsInvalidOutputParams
/// Failed to download sapling parameters file
/// - `error` is download error.
/// - `downloadURL` is URL from which was file downloaded.
// sourcery: code="ZSAPP0003"
case saplingParamsDownload(_ error: Error, _ downloadURL: URL)
/// Failed to move sapling parameters file to final destination after download.
/// - `error` is move error.
/// - `downloadURL` is URL from which was file downloaded.
/// - `destination` is filesystem URL pointing to location where downloaded file should be moved.
// sourcery: code="ZSAPP0004"
case saplingParamsCantMoveDownloadedFile(_ error: Error, _ downloadURL: URL, _ destination: URL)
// MARK: - NotesDAO
/// SQLite query failed when fetching received notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0001"
case notesDAOReceivedCount(_ sqliteError: Error)
/// SQLite query failed when fetching received notes from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0002"
case notesDAOReceivedNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
// sourcery: code="ZNDAO0003"
case notesDAOReceivedCantDecode(_ error: Error)
/// SQLite query failed when fetching sent notes count from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0004"
case notesDAOSentCount(_ sqliteError: Error)
/// SQLite query failed when fetching sent notes from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZNDAO0005"
case notesDAOSentNote(_ sqliteError: Error)
/// Fetched note from the SQLite but can't decode that.
/// - `error` is decoding error.
// sourcery: code="ZNDAO0006"
case notesDAOSentCantDecode(_ error: Error)
// MARK: - BlockDAO
/// SQLite query failed when fetching block information from database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZBDAO0001"
case blockDAOBlock(_ sqliteError: Error)
/// Fetched block information from DB but can't decode them.
/// - `error` is decoding error.
// sourcery: code="ZBDAO0002"
case blockDAOCantDecode(_ error: Error)
/// SQLite query failed when fetching height of the latest block from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZBDAO0003"
case blockDAOLatestBlockHeight(_ sqliteError: Error)
/// SQLite query failed when fetching the latest block from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZBDAO0004"
case blockDAOLatestBlock(_ sqliteError: Error)
/// Fetched latesxt block information from DB but can't decode them.
/// - `error` is decoding error.
// sourcery: code="ZBDAO0005"
case blockDAOLatestBlockCantDecode(_ error: Error)
// MARK: - Rust
/// Error from rust layer when calling ZcashRustBackend.createAccount
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0001"
case rustCreateAccount(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.createToAddress
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0002"
case rustCreateToAddress(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.decryptAndStoreTransaction
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0003"
case rustDecryptAndStoreTransaction(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getBalance
/// - `account` is account passed to ZcashRustBackend.getBalance.
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0004"
case rustGetBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getCurrentAddress
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0005"
case rustGetCurrentAddress(_ rustError: String)
/// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getCurrentAddress
// sourcery: code="ZRUST0006"
case rustGetCurrentAddressInvalidAddress
/// Error from rust layer when calling ZcashRustBackend.getNearestRewindHeight
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0007"
case rustGetNearestRewindHeight(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getNextAvailableAddress
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0008"
case rustGetNextAvailableAddress(_ rustError: String)
/// Unified address generated by rust layer is invalid when calling ZcashRustBackend.getNextAvailableAddress
// sourcery: code="ZRUST0009"
case rustGetNextAvailableAddressInvalidAddress
/// account parameter is lower than 0 when calling ZcashRustBackend.getTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getTransparentBalance.
// sourcery: code="ZRUST0010"
case rustGetTransparentBalanceNegativeAccount(_ account: Int)
/// Error from rust layer when calling ZcashRustBackend.getTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getTransparentBalance.
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0011"
case rustGetTransparentBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.getVerifiedBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedBalance.
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0012"
case rustGetVerifiedBalance(_ account: Int, _ rustError: String)
/// account parameter is lower than 0 when calling ZcashRustBackend.getVerifiedTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedTransparentBalance.
// sourcery: code="ZRUST0013"
case rustGetVerifiedTransparentBalanceNegativeAccount(_ account: Int)
/// Error from rust layer when calling ZcashRustBackend.getVerifiedTransparentBalance
/// - `account` is account passed to ZcashRustBackend.getVerifiedTransparentBalance.
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0014"
case rustGetVerifiedTransparentBalance(_ account: Int, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.initDataDb
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0015"
case rustInitDataDb(_ rustError: String)
/// Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method contains null bytes before end
// sourcery: code="ZRUST0016"
case rustInitAccountsTableViewingKeyCotainsNullBytes
/// Any of the viewing keys passed to the ZcashRustBackend.initAccountsTable method isn't valid
// sourcery: code="ZRUST0017"
case rustInitAccountsTableViewingKeyIsInvalid
/// Error from rust layer when calling ZcashRustBackend.initAccountsTable
// sourcery: code="ZRUST0018"
case rustInitAccountsTableDataDbNotEmpty
/// Error from rust layer when calling ZcashRustBackend.initAccountsTable
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0019"
case rustInitAccountsTable(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.initBlockMetadataDb
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0020"
case rustInitBlockMetadataDb(_ rustError: String)
/// Unable to allocate memory required to write blocks when calling ZcashRustBackend.writeBlocksMetadata
// sourcery: code="ZRUST0021"
case rustWriteBlocksMetadataAllocationProblem
/// Error from rust layer when calling ZcashRustBackend.writeBlocksMetadata
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0022"
case rustWriteBlocksMetadata(_ rustError: String)
/// hash passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end
// sourcery: code="ZRUST0023"
case rustInitBlocksTableHashContainsNullBytes
/// saplingTree passed to the ZcashRustBackend.initBlocksTable method contains null bytes before end
// sourcery: code="ZRUST0024"
case rustInitBlocksTableSaplingTreeContainsNullBytes
/// Error from rust layer when calling ZcashRustBackend.initBlocksTable
// sourcery: code="ZRUST0025"
case rustInitBlocksTableDataDbNotEmpty
/// Error from rust layer when calling ZcashRustBackend.initBlocksTable
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0026"
case rustInitBlocksTable(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.listTransparentReceivers
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0027"
case rustListTransparentReceivers(_ rustError: String)
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.listTransparentReceivers
// sourcery: code="ZRUST0028"
case rustListTransparentReceiversInvalidAddress
/// Error from rust layer when calling ZcashRustBackend.putUnspentTransparentOutput
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0029"
case rustPutUnspentTransparentOutput(_ rustError: String)
/// Error unrelated to chain validity from rust layer when calling ZcashRustBackend.validateCombinedChain
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0030"
case rustValidateCombinedChainValidationFailed(_ rustError: String)
/// Error from rust layer which means that combined chain isn't valid.
/// - `upperBound` is the height of the highest invalid block (on the assumption that the highest block in the cache database is correct).
// sourcery: code="ZRUST0031"
case rustValidateCombinedChainInvalidChain(_ upperBound: Int32)
/// Error from rust layer when calling ZcashRustBackend.rewindToHeight
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0032"
case rustRewindToHeight(_ height: Int32, _ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.rewindCacheToHeight
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0033"
case rustRewindCacheToHeight(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.scanBlocks
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0034"
case rustScanBlocks(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.shieldFunds
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0035"
case rustShieldFunds(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.consensusBranchIdFor
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0036"
case rustNoConsensusBranchId(_ height: Int32)
/// address passed to the ZcashRustBackend.receiverTypecodesOnUnifiedAddress method contains null bytes before end
/// - `address` is address passed to ZcashRustBackend.receiverTypecodesOnUnifiedAddress.
// sourcery: code="ZRUST0037"
case rustReceiverTypecodesOnUnifiedAddressContainsNullBytes(_ address: String)
/// Error from rust layer when calling ZcashRustBackend.receiverTypecodesOnUnifiedAddress
// sourcery: code="ZRUST0038"
case rustRustReceiverTypecodesOnUnifiedAddressMalformed
/// Error from rust layer when calling ZcashRustBackend.deriveUnifiedSpendingKey
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0039"
case rustDeriveUnifiedSpendingKey(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.deriveUnifiedFullViewingKey
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0040"
case rustDeriveUnifiedFullViewingKey(_ rustError: String)
/// Viewing key derived by rust layer is invalid when calling ZcashRustBackend.deriveUnifiedFullViewingKey
// sourcery: code="ZRUST0041"
case rustDeriveUnifiedFullViewingKeyInvalidDerivedKey
/// Error from rust layer when calling ZcashRustBackend.getSaplingReceiver
/// - `address` is address passed to ZcashRustBackend.getSaplingReceiver.
// sourcery: code="ZRUST0042"
case rustGetSaplingReceiverInvalidAddress(_ address: UnifiedAddress)
/// Sapling receiver generated by rust layer is invalid when calling ZcashRustBackend.getSaplingReceiver
// sourcery: code="ZRUST0043"
case rustGetSaplingReceiverInvalidReceiver
/// Error from rust layer when calling ZcashRustBackend.getTransparentReceiver
/// - `address` is address passed to ZcashRustBackend.getTransparentReceiver.
// sourcery: code="ZRUST0044"
case rustGetTransparentReceiverInvalidAddress(_ address: UnifiedAddress)
/// Transparent receiver generated by rust layer is invalid when calling ZcashRustBackend.getTransparentReceiver
// sourcery: code="ZRUST0045"
case rustGetTransparentReceiverInvalidReceiver
// MARK: - Account DAO
/// SQLite query failed when fetching all accounts from the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZADAO0001"
case accountDAOGetAll(_ sqliteError: Error)
/// Fetched accounts from SQLite but can't decode them.
/// - `error` is decoding error.
// sourcery: code="ZADAO0002"
case accountDAOGetAllCantDecode(_ error: Error)
/// SQLite query failed when seaching for accounts in the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZADAO0003"
case accountDAOFindBy(_ sqliteError: Error)
/// Fetched accounts from SQLite but can't decode them.
/// - `error` is decoding error.
// sourcery: code="ZADAO0004"
case accountDAOFindByCantDecode(_ error: Error)
/// Object passed to update() method conforms to `AccountEntity` protocol but isn't exactly `Account` type.
// sourcery: code="ZADAO0005"
case accountDAOUpdateInvalidAccount
/// SQLite query failed when updating account in the database.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZADAO0006"
case accountDAOUpdate(_ sqliteError: Error)
/// Update of the account updated 0 rows in the database. One row should be updated.
// sourcery: code="ZADAO0007"
case accountDAOUpdatedZeroRows
// MARK: - Block storage
/// Failed to write block to disk.
// sourcery: code="ZBLRP00001"
case blockRepositoryWriteBlock(_ block: ZcashCompactBlock)
/// Failed to get filename for the block from file URL.
// sourcery: code="ZBLRP0002"
case blockRepositoryGetFilename(_ url: URL)
/// Failed to parse block height from filename.
// sourcery: code="ZBLRP0003"
case blockRepositoryParseHeightFromFilename(_ filename: String)
/// Failed to remove existing block from disk.
// sourcery: code="ZBLRP0004"
case blockRepositoryRemoveExistingBlock(_ error: Error)
/// Failed to get filename and information if url points to directory from file URL.
// sourcery: code="ZBLRP0005"
case blockRepositoryGetFilenameAndIsDirectory(_ url: URL)
/// Failed to create blocks cache directory.
// sourcery: code="ZBLRP0006"
case blockRepositoryCreateBlocksCacheDirectory(_ url: URL)
/// Failed to read content of directory.
// sourcery: code="ZBLRP0007"
case blockRepositoryReadDirectoryContent(_ url: URL)
/// Failed to remove block from disk after rewind operation.
// sourcery: code="ZBLRP0008"
case blockRepositoryRemoveBlockAfterRewind(_ url: URL)
/// Failed to remove blocks cache directory while clearing storage.
// sourcery: code="ZBLRP0009"
case blockRepositoryRemoveBlocksCacheDirectory(_ url: URL)
// MARK: - BlockDownloaderService
/// Stream downloading the given block range failed.
// sourcery: code="ZBDSEO0001"
case blockDownloaderServiceDownloadBlockRange(_ error: Error)
// MARK: - Transaction Entity / ZcashTransaction
/// Initialization of `ZcashTransaction.Overview` failed.
// sourcery: code="ZTEZT0001"
case zcashTransactionOverviewInit(_ error: Error)
/// Initialization of `ZcashTransaction.Received` failed.
// sourcery: code="ZTEZT0002"
case zcashTransactionReceivedInit(_ error: Error)
/// Initialization of `ZcashTransaction.Sent` failed.
// sourcery: code="ZTEZT0003"
case zcashTransactionSentInit(_ error: Error)
// MARK: - Transaction Repository
/// Entity not found in the database, result of `createEntity` execution.
// sourcery: code="ZTREE0001"
case transactionRepositoryEntityNotFound
/// `Find` call is missing fields, required fields are transaction `index` and `blockTime`.
// sourcery: code="ZTREE0002"
case transactionRepositoryTransactionMissingRequiredFields
/// Counting all transactions failed.
// sourcery: code="ZTREE0003"
case transactionRepositoryCountAll(_ error: Error)
/// Counting all unmined transactions failed.
// sourcery: code="ZTREE0004"
case transactionRepositoryCountUnmined(_ error: Error)
/// Execution of a query failed.
// sourcery: code="ZTREE0005"
case transactionRepositoryQueryExecute(_ error: Error)
/// Finding memos in the database failed.
// sourcery: code="ZTREE0006"
case transactionRepositoryFindMemos(_ error: Error)
// MARK: - ZcashCompactBlock
/// Can't encode `ZcashCompactBlock` object.
// sourcery: code="ZCMPB0001"
case compactBlockEncode(_ error: Error)
// MARK: - Memo
/// Invalid UTF-8 Bytes where detected when attempting to create a MemoText.
// sourcery: code="ZMEMO0001"
case memoTextInvalidUTF8
/// Trailing null-bytes were found when attempting to create a MemoText.
// sourcery: code="ZMEMO0002"
case memoTextInputEndsWithNullBytes
/// The resulting bytes provided are too long to be stored as a MemoText.
// sourcery: code="ZMEMO0003"
case memoTextInputTooLong(_ length: Int)
/// The resulting bytes provided are too long to be stored as a MemoBytes.
// sourcery: code="ZMEMO0004"
case memoBytesInputTooLong(_ length: Int)
/// Invalid UTF-8 Bytes where detected when attempting to convert MemoBytes to Memo.
// sourcery: code="ZMEMO0005"
case memoBytesInvalidUTF8
// MARK: - Checkpoint
/// Failed to load JSON with checkpoint from disk.
// sourcery: code="ZCHKP0001"
case checkpointCantLoadFromDisk(_ error: Error)
/// Failed to decode `Checkpoint` object.
// sourcery: code="ZCHKP0002"
case checkpointDecode(_ error: Error)
// MARK: - PendingTransactionDAO
/// Decoding of `PendingTransaction` failed because of specific invalid data.
/// - `field` is list of fields names that contain invalid data.
// sourcery: code="ZPETR0001"
case pendingTransactionDecodeInvalidData(_ fields: [String])
/// Can't decode `PendingTransaction`.
/// - `error` is error which described why decoding failed.
// sourcery: code="ZPETR0002"
case pendingTransactionCantDecode(_ error: Error)
/// Can't encode `PendingTransaction`.
/// - `error` is error which described why encoding failed.
// sourcery: code="ZPETR0003"
case pendingTransactionCantEncode(_ error: Error)
/// SQLite query failed when creating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0004"
case pendingTransactionDAOCreate(_ sqliteError: Error)
/// Pending transaction which should be updated is missing ID.
// sourcery: code="ZPETR0005"
case pendingTransactionDAOUpdateMissingID
/// SQLite query failed when updating pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0006"
case pendingTransactionDAOUpdate(_ sqliteError: Error)
/// Pending transaction which should be deleted is missing ID.
// sourcery: code="ZPETR0007"
case pendingTransactionDAODeleteMissingID
/// SQLite query failed when deleting pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0008"
case pendingTransactionDAODelete(_ sqliteError: Error)
/// Pending transaction which should be canceled is missing ID.
// sourcery: code="ZPETR0009"
case pendingTransactionDAOCancelMissingID
/// SQLite query failed when canceling pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0010"
case pendingTransactionDAOCancel(_ sqliteError: Error)
/// SQLite query failed when seaching for pending transaction.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0011"
case pendingTransactionDAOFind(_ sqliteError: Error)
/// SQLite query failed when getting pending transactions.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0012"
case pendingTransactionDAOGetAll(_ sqliteError: Error)
/// SQLite query failed when applying mined height.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZPETR0013"
case pendingTransactionDAOApplyMinedHeight(_ sqliteError: Error)
// MARK: - DerivationTool
/// Invalid account when trying to derive spending key
// sourcery: code="ZDRVT0001"
case derivationToolSpendingKeyInvalidAccount
// MARK: - UnspentTransactionOutputDAO
/// Creation of the table for unspent transaction output failed.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZUTOD0001"
case unspentTransactionOutputDAOCreateTable(_ sqliteError: Error)
/// SQLite query failed when storing unspent transaction output.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZUTOD0002"
case unspentTransactionOutputDAOStore(_ sqliteError: Error)
/// SQLite query failed when removing all the unspent transation outputs.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZUTOD0003"
case unspentTransactionOutputDAOClearAll(_ sqliteError: Error)
/// Fetched information about unspent transaction output from the DB but it can't be decoded to `UTXO` object.
/// - `error` decoding error.
// sourcery: code="ZUTOD0004"
case unspentTransactionOutputDAOGetAllCantDecode(_ error: Error)
/// SQLite query failed when getting all the unspent transation outputs.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZUTOD0005"
case unspentTransactionOutputDAOGetAll(_ sqliteError: Error)
/// SQLite query failed when getting balance.
/// - `sqliteError` is error produced by SQLite library.
// sourcery: code="ZUTOD0006"
case unspentTransactionOutputDAOBalance(_ sqliteError: Error)
// MARK: - WalletTypes
/// Can't create `SaplingExtendedSpendingKey` because input is invalid.
// sourcery: code="ZWLTP0001"
case spendingKeyInvalidInput
/// Can't create `UnifiedFullViewingKey` because input is invalid.
// sourcery: code="ZWLTP0002"
case unifiedFullViewingKeyInvalidInput
/// Can't create `SaplingExtendedFullViewingKey` because input is invalid.
// sourcery: code="ZWLTP0003"
case extetendedFullViewingKeyInvalidInput
/// Can't create `TransparentAddress` because input is invalid.
// sourcery: code="ZWLTP0004"
case transparentAddressInvalidInput
/// Can't create `SaplingAddress` because input is invalid.
// sourcery: code="ZWLTP0005"
case saplingAddressInvalidInput
/// Can't create `UnifiedAddress` because input is invalid.
// sourcery: code="ZWLTP0006"
case unifiedAddressInvalidInput
/// Can't create `Recipient` because input is invalid.
// sourcery: code="ZWLTP0007"
case recipientInvalidInput
// MARK: - WalletTransactionEncoder
/// WalletTransactionEncoder wants to create transaction but files with sapling parameters are not present on disk.
// sourcery: code="ZWLTE0001"
case walletTransEncoderCreateTransactionMissingSaplingParams
/// WalletTransactionEncoder wants to shield funds but files with sapling parameters are not present on disk.
// sourcery: code="ZWLTE0002"
case walletTransEncoderShieldFundsMissingSaplingParams
// MARK: - PersistentTransactionManager
/// PersistentTransactionsManager cant create transaction.
// sourcery: code="ZPTRM0001"
case persistentTransManagerCantCreateTransaction(_ recipient: PendingTransactionRecipient, _ account: Int, _ zatoshi: Zatoshi)
/// PersistentTransactionsManager cant get to address from pending transaction.
// sourcery: code="ZPTRM0002"
case persistentTransManagerEncodeUknownToAddress(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id.
// sourcery: code="ZPTRM0003"
case persistentTransManagerSubmitTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing id. Transaction is probably not stored.
// sourcery: code="ZPTRM0004"
case persistentTransManagerSubmitTransactionNotFound(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is canceled.
// sourcery: code="ZPTRM0005"
case persistentTransManagerSubmitTransactionCanceled(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but transaction is missing raw data.
// sourcery: code="ZPTRM0006"
case persistentTransManagerSubmitTransactionRawDataMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to submit pending transaction but submit API call failed.
// sourcery: code="ZPTRM0007"
case persistentTransManagerSubmitFailed(_ entity: PendingTransactionEntity, _ serviceErrorCode: Int)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is missing id. Transaction is probably not stored.
// sourcery: code="ZPTRM0008"
case persistentTransManagerApplyMinedHeightTransactionIDMissing(_ entity: PendingTransactionEntity)
/// PersistentTransactionsManager wants to apply mined height to transaction but transaction is not found in storage. Transaction is probably not stored.
// sourcery: code="ZPTRM0009"
case persistentTransManagerApplyMinedHeightTransactionNotFound(_ entity: PendingTransactionEntity)
// MARK: - Zatoshi
/// Initiatilzation fo `Zatoshi` from a decoder failed.
// sourcery: code="ZTSHO0001"
case zatoshiDecode(_ error: Error)
/// Encode of `Zatoshi` failed.
// sourcery: code="ZTSHO0002"
case zatoshiEncode(_ error: Error)
// MARK: - UTXOFetcher
/// Awaiting transactions from the stream failed.
// sourcery: code="ZUTXO0001"
case unspentTransactionFetcherStream(_ error: Error)
// MARK: - CompactBlockProcessor
/// CompactBlockProcessor was started with an invalid configuration.
// sourcery: code="ZCBPEO0001"
case compactBlockProcessorInvalidConfiguration
/// CompactBlockProcessor was set up with path but that location couldn't be reached.
// sourcery: code="ZCBPEO0002"
case compactBlockProcessorMissingDbPath(_ path: String)
/// Data Db file couldn't be initialized at path.
// sourcery: code="ZCBPEO0003"
case compactBlockProcessorDataDbInitFailed(_ path: String)
/// There's a problem with the network connection.
// sourcery: code="ZCBPEO0004"
case compactBlockProcessorConnection(_ underlyingError: Error)
/// Error on gRPC happened.
// sourcery: code="ZCBPEO0005"
case compactBlockProcessorGrpcError(statusCode: Int, message: String)
/// Network connection timeout.
// sourcery: code="ZCBPEO0006"
case compactBlockProcessorConnectionTimeout
/// Compact Block failed and reached the maximum amount of retries it was set up to do.
// sourcery: code="ZCBPEO0007"
case compactBlockProcessorMaxAttemptsReached(_ attempts: Int)
/// Unspecified error occured.
// sourcery: code="ZCBPEO0008"
case compactBlockProcessorUnspecified(_ underlyingError: Error)
/// Critical error occured.
// sourcery: code="ZCBPEO0009"
case compactBlockProcessorCritical
/// Invalid Account.
// sourcery: code="ZCBPEO0010"
case compactBlockProcessorInvalidAccount
/// The remote server you are connecting to is publishing a different branch ID than the one your App is expecting This could be caused by your App being out of date or the server you are connecting you being either on a different network or out of date after a network upgrade.
// sourcery: code="ZCBPEO0011"
case compactBlockProcessorWrongConsensusBranchId(expectedLocally: ConsensusBranchID, found: ConsensusBranchID)
/// A server was reached, but it's targeting the wrong network Type. Make sure you are pointing to the right server.
// sourcery: code="ZCBPEO0012"
case compactBlockProcessorNetworkMismatch(expected: NetworkType, found: NetworkType)
/// A server was reached, it's showing a different sapling activation. Are you sure you are pointing to the right server?
// sourcery: code="ZCBPEO0013"
case compactBlockProcessorSaplingActivationMismatch(expected: BlockHeight, found: BlockHeight)
/// when the given URL is the same URL than the one provided as `self.fsBlockDbRoot` assuming that's a programming error being the `legacyCacheDbURL` a sqlite database file and not a directory
// sourcery: code="ZCBPEO0014"
case compactBlockProcessorCacheDbMigrationFsCacheMigrationFailedSameURL
/// Deletion of readable file at the provided URL failed.
// sourcery: code="ZCBPEO0015"
case compactBlockProcessorCacheDbMigrationFailedToDeleteLegacyDb(_ error: Error)
/// Chain name does not match. Expected either 'test' or 'main'. This is probably an API or programming error.
// sourcery: code="ZCBPEO0016"
case compactBlockProcessorChainName(_ name: String)
/// Consensus BranchIDs don't match this is probably an API or programming error.
// sourcery: code="ZCBPEO0017"
case compactBlockProcessorConsensusBranchID
// MARK: - SDKSynchronizer
/// The synchronizer is unprepared.
// sourcery: code="ZSYNCO0001"
case synchronizerNotPrepared
/// Memos can't be sent to transparent addresses.
// sourcery: code="ZSYNCO0002"
case synchronizerSendMemoToTransparentAddress
/// There is not enough transparent funds to cover fee for the shielding.
// sourcery: code="ZSYNCO0003"
case synchronizerShieldFundsInsuficientTransparentFunds
/// LatestUTXOs for the address failed, invalid t-address.
// sourcery: code="ZSYNCO0004"
case synchronizerLatestUTXOsInvalidTAddress
/// Rewind failed, unknown archor height
// sourcery: code="ZSYNCO0005"
case synchronizerRewindUnknownArchorHeight
}

View File

@ -1,45 +0,0 @@
//
// SynchronizerError+LocalizedError.swift
//
//
// Created by Francisco Gindre on 7/11/22.
//
import Foundation
extension SynchronizerError: LocalizedError {
public var errorDescription: String? {
switch self {
case .initFailed(message: let message):
return "Failed to initialize. Message: \(message)"
case .notPrepared:
return "Synchronizer is not prepared. run `prepare()` before `start()"
case .syncFailed:
return "Synchronizer failed"
case .connectionFailed(message: let message):
return "Connection Failed. Error: \(message)"
case .generalError(message: let message):
return "An error occurred when syncing. Message: \(message)"
case .maxRetryAttemptsReached(attempts: let attempts):
return "An error occurred. We made \(attempts) retry attempts."
case let .connectionError(status: status, message: message):
return "There's a connection error. Status #\(status). Message: \(message)"
case .networkTimeout:
return "Network Timeout. Please check Internet connection"
case .uncategorized(underlyingError: let underlyingError):
return "Uncategorized Error. Underlying Error: \(underlyingError)"
case .criticalError:
return "A critical Error Occurred"
case .parameterMissing(underlyingError: let underlyingError):
return "Sapling parameters are not present or couldn't be downloaded. Error: \(underlyingError)."
case .rewindError(underlyingError: let underlyingError):
return "Error when rescanning. Error: \(underlyingError)"
case .rewindErrorUnknownArchorHeight:
return "Error when rescanning. We couldn't find a point in time to rewind. Please attempt a full re-scan"
case .invalidAccount:
return "We couldn't find this account number."
case .lightwalletdValidationFailed(underlyingError: let underlyingError):
return "We connected to the network but we couldn't verify the server. `lightwalletdValidationFailed` error: \(underlyingError)."
}
}
}

View File

@ -8,7 +8,7 @@
import SQLite
extension Zatoshi: Value {
public static var declaredDatatype = Int64.declaredDatatype
public static let declaredDatatype = Int64.declaredDatatype
public static func fromDatatypeValue(_ datatypeValue: Int64) -> Zatoshi {
Zatoshi(datatypeValue)

View File

@ -8,28 +8,15 @@
import Foundation
/**
Wrapper for the Rust backend. This class basically represents all the Rust-wallet
capabilities and the supporting data required to exercise those abilities.
*/
public enum InitializerError: Error {
case fsCacheInitFailed(Error)
case dataDbInitFailed(Error)
case accountInitFailed(Error)
case invalidViewingKey(key: String)
case aliasAlreadyInUse(ZcashSynchronizerAlias)
case cantUpdateURLWithAlias(URL)
}
/**
Represents a lightwallet instance endpoint to connect to
*/
public struct LightWalletEndpoint {
public var host: String
public var port: Int
public var secure: Bool
public var singleCallTimeoutInMillis: Int64
public var streamingCallTimeoutInMillis: Int64
public let host: String
public let port: Int
public let secure: Bool
public let singleCallTimeoutInMillis: Int64
public let streamingCallTimeoutInMillis: Int64
/**
initializes a LightWalletEndpoint
@ -135,14 +122,14 @@ public class Initializer {
/// The effective birthday of the wallet based on the height provided when initializing and the checkpoints available on this SDK.
///
/// This contains valid value only after `initialize` function is called.
private(set) public var walletBirthday: BlockHeight
public private(set) var walletBirthday: BlockHeight
/// The purpose of this to migrate from cacheDb to fsBlockDb
private var cacheDbURL: URL?
private let cacheDbURL: URL?
/// Error that can be created when updating URLs according to alias. If this error is created then it is thrown from `SDKSynchronizer.prepare()`
/// or `SDKSynchronizer.wipe()`.
var urlsParsingError: InitializerError?
var urlsParsingError: ZcashError?
/// Constructs the Initializer and migrates an old cacheDb to the new file system block cache if a `cacheDbURL` is provided.
/// - Parameters:
@ -154,7 +141,7 @@ public class Initializer {
/// - endpoint: the endpoint representing the lightwalletd instance you want to point to
/// - spendParamsURL: location of the spend parameters
/// - outputParamsURL: location of the output parameters
convenience public init (
convenience public init(
cacheDbURL: URL?,
fsBlockDbRoot: URL,
dataDbURL: URL,
@ -235,7 +222,7 @@ public class Initializer {
storage: CompactBlockRepository,
saplingParamsSourceURL: SaplingParamsSourceURL,
alias: ZcashSynchronizerAlias,
urlsParsingError: InitializerError?,
urlsParsingError: ZcashError?,
logger: Logger
) {
self.cacheDbURL = cacheDbURL
@ -274,10 +261,10 @@ public class Initializer {
private static func tryToUpdateURLs(
with alias: ZcashSynchronizerAlias,
urls: URLs
) -> (URLs, InitializerError?) {
) -> (URLs, ZcashError?) {
let updatedURLsResult = Self.updateURLs(with: alias, urls: urls)
let parsingError: InitializerError?
let parsingError: ZcashError?
let updatedURLs: URLs
switch updatedURLsResult {
case let .success(updated):
@ -296,25 +283,25 @@ public class Initializer {
private static func updateURLs(
with alias: ZcashSynchronizerAlias,
urls: URLs
) -> Result<URLs, InitializerError> {
) -> Result<URLs, ZcashError> {
guard let updatedFsBlockDbRoot = urls.fsBlockDbRoot.updateLastPathComponent(with: alias) else {
return .failure(.cantUpdateURLWithAlias(urls.fsBlockDbRoot))
return .failure(.initializerCantUpdateURLWithAlias(urls.fsBlockDbRoot))
}
guard let updatedDataDbURL = urls.dataDbURL.updateLastPathComponent(with: alias) else {
return .failure(.cantUpdateURLWithAlias(urls.dataDbURL))
return .failure(.initializerCantUpdateURLWithAlias(urls.dataDbURL))
}
guard let updatedPendingDbURL = urls.pendingDbURL.updateLastPathComponent(with: alias) else {
return .failure(.cantUpdateURLWithAlias(urls.pendingDbURL))
return .failure(.initializerCantUpdateURLWithAlias(urls.pendingDbURL))
}
guard let updatedSpendParamsURL = urls.spendParamsURL.updateLastPathComponent(with: alias) else {
return .failure(.cantUpdateURLWithAlias(urls.spendParamsURL))
return .failure(.initializerCantUpdateURLWithAlias(urls.spendParamsURL))
}
guard let updateOutputParamsURL = urls.outputParamsURL.updateLastPathComponent(with: alias) else {
return .failure(.cantUpdateURLWithAlias(urls.outputParamsURL))
return .failure(.initializerCantUpdateURLWithAlias(urls.outputParamsURL))
}
return .success(
@ -342,18 +329,10 @@ public class Initializer {
/// - Throws: `InitializerError.dataDbInitFailed` if the creation of the dataDb fails
/// `InitializerError.accountInitFailed` if the account table can't be initialized.
func initialize(with seed: [UInt8]?, viewingKeys: [UnifiedFullViewingKey], walletBirthday: BlockHeight) async throws -> InitializationResult {
do {
try await storage.create()
} catch {
throw InitializerError.fsCacheInitFailed(error)
}
do {
if case .seedRequired = try await rustBackend.initDataDb(seed: seed) {
return .seedRequired
}
} catch {
throw InitializerError.dataDbInitFailed(error)
try await storage.create()
if case .seedRequired = try await rustBackend.initDataDb(seed: seed) {
return .seedRequired
}
let checkpoint = Checkpoint.birthday(with: walletBirthday, network: network)
@ -364,22 +343,20 @@ public class Initializer {
time: checkpoint.time,
saplingTree: checkpoint.saplingTree
)
} catch RustWeldingError.dataDbNotEmpty {
} catch ZcashError.rustInitBlocksTableDataDbNotEmpty {
// this is fine
} catch {
throw InitializerError.dataDbInitFailed(error)
throw error
}
self.walletBirthday = checkpoint.height
do {
try await rustBackend.initAccountsTable(ufvks: viewingKeys)
} catch RustWeldingError.dataDbNotEmpty {
} catch ZcashError.rustInitAccountsTableDataDbNotEmpty {
// this is fine
} catch RustWeldingError.malformedStringInput {
throw RustWeldingError.malformedStringInput
} catch {
throw InitializerError.accountInitFailed(error)
throw error
}
let migrationManager = MigrationManager(
@ -407,25 +384,3 @@ public class Initializer {
DerivationTool(networkType: network.networkType).isValidTransparentAddress(address)
}
}
extension InitializerError: LocalizedError {
public var errorDescription: String? {
switch self {
case .invalidViewingKey:
return "The provided viewing key is invalid"
case .dataDbInitFailed(let error):
return "dataDb init failed with error: \(error.localizedDescription)"
case .accountInitFailed(let error):
return "account table init failed with error: \(error.localizedDescription)"
case .fsCacheInitFailed(let error):
return "Compact Block Cache failed to initialize with error: \(error.localizedDescription)"
case let .aliasAlreadyInUse(alias):
return """
The Alias \(alias) used for this instance of the SDKSynchronizer is already in use. Each instance of the SDKSynchronizer must use unique \
Alias.
"""
case .cantUpdateURLWithAlias(let url):
return "Can't update path URL with alias. \(url)"
}
}
}

View File

@ -26,11 +26,11 @@ import Foundation
/// - orchardTree: the orchard tree corresponding to the given height. This field is optional since it won't be available prior
/// to NU5 activation height for the given network.
struct Checkpoint: Equatable {
private(set) var height: BlockHeight
private(set) var hash: String
private(set) var time: UInt32
private(set) var saplingTree: String
private(set) var orchardTree: String?
let height: BlockHeight
let hash: String
let time: UInt32
let saplingTree: String
let orchardTree: String?
}
extension Checkpoint: Decodable {
@ -43,12 +43,16 @@ extension Checkpoint: Decodable {
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.height = try Self.getHeight(from: container)
self.hash = try container.decode(String.self, forKey: .hash)
self.time = try container.decode(UInt32.self, forKey: .time)
self.saplingTree = try container.decode(String.self, forKey: .saplingTree)
self.orchardTree = try container.decodeIfPresent(String.self, forKey: .orchardTree)
do {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.height = try Self.getHeight(from: container)
self.hash = try container.decode(String.self, forKey: .hash)
self.time = try container.decode(UInt32.self, forKey: .time)
self.saplingTree = try container.decode(String.self, forKey: .saplingTree)
self.orchardTree = try container.decodeIfPresent(String.self, forKey: .orchardTree)
} catch {
throw ZcashError.checkpointDecode(error)
}
}
static func getHeight(from container: KeyedDecodingContainer<CodingKeys>) throws -> Int {

View File

@ -65,7 +65,7 @@ public extension Memo {
case .text(let textMemo):
guard let bytes = textMemo.string.data(using: .utf8)?.bytes else {
throw MemoBytes.Errors.invalidUTF8
throw ZcashError.memoTextInvalidUTF8
}
return try MemoBytes(bytes: bytes)
@ -82,17 +82,17 @@ public extension Memo {
/// A wrapper on `String` so that `Memo` can't be created with an invalid String
public struct MemoText: Equatable {
public private(set) var string: String
public let string: String
init(_ string: String) throws {
let trimmedString = String(string.reversed().drop(while: { $0 == "\u{0}" }).reversed())
guard trimmedString.count == string.count else {
throw MemoBytes.Errors.endsWithNullBytes
throw ZcashError.memoTextInputEndsWithNullBytes
}
guard string.utf8.count <= MemoBytes.capacity else {
throw MemoBytes.Errors.tooLong(string.utf8.count)
throw ZcashError.memoTextInputTooLong(string.utf8.count)
}
self.string = string
@ -111,7 +111,7 @@ public struct MemoBytes: Equatable {
public static let capacity: Int = 512
public private(set) var bytes: [UInt8]
public let bytes: [UInt8]
/// Copies the given bytes into the inner array if this struct in the context of ZIP-302
/// and returns a `MemoBytes` struct
@ -119,7 +119,7 @@ public struct MemoBytes: Equatable {
/// 512 bytes.
///
public init(bytes: [UInt8]) throws {
guard bytes.count <= Self.capacity else { throw Errors.tooLong(bytes.count) }
guard bytes.count <= Self.capacity else { throw ZcashError.memoBytesInputTooLong(bytes.count) }
var rawBytes = [UInt8](repeating: 0x0, count: Self.capacity)
@ -132,7 +132,7 @@ public struct MemoBytes: Equatable {
}
init(contiguousBytes: ContiguousArray<UInt8>) throws {
guard contiguousBytes.capacity <= Self.capacity else { throw Errors.tooLong(contiguousBytes.capacity) }
guard contiguousBytes.capacity <= Self.capacity else { throw ZcashError.memoBytesInputTooLong(contiguousBytes.capacity) }
var rawBytes = [UInt8](repeating: 0x0, count: Self.capacity)
@ -149,19 +149,6 @@ public struct MemoBytes: Equatable {
}
}
extension MemoBytes.Errors: LocalizedError {
var localizedDescription: String {
switch self {
case .endsWithNullBytes:
return "MemoBytes.Errors.endsWithNullBytes: The UTF-8 bytes provided have trailing null-bytes."
case .invalidUTF8:
return "MemoBytes.Errors.invalidUTF8: Invalid UTF-8 byte found on memo bytes"
case .tooLong(let length):
return "MemoBytes.Errors.tooLong(\(length)): found more bytes than the 512 bytes a ZIP-302 memo is allowed to contain."
}
}
}
public extension MemoBytes {
/// Parsing of the MemoBytes in terms of ZIP-302 Specification
/// See https://zips.z.cash/zip-0302#specification
@ -179,7 +166,7 @@ public extension MemoBytes {
switch self.bytes[0] {
case 0x00 ... 0xF4:
guard let validatedUTF8String = String(validatingUTF8: self.unpaddedRawBytes()) else {
throw MemoBytes.Errors.invalidUTF8
throw ZcashError.memoBytesInvalidUTF8
}
return .text(try MemoText(validatedUTF8String))
@ -256,6 +243,6 @@ extension Optional where WrappedType == String {
extension Data {
func intoMemoBytes() throws -> MemoBytes? {
try .init(bytes: self.bytes)
try MemoBytes(bytes: self.bytes)
}
}

View File

@ -5,24 +5,20 @@
// Created by Francisco Gindre on 4/6/21.
//
enum KeyEncodingError: Error {
case invalidEncoding
}
/// Something that can be encoded as a String
public protocol StringEncoded {
var stringEncoded: String { get }
}
public struct UnifiedSpendingKey: Equatable, Undescribable {
private(set) var network: NetworkType
var bytes: [UInt8]
public private(set) var account: UInt32
let network: NetworkType
let bytes: [UInt8]
public let account: UInt32
}
/// Sapling Extended Spending Key
public struct SaplingExtendedSpendingKey: Equatable, StringEncoded, Undescribable {
var encoding: String
let encoding: String
public var stringEncoded: String {
encoding
@ -32,11 +28,10 @@ public struct SaplingExtendedSpendingKey: Equatable, StringEncoded, Undescribabl
/// - Parameters:
/// - parameter encoding: String encoding of ExtSK
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
/// found to be invalid
/// - Throws: `spendingKeyInvalidInput`when the provided encoding is found to be invalid
public init(encoding: String, network: NetworkType) throws {
guard DerivationTool(networkType: network).isValidSaplingExtendedSpendingKey(encoding) else {
throw KeyEncodingError.invalidEncoding
throw ZcashError.spendingKeyInvalidInput
}
self.encoding = encoding
}
@ -44,14 +39,13 @@ public struct SaplingExtendedSpendingKey: Equatable, StringEncoded, Undescribabl
/// A Transparent Account Private Key
public struct TransparentAccountPrivKey: Equatable, Undescribable {
var encoding: String
let encoding: String
}
/// A ZIP 316 Unified Full Viewing Key.
public struct UnifiedFullViewingKey: Equatable, StringEncoded, Undescribable {
var encoding: String
public var account: UInt32
let encoding: String
public let account: UInt32
public var stringEncoded: String { encoding }
@ -60,11 +54,10 @@ public struct UnifiedFullViewingKey: Equatable, StringEncoded, Undescribable {
/// - parameter encoding: String encoding of unified full viewing key
/// - parameter account: account number of the given UFVK
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
/// found to be invalid
/// - Throws: `unifiedFullViewingKeyInvalidInput`when the provided encoding is found to be invalid
public init(encoding: String, account: UInt32, network: NetworkType) throws {
guard DerivationTool(networkType: network).isValidUnifiedFullViewingKey(encoding) else {
throw KeyEncodingError.invalidEncoding
throw ZcashError.unifiedFullViewingKeyInvalidInput
}
self.encoding = encoding
@ -73,7 +66,7 @@ public struct UnifiedFullViewingKey: Equatable, StringEncoded, Undescribable {
}
public struct SaplingExtendedFullViewingKey: Equatable, StringEncoded, Undescribable {
var encoding: String
let encoding: String
public var stringEncoded: String {
encoding
}
@ -82,11 +75,10 @@ public struct SaplingExtendedFullViewingKey: Equatable, StringEncoded, Undescrib
/// - Parameters:
/// - parameter encoding: String encoding of Sapling extended full viewing key
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
/// found to be invalid
/// - Throws: `extetendedFullViewingKeyInvalidInput`when the provided encoding is found to be invalid
public init(encoding: String, network: NetworkType) throws {
guard ZcashKeyDerivationBackend(networkType: network).isValidSaplingExtendedFullViewingKey(encoding) else {
throw KeyEncodingError.invalidEncoding
throw ZcashError.extetendedFullViewingKeyInvalidInput
}
self.encoding = encoding
}
@ -125,7 +117,7 @@ extension AddressType {
/// Transactions sent to this address are totally visible in the public
/// ledger. See "Multiple transaction types" in https://z.cash/technology/
public struct TransparentAddress: Equatable, StringEncoded, Comparable {
var encoding: String
let encoding: String
public var stringEncoded: String { encoding }
@ -133,11 +125,10 @@ public struct TransparentAddress: Equatable, StringEncoded, Comparable {
///
/// - parameter encoding: String encoding of the t-address
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
/// found to be invalid
/// - Throws: `transparentAddressInvalidInput`when the provided encoding is found to be invalid
public init(encoding: String, network: NetworkType) throws {
guard DerivationTool(networkType: network).isValidTransparentAddress(encoding) else {
throw KeyEncodingError.invalidEncoding
throw ZcashError.transparentAddressInvalidInput
}
self.encoding = encoding
@ -153,7 +144,7 @@ public struct TransparentAddress: Equatable, StringEncoded, Comparable {
/// Although this it is fully functional, we encourage developers to
/// choose `UnifiedAddress` before Sapling or Transparent ones.
public struct SaplingAddress: Equatable, StringEncoded {
var encoding: String
let encoding: String
public var stringEncoded: String { encoding }
@ -162,11 +153,10 @@ public struct SaplingAddress: Equatable, StringEncoded {
/// - parameter encoding: String encoding of the z-address
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
///
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
/// found to be invalid
/// - Throws: `saplingAddressInvalidInput` when the provided encoding is found to be invalid
public init(encoding: String, network: NetworkType) throws {
guard DerivationTool(networkType: network).isValidSaplingAddress(encoding) else {
throw KeyEncodingError.invalidEncoding
throw ZcashError.saplingAddressInvalidInput
}
self.encoding = encoding
@ -176,10 +166,6 @@ public struct SaplingAddress: Equatable, StringEncoded {
public struct UnifiedAddress: Equatable, StringEncoded {
let networkType: NetworkType
public enum Errors: Error {
case couldNotExtractTypecodes
}
public enum ReceiverTypecodes: Hashable {
case p2pkh
case p2sh
@ -203,7 +189,7 @@ public struct UnifiedAddress: Equatable, StringEncoded {
}
}
var encoding: String
let encoding: String
public var stringEncoded: String { encoding }
@ -211,26 +197,19 @@ public struct UnifiedAddress: Equatable, StringEncoded {
/// - Parameters:
/// - parameter encoding: String encoding of the UA
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
/// found to be invalid
/// - Throws: `unifiedAddressInvalidInput` when the provided encoding is found to be invalid
public init(encoding: String, network: NetworkType) throws {
networkType = network
guard DerivationTool(networkType: network).isValidUnifiedAddress(encoding) else {
throw KeyEncodingError.invalidEncoding
throw ZcashError.unifiedAddressInvalidInput
}
self.encoding = encoding
}
/// returns an array of `UnifiedAddress.ReceiverTypecodes` ordered by precedence
/// - Throws `UnifiedAddress.Errors.couldNotExtractTypecodes` when the typecodes
/// couldn't be extracted
public func availableReceiverTypecodes() throws -> [UnifiedAddress.ReceiverTypecodes] {
do {
return try DerivationTool(networkType: networkType).receiverTypecodesFromUnifiedAddress(self)
} catch {
throw Errors.couldNotExtractTypecodes
}
return try DerivationTool(networkType: networkType).receiverTypecodesFromUnifiedAddress(self)
}
}
@ -254,8 +233,7 @@ public enum Recipient: Equatable, StringEncoded {
/// Initializes a `Recipient` with string encoded Zcash address
/// - Parameter string: a string encoded Zcash address
/// - Parameter network: the `ZcashNetwork.NetworkType` of the recipient
/// - Throws: `KeyEncodingError.invalidEncoding` if the received string-encoded address
/// can't be initialized as a valid Zcash Address.
/// - Throws: `recipientInvalidInput` if the received string-encoded address can't be initialized as a valid Zcash Address.
public init(_ string: String, network: NetworkType) throws {
if let unified = try? UnifiedAddress(encoding: string, network: network) {
self = .unified(unified)
@ -264,7 +242,7 @@ public enum Recipient: Equatable, StringEncoded {
} else if let transparent = try? TransparentAddress(encoding: string, network: network) {
self = .transparent(transparent)
} else {
throw KeyEncodingError.invalidEncoding
throw ZcashError.recipientInvalidInput
}
}
@ -282,8 +260,8 @@ public enum Recipient: Equatable, StringEncoded {
}
public struct WalletBalance: Equatable {
public var verified: Zatoshi
public var total: Zatoshi
public let verified: Zatoshi
public let total: Zatoshi
public init(verified: Zatoshi, total: Zatoshi) {
self.verified = verified

View File

@ -96,12 +96,20 @@ extension Zatoshi: Codable {
}
public init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.amount = try values.decode(Int64.self, forKey: .amount)
do {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.amount = try values.decode(Int64.self, forKey: .amount)
} catch {
throw ZcashError.zatoshiDecode(error)
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.amount, forKey: .amount)
do {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(self.amount, forKey: .amount)
} catch {
throw ZcashError.zatoshiEncode(error)
}
}
}

View File

@ -48,7 +48,8 @@ class LiveLatestBlockHeightProvider: LatestBlockHeightProvider {
}
return blockHeight
} catch {
throw error.mapToServiceError()
let serviceError = error.mapToServiceError()
throw ZcashError.serviceLatestBlockHeightFailed(serviceError)
}
}
}
@ -66,7 +67,7 @@ class LightWalletGRPCService {
set { connectionManager.connectionStateChange = newValue }
}
var queue: DispatchQueue
let queue: DispatchQueue
convenience init(endpoint: LightWalletEndpoint) {
self.init(
@ -128,21 +129,11 @@ class LightWalletGRPCService {
do {
return try await compactTxStreamer.getLatestBlock(ChainSpec())
} catch {
throw error.mapToServiceError()
let serviceError = error.mapToServiceError()
throw ZcashError.serviceLatestBlockFailed(serviceError)
}
}
func getTx(hash: String) async throws -> RawTransaction {
var filter = TxFilter()
filter.hash = Data(hash.utf8)
do {
return try await compactTxStreamer.getTransaction(filter)
} catch {
throw error.mapToServiceError()
}
}
static func callOptions(timeLimit: TimeLimit) -> CallOptions {
CallOptions(
customMetadata: HPACKHeaders(),
@ -162,7 +153,8 @@ extension LightWalletGRPCService: LightWalletService {
do {
return try await compactTxStreamer.getLightdInfo(Empty())
} catch {
throw error.mapToServiceError()
let serviceError = error.mapToServiceError()
throw ZcashError.serviceGetInfoFailed(serviceError)
}
}
@ -181,7 +173,8 @@ extension LightWalletGRPCService: LightWalletService {
}
continuation.finish()
} catch {
continuation.finish(throwing: error.mapToServiceError())
let serviceError = error.mapToServiceError()
continuation.finish(throwing: ZcashError.serviceBlockRangeFailed(serviceError))
}
}
}
@ -192,7 +185,8 @@ extension LightWalletGRPCService: LightWalletService {
let transaction = RawTransaction.with { $0.data = spendTransaction }
return try await compactTxStreamer.sendTransaction(transaction)
} catch {
throw LightWalletServiceError.sentFailed(error: error)
let serviceError = error.mapToServiceError()
throw ZcashError.serviceSubmitFailed(serviceError)
}
}
@ -204,7 +198,8 @@ extension LightWalletGRPCService: LightWalletService {
let rawTx = try await compactTxStreamer.getTransaction(txFilter)
return ZcashTransaction.Fetched(rawID: txId, minedHeight: BlockHeight(rawTx.height), raw: rawTx.data)
} catch {
throw error.mapToServiceError()
let serviceError = error.mapToServiceError()
throw ZcashError.serviceFetchTransactionFailed(serviceError)
}
}
@ -245,7 +240,8 @@ extension LightWalletGRPCService: LightWalletService {
}
continuation.finish()
} catch {
continuation.finish(throwing: error.mapToServiceError())
let serviceError = error.mapToServiceError()
continuation.finish(throwing: ZcashError.serviceFetchUTXOsFailed(serviceError))
}
}
}
@ -271,7 +267,8 @@ extension LightWalletGRPCService: LightWalletService {
}
continuation.finish()
} catch {
continuation.finish(throwing: error.mapToServiceError())
let serviceError = error.mapToServiceError()
continuation.finish(throwing: ZcashError.serviceBlockStreamFailed(serviceError))
}
}
}

View File

@ -11,7 +11,7 @@ import Foundation
/**
Wrapper for errors received from a Lightwalletd endpoint
*/
enum LightWalletServiceError: Error {
public enum LightWalletServiceError: Error {
case generalError(message: String)
case failed(statusCode: Int, message: String)
case invalidBlock
@ -25,7 +25,7 @@ enum LightWalletServiceError: Error {
extension LightWalletServiceError: Equatable {
// swiftlint:disable cyclomatic_complexity
static func == (lhs: Self, rhs: Self) -> Bool {
public static func == (lhs: Self, rhs: Self) -> Bool {
switch lhs {
case .generalError(let message):
switch rhs {
@ -151,30 +151,41 @@ protocol LightWalletService: AnyObject {
var connectionStateChange: ((_ from: ConnectionState, _ to: ConnectionState) -> Void)? { get set }
/// Returns the info for this lightwalletd server
/// - Throws: `serviceGetInfoFailed` when GRPC call fails.
func getInfo() async throws -> LightWalletdInfo
/// - Throws: `serviceLatestBlockHeightFailed` when GRPC call fails.
func latestBlock() async throws -> BlockID
/// Return the latest block height known to the service.
/// - Throws: `serviceLatestBlockFailed` when GRPC call fails.
func latestBlockHeight() async throws -> BlockHeight
/// Return the given range of blocks.
/// - Parameter range: the inclusive range to fetch.
/// For instance if 1..5 is given, then every block in that will be fetched, including 1 and 5.
/// - Throws: `serviceBlockRangeFailed` when GRPC call fails.
func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error>
/// Submits a raw transaction over lightwalletd.
/// - Parameter spendTransaction: data representing the transaction to be sent
/// - Throws: `serviceSubmitFailed` when GRPC call fails.
func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse
/// Gets a transaction by id
/// - Parameter txId: data representing the transaction ID
/// - Throws: LightWalletServiceError
/// - Returns: LightWalletServiceResponse
/// - Throws: `serviceFetchTransactionFailed` when GRPC call fails.
func fetchTransaction(txId: Data) async throws -> ZcashTransaction.Fetched
/// - Throws: `serviceFetchUTXOsFailed` when GRPC call fails.
func fetchUTXOs(for tAddress: String, height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
/// - Throws: `serviceFetchUTXOsFailed` when GRPC call fails.
func fetchUTXOs(for tAddresses: [String], height: BlockHeight) -> AsyncThrowingStream<UnspentTransactionOutputEntity, Error>
/// - Throws: `serviceBlockStreamFailed` when GRPC call fails.
func blockStream(
startHeight: BlockHeight,
endHeight: BlockHeight

View File

@ -22,7 +22,7 @@ public protocol ResourceProvider {
Convenience provider for a data db and cache db resources.
*/
public struct DefaultResourceProvider: ResourceProvider {
var network: ZcashNetwork
let network: ZcashNetwork
public var dataDbURL: URL {
let constants = network.constants

View File

@ -8,25 +8,6 @@
import Foundation
enum CompactBlockRepositoryError: Error, Equatable {
/// cache is empty
case cacheEmpty
/// It was expected to have some cache entry but upon retrieval
/// it couldn't be parsed.
case malformedCacheEntry(String)
/// There was a failure when attempting to clear the repository
case cacheClearFailed
/// There was a problem storing a given block
case failedToWriteBlock(ZcashCompactBlock)
/// there was a problem saving the metadata of the cached blocks
/// the underlying error is returned as the associated value
case failedToWriteMetadata
/// failed to initialize cache with no underlying error
case failedToInitializeCache
/// failed to rewind the repository to given blockheight
case failedToRewind(BlockHeight)
}
protocol CompactBlockRepository {
/// Creates the underlying repository
func create() async throws
@ -57,24 +38,3 @@ protocol CompactBlockRepository {
/// Clears the repository
func clear() async throws
}
extension CompactBlockRepositoryError: LocalizedError {
public var errorDescription: String? {
switch self {
case .failedToWriteBlock(let block):
return "Failed to write compact block of height \(block.height)."
case .malformedCacheEntry(let message):
return "Malformed cache entry: \(message)."
case .cacheEmpty:
return "Cache is Empty."
case .cacheClearFailed:
return "Cache could not be cleared."
case .failedToWriteMetadata:
return "Failed to write metadata to FsBlockDb."
case .failedToInitializeCache:
return "Failed to initialize metadata FsBlockDb."
case .failedToRewind(let height):
return "Failed to rewind FsBlockDb to height \(height)."
}
}
}

View File

@ -7,12 +7,6 @@
import Foundation
enum TransactionRepositoryError: Error {
case malformedTransaction
case notFound
case transactionMissingRequiredFields
}
protocol TransactionRepository {
func closeDBConnection()
func countAll() async throws -> Int

View File

@ -39,7 +39,7 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
func receiverTypecodesOnUnifiedAddress(_ address: String) throws -> [UInt32] {
guard !address.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.invalidInput(message: "`address` contains null bytes.")
throw ZcashError.rustReceiverTypecodesOnUnifiedAddressContainsNullBytes(address)
}
var len = UInt(0)
@ -49,7 +49,7 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
&len
), len > 0
else {
throw RustWeldingError.malformedStringInput
throw ZcashError.rustRustReceiverTypecodesOnUnifiedAddressMalformed
}
var typecodes: [UInt32] = []
@ -133,7 +133,7 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
defer { zcashlc_free_binary_key(binaryKeyPtr) }
guard let binaryKey = binaryKeyPtr?.pointee else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustDeriveUnifiedSpendingKey(lastErrorMessage(fallback: "`deriveUnifiedSpendingKey` failed with unknown error"))
}
return binaryKey.unsafeToUnifiedSpendingKey(network: networkType)
@ -146,7 +146,9 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
UInt(spendingKey.bytes.count),
networkType.networkId
) else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustDeriveUnifiedFullViewingKey(
lastErrorMessage(fallback: "`deriveUnifiedFullViewingKey` failed with unknown error")
)
}
return extfvk
@ -155,7 +157,7 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
defer { zcashlc_string_free(extfvk) }
guard let derived = String(validatingUTF8: extfvk) else {
throw RustWeldingError.unableToDeriveKeys
throw ZcashError.rustDeriveUnifiedFullViewingKeyInvalidDerivedKey
}
return UnifiedFullViewingKey(validatedEncoding: derived, account: spendingKey.account)
@ -165,13 +167,13 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
guard let saplingCStr = zcashlc_get_sapling_receiver_for_unified_address(
[CChar](uAddr.encoding.utf8CString)
) else {
throw KeyDerivationErrors.invalidUnifiedAddress
throw ZcashError.rustGetSaplingReceiverInvalidAddress(uAddr)
}
defer { zcashlc_string_free(saplingCStr) }
guard let saplingReceiverStr = String(validatingUTF8: saplingCStr) else {
throw KeyDerivationErrors.receiverNotFound
throw ZcashError.rustGetSaplingReceiverInvalidReceiver
}
return SaplingAddress(validatedEncoding: saplingReceiverStr)
@ -181,13 +183,13 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
guard let transparentCStr = zcashlc_get_transparent_receiver_for_unified_address(
[CChar](uAddr.encoding.utf8CString)
) else {
throw KeyDerivationErrors.invalidUnifiedAddress
throw ZcashError.rustGetTransparentReceiverInvalidAddress(uAddr)
}
defer { zcashlc_string_free(transparentCStr) }
guard let transparentReceiverStr = String(validatingUTF8: transparentCStr) else {
throw KeyDerivationErrors.receiverNotFound
throw ZcashError.rustGetTransparentReceiverInvalidReceiver
}
return TransparentAddress(validatedEncoding: transparentReceiverStr)
@ -195,31 +197,22 @@ struct ZcashKeyDerivationBackend: ZcashKeyDerivationBackendWelding {
// MARK: Error Handling
private func lastError() -> RustWeldingError? {
private func lastErrorMessage(fallback: String) -> String {
let errorLen = zcashlc_last_error_length()
defer { zcashlc_clear_last_error() }
guard let message = getLastError() else {
return nil
}
if message.contains("couldn't load Sapling spend parameters") {
return RustWeldingError.saplingSpendParametersNotFound
} else if message.contains("is not empty") {
return RustWeldingError.dataDbNotEmpty
}
return RustWeldingError.genericError(message: message)
}
private func getLastError() -> String? {
let errorLen = zcashlc_last_error_length()
if errorLen > 0 {
let error = UnsafeMutablePointer<Int8>.allocate(capacity: Int(errorLen))
defer { error.deallocate() }
zcashlc_error_message_utf8(error, errorLen)
zcashlc_clear_last_error()
return String(validatingUTF8: error)
if let errorMessage = String(validatingUTF8: error) {
return errorMessage
} else {
return fallback
}
} else {
return nil
return fallback
}
}
}

View File

@ -19,25 +19,24 @@ protocol ZcashKeyDerivationBackendWelding {
/// Obtains the available receiver typecodes for the given String encoded Unified Address
/// - Parameter address: public key represented as a String
/// - Returns the `[UInt32]` that compose the given UA
/// - Throws `RustWeldingError.invalidInput(message: String)` when the UA is either invalid or malformed
/// - Note: not `NetworkType` bound
/// - Throws:
/// - `rustReceiverTypecodesOnUnifiedAddressContainsNullBytes` if `address` contains null bytes before end.
/// - `rustRustReceiverTypecodesOnUnifiedAddressMalformed` if getting typecodes for unified address fails.
func receiverTypecodesOnUnifiedAddress(_ address: String) throws -> [UInt32]
/// Validates the if the given string is a valid Sapling Address
/// - Parameter address: UTF-8 encoded String to validate
/// - Returns: true when the address is valid. Returns false in any other case
/// - Throws: Error when the provided address belongs to another network
func isValidSaplingAddress(_ address: String) -> Bool
/// Validates the if the given string is a valid Sapling Extended Full Viewing Key
/// - Parameter key: UTF-8 encoded String to validate
/// - Returns: `true` when the Sapling Extended Full Viewing Key is valid. `false` in any other case
/// - Throws: Error when there's another problem not related to validity of the string in question
func isValidSaplingExtendedFullViewingKey(_ key: String) -> Bool
/// Validates the if the given string is a valid Sapling Extended Spending Key
/// - Returns: `true` when the Sapling Extended Spending Key is valid, false in any other case.
/// - Throws: Error when the key is semantically valid but it belongs to another network
/// - parameter key: String encoded Extended Spending Key
func isValidSaplingExtendedSpendingKey(_ key: String) -> Bool
@ -60,23 +59,30 @@ protocol ZcashKeyDerivationBackendWelding {
/// Returns the binary encoding of the spending key. The caller should manage the memory of (and store, if necessary) the returned spending key in a secure fashion.
/// - Parameter seed: a Byte Array with the seed
/// - Parameter accountIndex:account index that the key can spend from
/// - Throws: `rustDeriveUnifiedSpendingKey` if rust layer returns error.
func deriveUnifiedSpendingKey(from seed: [UInt8], accountIndex: Int32) async throws -> UnifiedSpendingKey
/// Derives a `UnifiedFullViewingKey` from a `UnifiedSpendingKey`
/// - Parameter spendingKey: the `UnifiedSpendingKey` to derive from
/// - Throws: `RustWeldingError.unableToDeriveKeys` if the SDK couldn't derive the UFVK.
/// - Returns: the derived `UnifiedFullViewingKey`
/// - Throws:
/// - `rustDeriveUnifiedFullViewingKey` if rust layer returns error.
/// - `rustDeriveUnifiedFullViewingKeyInvalidDerivedKey` if derived viewing key is invalid.
func deriveUnifiedFullViewingKey(from spendingKey: UnifiedSpendingKey) async throws -> UnifiedFullViewingKey
/// Returns the Sapling receiver within the given Unified Address, if any.
/// - Parameter uAddr: a `UnifiedAddress`
/// - Returns a `SaplingAddress` if any
/// - Throws `receiverNotFound` when the receiver is not found. `invalidUnifiedAddress` if the UA provided is not valid
/// - Throws:
/// - `rustGetSaplingReceiverInvalidAddress` if failed to get sapling receiver for unified address.
/// - `rustGetSaplingReceiverInvalidReceiver` if generated sapling receiver is invalid.
func getSaplingReceiver(for uAddr: UnifiedAddress) throws -> SaplingAddress
/// Returns the transparent receiver within the given Unified Address, if any.
/// - parameter uAddr: a `UnifiedAddress`
/// - Returns a `TransparentAddress` if any
/// - Throws `receiverNotFound` when the receiver is not found. `invalidUnifiedAddress` if the UA provided is not valid
/// - Throws:
/// - `rustGetTransparentReceiverInvalidAddress` if failed to get transparent receiver for unified address.
/// - `rustGetTransparentReceiverInvalidReceiver` if generated transparent receiver is invalid.
func getTransparentReceiver(for uAddr: UnifiedAddress) throws -> TransparentAddress
}

View File

@ -40,14 +40,16 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func createAccount(seed: [UInt8]) async throws -> UnifiedSpendingKey {
guard let ffiBinaryKeyPtr = zcashlc_create_account(
let ffiBinaryKeyPtr = zcashlc_create_account(
dbData.0,
dbData.1,
seed,
UInt(seed.count),
networkType.networkId
) else {
throw lastError() ?? .genericError(message: "No error message available")
)
guard let ffiBinaryKeyPtr else {
throw ZcashError.rustCreateAccount(lastErrorMessage(fallback: "`createAccount` failed with unknown error"))
}
defer { zcashlc_free_binary_key(ffiBinaryKeyPtr) }
@ -81,7 +83,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
guard result > 0 else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustCreateToAddress(lastErrorMessage(fallback: "`createToAddress` failed with unknown error"))
}
return result
@ -98,7 +100,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
)
guard result != 0 else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustDecryptAndStoreTransaction(lastErrorMessage(fallback: "`decryptAndStoreTransaction` failed with unknown error"))
}
}
@ -106,26 +108,28 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let balance = zcashlc_get_balance(dbData.0, dbData.1, account, networkType.networkId)
guard balance >= 0 else {
throw throwBalanceError(account: account, lastError(), fallbackMessage: "Error getting total balance from account \(account)")
throw ZcashError.rustGetBalance(Int(account), lastErrorMessage(fallback: "Error getting total balance from account \(account)"))
}
return balance
}
func getCurrentAddress(account: Int32) async throws -> UnifiedAddress {
guard let addressCStr = zcashlc_get_current_address(
let addressCStr = zcashlc_get_current_address(
dbData.0,
dbData.1,
account,
networkType.networkId
) else {
throw lastError() ?? .genericError(message: "No error message available")
)
guard let addressCStr else {
throw ZcashError.rustGetCurrentAddress(lastErrorMessage(fallback: "`getCurrentAddress` failed with unknown error"))
}
defer { zcashlc_string_free(addressCStr) }
guard let address = String(validatingUTF8: addressCStr) else {
throw RustWeldingError.unableToDeriveKeys
throw ZcashError.rustGetCurrentAddressInvalidAddress
}
return UnifiedAddress(validatedEncoding: address, networkType: networkType)
@ -140,26 +144,28 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
)
guard result > 0 else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustGetNearestRewindHeight(lastErrorMessage(fallback: "`getNearestRewindHeight` failed with unknown error"))
}
return result
}
func getNextAvailableAddress(account: Int32) async throws -> UnifiedAddress {
guard let addressCStr = zcashlc_get_next_available_address(
let addressCStr = zcashlc_get_next_available_address(
dbData.0,
dbData.1,
account,
networkType.networkId
) else {
throw lastError() ?? .genericError(message: "No error message available")
)
guard let addressCStr else {
throw ZcashError.rustGetNextAvailableAddress(lastErrorMessage(fallback: "`getNextAvailableAddress` failed with unknown error"))
}
defer { zcashlc_string_free(addressCStr) }
guard let address = String(validatingUTF8: addressCStr) else {
throw RustWeldingError.unableToDeriveKeys
throw ZcashError.rustGetNextAvailableAddressInvalidAddress
}
return UnifiedAddress(validatedEncoding: address, networkType: networkType)
@ -193,7 +199,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
func getTransparentBalance(account: Int32) async throws -> Int64 {
guard account >= 0 else {
throw RustWeldingError.invalidInput(message: "Account index must be non-negative")
throw ZcashError.rustGetTransparentBalanceNegativeAccount(Int(account))
}
let balance = zcashlc_get_total_transparent_balance_for_account(
@ -204,7 +210,10 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
)
guard balance >= 0 else {
throw throwBalanceError(account: account, lastError(), fallbackMessage: "Error getting Total Transparent balance from account \(account)")
throw ZcashError.rustGetTransparentBalance(
Int(account),
lastErrorMessage(fallback: "Error getting Total Transparent balance from account \(account)")
)
}
return balance
@ -220,7 +229,10 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
)
guard balance >= 0 else {
throw throwBalanceError(account: account, lastError(), fallbackMessage: "Error getting verified balance from account \(account)")
throw ZcashError.rustGetVerifiedBalance(
Int(account),
lastErrorMessage(fallback: "Error getting verified balance from account \(account)")
)
}
return balance
@ -228,7 +240,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
func getVerifiedTransparentBalance(account: Int32) async throws -> Int64 {
guard account >= 0 else {
throw RustWeldingError.invalidInput(message: "`account` must be non-negative")
throw ZcashError.rustGetVerifiedTransparentBalanceNegativeAccount(Int(account))
}
let balance = zcashlc_get_verified_transparent_balance_for_account(
@ -240,44 +252,15 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
)
guard balance >= 0 else {
throw throwBalanceError(
account: account,
lastError(),
fallbackMessage: "Error getting verified transparent balance from account \(account)"
throw ZcashError.rustGetVerifiedTransparentBalance(
Int(account),
lastErrorMessage(fallback: "Error getting verified transparent balance from account \(account)")
)
}
return balance
}
private nonisolated func lastError() -> RustWeldingError? {
defer { zcashlc_clear_last_error() }
guard let message = getLastError() else {
return nil
}
if message.contains("couldn't load Sapling spend parameters") {
return RustWeldingError.saplingSpendParametersNotFound
} else if message.contains("is not empty") {
return RustWeldingError.dataDbNotEmpty
}
return RustWeldingError.genericError(message: message)
}
private nonisolated func getLastError() -> String? {
let errorLen = zcashlc_last_error_length()
if errorLen > 0 {
let error = UnsafeMutablePointer<Int8>.allocate(capacity: Int(errorLen))
zcashlc_error_message_utf8(error, errorLen)
zcashlc_clear_last_error()
return String(validatingUTF8: error)
} else {
return nil
}
}
func initDataDb(seed: [UInt8]?) async throws -> DbInitResult {
switch zcashlc_init_data_database(dbData.0, dbData.1, seed, UInt(seed?.count ?? 0), networkType.networkId) {
case 0: // ok
@ -285,7 +268,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
case 1:
return DbInitResult.seedRequired
default:
throw throwDataDbError(lastError() ?? .genericError(message: "No error message found"))
throw ZcashError.rustInitDataDb(lastErrorMessage(fallback: "`initDataDb` failed with unknown error"))
}
}
@ -293,11 +276,11 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
var ffiUfvks: [FFIEncodedKey] = []
for ufvk in ufvks {
guard !ufvk.encoding.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.invalidInput(message: "`UFVK` contains null bytes.")
throw ZcashError.rustInitAccountsTableViewingKeyCotainsNullBytes
}
guard self.keyDeriving.isValidUnifiedFullViewingKey(ufvk.encoding) else {
throw RustWeldingError.invalidInput(message: "UFVK is invalid.")
throw ZcashError.rustInitAccountsTableViewingKeyIsInvalid
}
let ufvkCStr = [CChar](String(ufvk.encoding).utf8CString)
@ -331,7 +314,12 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
guard result else {
throw lastError() ?? .genericError(message: "`initAccountsTable` failed with unknown error")
let message = lastErrorMessage(fallback: "`initAccountsTable` failed with unknown error")
if message.isDbNotEmptyErrorMessage() {
throw ZcashError.rustInitAccountsTableDataDbNotEmpty
} else {
throw ZcashError.rustInitAccountsTable(message)
}
}
}
@ -339,7 +327,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let result = zcashlc_init_block_metadata_db(fsBlockDbRoot.0, fsBlockDbRoot.1)
guard result else {
throw lastError() ?? .genericError(message: "`initAccountsTable` failed with unknown error")
throw ZcashError.rustInitBlockMetadataDb(lastErrorMessage(fallback: "`initBlockMetadataDb` failed with unknown error"))
}
}
@ -362,7 +350,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
hashPtr.deallocate()
ffiBlockMetaVec.deallocateElements()
}
throw RustWeldingError.writeBlocksMetadataAllocationProblem
throw ZcashError.rustWriteBlocksMetadataAllocationProblem
}
ffiBlockMetaVec.append(
@ -395,7 +383,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let res = zcashlc_write_block_metadata(fsBlockDbRoot.0, fsBlockDbRoot.1, fsBlocks)
guard res else {
throw lastError() ?? RustWeldingError.genericError(message: "failed to write block metadata")
throw ZcashError.rustWriteBlocksMetadata(lastErrorMessage(fallback: "`writeBlocksMetadata` failed with unknown error"))
}
}
}
@ -407,14 +395,14 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
saplingTree: String
) async throws {
guard !hash.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.invalidInput(message: "`hash` contains null bytes.")
throw ZcashError.rustInitBlocksTableHashContainsNullBytes
}
guard !saplingTree.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.invalidInput(message: "`saplingTree` contains null bytes.")
throw ZcashError.rustInitBlocksTableSaplingTreeContainsNullBytes
}
guard zcashlc_init_blocks_table(
let result = zcashlc_init_blocks_table(
dbData.0,
dbData.1,
height,
@ -422,8 +410,15 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
time,
[CChar](saplingTree.utf8CString),
networkType.networkId
) != 0 else {
throw lastError() ?? .genericError(message: "No error message available")
)
guard result != 0 else {
let message = lastErrorMessage(fallback: "`initBlocksTable` failed with unknown error")
if message.isDbNotEmptyErrorMessage() {
throw ZcashError.rustInitBlocksTableDataDbNotEmpty
} else {
throw ZcashError.rustInitBlocksTable(message)
}
}
}
@ -432,13 +427,15 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
func listTransparentReceivers(account: Int32) async throws -> [TransparentAddress] {
guard let encodedKeysPtr = zcashlc_list_transparent_receivers(
let encodedKeysPtr = zcashlc_list_transparent_receivers(
dbData.0,
dbData.1,
account,
networkType.networkId
) else {
throw lastError() ?? .genericError(message: "No error message available")
)
guard let encodedKeysPtr else {
throw ZcashError.rustListTransparentReceivers(lastErrorMessage(fallback: "`listTransparentReceivers` failed with unknown error"))
}
defer { zcashlc_free_keys(encodedKeysPtr) }
@ -449,7 +446,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let key = encodedKeysPtr.pointee.ptr.advanced(by: i).pointee
guard let taddrStr = String(validatingUTF8: key.encoding) else {
throw RustWeldingError.unableToDeriveKeys
throw ZcashError.rustListTransparentReceiversInvalidAddress
}
addresses.append(
@ -467,7 +464,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
value: Int64,
height: BlockHeight
) async throws {
guard zcashlc_put_utxo(
let result = zcashlc_put_utxo(
dbData.0,
dbData.1,
txid,
@ -478,8 +475,10 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
value,
Int32(height),
networkType.networkId
) else {
throw lastError() ?? .genericError(message: "No error message available")
)
guard result else {
throw ZcashError.rustPutUnspentTransparentOutput(lastErrorMessage(fallback: "`putUnspentTransparentOutput` failed with unknown error"))
}
}
@ -490,9 +489,11 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
case -1:
return
case 0:
throw RustWeldingError.chainValidationFailed(message: getLastError())
throw ZcashError.rustValidateCombinedChainValidationFailed(
lastErrorMessage(fallback: "`validateCombinedChain` failed with unknown error")
)
default:
throw RustWeldingError.invalidChain(upperBound: result)
throw ZcashError.rustValidateCombinedChainInvalidChain(result)
}
}
@ -500,7 +501,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let result = zcashlc_rewind_to_height(dbData.0, dbData.1, height, networkType.networkId)
guard result else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustRewindToHeight(height, lastErrorMessage(fallback: "`rewindToHeight` failed with unknown error"))
}
}
@ -508,7 +509,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let result = zcashlc_rewind_fs_block_cache_to_height(fsBlockDbRoot.0, fsBlockDbRoot.1, height)
guard result else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustRewindCacheToHeight(lastErrorMessage(fallback: "`rewindCacheToHeight` failed with unknown error"))
}
}
@ -516,7 +517,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let result = zcashlc_scan_blocks(fsBlockDbRoot.0, fsBlockDbRoot.1, dbData.0, dbData.1, limit, networkType.networkId)
guard result != 0 else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustScanBlocks(lastErrorMessage(fallback: "`scanBlocks` failed with unknown error"))
}
}
@ -544,7 +545,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
guard result > 0 else {
throw lastError() ?? .genericError(message: "No error message available")
throw ZcashError.rustShieldFunds(lastErrorMessage(fallback: "`shieldFunds` failed with unknown error"))
}
return result
@ -554,7 +555,7 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
let branchId = zcashlc_branch_id_for_height(height, networkType.networkId)
guard branchId != -1 else {
throw RustWeldingError.noConsensusBranchId(height: height)
throw ZcashError.rustNoConsensusBranchId(height)
}
return branchId
@ -562,20 +563,23 @@ actor ZcashRustBackend: ZcashRustBackendWelding {
}
private extension ZcashRustBackend {
func throwDataDbError(_ error: RustWeldingError) -> Error {
if case RustWeldingError.genericError(let message) = error, message.contains("is not empty") {
return RustWeldingError.dataDbNotEmpty
nonisolated func lastErrorMessage(fallback: String) -> String {
let errorLen = zcashlc_last_error_length()
defer { zcashlc_clear_last_error() }
if errorLen > 0 {
let error = UnsafeMutablePointer<Int8>.allocate(capacity: Int(errorLen))
defer { error.deallocate() }
zcashlc_error_message_utf8(error, errorLen)
if let errorMessage = String(validatingUTF8: error) {
return errorMessage
} else {
return fallback
}
} else {
return fallback
}
return RustWeldingError.dataDbInitFailed(message: error.localizedDescription)
}
func throwBalanceError(account: Int32, _ error: RustWeldingError?, fallbackMessage: String) -> Error {
guard let balanceError = error else {
return RustWeldingError.genericError(message: fallbackMessage)
}
return RustWeldingError.getBalanceError(Int(account), balanceError)
}
}
@ -599,6 +603,10 @@ extension String {
func containsCStringNullBytesBeforeStringEnding() -> Bool {
self.utf8CString.firstIndex(of: 0) != (self.utf8CString.count - 1)
}
func isDbNotEmptyErrorMessage() -> Bool {
return contains("is not empty")
}
}
extension FFIBinaryKey {
@ -627,41 +635,6 @@ extension UnsafeMutablePointer where Pointee == UInt8 {
return bytes
}
}
extension RustWeldingError: LocalizedError {
var errorDescription: String? {
switch self {
case .genericError(let message):
return "RustWeldingError generic error: \(message)"
case .dataDbInitFailed(let message):
return "`RustWeldingError.dataDbInitFailed` with message: \(message)"
case .dataDbNotEmpty:
return "`.DataDbNotEmpty`. This is usually not an error."
case .invalidInput(let message):
return "`RustWeldingError.invalidInput` with message: \(message)"
case .malformedStringInput:
return "`.malformedStringInput` Called a function with a malformed string input."
case .invalidRewind:
return "`.invalidRewind` called the rewind API with an arbitrary height that is not valid."
case .noConsensusBranchId(let branchId):
return "`.noConsensusBranchId` number \(branchId)"
case .saplingSpendParametersNotFound:
return "`.saplingSpendParametersNotFound` sapling parameters not present at specified URL"
case .unableToDeriveKeys:
return "`.unableToDeriveKeys` the requested keys could not be derived from the source provided"
case let .getBalanceError(account, error):
return "`.getBalanceError` could not retrieve balance from account: \(account), error:\(error)"
case let .invalidChain(upperBound: upperBound):
return "`.validateCombinedChain` failed to validate chain. Upper bound: \(upperBound)."
case let .chainValidationFailed(message):
return """
`.validateCombinedChain` failed to validate chain because of error unrelated to chain validity. \
Message: \(String(describing: message))
"""
case .writeBlocksMetadataAllocationProblem:
return "`.writeBlocksMetadata` failed to allocate memory on Swift side necessary to write blocks metadata to db."
}
}
}
extension Array where Element == FFIBlockMeta {
func deallocateElements() {

View File

@ -8,26 +8,6 @@
import Foundation
enum RustWeldingError: Error {
case genericError(message: String)
case dataDbInitFailed(message: String)
case dataDbNotEmpty
case saplingSpendParametersNotFound
case malformedStringInput
case noConsensusBranchId(height: Int32)
case unableToDeriveKeys
case getBalanceError(Int, Error)
case invalidInput(message: String)
case invalidRewind(suggestedHeight: Int32)
/// Thrown when `upperBound` if the combined chain is invalid. `upperBound` is the height of the highest invalid block (on the assumption that
/// the highest block in the cache database is correct).
case invalidChain(upperBound: Int32)
/// Thrown if there was an error during validation unrelated to chain validity.
case chainValidationFailed(message: String?)
/// Thrown if there was problem with memory allocation on the Swift side while trying to write blocks metadata to DB.
case writeBlocksMetadataAllocationProblem
}
enum ZcashRustBackendWeldingConstants {
static let validChain: Int32 = -1
}
@ -56,6 +36,7 @@ protocol ZcashRustBackendWelding {
/// automated account recovery).
/// - parameter seed: byte array of the zip32 seed
/// - Returns: The `UnifiedSpendingKey` structs for the number of accounts created
/// - Throws: `rustCreateAccount`.
func createAccount(seed: [UInt8]) async throws -> UnifiedSpendingKey
/// Creates a transaction to the given address from the given account
@ -63,6 +44,7 @@ protocol ZcashRustBackendWelding {
/// - Parameter to: recipient address
/// - Parameter value: transaction amount in Zatoshi
/// - Parameter memo: the `MemoBytes` for this transaction. pass `nil` when sending to transparent receivers
/// - Throws: `rustCreateToAddress`.
func createToAddress(
usk: UnifiedSpendingKey,
to address: String,
@ -73,14 +55,19 @@ protocol ZcashRustBackendWelding {
/// Scans a transaction for any information that can be decrypted by the accounts in the wallet, and saves it to the wallet.
/// - parameter tx: the transaction to decrypt
/// - parameter minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID.
/// - Throws: `rustDecryptAndStoreTransaction`.
func decryptAndStoreTransaction(txBytes: [UInt8], minedHeight: Int32) async throws
/// Get the (unverified) balance from the given account.
/// - parameter account: index of the given account
/// - Throws: `rustGetBalance`.
func getBalance(account: Int32) async throws -> Int64
/// Returns the most-recently-generated unified payment address for the specified account.
/// - parameter account: index of the given account
/// - Throws:
/// - `rustGetCurrentAddress` if rust layer returns error.
/// - `rustGetCurrentAddressInvalidAddress` if generated unified address isn't valid.
func getCurrentAddress(account: Int32) async throws -> UnifiedAddress
/// Wallets might need to be rewound because of a reorg, or by user request.
@ -92,10 +79,14 @@ protocol ZcashRustBackendWelding {
/// 100 blocks or back to the oldest unspent note that this wallet contains.
/// - parameter height: height you would like to rewind to.
/// - Returns: the blockheight of the nearest rewind height.
/// - Throws: `rustGetNearestRewindHeight`.
func getNearestRewindHeight(height: Int32) async throws -> Int32
/// Returns a newly-generated unified payment address for the specified account, with the next available diversifier.
/// - parameter account: index of the given account
/// - Throws:
/// - `rustGetNextAvailableAddress` if rust layer returns error.
/// - `rustGetNextAvailableAddressInvalidAddress` if generated unified address isn't valid.
func getNextAvailableAddress(account: Int32) async throws -> UnifiedAddress
/// Get received memo from note.
@ -109,11 +100,17 @@ protocol ZcashRustBackendWelding {
/// Get the verified cached transparent balance for the given address
/// - parameter account; the account index to query
/// - Throws:
/// - `rustGetTransparentBalanceNegativeAccount` if `account` is < 0.
/// - `rustGetTransparentBalance` if rust layer returns error.
func getTransparentBalance(account: Int32) async throws -> Int64
/// Initialize the accounts table from a set of unified full viewing keys.
/// - Note: this function should only be used when restoring an existing seed phrase. when creating a new wallet, use `createAccount()` instead.
/// - Parameter ufvks: an array of UnifiedFullViewingKeys
/// - Throws:
/// - `rustInitAccountsTableViewingKeyCotainsNullBytes` if any of the key in `ufvks` contains null bytes before end.
/// - `rustInitAccountsTableViewingKeyIsInvalid` if any of the key in `ufvks` isn't valid.
func initAccountsTable(ufvks: [UnifiedFullViewingKey]) async throws
/// Initializes the data db. This will performs any migrations needed on the sqlite file
@ -122,6 +119,7 @@ protocol ZcashRustBackendWelding {
/// - Returns: `DbInitResult.success` if the dataDb was initialized successfully
/// or `DbInitResult.seedRequired` if the operation requires the seed to be passed
/// in order to be completed successfully.
/// Throws `rustInitDataDb` if rust layer returns error.
func initDataDb(seed: [UInt8]?) async throws -> DbInitResult
/// Initialize the blocks table from a given checkpoint (heigh, hash, time, saplingTree and networkType).
@ -129,6 +127,11 @@ protocol ZcashRustBackendWelding {
/// - parameter hash: hash of the merkle tree
/// - parameter time: in milliseconds from reference
/// - parameter saplingTree: hash of the sapling tree
/// - Throws:
/// - `rustInitBlocksTableHashContainsNullBytes` if `hash` contains null bytes before end.
/// - `rustInitBlocksTableSaplingTreeContainsNullBytes` if `saplingTree` contains null bytes before end.
/// - `rustInitBlocksTableDataDbNotEmpty` if data DB is not empty.
/// - `rustInitBlocksTable` if rust layer returns error.
func initBlocksTable(
height: Int32,
hash: String,
@ -139,14 +142,21 @@ protocol ZcashRustBackendWelding {
/// Returns a list of the transparent receivers for the diversified unified addresses that have
/// been allocated for the provided account.
/// - parameter account: index of the given account
/// - Throws:
/// - `rustListTransparentReceivers` if rust layer returns error.
/// - `rustListTransparentReceiversInvalidAddress` if transarent received generated by rust is invalid.
func listTransparentReceivers(account: Int32) async throws -> [TransparentAddress]
/// Get the verified balance from the given account
/// - parameter account: index of the given account
/// - Throws: `rustGetVerifiedBalance` when rust layer throws error.
func getVerifiedBalance(account: Int32) async throws -> Int64
/// Get the verified cached transparent balance for the given account
/// - parameter account: account index to query the balance for.
/// - Throws:
/// - `rustGetVerifiedTransparentBalanceNegativeAccount` if `account` is < 0.
/// - `rustGetVerifiedTransparentBalance` if rust layer returns error.
func getVerifiedTransparentBalance(account: Int32) async throws -> Int64
/// Checks that the scanned blocks in the data database, when combined with the recent
@ -162,20 +172,22 @@ protocol ZcashRustBackendWelding {
/// - parameter networkType: the network type
/// - parameter limit: a limit to validate a fixed number of blocks instead of the whole cache.
/// - Throws:
/// - `RustWeldingError.chainValidationFailed` if there was an error during validation unrelated to chain validity.
/// - `RustWeldingError.invalidChain(upperBound)` if the combined chain is invalid. `upperBound` is the height of the highest invalid block
/// (on the assumption that the highest block in the cache database is correct).
/// - `rustValidateCombinedChainValidationFailed` if there was an error during validation unrelated to chain validity.
/// - `rustValidateCombinedChainInvalidChain(upperBound)` if the combined chain is invalid. `upperBound` is the height of the highest invalid
/// block(on the assumption that the highest block in the cache database is correct).
///
/// - Important: This function does not mutate either of the databases.
func validateCombinedChain(limit: UInt32) async throws
/// Resets the state of the database to only contain block and transaction information up to the given height. clears up all derived data as well
/// - parameter height: height to rewind to.
/// - Throws: `rustRewindToHeight` if rust layer returns error.
func rewindToHeight(height: Int32) async throws
/// Resets the state of the FsBlock database to only contain block and transaction information up to the given height.
/// - Note: this does not delete the files. Only rolls back the database.
/// - parameter height: height to rewind to. DON'T PASS ARBITRARY HEIGHT. Use `getNearestRewindHeight` when unsure
/// - Throws: `rustRewindCacheToHeight` if rust layer returns error.
func rewindCacheToHeight(height: Int32) async throws
/// Scans new blocks added to the cache for any transactions received by the tracked
@ -191,6 +203,7 @@ protocol ZcashRustBackendWelding {
/// cache, an error will be signalled.
///
/// - parameter limit: scan up to limit blocks. pass 0 to set no limit.
/// - Throws: `rustScanBlocks` if rust layer returns error.
func scanBlocks(limit: UInt32) async throws
/// Upserts a UTXO into the data db database
@ -199,6 +212,7 @@ protocol ZcashRustBackendWelding {
/// - parameter script: the script of the UTXO
/// - parameter value: the value of the UTXO
/// - parameter height: the mined height for the UTXO
/// - Throws: `rustPutUnspentTransparentOutput` if rust layer returns error.
func putUnspentTransparentOutput(
txid: [UInt8],
index: Int,
@ -210,6 +224,7 @@ protocol ZcashRustBackendWelding {
/// Creates a transaction to shield all found UTXOs in data db for the account the provided `UnifiedSpendingKey` has spend authority for.
/// - Parameter usk: `UnifiedSpendingKey` that spend transparent funds and where the funds will be shielded to.
/// - Parameter memo: the `Memo` for this transaction
/// - Throws: `rustShieldFunds` if rust layer returns error.
func shieldFunds(
usk: UnifiedSpendingKey,
memo: MemoBytes?,
@ -218,14 +233,18 @@ protocol ZcashRustBackendWelding {
/// Gets the consensus branch id for the given height
/// - Parameter height: the height you what to know the branch id for
/// - Throws: `rustNoConsensusBranchId` if rust layer returns error.
func consensusBranchIdFor(height: Int32) throws -> Int32
/// Initializes Filesystem based block cache
/// - throws `RustWeldingError` when fails to initialize
/// - Throws: `rustInitBlockMetadataDb` if rust layer returns error.
func initBlockMetadataDb() async throws
/// Write compact block metadata to a database known to the Rust layer
/// - Parameter blocks: The `ZcashCompactBlock`s that are going to be marked as stored by the metadata Db.
/// - Throws:
/// - `rustWriteBlocksMetadataAllocationProblem` if there problem with allocating memory on Swift side.
/// - `rustWriteBlocksMetadata` if there is problem with writing blocks metadata.
func writeBlocksMetadata(blocks: [ZcashCompactBlock]) async throws
/// Gets the latest block height stored in the filesystem based cache.

View File

@ -9,44 +9,6 @@
import Combine
import Foundation
/// Represents errors thrown by a Synchronizer
public enum SynchronizerError: Error {
case initFailed(message: String) // ZcashLightClientKit.SynchronizerError error 0.
case notPrepared // ZcashLightClientKit.SynchronizerError error 9.
case syncFailed // ZcashLightClientKit.SynchronizerError error 10.
case connectionFailed(message: Error) // ZcashLightClientKit.SynchronizerError error 1.
case generalError(message: String) // ZcashLightClientKit.SynchronizerError error 2.
case maxRetryAttemptsReached(attempts: Int) // ZcashLightClientKit.SynchronizerError error 3.
case connectionError(status: Int, message: String) // ZcashLightClientKit.SynchronizerError error 4.
case networkTimeout // ZcashLightClientKit.SynchronizerError error 11.
case uncategorized(underlyingError: Error) // ZcashLightClientKit.SynchronizerError error 5.
case criticalError // ZcashLightClientKit.SynchronizerError error 12.
case parameterMissing(underlyingError: Error) // ZcashLightClientKit.SynchronizerError error 6.
case rewindError(underlyingError: Error) // ZcashLightClientKit.SynchronizerError error 7.
case rewindErrorUnknownArchorHeight // ZcashLightClientKit.SynchronizerError error 13.
case invalidAccount // ZcashLightClientKit.SynchronizerError error 14.
case lightwalletdValidationFailed(underlyingError: Error) // ZcashLightClientKit.SynchronizerError error 8.
}
public enum ShieldFundsError: Error {
case noUTXOFound
case insuficientTransparentFunds
case shieldingFailed(underlyingError: Error)
}
extension ShieldFundsError: LocalizedError {
public var errorDescription: String? {
switch self {
case .noUTXOFound:
return "Could not find UTXOs for the given t-address"
case .insuficientTransparentFunds:
return "You don't have enough confirmed transparent funds to perform a shielding transaction."
case .shieldingFailed(let underlyingError):
return "Shielding transaction failed. Reason: \(underlyingError)"
}
}
}
/// Represent the connection state to the lightwalletd server
public enum ConnectionState {
/// not in use
@ -151,12 +113,11 @@ public protocol Synchronizer: AnyObject {
/// - viewingKeys: Viewing key derived from seed.
/// - walletBirthday: Birthday of wallet.
/// - Throws:
/// `InitializerError.dataDbInitFailed` if the creation of the dataDb fails
/// `InitializerError.accountInitFailed` if the account table can't be initialized.
/// `InitializerError.aliasAlreadyInUse` if the Alias used to create this instance is already used by other instance
/// `InitializerError.cantUpdateURLWithAlias` if the updating of paths in `Initilizer` according to alias fails. When this happens it means that
/// some path passed to `Initializer` is invalid. The SDK can't recover from this and this instance
/// won't do anything.
/// - `aliasAlreadyInUse` if the Alias used to create this instance is already used by other instance.
/// - `cantUpdateURLWithAlias` if the updating of paths in `Initilizer` according to alias fails. When this happens it means that
/// some path passed to `Initializer` is invalid. The SDK can't recover from this and this instance
/// won't do anything.
/// - Some other `ZcashError` thrown by lower layer of the SDK.
func prepare(
with seed: [UInt8]?,
viewingKeys: [UnifiedFullViewingKey],
@ -330,10 +291,10 @@ public protocol Synchronizer: AnyObject {
/// fails then something is seriously wrong. If the wipe fails then the SDK may be in inconsistent state. It's suggested to call wipe again until
/// it succeed.
///
/// Returned publisher emits `InitializerError.aliasAlreadyInUse` error if the Alias used to create this instance is already used by other
/// Returned publisher emits `initializerCantUpdateURLWithAlias` error if the Alias used to create this instance is already used by other
/// instance.
///
/// Returned publisher emits `InitializerError.cantUpdateURLWithAlias` if the updating of paths in `Initilizer` according to alias fails. When
/// Returned publisher emits `initializerAliasAlreadyInUse` if the updating of paths in `Initilizer` according to alias fails. When
/// this happens it means that some path passed to `Initializer` is invalid. The SDK can't recover from this and this instance won't do anything.
///
func wipe() -> AnyPublisher<Void, Error>
@ -365,7 +326,7 @@ public enum SyncStatus: Equatable {
/// When set, a UI element may want to turn red.
case disconnected
case error(_ error: SynchronizerError)
case error(_ error: Error)
public var isSyncing: Bool {
switch self {

View File

@ -34,16 +34,16 @@ public class SDKSynchronizer: Synchronizer {
let blockProcessor: CompactBlockProcessor
lazy var blockProcessorEventProcessingQueue = { DispatchQueue(label: "blockProcessorEventProcessingQueue_\(initializer.alias.description)") }()
public private(set) var initializer: Initializer
public private(set) var connectionState: ConnectionState
public private(set) var network: ZcashNetwork
private var transactionManager: OutboundTransactionManager
private var transactionRepository: TransactionRepository
private var utxoRepository: UnspentTransactionOutputRepository
public let initializer: Initializer
public var connectionState: ConnectionState
public let network: ZcashNetwork
private let transactionManager: OutboundTransactionManager
private let transactionRepository: TransactionRepository
private let utxoRepository: UnspentTransactionOutputRepository
private var syncSessionIDGenerator: SyncSessionIDGenerator
private var syncSession: SyncSession
private var syncSessionTicker: SessionTicker
private let syncSessionIDGenerator: SyncSessionIDGenerator
private let syncSession: SyncSession
private let syncSessionTicker: SessionTicker
private var syncStartDate: Date?
let latestBlocksDataProvider: LatestBlocksDataProvider
@ -123,17 +123,17 @@ public class SDKSynchronizer: Synchronizer {
func throwIfUnprepared() throws {
if !latestState.syncStatus.isPrepared {
throw SynchronizerError.notPrepared
throw ZcashError.synchronizerNotPrepared
}
}
func checkIfCanContinueInitialisation() -> InitializerError? {
func checkIfCanContinueInitialisation() -> ZcashError? {
if let initialisationError = initializer.urlsParsingError {
return initialisationError
}
if !UsedAliasesChecker.tryToUse(alias: initializer.alias, id: initializer.id) {
return InitializerError.aliasAlreadyInUse(initializer.alias)
return .initializerAliasAlreadyInUse(initializer.alias)
}
return nil
@ -165,11 +165,11 @@ public class SDKSynchronizer: Synchronizer {
}
/// Starts the synchronizer
/// - Throws: CompactBlockProcessorError when failures occur
/// - Throws: ZcashError when failures occur
public func start(retry: Bool = false) async throws {
switch await status {
case .unprepared:
throw SynchronizerError.notPrepared
throw ZcashError.synchronizerNotPrepared
case .syncing, .enhancing, .fetching:
logger.warn("warning: Synchronizer started when already running. Next sync process will be started when the current one stops.")
@ -244,8 +244,8 @@ public class SDKSynchronizer: Synchronizer {
await processor.updateEventClosure(identifier: "SDKSynchronizer", closure: eventClosure)
}
private func failed(error: CompactBlockProcessorError) async {
await updateStatus(.error(self.mapError(error)))
private func failed(error: Error) async {
await updateStatus(.error(error))
}
private func finished(lastScannedHeight: BlockHeight, foundBlocks: Bool) async {
@ -299,20 +299,16 @@ public class SDKSynchronizer: Synchronizer {
) async throws -> PendingTransactionEntity {
try throwIfUnprepared()
do {
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
spendURL: initializer.spendParamsURL,
spendSourceURL: initializer.saplingParamsSourceURL.spendParamFileURL,
outputURL: initializer.outputParamsURL,
outputSourceURL: initializer.saplingParamsSourceURL.outputParamFileURL,
logger: logger
)
} catch {
throw SynchronizerError.parameterMissing(underlyingError: error)
}
try await SaplingParameterDownloader.downloadParamsIfnotPresent(
spendURL: initializer.spendParamsURL,
spendSourceURL: initializer.saplingParamsSourceURL.spendParamFileURL,
outputURL: initializer.outputParamsURL,
outputSourceURL: initializer.saplingParamsSourceURL.outputParamFileURL,
logger: logger
)
if case Recipient.transparent = toAddress, memo != nil {
throw SynchronizerError.generalError(message: "Memos can't be sent to transparent addresses.")
throw ZcashError.synchronizerSendMemoToTransparentAddress
}
return try await createToAddress(
@ -332,32 +328,28 @@ public class SDKSynchronizer: Synchronizer {
// let's see if there are funds to shield
let accountIndex = Int(spendingKey.account)
do {
let tBalance = try await self.getTransparentBalance(accountIndex: accountIndex)
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
guard tBalance.verified >= self.network.constants.defaultFee(for: await self.latestBlocksDataProvider.latestScannedHeight) else {
throw ShieldFundsError.insuficientTransparentFunds
}
let shieldingSpend = try await transactionManager.initSpend(
zatoshi: tBalance.verified,
recipient: .internalAccount(spendingKey.account),
memo: try memo.asMemoBytes(),
from: accountIndex
)
// TODO: [#487] Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487
let transaction = try await transactionManager.encodeShieldingTransaction(
spendingKey: spendingKey,
shieldingThreshold: shieldingThreshold,
pendingTransaction: shieldingSpend
)
return try await transactionManager.submit(pendingTransaction: transaction)
} catch {
throw error
let tBalance = try await self.getTransparentBalance(accountIndex: accountIndex)
// Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet.
guard tBalance.verified >= self.network.constants.defaultFee(for: await self.latestBlocksDataProvider.latestScannedHeight) else {
throw ZcashError.synchronizerShieldFundsInsuficientTransparentFunds
}
let shieldingSpend = try await transactionManager.initSpend(
zatoshi: tBalance.verified,
recipient: .internalAccount(spendingKey.account),
memo: try memo.asMemoBytes(),
from: accountIndex
)
// TODO: [#487] Task will be removed when this method is changed to async, issue 487, https://github.com/zcash/ZcashLightClientKit/issues/487
let transaction = try await transactionManager.encodeShieldingTransaction(
spendingKey: spendingKey,
shieldingThreshold: shieldingThreshold,
pendingTransaction: shieldingSpend
)
return try await transactionManager.submit(pendingTransaction: transaction)
}
func createToAddress(
@ -366,23 +358,19 @@ public class SDKSynchronizer: Synchronizer {
recipient: Recipient,
memo: Memo?
) async throws -> PendingTransactionEntity {
do {
let spend = try await transactionManager.initSpend(
zatoshi: zatoshi,
recipient: .address(recipient),
memo: memo?.asMemoBytes(),
from: Int(spendingKey.account)
)
let transaction = try await transactionManager.encode(
spendingKey: spendingKey,
pendingTransaction: spend
)
let submittedTx = try await transactionManager.submit(pendingTransaction: transaction)
return submittedTx
} catch {
throw error
}
let spend = try await transactionManager.initSpend(
zatoshi: zatoshi,
recipient: .address(recipient),
memo: memo?.asMemoBytes(),
from: Int(spendingKey.account)
)
let transaction = try await transactionManager.encode(
spendingKey: spendingKey,
pendingTransaction: spend
)
let submittedTx = try await transactionManager.submit(pendingTransaction: transaction)
return submittedTx
}
public func cancelSpend(transaction: PendingTransactionEntity) async -> Bool {
@ -441,23 +429,19 @@ public class SDKSynchronizer: Synchronizer {
try throwIfUnprepared()
guard initializer.isValidTransparentAddress(address) else {
throw SynchronizerError.generalError(message: "invalid t-address")
throw ZcashError.synchronizerLatestUTXOsInvalidTAddress
}
let stream = initializer.lightWalletService.fetchUTXOs(for: address, height: network.constants.saplingActivationHeight)
do {
// swiftlint:disable:next array_constructor
var utxos: [UnspentTransactionOutputEntity] = []
for try await transactionEntity in stream {
utxos.append(transactionEntity)
}
try await self.utxoRepository.clearAll(address: address)
try await self.utxoRepository.store(utxos: utxos)
return utxos
} catch {
throw SynchronizerError.generalError(message: "\(error)")
// swiftlint:disable:next array_constructor
var utxos: [UnspentTransactionOutputEntity] = []
for try await transactionEntity in stream {
utxos.append(transactionEntity)
}
try await self.utxoRepository.clearAll(address: address)
try await self.utxoRepository.store(utxos: utxos)
return utxos
}
public func refreshUTXOs(address: TransparentAddress, from height: BlockHeight) async throws -> RefreshedUTXOs {
@ -500,7 +484,7 @@ public class SDKSynchronizer: Synchronizer {
let subject = PassthroughSubject<Void, Error>()
Task(priority: .high) {
if !latestState.syncStatus.isPrepared {
subject.send(completion: .failure(SynchronizerError.notPrepared))
subject.send(completion: .failure(ZcashError.synchronizerNotPrepared))
return
}
@ -519,7 +503,7 @@ public class SDKSynchronizer: Synchronizer {
case .transaction(let transaction):
guard let txHeight = transaction.anchor(network: self.network) else {
throw SynchronizerError.rewindErrorUnknownArchorHeight
throw ZcashError.synchronizerRewindUnknownArchorHeight
}
height = txHeight
}
@ -533,7 +517,7 @@ public class SDKSynchronizer: Synchronizer {
try await self?.transactionManager.handleReorg(at: rewindHeight)
subject.send(completion: .finished)
} catch {
subject.send(completion: .failure(SynchronizerError.rewindError(underlyingError: error)))
subject.send(completion: .failure(error))
}
case let .failure(error):
@ -670,46 +654,6 @@ public class SDKSynchronizer: Synchronizer {
self?.eventSubject.send(.minedTransaction(transaction))
}
}
// swiftlint:disable cyclomatic_complexity
private func mapError(_ error: Error) -> SynchronizerError {
if let compactBlockProcessorError = error as? CompactBlockProcessorError {
switch compactBlockProcessorError {
case .dataDbInitFailed(let path):
return SynchronizerError.initFailed(message: "DataDb init failed at path: \(path)")
case .connectionError(let message):
return SynchronizerError.connectionFailed(message: message)
case .invalidConfiguration:
return SynchronizerError.generalError(message: "Invalid Configuration")
case .missingDbPath(let path):
return SynchronizerError.initFailed(message: "missing Db path: \(path)")
case .generalError(let message):
return SynchronizerError.generalError(message: message)
case .maxAttemptsReached(attempts: let attempts):
return SynchronizerError.maxRetryAttemptsReached(attempts: attempts)
case let .grpcError(statusCode, message):
return SynchronizerError.connectionError(status: statusCode, message: message)
case .connectionTimeout:
return SynchronizerError.networkTimeout
case .unspecifiedError(let underlyingError):
return SynchronizerError.uncategorized(underlyingError: underlyingError)
case .criticalError:
return SynchronizerError.criticalError
case .invalidAccount:
return SynchronizerError.invalidAccount
case .wrongConsensusBranchId:
return SynchronizerError.lightwalletdValidationFailed(underlyingError: compactBlockProcessorError)
case .networkMismatch:
return SynchronizerError.lightwalletdValidationFailed(underlyingError: compactBlockProcessorError)
case .saplingActivationMismatch:
return SynchronizerError.lightwalletdValidationFailed(underlyingError: compactBlockProcessorError)
case .unknown:
break
}
}
return SynchronizerError.uncategorized(underlyingError: error)
}
}
extension SDKSynchronizer {

View File

@ -20,7 +20,9 @@ public protocol KeyDeriving {
/// Given the seed bytes tand the account index, return the UnifiedSpendingKey
/// - Parameter seed: `[Uint8]` seed bytes
/// - Parameter accountNumber: `Int` with the account number
/// - Throws `.unableToDerive` if there's a problem deriving this key
/// - Throws:
/// - `derivationToolSpendingKeyInvalidAccount` if the `accountIndex` is invalid.
/// - some `ZcashError.rust*` error if the derivation fails.
/// - Returns a `UnifiedSpendingKey`
func deriveUnifiedSpendingKey(seed: [UInt8], accountIndex: Int) async throws -> UnifiedSpendingKey
func deriveUnifiedSpendingKey(seed: [UInt8], accountIndex: Int, completion: @escaping (Result<UnifiedSpendingKey, Error>) -> Void)
@ -28,6 +30,7 @@ public protocol KeyDeriving {
/// Given a spending key, return the associated viewing key.
/// - Parameter spendingKey: the `UnifiedSpendingKey` from which to derive the `UnifiedFullViewingKey` from.
/// - Throws: some `ZcashError.rust*` error if the derivation fails.
/// - Returns: the viewing key that corresponds to the spending key.
func deriveUnifiedFullViewingKey(from spendingKey: UnifiedSpendingKey) async throws -> UnifiedFullViewingKey
func deriveUnifiedFullViewingKey(from spendingKey: UnifiedSpendingKey, completion: @escaping (Result<UnifiedFullViewingKey, Error>) -> Void)
@ -35,32 +38,22 @@ public protocol KeyDeriving {
/// Extracts the `SaplingAddress` from the given `UnifiedAddress`
/// - Parameter address: the `UnifiedAddress`
/// - Throws `KeyDerivationErrors.receiverNotFound` if the receiver is not present
/// - Throws: some `ZcashError.rust*` error if the derivation fails.
func saplingReceiver(from unifiedAddress: UnifiedAddress) throws -> SaplingAddress
/// Extracts the `TransparentAddress` from the given `UnifiedAddress`
/// - Parameter address: the `UnifiedAddress`
/// - Throws `KeyDerivationErrors.receiverNotFound` if the receiver is not present
/// - Throws: some `ZcashError.rust*` error if the derivation fails.
func transparentReceiver(from unifiedAddress: UnifiedAddress) throws -> TransparentAddress
/// Extracts the `UnifiedAddress.ReceiverTypecodes` from the given `UnifiedAddress`
/// - Throws: some `ZcashError.rust*` error if the derivation fails.
/// - Parameter address: the `UnifiedAddress`
/// - Throws
func receiverTypecodesFromUnifiedAddress(_ address: UnifiedAddress) throws -> [UnifiedAddress.ReceiverTypecodes]
static func getAddressMetadata(_ addr: String) -> AddressMetadata?
}
public enum KeyDerivationErrors: Error {
case derivationError(underlyingError: Error)
// When something happens that is not related to derivation itself happens. For example if self is nil in closure.
case genericOtherError
case unableToDerive
case invalidInput
case invalidUnifiedAddress
case receiverNotFound
}
public class DerivationTool: KeyDeriving {
let backend: ZcashKeyDerivationBackendWelding
@ -88,16 +81,14 @@ public class DerivationTool: KeyDeriving {
}
public func deriveUnifiedFullViewingKey(from spendingKey: UnifiedSpendingKey, completion: @escaping (Result<UnifiedFullViewingKey, Error>) -> Void) {
AsyncToClosureGateway.executeThrowingAction(completion) { [weak self] in
guard let self else { throw KeyDerivationErrors.genericOtherError }
return try await self.deriveUnifiedFullViewingKey(from: spendingKey)
AsyncToClosureGateway.executeThrowingAction(completion) { [backend] in
return try await backend.deriveUnifiedFullViewingKey(from: spendingKey)
}
}
public func deriveUnifiedFullViewingKey(from spendingKey: UnifiedSpendingKey) -> SinglePublisher<UnifiedFullViewingKey, Error> {
AsyncToCombineGateway.executeThrowingAction() { [weak self] in
guard let self else { throw KeyDerivationErrors.genericOtherError }
return try await self.deriveUnifiedFullViewingKey(from: spendingKey)
AsyncToCombineGateway.executeThrowingAction() { [backend] in
return try await backend.deriveUnifiedFullViewingKey(from: spendingKey)
}
}
@ -107,43 +98,33 @@ public class DerivationTool: KeyDeriving {
/// supported so the default value of 1 is recommended.
/// - Returns: the spending keys that correspond to the seed, formatted as Strings.
public func deriveUnifiedSpendingKey(seed: [UInt8], accountIndex: Int) async throws -> UnifiedSpendingKey {
guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else {
throw KeyDerivationErrors.invalidInput
}
do {
return try await backend.deriveUnifiedSpendingKey(from: seed, accountIndex: accountIndex)
} catch {
throw KeyDerivationErrors.unableToDerive
}
guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else { throw ZcashError.derivationToolSpendingKeyInvalidAccount }
return try await backend.deriveUnifiedSpendingKey(from: seed, accountIndex: accountIndex)
}
public func deriveUnifiedSpendingKey(seed: [UInt8], accountIndex: Int, completion: @escaping (Result<UnifiedSpendingKey, Error>) -> Void) {
AsyncToClosureGateway.executeThrowingAction(completion) { [weak self] in
guard let self else { throw KeyDerivationErrors.genericOtherError }
return try await self.deriveUnifiedSpendingKey(seed: seed, accountIndex: accountIndex)
AsyncToClosureGateway.executeThrowingAction(completion) { [backend] in
guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else { throw ZcashError.derivationToolSpendingKeyInvalidAccount }
return try await backend.deriveUnifiedSpendingKey(from: seed, accountIndex: accountIndex)
}
}
public func deriveUnifiedSpendingKey(seed: [UInt8], accountIndex: Int) -> SinglePublisher<UnifiedSpendingKey, Error> {
AsyncToCombineGateway.executeThrowingAction() { [weak self] in
guard let self else { throw KeyDerivationErrors.genericOtherError }
return try await self.deriveUnifiedSpendingKey(seed: seed, accountIndex: accountIndex)
AsyncToCombineGateway.executeThrowingAction() { [backend] in
guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else { throw ZcashError.derivationToolSpendingKeyInvalidAccount }
return try await backend.deriveUnifiedSpendingKey(from: seed, accountIndex: accountIndex)
}
}
public func receiverTypecodesFromUnifiedAddress(_ address: UnifiedAddress) throws -> [UnifiedAddress.ReceiverTypecodes] {
do {
return try backend.receiverTypecodesOnUnifiedAddress(address.stringEncoded)
.map({ UnifiedAddress.ReceiverTypecodes(typecode: $0) })
} catch {
throw KeyDerivationErrors.invalidUnifiedAddress
}
return try backend.receiverTypecodesOnUnifiedAddress(address.stringEncoded)
.map { UnifiedAddress.ReceiverTypecodes(typecode: $0) }
}
}
public struct AddressMetadata {
var networkType: NetworkType
var addressType: AddressType
let networkType: NetworkType
let addressType: AddressType
public init(network: NetworkType, addrType: AddressType) {
self.networkType = network

View File

@ -7,24 +7,12 @@
import Foundation
enum TransactionManagerError: Error {
case couldNotCreateSpend(recipient: PendingTransactionRecipient, account: Int, zatoshi: Zatoshi)
case encodingFailed(PendingTransactionEntity)
case updateFailed(PendingTransactionEntity)
case notPending(PendingTransactionEntity)
case cancelled(PendingTransactionEntity)
case internalInconsistency(PendingTransactionEntity)
case submitFailed(PendingTransactionEntity, errorCode: Int)
case shieldingEncodingFailed(PendingTransactionEntity, reason: String)
case cannotEncodeInternalTx(PendingTransactionEntity)
}
class PersistentTransactionManager: OutboundTransactionManager {
var repository: PendingTransactionRepository
var encoder: TransactionEncoder
var service: LightWalletService
var queue: DispatchQueue
var network: NetworkType
let repository: PendingTransactionRepository
let encoder: TransactionEncoder
let service: LightWalletService
let queue: DispatchQueue
let network: NetworkType
let logger: Logger
init(
@ -58,11 +46,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
)
)
) else {
throw TransactionManagerError.couldNotCreateSpend(
recipient: recipient,
account: accountIndex,
zatoshi: zatoshi
)
throw ZcashError.persistentTransManagerCantCreateTransaction(recipient, accountIndex, zatoshi)
}
logger.debug("pending transaction \(String(describing: insertedTx.id)) created")
return insertedTx
@ -73,36 +57,23 @@ class PersistentTransactionManager: OutboundTransactionManager {
shieldingThreshold: Zatoshi,
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity {
do {
let transaction = try await self.encoder.createShieldingTransaction(
spendingKey: spendingKey,
shieldingThreshold: shieldingThreshold,
memoBytes: try pendingTransaction.memo?.intoMemoBytes(),
from: pendingTransaction.accountIndex
)
var pending = pendingTransaction
pending.encodeAttempts += 1
pending.raw = transaction.raw
pending.rawTransactionId = transaction.rawID
pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty()
pending.minedHeight = transaction.minedHeight ?? BlockHeight.empty()
try self.repository.update(pending)
return pending
} catch DatabaseStorageError.updateFailed {
throw TransactionManagerError.updateFailed(pendingTransaction)
} catch MemoBytes.Errors.invalidUTF8 {
throw TransactionManagerError.shieldingEncodingFailed(pendingTransaction, reason: "Memo contains invalid UTF-8 bytes")
} catch MemoBytes.Errors.tooLong(let length) {
throw TransactionManagerError.shieldingEncodingFailed(
pendingTransaction,
reason: "Memo is too long. expected 512 bytes, received \(length)"
)
} catch {
throw error
}
let transaction = try await self.encoder.createShieldingTransaction(
spendingKey: spendingKey,
shieldingThreshold: shieldingThreshold,
memoBytes: try pendingTransaction.memo?.intoMemoBytes(),
from: pendingTransaction.accountIndex
)
var pending = pendingTransaction
pending.encodeAttempts += 1
pending.raw = transaction.raw
pending.rawTransactionId = transaction.rawID
pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty()
pending.minedHeight = transaction.minedHeight ?? BlockHeight.empty()
try self.repository.update(pending)
return pending
}
func encode(
@ -119,7 +90,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
}
guard let toAddress else {
throw TransactionManagerError.cannotEncodeInternalTx(pendingTransaction)
throw ZcashError.persistentTransManagerEncodeUknownToAddress(pendingTransaction)
}
let transaction = try await self.encoder.createTransaction(
@ -140,14 +111,8 @@ class PersistentTransactionManager: OutboundTransactionManager {
try self.repository.update(pending)
return pending
} catch DatabaseStorageError.updateFailed {
throw TransactionManagerError.updateFailed(pendingTransaction)
} catch {
do {
try await self.updateOnFailure(transaction: pendingTransaction, error: error)
} catch {
throw TransactionManagerError.updateFailed(pendingTransaction)
}
try await self.updateOnFailure(transaction: pendingTransaction, error: error)
throw error
}
}
@ -156,56 +121,53 @@ class PersistentTransactionManager: OutboundTransactionManager {
pendingTransaction: PendingTransactionEntity
) async throws -> PendingTransactionEntity {
guard let txId = pendingTransaction.id else {
throw TransactionManagerError.notPending(pendingTransaction) // this transaction is not stored
// this transaction is not stored
throw ZcashError.persistentTransManagerSubmitTransactionIDMissing(pendingTransaction)
}
do {
guard let storedTx = try self.repository.find(by: txId) else {
throw TransactionManagerError.notPending(pendingTransaction)
throw ZcashError.persistentTransManagerSubmitTransactionNotFound(pendingTransaction)
}
guard !storedTx.isCancelled else {
logger.debug("ignoring cancelled transaction \(storedTx)")
throw TransactionManagerError.cancelled(storedTx)
throw ZcashError.persistentTransManagerSubmitTransactionCanceled(storedTx)
}
guard let raw = storedTx.raw else {
logger.debug("INCONSISTENCY: attempt to send pending transaction \(txId) that has not raw data")
throw TransactionManagerError.internalInconsistency(storedTx)
throw ZcashError.persistentTransManagerSubmitTransactionRawDataMissing(storedTx)
}
let response = try await self.service.submit(spendTransaction: raw)
let transaction = try await self.update(transaction: storedTx, on: response)
guard response.errorCode >= 0 else {
throw TransactionManagerError.submitFailed(transaction, errorCode: Int(response.errorCode))
throw ZcashError.persistentTransManagerSubmitFailed(transaction, Int(response.errorCode))
}
return transaction
} catch {
try? await self.updateOnFailure(transaction: pendingTransaction, error: error)
try await self.updateOnFailure(transaction: pendingTransaction, error: error)
throw error
}
}
func applyMinedHeight(pendingTransaction: PendingTransactionEntity, minedHeight: BlockHeight) async throws -> PendingTransactionEntity {
guard let id = pendingTransaction.id else {
throw TransactionManagerError.internalInconsistency(pendingTransaction)
throw ZcashError.persistentTransManagerApplyMinedHeightTransactionIDMissing(pendingTransaction)
}
guard var transaction = try repository.find(by: id) else {
throw TransactionManagerError.notPending(pendingTransaction)
throw ZcashError.persistentTransManagerApplyMinedHeightTransactionNotFound(pendingTransaction)
}
transaction.minedHeight = minedHeight
guard let pendingTxId = pendingTransaction.id else {
throw TransactionManagerError.updateFailed(pendingTransaction)
}
do {
try repository.applyMinedHeight(minedHeight, id: pendingTxId)
} catch {
throw TransactionManagerError.updateFailed(transaction)
throw ZcashError.persistentTransManagerApplyMinedHeightTransactionIDMissing(pendingTransaction)
}
try repository.applyMinedHeight(minedHeight, id: pendingTxId)
return transaction
}
@ -257,11 +219,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
}
func delete(pendingTransaction: PendingTransactionEntity) async throws {
do {
try repository.delete(pendingTransaction)
} catch {
throw TransactionManagerError.notPending(pendingTransaction)
}
try repository.delete(pendingTransaction)
}
func closeDBConnection() {

View File

@ -9,14 +9,6 @@ import Foundation
typealias TransactionEncoderResultBlock = (_ result: Result<EncodedTransaction, Error>) -> Void
public enum TransactionEncoderError: Error {
case notFound(transactionId: Int)
case notEncoded(transactionId: Int)
case missingParams
case spendingKeyWrongNetwork
case couldNotExpand(txId: Data)
}
protocol TransactionEncoder {
/// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
/// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
@ -28,6 +20,9 @@ protocol TransactionEncoder {
/// - Parameter to: string containing the recipient address
/// - Parameter MemoBytes: string containing the memo (optional)
/// - Parameter accountIndex: index of the account that will be used to send the funds
/// - Throws:
/// - `walletTransEncoderCreateTransactionMissingSaplingParams` if the sapling parameters aren't downloaded.
/// - Some `ZcashError.rust*` if the creation of transaction fails.
func createTransaction(
spendingKey: UnifiedSpendingKey,
zatoshi: Zatoshi,
@ -44,7 +39,9 @@ protocol TransactionEncoder {
/// - Parameter spendingKey: `UnifiedSpendingKey` to spend the UTXOs
/// - Parameter memoBytes: containing the memo (optional)
/// - Parameter accountIndex: index of the account that will be used to send the funds
/// - Throws: a TransactionEncoderError
/// - Throws:
/// - `walletTransEncoderShieldFundsMissingSaplingParams` if the sapling parameters aren't downloaded.
/// - Some `ZcashError.rust*` if the creation of transaction fails.
func createShieldingTransaction(
spendingKey: UnifiedSpendingKey,
shieldingThreshold: Zatoshi,

View File

@ -8,15 +8,15 @@
import Foundation
class WalletTransactionEncoder: TransactionEncoder {
var rustBackend: ZcashRustBackendWelding
var repository: TransactionRepository
let rustBackend: ZcashRustBackendWelding
let repository: TransactionRepository
let logger: Logger
private var outputParamsURL: URL
private var spendParamsURL: URL
private var dataDbURL: URL
private var fsBlockDbRoot: URL
private var networkType: NetworkType
private let outputParamsURL: URL
private let spendParamsURL: URL
private let dataDbURL: URL
private let fsBlockDbRoot: URL
private let networkType: NetworkType
init(
rustBackend: ZcashRustBackendWelding,
@ -66,12 +66,8 @@ class WalletTransactionEncoder: TransactionEncoder {
from: accountIndex
)
do {
logger.debug("transaction id: \(txId)")
return try await repository.find(id: txId)
} catch {
throw TransactionEncoderError.notFound(transactionId: txId)
}
logger.debug("transaction id: \(txId)")
return try await repository.find(id: txId)
}
func createSpend(
@ -82,7 +78,7 @@ class WalletTransactionEncoder: TransactionEncoder {
from accountIndex: Int
) async throws -> Int {
guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else {
throw TransactionEncoderError.missingParams
throw ZcashError.walletTransEncoderCreateTransactionMissingSaplingParams
}
let txId = try await rustBackend.createToAddress(
@ -108,12 +104,8 @@ class WalletTransactionEncoder: TransactionEncoder {
accountIndex: accountIndex
)
do {
logger.debug("transaction id: \(txId)")
return try await repository.find(id: txId)
} catch {
throw TransactionEncoderError.notFound(transactionId: txId)
}
logger.debug("transaction id: \(txId)")
return try await repository.find(id: txId)
}
func createShieldingSpend(
@ -123,7 +115,7 @@ class WalletTransactionEncoder: TransactionEncoder {
accountIndex: Int
) async throws -> Int {
guard ensureParams(spend: self.spendParamsURL, output: self.outputParamsURL) else {
throw TransactionEncoderError.missingParams
throw ZcashError.walletTransEncoderShieldFundsMissingSaplingParams
}
let txId = try await rustBackend.shieldFunds(

View File

@ -9,7 +9,7 @@ import Foundation
import os
public class OSLogger: Logger {
public var alias: ZcashSynchronizerAlias?
public let alias: ZcashSynchronizerAlias?
public enum LogLevel: Int {
case debug
@ -19,7 +19,7 @@ public class OSLogger: Logger {
case info
}
public private(set) var oslog: OSLog?
public let oslog: OSLog?
var level: LogLevel
@ -30,6 +30,8 @@ public class OSLogger: Logger {
var postfix = ""
if let alias { postfix = "_\(alias.description)" }
self.oslog = OSLog(subsystem: bundleName, category: "\(category)\(postfix)")
} else {
oslog = nil
}
}

View File

@ -20,18 +20,16 @@ extension Digest {
/// Helper class to handle the download of Sapling parameters
public enum SaplingParameterDownloader {
public enum Errors: Error {
case failed(error: Error)
case spendParamsInvalidSHA1
case outputParamsInvalidSHA1
}
/// Download a Spend parameter from default host and stores it at given URL
/// - Parameters:
/// - at: The destination URL for the download
/// - Throws:
/// - `saplingParamsDownload` if file downloading fails.
/// - `saplingParamsCantMoveDownloadedFile` if file is downloaded but moving to final destination fails.
/// - `saplingParamsInvalidSpendParams` if the downloaded file is invalid.
@discardableResult
public static func downloadSpendParameter(_ at: URL, sourceURL: URL, logger: Logger) async throws -> URL {
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), logger: logger, at: at)
let resultURL = try await downloadFileWithRequestWithContinuation(sourceURL, logger: logger, at: at)
try isSpendParamsSHA1Valid(url: resultURL)
return resultURL
}
@ -39,9 +37,13 @@ public enum SaplingParameterDownloader {
/// Download an Output parameter from default host and stores it at given URL
/// - Parameters:
/// - at: The destination URL for the download
/// - Throws:
/// - `saplingParamsDownload` if file downloading fails.
/// - `saplingParamsCantMoveDownloadedFile` if file is downloaded but moving to final destination fails.
/// - `saplingParamsInvalidOutputParams` if the downloaded file is invalid.
@discardableResult
public static func downloadOutputParameter(_ at: URL, sourceURL: URL, logger: Logger) async throws -> URL {
let resultURL = try await downloadFileWithRequestWithContinuation(URLRequest(url: sourceURL), logger: logger, at: at)
let resultURL = try await downloadFileWithRequestWithContinuation(sourceURL, logger: logger, at: at)
try isOutputParamsSHA1Valid(url: resultURL)
return resultURL
}
@ -50,6 +52,11 @@ public enum SaplingParameterDownloader {
/// - Parameters:
/// - spendURL: URL to check whether the parameter is already downloaded
/// - outputURL: URL to check whether the parameter is already downloaded
/// - Throws:
/// - `saplingParamsDownload` if file downloading fails.
/// - `saplingParamsCantMoveDownloadedFile` if file is downloaded but moving to final destination fails.
/// - `saplingParamsInvalidSpendParams` if the downloaded file is invalid.
/// - `saplingParamsInvalidOutputParams` if the downloaded file is invalid.
@discardableResult
public static func downloadParamsIfnotPresent(
spendURL: URL,
@ -58,15 +65,11 @@ public enum SaplingParameterDownloader {
outputSourceURL: URL,
logger: Logger
) async throws -> (spend: URL, output: URL) {
do {
async let spendResultURL = ensureSpendParameter(at: spendURL, sourceURL: spendSourceURL, logger: logger)
async let outputResultURL = ensureOutputParameter(at: outputURL, sourceURL: outputSourceURL, logger: logger)
let results = try await [spendResultURL, outputResultURL]
return (spend: results[0], output: results[1])
} catch {
throw Errors.failed(error: error)
}
async let spendResultURL = ensureSpendParameter(at: spendURL, sourceURL: spendSourceURL, logger: logger)
async let outputResultURL = ensureOutputParameter(at: outputURL, sourceURL: outputSourceURL, logger: logger)
let results = try await [spendResultURL, outputResultURL]
return (spend: results[0], output: results[1])
}
static func ensureSpendParameter(at url: URL, sourceURL: URL, logger: Logger) async throws -> URL {
@ -93,15 +96,15 @@ public enum SaplingParameterDownloader {
static func isSpendParamsSHA1Valid(url: URL) throws {
if Insecure.SHA1.hash(data: try Data(contentsOf: url)).hexString != Constants.spendParamFileSHA1 {
try FileManager.default.removeItem(at: url)
throw Errors.spendParamsInvalidSHA1
try? FileManager.default.removeItem(at: url)
throw ZcashError.saplingParamsInvalidSpendParams
}
}
static func isOutputParamsSHA1Valid(url: URL) throws {
if Insecure.SHA1.hash(data: try Data(contentsOf: url)).hexString != Constants.outputParamFileSHA1 {
try FileManager.default.removeItem(at: url)
throw Errors.outputParamsInvalidSHA1
try? FileManager.default.removeItem(at: url)
throw ZcashError.saplingParamsInvalidOutputParams
}
}
}
@ -113,12 +116,12 @@ private extension SaplingParameterDownloader {
}
static func downloadFileWithRequestWithContinuation(
_ request: URLRequest,
_ sourceURL: URL,
logger: Logger,
at destination: URL
) async throws -> URL {
return try await withCheckedThrowingContinuation { continuation in
downloadFileWithRequest(request, at: destination, logger: logger) { result in
downloadFileWithRequest(sourceURL, at: destination, logger: logger) { result in
switch result {
case .success(let outputResultURL):
continuation.resume(returning: outputResultURL)
@ -130,22 +133,23 @@ private extension SaplingParameterDownloader {
}
static func downloadFileWithRequest(
_ request: URLRequest,
_ sourceURL: URL,
at destination: URL,
logger: Logger,
result: @escaping (Result<URL, Error>) -> Void
) {
logger.debug("Downloading sapling file from \(String(describing: request.url))")
logger.debug("Downloading sapling file from \(sourceURL)")
let request = URLRequest(url: sourceURL)
let task = URLSession.shared.downloadTask(with: request) { url, _, error in
if let error {
result(.failure(Errors.failed(error: error)))
result(.failure(ZcashError.saplingParamsDownload(error, sourceURL)))
return
} else if let localUrl = url {
do {
try FileManager.default.moveItem(at: localUrl, to: destination)
result(.success(destination))
} catch {
result(.failure(error))
result(.failure(ZcashError.saplingParamsCantMoveDownloadedFile(error, sourceURL, destination)))
}
}
}

View File

@ -454,7 +454,7 @@ class AdvancedReOrgTests: XCTestCase {
try coordinator.applyStaged(blockheight: 663195)
sleep(1)
let firstSyncExpectation = XCTestExpectation(description: "first sync expectation")
var preReorgTotalBalance = Zatoshi.zero
var preReorgVerifiedBalance = Zatoshi.zero
try await coordinator.sync(

View File

@ -98,13 +98,11 @@ class Z2TReceiveTests: XCTestCase {
XCTFail("Should have thrown error")
} catch {
sendExpectation.fulfill()
if case let SynchronizerError.generalError(message) = error {
XCTAssertEqual(message, "Memos can't be sent to transparent addresses.")
} else {
guard case ZcashError.synchronizerSendMemoToTransparentAddress = error else {
// swiftlint:disable:next line_length
XCTFail("expected SynchronizerError.genericError(\"Memos can't be sent to transparent addresses.\") but received \(error.localizedDescription)")
return
}
return
}
}

View File

@ -180,17 +180,18 @@ class BlockScanTests: XCTestCase {
try await fsBlockRepository.create()
var processorConfig = CompactBlockProcessor.Configuration(
let processorConfig = CompactBlockProcessor.Configuration(
alias: .default,
fsBlockCacheRoot: testTempDirectory,
dataDb: dataDbURL,
spendParamsURL: spendParamsURL,
outputParamsURL: outputParamsURL,
saplingParamsSourceURL: SaplingParamsSourceURL.tests,
downloadBatchSize: 1000,
scanningBatchSize: 1000,
walletBirthdayProvider: { [weak self] in self?.network.constants.saplingActivationHeight ?? .zero },
network: network
)
processorConfig.scanningBatchSize = 1000
let compactBlockProcessor = CompactBlockProcessor(
service: service,
@ -235,9 +236,9 @@ class BlockScanTests: XCTestCase {
try await compactBlockProcessor.blockScanner.scanBlocks(at: range, totalProgressRange: range, didScan: { _ in })
XCTAssertFalse(Task.isCancelled)
} catch {
if let lwdError = error as? LightWalletServiceError {
if let lwdError = error as? ZcashError {
switch lwdError {
case .timeOut:
case .serviceBlockStreamFailed:
XCTAssert(true)
default:
XCTFail("LWD Service error found, but should have been a timeLimit reached Error - \(lwdError)")

View File

@ -180,12 +180,12 @@ class BlockStreamingTest: XCTestCase {
totalProgressRange: startHeight...latestBlockHeight
)
} catch {
if let lwdError = error as? LightWalletServiceError {
if let lwdError = error as? ZcashError {
switch lwdError {
case .timeOut:
case .serviceBlockStreamFailed:
XCTAssert(true)
default:
XCTFail("LWD Service error found, but should have been a timeLimit reached Error")
XCTFail("LWD Service error found, but should have been a timeLimit reached \(lwdError)")
}
} else {
XCTFail("Error should have been a timeLimit reached Error")

View File

@ -101,7 +101,7 @@ class CompactBlockReorgTests: XCTestCase {
rustBackend: rustBackend,
mockValidateCombinedChainFailAfterAttempts: 3,
mockValidateCombinedChainKeepFailing: false,
mockValidateCombinedChainFailureError: .invalidChain(upperBound: Int32(network.constants.saplingActivationHeight + 320))
mockValidateCombinedChainFailureError: .rustValidateCombinedChainInvalidChain(Int32(network.constants.saplingActivationHeight + 320))
)
let transactionRepository = MockTransactionRepository(

View File

@ -19,7 +19,8 @@ class BirthdayTests: XCTestCase {
0c2415a69eddb56d7a0846f03f4c98936607d5c0e7f580748224bd2117e51200000149f61a12a3f8407f4f7bd3e4f619937fa1a09e984a5f7334fcd7734c4ba3e3690000\
0001bab80e68a5c63460d1e5c94ef540940792fa4703fa488b09fdfded97f8ec8a3d00013d2fd009bf8a22d68f720eac19c411c99014ed9c5f85d5942e15d1fc039e2868\
0001f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39
"""
""",
orchardTree: nil
)
XCTAssertEqual(birthday, expected)
@ -43,7 +44,8 @@ class BirthdayTests: XCTestCase {
55aeeeb82cceddbe832919324d7011418749fc9dea759cfa6c2cc21501f4a3504117d35efa15f57d5fdd19515b7fb1dd14c3b98b8a91685f0f788db330000000018846ec\
9170ad4e40a093cfb53162e5211d55377d8d22f826cde7783d30c1dd5f01b35fe4a943a47404f68db220c77b0573e13c3378a65c6f2396f93be7609d8f2a000125911f45\
24469c00ccb1ba69e64f0ee7380c8d17bbfc76ecd238421b86eb6e09000118f64df255c9c43db708255e7bf6bffd481e5c2f38fe9ed8f3d189f7f9cf2644
"""
""",
orchardTree: nil
)
XCTAssertEqual(birthday, expected)
@ -59,7 +61,8 @@ class BirthdayTests: XCTestCase {
height: 280000,
hash: "000420e7fcc3a49d729479fb0b560dd7b8617b178a08e9e389620a9d1dd6361a",
time: 1535262293,
saplingTree: "000000"
saplingTree: "000000",
orchardTree: nil
)
XCTAssertEqual(birthday, expected)
@ -75,7 +78,8 @@ class BirthdayTests: XCTestCase {
height: 419200,
hash: "00000000025a57200d898ac7f21e26bf29028bbe96ec46e05b2c17cc9db9e4f3",
time: 1540779337,
saplingTree: "000000"
saplingTree: "000000",
orchardTree: nil
)
XCTAssertEqual(birthday, expected)

View File

@ -93,10 +93,10 @@ class BlockBatchValidationTests: XCTestCase {
XCTAssertFalse(Task.isCancelled)
} catch {
switch error {
case CompactBlockProcessorError.wrongConsensusBranchId:
case ZcashError.compactBlockProcessorWrongConsensusBranchId:
break
default:
XCTFail("Expected CompactBlockProcessorError.wrongConsensusBranchId but found \(error)")
XCTFail("Expected ZcashError.compactBlockProcessorWrongConsensusBranchId but found \(error)")
}
}
}
@ -166,10 +166,10 @@ class BlockBatchValidationTests: XCTestCase {
XCTAssertFalse(Task.isCancelled)
} catch {
switch error {
case CompactBlockProcessorError.networkMismatch(expected: .mainnet, found: .testnet):
case ZcashError.compactBlockProcessorNetworkMismatch(.mainnet, .testnet):
break
default:
XCTFail("Expected CompactBlockProcessorError.networkMismatch but found \(error)")
XCTFail("Expected ZcashError.compactBlockProcessorNetworkMismatch but found \(error)")
}
}
}
@ -239,10 +239,10 @@ class BlockBatchValidationTests: XCTestCase {
XCTAssertFalse(Task.isCancelled)
} catch {
switch error {
case CompactBlockProcessorError.generalError:
case ZcashError.compactBlockProcessorChainName:
break
default:
XCTFail("Expected CompactBlockProcessorError.generalError but found \(error)")
XCTFail("Expected ZcashError.compactBlockProcessorChainName but found \(error)")
}
}
}
@ -313,13 +313,13 @@ class BlockBatchValidationTests: XCTestCase {
XCTAssertFalse(Task.isCancelled)
} catch {
switch error {
case CompactBlockProcessorError.saplingActivationMismatch(
expected: network.constants.saplingActivationHeight,
found: BlockHeight(info.saplingActivationHeight)
case ZcashError.compactBlockProcessorSaplingActivationMismatch(
network.constants.saplingActivationHeight,
BlockHeight(info.saplingActivationHeight)
):
break
default:
XCTFail("Expected CompactBlockProcessorError.saplingActivationMismatch but found \(error)")
XCTFail("Expected ZcashError.compactBlockProcessorSaplingActivationMismatch but found \(error)")
}
}
}

View File

@ -11,8 +11,6 @@ import Foundation
import XCTest
@testable import ZcashLightClientKit
extension String: Error { }
class ClosureSynchronizerOfflineTests: XCTestCase {
var data: TestsData!

View File

@ -1,15 +0,0 @@
//
// ErrorLocalizationTests.swift
//
//
// Created by Francisco Gindre on 7/11/22.
//
import XCTest
import ZcashLightClientKit
class ErrorLocalizationTests: XCTestCase {
func testLocalizedError() throws {
let sychronizerError = SynchronizerError.networkTimeout as Error
XCTAssertEqual(sychronizerError.localizedDescription, "Network Timeout. Please check Internet connection")
}
}

View File

@ -251,7 +251,7 @@ final class FsBlockStorageTests: XCTestCase {
var url = self.fsBlockDb.appendingPathComponent(filename)
guard self.testFileManager.createFile(atPath: url.path, contents: nil) else {
XCTFail("couldn't create file at \(url.absoluteString)")
throw CompactBlockRepositoryError.malformedCacheEntry("couldn't create file at \(url.path)")
throw "couldn't create file at \(url.path)"
}
var resourceValues = URLResourceValues()
resourceValues.name = filename
@ -369,7 +369,7 @@ final class FsBlockStorageTests: XCTestCase {
try await realCache.write(blocks: sandblastedBlocks)
XCTFail("This call should have failed")
} catch {
XCTAssertEqual(error as? CompactBlockRepositoryError, CompactBlockRepositoryError.failedToWriteBlock(sandblastedBlocks[0]))
XCTAssertEqual(error as? ZcashError, ZcashError.blockRepositoryWriteBlock(sandblastedBlocks[0]))
}
}
@ -421,7 +421,7 @@ final class FsBlockStorageTests: XCTestCase {
return
}
let mockBackend = await RustBackendMockHelper(rustBackend: rustBackend)
await mockBackend.rustBackendMock.setWriteBlocksMetadataBlocksThrowableError(RustWeldingError.genericError(message: "oops"))
await mockBackend.rustBackendMock.setWriteBlocksMetadataBlocksThrowableError(ZcashError.rustWriteBlocksMetadataAllocationProblem)
do {
try await FSMetadataStore.saveBlocksMeta(
@ -430,19 +430,19 @@ final class FsBlockStorageTests: XCTestCase {
rustBackend: mockBackend.rustBackendMock,
logger: logger
)
} catch CompactBlockRepositoryError.failedToWriteMetadata {
} catch ZcashError.rustWriteBlocksMetadataAllocationProblem {
// this is fine
} catch {
XCTFail("Expected `CompactBlockRepositoryError.failedToWriteMetadata` but found: \(error.localizedDescription)")
XCTFail("Expected `CompactBlockRepositoryError.failedToWriteMetadata` but found: \(error)")
}
}
func testMetadataStoreThrowsWhenRewindFails() async {
let mockBackend = await RustBackendMockHelper(rustBackend: rustBackend)
await mockBackend.rustBackendMock.setRewindCacheToHeightHeightThrowableError(RustWeldingError.genericError(message: "oops"))
let expectedHeight = BlockHeight(1000)
let mockBackend = await RustBackendMockHelper(rustBackend: rustBackend)
await mockBackend.rustBackendMock.setRewindCacheToHeightHeightThrowableError(ZcashError.rustRewindToHeight(Int32(expectedHeight), "oops"))
do {
try await FSMetadataStore.live(
fsBlockDbRoot: testTempDirectory,
@ -452,16 +452,16 @@ final class FsBlockStorageTests: XCTestCase {
.rewindToHeight(expectedHeight)
XCTFail("rewindToHeight should fail")
} catch {
guard let repositoryError = error as? CompactBlockRepositoryError else {
XCTFail("Expected CompactBlockRepositoryError. Found \(error)")
guard let error = error as? ZcashError else {
XCTFail("Expected ZcashError. Found \(error)")
return
}
switch repositoryError {
case .failedToRewind(let height):
XCTAssertEqual(height, expectedHeight)
switch error {
case let .rustRewindToHeight(height, _):
XCTAssertEqual(BlockHeight(height), expectedHeight)
default:
XCTFail("Expected `CompactBlockRepositoryError.failedToRewind`. Found \(error)")
XCTFail("Expected `ZcashError.rustRewindToHeight`. Found \(error)")
}
}
}

View File

@ -69,7 +69,7 @@ class InitializerOfflineTests: XCTestCase {
alias: alias
)
if let error = initializer.urlsParsingError, case let .cantUpdateURLWithAlias(failedURL) = error {
if let error = initializer.urlsParsingError, case let .initializerCantUpdateURLWithAlias(failedURL) = error {
XCTAssertEqual(failedURL, invalidPathURL, "Failing \(function)")
} else {
XCTFail("URLs parsing error expected. Failing \(function)")

View File

@ -31,7 +31,7 @@ class InternalSyncProgressTests: XCTestCase {
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
let nextState = try await internalSyncProgress.computeNextState(
let nextState = await internalSyncProgress.computeNextState(
latestBlockHeight: latestHeight,
latestScannedHeight: 630000,
walletBirthday: 600000
@ -53,7 +53,7 @@ class InternalSyncProgressTests: XCTestCase {
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
let nextState = try await internalSyncProgress.computeNextState(
let nextState = await internalSyncProgress.computeNextState(
latestBlockHeight: latestHeight,
latestScannedHeight: 620000,
walletBirthday: 600000
@ -77,7 +77,7 @@ class InternalSyncProgressTests: XCTestCase {
await internalSyncProgress.set(630000, .latestUTXOFetchedHeight)
await internalSyncProgress.set(630000, .latestEnhancedHeight)
let nextState = try await internalSyncProgress.computeNextState(
let nextState = await internalSyncProgress.computeNextState(
latestBlockHeight: latestHeight,
latestScannedHeight: 630000,
walletBirthday: 600000

View File

@ -65,16 +65,13 @@ class NullBytesTests: XCTestCase {
)
XCTFail("InitBlocksTable with Null bytes on hash string should have failed")
} catch {
guard let rustError = error as? RustWeldingError else {
XCTFail("Expected RustWeldingError")
guard let rustError = error as? ZcashError else {
XCTFail("Expected ZcashError")
return
}
switch rustError {
case .invalidInput:
XCTAssertTrue(true)
default:
XCTFail("expected \(String(describing: RustWeldingError.invalidInput)) and got \(rustError)")
if rustError.code != .rustInitBlocksTableHashContainsNullBytes {
XCTFail("expected error code \(ZcashError.rustInitBlocksTableHashContainsNullBytes.code.rawValue) and got error \(rustError)")
}
}
@ -87,16 +84,13 @@ class NullBytesTests: XCTestCase {
)
XCTFail("InitBlocksTable with Null bytes on saplingTree string should have failed")
} catch {
guard let rustError = error as? RustWeldingError else {
XCTFail("Expected RustWeldingError")
guard let rustError = error as? ZcashError else {
XCTFail("Expected ZcashError")
return
}
switch rustError {
case .invalidInput:
XCTAssertTrue(true)
default:
XCTFail("expected \(String(describing: RustWeldingError.invalidInput)) and got \(rustError)")
if rustError.code != .rustInitBlocksTableSaplingTreeContainsNullBytes {
XCTFail("expected error code \(ZcashError.rustInitBlocksTableSaplingTreeContainsNullBytes.code.rawValue) and got error \(rustError)")
}
}
}

View File

@ -114,7 +114,7 @@ class SynchronizerOfflineTests: XCTestCase {
case .finished:
XCTFail("Second wipe should fail with error.")
case let .failure(error):
if let error = error as? InitializerError, case .aliasAlreadyInUse = error {
if let error = error as? ZcashError, case .initializerAliasAlreadyInUse = error {
secondWipeExpectation.fulfill()
} else {
XCTFail("Wipe failed with unexpected error: \(error)")
@ -168,7 +168,7 @@ class SynchronizerOfflineTests: XCTestCase {
)
XCTFail("Send to address should fail.")
} catch {
if let error = error as? SynchronizerError, case .notPrepared = error {
if let error = error as? ZcashError, case .synchronizerNotPrepared = error {
} else {
XCTFail("Send to address failed with unexpected error: \(error)")
}
@ -186,7 +186,7 @@ class SynchronizerOfflineTests: XCTestCase {
)
XCTFail("Shield funds should fail.")
} catch {
if let error = error as? SynchronizerError, case .notPrepared = error {
if let error = error as? ZcashError, case .synchronizerNotPrepared = error {
} else {
XCTFail("Shield funds failed with unexpected error: \(error)")
}
@ -200,7 +200,7 @@ class SynchronizerOfflineTests: XCTestCase {
_ = try await testCoordinator.synchronizer.refreshUTXOs(address: data.transparentAddress, from: 1)
XCTFail("Shield funds should fail.")
} catch {
if let error = error as? SynchronizerError, case .notPrepared = error {
if let error = error as? ZcashError, case .synchronizerNotPrepared = error {
} else {
XCTFail("Shield funds failed with unexpected error: \(error)")
}
@ -219,7 +219,7 @@ class SynchronizerOfflineTests: XCTestCase {
case .finished:
XCTFail("Rewind should fail with error.")
case let .failure(error):
if let error = error as? SynchronizerError, case .notPrepared = error {
if let error = error as? ZcashError, case .synchronizerNotPrepared = error {
expectation.fulfill()
} else {
XCTFail("Rewind failed with unexpected error: \(error)")
@ -266,7 +266,7 @@ class SynchronizerOfflineTests: XCTestCase {
_ = try await synchronizer.prepare(with: Environment.seedBytes, viewingKeys: [viewingKey], walletBirthday: 123000)
XCTFail("Failure of prepare is expected.")
} catch {
if let error = error as? InitializerError, case let .cantUpdateURLWithAlias(failedURL) = error {
if let error = error as? ZcashError, case let .initializerCantUpdateURLWithAlias(failedURL) = error {
XCTAssertEqual(failedURL, invalidPathURL)
} else {
XCTFail("Failed with unexpected error: \(error)")
@ -305,7 +305,7 @@ class SynchronizerOfflineTests: XCTestCase {
case .finished:
XCTFail("Failure of wipe is expected.")
case let .failure(error):
if let error = error as? InitializerError, case let .cantUpdateURLWithAlias(failedURL) = error {
if let error = error as? ZcashError, case let .initializerCantUpdateURLWithAlias(failedURL) = error {
XCTAssertEqual(failedURL, invalidPathURL)
expectation.fulfill()
} else {
@ -325,7 +325,6 @@ class SynchronizerOfflineTests: XCTestCase {
}
func testIsNotNewSessionOnUnpreparedToStateThatWontSync() {
XCTAssertFalse(SessionTicker.live.isNewSyncSession(.unprepared, .error(SynchronizerError.criticalError)))
XCTAssertFalse(SessionTicker.live.isNewSyncSession(.unprepared, .disconnected))
XCTAssertFalse(SessionTicker.live.isNewSyncSession(.unprepared, .unprepared))
}
@ -349,17 +348,6 @@ class SynchronizerOfflineTests: XCTestCase {
)
}
func testIsNewSyncSessionWhenRestartingFromError() {
XCTAssertTrue(
SessionTicker.live.isNewSyncSession(
.error(SynchronizerError.generalError(message: "some error")),
.syncing(
BlockProgress(startHeight: 1, targetHeight: 10, progressHeight: 4)
)
)
)
}
func testIsNewSyncSessionWhenStartingFromSynced() {
XCTAssertTrue(
SessionTicker.live.isNewSyncSession(
@ -394,7 +382,6 @@ class SynchronizerOfflineTests: XCTestCase {
}
func testSyncStatusesDontDifferWhenOuterStatusIsTheSame() {
XCTAssertFalse(SyncStatus.error(SynchronizerError.criticalError).isDifferent(from: .error(.invalidAccount)))
XCTAssertFalse(SyncStatus.disconnected.isDifferent(from: .disconnected))
XCTAssertFalse(SyncStatus.fetching.isDifferent(from: .fetching))
XCTAssertFalse(SyncStatus.stopped.isDifferent(from: .stopped))

View File

@ -91,17 +91,16 @@ class Zip302MemoTests: XCTestCase {
"""
XCTAssertThrowsError(try Memo(string: tooLongString)) { err in
guard let error = err as? MemoBytes.Errors else {
XCTFail("Expected `MemoBytes.Errors.TooLong` error but found \(err.localizedDescription)")
guard let error = err as? ZcashError else {
XCTFail("Expected `ZCashError.memoTextInputTooLong` error but found \(err.localizedDescription)")
return
}
switch error {
case .tooLong(let length):
case .memoTextInputTooLong(let length):
XCTAssertEqual(length, 513)
default:
XCTFail("Expected `MemoBytes.Errors.TooLong` error but found \(err.localizedDescription)")
XCTFail("Expected `ZCashError.memoTextInputTooLong` error but found \(err.localizedDescription)")
}
}
}
@ -131,18 +130,16 @@ class Zip302MemoTests: XCTestCase {
"""
XCTAssertThrowsError(try Memo(string: almostTooLongString)) { err in
guard let error = err as? MemoBytes.Errors else {
XCTFail("Expected `MemoBytes.Errors kind of error but found \(err)")
guard let error = err as? ZcashError else {
XCTFail("Expected `ZcashError.memoTextInputTooLong kind of error but found \(err)")
return
}
switch error {
case .tooLong(let count):
case .memoTextInputTooLong(let count):
XCTAssertEqual(count, 515)
case .invalidUTF8:
XCTFail("Expected `.tooLong(515) but found `.invalidUTF8`")
case .endsWithNullBytes:
XCTFail("Expected `.tooLong(515) but found `.endsWithNullBytes`")
default:
XCTFail("Expected `ZCashError.memoTextInputTooLong` error but found \(err.localizedDescription)")
}
}
}
@ -165,16 +162,16 @@ class Zip302MemoTests: XCTestCase {
let nullTrailedString = "This Is a memo with text and trailing null bytes\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}\u{0}"
XCTAssertThrowsError(try Memo(string: nullTrailedString)) { error in
guard let thrownError = error as? MemoBytes.Errors else {
XCTFail("Thrown error is not MemoBytes.Error")
guard let thrownError = error as? ZcashError else {
XCTFail("Thrown error is not ZcashError.memoTextInputEndsWithNullBytes")
return
}
switch thrownError {
case .invalidUTF8, .tooLong:
XCTFail("Expected .endsWithNullBytes found other errors")
case .endsWithNullBytes:
case .memoTextInputEndsWithNullBytes:
return
default:
XCTFail("Expected `ZCashError.memoTextInputEndsWithNullBytes` error but found \(thrownError.localizedDescription)")
}
}
}
@ -183,16 +180,16 @@ class Zip302MemoTests: XCTestCase {
let nullTrailedString = "\u{0}"
XCTAssertThrowsError(try Memo(string: nullTrailedString)) { error in
guard let thrownError = error as? MemoBytes.Errors else {
XCTFail("Thrown error is not MemoBytes.Error")
guard let thrownError = error as? ZcashError else {
XCTFail("Thrown error is not ZcashError.memoTextInputEndsWithNullBytes")
return
}
switch thrownError {
case .invalidUTF8, .tooLong:
XCTFail("Expected .endsWithNullBytes found other errors")
case .endsWithNullBytes:
case .memoTextInputEndsWithNullBytes:
return
default:
XCTFail("Expected `ZCashError.memoTextInputEndsWithNullBytes` error but found \(thrownError.localizedDescription)")
}
}
}

View File

@ -65,6 +65,10 @@ class DarksideWalletService: LightWalletService {
func blockStream(startHeight: BlockHeight, endHeight: BlockHeight) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
service.blockStream(startHeight: startHeight, endHeight: endHeight)
}
func latestBlock() async throws -> ZcashLightClientKit.BlockID {
throw "Not mocked"
}
func closeConnection() {
}
@ -177,7 +181,7 @@ class DarksideWalletService: LightWalletService {
}
enum DarksideWalletDConstants: NetworkConstants {
static var defaultFsBlockDbRootName = "fs_cache"
static let defaultFsBlockDbRootName = "fs_cache"
static var saplingActivationHeight: BlockHeight {
663150
@ -205,6 +209,6 @@ enum DarksideWalletDConstants: NetworkConstants {
}
class DarksideWalletDNetwork: ZcashNetwork {
var constants: NetworkConstants.Type = DarksideWalletDConstants.self
var networkType = NetworkType.mainnet
let constants: NetworkConstants.Type = DarksideWalletDConstants.self
let networkType = NetworkType.mainnet
}

View File

@ -116,7 +116,7 @@ enum FakeChainBuilder {
"https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/transactions/recv/\(id).txt"
}
static var txUrls = [
static let txUrls = [
663174: buildTxUrl(for: "8f064d23c66dc36e32445e5f3b50e0f32ac3ddb78cff21fb521eb6c19c07c99a"),
663188: buildTxUrl(for: "15a677b6770c5505fb47439361d3d3a7c21238ee1a6874fdedad18ae96850590"),
663202: buildTxUrl(for: "d2e7be14bbb308f9d4d68de424d622cbf774226d01cd63cc6f155fafd5cd212c"),

View File

@ -32,6 +32,10 @@ class MockLightWalletService: LightWalletService {
service.blockStream(startHeight: startHeight, endHeight: endHeight)
}
func latestBlock() async throws -> ZcashLightClientKit.BlockID {
throw "Not mocked"
}
func closeConnection() {
}
@ -58,7 +62,7 @@ class MockLightWalletService: LightWalletService {
func getInfo() async throws -> LightWalletdInfo {
guard let info = mockLightDInfo else {
throw LightWalletServiceError.generalError(message: "Not Implemented")
throw ZcashError.serviceGetInfoFailed(.generalError(message: "Not Implemented"))
}
return info
}

View File

@ -177,7 +177,7 @@ extension MockTransactionRepository: TransactionRepository {
func find(id: Int) throws -> ZcashTransaction.Overview {
guard let transaction = transactions.first(where: { $0.id == id }) else {
throw TransactionRepositoryError.notFound
throw ZcashError.transactionRepositoryEntityNotFound
}
return transaction
@ -185,7 +185,7 @@ extension MockTransactionRepository: TransactionRepository {
func find(rawID: Data) throws -> ZcashTransaction.Overview {
guard let transaction = transactions.first(where: { $0.rawID == rawID }) else {
throw TransactionRepositoryError.notFound
throw ZcashError.transactionRepositoryEntityNotFound
}
return transaction

View File

@ -11,18 +11,20 @@ import GRPC
import SwiftProtobuf
@testable import ZcashLightClientKit
extension String: Error { }
class AwfulLightWalletService: MockLightWalletService {
override func latestBlockHeight() async throws -> BlockHeight {
throw LightWalletServiceError.criticalError
throw ZcashError.serviceLatestBlockFailed(.criticalError)
}
override func blockRange(_ range: CompactBlockRange) -> AsyncThrowingStream<ZcashCompactBlock, Error> {
AsyncThrowingStream { continuation in continuation.finish(throwing: LightWalletServiceError.invalidBlock) }
AsyncThrowingStream { continuation in continuation.finish(throwing: ZcashError.serviceSubmitFailed(.invalidBlock)) }
}
/// Submits a raw transaction over lightwalletd.
override func submit(spendTransaction: Data) async throws -> LightWalletServiceResponse {
throw LightWalletServiceError.invalidBlock
throw ZcashError.serviceSubmitFailed(.invalidBlock)
}
}
@ -59,7 +61,7 @@ class RustBackendMockHelper {
mockValidateCombinedChainSuccessRate: Float? = nil,
mockValidateCombinedChainFailAfterAttempts: Int? = nil,
mockValidateCombinedChainKeepFailing: Bool = false,
mockValidateCombinedChainFailureError: RustWeldingError = .chainValidationFailed(message: nil)
mockValidateCombinedChainFailureError: ZcashError = .rustValidateCombinedChainValidationFailed("mock fail")
) async {
self.mockValidateCombinedChainFailAfterAttempts = mockValidateCombinedChainFailAfterAttempts
self.rustBackendMock = ZcashRustBackendWeldingMock(
@ -83,7 +85,7 @@ class RustBackendMockHelper {
rustBackend: ZcashRustBackendWelding,
mockValidateCombinedChainSuccessRate: Float? = nil,
mockValidateCombinedChainKeepFailing: Bool = false,
mockValidateCombinedChainFailureError: RustWeldingError = .chainValidationFailed(message: nil)
mockValidateCombinedChainFailureError: ZcashError
) async {
await rustBackendMock.setLatestCachedBlockHeightReturnValue(.empty())
await rustBackendMock.setInitBlockMetadataDbClosure() { }
@ -94,10 +96,10 @@ class RustBackendMockHelper {
await rustBackendMock.setGetTransparentBalanceAccountReturnValue(0)
await rustBackendMock.setGetVerifiedBalanceAccountReturnValue(0)
await rustBackendMock.setListTransparentReceiversAccountReturnValue([])
await rustBackendMock.setGetCurrentAddressAccountThrowableError(KeyDerivationErrors.unableToDerive)
await rustBackendMock.setGetNextAvailableAddressAccountThrowableError(KeyDerivationErrors.unableToDerive)
await rustBackendMock.setGetCurrentAddressAccountThrowableError(ZcashError.rustGetCurrentAddress("mocked error"))
await rustBackendMock.setGetNextAvailableAddressAccountThrowableError(ZcashError.rustGetNextAvailableAddress("mocked error"))
await rustBackendMock.setShieldFundsUskMemoShieldingThresholdReturnValue(-1)
await rustBackendMock.setCreateAccountSeedThrowableError(KeyDerivationErrors.unableToDerive)
await rustBackendMock.setCreateAccountSeedThrowableError(ZcashError.rustInitAccountsTableViewingKeyCotainsNullBytes)
await rustBackendMock.setGetReceivedMemoIdNoteReturnValue(nil)
await rustBackendMock.setGetSentMemoIdNoteReturnValue(nil)
await rustBackendMock.setCreateToAddressUskToValueMemoReturnValue(-1)
@ -107,7 +109,7 @@ class RustBackendMockHelper {
await rustBackendMock.setPutUnspentTransparentOutputTxidIndexScriptValueHeightClosure() { _, _, _, _, _ in }
await rustBackendMock.setCreateToAddressUskToValueMemoReturnValue(-1)
await rustBackendMock.setCreateToAddressUskToValueMemoReturnValue(-1)
await rustBackendMock.setDecryptAndStoreTransactionTxBytesMinedHeightThrowableError(RustWeldingError.genericError(message: "mock fail"))
await rustBackendMock.setDecryptAndStoreTransactionTxBytesMinedHeightThrowableError(ZcashError.rustDecryptAndStoreTransaction("mock fail"))
await rustBackendMock.setInitDataDbSeedClosure() { seed in
return try await rustBackend.initDataDb(seed: seed)
@ -131,7 +133,7 @@ class RustBackendMockHelper {
}
await rustBackendMock.setValidateCombinedChainLimitClosure() { [weak self] limit in
guard let self else { throw RustWeldingError.genericError(message: "Self is nil") }
guard let self else { throw ZcashError.rustValidateCombinedChainValidationFailed("Self is nil") }
if let rate = mockValidateCombinedChainSuccessRate {
if Self.shouldSucceed(successRate: rate) {
return try await rustBackend.validateCombinedChain(limit: limit)
@ -174,7 +176,7 @@ class RustBackendMockHelper {
}
extension SaplingParamsSourceURL {
static var tests = SaplingParamsSourceURL(
static let tests = SaplingParamsSourceURL(
spendParamFileURL: Bundle.module.url(forResource: "sapling-spend", withExtension: "params")!,
outputParamFileURL: Bundle.module.url(forResource: "sapling-output", withExtension: "params")!
)

View File

@ -68,7 +68,7 @@ enum TestDbBuilder {
switch initResult {
case .success: return provider
case .seedRequired:
throw DatabaseStorageError.migrationFailedWithMessage(message: "Seed value required to initialize the wallet database")
throw ZcashError.dbMigrationGenericFailure("Seed value required to initialize the wallet database")
}
}