Merge pull request #1373 from LukasKorba/1153-Allow-runtime-switch-of-lightwalletd-servers

[#1153] Allow runtime switch of lightwalletd servers
This commit is contained in:
Lukas Korba 2024-02-12 17:37:55 +01:00 committed by GitHub
commit 09fe70dff2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 253 additions and 54 deletions

View File

@ -11,6 +11,14 @@ and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### [#1363] Account balances in the SynchronizerState
`shieldedBalance: WalletBalance` has been replaced with `accountBalances: AccountBalance`. `AccountBalance` provides the same values as `shieldedBalance` but adds up a pending changes. Under the hood this calls rust's `getWalletSummary` which improved also the syncing initial values of % and balances.
## Added
### [#1153] Allow runtime switch of lightwalletd servers
New API implemented that allows clients to change the `mainnet` endpoint. Use `func switchTo(endpoint: LightWalletEndpoint) async throws`.
Possible errors:
- `ZcashError.synchronizerServerSwitch`: endpoint fails, check the address, port and format address:port,
- Some `ZcashError` related to `synchronizer.Start()`: the switch calls `start()` at the end and that is the only throwing function except the validation.
# 2.0.8 - 2024-01-30
Adopt `zcash-light-client-ffi 0.5.1`. This fixes a serialization problem

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Ewq-Xy-xHb">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Ewq-Xy-xHb">
<device id="retina6_0" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22504"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
@ -959,16 +959,16 @@
</constraints>
</view>
<stackView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="246" verticalCompressionResistancePriority="250" ambiguous="YES" alignment="center" spacing="13" translatesAutoresizingMaskIntoConstraints="NO" id="IIl-kO-2M8">
<rect key="frame" x="0.0" y="79.000000000000014" width="374" height="177.33333333333337"/>
<rect key="frame" x="0.0" y="79" width="374" height="240"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" text="Status:" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3t3-Tr-UlI">
<rect key="frame" x="0.0" y="69.666666666666657" width="95.666666666666671" height="38.333333333333343"/>
<rect key="frame" x="0.0" y="101" width="95.666666666666671" height="38.333333333333343"/>
<fontDescription key="fontDescription" type="system" pointSize="32"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jj9-7r-s2Y">
<rect key="frame" x="108.66666666666666" y="75" width="265.33333333333337" height="27.666666666666671"/>
<rect key="frame" x="108.66666666666666" y="106.33333333333331" width="265.33333333333337" height="27.666666666666657"/>
<fontDescription key="fontDescription" type="italicSystem" pointSize="23"/>
<color key="textColor" systemColor="scrollViewTexturedBackgroundColor"/>
<nil key="highlightedColor"/>
@ -979,28 +979,28 @@
</constraints>
</stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Progress" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JPx-ol-2nc">
<rect key="frame" x="0.0" y="280.33333333333331" width="366" height="43"/>
<rect key="frame" x="0.0" y="343" width="366" height="43"/>
<fontDescription key="fontDescription" type="system" pointSize="36"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" ambiguous="YES" progressViewStyle="bar" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="oKg-9s-8Ym">
<rect key="frame" x="0.0" y="347.33333333333331" width="366" height="2.6666666666666856"/>
<rect key="frame" x="0.0" y="410" width="366" height="2.6666666666666856"/>
</progressView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="0%" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="kXE-5z-HN2">
<rect key="frame" x="0.0" y="373" width="374" height="39.666666666666686"/>
<rect key="frame" x="0.0" y="435.66666666666663" width="374" height="39.666666666666686"/>
<fontDescription key="fontDescription" type="system" pointSize="33"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="x3B-hR-coo" userLabel="ProgressDataLabel">
<rect key="frame" x="0.0" y="436.66666666666663" width="0.0" height="0.0"/>
<rect key="frame" x="0.0" y="499.33333333333337" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" ambiguous="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="G5M-gm-1ux">
<rect key="frame" x="0.0" y="460.66666666666663" width="374" height="45"/>
<rect key="frame" x="0.0" y="523.33333333333337" width="374" height="45"/>
<fontDescription key="fontDescription" type="system" pointSize="27"/>
<state key="normal" title="Start"/>
<connections>
@ -1008,19 +1008,19 @@
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="XK2-GA-Ufv">
<rect key="frame" x="0.0" y="529.66666666666663" width="374" height="14.333333333333371"/>
<rect key="frame" x="0.0" y="592.33333333333337" width="374" height="14.333333333333371"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" ambiguous="YES" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ROk-70-c41">
<rect key="frame" x="0.0" y="568" width="374" height="14.333333333333371"/>
<rect key="frame" x="0.0" y="630.66666666666663" width="374" height="14.333333333333371"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" verticalHuggingPriority="750" verticalCompressionResistancePriority="737" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="fB9-xh-4fl" userLabel="Trailing View">
<rect key="frame" x="0.0" y="606.33333333333337" width="374" height="112.66666666666663"/>
<rect key="frame" x="0.0" y="669" width="374" height="50"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" priority="750" constant="20" id="Bpt-XM-IZA"/>
@ -1399,7 +1399,7 @@
<resources>
<image name="play.circle" catalog="system" width="128" height="123"/>
<systemColor name="labelColor">
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="scrollViewTexturedBackgroundColor">
<color red="0.43529411764705878" green="0.44313725490196082" blue="0.47450980392156861" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
@ -1408,10 +1408,10 @@
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="systemGray2Color">
<color red="0.68235294117647061" green="0.68235294117647061" blue="0.69803921568627447" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="0.68235294120000001" green="0.68235294120000001" blue="0.69803921570000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemRedColor">
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<color red="1" green="0.23137254900000001" blue="0.18823529410000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@ -21,15 +21,15 @@ enum DemoAppConfig {
static let host = ZcashSDK.isMainnet ? "mainnet.lightwalletd.com" : "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
// """)
static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
// static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
// wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame
// """)
static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 1935000 : 2170000
static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame
""")
live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor
""")
static let otherSynchronizers: [SynchronizerInitData] = [
SynchronizerInitData(
@ -53,7 +53,7 @@ enum DemoAppConfig {
static var address: String {
"\(host):\(port)"
}
static var endpoint: LightWalletEndpoint {
return LightWalletEndpoint(address: self.host, port: self.port, secure: true, streamingCallTimeoutInMillis: 10 * 60 * 60 * 1000)
}

View File

@ -9,7 +9,7 @@ import Foundation
final class DownloadAction {
let configProvider: CompactBlockProcessor.ConfigProvider
let downloader: BlockDownloader
var downloader: BlockDownloader
let transactionRepository: TransactionRepository
let logger: Logger

View File

@ -8,7 +8,7 @@
import Foundation
final class EnhanceAction {
let blockEnhancer: BlockEnhancer
var blockEnhancer: BlockEnhancer
let configProvider: CompactBlockProcessor.ConfigProvider
let logger: Logger

View File

@ -8,7 +8,7 @@
import Foundation
final class FetchUTXOsAction {
let utxoFetcher: UTXOFetcher
var utxoFetcher: UTXOFetcher
let logger: Logger
init(container: DIContainer) {

View File

@ -9,7 +9,7 @@ import Foundation
final class ProcessSuggestedScanRangesAction {
let rustBackend: ZcashRustBackendWelding
let service: LightWalletService
var service: LightWalletService
let logger: Logger
let metrics: SDKMetrics

View File

@ -8,9 +8,9 @@
import Foundation
final class RewindAction {
let downloader: BlockDownloader
var downloader: BlockDownloader
let rustBackend: ZcashRustBackendWelding
let downloaderService: BlockDownloaderService
var downloaderService: BlockDownloaderService
let logger: Logger
init(container: DIContainer) {

View File

@ -15,7 +15,7 @@ final class ScanAction {
let configProvider: CompactBlockProcessor.ConfigProvider
let blockScanner: BlockScanner
let rustBackend: ZcashRustBackendWelding
let latestBlocksDataProvider: LatestBlocksDataProvider
var latestBlocksDataProvider: LatestBlocksDataProvider
let logger: Logger
var progressReportReducer = 0

View File

@ -9,9 +9,9 @@ import Foundation
final class UpdateChainTipAction {
let rustBackend: ZcashRustBackendWelding
let downloader: BlockDownloader
let service: LightWalletService
let latestBlocksDataProvider: LatestBlocksDataProvider
var downloader: BlockDownloader
var service: LightWalletService
var latestBlocksDataProvider: LatestBlocksDataProvider
let logger: Logger
init(container: DIContainer) {

View File

@ -10,7 +10,7 @@ import Foundation
final class UpdateSubtreeRootsAction {
let configProvider: CompactBlockProcessor.ConfigProvider
let rustBackend: ZcashRustBackendWelding
let service: LightWalletService
var service: LightWalletService
let logger: Logger
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {

View File

@ -10,7 +10,7 @@ import Foundation
final class ValidateServerAction {
let configProvider: CompactBlockProcessor.ConfigProvider
let rustBackend: ZcashRustBackendWelding
let service: LightWalletService
var service: LightWalletService
init(container: DIContainer, configProvider: CompactBlockProcessor.ConfigProvider) {
self.configProvider = configProvider

View File

@ -29,12 +29,12 @@ actor CompactBlockProcessor {
private var afterSyncHooksManager = AfterSyncHooksManager()
private let accountRepository: AccountRepository
let blockDownloaderService: BlockDownloaderService
private let latestBlocksDataProvider: LatestBlocksDataProvider
var blockDownloaderService: BlockDownloaderService
private var latestBlocksDataProvider: LatestBlocksDataProvider
private let logger: Logger
private let metrics: SDKMetrics
private let rustBackend: ZcashRustBackendWelding
let service: LightWalletService
var service: LightWalletService
let storage: CompactBlockRepository
private let transactionRepository: TransactionRepository
private let fileManager: ZcashFileManager
@ -412,6 +412,52 @@ extension CompactBlockProcessor {
}
}
// MARK: - Switch server
extension CompactBlockProcessor {
func updateService(_ container: DIContainer) {
// LightWalletGRPCService
let updatedLWDService = container.resolve(LightWalletService.self)
(actions[.processSuggestedScanRanges] as? ProcessSuggestedScanRangesAction)?.service = updatedLWDService
(actions[.updateChainTip] as? UpdateChainTipAction)?.service = updatedLWDService
(actions[.updateSubtreeRoots] as? UpdateSubtreeRootsAction)?.service = updatedLWDService
(actions[.validateServer] as? ValidateServerAction)?.service = updatedLWDService
self.service = updatedLWDService
// BlockDownloaderService
let updatedDownloaderService = container.resolve(BlockDownloaderService.self)
(actions[.rewind] as? RewindAction)?.downloaderService = updatedDownloaderService
self.blockDownloaderService = updatedDownloaderService
// LatestBlocksDataProvider
let updatedLBDProvider = container.resolve(LatestBlocksDataProvider.self)
(actions[.scan] as? ScanAction)?.latestBlocksDataProvider = updatedLBDProvider
(actions[.updateChainTip] as? UpdateChainTipAction)?.latestBlocksDataProvider = updatedLBDProvider
self.latestBlocksDataProvider = updatedLBDProvider
// BlockDownloader
let updatedBlockDownloader = container.resolve(BlockDownloader.self)
(actions[.download] as? DownloadAction)?.downloader = updatedBlockDownloader
(actions[.updateChainTip] as? UpdateChainTipAction)?.downloader = updatedBlockDownloader
(actions[.rewind] as? RewindAction)?.downloader = updatedBlockDownloader
self.blockDownloaderService = updatedDownloaderService
// BlockEnhancer
let updatedEnhancer = container.resolve(BlockEnhancer.self)
(actions[.enhance] as? EnhanceAction)?.blockEnhancer = updatedEnhancer
// UTXOFetcher
let updatedUTXOFetcher = container.resolve(UTXOFetcher.self)
(actions[.fetchUTXO] as? FetchUTXOsAction)?.utxoFetcher = updatedUTXOFetcher
}
}
// MARK: - Events
extension CompactBlockProcessor {

View File

@ -3,7 +3,7 @@
scriptDir=${0:a:h}
cd "${scriptDir}"
sourcery_version=2.0.3
sourcery_version=2.1.7
if which sourcery >/dev/null; then
if [[ $(sourcery --version) != $sourcery_version ]]; then

View File

@ -1,4 +1,4 @@
// Generated using Sourcery 2.0.3 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.1.7 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
/*
@ -121,6 +121,11 @@ public enum ZcashError: Equatable, Error {
/// - `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
@ -148,6 +153,11 @@ public enum ZcashError: Equatable, Error {
/// - `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
@ -287,6 +297,10 @@ public enum ZcashError: Equatable, Error {
/// Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes.
/// ZRUST0050
case rustGetMemoInvalidTxIdLength
/// Error from rust layer when calling ZcashRustBackend.getScanProgress
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0051
case rustGetScanProgress(_ rustError: String)
/// Error from rust layer when calling ZcashRustBackend.fullyScannedHeight
/// - `rustError` contains error generated by the rust layer.
/// ZRUST0052
@ -570,6 +584,9 @@ public enum ZcashError: Equatable, Error {
/// Indicates that this Synchronizer is disconnected from its lightwalletd server.
/// ZSYNCO0006
case synchronizerDisconnected
/// The attempt to switch endpoints failed. Check that the hostname and port are correct, and are formatted as <hostname>:<port>.
/// ZSYNCO0007
case synchronizerServerSwitch
public var message: String {
switch self {
@ -604,6 +621,7 @@ public enum ZcashError: Equatable, Error {
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"
@ -611,6 +629,7 @@ public enum ZcashError: Equatable, Error {
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"
@ -648,6 +667,7 @@ public enum ZcashError: Equatable, Error {
case .rustUpdateChainTip: return "Error from rust layer when calling ZcashRustBackend.updateChainTip"
case .rustSuggestScanRanges: return "Error from rust layer when calling ZcashRustBackend.suggestScanRanges"
case .rustGetMemoInvalidTxIdLength: return "Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes."
case .rustGetScanProgress: return "Error from rust layer when calling ZcashRustBackend.getScanProgress"
case .rustFullyScannedHeight: return "Error from rust layer when calling ZcashRustBackend.fullyScannedHeight"
case .rustMaxScannedHeight: return "Error from rust layer when calling ZcashRustBackend.maxScannedHeight"
case .rustLatestCachedBlockHeight: return "Error from rust layer when calling ZcashRustBackend.latestCachedBlockHeight"
@ -737,6 +757,7 @@ public enum ZcashError: Equatable, Error {
case .synchronizerLatestUTXOsInvalidTAddress: return "LatestUTXOs for the address failed, invalid t-address."
case .synchronizerRewindUnknownArchorHeight: return "Rewind failed, unknown archor height"
case .synchronizerDisconnected: return "Indicates that this Synchronizer is disconnected from its lightwalletd server."
case .synchronizerServerSwitch: return "The attempt to switch endpoints failed. Check that the hostname and port are correct, and are formatted as <hostname>:<port>."
}
}
@ -773,6 +794,7 @@ public enum ZcashError: Equatable, Error {
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
@ -780,6 +802,7 @@ public enum ZcashError: Equatable, Error {
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
@ -817,6 +840,7 @@ public enum ZcashError: Equatable, Error {
case .rustUpdateChainTip: return .rustUpdateChainTip
case .rustSuggestScanRanges: return .rustSuggestScanRanges
case .rustGetMemoInvalidTxIdLength: return .rustGetMemoInvalidTxIdLength
case .rustGetScanProgress: return .rustGetScanProgress
case .rustFullyScannedHeight: return .rustFullyScannedHeight
case .rustMaxScannedHeight: return .rustMaxScannedHeight
case .rustLatestCachedBlockHeight: return .rustLatestCachedBlockHeight
@ -906,6 +930,7 @@ public enum ZcashError: Equatable, Error {
case .synchronizerLatestUTXOsInvalidTAddress: return .synchronizerLatestUTXOsInvalidTAddress
case .synchronizerRewindUnknownArchorHeight: return .synchronizerRewindUnknownArchorHeight
case .synchronizerDisconnected: return .synchronizerDisconnected
case .synchronizerServerSwitch: return .synchronizerServerSwitch
}
}

View File

@ -1,4 +1,4 @@
// Generated using Sourcery 2.0.3 https://github.com/krzysztofzablocki/Sourcery
// Generated using Sourcery 2.1.7 https://github.com/krzysztofzablocki/Sourcery
// DO NOT EDIT
/*
@ -71,6 +71,8 @@ public enum ZcashErrorCode: String {
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
@ -85,6 +87,8 @@ public enum ZcashErrorCode: String {
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
@ -159,6 +163,8 @@ public enum ZcashErrorCode: String {
case rustSuggestScanRanges = "ZRUST0049"
/// Invalid transaction ID length when calling ZcashRustBackend.getMemo. txId must be 32 bytes.
case rustGetMemoInvalidTxIdLength = "ZRUST0050"
/// Error from rust layer when calling ZcashRustBackend.getScanProgress
case rustGetScanProgress = "ZRUST0051"
/// Error from rust layer when calling ZcashRustBackend.fullyScannedHeight
case rustFullyScannedHeight = "ZRUST0052"
/// Error from rust layer when calling ZcashRustBackend.maxScannedHeight
@ -337,4 +343,6 @@ public enum ZcashErrorCode: String {
case synchronizerRewindUnknownArchorHeight = "ZSYNCO0005"
/// Indicates that this Synchronizer is disconnected from its lightwalletd server.
case synchronizerDisconnected = "ZSYNCO0006"
/// The attempt to switch endpoints failed. Check that the hostname and port are correct, and are formatted as <hostname>:<port>.
case synchronizerServerSwitch = "ZSYNCO0007"
}

View File

@ -344,7 +344,11 @@ enum ZcashErrorDefinition {
/// - `progress` value reported
// sourcery: code="ZRUST0055"
case rustScanProgressOutOfRange(_ progress: String)
/// Error from rust layer when calling ZcashRustBackend.getWalletSummary
/// - `rustError` contains error generated by the rust layer.
// sourcery: code="ZRUST0056"
case rustGetWalletSummary(_ rustError: String)
// MARK: - Account DAO
/// SQLite query failed when fetching all accounts from the database.
@ -659,4 +663,7 @@ enum ZcashErrorDefinition {
/// Indicates that this Synchronizer is disconnected from its lightwalletd server.
// sourcery: code="ZSYNCO0006"
case synchronizerDisconnected
/// The attempt to switch endpoints failed. Check that the hostname and port are correct, and are formatted as <hostname>:<port>.
// sourcery: code="ZSYNCO0007"
case synchronizerServerSwitch
}

View File

@ -111,18 +111,18 @@ public class Initializer {
let container: DIContainer
let alias: ZcashSynchronizerAlias
let endpoint: LightWalletEndpoint
var endpoint: LightWalletEndpoint
let fsBlockDbRoot: URL
let generalStorageURL: URL
let dataDbURL: URL
let spendParamsURL: URL
let outputParamsURL: URL
let saplingParamsSourceURL: SaplingParamsSourceURL
let lightWalletService: LightWalletService
var lightWalletService: LightWalletService
let transactionRepository: TransactionRepository
let accountRepository: AccountRepository
let storage: CompactBlockRepository
let blockDownloaderService: BlockDownloaderService
var blockDownloaderService: BlockDownloaderService
let network: ZcashNetwork
let logger: Logger
let rustBackend: ZcashRustBackendWelding
@ -286,10 +286,6 @@ public class Initializer {
self.logger = container.resolve(Logger.self)
}
private static func makeLightWalletServiceFactory(endpoint: LightWalletEndpoint) -> LightWalletServiceFactory {
return LightWalletServiceFactory(endpoint: endpoint)
}
// swiftlint:disable:next function_parameter_count
private static func setup(
container: DIContainer,

View File

@ -295,6 +295,12 @@ public protocol Synchronizer: AnyObject {
/// 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>
/// This API stops the synchronization and re-initalizes everything according to the new endpoint provided.
/// It can be called anytime.
/// - Throws: ZcashError when failures occur and related to `synchronizer.start(retry: Bool)`, it's the only throwing operation
/// during the whole endpoint change.
func switchTo(endpoint: LightWalletEndpoint) async throws
}
public enum SyncStatus: Equatable {

View File

@ -18,7 +18,7 @@ enum Dependencies {
enableBackendTracing: Bool = false
) {
container.register(type: CheckpointSource.self, isSingleton: true) { _ in
return CheckpointSourceFactory.fromBundle(for: networkType)
CheckpointSourceFactory.fromBundle(for: networkType)
}
container.register(type: Logger.self, isSingleton: true) { _ in
@ -36,7 +36,7 @@ enum Dependencies {
}
container.register(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in
return ZcashRustBackend(
ZcashRustBackend(
dbData: urls.dataDbURL,
fsBlockDbRoot: urls.fsBlockDbRoot,
spendParamsPath: urls.spendParamsURL,
@ -47,7 +47,7 @@ enum Dependencies {
}
container.register(type: LightWalletService.self, isSingleton: true) { _ in
return LightWalletGRPCService(endpoint: endpoint)
LightWalletGRPCService(endpoint: endpoint)
}
container.register(type: TransactionRepository.self, isSingleton: true) { _ in

View File

@ -44,7 +44,7 @@ public class SDKSynchronizer: Synchronizer {
private let syncSessionIDGenerator: SyncSessionIDGenerator
private let syncSession: SyncSession
private let syncSessionTicker: SessionTicker
let latestBlocksDataProvider: LatestBlocksDataProvider
var latestBlocksDataProvider: LatestBlocksDataProvider
/// Creates an SDKSynchronizer instance
/// - Parameter initializer: a wallet Initializer object
@ -532,6 +532,90 @@ public class SDKSynchronizer: Synchronizer {
return subject.eraseToAnyPublisher()
}
// MARK: Server switch
public func switchTo(endpoint: LightWalletEndpoint) async throws {
// Stop synchronization
let status = await self.status
if status != .stopped && status != .disconnected {
await blockProcessor.stop()
}
// Validation of the server is first because any custom endpoint can be passed here
// Extra instance of the service is created with lower timeout ofr a single call
initializer.container.register(type: LightWalletService.self, isSingleton: true) { _ in
LightWalletGRPCService(
host: endpoint.host,
port: endpoint.port,
secure: endpoint.secure,
singleCallTimeout: 5000,
streamingCallTimeout: endpoint.streamingCallTimeoutInMillis
)
}
let validateSever = ValidateServerAction(
container: initializer.container,
configProvider: CompactBlockProcessor.ConfigProvider(config: await blockProcessor.config)
)
do {
_ = try await validateSever.run(with: ActionContextImpl(state: .idle)) { _ in }
} catch {
throw ZcashError.synchronizerServerSwitch
}
// The `ValidateServerAction` confirmed the server is ok and we can continue
// final instance of the service will be instantiated and propagated to the all parties
// SWITCH TO NEW ENDPOINT
// LightWalletService dependency update
initializer.container.register(type: LightWalletService.self, isSingleton: true) { _ in
LightWalletGRPCService(endpoint: endpoint)
}
// DEPENDENCIES
// BlockDownloaderService dependency update
initializer.container.register(type: BlockDownloaderService.self, isSingleton: true) { di in
let service = di.resolve(LightWalletService.self)
let storage = di.resolve(CompactBlockRepository.self)
return BlockDownloaderServiceImpl(service: service, storage: storage)
}
// LatestBlocksDataProvider dependency update
initializer.container.register(type: LatestBlocksDataProvider.self, isSingleton: true) { di in
let service = di.resolve(LightWalletService.self)
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
return LatestBlocksDataProviderImpl(service: service, rustBackend: rustBackend)
}
// CompactBlockProcessor dependency update
Dependencies.setupCompactBlockProcessor(
in: initializer.container,
config: await blockProcessor.config,
accountRepository: initializer.accountRepository
)
// INITIALIZER
initializer.lightWalletService = initializer.container.resolve(LightWalletService.self)
initializer.blockDownloaderService = initializer.container.resolve(BlockDownloaderService.self)
initializer.endpoint = endpoint
// SELF
self.latestBlocksDataProvider = initializer.container.resolve(LatestBlocksDataProvider.self)
// COMPACT BLOCK PROCESSOR
await blockProcessor.updateService(initializer.container)
// Start synchronization
if status != .unprepared {
try await start(retry: true)
}
}
// MARK: notify state
private func snapshotState(status: InternalSyncStatus) async -> SynchronizerState {

View File

@ -1733,6 +1733,25 @@ class SynchronizerMock: Synchronizer {
}
}
// MARK: - switchTo
var switchToEndpointThrowableError: Error?
var switchToEndpointCallsCount = 0
var switchToEndpointCalled: Bool {
return switchToEndpointCallsCount > 0
}
var switchToEndpointReceivedEndpoint: LightWalletEndpoint?
var switchToEndpointClosure: ((LightWalletEndpoint) async throws -> Void)?
func switchTo(endpoint: LightWalletEndpoint) async throws {
if let error = switchToEndpointThrowableError {
throw error
}
switchToEndpointCallsCount += 1
switchToEndpointReceivedEndpoint = endpoint
try await switchToEndpointClosure!(endpoint)
}
}
class TransactionRepositoryMock: TransactionRepository {