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:
parent
ed87069c03
commit
724d410fad
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
""")
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
|
|
@ -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] = []
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -115,7 +115,7 @@ actor InternalSyncProgress {
|
|||
latestBlockHeight: BlockHeight,
|
||||
latestScannedHeight: BlockHeight,
|
||||
walletBirthday: BlockHeight
|
||||
) throws -> CompactBlockProcessor.NextState {
|
||||
) -> CompactBlockProcessor.NextState {
|
||||
logger.debug("""
|
||||
Init numbers:
|
||||
latestBlockHeight: \(latestBlockHeight)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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/")
|
||||
|
|
|
@ -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/")
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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] {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
import Foundation
|
||||
|
||||
struct EncodedTransaction {
|
||||
var transactionId: Data
|
||||
var raw: Data?
|
||||
let transactionId: Data
|
||||
let raw: Data?
|
||||
}
|
||||
|
||||
extension EncodedTransaction: Hashable {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 %}
|
|
@ -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 %}
|
|
@ -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
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)."
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,12 +7,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
enum TransactionRepositoryError: Error {
|
||||
case malformedTransaction
|
||||
case notFound
|
||||
case transactionMissingRequiredFields
|
||||
}
|
||||
|
||||
protocol TransactionRepository {
|
||||
func closeDBConnection()
|
||||
func countAll() async throws -> Int
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)")
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,8 +11,6 @@ import Foundation
|
|||
import XCTest
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
extension String: Error { }
|
||||
|
||||
class ClosureSynchronizerOfflineTests: XCTestCase {
|
||||
var data: TestsData!
|
||||
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")!
|
||||
)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue