react-native-zcash 0.8.1 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/android/build.gradle +3 -3
- package/android/src/main/assets/co.electriccoin.zcash/checkpoint/mainnet/2650000.json +8 -0
- package/android/src/main/java/app/edge/rnzcash/RNZcashModule.kt +15 -25
- package/ios/RNZcash.m +3 -5
- package/ios/RNZcash.swift +42 -23
- package/ios/ZCashLightClientKit/Block/Actions/Action.swift +1 -0
- package/ios/ZCashLightClientKit/Block/Actions/EnhanceAction.swift +1 -1
- package/ios/ZCashLightClientKit/Block/Actions/ProcessSuggestedScanRangesAction.swift +1 -1
- package/ios/ZCashLightClientKit/Block/Actions/ScanAction.swift +1 -1
- package/ios/ZCashLightClientKit/Block/Actions/TxResubmissionAction.swift +75 -0
- package/ios/ZCashLightClientKit/Block/Actions/UpdateChainTipAction.swift +1 -1
- package/ios/ZCashLightClientKit/Block/Actions/UpdateSubtreeRootsAction.swift +3 -1
- package/ios/ZCashLightClientKit/Block/CompactBlockProcessor.swift +14 -0
- package/ios/ZCashLightClientKit/Block/Download/BlockDownloaderService.swift +2 -2
- package/ios/ZCashLightClientKit/Block/Enhance/BlockEnhancer.swift +54 -49
- package/ios/ZCashLightClientKit/Checkpoint/BundleCheckpointSource.swift +1 -6
- package/ios/ZCashLightClientKit/Checkpoint/CheckpointSourceFactory.swift +1 -1
- package/ios/ZCashLightClientKit/ClosureSynchronizer.swift +3 -1
- package/ios/ZCashLightClientKit/CombineSynchronizer.swift +6 -2
- package/ios/ZCashLightClientKit/Constants/ZcashSDK.swift +15 -0
- package/ios/ZCashLightClientKit/DAO/TransactionDao.swift +20 -4
- package/ios/ZCashLightClientKit/Entity/TransactionEntity.swift +27 -24
- package/ios/ZCashLightClientKit/Error/Sourcery/generateErrorCode.sh +2 -2
- package/ios/ZCashLightClientKit/Error/ZcashError.swift +32 -1
- package/ios/ZCashLightClientKit/Error/ZcashErrorCode.swift +11 -1
- package/ios/ZCashLightClientKit/Error/ZcashErrorCodeDefinition.swift +18 -0
- package/ios/ZCashLightClientKit/Initializer.swift +22 -14
- package/ios/ZCashLightClientKit/Metrics/SDKMetrics.swift +0 -1
- package/ios/ZCashLightClientKit/Model/FiatCurrencyResult.swift +25 -0
- package/ios/ZCashLightClientKit/Model/Proposal.swift +1 -1
- package/ios/ZCashLightClientKit/Model/TransactionDataRequest.swift +26 -0
- package/ios/ZCashLightClientKit/Model/WalletTypes.swift +39 -1
- package/ios/ZCashLightClientKit/Model/Zatoshi.swift +1 -1
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/LightWalletGRPCService.swift +39 -2
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/proto/service.proto +5 -4
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.grpc.swift +819 -3
- package/ios/ZCashLightClientKit/Modules/Service/GRPC/ProtoBuf/service.pb.swift +2 -2
- package/ios/ZCashLightClientKit/Modules/Service/LightWalletService.swift +3 -1
- package/ios/ZCashLightClientKit/Providers/ResourceProvider.swift +10 -0
- package/ios/ZCashLightClientKit/Repository/TransactionRepository.swift +4 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2475000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2477500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2482500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2485000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2487500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2492500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2495000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2497500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2502500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2505000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2507500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2512500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2515000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2517500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2522500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2525000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2527500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2532500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2535000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2537500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2542500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2545000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2547500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2552500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2555000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2557500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2562500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2565000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2567500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2572500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2575000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2577500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2582500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2585000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2587500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2592500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2595000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2597500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2602500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2605000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2607500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2612500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2615000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2617500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2622500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2625000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2627500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2632500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2635000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2637500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2642500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2645000.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2647500.json +8 -0
- package/ios/ZCashLightClientKit/Resources/checkpoints/mainnet/2650000.json +8 -0
- package/ios/ZCashLightClientKit/Rust/ZcashKeyDerivationBackend.swift +4 -24
- package/ios/ZCashLightClientKit/Rust/ZcashKeyDerivationBackendWelding.swift +0 -15
- package/ios/ZCashLightClientKit/Rust/ZcashRustBackend.swift +133 -32
- package/ios/ZCashLightClientKit/Rust/ZcashRustBackendWelding.swift +18 -1
- package/ios/ZCashLightClientKit/Synchronizer/ClosureSDKSynchronizer.swift +4 -0
- package/ios/ZCashLightClientKit/Synchronizer/CombineSDKSynchronizer.swift +5 -1
- package/ios/ZCashLightClientKit/Synchronizer/Dependencies.swift +53 -3
- package/ios/ZCashLightClientKit/Synchronizer/SDKSynchronizer.swift +236 -7
- package/ios/ZCashLightClientKit/Synchronizer.swift +28 -0
- package/ios/ZCashLightClientKit/Tool/DerivationTool.swift +29 -5
- package/ios/ZCashLightClientKit/Tor/TorClient.swift +57 -0
- package/ios/ZCashLightClientKit/Utils/LoggingProxy.swift +4 -0
- package/ios/ZCashLightClientKit/Utils/OSLogger.swift +4 -0
- package/ios/libzcashlc.xcframework/ios-arm64/libzcashlc.a +0 -0
- package/ios/libzcashlc.xcframework/ios-arm64_x86_64-simulator/libzcashlc.a +0 -0
- package/ios/zcashlc.h +287 -42
- package/lib/rnzcash.rn.js +8 -8
- package/lib/rnzcash.rn.js.map +1 -1
- package/lib/src/react-native.d.ts +3 -3
- package/lib/src/types.d.ts +4 -2
- package/package.json +1 -1
- package/src/react-native.ts +13 -18
- package/src/types.ts +5 -2
|
@@ -14,8 +14,7 @@ enum Dependencies {
|
|
|
14
14
|
alias: ZcashSynchronizerAlias,
|
|
15
15
|
networkType: NetworkType,
|
|
16
16
|
endpoint: LightWalletEndpoint,
|
|
17
|
-
loggingPolicy: Initializer.LoggingPolicy = .default(.debug)
|
|
18
|
-
enableBackendTracing: Bool = false
|
|
17
|
+
loggingPolicy: Initializer.LoggingPolicy = .default(.debug)
|
|
19
18
|
) {
|
|
20
19
|
container.register(type: CheckpointSource.self, isSingleton: true) { _ in
|
|
21
20
|
CheckpointSourceFactory.fromBundle(for: networkType)
|
|
@@ -35,6 +34,36 @@ enum Dependencies {
|
|
|
35
34
|
return logger
|
|
36
35
|
}
|
|
37
36
|
|
|
37
|
+
let rustLogging: RustLogging
|
|
38
|
+
switch loggingPolicy {
|
|
39
|
+
case .default(let logLevel):
|
|
40
|
+
switch logLevel {
|
|
41
|
+
case .debug:
|
|
42
|
+
rustLogging = RustLogging.debug
|
|
43
|
+
case .info, .event:
|
|
44
|
+
rustLogging = RustLogging.info
|
|
45
|
+
case .warning:
|
|
46
|
+
rustLogging = RustLogging.warn
|
|
47
|
+
case .error:
|
|
48
|
+
rustLogging = RustLogging.error
|
|
49
|
+
}
|
|
50
|
+
case .custom(let logger):
|
|
51
|
+
switch logger.maxLogLevel() {
|
|
52
|
+
case .debug:
|
|
53
|
+
rustLogging = RustLogging.debug
|
|
54
|
+
case .info, .event:
|
|
55
|
+
rustLogging = RustLogging.info
|
|
56
|
+
case .warning:
|
|
57
|
+
rustLogging = RustLogging.warn
|
|
58
|
+
case .error:
|
|
59
|
+
rustLogging = RustLogging.error
|
|
60
|
+
case .none:
|
|
61
|
+
rustLogging = RustLogging.off
|
|
62
|
+
}
|
|
63
|
+
case .noLogging:
|
|
64
|
+
rustLogging = RustLogging.off
|
|
65
|
+
}
|
|
66
|
+
|
|
38
67
|
container.register(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in
|
|
39
68
|
ZcashRustBackend(
|
|
40
69
|
dbData: urls.dataDbURL,
|
|
@@ -42,7 +71,7 @@ enum Dependencies {
|
|
|
42
71
|
spendParamsPath: urls.spendParamsURL,
|
|
43
72
|
outputParamsPath: urls.outputParamsURL,
|
|
44
73
|
networkType: networkType,
|
|
45
|
-
|
|
74
|
+
logLevel: rustLogging
|
|
46
75
|
)
|
|
47
76
|
}
|
|
48
77
|
|
|
@@ -96,6 +125,25 @@ enum Dependencies {
|
|
|
96
125
|
container.register(type: ZcashFileManager.self, isSingleton: true) { _ in
|
|
97
126
|
FileManager.default
|
|
98
127
|
}
|
|
128
|
+
|
|
129
|
+
container.register(type: TransactionEncoder.self, isSingleton: true) { di in
|
|
130
|
+
let service = di.resolve(LightWalletService.self)
|
|
131
|
+
let logger = di.resolve(Logger.self)
|
|
132
|
+
let transactionRepository = di.resolve(TransactionRepository.self)
|
|
133
|
+
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
|
|
134
|
+
|
|
135
|
+
return WalletTransactionEncoder(
|
|
136
|
+
rustBackend: rustBackend,
|
|
137
|
+
dataDb: urls.dataDbURL,
|
|
138
|
+
fsBlockDbRoot: urls.fsBlockDbRoot,
|
|
139
|
+
service: service,
|
|
140
|
+
repository: transactionRepository,
|
|
141
|
+
outputParams: urls.outputParamsURL,
|
|
142
|
+
spendParams: urls.spendParamsURL,
|
|
143
|
+
networkType: networkType,
|
|
144
|
+
logger: logger
|
|
145
|
+
)
|
|
146
|
+
}
|
|
99
147
|
}
|
|
100
148
|
|
|
101
149
|
static func setupCompactBlockProcessor(
|
|
@@ -145,6 +193,7 @@ enum Dependencies {
|
|
|
145
193
|
let rustBackend = di.resolve(ZcashRustBackendWelding.self)
|
|
146
194
|
let transactionRepository = di.resolve(TransactionRepository.self)
|
|
147
195
|
let metrics = di.resolve(SDKMetrics.self)
|
|
196
|
+
let service = di.resolve(LightWalletService.self)
|
|
148
197
|
let logger = di.resolve(Logger.self)
|
|
149
198
|
|
|
150
199
|
return BlockEnhancerImpl(
|
|
@@ -152,6 +201,7 @@ enum Dependencies {
|
|
|
152
201
|
rustBackend: rustBackend,
|
|
153
202
|
transactionRepository: transactionRepository,
|
|
154
203
|
metrics: metrics,
|
|
204
|
+
service: service,
|
|
155
205
|
logger: logger
|
|
156
206
|
)
|
|
157
207
|
}
|
|
@@ -12,7 +12,6 @@ import Combine
|
|
|
12
12
|
/// Synchronizer implementation for UIKit and iOS 13+
|
|
13
13
|
// swiftlint:disable type_body_length
|
|
14
14
|
public class SDKSynchronizer: Synchronizer {
|
|
15
|
-
|
|
16
15
|
public var alias: ZcashSynchronizerAlias { initializer.alias }
|
|
17
16
|
|
|
18
17
|
private lazy var streamsUpdateQueue = { DispatchQueue(label: "streamsUpdateQueue_\(initializer.alias.description)") }()
|
|
@@ -23,8 +22,12 @@ public class SDKSynchronizer: Synchronizer {
|
|
|
23
22
|
private let eventSubject = PassthroughSubject<SynchronizerEvent, Never>()
|
|
24
23
|
public var eventStream: AnyPublisher<SynchronizerEvent, Never> { eventSubject.eraseToAnyPublisher() }
|
|
25
24
|
|
|
25
|
+
private let exchangeRateUSDSubject = CurrentValueSubject<FiatCurrencyResult?, Never>(nil)
|
|
26
|
+
public var exchangeRateUSDStream: AnyPublisher<FiatCurrencyResult?, Never> { exchangeRateUSDSubject.eraseToAnyPublisher() }
|
|
27
|
+
|
|
26
28
|
let metrics: SDKMetrics
|
|
27
29
|
public let logger: Logger
|
|
30
|
+
var tor: TorClient?
|
|
28
31
|
|
|
29
32
|
// Don't read this variable directly. Use `status` instead. And don't update this variable directly use `updateStatus()` methods instead.
|
|
30
33
|
private var underlyingStatus: GenericActor<InternalSyncStatus>
|
|
@@ -83,12 +86,14 @@ public class SDKSynchronizer: Synchronizer {
|
|
|
83
86
|
self.syncSession = SyncSession(.nullID)
|
|
84
87
|
self.syncSessionTicker = syncSessionTicker
|
|
85
88
|
self.latestBlocksDataProvider = initializer.container.resolve(LatestBlocksDataProvider.self)
|
|
86
|
-
|
|
89
|
+
|
|
87
90
|
initializer.lightWalletService.connectionStateChange = { [weak self] oldState, newState in
|
|
88
91
|
self?.connectivityStateChanged(oldState: oldState, newState: newState)
|
|
89
92
|
}
|
|
90
93
|
|
|
91
|
-
Task(priority: .high) { [weak self] in
|
|
94
|
+
Task(priority: .high) { [weak self] in
|
|
95
|
+
await self?.subscribeToProcessorEvents(blockProcessor)
|
|
96
|
+
}
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
deinit {
|
|
@@ -300,9 +305,9 @@ public class SDKSynchronizer: Synchronizer {
|
|
|
300
305
|
do {
|
|
301
306
|
try throwIfUnprepared()
|
|
302
307
|
return try await transactionEncoder.proposeFulfillingPaymentFromURI(
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
308
|
+
uri,
|
|
309
|
+
accountIndex: accountIndex
|
|
310
|
+
)
|
|
306
311
|
} catch ZcashError.rustCreateToAddress(let error) {
|
|
307
312
|
throw ZcashError.rustProposeTransferFromURI(error)
|
|
308
313
|
} catch {
|
|
@@ -480,8 +485,12 @@ public class SDKSynchronizer: Synchronizer {
|
|
|
480
485
|
PagedTransactionRepositoryBuilder.build(initializer: initializer, kind: .all)
|
|
481
486
|
}
|
|
482
487
|
|
|
488
|
+
public func getMemos(for rawID: Data) async throws -> [Memo] {
|
|
489
|
+
return try await transactionRepository.findMemos(for: rawID)
|
|
490
|
+
}
|
|
491
|
+
|
|
483
492
|
public func getMemos(for transaction: ZcashTransaction.Overview) async throws -> [Memo] {
|
|
484
|
-
return try await transactionRepository.findMemos(for: transaction)
|
|
493
|
+
return try await transactionRepository.findMemos(for: transaction.rawID)
|
|
485
494
|
}
|
|
486
495
|
|
|
487
496
|
public func getRecipients(for transaction: ZcashTransaction.Overview) async -> [TransactionRecipient] {
|
|
@@ -505,6 +514,41 @@ public class SDKSynchronizer: Synchronizer {
|
|
|
505
514
|
try await initializer.rustBackend.getWalletSummary()?.accountBalances[UInt32(accountIndex)]
|
|
506
515
|
}
|
|
507
516
|
|
|
517
|
+
/// Fetches the latest ZEC-USD exchange rate.
|
|
518
|
+
public func refreshExchangeRateUSD() {
|
|
519
|
+
// ignore refresh request when one is already in flight
|
|
520
|
+
if let latestState = tor?.cachedFiatCurrencyResult?.state, latestState == .fetching {
|
|
521
|
+
return
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// broadcast cached value but update the state
|
|
525
|
+
if let cachedFiatCurrencyResult = tor?.cachedFiatCurrencyResult {
|
|
526
|
+
var fetchingState = cachedFiatCurrencyResult
|
|
527
|
+
fetchingState.state = .fetching
|
|
528
|
+
tor?.cachedFiatCurrencyResult = fetchingState
|
|
529
|
+
|
|
530
|
+
exchangeRateUSDSubject.send(fetchingState)
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
Task {
|
|
534
|
+
do {
|
|
535
|
+
if tor == nil {
|
|
536
|
+
logger.info("Bootstrapping Tor client for fetching exchange rates")
|
|
537
|
+
tor = try await TorClient(torDir: initializer.torDirURL)
|
|
538
|
+
}
|
|
539
|
+
// broadcast new value in case of success
|
|
540
|
+
exchangeRateUSDSubject.send(try await tor?.getExchangeRateUSD())
|
|
541
|
+
} catch {
|
|
542
|
+
// broadcast cached value but update the state
|
|
543
|
+
var errorState = tor?.cachedFiatCurrencyResult
|
|
544
|
+
errorState?.state = .error
|
|
545
|
+
tor?.cachedFiatCurrencyResult = errorState
|
|
546
|
+
|
|
547
|
+
exchangeRateUSDSubject.send(errorState)
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
508
552
|
public func getUnifiedAddress(accountIndex: Int) async throws -> UnifiedAddress {
|
|
509
553
|
try await blockProcessor.getUnifiedAddress(accountIndex: accountIndex)
|
|
510
554
|
}
|
|
@@ -608,6 +652,191 @@ public class SDKSynchronizer: Synchronizer {
|
|
|
608
652
|
try await initializer.rustBackend.isSeedRelevantToAnyDerivedAccount(seed: seed)
|
|
609
653
|
}
|
|
610
654
|
|
|
655
|
+
/// Takes the list of endpoints and runs it through a series of checks to evaluate its performance.
|
|
656
|
+
/// - Parameters:
|
|
657
|
+
/// - endpoints: Array of endpoints to evaluate.
|
|
658
|
+
/// - latencyThresholdMillis: The mean latency of `getInfo` and `getTheLatestHeight` calls must be below this threshold. The default is 300 ms.
|
|
659
|
+
/// - fetchThresholdSeconds: The time to download `nBlocksToFetch` blocks from the stream must be below this threshold. The default is 60 seconds.
|
|
660
|
+
/// - nBlocksToFetch: The number of blocks expected to be downloaded from the stream, with the time compared to `fetchThresholdSeconds`. The default is 100.
|
|
661
|
+
/// - kServers: The expected number of endpoints in the output. The default is 3.
|
|
662
|
+
/// - network: Mainnet or testnet. The default is mainnet.
|
|
663
|
+
public func evaluateBestOf(
|
|
664
|
+
endpoints: [LightWalletEndpoint],
|
|
665
|
+
latencyThresholdMillis: Double = 300.0,
|
|
666
|
+
fetchThresholdSeconds: Double = 60.0,
|
|
667
|
+
nBlocksToFetch: UInt64 = 100,
|
|
668
|
+
kServers: Int = 3,
|
|
669
|
+
network: NetworkType = .mainnet
|
|
670
|
+
) async -> [LightWalletEndpoint] {
|
|
671
|
+
struct Service {
|
|
672
|
+
let originalEndpoint: LightWalletEndpoint
|
|
673
|
+
let service: LightWalletGRPCService
|
|
674
|
+
let url: String
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
struct CheckResult {
|
|
678
|
+
let id: String
|
|
679
|
+
let info: LightWalletdInfo?
|
|
680
|
+
let getInfoTime: TimeInterval
|
|
681
|
+
let latestBlockHeight: BlockHeight?
|
|
682
|
+
let latestBlockHeightTime: TimeInterval
|
|
683
|
+
let mean: TimeInterval
|
|
684
|
+
let service: Service
|
|
685
|
+
var blockTime: TimeInterval
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Initialize services for the endpoints
|
|
689
|
+
let services = endpoints.map {
|
|
690
|
+
Service(
|
|
691
|
+
originalEndpoint: $0,
|
|
692
|
+
service: LightWalletGRPCService(
|
|
693
|
+
host: $0.host,
|
|
694
|
+
port: $0.port,
|
|
695
|
+
secure: $0.secure,
|
|
696
|
+
singleCallTimeout: 5000,
|
|
697
|
+
streamingCallTimeout: Int64(fetchThresholdSeconds) * 1000
|
|
698
|
+
),
|
|
699
|
+
url: "\($0.host):\($0.port)"
|
|
700
|
+
)
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
// Parallel part
|
|
704
|
+
var checkResults: [String: CheckResult] = [:]
|
|
705
|
+
|
|
706
|
+
await withTaskGroup(of: CheckResult.self) { group in
|
|
707
|
+
for service in services {
|
|
708
|
+
group.addTask {
|
|
709
|
+
let startTime = Date().timeIntervalSince1970
|
|
710
|
+
let info = try? await service.service.getInfo()
|
|
711
|
+
let markTime = Date().timeIntervalSince1970
|
|
712
|
+
let latestBlockHeight = try? await service.service.latestBlockHeight()
|
|
713
|
+
let endTime = Date().timeIntervalSince1970
|
|
714
|
+
|
|
715
|
+
let getInfoTime = markTime - startTime
|
|
716
|
+
let latestBlockHeightTime = endTime - markTime
|
|
717
|
+
let mean = (getInfoTime + latestBlockHeightTime) / 2
|
|
718
|
+
|
|
719
|
+
return CheckResult(
|
|
720
|
+
id: service.url,
|
|
721
|
+
info: info,
|
|
722
|
+
getInfoTime: getInfoTime,
|
|
723
|
+
latestBlockHeight: latestBlockHeight,
|
|
724
|
+
latestBlockHeightTime: latestBlockHeightTime,
|
|
725
|
+
mean: mean,
|
|
726
|
+
service: service,
|
|
727
|
+
blockTime: 0
|
|
728
|
+
)
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
var tmpResults: [String: CheckResult] = [:]
|
|
733
|
+
|
|
734
|
+
for await result in group {
|
|
735
|
+
// rule out results where calls failed
|
|
736
|
+
guard let info = result.info, result.latestBlockHeight != nil else {
|
|
737
|
+
continue
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// rule out if mismatch of networks
|
|
741
|
+
guard (info.chainName == "main" && network == .mainnet)
|
|
742
|
+
|| (info.chainName == "test" && network == .testnet) else {
|
|
743
|
+
continue
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// rule out mismatch of consensus branch IDs
|
|
747
|
+
guard let localBranchID = await blockProcessor.consensusBranchIdFor(Int32(info.blockHeight)) else {
|
|
748
|
+
continue
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
guard let remoteBranchID = ConsensusBranchID.fromString(info.consensusBranchID) else {
|
|
752
|
+
continue
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
guard remoteBranchID == localBranchID else {
|
|
756
|
+
continue
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
// Rule out servers that are syncing, stuck, or probably on the wrong fork.
|
|
760
|
+
// To avoid falsely ruling out all servers this can only be a very loose check
|
|
761
|
+
// (i.e. `ZcashSDK.syncedThresholdBlocks` should not be too small),
|
|
762
|
+
// because `info.estimatedHeight` may be quite inaccurate.
|
|
763
|
+
guard info.blockHeight + ZcashSDK.syncedThresholdBlocks >= info.estimatedHeight else {
|
|
764
|
+
continue
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
tmpResults[result.id] = result
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// rule out all means above latencyThreshold
|
|
771
|
+
let underThreshold = tmpResults.compactMap {
|
|
772
|
+
$0.value.mean < (latencyThresholdMillis / 1000.0) ? $0 : nil
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
// sort the server responses by mean
|
|
776
|
+
let sortedUnderThreshold = underThreshold.sorted {
|
|
777
|
+
$0.value.mean < $1.value.mean
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
// retain only k servers
|
|
781
|
+
let sortedUnderThresholdKOnly = sortedUnderThreshold.prefix(3)
|
|
782
|
+
|
|
783
|
+
sortedUnderThresholdKOnly.forEach {
|
|
784
|
+
checkResults[$0.key] = $0.value
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Sequential part
|
|
789
|
+
var blockResults: [String: CheckResult] = [:]
|
|
790
|
+
|
|
791
|
+
for serviceDict in checkResults {
|
|
792
|
+
guard let info = serviceDict.value.info else {
|
|
793
|
+
continue
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
let service = serviceDict.value.service
|
|
797
|
+
|
|
798
|
+
guard info.blockHeight >= nBlocksToFetch else {
|
|
799
|
+
continue
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
let stream = service.service.blockStream(startHeight: BlockHeight(info.blockHeight - nBlocksToFetch), endHeight: BlockHeight(info.blockHeight))
|
|
803
|
+
|
|
804
|
+
do {
|
|
805
|
+
let startTime = Date().timeIntervalSince1970
|
|
806
|
+
var endTime = startTime
|
|
807
|
+
for try await _ in stream {
|
|
808
|
+
endTime = Date().timeIntervalSince1970
|
|
809
|
+
if endTime - startTime >= fetchThresholdSeconds {
|
|
810
|
+
break
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
let blockTime = endTime - startTime
|
|
815
|
+
|
|
816
|
+
// rule out servers that can't fetch `nBlocksToFetch` blocks under fetchThresholdSeconds
|
|
817
|
+
if blockTime < fetchThresholdSeconds {
|
|
818
|
+
var value = serviceDict.value
|
|
819
|
+
value.blockTime = blockTime
|
|
820
|
+
|
|
821
|
+
blockResults[serviceDict.key] = value
|
|
822
|
+
}
|
|
823
|
+
} catch {
|
|
824
|
+
continue
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
// return what's left
|
|
829
|
+
let sortedServers = blockResults.sorted {
|
|
830
|
+
$0.value.blockTime < $1.value.blockTime
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
let finalResult = sortedServers.map {
|
|
834
|
+
$0.value.service.originalEndpoint
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
return finalResult
|
|
838
|
+
}
|
|
839
|
+
|
|
611
840
|
// MARK: Server switch
|
|
612
841
|
|
|
613
842
|
public func switchTo(endpoint: LightWalletEndpoint) async throws {
|
|
@@ -101,6 +101,9 @@ public protocol Synchronizer: AnyObject {
|
|
|
101
101
|
/// This stream is backed by `PassthroughSubject`. Check `SynchronizerEvent` to see which events may be emitted.
|
|
102
102
|
var eventStream: AnyPublisher<SynchronizerEvent, Never> { get }
|
|
103
103
|
|
|
104
|
+
/// This stream emits the latest known USD/ZEC exchange rate, paired with the time it was queried. See `FiatCurrencyResult`.
|
|
105
|
+
var exchangeRateUSDStream: AnyPublisher<FiatCurrencyResult?, Never> { get }
|
|
106
|
+
|
|
104
107
|
/// Initialize the wallet. The ZIP-32 seed bytes can optionally be passed to perform
|
|
105
108
|
/// database migrations. most of the times the seed won't be needed. If they do and are
|
|
106
109
|
/// not provided this will fail with `InitializationResult.seedRequired`. It could
|
|
@@ -263,6 +266,11 @@ public protocol Synchronizer: AnyObject {
|
|
|
263
266
|
/// - Parameter kind: Transaction Kind expected from this PaginatedTransactionRepository
|
|
264
267
|
func paginatedTransactions(of kind: TransactionKind) -> PaginatedTransactionRepository
|
|
265
268
|
|
|
269
|
+
/// Get all memos for `transaction.rawID`.
|
|
270
|
+
///
|
|
271
|
+
// sourcery: mockedName="getMemosForRawID"
|
|
272
|
+
func getMemos(for rawID: Data) async throws -> [Memo]
|
|
273
|
+
|
|
266
274
|
/// Get all memos for `transaction`.
|
|
267
275
|
///
|
|
268
276
|
// sourcery: mockedName="getMemosForClearedTransaction"
|
|
@@ -304,6 +312,9 @@ public protocol Synchronizer: AnyObject {
|
|
|
304
312
|
/// - Returns: `AccountBalance`, struct that holds sapling and unshielded balances or `nil` when no account is associated with `accountIndex`
|
|
305
313
|
func getAccountBalance(accountIndex: Int) async throws -> AccountBalance?
|
|
306
314
|
|
|
315
|
+
/// Fetches the latest ZEC-USD exchange rate and updates `exchangeRateUSDSubject`.
|
|
316
|
+
func refreshExchangeRateUSD()
|
|
317
|
+
|
|
307
318
|
/// Rescans the known blocks with the current keys.
|
|
308
319
|
///
|
|
309
320
|
/// `rewind(policy:)` can be called anytime. If the sync process is in progress then it is stopped first. In this case, it make some significant
|
|
@@ -356,6 +367,23 @@ public protocol Synchronizer: AnyObject {
|
|
|
356
367
|
///
|
|
357
368
|
/// - parameter seed: byte array of the seed
|
|
358
369
|
func isSeedRelevantToAnyDerivedAccount(seed: [UInt8]) async throws -> Bool
|
|
370
|
+
|
|
371
|
+
/// Takes the list of endpoints and runs it through a series of checks to evaluate its performance.
|
|
372
|
+
/// - Parameters:
|
|
373
|
+
/// - endpoints: Array of endpoints to evaluate.
|
|
374
|
+
/// - latencyThresholdMillis: The mean latency of `getInfo` and `getTheLatestHeight` calls must be below this threshold. The default is 300 ms.
|
|
375
|
+
/// - fetchThresholdSeconds: The time to download `nBlocksToFetch` blocks from the stream must be below this threshold. The default is 60 seconds.
|
|
376
|
+
/// - nBlocksToFetch: The number of blocks expected to be downloaded from the stream, with the time compared to `fetchThresholdSeconds`. The default is 100.
|
|
377
|
+
/// - kServers: The expected number of endpoints in the output. The default is 3.
|
|
378
|
+
/// - network: Mainnet or testnet. The default is mainnet.
|
|
379
|
+
func evaluateBestOf(
|
|
380
|
+
endpoints: [LightWalletEndpoint],
|
|
381
|
+
latencyThresholdMillis: Double,
|
|
382
|
+
fetchThresholdSeconds: Double,
|
|
383
|
+
nBlocksToFetch: UInt64,
|
|
384
|
+
kServers: Int,
|
|
385
|
+
network: NetworkType
|
|
386
|
+
) async -> [LightWalletEndpoint]
|
|
359
387
|
}
|
|
360
388
|
|
|
361
389
|
public enum SyncStatus: Equatable {
|
|
@@ -108,15 +108,29 @@ extension DerivationTool: KeyValidation {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
public func isValidUnifiedAddress(_ unifiedAddress: String) -> Bool {
|
|
111
|
-
|
|
111
|
+
DerivationTool.getAddressMetadata(unifiedAddress).map {
|
|
112
|
+
$0.networkType == backend.networkType && $0.addressType == AddressType.unified
|
|
113
|
+
} ?? false
|
|
112
114
|
}
|
|
113
|
-
|
|
115
|
+
|
|
114
116
|
public func isValidTransparentAddress(_ tAddress: String) -> Bool {
|
|
115
|
-
|
|
117
|
+
DerivationTool.getAddressMetadata(tAddress).map {
|
|
118
|
+
$0.networkType == backend.networkType && (
|
|
119
|
+
$0.addressType == AddressType.p2pkh || $0.addressType == AddressType.p2sh
|
|
120
|
+
)
|
|
121
|
+
} ?? false
|
|
116
122
|
}
|
|
117
|
-
|
|
123
|
+
|
|
118
124
|
public func isValidSaplingAddress(_ zAddress: String) -> Bool {
|
|
119
|
-
|
|
125
|
+
DerivationTool.getAddressMetadata(zAddress).map {
|
|
126
|
+
$0.networkType == backend.networkType && $0.addressType == AddressType.sapling
|
|
127
|
+
} ?? false
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public func isValidTexAddress(_ texAddress: String) -> Bool {
|
|
131
|
+
DerivationTool.getAddressMetadata(texAddress).map {
|
|
132
|
+
$0.networkType == backend.networkType && $0.addressType == AddressType.tex
|
|
133
|
+
} ?? false
|
|
120
134
|
}
|
|
121
135
|
|
|
122
136
|
public func isValidSaplingExtendedSpendingKey(_ extsk: String) -> Bool {
|
|
@@ -155,6 +169,16 @@ extension UnifiedAddress {
|
|
|
155
169
|
}
|
|
156
170
|
}
|
|
157
171
|
|
|
172
|
+
extension TexAddress {
|
|
173
|
+
/// This constructor is for internal use for Strings encodings that are assumed to be
|
|
174
|
+
/// already validated by another function. only for internal use. Unless you are
|
|
175
|
+
/// constructing an address from a primitive function of the FFI, you probably
|
|
176
|
+
/// shouldn't be using this.
|
|
177
|
+
init(validatedEncoding: String) {
|
|
178
|
+
self.encoding = validatedEncoding
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
158
182
|
extension UnifiedFullViewingKey {
|
|
159
183
|
/// This constructor is for internal use for Strings encodings that are assumed to be
|
|
160
184
|
/// already validated by another function. only for internal use. Unless you are
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
//
|
|
2
|
+
// TorRuntime.swift
|
|
3
|
+
//
|
|
4
|
+
//
|
|
5
|
+
// Created by Jack Grigg on 04/06/2024.
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
import Foundation
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
public class TorClient {
|
|
12
|
+
private let runtime: OpaquePointer
|
|
13
|
+
public var cachedFiatCurrencyResult: FiatCurrencyResult?
|
|
14
|
+
|
|
15
|
+
init(torDir: URL) async throws {
|
|
16
|
+
// Ensure that the directory exists.
|
|
17
|
+
let fileManager = FileManager()
|
|
18
|
+
if !fileManager.fileExists(atPath: torDir.path) {
|
|
19
|
+
do {
|
|
20
|
+
try fileManager.createDirectory(at: torDir, withIntermediateDirectories: true)
|
|
21
|
+
} catch {
|
|
22
|
+
throw ZcashError.blockRepositoryCreateBlocksCacheDirectory(torDir, error)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let rawDir = torDir.osPathStr()
|
|
27
|
+
let runtimePtr = zcashlc_create_tor_runtime(rawDir.0, rawDir.1)
|
|
28
|
+
|
|
29
|
+
guard let runtimePtr else {
|
|
30
|
+
throw ZcashError.rustTorClientInit(lastErrorMessage(fallback: "`TorClient` init failed with unknown error"))
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
runtime = runtimePtr
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
deinit {
|
|
37
|
+
zcashlc_free_tor_runtime(runtime)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public func getExchangeRateUSD() async throws -> FiatCurrencyResult {
|
|
41
|
+
let rate = zcashlc_get_exchange_rate_usd(runtime)
|
|
42
|
+
|
|
43
|
+
if rate.is_sign_negative {
|
|
44
|
+
throw ZcashError.rustTorClientGet(lastErrorMessage(fallback: "`TorClient.get` failed with unknown error"))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
let newValue = FiatCurrencyResult(
|
|
48
|
+
date: Date(),
|
|
49
|
+
rate: NSDecimalNumber(mantissa: rate.mantissa, exponent: rate.exponent, isNegative: rate.is_sign_negative),
|
|
50
|
+
state: .success
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
cachedFiatCurrencyResult = newValue
|
|
54
|
+
|
|
55
|
+
return newValue
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -11,6 +11,7 @@ import Foundation
|
|
|
11
11
|
Represents what's expected from a logging entity
|
|
12
12
|
*/
|
|
13
13
|
public protocol Logger {
|
|
14
|
+
func maxLogLevel() -> OSLogger.LogLevel?
|
|
14
15
|
func debug(_ message: String, file: StaticString, function: StaticString, line: Int)
|
|
15
16
|
func info(_ message: String, file: StaticString, function: StaticString, line: Int)
|
|
16
17
|
func event(_ message: String, file: StaticString, function: StaticString, line: Int)
|
|
@@ -44,6 +45,9 @@ extension Logger {
|
|
|
44
45
|
A concrete logger implementation that logs nothing at all
|
|
45
46
|
*/
|
|
46
47
|
struct NullLogger: Logger {
|
|
48
|
+
func maxLogLevel() -> OSLogger.LogLevel? {
|
|
49
|
+
nil
|
|
50
|
+
}
|
|
47
51
|
func debug(_ message: String, file: StaticString, function: StaticString, line: Int) {}
|
|
48
52
|
func info(_ message: String, file: StaticString, function: StaticString, line: Int) {}
|
|
49
53
|
func event(_ message: String, file: StaticString, function: StaticString, line: Int) {}
|
|
Binary file
|
|
Binary file
|