Clear SwiftLint on ZcashLightClientKit

This commit is contained in:
Francisco Gindre 2021-09-15 09:21:29 -03:00
parent 185cbb4b91
commit b1a0b01673
56 changed files with 1096 additions and 774 deletions

View File

@ -5,6 +5,7 @@
// Created by Francisco Gindre on 06/09/2019. // Created by Francisco Gindre on 06/09/2019.
// Copyright © 2019 Electric Coin Company. All rights reserved. // Copyright © 2019 Electric Coin Company. All rights reserved.
// //
// swiftlint:disable all
import UIKit import UIKit
import ZcashLightClientKit import ZcashLightClientKit
@ -33,14 +34,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
if let wallet = wallet { if let wallet = wallet {
return wallet return wallet
} else { } else {
let unifiedViewingKeys = try! DerivationTool(networkType: ZCASH_NETWORK.networkType).deriveUnifiedViewingKeysFromSeed(DemoAppConfig.seed, numberOfAccounts: 1) let unifiedViewingKeys = try! DerivationTool(networkType: kZcashNetwork.networkType).deriveUnifiedViewingKeysFromSeed(DemoAppConfig.seed, numberOfAccounts: 1)
let wallet = Initializer(cacheDbURL:try! __cacheDbURL(), let wallet = Initializer(cacheDbURL:try! cacheDbURLHelper(),
dataDbURL: try! __dataDbURL(), dataDbURL: try! dataDbURLHelper(),
pendingDbURL: try! __pendingDbURL(), pendingDbURL: try! pendingDbURLHelper(),
endpoint: DemoAppConfig.endpoint, endpoint: DemoAppConfig.endpoint,
network: ZCASH_NETWORK, network: kZcashNetwork,
spendParamsURL: try! __spendParamsURL(), spendParamsURL: try! spendParamsURLHelper(),
outputParamsURL: try! __outputParamsURL(), outputParamsURL: try! outputParamsURLHelper(),
viewingKeys: unifiedViewingKeys, viewingKeys: unifiedViewingKeys,
walletBirthday: DemoAppConfig.birthdayHeight, walletBirthday: DemoAppConfig.birthdayHeight,
loggerProxy: loggerProxy) loggerProxy: loggerProxy)
@ -49,7 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
try! wallet.initialize() try! wallet.initialize()
var storage = SampleStorage.shared var storage = SampleStorage.shared
storage!.seed = Data(DemoAppConfig.seed) storage!.seed = Data(DemoAppConfig.seed)
storage!.privateKey = try! DerivationTool(networkType: ZCASH_NETWORK.networkType).deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1)[0] storage!.privateKey = try! DerivationTool(networkType: kZcashNetwork.networkType).deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1)[0]
self.wallet = wallet self.wallet = wallet
return wallet return wallet
} }
@ -65,7 +66,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
return return
} }
NotificationBubble.display(in: window!.rootViewController!.view, options: NotificationBubble.sucessOptions(animation: NotificationBubble.Animation.fade(duration: 1)), attributedText: NSAttributedString(string: "Transaction \(String(describing: tx.id))mined!")) {} NotificationBubble.display(in: window!.rootViewController!.view, options: NotificationBubble.sucessOptions(animation: NotificationBubble.Animation.fade(duration: 1)), attributedText: NSAttributedString(string: "Transaction \(String(describing: tx.id))mined!")) {}
} }
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
@ -112,19 +112,19 @@ extension AppDelegate {
func clearDatabases() { func clearDatabases() {
do { do {
try FileManager.default.removeItem(at: try __cacheDbURL()) try FileManager.default.removeItem(at: try cacheDbURLHelper())
} catch { } catch {
loggerProxy.error("error clearing cache DB: \(error)") loggerProxy.error("error clearing cache DB: \(error)")
} }
do { do {
try FileManager.default.removeItem(at: try __dataDbURL()) try FileManager.default.removeItem(at: try dataDbURLHelper())
} catch { } catch {
loggerProxy.error("error clearing data db: \(error)") loggerProxy.error("error clearing data db: \(error)")
} }
do { do {
try FileManager.default.removeItem(at: try __pendingDbURL()) try FileManager.default.removeItem(at: try pendingDbURLHelper())
} catch { } catch {
loggerProxy.error("error clearing data db: \(error)") loggerProxy.error("error clearing data db: \(error)")
} }
@ -148,38 +148,38 @@ extension Synchronizer {
} }
} }
func __documentsDirectory() throws -> URL { func documentsDirectoryHelper() throws -> URL {
try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
} }
func __cacheDbURL() throws -> URL { func cacheDbURLHelper() throws -> URL {
try __documentsDirectory().appendingPathComponent(ZCASH_NETWORK.constants.DEFAULT_DB_NAME_PREFIX+ZcashSDK.DEFAULT_CACHES_DB_NAME, isDirectory: false) try documentsDirectoryHelper().appendingPathComponent(kZcashNetwork.constants.defaultDbNamePrefix+ZcashSDK.defaultCacheDbName, isDirectory: false)
} }
func __dataDbURL() throws -> URL { func dataDbURLHelper() throws -> URL {
try __documentsDirectory().appendingPathComponent(ZCASH_NETWORK.constants.DEFAULT_DB_NAME_PREFIX+ZcashSDK.DEFAULT_DATA_DB_NAME, isDirectory: false) try documentsDirectoryHelper().appendingPathComponent(kZcashNetwork.constants.defaultDbNamePrefix+ZcashSDK.defaultDataDbName, isDirectory: false)
} }
func __pendingDbURL() throws -> URL { func pendingDbURLHelper() throws -> URL {
try __documentsDirectory().appendingPathComponent(ZCASH_NETWORK.constants.DEFAULT_DB_NAME_PREFIX+ZcashSDK.DEFAULT_PENDING_DB_NAME) try documentsDirectoryHelper().appendingPathComponent(kZcashNetwork.constants.defaultDbNamePrefix+ZcashSDK.defaultPendingDbName)
} }
func __spendParamsURL() throws -> URL { func spendParamsURLHelper() throws -> URL {
try __documentsDirectory().appendingPathComponent("sapling-spend.params") try documentsDirectoryHelper().appendingPathComponent("sapling-spend.params")
} }
func __outputParamsURL() throws -> URL { func outputParamsURLHelper() throws -> URL {
try __documentsDirectory().appendingPathComponent("sapling-output.params") try documentsDirectoryHelper().appendingPathComponent("sapling-output.params")
} }
public extension NotificationBubble { public extension NotificationBubble {
static func sucessOptions(animation: NotificationBubble.Animation) -> [NotificationBubble.Style] { static func sucessOptions(animation: NotificationBubble.Animation) -> [NotificationBubble.Style] {
return [ NotificationBubble.Style.animation(animation), return [
NotificationBubble.Style.animation(animation),
NotificationBubble.Style.margins(UIEdgeInsets(top: 40, left: 0, bottom: 0, right: 0)), NotificationBubble.Style.margins(UIEdgeInsets(top: 40, left: 0, bottom: 0, right: 0)),
NotificationBubble.Style.cornerRadius(8), NotificationBubble.Style.cornerRadius(8),
NotificationBubble.Style.duration(timeInterval: 10), NotificationBubble.Style.duration(timeInterval: 10),
NotificationBubble.Style.backgroundColor(UIColor.green)] NotificationBubble.Style.backgroundColor(UIColor.green)
]
} }
} }

View File

@ -9,4 +9,4 @@
import Foundation import Foundation
import ZcashLightClientKit import ZcashLightClientKit
let ZCASH_NETWORK = ZcashNetworkBuilder.network(for: .mainnet) let kZcashNetwork = ZcashNetworkBuilder.network(for: .mainnet)

View File

@ -9,4 +9,4 @@
import Foundation import Foundation
import ZcashLightClientKit import ZcashLightClientKit
let ZCASH_NETWORK = ZcashNetworkBuilder.network(for: .testnet) let kZcashNetwork = ZcashNetworkBuilder.network(for: .testnet)

View File

@ -9,7 +9,8 @@
import Foundation import Foundation
import ZcashLightClientKit import ZcashLightClientKit
import MnemonicSwift import MnemonicSwift
struct DemoAppConfig { // swiftlint:disable line_length force_try
enum DemoAppConfig {
static var host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co" static var host = ZcashSDK.isMainnet ? "lightwalletd.electriccoin.co" : "lightwalletd.testnet.electriccoin.co"
static var port: Int = 9067 static var port: Int = 9067
static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000 static var birthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
@ -20,7 +21,7 @@ struct DemoAppConfig {
} }
static var processorConfig: CompactBlockProcessor.Configuration = { static var processorConfig: CompactBlockProcessor.Configuration = {
CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), walletBirthday: Self.birthdayHeight, network: ZCASH_NETWORK) CompactBlockProcessor.Configuration(cacheDb: try! cacheDbURLHelper(), dataDb: try! dataDbURLHelper(), walletBirthday: Self.birthdayHeight, network: kZcashNetwork)
}() }()
static var endpoint: LightWalletEndpoint { static var endpoint: LightWalletEndpoint {
@ -31,7 +32,7 @@ struct DemoAppConfig {
extension ZcashSDK { extension ZcashSDK {
static var isMainnet: Bool { static var isMainnet: Bool {
switch ZCASH_NETWORK.networkType { switch kZcashNetwork.networkType {
case .mainnet: case .mainnet:
return true return true
case .testnet: case .testnet:

View File

@ -83,7 +83,7 @@ class DerivationToolViewController: UIViewController {
func deriveFrom(seedPhrase: String) throws { func deriveFrom(seedPhrase: String) throws {
let seedBytes = try Mnemonic.deterministicSeedBytes(from: seedPhrase) let seedBytes = try Mnemonic.deterministicSeedBytes(from: seedPhrase)
let derivationTool = DerivationTool(networkType: ZCASH_NETWORK.networkType) let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
guard let spendingKey = try derivationTool.deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1).first else { guard let spendingKey = try derivationTool.deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1).first else {
throw DerivationErrors.couldNotDeriveSpendingKeys(underlyingError: DerivationErrors.unknown) throw DerivationErrors.couldNotDeriveSpendingKeys(underlyingError: DerivationErrors.unknown)
} }

View File

@ -15,7 +15,7 @@ class GetAddressViewController: UIViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
let derivationTool = DerivationTool(networkType: ZCASH_NETWORK.networkType) let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
zAddressLabel.text = (try? derivationTool.deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found" zAddressLabel.text = (try? derivationTool.deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found"
@ -60,7 +60,7 @@ class GetAddressViewController: UIViewController {
@IBAction func addressTapped(_ gesture: UIGestureRecognizer) { @IBAction func addressTapped(_ gesture: UIGestureRecognizer) {
loggerProxy.event("copied to clipboard") loggerProxy.event("copied to clipboard")
UIPasteboard.general.string = try? DerivationTool(networkType: ZCASH_NETWORK.networkType).deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0) UIPasteboard.general.string = try? DerivationTool(networkType: kZcashNetwork.networkType).deriveShieldedAddress(seed: DemoAppConfig.seed, accountIndex: 0)
let alert = UIAlertController(title: "", message: "Address Copied to clipboard", preferredStyle: UIAlertController.Style.alert) let alert = UIAlertController(title: "", message: "Address Copied to clipboard", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)
@ -68,7 +68,7 @@ class GetAddressViewController: UIViewController {
@IBAction func tAddressTapped(_ gesture: UIGestureRecognizer) { @IBAction func tAddressTapped(_ gesture: UIGestureRecognizer) {
loggerProxy.event("copied to clipboard") loggerProxy.event("copied to clipboard")
UIPasteboard.general.string = try? DerivationTool(networkType: ZCASH_NETWORK.networkType).deriveTransparentAddress(seed: DemoAppConfig.seed) UIPasteboard.general.string = try? DerivationTool(networkType: kZcashNetwork.networkType).deriveTransparentAddress(seed: DemoAppConfig.seed)
let alert = UIAlertController(title: "", message: "Address Copied to clipboard", preferredStyle: UIAlertController.Style.alert) let alert = UIAlertController(title: "", message: "Address Copied to clipboard", preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil)) alert.addAction(UIAlertAction(title: "OK", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil) self.present(alert, animated: true, completion: nil)

View File

@ -35,12 +35,12 @@ class GetBalanceViewController: UIViewController {
extension Int64 { extension Int64 {
func asHumanReadableZecBalance() -> Double { func asHumanReadableZecBalance() -> Double {
Double(self) / Double(ZcashSDK.ZATOSHI_PER_ZEC) Double(self) / Double(ZcashSDK.zatoshiPerZEC)
} }
} }
extension Double { extension Double {
func toZatoshi() -> Int64 { func toZatoshi() -> Int64 {
Int64(self * Double(ZcashSDK.ZATOSHI_PER_ZEC)) Int64(self * Double(ZcashSDK.zatoshiPerZEC))
} }
} }

View File

@ -26,7 +26,7 @@ class GetUTXOsViewController: UIViewController {
} }
func updateUI() { func updateUI() {
let tAddress = try! DerivationTool(networkType: ZCASH_NETWORK.networkType).deriveTransparentAddress(seed: DemoAppConfig.seed) let tAddress = try! DerivationTool(networkType: kZcashNetwork.networkType).deriveTransparentAddress(seed: DemoAppConfig.seed)
self.transparentAddressLabel.text = tAddress self.transparentAddressLabel.text = tAddress
let balance = try! AppDelegate.shared.sharedSynchronizer.getTransparentBalance(accountIndex: 0) let balance = try! AppDelegate.shared.sharedSynchronizer.getTransparentBalance(accountIndex: 0)
@ -38,7 +38,7 @@ class GetUTXOsViewController: UIViewController {
@IBAction func shieldFunds(_ sender: Any) { @IBAction func shieldFunds(_ sender: Any) {
do { do {
let seed = DemoAppConfig.seed let seed = DemoAppConfig.seed
let derivationTool = DerivationTool(networkType: ZCASH_NETWORK.networkType) let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
let sk = try derivationTool.deriveSpendingKeys(seed: seed, numberOfAccounts: 1).first! let sk = try derivationTool.deriveSpendingKeys(seed: seed, numberOfAccounts: 1).first!
let tsk = try derivationTool.deriveTransparentPrivateKey(seed: seed) let tsk = try derivationTool.deriveTransparentPrivateKey(seed: seed)

View File

@ -5,12 +5,11 @@
// Created by Francisco Gindre on 12/20/19. // Created by Francisco Gindre on 12/20/19.
// Copyright © 2019 Electric Coin Company. All rights reserved. // Copyright © 2019 Electric Coin Company. All rights reserved.
// //
// swiftlint:disable line_length force_try
import Foundation import Foundation
protocol WalletStorage { protocol WalletStorage {
var seed: Data? {get set} var seed: Data? { get set }
var privateKey: String? { get set } var privateKey: String? { get set }
} }
@ -25,24 +24,23 @@ class SampleStorage: WalletStorage {
static var shared: WalletStorage! { static var shared: WalletStorage! {
_shared _shared
} }
private let KEY_SEED = "cash.z.wallet.sdk.demoapp.SEED" private let keySeed = "cash.z.wallet.sdk.demoapp.SEED"
private let KEY_PK = "cash.z.wallet.sdk.demoapp.PK" private let keyPK = "cash.z.wallet.sdk.demoapp.PK"
var seed: Data? { var seed: Data? {
set { set {
UserDefaults.standard.set(newValue, forKey: KEY_SEED) UserDefaults.standard.set(newValue, forKey: keySeed)
} }
get { get {
UserDefaults.standard.value(forKey: KEY_SEED) as! Data? UserDefaults.standard.value(forKey: keySeed) as! Data?
} }
} }
var privateKey: String? { var privateKey: String? {
set { set {
UserDefaults.standard.set(newValue, forKey: KEY_PK) UserDefaults.standard.set(newValue, forKey: keyPK)
} }
get { get {
UserDefaults.standard.value(forKey: KEY_PK) as! String? UserDefaults.standard.value(forKey: keyPK) as! String?
} }
} }
} }

View File

@ -15,8 +15,8 @@ class SaplingParametersViewController: UIViewController {
@IBOutlet weak var deleteButton: UIButton! @IBOutlet weak var deleteButton: UIButton!
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
let spendParamPath = try! __spendParamsURL().path let spendParamPath = try! spendParamsURLHelper().path
let outputParamPath = try! __outputParamsURL().path let outputParamPath = try! outputParamsURLHelper().path
// Do any additional setup after loading the view. // Do any additional setup after loading the view.
self.spendPath.text = spendParamPath self.spendPath.text = spendParamPath
self.outputPath.text = outputParamPath self.outputPath.text = outputParamPath
@ -29,14 +29,14 @@ class SaplingParametersViewController: UIViewController {
self.updateButtons() self.updateButtons()
} }
func updateButtons() { func updateButtons() {
let spendParamPath = try! __spendParamsURL().path let spendParamPath = try! spendParamsURLHelper().path
let outputParamPath = try! __outputParamsURL().path let outputParamPath = try! outputParamsURLHelper().path
self.downloadButton.isHidden = fileExists(outputParamPath) && fileExists(spendParamPath) self.downloadButton.isHidden = fileExists(outputParamPath) && fileExists(spendParamPath)
self.deleteButton.isHidden = !(fileExists(outputParamPath) || fileExists(spendParamPath)) self.deleteButton.isHidden = !(fileExists(outputParamPath) || fileExists(spendParamPath))
} }
func updateColor() { func updateColor() {
let spendParamPath = try! __spendParamsURL().path let spendParamPath = try! spendParamsURLHelper().path
let outputParamPath = try! __outputParamsURL().path let outputParamPath = try! outputParamsURLHelper().path
self.spendPath.textColor = fileExists(spendParamPath) ? UIColor.green : UIColor.red self.spendPath.textColor = fileExists(spendParamPath) ? UIColor.green : UIColor.red
self.outputPath.textColor = fileExists(outputParamPath) ? UIColor.green : UIColor.red self.outputPath.textColor = fileExists(outputParamPath) ? UIColor.green : UIColor.red
} }
@ -57,8 +57,8 @@ class SaplingParametersViewController: UIViewController {
} }
@IBAction func download(_ sender: Any) { @IBAction func download(_ sender: Any) {
let outputParameter = try! __outputParamsURL() let outputParameter = try! outputParamsURLHelper()
let spendParameter = try! __spendParamsURL() let spendParameter = try! spendParamsURLHelper()
SaplingParameterDownloader.downloadParamsIfnotPresent(spendURL: spendParameter, outputURL: outputParameter) { (result) in SaplingParameterDownloader.downloadParamsIfnotPresent(spendURL: spendParameter, outputURL: outputParameter) { (result) in
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -87,8 +87,8 @@ class SaplingParametersViewController: UIViewController {
} }
@IBAction func deleteFiles(_ sender: Any) { @IBAction func deleteFiles(_ sender: Any) {
let spendParamURL = try! __spendParamsURL() let spendParamURL = try! spendParamsURLHelper()
let outputParamURL = try! __outputParamsURL() let outputParamURL = try! outputParamsURLHelper()
try? FileManager.default.removeItem(at: spendParamURL) try? FileManager.default.removeItem(at: spendParamURL)
try? FileManager.default.removeItem(at: outputParamURL) try? FileManager.default.removeItem(at: outputParamURL)

View File

@ -173,8 +173,13 @@ class SendViewController: UIViewController {
KRProgressHUD.show() KRProgressHUD.show()
synchronizer.sendToAddress(spendingKey: address, zatoshi: zec, toAddress: recipient, memo: self.memoField.text.count > 0 ? self.memoField.text : nil, from: 0) { [weak self] result in synchronizer.sendToAddress(
spendingKey: address,
zatoshi: zec,
toAddress: recipient,
memo: !self.memoField.text.isEmpty ? self.memoField.text : nil,
from: 0
) { [weak self] result in
DispatchQueue.main.async { DispatchQueue.main.async {
KRProgressHUD.dismiss() KRProgressHUD.dismiss()
} }

View File

@ -37,13 +37,13 @@ class CompactBlockStorage: CompactBlockDAO {
let db = try dbProvider.connection() let db = try dbProvider.connection()
try db.run(compactBlocks.create(ifNotExists: true) { t in try db.run(compactBlocks.create(ifNotExists: true) { table in
t.column(height, primaryKey: true) table.column(height, primaryKey: true)
t.column(data) table.column(data)
} ) }
)
try db.run(compactBlocks.createIndex(height, ifNotExists: true)) try db.run(compactBlocks.createIndex(height, ifNotExists: true))
} catch { } catch {
throw StorageError.couldNotCreate throw StorageError.couldNotCreate
} }
@ -64,7 +64,6 @@ class CompactBlockStorage: CompactBlockDAO {
} }
func latestBlockHeight() throws -> BlockHeight { func latestBlockHeight() throws -> BlockHeight {
guard let maxHeight = try dbProvider.connection().scalar(compactBlocksTable().select(heightColumn().max)) else { guard let maxHeight = try dbProvider.connection().scalar(compactBlocksTable().select(heightColumn().max)) else {
return BlockHeight.empty() return BlockHeight.empty()
} }
@ -82,7 +81,6 @@ class CompactBlockStorage: CompactBlockDAO {
} }
extension CompactBlockStorage: CompactBlockRepository { extension CompactBlockStorage: CompactBlockRepository {
func latestHeight() throws -> BlockHeight { func latestHeight() throws -> BlockHeight {
try latestBlockHeight() try latestBlockHeight()
} }
@ -117,11 +115,9 @@ extension CompactBlockStorage: CompactBlockRepository {
do { do {
try self.rewind(to: height) try self.rewind(to: height)
completion?(nil) completion?(nil)
} catch { } catch {
completion?(error) completion?(error)
} }
} }
} }
} }

View File

@ -59,7 +59,7 @@ class MigrationManager {
} }
} }
fileprivate func migrateCacheDb() throws { private func migrateCacheDb() throws {
let currentCacheDbVersion = try cacheDb.connection().getUserVersion() let currentCacheDbVersion = try cacheDb.connection().getUserVersion()
LoggerProxy.debug("Attempting to perform migration for cache Db - currentVersion: \(currentCacheDbVersion). Latest version is: \(Self.latestCacheDbMigrationVersion)") LoggerProxy.debug("Attempting to perform migration for cache Db - currentVersion: \(currentCacheDbVersion). Latest version is: \(Self.latestCacheDbMigrationVersion)")
@ -72,15 +72,17 @@ class MigrationManager {
} }
} }
fileprivate func migrateDataDb(uvks: [UnifiedViewingKey]) throws { private func migrateDataDb(uvks: [UnifiedViewingKey]) throws {
let currentDataDbVersion = try dataDb.connection().getUserVersion() let currentDataDbVersion = try dataDb.connection().getUserVersion()
LoggerProxy.debug("Attempting to perform migration for data Db - currentVersion: \(currentDataDbVersion). Latest version is: \(Self.latestDataDbMigrationVersion)") LoggerProxy.debug(
"Attempting to perform migration for data Db - currentVersion: \(currentDataDbVersion). Latest version is: \(Self.latestDataDbMigrationVersion)" // swiftlint:disable line_length
)
if currentDataDbVersion < Self.latestDataDbMigrationVersion { if currentDataDbVersion < Self.latestDataDbMigrationVersion {
for v in (currentDataDbVersion + 1) ... Self.latestDataDbMigrationVersion { for version in (currentDataDbVersion + 1) ... Self.latestDataDbMigrationVersion {
guard let version = DataDbMigrations.init(rawValue: v) else { guard let version = DataDbMigrations.init(rawValue: version) else {
LoggerProxy.error("failed to determine migration version") LoggerProxy.error("failed to determine migration version")
throw StorageError.invalidMigrationVersion(version: v) throw StorageError.invalidMigrationVersion(version: version)
} }
switch version { switch version {
case .version1: case .version1:
@ -95,7 +97,9 @@ class MigrationManager {
} }
func performVersion1Migration(viewingKeys: [UnifiedViewingKey]) throws { func performVersion1Migration(viewingKeys: [UnifiedViewingKey]) throws {
LoggerProxy.debug("Starting migration version 1 from viewing Keys") LoggerProxy.debug(
"Starting migration version 1 from viewing Keys"
)
let db = try self.dataDb.connection() let db = try self.dataDb.connection()
let placeholder = "deriveMe" let placeholder = "deriveMe"
@ -147,7 +151,10 @@ class MigrationManager {
} }
guard accounts.count == viewingKeys.count else { guard accounts.count == viewingKeys.count else {
let message = "Number of accounts found and viewing keys provided don't match. Found \(accounts.count) account(s) and there were \(viewingKeys.count) Viewing key(s) provided." let message = """
Number of accounts found and viewing keys provided don't match.
Found \(accounts.count) account(s) and there were \(viewingKeys.count) Viewing key(s) provided.
"""
LoggerProxy.debug(message) LoggerProxy.debug(message)
throw StorageError.migrationFailedWithMessage(message: message) throw StorageError.migrationFailedWithMessage(message: message)
} }
@ -189,10 +196,10 @@ class MigrationManager {
extension Connection { extension Connection {
func getUserVersion() throws -> Int32 { func getUserVersion() throws -> Int32 {
guard let v = try scalar("PRAGMA user_version") as? Int64 else { guard let version = try scalar("PRAGMA user_version") as? Int64 else {
return 0 return 0
} }
return Int32(v) return Int32(version)
} }
func setUserVersion(_ version: Int32) throws { func setUserVersion(_ version: Int32) throws {

View File

@ -55,7 +55,6 @@ private extension Connection {
} }
class SimpleConnectionProvider: ConnectionProvider { class SimpleConnectionProvider: ConnectionProvider {
var path: String var path: String
var readonly: Bool var readonly: Bool
var db: Connection? var db: Connection?
@ -66,12 +65,11 @@ class SimpleConnectionProvider: ConnectionProvider {
} }
func connection() throws -> Connection { func connection() throws -> Connection {
guard let c = db else { guard let conn = db else {
let c = try Connection(path, readonly: readonly) let conn = try Connection(path, readonly: readonly)
self.db = c self.db = conn
return c return conn
} }
return c return conn
} }
} }

View File

@ -196,14 +196,12 @@ extension CompactBlockDownloader: CompactBlockDownloading {
} }
func rewind(to height: BlockHeight, completion: @escaping (Error?) -> Void) { func rewind(to height: BlockHeight, completion: @escaping (Error?) -> Void) {
storage.rewind(to: height) { (e) in storage.rewind(to: height) { (e) in
completion(e) completion(e)
} }
} }
func lastDownloadedBlockHeight(result: @escaping (Result<BlockHeight,Error>) -> Void) { func lastDownloadedBlockHeight(result: @escaping (Result<BlockHeight,Error>) -> Void) {
storage.latestHeight { (r) in storage.latestHeight { (r) in
switch r { switch r {
case .failure(let e): case .failure(let e):
@ -223,12 +221,12 @@ extension CompactBlockDownloader: CompactBlockDownloading {
try self.storage.latestHeight() try self.storage.latestHeight()
} }
func fetchTransaction(txId: Data) throws -> TransactionEntity{ func fetchTransaction(txId: Data) throws -> TransactionEntity {
try lightwalletService.fetchTransaction(txId: txId) try lightwalletService.fetchTransaction(txId: txId)
} }
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, Error>) -> Void) { func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, Error>) -> Void) {
lightwalletService.fetchTransaction(txId: txId) { (txResult) in lightwalletService.fetchTransaction(txId: txId) { txResult in
switch txResult { switch txResult {
case .failure(let error): case .failure(let error):
result(.failure(error)) result(.failure(error))

View File

@ -81,7 +81,6 @@ class CompactBlockStreamDownloadOperation: ZcashOperation {
} }
self.startedHandler?() self.startedHandler?()
do { do {
if self.targetHeight == nil { if self.targetHeight == nil {
self.targetHeight = try service.latestBlockHeight() self.targetHeight = try service.latestBlockHeight()
} }
@ -93,7 +92,6 @@ class CompactBlockStreamDownloadOperation: ZcashOperation {
self.cancelable = self.service.blockStream(startHeight: startHeight, endHeight: latestHeight) { [weak self] result in self.cancelable = self.service.blockStream(startHeight: startHeight, endHeight: latestHeight) { [weak self] result in
switch result { switch result {
case .success(let r): case .success(let r):
switch r { switch r {
case .ok: case .ok:
@ -109,7 +107,6 @@ class CompactBlockStreamDownloadOperation: ZcashOperation {
self?.fail(error: e) self?.fail(error: e)
} }
} }
} handler: {[weak self] block in } handler: {[weak self] block in
guard let self = self else { return } guard let self = self else { return }
do { do {
@ -156,14 +153,15 @@ class CompactBlockBatchDownloadOperation: ZcashOperation {
private var startHeight: BlockHeight private var startHeight: BlockHeight
private var targetHeight: BlockHeight private var targetHeight: BlockHeight
private weak var progressDelegate: CompactBlockProgressDelegate? private weak var progressDelegate: CompactBlockProgressDelegate?
required init(service: LightWalletService, required init(
storage: CompactBlockStorage, service: LightWalletService,
startHeight: BlockHeight, storage: CompactBlockStorage,
targetHeight: BlockHeight, startHeight: BlockHeight,
batchSize: Int = 100, targetHeight: BlockHeight,
maxRetries: Int = 5, batchSize: Int = 100,
progressDelegate: CompactBlockProgressDelegate? = nil) { maxRetries: Int = 5,
progressDelegate: CompactBlockProgressDelegate? = nil
) {
self.storage = storage self.storage = storage
self.service = service self.service = service
self.startHeight = startHeight self.startHeight = startHeight
@ -182,7 +180,6 @@ class CompactBlockBatchDownloadOperation: ZcashOperation {
} }
self.startedHandler?() self.startedHandler?()
do { do {
let localDownloadedHeight = try self.storage.latestHeight() let localDownloadedHeight = try self.storage.latestHeight()
if localDownloadedHeight != BlockHeight.empty() && localDownloadedHeight > startHeight { if localDownloadedHeight != BlockHeight.empty() && localDownloadedHeight > startHeight {
@ -196,7 +193,7 @@ class CompactBlockBatchDownloadOperation: ZcashOperation {
while !isCancelled && currentHeight <= targetHeight { while !isCancelled && currentHeight <= targetHeight {
var retries = 0 var retries = 0
var success = true var success = true
var localError: Error? = nil var localError: Error?
let range = nextRange(currentHeight: currentHeight, targetHeight: targetHeight) let range = nextRange(currentHeight: currentHeight, targetHeight: targetHeight)
@ -205,18 +202,25 @@ class CompactBlockBatchDownloadOperation: ZcashOperation {
let blocks = try service.blockRange(range) let blocks = try service.blockRange(range)
try storage.insert(blocks) try storage.insert(blocks)
success = true success = true
} catch { } catch {
success = false success = false
localError = error localError = error
retries = retries + 1 retries += 1
} }
} while !isCancelled && !success && retries < maxRetries } while !isCancelled && !success && retries < maxRetries
if retries >= maxRetries { if retries >= maxRetries {
throw CompactBlockBatchDownloadOperationError.batchDownloadFailed(range: range, error: localError) throw CompactBlockBatchDownloadOperationError.batchDownloadFailed(range: range, error: localError)
} }
self.progressDelegate?.progressUpdated(.download(BlockProgress(startHeight: startHeight, targetHeight: targetHeight, progressHeight: range.upperBound))) self.progressDelegate?.progressUpdated(
.download(
BlockProgress(
startHeight: startHeight,
targetHeight: targetHeight,
progressHeight: range.upperBound
)
)
)
currentHeight = range.upperBound + 1 currentHeight = range.upperBound + 1
} }
} catch { } catch {

View File

@ -56,25 +56,27 @@ class CompactBlockEnhancementOperation: ZcashOperation {
// fetch transactions // fetch transactions
do { do {
guard let transactions = try repository.findTransactions(in: self.range, limit: Int.max), transactions.count > 0 else { guard let transactions = try repository.findTransactions(in: self.range, limit: Int.max), !transactions.isEmpty else {
LoggerProxy.debug("no transactions detected on range: \(range.printRange)") LoggerProxy.debug("no transactions detected on range: \(range.printRange)")
return return
} }
for index in 0 ..< transactions.count { for index in 0 ..< transactions.count {
let tx = transactions[index] let transaction = transactions[index]
var retry = true var retry = true
while retry && self.retries < maxRetries { while retry && self.retries < maxRetries {
do { do {
let confirmedTx = try enhance(transaction: tx) let confirmedTx = try enhance(transaction: transaction)
retry = false retry = false
self.reportProgress(totalTransactions: transactions.count, self.reportProgress(
enhanced: index + 1, totalTransactions: transactions.count,
txEnhanced: confirmedTx) enhanced: index + 1,
txEnhanced: confirmedTx
)
} catch { } catch {
self.retries = self.retries + 1 self.retries += 1
LoggerProxy.error("could not enhance txId \(tx.transactionId.toHexStringTxId()) - Error: \(error)") LoggerProxy.error("could not enhance txId \(transaction.transactionId.toHexStringTxId()) - Error: \(error)")
if retries > maxRetries { if retries > maxRetries {
throw error throw error
} }
@ -94,34 +96,42 @@ class CompactBlockEnhancementOperation: ZcashOperation {
} }
func reportProgress(totalTransactions: Int, enhanced: Int, txEnhanced: ConfirmedTransactionEntity) { func reportProgress(totalTransactions: Int, enhanced: Int, txEnhanced: ConfirmedTransactionEntity) {
self.progressDelegate?.progressUpdated(.enhance( self.progressDelegate?.progressUpdated(
EnhancementStreamProgress( .enhance(
totalTransactions: totalTransactions, EnhancementStreamProgress(
enhancedTransactions: enhanced, totalTransactions: totalTransactions,
lastFoundTransaction: txEnhanced, enhancedTransactions: enhanced,
range: self.range.compactBlockRange))) lastFoundTransaction: txEnhanced,
range: self.range.compactBlockRange
)
)
)
} }
func enhance(transaction: TransactionEntity) throws -> ConfirmedTransactionEntity { func enhance(transaction: TransactionEntity) throws -> ConfirmedTransactionEntity {
LoggerProxy.debug("Zoom.... Enhance... Tx: \(transaction.transactionId.toHexStringTxId())") LoggerProxy.debug("Zoom.... Enhance... Tx: \(transaction.transactionId.toHexStringTxId())")
let tx = try downloader.fetchTransaction(txId: transaction.transactionId) let transaction = try downloader.fetchTransaction(txId: transaction.transactionId)
LoggerProxy.debug("Decrypting and storing transaction id: \(tx.transactionId.toHexStringTxId()) block: \(String(describing: tx.minedHeight))") LoggerProxy.debug("Decrypting and storing transaction id: \(transaction.transactionId.toHexStringTxId()) block: \(String(describing: transaction.minedHeight))")
guard let rawBytes = tx.raw?.bytes else { guard let rawBytes = transaction.raw?.bytes else {
let error = EnhancementError.noRawData(message: "Critical Error: transaction id: \(tx.transactionId.toHexStringTxId()) has no data") let error = EnhancementError.noRawData(
message: "Critical Error: transaction id: \(transaction.transactionId.toHexStringTxId()) has no data"
)
LoggerProxy.error("\(error)") LoggerProxy.error("\(error)")
throw error throw error
} }
guard let minedHeight = tx.minedHeight else { guard let minedHeight = transaction.minedHeight else {
let error = EnhancementError.noRawData(message: "Critical Error - Attempt to decrypt and store an unmined transaction. Id: \(tx.transactionId.toHexStringTxId()) ") let error = EnhancementError.noRawData(
message: "Critical Error - Attempt to decrypt and store an unmined transaction. Id: \(transaction.transactionId.toHexStringTxId())"
)
LoggerProxy.error("\(error)") LoggerProxy.error("\(error)")
throw error throw error
} }
guard rustBackend.decryptAndStoreTransaction(dbData: dataDb, tx: rawBytes, minedHeight: Int32(minedHeight), networkType: network) else { guard rustBackend.decryptAndStoreTransaction(dbData: dataDb, txBytes: rawBytes, minedHeight: Int32(minedHeight), networkType: network) else {
if let rustError = rustBackend.lastError() { if let rustError = rustBackend.lastError() {
throw EnhancementError.decryptError(error: rustError) throw EnhancementError.decryptError(error: rustError)
} }

View File

@ -5,6 +5,7 @@
// Created by Francisco Gindre on 18/09/2019. // Created by Francisco Gindre on 18/09/2019.
// Copyright © 2019 Electric Coin Company. All rights reserved. // Copyright © 2019 Electric Coin Company. All rights reserved.
// //
// swiftlint:disable file_length type_body_length
import Foundation import Foundation
import GRPC import GRPC
@ -34,7 +35,7 @@ public enum CompactBlockProcessorError: Error {
CompactBlockProcessor notification userInfo object keys. CompactBlockProcessor notification userInfo object keys.
check Notification.Name extensions for more details. check Notification.Name extensions for more details.
*/ */
public struct CompactBlockProcessorNotificationKey { public enum CompactBlockProcessorNotificationKey {
public static let progress = "CompactBlockProcessorNotificationKey.progress" public static let progress = "CompactBlockProcessorNotificationKey.progress"
// public static let progressStartHeight = "CompactBlockProcessorNotificationKey.progressStartHeight" // public static let progressStartHeight = "CompactBlockProcessorNotificationKey.progressStartHeight"
// public static let progressTargetHeight = "CompactBlockProcessorNotificationKey.progressTargetHeight" // public static let progressTargetHeight = "CompactBlockProcessorNotificationKey.progressTargetHeight"
@ -63,11 +64,11 @@ public enum CompactBlockProgress {
public var progress: Float { public var progress: Float {
switch self { switch self {
case .download(let p), case .download(let blockProgress),
.scan(let p): .scan(let blockProgress):
return p.progress return blockProgress.progress
case .enhance(let p): case .enhance(let enhancementProgress):
return p.progress return enhancementProgress.progress
default: default:
return 0 return 0
} }
@ -75,18 +76,19 @@ public enum CompactBlockProgress {
public var progressHeight: BlockHeight? { public var progressHeight: BlockHeight? {
switch self { switch self {
case .download(let p), case .download(let blockProgress),
.scan(let p): .scan(let blockProgress):
return p.progressHeight return blockProgress.progressHeight
case .enhance(let p): case .enhance(let enhancementProgress):
return p.lastFoundTransaction?.minedHeight return enhancementProgress.lastFoundTransaction?.minedHeight
default: default:
return 0 return 0
} }
} }
public var blockDate: Date? { public var blockDate: Date? {
if case .enhance(let p ) = self, let time = p.lastFoundTransaction?.blockTimeInSeconds { if case .enhance(let enhancementProgress) = self,
let time = enhancementProgress.lastFoundTransaction?.blockTimeInSeconds {
return Date(timeIntervalSince1970: time) return Date(timeIntervalSince1970: time)
} }
return nil return nil
@ -94,8 +96,8 @@ public enum CompactBlockProgress {
public var targetHeight: BlockHeight? { public var targetHeight: BlockHeight? {
switch self { switch self {
case .download(let p), .scan(let p): case .download(let blockProgress), .scan(let blockProgress):
return p.targetHeight return blockProgress.targetHeight
default: default:
return nil return nil
} }
@ -201,7 +203,6 @@ public extension Notification.Name {
Notification sent when the grpc service connection detects a change. Query the user info object for status change details `currentConnectivityStatus` for current and previous with `previousConnectivityStatus` Notification sent when the grpc service connection detects a change. Query the user info object for status change details `currentConnectivityStatus` for current and previous with `previousConnectivityStatus`
*/ */
static let blockProcessorConnectivityStateChanged = Notification.Name("CompactBlockProcessorConnectivityStateChanged") static let blockProcessorConnectivityStateChanged = Notification.Name("CompactBlockProcessorConnectivityStateChanged")
} }
/** /**
@ -219,14 +220,14 @@ public class CompactBlockProcessor {
public struct Configuration { public struct Configuration {
public var cacheDb: URL public var cacheDb: URL
public var dataDb: URL public var dataDb: URL
public var downloadBatchSize = ZcashSDK.DEFAULT_BATCH_SIZE public var downloadBatchSize = ZcashSDK.DefaultBatchSize
public var blockPollInterval: TimeInterval { public var blockPollInterval: TimeInterval {
TimeInterval.random(in: ZcashSDK.DEFAULT_POLL_INTERVAL / 2 ... ZcashSDK.DEFAULT_POLL_INTERVAL * 1.5) TimeInterval.random(in: ZcashSDK.defaultPollInterval / 2 ... ZcashSDK.defaultPollInterval * 1.5)
} }
public var retries = ZcashSDK.DEFAULT_RETRIES public var retries = ZcashSDK.defaultRetries
public var maxBackoffInterval = ZcashSDK.DEFAULT_MAX_BACKOFF_INTERVAL public var maxBackoffInterval = ZcashSDK.defaultMaxBackOffInterval
public var rewindDistance = ZcashSDK.DEFAULT_REWIND_DISTANCE public var rewindDistance = ZcashSDK.defaultRewindDistance
public var walletBirthday: BlockHeight public var walletBirthday: BlockHeight
private(set) var network: ZcashNetwork private(set) var network: ZcashNetwork
private(set) var saplingActivation: BlockHeight private(set) var saplingActivation: BlockHeight
@ -257,7 +258,7 @@ public class CompactBlockProcessor {
self.cacheDb = cacheDb self.cacheDb = cacheDb
self.dataDb = dataDb self.dataDb = dataDb
self.walletBirthday = walletBirthday self.walletBirthday = walletBirthday
self.saplingActivation = network.constants.SAPLING_ACTIVATION_HEIGHT self.saplingActivation = network.constants.saplingActivationHeight
self.network = network self.network = network
} }
} }
@ -322,12 +323,12 @@ public class CompactBlockProcessor {
self.stop() self.stop()
} }
} }
private var queue: OperationQueue = { private var operationQueue: OperationQueue = {
let q = OperationQueue() let queue = OperationQueue()
q.name = "CompactBlockProcessorQueue" queue.name = "CompactBlockProcessorQueue"
q.maxConcurrentOperationCount = 1 queue.maxConcurrentOperationCount = 1
return q return queue
} () }()
private var retryAttempts: Int = 0 private var retryAttempts: Int = 0
private var backoffTimer: Timer? private var backoffTimer: Timer?
@ -352,7 +353,7 @@ public class CompactBlockProcessor {
- Throws CompactBlockProcessorError.invalidConfiguration if block height is invalid or if processor is already started - Throws CompactBlockProcessorError.invalidConfiguration if block height is invalid or if processor is already started
*/ */
func setStartHeight(_ startHeight: BlockHeight) throws { func setStartHeight(_ startHeight: BlockHeight) throws {
guard self.state == .stopped, startHeight >= config.network.constants.SAPLING_ACTIVATION_HEIGHT else { guard self.state == .stopped, startHeight >= config.network.constants.saplingActivationHeight else {
throw CompactBlockProcessorError.invalidConfiguration throw CompactBlockProcessorError.invalidConfiguration
} }
@ -367,16 +368,21 @@ public class CompactBlockProcessor {
- downloader: an instance that complies to CompactBlockDownloading protocol - downloader: an instance that complies to CompactBlockDownloading protocol
- backend: a class that complies to ZcashRustBackendWelding - backend: a class that complies to ZcashRustBackendWelding
*/ */
convenience init(service: LightWalletService, convenience init(
storage: CompactBlockStorage, service: LightWalletService,
backend: ZcashRustBackendWelding.Type, storage: CompactBlockStorage,
config: Configuration) { backend: ZcashRustBackendWelding.Type,
self.init(service: service, config: Configuration
storage: storage, ) {
backend: backend, self.init(
config: config, service: service,
repository: TransactionRepositoryBuilder.build(dataDbURL: config.dataDb), storage: storage,
accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true)) backend: backend,
config: config,
repository: TransactionRepositoryBuilder.build(
dataDbURL: config.dataDb
),
accountRepository: AccountRepositoryBuilder.build(dataDbURL: config.dataDb, readOnly: true))
} }
/** /**
@ -385,24 +391,28 @@ public class CompactBlockProcessor {
- initializer: an instance that complies to CompactBlockDownloading protocol - initializer: an instance that complies to CompactBlockDownloading protocol
*/ */
public convenience init(initializer: Initializer) { public convenience init(initializer: Initializer) {
self.init(
self.init(service: initializer.lightWalletService, service: initializer.lightWalletService,
storage: initializer.storage, storage: initializer.storage,
backend: initializer.rustBackend, backend: initializer.rustBackend,
config: Configuration(cacheDb: initializer.cacheDbURL, config: Configuration(
dataDb: initializer.dataDbURL, cacheDb: initializer.cacheDbURL,
walletBirthday: initializer.walletBirthday.height, dataDb: initializer.dataDbURL,
network: initializer.network), walletBirthday: initializer.walletBirthday.height,
repository: initializer.transactionRepository, network: initializer.network
accountRepository: initializer.accountRepository) ),
repository: initializer.transactionRepository,
accountRepository: initializer.accountRepository)
} }
internal init(service: LightWalletService, internal init(
storage: CompactBlockStorage, service: LightWalletService,
backend: ZcashRustBackendWelding.Type, storage: CompactBlockStorage,
config: Configuration, backend: ZcashRustBackendWelding.Type,
repository: TransactionRepository, config: Configuration,
accountRepository: AccountRepository) { repository: TransactionRepository,
accountRepository: AccountRepository
) {
self.service = service self.service = service
self.downloader = CompactBlockDownloader(service: service, storage: storage) self.downloader = CompactBlockDownloader(service: service, storage: storage)
self.rustBackend = backend self.rustBackend = backend
@ -414,7 +424,7 @@ public class CompactBlockProcessor {
} }
deinit { deinit {
self.queue.cancelAllOperations() self.operationQueue.cancelAllOperations()
} }
var maxAttemptsReached: Bool { var maxAttemptsReached: Bool {
@ -447,7 +457,6 @@ public class CompactBlockProcessor {
*/ */
public func start(retry: Bool = false) throws { public func start(retry: Bool = false) throws {
// TODO: check if this validation makes sense at all // TODO: check if this validation makes sense at all
// try validateConfiguration() // try validateConfiguration()
if retry { if retry {
@ -456,9 +465,9 @@ public class CompactBlockProcessor {
self.backoffTimer?.invalidate() self.backoffTimer?.invalidate()
self.backoffTimer = nil self.backoffTimer = nil
} }
guard !queue.isSuspended else { guard !operationQueue.isSuspended else {
LoggerProxy.debug("restarting suspended queue") LoggerProxy.debug("restarting suspended queue")
queue.isSuspended = false operationQueue.isSuspended = false
return return
} }
@ -487,7 +496,6 @@ public class CompactBlockProcessor {
} }
func validateServer(completionBlock: @escaping (() -> Void)) { func validateServer(completionBlock: @escaping (() -> Void)) {
self.service.getInfo(result: { [weak self] result in self.service.getInfo(result: { [weak self] result in
guard let self = self else { return } guard let self = self else { return }
@ -496,10 +504,12 @@ public class CompactBlockProcessor {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
do { do {
try Self.validateServerInfo(info, try Self.validateServerInfo(
saplingActivation: self.config.saplingActivation, info,
localNetwork: self.config.network, saplingActivation: self.config.saplingActivation,
rustBackend: self.rustBackend) localNetwork: self.config.network,
rustBackend: self.rustBackend
)
completionBlock() completionBlock()
} catch { } catch {
self.severeFailure(error) self.severeFailure(error)
@ -511,14 +521,18 @@ public class CompactBlockProcessor {
}) })
} }
static func validateServerInfo(_ info: LightWalletdInfo, static func validateServerInfo(
saplingActivation: BlockHeight, _ info: LightWalletdInfo,
localNetwork: ZcashNetwork, saplingActivation: BlockHeight,
rustBackend: ZcashRustBackendWelding.Type) throws { localNetwork: ZcashNetwork,
rustBackend: ZcashRustBackendWelding.Type) throws {
// check network types // check network types
guard let remoteNetworkType = NetworkType.forChainName(info.chainName) else { 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 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"
)
} }
guard remoteNetworkType == localNetwork.networkType else { guard remoteNetworkType == localNetwork.networkType else {
@ -539,8 +553,7 @@ public class CompactBlockProcessor {
guard remoteBranchID == localBranch else { guard remoteBranchID == localBranch else {
throw CompactBlockProcessorError.wrongConsensusBranchId(expectedLocally: localBranch, found: remoteBranchID) throw CompactBlockProcessorError.wrongConsensusBranchId(expectedLocally: localBranch, found: remoteBranchID)
} }
} }
/** /**
@ -553,9 +566,9 @@ public class CompactBlockProcessor {
self.backoffTimer?.invalidate() self.backoffTimer?.invalidate()
self.backoffTimer = nil self.backoffTimer = nil
if cancelTasks { if cancelTasks {
queue.cancelAllOperations() operationQueue.cancelAllOperations()
} else { } else {
self.queue.isSuspended = true self.operationQueue.isSuspended = true
} }
self.retryAttempts = 0 self.retryAttempts = 0
@ -705,7 +718,15 @@ public class CompactBlockProcessor {
} }
} }
let scanBlocksOperation = CompactBlockBatchScanningOperation(rustWelding: rustBackend, cacheDb: config.cacheDb, dataDb: config.dataDb, transactionRepository: transactionRepository, range: range, networkType: self.config.network.networkType, progressDelegate: self) let scanBlocksOperation = CompactBlockBatchScanningOperation(
rustWelding: rustBackend,
cacheDb: config.cacheDb,
dataDb: config.dataDb,
transactionRepository: transactionRepository,
range: range,
networkType: self.config.network.networkType,
progressDelegate: self
)
let validateScanningAdapterOperation = BlockOperation { [weak scanBlocksOperation, weak validateChainOperation] in let validateScanningAdapterOperation = BlockOperation { [weak scanBlocksOperation, weak validateChainOperation] in
scanBlocksOperation?.error = validateChainOperation?.error scanBlocksOperation?.error = validateChainOperation?.error
@ -716,7 +737,7 @@ public class CompactBlockProcessor {
} }
} }
scanBlocksOperation.completionHandler = { [weak self] (finished, cancelled) in scanBlocksOperation.completionHandler = { [weak self] finished, cancelled in
guard !cancelled else { guard !cancelled else {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
self?.state = .stopped self?.state = .stopped
@ -726,7 +747,7 @@ public class CompactBlockProcessor {
} }
} }
scanBlocksOperation.errorHandler = { [weak self] (error) in scanBlocksOperation.errorHandler = { [weak self] error in
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
self.processingError = error self.processingError = error
@ -734,7 +755,14 @@ public class CompactBlockProcessor {
} }
} }
let enhanceOperation = CompactBlockEnhancementOperation(rustWelding: rustBackend, dataDb: config.dataDb, downloader: downloader, repository: transactionRepository, range: range.blockRange(), networkType: self.config.network.networkType) let enhanceOperation = CompactBlockEnhancementOperation(
rustWelding: rustBackend,
dataDb: config.dataDb,
downloader: downloader,
repository: transactionRepository,
range: range.blockRange(),
networkType: self.config.network.networkType
)
enhanceOperation.startedHandler = { enhanceOperation.startedHandler = {
LoggerProxy.debug("Started Enhancing range: \(range)") LoggerProxy.debug("Started Enhancing range: \(range)")
@ -743,18 +771,18 @@ public class CompactBlockProcessor {
} }
} }
enhanceOperation.txFoundHandler = { [weak self] (txs,range) in enhanceOperation.txFoundHandler = { [weak self] txs, range in
self?.notifyTransactions(txs,in: range) self?.notifyTransactions(txs, in: range)
} }
enhanceOperation.completionHandler = { (finished, cancelled) in enhanceOperation.completionHandler = { finished, cancelled in
guard !cancelled else { guard !cancelled else {
LoggerProxy.debug("Warning: enhance operation on range \(range) cancelled") LoggerProxy.debug("Warning: enhance operation on range \(range) cancelled")
return return
} }
} }
enhanceOperation.errorHandler = { [weak self] (error) in enhanceOperation.errorHandler = { [weak self] error in
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -767,7 +795,14 @@ public class CompactBlockProcessor {
enhanceOperation?.error = scanBlocksOperation?.error enhanceOperation?.error = scanBlocksOperation?.error
} }
let fetchOperation = FetchUnspentTxOutputsOperation(accountRepository: accountRepository, downloader: self.downloader, rustbackend: rustBackend, dataDb: config.dataDb, startHeight: config.walletBirthday, networkType: self.config.network.networkType) let fetchOperation = FetchUnspentTxOutputsOperation(
accountRepository: accountRepository,
downloader: self.downloader,
rustbackend: rustBackend,
dataDb: config.dataDb,
startHeight: config.walletBirthday,
networkType: self.config.network.networkType
)
fetchOperation.startedHandler = { [weak self] in fetchOperation.startedHandler = { [weak self] in
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
@ -775,7 +810,7 @@ public class CompactBlockProcessor {
} }
} }
fetchOperation.completionHandler = { [weak self] (finished, cancelled) in fetchOperation.completionHandler = { [weak self] finished, cancelled in
guard !cancelled else { guard !cancelled else {
LoggerProxy.debug("Warning: fetch operation on range \(range) cancelled") LoggerProxy.debug("Warning: fetch operation on range \(range) cancelled")
return return
@ -784,7 +819,7 @@ public class CompactBlockProcessor {
self?.processBatchFinished(range: range) self?.processBatchFinished(range: range)
} }
} }
fetchOperation.errorHandler = { [weak self] (error) in fetchOperation.errorHandler = { [weak self] error in
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
@ -809,7 +844,7 @@ public class CompactBlockProcessor {
enhanceFetchAdapterOperation.addDependency(enhanceOperation) enhanceFetchAdapterOperation.addDependency(enhanceOperation)
fetchOperation.addDependency(enhanceFetchAdapterOperation) fetchOperation.addDependency(enhanceFetchAdapterOperation)
queue.addOperations([downloadBlockOperation, operationQueue.addOperations([downloadBlockOperation,
downloadValidateAdapterOperation, downloadValidateAdapterOperation,
validateChainOperation, validateChainOperation,
validateScanningAdapterOperation, validateScanningAdapterOperation,
@ -853,15 +888,19 @@ public class CompactBlockProcessor {
private func validationFailed(at height: BlockHeight) { private func validationFailed(at height: BlockHeight) {
// cancel all Tasks // cancel all Tasks
queue.cancelAllOperations() operationQueue.cancelAllOperations()
// register latest failure // register latest failure
self.lastChainValidationFailure = height self.lastChainValidationFailure = height
self.consecutiveChainValidationErrors = self.consecutiveChainValidationErrors + 1 self.consecutiveChainValidationErrors += 1
// rewind // rewind
let rewindHeight = determineLowerBound(errorHeight: height, consecutiveErrors: consecutiveChainValidationErrors, walletBirthday: self.config.walletBirthday) let rewindHeight = determineLowerBound(
errorHeight: height,
consecutiveErrors: consecutiveChainValidationErrors,
walletBirthday: self.config.walletBirthday
)
guard rustBackend.rewindToHeight(dbData: config.dataDb, height: Int32(rewindHeight), networkType: self.config.network.networkType) else { guard rustBackend.rewindToHeight(dbData: config.dataDb, height: Int32(rewindHeight), networkType: self.config.network.networkType) else {
fail(rustBackend.lastError() ?? RustWeldingError.genericError(message: "unknown error rewinding to height \(height)")) fail(rustBackend.lastError() ?? RustWeldingError.genericError(message: "unknown error rewinding to height \(height)"))
return return
@ -871,7 +910,13 @@ public class CompactBlockProcessor {
try downloader.rewind(to: rewindHeight) try downloader.rewind(to: rewindHeight)
// notify reorg // notify reorg
NotificationCenter.default.post(name: Notification.Name.blockProcessorHandledReOrg, object: self, userInfo: [CompactBlockProcessorNotificationKey.reorgHeight : height, CompactBlockProcessorNotificationKey.rewindHeight : rewindHeight]) NotificationCenter.default.post(
name: Notification.Name.blockProcessorHandledReOrg,
object: self,
userInfo: [
CompactBlockProcessorNotificationKey.reorgHeight: height, CompactBlockProcessorNotificationKey.rewindHeight: rewindHeight
]
)
// process next batch // process next batch
self.nextBatch() self.nextBatch()
@ -880,13 +925,16 @@ public class CompactBlockProcessor {
} }
} }
func determineLowerBound(errorHeight: Int, consecutiveErrors: Int, walletBirthday: BlockHeight) -> BlockHeight { func determineLowerBound(
let offset = min(ZcashSDK.MAX_REORG_SIZE, ZcashSDK.DEFAULT_REWIND_DISTANCE * (consecutiveErrors + 1)) errorHeight: Int,
return max(errorHeight - offset, walletBirthday - ZcashSDK.MAX_REORG_SIZE) consecutiveErrors: Int,
walletBirthday: BlockHeight
) -> BlockHeight {
let offset = min(ZcashSDK.maxReorgSize, ZcashSDK.defaultRewindDistance * (consecutiveErrors + 1))
return max(errorHeight - offset, walletBirthday - ZcashSDK.maxReorgSize)
} }
private func processBatchFinished(range: CompactBlockRange) { private func processBatchFinished(range: CompactBlockRange) {
guard processingError == nil else { guard processingError == nil else {
retryProcessing(range: range) retryProcessing(range: range)
return return
@ -908,9 +956,10 @@ public class CompactBlockProcessor {
name: Notification.Name.blockProcessorFinished, name: Notification.Name.blockProcessorFinished,
object: self, object: self,
userInfo: [ userInfo: [
CompactBlockProcessorNotificationKey.latestScannedBlockHeight : height, CompactBlockProcessorNotificationKey.latestScannedBlockHeight: height,
CompactBlockProcessorNotificationKey.foundBlocks : self.foundBlocks CompactBlockProcessorNotificationKey.foundBlocks: self.foundBlocks
]) ]
)
self.state = .synced self.state = .synced
setTimer() setTimer()
} }
@ -923,13 +972,15 @@ public class CompactBlockProcessor {
do { do {
if self.shouldStart { if self.shouldStart {
LoggerProxy.debug(""" LoggerProxy.debug(
Timer triggered: Starting compact Block processor!. """
Processor State: \(self.state) Timer triggered: Starting compact Block processor!.
latestHeight: \(self.latestBlockHeight) Processor State: \(self.state)
attempts: \(self.retryAttempts) latestHeight: \(self.latestBlockHeight)
lowerbound: \(String(describing: self.lowerBoundHeight)) attempts: \(self.retryAttempts)
""") lowerbound: \(String(describing: self.lowerBoundHeight))
"""
)
try self.start() try self.start()
} else if self.maxAttemptsReached { } else if self.maxAttemptsReached {
self.fail(CompactBlockProcessorError.maxAttemptsReached(attempts: self.config.retries)) self.fail(CompactBlockProcessorError.maxAttemptsReached(attempts: self.config.retries))
@ -944,7 +995,6 @@ public class CompactBlockProcessor {
} }
static func nextBatchBlockRange(latestHeight: BlockHeight, latestDownloadedHeight: BlockHeight, walletBirthday: BlockHeight) -> CompactBlockRange { static func nextBatchBlockRange(latestHeight: BlockHeight, latestDownloadedHeight: BlockHeight, walletBirthday: BlockHeight) -> CompactBlockRange {
let lowerBound = latestDownloadedHeight <= walletBirthday ? walletBirthday : latestDownloadedHeight + 1 let lowerBound = latestDownloadedHeight <= walletBirthday ? walletBirthday : latestDownloadedHeight + 1
let upperBound = latestHeight let upperBound = latestHeight
@ -952,10 +1002,9 @@ public class CompactBlockProcessor {
} }
func retryProcessing(range: CompactBlockRange) { func retryProcessing(range: CompactBlockRange) {
operationQueue.cancelAllOperations()
queue.cancelAllOperations()
// update retries // update retries
self.retryAttempts = self.retryAttempts + 1 self.retryAttempts += 1
self.processingError = nil self.processingError = nil
guard self.retryAttempts < config.retries else { guard self.retryAttempts < config.retries else {
self.notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.retryAttempts)) self.notifyError(CompactBlockProcessorError.maxAttemptsReached(attempts: self.retryAttempts))
@ -972,25 +1021,23 @@ public class CompactBlockProcessor {
} catch { } catch {
self.fail(error) self.fail(error)
} }
} }
func severeFailure(_ error: Error) { func severeFailure(_ error: Error) {
queue.cancelAllOperations() operationQueue.cancelAllOperations()
LoggerProxy.error("show stoppper failure: \(error)") LoggerProxy.error("show stoppper failure: \(error)")
self.backoffTimer?.invalidate() self.backoffTimer?.invalidate()
self.retryAttempts = config.retries self.retryAttempts = config.retries
self.processingError = error self.processingError = error
self.state = .error(error) self.state = .error(error)
self.notifyError(error) self.notifyError(error)
} }
func fail(_ error: Error) { func fail(_ error: Error) {
// todo specify: failure // todo specify: failure
LoggerProxy.error("\(error)") LoggerProxy.error("\(error)")
queue.cancelAllOperations() operationQueue.cancelAllOperations()
self.retryAttempts = self.retryAttempts + 1 self.retryAttempts += 1
self.processingError = error self.processingError = error
switch self.state { switch self.state {
case .error: case .error:
@ -1002,7 +1049,6 @@ public class CompactBlockProcessor {
guard self.maxAttemptsReached else { return } guard self.maxAttemptsReached else { return }
// don't set a new timer if there are no more attempts. // don't set a new timer if there are no more attempts.
self.setTimer() self.setTimer()
} }
private func transitionState(from oldValue: State, to newValue: State) { private func transitionState(from oldValue: State, to newValue: State) {
@ -1125,7 +1171,7 @@ extension CompactBlockProcessor {
guard let account = try? accountRepository.findBy(account: accountIndex) else { guard let account = try? accountRepository.findBy(account: accountIndex) else {
return nil return nil
} }
return UnifiedAddressShim(__account: account) return UnifiedAddressShim(account: account)
} }
public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? { public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? {
@ -1144,38 +1190,40 @@ extension CompactBlockProcessor {
} }
} }
fileprivate struct UnifiedAddressShim { private struct UnifiedAddressShim {
let __account: AccountEntity let account: AccountEntity
} }
extension UnifiedAddressShim: UnifiedAddress { extension UnifiedAddressShim: UnifiedAddress {
var tAddress: TransparentAddress { var tAddress: TransparentAddress {
get { get {
__account.transparentAddress account.transparentAddress
} }
} }
var zAddress: SaplingShieldedAddress { var zAddress: SaplingShieldedAddress {
get { get {
__account.address account.address
} }
} }
} }
extension CompactBlockProcessor { extension CompactBlockProcessor {
func refreshUTXOs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result<RefreshedUTXOs,Error>) -> Void) { func refreshUTXOs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result<RefreshedUTXOs,Error>) -> Void) {
let dataDb = self.config.dataDb let dataDb = self.config.dataDb
self.downloader.fetchUnspentTransactionOutputs(tAddress: tAddress, startHeight: startHeight) { [weak self](r) in self.downloader.fetchUnspentTransactionOutputs(tAddress: tAddress, startHeight: startHeight) { [weak self](r) in
switch r { switch r {
case .success(let utxos): case .success(let utxos):
DispatchQueue.main.async { DispatchQueue.main.async {
self?.queue.addOperation { [self] in self?.operationQueue.addOperation { [self] in
guard let self = self else { return } guard let self = self else { return }
do { do {
guard try self.rustBackend.clearUtxos(dbData: dataDb, address: tAddress, sinceHeight: startHeight - 1, networkType: self.config.network.networkType) >= 0 else { guard try self.rustBackend.clearUtxos(
dbData: dataDb,
address: tAddress,
sinceHeight: startHeight - 1,
networkType: self.config.network.networkType
) >= 0 else {
result(.failure(CompactBlockProcessorError.generalError(message: "attempted to clear utxos but -1 was returned"))) result(.failure(CompactBlockProcessorError.generalError(message: "attempted to clear utxos but -1 was returned")))
return return
} }
@ -1204,7 +1252,8 @@ extension CompactBlockProcessor {
script: utxo.script.bytes, script: utxo.script.bytes,
value: Int64(utxo.valueZat), value: Int64(utxo.valueZat),
height: utxo.height, height: utxo.height,
networkType: self.config.network.networkType) ? refreshed.append(utxo) : skipped.append(utxo) networkType: self.config.network.networkType
) ? refreshed.append(utxo) : skipped.append(utxo)
} catch { } catch {
LoggerProxy.info("failed to put utxo - error: \(error)") LoggerProxy.info("failed to put utxo - error: \(error)")
skipped.append(utxo) skipped.append(utxo)
@ -1239,12 +1288,15 @@ extension CompactBlockProcessorError: LocalizedError {
case .missingDbPath(let path): case .missingDbPath(let path):
return "CompactBlockProcessor was set up with path \(path) but thath location couldn't be reached" return "CompactBlockProcessor was set up with path \(path) but thath location couldn't be reached"
case .networkMismatch(let expected, let found): case .networkMismatch(let expected, let found):
// swiftlint:disable:next line_length
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" 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 .saplingActivationMismatch(let expected, let found): case .saplingActivationMismatch(let expected, let found):
// swiftlint:disable:next line_length
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?" 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): case .unspecifiedError(let underlyingError):
return "Unspecified error caused by this underlying error: \(underlyingError)" return "Unspecified error caused by this underlying error: \(underlyingError)"
case .wrongConsensusBranchId(let expectedLocally, let found): case .wrongConsensusBranchId(let expectedLocally, let found):
// swiftlint:disable:next line_length
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." 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."
} }
} }
@ -1286,24 +1338,24 @@ public extension BlockProgressReporting {
} }
extension CompactBlockProcessor { extension CompactBlockProcessor {
enum NextStateHelper {
class NextStateHelper { static func nextState(
service: LightWalletService,
static func nextState(service: LightWalletService, downloader: CompactBlockDownloading,
downloader: CompactBlockDownloading, config: Configuration,
config: Configuration, rustBackend: ZcashRustBackendWelding.Type,
rustBackend: ZcashRustBackendWelding.Type, queue: DispatchQueue?,
queue: DispatchQueue?, result: @escaping (Result<FigureNextBatchOperation.NextState, Error>) -> Void
result: @escaping (Result<FigureNextBatchOperation.NextState, Error>) -> Void) { ) {
let dispatchQueue = queue ?? DispatchQueue.global(qos: .userInitiated) let dispatchQueue = queue ?? DispatchQueue.global(qos: .userInitiated)
dispatchQueue.async { dispatchQueue.async {
do { do {
let r = try self.nextState(service: service, let r = try self.nextState(
downloader: downloader, service: service,
config: config, downloader: downloader,
rustBackend: rustBackend) config: config,
rustBackend: rustBackend)
result(.success(r)) result(.success(r))
} catch { } catch {
result(.failure(error)) result(.failure(error))
@ -1311,28 +1363,34 @@ extension CompactBlockProcessor {
} }
} }
static func nextState(service: LightWalletService, static func nextState(
downloader: CompactBlockDownloading, service: LightWalletService,
config: Configuration, downloader: CompactBlockDownloading,
rustBackend: ZcashRustBackendWelding.Type) throws -> FigureNextBatchOperation.NextState { config: Configuration,
rustBackend: ZcashRustBackendWelding.Type
) throws -> FigureNextBatchOperation.NextState {
let info = try service.getInfo() let info = try service.getInfo()
try CompactBlockProcessor.validateServerInfo(info, saplingActivation: config.saplingActivation, localNetwork: config.network, rustBackend: rustBackend) try CompactBlockProcessor.validateServerInfo(info, saplingActivation: config.saplingActivation, localNetwork: config.network, rustBackend: rustBackend)
// get latest block height // get latest block height
let latestDownloadedBlockHeight: BlockHeight = max(config.walletBirthday,try downloader.lastDownloadedBlockHeight()) let latestDownloadedBlockHeight: BlockHeight = max(config.walletBirthday, try downloader.lastDownloadedBlockHeight())
let latestBlockheight = try service.latestBlockHeight() let latestBlockheight = try service.latestBlockHeight()
if latestDownloadedBlockHeight < latestBlockheight { if latestDownloadedBlockHeight < latestBlockheight {
return .processNewBlocks(range: CompactBlockProcessor.nextBatchBlockRange(latestHeight: latestBlockheight, latestDownloadedHeight: latestDownloadedBlockHeight, walletBirthday: config.walletBirthday)) return .processNewBlocks(
range: CompactBlockProcessor.nextBatchBlockRange(
latestHeight: latestBlockheight,
latestDownloadedHeight: latestDownloadedBlockHeight,
walletBirthday: config.walletBirthday
)
)
} else if latestBlockheight == latestDownloadedBlockHeight { } else if latestBlockheight == latestDownloadedBlockHeight {
return .finishProcessing(height: latestBlockheight) return .finishProcessing(height: latestBlockheight)
} }
return .wait(latestHeight: latestBlockheight, latestDownloadHeight: latestBlockheight) return .wait(latestHeight: latestBlockheight, latestDownloadHeight: latestBlockheight)
} }
} }
} }

View File

@ -169,10 +169,12 @@ class CompactBlockBatchScanningOperation: ZcashOperation {
} }
let previousScannedHeight = lastScannedHeight let previousScannedHeight = lastScannedHeight
let scanStartTime = Date() let scanStartTime = Date()
guard self.rustBackend.scanBlocks(dbCache: self.cacheDb, guard self.rustBackend.scanBlocks(
dbData: self.dataDb, dbCache: self.cacheDb,
limit: batchSize, dbData: self.dataDb,
networkType: network) else { limit: batchSize,
networkType: network
) else {
self.scanFailed(self.rustBackend.lastError() ?? ZcashOperationError.unknown) self.scanFailed(self.rustBackend.lastError() ?? ZcashOperationError.unknown)
return return
} }
@ -184,12 +186,18 @@ class CompactBlockBatchScanningOperation: ZcashOperation {
if scannedNewBlocks { if scannedNewBlocks {
let progress = BlockProgress(startHeight: scanStartHeight, targetHeight: targetScanHeight, progressHeight: lastScannedHeight) let progress = BlockProgress(startHeight: scanStartHeight, targetHeight: targetScanHeight, progressHeight: lastScannedHeight)
progressDelegate?.progressUpdated(.scan(progress)) progressDelegate?.progressUpdated(.scan(progress))
NotificationCenter.default.post(SDKMetrics.progressReportNotification(progress: progress, start: scanStartTime, end: scanFinishTime, task: .scanBlocks)) NotificationCenter.default.post(
SDKMetrics.progressReportNotification(
progress: progress,
start: scanStartTime,
end: scanFinishTime,
task: .scanBlocks
)
)
// swiftlint:disable line_length
LoggerProxy.debug("Scanned \(lastScannedHeight - previousScannedHeight) blocks in \(scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate) seconds") LoggerProxy.debug("Scanned \(lastScannedHeight - previousScannedHeight) blocks in \(scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate) seconds")
} }
} while !self.isCancelled && scannedNewBlocks && lastScannedHeight < targetScanHeight } while !self.isCancelled && scannedNewBlocks && lastScannedHeight < targetScanHeight
} }
} catch { } catch {
scanFailed(error) scanFailed(error)

View File

@ -51,8 +51,8 @@ class ZcashOperation: Operation {
self.cancel() self.cancel()
} }
if let e = error { if let error = error {
self.error = e self.error = error
} }
LoggerProxy.debug("\(self) failed") LoggerProxy.debug("\(self) failed")
@ -61,9 +61,8 @@ class ZcashOperation: Operation {
} }
self.handlerDispatchQueue.async { [weak self] in self.handlerDispatchQueue.async { [weak self] in
let e = error ?? (self?.error ?? ZcashOperationError.unknown) let error = error ?? (self?.error ?? ZcashOperationError.unknown)
errorHandler(e) errorHandler(error)
} }
} }
} }

View File

@ -4,7 +4,7 @@
// //
// Created by Francisco Gindre on 7/28/21. // Created by Francisco Gindre on 7/28/21.
// //
// swiftlint:disable function_body_length line_length cyclomatic_complexity
import Foundation import Foundation
extension WalletBirthday { extension WalletBirthday {

View File

@ -4,7 +4,7 @@
// //
// Created by Francisco Gindre on 7/28/21. // Created by Francisco Gindre on 7/28/21.
// //
// swiftlint:disable all
import Foundation import Foundation
extension WalletBirthday { extension WalletBirthday {

View File

@ -67,146 +67,142 @@ public class ZcashSDK {
/** /**
The number of zatoshi that equal 1 ZEC. The number of zatoshi that equal 1 ZEC.
*/ */
public static var ZATOSHI_PER_ZEC: BlockHeight = 100_000_000 public static var zatoshiPerZEC: BlockHeight = 100_000_000
/** /**
The theoretical maximum number of blocks in a reorg, due to other bottlenecks in the protocol design. The theoretical maximum number of blocks in a reorg, due to other bottlenecks in the protocol design.
*/ */
public static var MAX_REORG_SIZE = 100 public static var maxReorgSize = 100
/** /**
The amount of blocks ahead of the current height where new transactions are set to expire. This value is controlled 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. by the rust backend but it is helpful to know what it is set to and should be kept in sync.
*/ */
public static var EXPIRY_OFFSET = 20 public static var expiryOffset = 20
// //
// Defaults // Defaults
// //
/** /**
Default size of batches of blocks to request from the compact block service. Default size of batches of blocks to request from the compact block service.
*/ */
public static var DEFAULT_BATCH_SIZE = 100 public static var DefaultBatchSize = 100
/** /**
Default amount of time, in in seconds, to poll for new blocks. Typically, this should be about half the average Default amount of time, in in seconds, to poll for new blocks. Typically, this should be about half the average
block time. block time.
*/ */
public static var DEFAULT_POLL_INTERVAL: TimeInterval = 20 public static var defaultPollInterval: TimeInterval = 20
/** /**
Default attempts at retrying. Default attempts at retrying.
*/ */
public static var DEFAULT_RETRIES: Int = 5 public static var defaultRetries: Int = 5
/** /**
The default maximum amount of time to wait during retry backoff intervals. Failed loops will never wait longer than The default maximum amount of time to wait during retry backoff intervals. Failed loops will never wait longer than
this before retyring. this before retyring.
*/ */
public static var DEFAULT_MAX_BACKOFF_INTERVAL: TimeInterval = 600 public static var defaultMaxBackOffInterval: TimeInterval = 600
/** /**
Default number of blocks to rewind when a chain reorg is detected. This should be large enough to recover from the 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. reorg but smaller than the theoretical max reorg size of 100.
*/ */
public static var DEFAULT_REWIND_DISTANCE: Int = 10 public static var defaultRewindDistance: Int = 10
/** /**
The number of blocks to allow before considering our data to be stale. This usually helps with what to do when 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. returning from the background and is exposed via the Synchronizer's isStale function.
*/ */
public static var DEFAULT_STALE_TOLERANCE: Int = 10 public static var defaultStaleTolerance: Int = 10
/** /**
Default Name for LibRustZcash data.db Default Name for LibRustZcash data.db
*/ */
public static var DEFAULT_DATA_DB_NAME = "data.db" public static var defaultDataDbName = "data.db"
/** /**
Default Name for Compact Block caches db Default Name for Compact Block caches db
*/ */
public static var DEFAULT_CACHES_DB_NAME = "caches.db" public static var defaultCacheDbName = "caches.db"
/** /**
Default name for pending transactions db Default name for pending transactions db
*/ */
public static var DEFAULT_PENDING_DB_NAME = "pending.db" public static var defaultPendingDbName = "pending.db"
/** /**
File name for the sapling spend params File name for the sapling spend params
*/ */
public static var SPEND_PARAM_FILE_NAME = "sapling-spend.params" public static var spendParamFilename = "sapling-spend.params"
/** /**
File name for the sapling output params File name for the sapling output params
*/ */
public static var OUTPUT_PARAM_FILE_NAME = "sapling-output.params" public static var outputParamFilename = "sapling-output.params"
/** /**
The Url that is used by default in zcashd. 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 We'll want to make this externally configurable, rather than baking it into the SDK but
this will do for now, since we're using a cloudfront URL that already redirects. this will do for now, since we're using a cloudfront URL that already redirects.
*/ */
public static var CLOUD_PARAM_DIR_URL = "https://z.cash/downloads/" public static var cloudParameterURL = "https://z.cash/downloads/"
} }
public protocol NetworkConstants { public protocol NetworkConstants {
/** /**
The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks 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. prior to this height, at all.
*/ */
static var SAPLING_ACTIVATION_HEIGHT: BlockHeight { get } static var saplingActivationHeight: BlockHeight { get }
/** /**
Default Name for LibRustZcash data.db Default Name for LibRustZcash data.db
*/ */
static var DEFAULT_DATA_DB_NAME: String { get } static var defaultDataDbName: String { get }
/** /**
Default Name for Compact Block caches db Default Name for Compact Block caches db
*/ */
static var DEFAULT_CACHES_DB_NAME: String { get } static var defaultCacheDbName: String { get }
/** /**
Default name for pending transactions db Default name for pending transactions db
*/ */
static var DEFAULT_PENDING_DB_NAME: String { get } static var defaultPendingDbName: String { get }
static var DEFAULT_DB_NAME_PREFIX: String { get } static var defaultDbNamePrefix: String { get }
/** /**
fixed height where the SDK considers that the ZIP-321 was deployed. This is a workaround fixed height where the SDK considers that the ZIP-321 was deployed. This is a workaround
for librustzcash not figuring out the tx fee from the tx itself. for librustzcash not figuring out the tx fee from the tx itself.
*/ */
static var FEE_CHANGE_HEIGHT: BlockHeight { get } static var feeChangeHeight: BlockHeight { get }
static func defaultFee(for height: BlockHeight) -> Int64 static func defaultFee(for height: BlockHeight) -> Int64
} }
public extension NetworkConstants { public extension NetworkConstants {
static func defaultFee(for height: BlockHeight = BlockHeight.max) -> Int64 { static func defaultFee(for height: BlockHeight = BlockHeight.max) -> Int64 {
guard height >= FEE_CHANGE_HEIGHT else { return 10_000 } guard height >= feeChangeHeight else { return 10_000 }
return 1_000 return 1_000
} }
} }
public class ZcashSDKMainnetConstants: NetworkConstants { public class ZcashSDKMainnetConstants: NetworkConstants {
private init() {} private init() {}
/** /**
The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks 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. prior to this height, at all.
*/ */
public static var SAPLING_ACTIVATION_HEIGHT: BlockHeight = 419_200 public static var saplingActivationHeight: BlockHeight = 419_200
/** /**
Default Name for LibRustZcash data.db Default Name for LibRustZcash data.db
*/ */
public static var DEFAULT_DATA_DB_NAME = "data.db" public static var defaultDataDbName = "data.db"
/** /**
Default Name for Compact Block caches db Default Name for Compact Block caches db
*/ */
public static var DEFAULT_CACHES_DB_NAME = "caches.db" public static var defaultCacheDbName = "caches.db"
/** /**
Default name for pending transactions db Default name for pending transactions db
*/ */
public static var DEFAULT_PENDING_DB_NAME = "pending.db" public static var defaultPendingDbName = "pending.db"
public static var DEFAULT_DB_NAME_PREFIX = "ZcashSdk_mainnet_" public static var defaultDbNamePrefix = "ZcashSdk_mainnet_"
public static var FEE_CHANGE_HEIGHT: BlockHeight = 1_077_550 public static var feeChangeHeight: BlockHeight = 1_077_550
} }
public class ZcashSDKTestnetConstants: NetworkConstants { public class ZcashSDKTestnetConstants: NetworkConstants {
@ -216,26 +212,25 @@ public class ZcashSDKTestnetConstants: NetworkConstants {
The height of the first sapling block. When it comes to shielded transactions, we do not need to consider any blocks 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. prior to this height, at all.
*/ */
public static var SAPLING_ACTIVATION_HEIGHT: BlockHeight = 280_000 public static var saplingActivationHeight: BlockHeight = 280_000
/** /**
Default Name for LibRustZcash data.db Default Name for LibRustZcash data.db
*/ */
public static var DEFAULT_DATA_DB_NAME = "data.db" public static var defaultDataDbName = "data.db"
/** /**
Default Name for Compact Block caches db Default Name for Compact Block caches db
*/ */
public static var DEFAULT_CACHES_DB_NAME = "caches.db" public static var defaultCacheDbName = "caches.db"
/** /**
Default name for pending transactions db Default name for pending transactions db
*/ */
public static var DEFAULT_PENDING_DB_NAME = "pending.db" public static var defaultPendingDbName = "pending.db"
public static var DEFAULT_DB_NAME_PREFIX = "ZcashSdk_testnet_" public static var defaultDbNamePrefix = "ZcashSdk_testnet_"
/** /**
Estimated height where wallets are supposed to change the fee Estimated height where wallets are supposed to change the fee
*/ */
public static var FEE_CHANGE_HEIGHT: BlockHeight = 1_028_500 public static var feeChangeHeight: BlockHeight = 1_028_500
} }

View File

@ -118,40 +118,39 @@ class PendingTransactionSQLDAO: PendingTransactionRepository {
} }
func createrTableIfNeeded() throws { func createrTableIfNeeded() throws {
let statement = table.create(ifNotExists: true) { t in let statement = table.create(ifNotExists: true) { createdTable in
t.column(TableColumns.id, primaryKey: .autoincrement) createdTable.column(TableColumns.id, primaryKey: .autoincrement)
t.column(TableColumns.toAddress) createdTable.column(TableColumns.toAddress)
t.column(TableColumns.accountIndex) createdTable.column(TableColumns.accountIndex)
t.column(TableColumns.minedHeight) createdTable.column(TableColumns.minedHeight)
t.column(TableColumns.expiryHeight) createdTable.column(TableColumns.expiryHeight)
t.column(TableColumns.cancelled) createdTable.column(TableColumns.cancelled)
t.column(TableColumns.encodeAttempts, defaultValue: 0) createdTable.column(TableColumns.encodeAttempts, defaultValue: 0)
t.column(TableColumns.errorMessage) createdTable.column(TableColumns.errorMessage)
t.column(TableColumns.errorCode) createdTable.column(TableColumns.errorCode)
t.column(TableColumns.submitAttempts, defaultValue: 0) createdTable.column(TableColumns.submitAttempts, defaultValue: 0)
t.column(TableColumns.createTime) createdTable.column(TableColumns.createTime)
t.column(TableColumns.rawTransactionId) createdTable.column(TableColumns.rawTransactionId)
t.column(TableColumns.value) createdTable.column(TableColumns.value)
t.column(TableColumns.raw) createdTable.column(TableColumns.raw)
t.column(TableColumns.memo) createdTable.column(TableColumns.memo)
} }
try dbProvider.connection().run(statement) try dbProvider.connection().run(statement)
} }
func create(_ transaction: PendingTransactionEntity) throws -> Int { func create(_ transaction: PendingTransactionEntity) throws -> Int {
let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
let tx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction) return try Int(dbProvider.connection().run(table.insert(pendingTx)))
return try Int(dbProvider.connection().run(table.insert(tx)))
} }
func update(_ transaction: PendingTransactionEntity) throws { func update(_ transaction: PendingTransactionEntity) throws {
let tx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction) let pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
guard let id = tx.id else { guard let id = pendingTx.id else {
throw StorageError.malformedEntity(fields: ["id"]) throw StorageError.malformedEntity(fields: ["id"])
} }
let updatedRows = try dbProvider.connection().run(table.filter(TableColumns.id == id).update(tx)) let updatedRows = try dbProvider.connection().run(table.filter(TableColumns.id == id).update(pendingTx))
if updatedRows == 0 { if updatedRows == 0 {
LoggerProxy.error("attempted to update pending transactions but no rows were updated") LoggerProxy.error("attempted to update pending transactions but no rows were updated")
} }
@ -166,17 +165,16 @@ class PendingTransactionSQLDAO: PendingTransactionRepository {
} catch { } catch {
throw StorageError.updateFailed throw StorageError.updateFailed
} }
} }
func cancel(_ transaction: PendingTransactionEntity) throws { func cancel(_ transaction: PendingTransactionEntity) throws {
var tx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction) var pendingTx = transaction as? PendingTransaction ?? PendingTransaction.from(entity: transaction)
tx.cancelled = 1 pendingTx.cancelled = 1
guard let id = tx.id else { guard let txId = pendingTx.id else {
throw StorageError.malformedEntity(fields: ["id"]) throw StorageError.malformedEntity(fields: ["id"])
} }
try dbProvider.connection().run(table.filter(TableColumns.id == id).update(tx)) try dbProvider.connection().run(table.filter(TableColumns.id == txId).update(pendingTx))
} }
func find(by id: Int) throws -> PendingTransactionEntity? { func find(by id: Int) throws -> PendingTransactionEntity? {
@ -184,8 +182,8 @@ class PendingTransactionSQLDAO: PendingTransactionRepository {
return nil return nil
} }
do { do {
let tx: PendingTransaction = try row.decode() let pendingTx: PendingTransaction = try row.decode()
return tx return pendingTx
} catch { } catch {
throw StorageError.operationFailed throw StorageError.operationFailed
} }

View File

@ -60,14 +60,13 @@ extension UnspentTransactionOutputEntity {
} }
import SQLite import SQLite
class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository { class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
func store(utxos: [UnspentTransactionOutputEntity]) throws { func store(utxos: [UnspentTransactionOutputEntity]) throws {
do { do {
let db = try dbProvider.connection() let db = try dbProvider.connection()
try dbProvider.connection().transaction { try dbProvider.connection().transaction {
for utxo in utxos.map({ (u) -> UTXO in for utxo in utxos.map({ (mappedUTXO) -> UTXO in
u as? UTXO ?? u.asUTXO() mappedUTXO as? UTXO ?? mappedUTXO.asUTXO()
}) { }) {
try db.run(table.insert(utxo)) try db.run(table.insert(utxo))
} }
@ -153,7 +152,7 @@ class UnspentTransactionOutputSQLDAO: UnspentTransactionOutputRepository {
let verified = try dbProvider.connection().scalar( let verified = try dbProvider.connection().scalar(
table.select(TableColumns.valueZat.sum) table.select(TableColumns.valueZat.sum)
.filter(TableColumns.address == address) .filter(TableColumns.address == address)
.filter(TableColumns.height <= latestHeight - ZcashSDK.DEFAULT_STALE_TOLERANCE)) ?? 0 .filter(TableColumns.height <= latestHeight - ZcashSDK.defaultStaleTolerance)) ?? 0
let total = try dbProvider.connection().scalar( let total = try dbProvider.connection().scalar(
table.select(TableColumns.valueZat.sum) table.select(TableColumns.valueZat.sum)
.filter(TableColumns.address == address)) ?? 0 .filter(TableColumns.address == address)) ?? 0

View File

@ -114,10 +114,10 @@ class AccountSQDAO: AccountRepository {
} }
func update(_ account: AccountEntity) throws { func update(_ account: AccountEntity) throws {
guard let a = account as? Account else { guard let acc = account as? Account else {
throw StorageError.updateFailed throw StorageError.updateFailed
} }
let updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == a.account).update(a)) let updatedRows = try dbProvider.connection().run(table.filter(TableColums.account == acc.account).update(acc))
if updatedRows == 0 { if updatedRows == 0 {
LoggerProxy.error("attempted to update pending transactions but no rows were updated") LoggerProxy.error("attempted to update pending transactions but no rows were updated")
throw StorageError.updateFailed throw StorageError.updateFailed
@ -126,17 +126,16 @@ class AccountSQDAO: AccountRepository {
} }
class CachingAccountDao: AccountRepository { class CachingAccountDao: AccountRepository {
var dao: AccountRepository var dao: AccountRepository
lazy var cache: [Int: AccountEntity] = { lazy var cache: [Int: AccountEntity] = {
var c = [Int : AccountEntity]() var accountCache = [Int: AccountEntity]()
guard let all = try? dao.getAll() else { guard let all = try? dao.getAll() else {
return c return accountCache
} }
for a in all { for acc in all {
c[a.account] = a accountCache[acc.account] = acc
} }
return c return accountCache
}() }()
init(dao: AccountRepository) { init(dao: AccountRepository) {
@ -149,20 +148,20 @@ class CachingAccountDao: AccountRepository {
} }
let all = try dao.getAll() let all = try dao.getAll()
for a in all { for acc in all {
cache[a.account] = a cache[acc.account] = acc
} }
return all return all
} }
func findBy(account: Int) throws -> AccountEntity? { func findBy(account: Int) throws -> AccountEntity? {
if let a = cache[account] { if let acc = cache[account] {
return a return acc
} }
let a = try dao.findBy(account: account) let acc = try dao.findBy(account: account)
cache[account] = a cache[account] = acc
return a return acc
} }
func findBy(address: String) throws -> AccountEntity? { func findBy(address: String) throws -> AccountEntity? {

View File

@ -153,7 +153,7 @@ public extension PendingTransactionEntity {
return false return false
} }
return abs(currentHeight - minedHeight) >= ZcashSDK.DEFAULT_STALE_TOLERANCE return abs(currentHeight - minedHeight) >= ZcashSDK.defaultStaleTolerance
} }
} }
@ -162,7 +162,15 @@ public extension PendingTransactionEntity {
TransactionEntity representation of this PendingTransactionEntity transaction TransactionEntity representation of this PendingTransactionEntity transaction
*/ */
var transactionEntity: TransactionEntity { var transactionEntity: TransactionEntity {
Transaction(id: self.id ?? -1, transactionId: self.rawTransactionId ?? Data(), created: Date(timeIntervalSince1970: self.createTime).description, transactionIndex: -1, expiryHeight: self.expiryHeight, minedHeight: self.minedHeight, raw: self.raw) Transaction(
id: self.id ?? -1,
transactionId: self.rawTransactionId ?? Data(),
created: Date(timeIntervalSince1970: self.createTime).description,
transactionIndex: -1,
expiryHeight: self.expiryHeight,
minedHeight: self.minedHeight,
raw: self.raw
)
} }
} }
@ -171,6 +179,14 @@ public extension ConfirmedTransactionEntity {
TransactionEntity representation of this ConfirmedTransactionEntity transaction TransactionEntity representation of this ConfirmedTransactionEntity transaction
*/ */
var transactionEntity: TransactionEntity { var transactionEntity: TransactionEntity {
Transaction(id: self.id ?? -1, transactionId: self.rawTransactionId ?? Data(), created: Date(timeIntervalSince1970: self.blockTimeInSeconds).description, transactionIndex: self.transactionIndex, expiryHeight: self.expiryHeight, minedHeight: self.minedHeight, raw: self.raw) Transaction(
id: self.id ?? -1,
transactionId: self.rawTransactionId ?? Data(),
created: Date(timeIntervalSince1970: self.blockTimeInSeconds).description,
transactionIndex: self.transactionIndex,
expiryHeight: self.expiryHeight,
minedHeight: self.minedHeight,
raw: self.raw
)
} }
} }

View File

@ -59,11 +59,11 @@ public extension TransactionEntity {
func anchor(network: ZcashNetwork) -> BlockHeight? { func anchor(network: ZcashNetwork) -> BlockHeight? {
if let minedHeight = self.minedHeight, minedHeight != -1 { if let minedHeight = self.minedHeight, minedHeight != -1 {
return max(minedHeight - ZcashSDK.DEFAULT_STALE_TOLERANCE, network.constants.SAPLING_ACTIVATION_HEIGHT) return max(minedHeight - ZcashSDK.defaultStaleTolerance, network.constants.saplingActivationHeight)
} }
if let expiryHeight = self.expiryHeight, expiryHeight != -1 { if let expiryHeight = self.expiryHeight, expiryHeight != -1 {
return max(expiryHeight - ZcashSDK.EXPIRY_OFFSET - ZcashSDK.DEFAULT_STALE_TOLERANCE, network.constants.SAPLING_ACTIVATION_HEIGHT) return max(expiryHeight - ZcashSDK.expiryOffset - ZcashSDK.defaultStaleTolerance, network.constants.saplingActivationHeight)
} }
return nil return nil

View File

@ -10,11 +10,13 @@ import Foundation
extension String { extension String {
func toTxIdString() -> String { func toTxIdString() -> String {
var id = "" var id = ""
self.reversed().pairs.map { self.reversed().pairs
$0.reversed() .map {
}.forEach { (reversed) in $0.reversed()
id.append(String(reversed)) }
} .forEach { (reversed) in
id.append(String(reversed))
}
return id return id
} }
} }
@ -23,8 +25,8 @@ extension Collection {
var pairs: [SubSequence] { var pairs: [SubSequence] {
var startIndex = self.startIndex var startIndex = self.startIndex
let count = self.count let count = self.count
let n = count / 2 + count % 2 let halving = count / 2 + count % 2
return (0..<n).map { _ in return (0..<halving).map { _ in
let endIndex = index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex let endIndex = index(startIndex, offsetBy: 2, limitedBy: self.endIndex) ?? self.endIndex
defer { startIndex = endIndex } defer { startIndex = endIndex }
return self[startIndex..<endIndex] return self[startIndex..<endIndex]

View File

@ -195,7 +195,14 @@ public class Initializer {
} }
do { do {
try rustBackend.initBlocksTable(dbData: dataDbURL, height: Int32(walletBirthday.height), hash: walletBirthday.hash, time: walletBirthday.time, saplingTree: walletBirthday.tree, networkType: network.networkType) try rustBackend.initBlocksTable(
dbData: dataDbURL,
height: Int32(walletBirthday.height),
hash: walletBirthday.hash,
time: walletBirthday.time,
saplingTree: walletBirthday.tree,
networkType: network.networkType
)
} catch RustWeldingError.dataDbNotEmpty { } catch RustWeldingError.dataDbNotEmpty {
// this is fine // this is fine
} catch { } catch {
@ -212,14 +219,16 @@ public class Initializer {
} }
} catch RustWeldingError.dataDbNotEmpty { } catch RustWeldingError.dataDbNotEmpty {
// this is fine // this is fine
}catch { } catch {
throw rustBackend.lastError() ?? InitializerError.accountInitFailed throw rustBackend.lastError() ?? InitializerError.accountInitFailed
} }
let migrationManager = MigrationManager(cacheDbConnection: SimpleConnectionProvider(path: cacheDbURL.path), let migrationManager = MigrationManager(
dataDbConnection: SimpleConnectionProvider(path: dataDbURL.path), cacheDbConnection: SimpleConnectionProvider(path: cacheDbURL.path),
pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path), dataDbConnection: SimpleConnectionProvider(path: dataDbURL.path),
networkType: self.network.networkType) pendingDbConnection: SimpleConnectionProvider(path: pendingDbURL.path),
networkType: self.network.networkType
)
try migrationManager.performMigration(uvks: viewingKeys) try migrationManager.performMigration(uvks: viewingKeys)
} }
@ -257,7 +266,7 @@ public class Initializer {
checks if the provided address is a transparent zAddress checks if the provided address is a transparent zAddress
*/ */
public func isValidTransparentAddress(_ address: String) -> Bool { public func isValidTransparentAddress(_ address: String) -> Bool {
(try? rustBackend.isValidTransparentAddress(address,networkType: network.networkType)) ?? false (try? rustBackend.isValidTransparentAddress(address, networkType: network.networkType)) ?? false
} }
func isSpendParameterPresent() -> Bool { func isSpendParameterPresent() -> Bool {
@ -268,7 +277,7 @@ public class Initializer {
FileManager.default.isReadableFile(atPath: self.outputParamsURL.path) FileManager.default.isReadableFile(atPath: self.outputParamsURL.path)
} }
func downloadParametersIfNeeded(result: @escaping (Result<Bool,Error>) -> Void) { func downloadParametersIfNeeded(result: @escaping (Result<Bool, Error>) -> Void) {
let spendParameterPresent = isSpendParameterPresent() let spendParameterPresent = isSpendParameterPresent()
let outputParameterPresent = isOutputParameterPresent() let outputParameterPresent = isOutputParameterPresent()
@ -283,17 +292,17 @@ public class Initializer {
if !outputParameterPresent { if !outputParameterPresent {
SaplingParameterDownloader.downloadOutputParameter(outputURL) { outputResult in SaplingParameterDownloader.downloadOutputParameter(outputURL) { outputResult in
switch outputResult { switch outputResult {
case .failure(let e): case .failure(let error):
result(.failure(e)) result(.failure(error))
case .success: case .success:
guard !spendParameterPresent else { guard !spendParameterPresent else {
result(.success(false)) result(.success(false))
return return
} }
SaplingParameterDownloader.downloadSpendParameter(spendURL) { (spendResult) in SaplingParameterDownloader.downloadSpendParameter(spendURL) { spendResult in
switch spendResult { switch spendResult {
case .failure(let e): case .failure(let error):
result(.failure(e)) result(.failure(error))
case .success: case .success:
result(.success(false)) result(.success(false))
} }
@ -301,10 +310,10 @@ public class Initializer {
} }
} }
} else if !spendParameterPresent { } else if !spendParameterPresent {
SaplingParameterDownloader.downloadSpendParameter(spendURL) { (spendResult) in SaplingParameterDownloader.downloadSpendParameter(spendURL) { spendResult in
switch spendResult { switch spendResult {
case .failure(let e): case .failure(let error):
result(.failure(e)) result(.failure(error))
case .success: case .success:
result(.success(false)) result(.success(false))
} }
@ -314,17 +323,21 @@ public class Initializer {
} }
class CompactBlockProcessorBuilder { class CompactBlockProcessorBuilder {
static func buildProcessor(configuration: CompactBlockProcessor.Configuration, static func buildProcessor(
service: LightWalletService, configuration: CompactBlockProcessor.Configuration,
storage: CompactBlockStorage, service: LightWalletService,
transactionRepository: TransactionRepository, storage: CompactBlockStorage,
accountRepository: AccountRepository, transactionRepository: TransactionRepository,
backend: ZcashRustBackendWelding.Type) -> CompactBlockProcessor { accountRepository: AccountRepository,
return CompactBlockProcessor(service: service, backend: ZcashRustBackendWelding.Type
storage: storage, ) -> CompactBlockProcessor {
backend: backend, return CompactBlockProcessor(
config: configuration, service: service,
repository: transactionRepository, storage: storage,
accountRepository: accountRepository) backend: backend,
config: configuration,
repository: transactionRepository,
accountRepository: accountRepository
)
} }
} }

View File

@ -29,9 +29,9 @@ public struct DefaultResourceProvider: ResourceProvider {
let constants = network.constants let constants = network.constants
do { do {
let url = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let url = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
return url.appendingPathComponent(constants.DEFAULT_DATA_DB_NAME) return url.appendingPathComponent(constants.defaultDataDbName)
} catch { } catch {
return URL(fileURLWithPath: "file://\(constants.DEFAULT_DATA_DB_NAME)") return URL(fileURLWithPath: "file://\(constants.defaultDataDbName)")
} }
} }
@ -40,9 +40,9 @@ public struct DefaultResourceProvider: ResourceProvider {
let constants = network.constants let constants = network.constants
do { do {
let path = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let path = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
return path.appendingPathComponent(constants.DEFAULT_CACHES_DB_NAME) return path.appendingPathComponent(constants.defaultCacheDbName)
} catch { } catch {
return URL(fileURLWithPath: "file://\(constants.DEFAULT_CACHES_DB_NAME)") return URL(fileURLWithPath: "file://\(constants.defaultCacheDbName)")
} }
} }

View File

@ -5,11 +5,10 @@
// Created by Jack Grigg on 5/8/19. // Created by Jack Grigg on 5/8/19.
// Copyright © 2019 Electric Coin Company. All rights reserved. // Copyright © 2019 Electric Coin Company. All rights reserved.
// //
// swiftlint:disable type_body_length
import Foundation import Foundation
class ZcashRustBackend: ZcashRustBackendWelding { class ZcashRustBackend: ZcashRustBackendWelding {
static func lastError() -> RustWeldingError? { static func lastError() -> RustWeldingError? {
guard let message = getLastError() else { return nil } guard let message = getLastError() else { return nil }
zcashlc_clear_last_error() zcashlc_clear_last_error()
@ -127,15 +126,15 @@ class ZcashRustBackend: ZcashRustBackendWelding {
let extpubCStr = [CChar](String(uvk.extpub).utf8CString) let extpubCStr = [CChar](String(uvk.extpub).utf8CString)
let extpubPtr = UnsafeMutablePointer<CChar>.allocate(capacity: extpubCStr.count) let extpubPtr = UnsafeMutablePointer<CChar>.allocate(capacity: extpubCStr.count)
extpubPtr.initialize(from:extpubCStr, count: extpubCStr.count) extpubPtr.initialize(from: extpubCStr, count: extpubCStr.count)
ffiUvks.append(FFIUnifiedViewingKey(extfvk: extfvkPtr, extpub: extpubPtr)) ffiUvks.append(FFIUnifiedViewingKey(extfvk: extfvkPtr, extpub: extpubPtr))
} }
var result = false var result = false
ffiUvks.withContiguousMutableStorageIfAvailable { p in ffiUvks.withContiguousMutableStorageIfAvailable { pointer in
let slice = UnsafeMutablePointer<FFIUVKBoxedSlice>.allocate(capacity: 1) let slice = UnsafeMutablePointer<FFIUVKBoxedSlice>.allocate(capacity: 1)
slice.initialize(to: FFIUVKBoxedSlice(ptr: p.baseAddress, len: UInt(p.count))) slice.initialize(to: FFIUVKBoxedSlice(ptr: pointer.baseAddress, len: UInt(pointer.count)))
result = zcashlc_init_accounts_table_with_keys(dbData.0, dbData.1, slice, networkType.networkId) result = zcashlc_init_accounts_table_with_keys(dbData.0, dbData.1, slice, networkType.networkId)
slice.deinitialize(count: 1) slice.deinitialize(count: 1)
@ -149,10 +148,16 @@ class ZcashRustBackend: ZcashRustBackendWelding {
} }
return result return result
} }
// swiftlint:disable function_parameter_count
static func initBlocksTable(dbData: URL, height: Int32, hash: String, time: UInt32, saplingTree: String, networkType: NetworkType) throws { static func initBlocksTable(
dbData: URL,
height: Int32,
hash: String,
time: UInt32,
saplingTree: String,
networkType: NetworkType
) throws {
let dbData = dbData.osStr() let dbData = dbData.osStr()
guard !hash.containsCStringNullBytesBeforeStringEnding() else { guard !hash.containsCStringNullBytesBeforeStringEnding() else {
@ -163,7 +168,15 @@ class ZcashRustBackend: ZcashRustBackendWelding {
throw RustWeldingError.malformedStringInput throw RustWeldingError.malformedStringInput
} }
guard zcashlc_init_blocks_table(dbData.0, dbData.1, height, [CChar](hash.utf8CString), time, [CChar](saplingTree.utf8CString), networkType.networkId) != 0 else { guard zcashlc_init_blocks_table(
dbData.0,
dbData.1,
height,
[CChar](hash.utf8CString),
time,
[CChar](saplingTree.utf8CString),
networkType.networkId
) != 0 else {
if let error = lastError() { if let error = lastError() {
throw error throw error
} }
@ -315,55 +328,70 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return zcashlc_scan_blocks(dbCache.0, dbCache.1, dbData.0, dbData.1, limit, networkType.networkId) != 0 return zcashlc_scan_blocks(dbCache.0, dbCache.1, dbData.0, dbData.1, limit, networkType.networkId) != 0
} }
static func decryptAndStoreTransaction(dbData: URL, tx: [UInt8], minedHeight: Int32, networkType: NetworkType) -> Bool { static func decryptAndStoreTransaction(dbData: URL, txBytes: [UInt8], minedHeight: Int32, networkType: NetworkType) -> Bool {
let dbData = dbData.osStr() let dbData = dbData.osStr()
return zcashlc_decrypt_and_store_transaction(dbData.0, dbData.1, tx, UInt(tx.count), UInt32(minedHeight), networkType.networkId) != 0 return zcashlc_decrypt_and_store_transaction(dbData.0, dbData.1, txBytes, UInt(txBytes.count), UInt32(minedHeight), networkType.networkId) != 0
} }
// swiftlint:disable function_parameter_count
static func createToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 { static func createToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 {
let dbData = dbData.osStr() let dbData = dbData.osStr()
let memoBytes = memo ?? "" let memoBytes = memo ?? ""
return zcashlc_create_to_address(dbData.0, return zcashlc_create_to_address(
dbData.1, dbData.0,
account, dbData.1,
[CChar](extsk.utf8CString), account,
[CChar](to.utf8CString), [CChar](extsk.utf8CString),
value, [CChar](to.utf8CString),
[CChar](memoBytes.utf8CString), value,
spendParamsPath, [CChar](memoBytes.utf8CString),
UInt(spendParamsPath.lengthOfBytes(using: .utf8)), spendParamsPath,
outputParamsPath, UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
UInt(outputParamsPath.lengthOfBytes(using: .utf8)), outputParamsPath,
networkType.networkId) UInt(outputParamsPath.lengthOfBytes(using: .utf8)),
networkType.networkId
)
} }
static func shieldFunds(dbCache: URL, dbData: URL, account: Int32, tsk: String, extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 { static func shieldFunds(
dbCache: URL,
dbData: URL,
account: Int32,
tsk: String,
extsk: String,
memo: String?,
spendParamsPath: String,
outputParamsPath: String,
networkType: NetworkType
) -> Int64 {
let dbData = dbData.osStr() let dbData = dbData.osStr()
let memoBytes = memo ?? "" let memoBytes = memo ?? ""
return zcashlc_shield_funds(dbData.0, return zcashlc_shield_funds(
dbData.1, dbData.0,
account, dbData.1,
[CChar](tsk.utf8CString), account,
[CChar](extsk.utf8CString), [CChar](tsk.utf8CString),
[CChar](memoBytes.utf8CString), [CChar](extsk.utf8CString),
spendParamsPath, [CChar](memoBytes.utf8CString),
UInt(spendParamsPath.lengthOfBytes(using: .utf8)), spendParamsPath,
outputParamsPath, UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
UInt(outputParamsPath.lengthOfBytes(using: .utf8)), outputParamsPath,
networkType.networkId) UInt(outputParamsPath.lengthOfBytes(using: .utf8)),
networkType.networkId
)
} }
static func deriveExtendedFullViewingKey(_ spendingKey: String, networkType: NetworkType) throws -> String? { static func deriveExtendedFullViewingKey(_ spendingKey: String, networkType: NetworkType) throws -> String? {
guard !spendingKey.containsCStringNullBytesBeforeStringEnding() else { guard !spendingKey.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.malformedStringInput throw RustWeldingError.malformedStringInput
} }
guard let extsk = zcashlc_derive_extended_full_viewing_key([CChar](spendingKey.utf8CString), guard let extsk = zcashlc_derive_extended_full_viewing_key(
networkType.networkId) else { [CChar](spendingKey.utf8CString),
networkType.networkId
) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
} }
@ -377,7 +405,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
} }
static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? {
var capacity = UInt(0); var capacity = UInt(0)
guard let extsksCStr = zcashlc_derive_extended_full_viewing_keys(seed, UInt(seed.count), accounts, &capacity, networkType.networkId) else { guard let extsksCStr = zcashlc_derive_extended_full_viewing_keys(seed, UInt(seed.count), accounts, &capacity, networkType.networkId) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
@ -385,16 +413,17 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return nil return nil
} }
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap({ (cStr) -> String? in let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { (cStr) -> String? in
guard let str = cStr else { return nil } guard let str = cStr else { return nil }
return String(cString: str) return String(cString: str)
}) }
zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity) zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity)
return extsks return extsks
} }
static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? {
var capacity = UInt(0); var capacity = UInt(0)
guard let extsksCStr = zcashlc_derive_extended_spending_keys(seed, UInt(seed.count), accounts, &capacity, networkType.networkId) else { guard let extsksCStr = zcashlc_derive_extended_spending_keys(seed, UInt(seed.count), accounts, &capacity, networkType.networkId) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
@ -402,31 +431,35 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return nil return nil
} }
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap({ (cStr) -> String? in let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { cStr -> String? in
guard let str = cStr else { return nil } guard let str = cStr else { return nil }
return String(cString: str) return String(cString: str)
}) }
zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity) zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity)
return extsks return extsks
} }
static func deriveUnifiedViewingKeyFromSeed(_ seed: [UInt8], numberOfAccounts: Int, networkType: NetworkType) throws -> [UnifiedViewingKey] { static func deriveUnifiedViewingKeyFromSeed(_ seed: [UInt8], numberOfAccounts: Int, networkType: NetworkType) throws -> [UnifiedViewingKey] {
guard let uvksStruct = zcashlc_derive_unified_viewing_keys_from_seed(
guard let uvks_struct = zcashlc_derive_unified_viewing_keys_from_seed(seed, UInt(seed.count), Int32(numberOfAccounts), networkType.networkId) else { seed,
UInt(seed.count),
Int32(numberOfAccounts),
networkType.networkId
) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
} }
throw RustWeldingError.unableToDeriveKeys throw RustWeldingError.unableToDeriveKeys
} }
let uvks_size = uvks_struct.pointee.len let uvksSize = uvksStruct.pointee.len
guard let uvks_array_pointer = uvks_struct.pointee.ptr, uvks_size > 0 else { guard let uvksArrayPointer = uvksStruct.pointee.ptr, uvksSize > 0 else {
throw RustWeldingError.unableToDeriveKeys throw RustWeldingError.unableToDeriveKeys
} }
var uvks = [UnifiedViewingKey]() var uvks = [UnifiedViewingKey]()
for i: Int in 0 ..< Int(uvks_size) { for i: Int in 0 ..< Int(uvksSize) {
let itemPointer = uvks_array_pointer.advanced(by: i) let itemPointer = uvksArrayPointer.advanced(by: i)
guard let extfvk = String(validatingUTF8: itemPointer.pointee.extfvk) else { guard let extfvk = String(validatingUTF8: itemPointer.pointee.extfvk) else {
throw RustWeldingError.unableToDeriveKeys throw RustWeldingError.unableToDeriveKeys
@ -439,12 +472,16 @@ class ZcashRustBackend: ZcashRustBackendWelding {
uvks.append(UVK(extfvk: extfvk, extpub: extpub)) uvks.append(UVK(extfvk: extfvk, extpub: extpub))
} }
zcashlc_free_uvk_array(uvks_struct) zcashlc_free_uvk_array(uvksStruct)
return uvks return uvks
} }
static func deriveShieldedAddressFromSeed(seed: [UInt8], accountIndex: Int32, networkType: NetworkType) throws -> String? { static func deriveShieldedAddressFromSeed(
seed: [UInt8],
accountIndex: Int32,
networkType: NetworkType
) throws -> String? {
guard let zaddrCStr = zcashlc_derive_shielded_address_from_seed(seed, UInt(seed.count), accountIndex, networkType.networkId) else { guard let zaddrCStr = zcashlc_derive_shielded_address_from_seed(seed, UInt(seed.count), accountIndex, networkType.networkId) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
@ -458,12 +495,17 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return zAddr return zAddr
} }
static func deriveShieldedAddressFromViewingKey(_ extfvk: String, networkType: NetworkType) throws -> String? { static func deriveShieldedAddressFromViewingKey(
_ extfvk: String,
networkType: NetworkType
) throws -> String? {
guard !extfvk.containsCStringNullBytesBeforeStringEnding() else { guard !extfvk.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.malformedStringInput throw RustWeldingError.malformedStringInput
} }
guard let zaddrCStr = zcashlc_derive_shielded_address_from_viewing_key(
guard let zaddrCStr = zcashlc_derive_shielded_address_from_viewing_key([CChar](extfvk.utf8CString), networkType.networkId) else { [CChar](extfvk.utf8CString),
networkType.networkId
) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
} }
@ -476,7 +518,12 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return zAddr return zAddr
} }
static func deriveTransparentAddressFromSeed(seed: [UInt8], account: Int, index: Int, networkType: NetworkType) throws -> String? { static func deriveTransparentAddressFromSeed(
seed: [UInt8],
account: Int,
index: Int,
networkType: NetworkType
) throws -> String? {
guard let tAddrCStr = zcashlc_derive_transparent_address_from_seed(seed, UInt(seed.count), Int32(account), Int32(index), networkType.networkId) else { guard let tAddrCStr = zcashlc_derive_transparent_address_from_seed(seed, UInt(seed.count), Int32(account), Int32(index), networkType.networkId) else {
if let error = lastError() { if let error = lastError() {
@ -490,7 +537,12 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return tAddr return tAddr
} }
static func deriveTransparentPrivateKeyFromSeed(seed: [UInt8], account: Int, index: Int, networkType: NetworkType) throws -> String? { static func deriveTransparentPrivateKeyFromSeed(
seed: [UInt8],
account: Int,
index: Int,
networkType: NetworkType
) throws -> String? {
guard let skCStr = zcashlc_derive_transparent_private_key_from_seed(seed, UInt(seed.count), Int32(account), Int32(index), networkType.networkId) else { guard let skCStr = zcashlc_derive_transparent_private_key_from_seed(seed, UInt(seed.count), Int32(account), Int32(index), networkType.networkId) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
@ -502,12 +554,18 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return sk return sk
} }
static func derivedTransparentAddressFromPublicKey(_ pubkey: String, networkType: NetworkType) throws -> String { static func derivedTransparentAddressFromPublicKey(
_ pubkey: String,
networkType: NetworkType
) throws -> String {
guard !pubkey.containsCStringNullBytesBeforeStringEnding() else { guard !pubkey.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.malformedStringInput throw RustWeldingError.malformedStringInput
} }
guard let tAddrCStr = zcashlc_derive_transparent_address_from_public_key([CChar](pubkey.utf8CString), networkType.networkId), let tAddr = String(validatingUTF8: tAddrCStr) else { guard let tAddrCStr = zcashlc_derive_transparent_address_from_public_key(
[CChar](pubkey.utf8CString),
networkType.networkId),
let tAddr = String(validatingUTF8: tAddrCStr) else {
if let error = lastError() { if let error = lastError() {
throw error throw error
} }
@ -541,7 +599,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return branchId return branchId
} }
} }
private struct UVK: UnifiedViewingKey { private struct UVK: UnifiedViewingKey {
@ -551,26 +608,21 @@ private struct UVK: UnifiedViewingKey {
private extension ZcashRustBackend { private extension ZcashRustBackend {
static func throwDataDbError(_ error: RustWeldingError) -> Error { static func throwDataDbError(_ error: RustWeldingError) -> Error {
if case RustWeldingError.genericError(let message) = error, message.contains("is not empty") { if case RustWeldingError.genericError(let message) = error, message.contains("is not empty") {
return RustWeldingError.dataDbNotEmpty return RustWeldingError.dataDbNotEmpty
} }
return RustWeldingError.dataDbInitFailed(message: error.localizedDescription) return RustWeldingError.dataDbInitFailed(message: error.localizedDescription)
} }
} }
private extension URL { private extension URL {
func osStr() -> (String, UInt) { func osStr() -> (String, UInt) {
let path = self.absoluteString let path = self.absoluteString
return (path, UInt(path.lengthOfBytes(using: .utf8))) return (path, UInt(path.lengthOfBytes(using: .utf8)))
} }
} }
extension String { extension String {
/** /**
Checks whether this string contains null bytes before it's real ending Checks whether this string contains null bytes before it's real ending
*/ */

View File

@ -81,7 +81,15 @@ public protocol ZcashRustBackendWelding {
- time: in milliseconds from reference - time: in milliseconds from reference
- saplingTree: hash of the sapling tree - saplingTree: hash of the sapling tree
*/ */
static func initBlocksTable(dbData: URL, height: Int32, hash: String, time: UInt32, saplingTree: String, networkType: NetworkType) throws // swiftlint:disable function_parameter_count
static func initBlocksTable(
dbData: URL,
height: Int32,
hash: String,
time: UInt32,
saplingTree: String,
networkType: NetworkType
) throws
/** /**
gets the address from data db from the given account gets the address from data db from the given account
@ -228,7 +236,10 @@ public protocol ZcashRustBackendWelding {
- minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID. - minedHeight: height on which this transaction was mined. this is used to fetch the consensus branch ID.
returns false if fails to decrypt. returns false if fails to decrypt.
*/ */
static func decryptAndStoreTransaction(dbData: URL, tx: [UInt8], minedHeight: Int32, networkType: NetworkType) -> Bool static func decryptAndStoreTransaction(dbData: URL,
txBytes: [UInt8],
minedHeight: Int32,
networkType: NetworkType) -> Bool
/** /**
Creates a transaction to the given address from the given account Creates a transaction to the given address from the given account
@ -242,7 +253,18 @@ public protocol ZcashRustBackendWelding {
- spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located - spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located
- outputParamsPath: path escaped String for the filesystem locations where the output parameters are located - outputParamsPath: path escaped String for the filesystem locations where the output parameters are located
*/ */
static func createToAddress(dbData: URL, account: Int32, extsk: String, to: String, value: Int64, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 // swiftlint:disable function_parameter_count
static func createToAddress(
dbData: URL,
account: Int32,
extsk: String,
to: String,
value: Int64,
memo: String?,
spendParamsPath: String,
outputParamsPath: String,
networkType: NetworkType
) -> Int64
/** /**
Creates a transaction to shield all found UTXOs in cache db. Creates a transaction to shield all found UTXOs in cache db.
@ -256,7 +278,18 @@ public protocol ZcashRustBackendWelding {
- spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located - spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located
- outputParamsPath: path escaped String for the filesystem locations where the output parameters are located - outputParamsPath: path escaped String for the filesystem locations where the output parameters are located
*/ */
static func shieldFunds(dbCache: URL, dbData: URL, account: Int32, tsk: String, extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String, networkType: NetworkType) -> Int64 // swiftlint:disable function_parameter_count
static func shieldFunds(
dbCache: URL,
dbData: URL,
account: Int32,
tsk: String,
extsk: String,
memo: String?,
spendParamsPath: String,
outputParamsPath: String,
networkType: NetworkType
) -> Int64
/** /**
Derives a full viewing key from a seed Derives a full viewing key from a seed

View File

@ -164,18 +164,31 @@ public class LightWalletGRPCService {
} }
extension LightWalletGRPCService: LightWalletService { extension LightWalletGRPCService: LightWalletService {
@discardableResult public func blockStream(
@discardableResult public func blockStream(startHeight: BlockHeight, endHeight: BlockHeight, result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void, handler: @escaping (ZcashCompactBlock) -> Void, progress: @escaping (BlockProgressReporting) -> Void) -> CancellableCall { startHeight: BlockHeight,
endHeight: BlockHeight,
let future = compactTxStreamer.getBlockRange(BlockRange(startHeight: startHeight, endHeight: endHeight), callOptions: Self.callOptions(timeLimit: self.streamingCallTimeout), handler: { compactBlock in result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
handler: @escaping (ZcashCompactBlock) -> Void,
handler(ZcashCompactBlock(compactBlock: compactBlock)) progress: @escaping (BlockProgressReporting) -> Void
progress(BlockProgress(startHeight: startHeight, targetHeight: endHeight, progressHeight: BlockHeight(compactBlock.height))) ) -> CancellableCall {
let future = compactTxStreamer.getBlockRange(
BlockRange(
startHeight: startHeight,
endHeight: endHeight),
callOptions: Self.callOptions(timeLimit: self.streamingCallTimeout),
handler: { compactBlock in
handler(ZcashCompactBlock(compactBlock: compactBlock))
progress(BlockProgress(
startHeight: startHeight,
targetHeight: endHeight,
progressHeight: BlockHeight(compactBlock.height)
)
)
} }
) )
future.status.whenComplete { r in future.status.whenComplete { completionResult in
switch r { switch completionResult {
case .success(let status): case .success(let status):
switch status.code { switch status.code {
case .ok: case .ok:
@ -195,8 +208,8 @@ extension LightWalletGRPCService: LightWalletService {
} }
public func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void) { public func getInfo(result: @escaping (Result<LightWalletdInfo, LightWalletServiceError>) -> Void) {
compactTxStreamer.getLightdInfo(Empty()).response.whenComplete { r in compactTxStreamer.getLightdInfo(Empty()).response.whenComplete { completionResult in
switch r { switch completionResult {
case .success(let info): case .success(let info):
result(.success(info)) result(.success(info))
case .failure(let error): case .failure(let error):
@ -223,19 +236,17 @@ extension LightWalletGRPCService: LightWalletService {
} }
public func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) { public func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void) {
var txFilter = TxFilter() var txFilter = TxFilter()
txFilter.hash = txId txFilter.hash = txId
compactTxStreamer.getTransaction(txFilter).response.whenComplete({ response in compactTxStreamer.getTransaction(txFilter).response.whenComplete { response in
switch response { switch response {
case .failure(let error): case .failure(let error):
result(.failure(error.mapToServiceError())) result(.failure(error.mapToServiceError()))
case .success(let rawTx): case .success(let rawTx):
result(.success(TransactionBuilder.createTransactionEntity(txId: txId, rawTransaction: rawTx))) result(.success(TransactionBuilder.createTransactionEntity(txId: txId, rawTransaction: rawTx)))
} }
}) }
} }
public func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) { public func submit(spendTransaction: Data, result: @escaping (Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void) {
@ -243,12 +254,12 @@ extension LightWalletGRPCService: LightWalletService {
let tx = try RawTransaction(serializedData: spendTransaction) let tx = try RawTransaction(serializedData: spendTransaction)
let response = self.compactTxStreamer.sendTransaction(tx).response let response = self.compactTxStreamer.sendTransaction(tx).response
response.whenComplete { (responseResult) in response.whenComplete { responseResult in
switch responseResult { switch responseResult {
case .failure(let e): case .failure(let error):
result(.failure(LightWalletServiceError.sentFailed(error: e))) result(.failure(LightWalletServiceError.sentFailed(error: error)))
case .success(let s): case .success(let success):
result(.success(s)) result(.success(success))
} }
} }
} catch { } catch {
@ -257,8 +268,7 @@ extension LightWalletGRPCService: LightWalletService {
} }
public func submit(spendTransaction: Data) throws -> LightWalletServiceResponse { public func submit(spendTransaction: Data) throws -> LightWalletServiceResponse {
let rawTx = RawTransaction.with { raw in
let rawTx = RawTransaction.with { (raw) in
raw.data = spendTransaction raw.data = spendTransaction
} }
do { do {
@ -273,14 +283,15 @@ extension LightWalletGRPCService: LightWalletService {
let response = compactTxStreamer.getBlockRange(range.blockRange(), handler: { let response = compactTxStreamer.getBlockRange(range.blockRange(), handler: {
blocks.append($0) blocks.append($0)
}) }
)
let status = try response.status.wait() let status = try response.status.wait()
switch status.code { switch status.code {
case .ok: case .ok:
return blocks.asZcashCompactBlocks() return blocks.asZcashCompactBlocks()
default: default:
throw LightWalletServiceError.mapCode(status) throw LightWalletServiceError.mapCode(status)
} }
} }
@ -299,13 +310,10 @@ extension LightWalletGRPCService: LightWalletService {
response.whenFailureBlocking(onto: queue) { error in response.whenFailureBlocking(onto: queue) { error in
result(.failure(error.mapToServiceError())) result(.failure(error.mapToServiceError()))
} }
} }
public func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) { public func blockRange(_ range: CompactBlockRange, result: @escaping (Result<[ZcashCompactBlock], LightWalletServiceError>) -> Void) {
queue.async { [weak self] in queue.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
var blocks = [CompactBlock]() var blocks = [CompactBlock]()
@ -321,7 +329,6 @@ extension LightWalletGRPCService: LightWalletService {
default: default:
result(.failure(.mapCode(status))) result(.failure(.mapCode(status)))
} }
} catch { } catch {
result(.failure(error.mapToServiceError())) result(.failure(error.mapToServiceError()))
} }
@ -329,7 +336,6 @@ extension LightWalletGRPCService: LightWalletService {
} }
public func latestBlockHeight() throws -> BlockHeight { public func latestBlockHeight() throws -> BlockHeight {
guard let height = try? latestBlock().compactBlockHeight() else { guard let height = try? latestBlock().compactBlockHeight() else {
throw LightWalletServiceError.invalidBlock throw LightWalletServiceError.invalidBlock
} }
@ -337,20 +343,21 @@ extension LightWalletGRPCService: LightWalletService {
} }
public func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] { public func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
let arg = GetAddressUtxosArg.with { (utxoArgs) in let arg = GetAddressUtxosArg.with { utxoArgs in
utxoArgs.addresses = [tAddress] utxoArgs.addresses = [tAddress]
utxoArgs.startHeight = UInt64(height) utxoArgs.startHeight = UInt64(height)
} }
do { do {
return try self.compactTxStreamer.getAddressUtxos(arg).response.wait().addressUtxos.map { reply in return try self.compactTxStreamer.getAddressUtxos(arg).response.wait().addressUtxos.map { reply in
UTXO(id: nil, UTXO(
address: tAddress, id: nil,
prevoutTxId: reply.txid, address: tAddress,
prevoutIndex: Int(reply.index), prevoutTxId: reply.txid,
script: reply.script, prevoutIndex: Int(reply.index),
valueZat: Int(reply.valueZat), script: reply.script,
height: Int(reply.height), valueZat: Int(reply.valueZat),
spentInTx: nil height: Int(reply.height),
spentInTx: nil
) )
} }
} catch { } catch {
@ -361,21 +368,22 @@ extension LightWalletGRPCService: LightWalletService {
public func fetchUTXOs(for tAddress: String, height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { public func fetchUTXOs(for tAddress: String, height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) {
queue.async { [weak self] in queue.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
let arg = GetAddressUtxosArg.with { (utxoArgs) in let arg = GetAddressUtxosArg.with { utxoArgs in
utxoArgs.addresses = [tAddress] utxoArgs.addresses = [tAddress]
utxoArgs.startHeight = UInt64(height) utxoArgs.startHeight = UInt64(height)
} }
var utxos = [UnspentTransactionOutputEntity]() var utxos = [UnspentTransactionOutputEntity]()
let response = self.compactTxStreamer.getAddressUtxosStream(arg) { (reply) in let response = self.compactTxStreamer.getAddressUtxosStream(arg) { reply in
utxos.append( utxos.append(
UTXO(id: nil, UTXO(
address: tAddress, id: nil,
prevoutTxId: reply.txid, address: tAddress,
prevoutIndex: Int(reply.index), prevoutTxId: reply.txid,
script: reply.script, prevoutIndex: Int(reply.index),
valueZat: Int(reply.valueZat), script: reply.script,
height: Int(reply.height), valueZat: Int(reply.valueZat),
spentInTx: nil height: Int(reply.height),
spentInTx: nil
) )
) )
} }
@ -395,37 +403,37 @@ extension LightWalletGRPCService: LightWalletService {
} }
public func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] { public func fetchUTXOs(for tAddresses: [String], height: BlockHeight) throws -> [UnspentTransactionOutputEntity] {
guard !tAddresses.isEmpty else {
guard tAddresses.count > 0 else {
return [] // FIXME: throw a real error return [] // FIXME: throw a real error
} }
var utxos = [UnspentTransactionOutputEntity]() var utxos = [UnspentTransactionOutputEntity]()
let arg = GetAddressUtxosArg.with { (utxoArgs) in let arg = GetAddressUtxosArg.with { utxoArgs in
utxoArgs.addresses = tAddresses utxoArgs.addresses = tAddresses
utxoArgs.startHeight = UInt64(height) utxoArgs.startHeight = UInt64(height)
} }
utxos.append(contentsOf: utxos.append(
try self.compactTxStreamer.getAddressUtxos(arg).response.wait().addressUtxos.map({ reply in contentsOf:
UTXO(id: nil, try self.compactTxStreamer.getAddressUtxos(arg).response.wait().addressUtxos.map { reply in
address: reply.address, UTXO(
prevoutTxId: reply.txid, id: nil,
prevoutIndex: Int(reply.index), address: reply.address,
script: reply.script, prevoutTxId: reply.txid,
valueZat: Int(reply.valueZat), prevoutIndex: Int(reply.index),
height: Int(reply.height), script: reply.script,
spentInTx: nil) valueZat: Int(reply.valueZat),
}) height: Int(reply.height),
spentInTx: nil
)
}
) )
return utxos return utxos
} }
public func fetchUTXOs(for tAddresses: [String], height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) { public func fetchUTXOs(for tAddresses: [String], height: BlockHeight, result: @escaping (Result<[UnspentTransactionOutputEntity], LightWalletServiceError>) -> Void) {
guard !tAddresses.isEmpty else {
guard tAddresses.count > 0 else {
return result(.success([])) // FIXME: throw a real error return result(.success([])) // FIXME: throw a real error
} }
@ -439,16 +447,20 @@ extension LightWalletGRPCService: LightWalletService {
do { do {
let response = try self.compactTxStreamer.getAddressUtxosStream(args) { reply in let response = try self.compactTxStreamer.getAddressUtxosStream(args) { reply in
utxos.append( utxos.append(
UTXO(id: nil, UTXO(
address: reply.address, id: nil,
prevoutTxId: reply.txid, address: reply.address,
prevoutIndex: Int(reply.index), prevoutTxId: reply.txid,
script: reply.script, prevoutIndex: Int(reply.index),
valueZat: Int(reply.valueZat), script: reply.script,
height: Int(reply.height), valueZat: Int(reply.valueZat),
spentInTx: nil) height: Int(reply.height),
spentInTx: nil
)
) )
}.status.wait() }
.status
.wait()
switch response.code { switch response.code {
case .ok: case .ok:
result(.success(utxos)) result(.success(utxos))
@ -476,7 +488,6 @@ extension Error {
extension LightWalletServiceError { extension LightWalletServiceError {
static func mapCode(_ status: GRPCStatus) -> LightWalletServiceError { static func mapCode(_ status: GRPCStatus) -> LightWalletServiceError {
switch status.code { switch status.code {
case .ok: case .ok:
return LightWalletServiceError.unknown return LightWalletServiceError.unknown
case .cancelled: case .cancelled:
@ -499,8 +510,9 @@ class ConnectionStatusManager: ConnectivityStateDelegate {
name: .blockProcessorConnectivityStateChanged, name: .blockProcessorConnectivityStateChanged,
object: self, object: self,
userInfo: [ userInfo: [
CompactBlockProcessorNotificationKey.currentConnectivityStatus : newState, CompactBlockProcessorNotificationKey.currentConnectivityStatus: newState,
CompactBlockProcessorNotificationKey.previousConnectivityStatus : oldState CompactBlockProcessorNotificationKey.previousConnectivityStatus: oldState
]) ]
)
} }
} }

View File

@ -32,6 +32,8 @@ public protocol BlockProgressReporting {
} }
extension LightWalletServiceError: Equatable { extension LightWalletServiceError: Equatable {
// swiftlint:disable cyclomatic_complexity
// swiftlint:disable identifier_name
public static func == (lhs: Self, rhs: Self) -> Bool { public static func == (lhs: Self, rhs: Self) -> Bool {
switch lhs { switch lhs {
case .generalError(let m): case .generalError(let m):
@ -74,21 +76,21 @@ extension LightWalletServiceError: Equatable {
return false return false
} }
case .criticalError: case .criticalError:
switch rhs { switch rhs {
case .criticalError: case .criticalError:
return true return true
default: default:
return false return false
} }
case .userCancelled: case .userCancelled:
switch rhs { switch rhs {
case .userCancelled: case .userCancelled:
return true return true
default: default:
return false return false
} }
case .unknown: case .unknown:
switch rhs { switch rhs {
case .unknown: case .unknown:
return true return true
default: default:
@ -121,7 +123,7 @@ public protocol LightWalletService {
- Parameter result: a result containing the height or an Error - Parameter result: a result containing the height or an Error
*/ */
func latestBlockHeight(result: @escaping (Result<BlockHeight,LightWalletServiceError>) -> Void) func latestBlockHeight(result: @escaping (Result<BlockHeight, LightWalletServiceError>) -> Void)
/** /**
Return the latest block height known to the service. Return the latest block height known to the service.
@ -150,13 +152,19 @@ public protocol LightWalletService {
*/ */
func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock] func blockRange(_ range: CompactBlockRange) throws -> [ZcashCompactBlock]
@discardableResult func blockStream(startHeight: BlockHeight, endHeight: BlockHeight, result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void, handler: @escaping (ZcashCompactBlock) -> Void, progress: @escaping (BlockProgressReporting) -> Void) -> CancellableCall @discardableResult func blockStream(
startHeight: BlockHeight,
endHeight: BlockHeight,
result: @escaping (Result<GRPCResult, LightWalletServiceError>) -> Void,
handler: @escaping (ZcashCompactBlock) -> Void,
progress: @escaping (BlockProgressReporting) -> Void
) -> CancellableCall
/** /**
Submits a raw transaction over lightwalletd. Non-Blocking Submits a raw transaction over lightwalletd. Non-Blocking
- Parameter spendTransaction: data representing the transaction to be sent - Parameter spendTransaction: data representing the transaction to be sent
- Parameter result: escaping closure that takes a result containing either LightWalletServiceResponse or LightWalletServiceError - Parameter result: escaping closure that takes a result containing either LightWalletServiceResponse or LightWalletServiceError
*/ */
func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse,LightWalletServiceError>) -> Void) func submit(spendTransaction: Data, result: @escaping(Result<LightWalletServiceResponse, LightWalletServiceError>) -> Void)
/** /**
Submits a raw transaction over lightwalletd. Blocking Submits a raw transaction over lightwalletd. Blocking
@ -183,7 +191,7 @@ public protocol LightWalletService {
- Throws: LightWalletServiceError - Throws: LightWalletServiceError
- Returns: LightWalletServiceResponse - Returns: LightWalletServiceResponse
*/ */
func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity,LightWalletServiceError>) -> Void) func fetchTransaction(txId: Data, result: @escaping (Result<TransactionEntity, LightWalletServiceError>) -> Void)
func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity] func fetchUTXOs(for tAddress: String, height: BlockHeight) throws -> [UnspentTransactionOutputEntity]

View File

@ -11,6 +11,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php . // file COPYING or https://www.opensource.org/licenses/mit-license.php .
// swiftlint:disable all
import Foundation import Foundation
import SwiftProtobuf import SwiftProtobuf

View File

@ -11,6 +11,7 @@
// Distributed under the MIT software license, see the accompanying // Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php . // file COPYING or https://www.opensource.org/licenses/mit-license.php .
// swiftlint:disable all
import Foundation import Foundation
import SwiftProtobuf import SwiftProtobuf

View File

@ -51,22 +51,46 @@ class PersistentTransactionManager: OutboundTransactionManager {
guard let self = self else { return } guard let self = self else { return }
let derivationTool = DerivationTool(networkType: self.network) let derivationTool = DerivationTool(networkType: self.network)
guard let vk = try? derivationTool.deriveViewingKey(spendingKey: spendingKey), guard let viewingKey = try? derivationTool.deriveViewingKey(spendingKey: spendingKey),
let zAddr = try? derivationTool.deriveShieldedAddress(viewingKey: vk) else { let zAddr = try? derivationTool.deriveShieldedAddress(viewingKey: viewingKey) else {
result(.failure(TransactionManagerError.shieldingEncodingFailed(tx: pendingTransaction, reason: "There was an error Deriving your keys"))) result(
return .failure(
TransactionManagerError.shieldingEncodingFailed(
tx: pendingTransaction,
reason: "There was an error Deriving your keys")
)
)
return
} }
guard pendingTransaction.toAddress == zAddr else { guard pendingTransaction.toAddress == zAddr else {
result(.failure(TransactionManagerError.shieldingEncodingFailed(tx: pendingTransaction, reason: "the recipient address does not match your derived shielded address. Shielding transactions addresses must match the ones derived from your keys. This is a serious error. We are not letting you encode this shielding transaction because it can lead to loss of funds"))) result(
.failure(
TransactionManagerError.shieldingEncodingFailed(
tx: pendingTransaction,
reason: """
the recipient address does not match your
derived shielded address. Shielding transactions
addresses must match the ones derived from your keys.
This is a serious error. We are not letting you encode
this shielding transaction because it can lead to loss
of funds
"""
)
)
)
return return
} }
do { do {
let encodedTransaction = try self.encoder.createShieldingTransaction(spendingKey: spendingKey, tSecretKey: tsk, memo: pendingTransaction.memo?.asZcashTransactionMemo(), from: pendingTransaction.accountIndex) let encodedTransaction = try self.encoder.createShieldingTransaction(
spendingKey: spendingKey,
tSecretKey: tsk,
memo: pendingTransaction.memo?.asZcashTransactionMemo(),
from: pendingTransaction.accountIndex)
let transaction = try self.encoder.expandEncodedTransaction(encodedTransaction) let transaction = try self.encoder.expandEncodedTransaction(encodedTransaction)
var pending = pendingTransaction var pending = pendingTransaction
pending.encodeAttempts = pending.encodeAttempts + 1 pending.encodeAttempts += 1
pending.raw = encodedTransaction.raw pending.raw = encodedTransaction.raw
pending.rawTransactionId = encodedTransaction.transactionId pending.rawTransactionId = encodedTransaction.transactionId
pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty() pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty()
@ -85,16 +109,23 @@ class PersistentTransactionManager: OutboundTransactionManager {
} }
} }
func encode(spendingKey: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) { func encode(spendingKey: String,
pendingTransaction: PendingTransactionEntity,
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
queue.async { [weak self] in queue.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
do { do {
let encodedTransaction = try self.encoder.createTransaction(spendingKey: spendingKey, zatoshi: pendingTransaction.value, to: pendingTransaction.toAddress, memo: pendingTransaction.memo?.asZcashTransactionMemo(), from: pendingTransaction.accountIndex) let encodedTransaction = try self.encoder.createTransaction(
spendingKey: spendingKey,
zatoshi: pendingTransaction.value,
to: pendingTransaction.toAddress,
memo: pendingTransaction.memo?.asZcashTransactionMemo(),
from: pendingTransaction.accountIndex)
let transaction = try self.encoder.expandEncodedTransaction(encodedTransaction) let transaction = try self.encoder.expandEncodedTransaction(encodedTransaction)
var pending = pendingTransaction var pending = pendingTransaction
pending.encodeAttempts = pending.encodeAttempts + 1 pending.encodeAttempts += 1
pending.raw = encodedTransaction.raw pending.raw = encodedTransaction.raw
pending.rawTransactionId = encodedTransaction.transactionId pending.rawTransactionId = encodedTransaction.transactionId
pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty() pending.expiryHeight = transaction.expiryHeight ?? BlockHeight.empty()
@ -120,8 +151,8 @@ class PersistentTransactionManager: OutboundTransactionManager {
} }
} }
func submit(pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) { func submit(pendingTransaction: PendingTransactionEntity,
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) {
guard let txId = pendingTransaction.id else { guard let txId = pendingTransaction.id else {
result(.failure(TransactionManagerError.notPending(tx: pendingTransaction)))// this transaction is not stored result(.failure(TransactionManagerError.notPending(tx: pendingTransaction)))// this transaction is not stored
return return
@ -149,13 +180,13 @@ class PersistentTransactionManager: OutboundTransactionManager {
} }
let response = try self.service.submit(spendTransaction: raw) let response = try self.service.submit(spendTransaction: raw)
let tx = try self.update(transaction: storedTx, on: response) let transaction = try self.update(transaction: storedTx, on: response)
guard response.errorCode >= 0 else { guard response.errorCode >= 0 else {
result(.failure(TransactionManagerError.submitFailed(tx: tx, errorCode: Int(response.errorCode)))) result(.failure(TransactionManagerError.submitFailed(tx: transaction, errorCode: Int(response.errorCode))))
return return
} }
result(.success(tx)) result(.success(transaction))
} catch { } catch {
try? self.updateOnFailure(tx: pendingTransaction, error: error) try? self.updateOnFailure(tx: pendingTransaction, error: error)
result(.failure(error)) result(.failure(error))
@ -191,8 +222,8 @@ class PersistentTransactionManager: OutboundTransactionManager {
return return
} }
try affectedTxs.map { (tx) -> PendingTransactionEntity in try affectedTxs.map { (transaction) -> PendingTransactionEntity in
var updatedTx = tx var updatedTx = transaction
updatedTx.minedHeight = -1 updatedTx.minedHeight = -1
return updatedTx return updatedTx
} .forEach({ try self.repository.update($0) }) } .forEach({ try self.repository.update($0) })
@ -227,13 +258,13 @@ class PersistentTransactionManager: OutboundTransactionManager {
} }
private func update(transaction: PendingTransactionEntity, on sendResponse: LightWalletServiceResponse) throws -> PendingTransactionEntity { private func update(transaction: PendingTransactionEntity, on sendResponse: LightWalletServiceResponse) throws -> PendingTransactionEntity {
var tx = transaction var pendingTx = transaction
tx.submitAttempts = tx.submitAttempts + 1 pendingTx.submitAttempts += 1
let error = sendResponse.errorCode < 0 let error = sendResponse.errorCode < 0
tx.errorCode = error ? Int(sendResponse.errorCode) : nil pendingTx.errorCode = error ? Int(sendResponse.errorCode) : nil
tx.errorMessage = error ? sendResponse.errorMessage : nil pendingTx.errorMessage = error ? sendResponse.errorMessage : nil
try repository.update(tx) try repository.update(pendingTx)
return tx return pendingTx
} }
func delete(pendingTransaction: PendingTransactionEntity) throws { func delete(pendingTransaction: PendingTransactionEntity) throws {
@ -243,18 +274,20 @@ class PersistentTransactionManager: OutboundTransactionManager {
throw TransactionManagerError.notPending(tx: pendingTransaction) throw TransactionManagerError.notPending(tx: pendingTransaction)
} }
} }
} }
class OutboundTransactionManagerBuilder { enum OutboundTransactionManagerBuilder {
static func build(initializer: Initializer) throws -> OutboundTransactionManager { static func build(initializer: Initializer) throws -> OutboundTransactionManager {
return PersistentTransactionManager(encoder: TransactionEncoderbuilder.build(initializer: initializer), service: initializer.lightWalletService, repository: try PendingTransactionRepositoryBuilder.build(initializer: initializer), networkType: initializer.network.networkType) PersistentTransactionManager(
encoder: TransactionEncoderbuilder.build(initializer: initializer),
service: initializer.lightWalletService,
repository: try PendingTransactionRepositoryBuilder.build(initializer: initializer),
networkType: initializer.network.networkType
)
} }
} }
class PendingTransactionRepositoryBuilder { enum PendingTransactionRepositoryBuilder {
static func build(initializer: Initializer) throws -> PendingTransactionRepository { static func build(initializer: Initializer) throws -> PendingTransactionRepository {
let dao = PendingTransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: initializer.pendingDbURL.path, readonly: false)) let dao = PendingTransactionSQLDAO(dbProvider: SimpleConnectionProvider(path: initializer.pendingDbURL.path, readonly: false))
try dao.createrTableIfNeeded() try dao.createrTableIfNeeded()
@ -262,7 +295,7 @@ class PendingTransactionRepositoryBuilder {
} }
} }
class TransactionEncoderbuilder { enum TransactionEncoderbuilder {
static func build(initializer: Initializer) -> TransactionEncoder { static func build(initializer: Initializer) -> TransactionEncoder {
WalletTransactionEncoder(initializer: initializer) WalletTransactionEncoder(initializer: initializer)
} }

View File

@ -7,11 +7,11 @@
import Foundation import Foundation
typealias TransactionEncoderResultBlock = (_ result: Result<EncodedTransaction,Error>) -> Void typealias TransactionEncoderResultBlock = (_ result: Result<EncodedTransaction, Error>) -> Void
public enum TransactionEncoderError: Error { public enum TransactionEncoderError: Error {
case notFound(transactionId: Int) case notFound(transactionId: Int)
case NotEncoded(transactionId: Int) case notEncoded(transactionId: Int)
case missingParams case missingParams
case spendingKeyWrongNetwork case spendingKeyWrongNetwork
case couldNotExpand(txId: Data) case couldNotExpand(txId: Data)
@ -34,7 +34,13 @@ protocol TransactionEncoder {
- Throws: a TransactionEncoderError - Throws: a TransactionEncoderError
*/ */
func createTransaction(spendingKey: String, zatoshi: Int, to: String, memo: String?, from accountIndex: Int) throws -> EncodedTransaction func createTransaction(
spendingKey: String,
zatoshi: Int,
to: String,
memo: String?,
from accountIndex: Int
) throws -> EncodedTransaction
/** /**
Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
@ -50,7 +56,14 @@ protocol TransactionEncoder {
- Parameter accountIndex: index of the account that will be used to send the funds - Parameter accountIndex: index of the account that will be used to send the funds
- Parameter result: a non escaping closure that receives a Result containing either an EncodedTransaction or a TransactionEncoderError - Parameter result: a non escaping closure that receives a Result containing either an EncodedTransaction or a TransactionEncoderError
*/ */
func createTransaction(spendingKey: String, zatoshi: Int, to: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock) func createTransaction(
spendingKey: String,
zatoshi: Int,
to: String,
memo: String?,
from accountIndex: Int,
result: @escaping TransactionEncoderResultBlock
)
/** /**
Creates a transaction that will attempt to shield transparent funds that are present on the cacheDB .throwing an exception whenever things are missing. When the provided wallet implementation Creates a transaction that will attempt to shield transparent funds that are present on the cacheDB .throwing an exception whenever things are missing. When the provided wallet implementation
@ -66,7 +79,12 @@ protocol TransactionEncoder {
- Throws: a TransactionEncoderError - Throws: a TransactionEncoderError
*/ */
func createShieldingTransaction(spendingKey: String, tSecretKey: String, memo: String?, from accountIndex: Int) throws -> EncodedTransaction func createShieldingTransaction(
spendingKey: String,
tSecretKey: String,
memo: String?,
from accountIndex: Int
) throws -> EncodedTransaction
/** /**
Creates a transaction that will attempt to shield transparent funds that are present on the cacheDB .throwing an exception whenever things are missing. When the provided wallet implementation Creates a transaction that will attempt to shield transparent funds that are present on the cacheDB .throwing an exception whenever things are missing. When the provided wallet implementation
@ -83,7 +101,13 @@ protocol TransactionEncoder {
- Returns: a TransactionEncoderResultBlock - Returns: a TransactionEncoderResultBlock
*/ */
func createShieldingTransaction(spendingKey: String, tSecretKey: String, memo: String?, from accountIndex: Int, result: @escaping TransactionEncoderResultBlock) func createShieldingTransaction(
spendingKey: String,
tSecretKey: String,
memo: String?,
from accountIndex: Int,
result: @escaping TransactionEncoderResultBlock
)
/** /**
Fetch the Transaction Entity from the encoded representation Fetch the Transaction Entity from the encoded representation

View File

@ -161,9 +161,9 @@ class WalletTransactionEncoder: TransactionEncoder {
- Throws: a TransactionEncoderError - Throws: a TransactionEncoderError
*/ */
func expandEncodedTransaction(_ encodedTransaction: EncodedTransaction) throws -> TransactionEntity { func expandEncodedTransaction(_ encodedTransaction: EncodedTransaction) throws -> TransactionEntity {
guard let t = try? repository.findBy(rawId: encodedTransaction.transactionId) else { guard let transaction = try? repository.findBy(rawId: encodedTransaction.transactionId) else {
throw TransactionEncoderError.couldNotExpand(txId: encodedTransaction.transactionId) throw TransactionEncoderError.couldNotExpand(txId: encodedTransaction.transactionId)
} }
return t return transaction
} }
} }

View File

@ -98,9 +98,9 @@ public extension Notification.Name {
/** /**
Synchronizer implementation for UIKit and iOS 12+ Synchronizer implementation for UIKit and iOS 12+
*/ */
// swiftlint:disable type_body_length
public class SDKSynchronizer: Synchronizer { public class SDKSynchronizer: Synchronizer {
public enum NotificationKeys {
public struct NotificationKeys {
public static let progress = "SDKSynchronizer.progress" public static let progress = "SDKSynchronizer.progress"
public static let blockHeight = "SDKSynchronizer.blockHeight" public static let blockHeight = "SDKSynchronizer.blockHeight"
public static let blockDate = "SDKSynchronizer.blockDate" public static let blockDate = "SDKSynchronizer.blockDate"
@ -135,22 +135,23 @@ public class SDKSynchronizer: Synchronizer {
- Parameter initializer: a wallet Initializer object - Parameter initializer: a wallet Initializer object
*/ */
public convenience init(initializer: Initializer) throws { public convenience init(initializer: Initializer) throws {
try self.init(
try self.init(status: .unprepared, status: .unprepared,
initializer: initializer, initializer: initializer,
transactionManager: try OutboundTransactionManagerBuilder.build(initializer: initializer), transactionManager: try OutboundTransactionManagerBuilder.build(initializer: initializer),
transactionRepository: initializer.transactionRepository, transactionRepository: initializer.transactionRepository,
utxoRepository: try UTXORepositoryBuilder.build(initializer: initializer), utxoRepository: try UTXORepositoryBuilder.build(initializer: initializer),
blockProcessor: CompactBlockProcessor(initializer: initializer)) blockProcessor: CompactBlockProcessor(initializer: initializer))
} }
init(status: SyncStatus, init(
initializer: Initializer, status: SyncStatus,
transactionManager: OutboundTransactionManager, initializer: Initializer,
transactionRepository: TransactionRepository, transactionManager: OutboundTransactionManager,
utxoRepository: UnspentTransactionOutputRepository, transactionRepository: TransactionRepository,
blockProcessor: CompactBlockProcessor) throws { utxoRepository: UnspentTransactionOutputRepository,
blockProcessor: CompactBlockProcessor
) throws {
self.connectionState = .idle self.connectionState = .idle
self.status = status self.status = status
self.initializer = initializer self.initializer = initializer
@ -183,7 +184,6 @@ public class SDKSynchronizer: Synchronizer {
- Throws: CompactBlockProcessorError when failures occur - Throws: CompactBlockProcessorError when failures occur
*/ */
public func start(retry: Bool = false) throws { public func start(retry: Bool = false) throws {
switch status { switch status {
case .unprepared: case .unprepared:
throw SynchronizerError.notPrepared throw SynchronizerError.notPrepared
@ -195,7 +195,7 @@ public class SDKSynchronizer: Synchronizer {
// assert(false,"warning: synchronizer started when already started") // TODO: remove this assertion sometime in the near future // assert(false,"warning: synchronizer started when already started") // TODO: remove this assertion sometime in the near future
LoggerProxy.warn("warning: synchronizer started when already started") LoggerProxy.warn("warning: synchronizer started when already started")
return return
case .stopped, .synced,.disconnected, .error: case .stopped, .synced, .disconnected, .error:
do { do {
try blockProcessor.start(retry: retry) try blockProcessor.start(retry: retry)
} catch { } catch {
@ -208,7 +208,6 @@ public class SDKSynchronizer: Synchronizer {
Stops the synchronizer Stops the synchronizer
*/ */
public func stop() { public func stop() {
guard status != .stopped, status != .disconnected else { guard status != .stopped, status != .disconnected else {
LoggerProxy.info("attempted to stop when status was: \(status)") LoggerProxy.info("attempted to stop when status was: \(status)")
return return
@ -221,74 +220,103 @@ public class SDKSynchronizer: Synchronizer {
private func subscribeToProcessorNotifications(_ processor: CompactBlockProcessor) { private func subscribeToProcessorNotifications(_ processor: CompactBlockProcessor) {
let center = NotificationCenter.default let center = NotificationCenter.default
center.addObserver(self, center.addObserver(
selector: #selector(processorUpdated(_:)), self,
name: Notification.Name.blockProcessorUpdated, selector: #selector(processorUpdated(_:)),
object: processor) name: Notification.Name.blockProcessorUpdated,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorStartedDownloading(_:)), self,
name: Notification.Name.blockProcessorStartedDownloading, selector: #selector(processorStartedDownloading(_:)),
object: processor) name: Notification.Name.blockProcessorStartedDownloading,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorStartedValidating(_:)), self,
name: Notification.Name.blockProcessorStartedValidating, selector: #selector(processorStartedValidating(_:)),
object: processor) name: Notification.Name.blockProcessorStartedValidating,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorStartedScanning(_:)), self,
name: Notification.Name.blockProcessorStartedScanning, selector: #selector(processorStartedScanning(_:)),
object: processor) name: Notification.Name.blockProcessorStartedScanning,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorStartedEnhancing(_:)), self,
name: Notification.Name.blockProcessorStartedEnhancing, selector: #selector(processorStartedEnhancing(_:)),
object: processor) name: Notification.Name.blockProcessorStartedEnhancing,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorStartedFetching(_:)), self,
name: Notification.Name.blockProcessorStartedFetching, selector: #selector(processorStartedFetching(_:)),
object: processor) name: Notification.Name.blockProcessorStartedFetching,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorStopped(_:)), self,
name: Notification.Name.blockProcessorStopped, selector: #selector(processorStopped(_:)),
object: processor) name: Notification.Name.blockProcessorStopped,
object: processor
)
center.addObserver(self, selector: #selector(processorFailed(_:)), center.addObserver(
name: Notification.Name.blockProcessorFailed, self,
object: processor) selector: #selector(processorFailed(_:)),
name: Notification.Name.blockProcessorFailed,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorIdle(_:)), self,
name: Notification.Name.blockProcessorIdle, selector: #selector(processorIdle(_:)),
object: processor) name: Notification.Name.blockProcessorIdle,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorFinished(_:)), self,
name: Notification.Name.blockProcessorFinished, selector: #selector(processorFinished(_:)),
object: processor) name: Notification.Name.blockProcessorFinished,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(processorTransitionUnknown(_:)), self,
name: Notification.Name.blockProcessorUnknownTransition, selector: #selector(processorTransitionUnknown(_:)),
object: processor) name: Notification.Name.blockProcessorUnknownTransition,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(reorgDetected(_:)), self,
name: Notification.Name.blockProcessorHandledReOrg, selector: #selector(reorgDetected(_:)),
object: processor) name: Notification.Name.blockProcessorHandledReOrg,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(transactionsFound(_:)), self,
name: Notification.Name.blockProcessorFoundTransactions, selector: #selector(transactionsFound(_:)),
object: processor) name: Notification.Name.blockProcessorFoundTransactions,
object: processor
)
center.addObserver(self, center.addObserver(
selector: #selector(connectivityStateChanged(_:)), self,
name: Notification.Name.blockProcessorConnectivityStateChanged, selector: #selector(connectivityStateChanged(_:)),
object: nil) name: Notification.Name.blockProcessorConnectivityStateChanged,
object: nil
)
} }
// MARK: Block Processor notifications // MARK: Block Processor notifications
@ -304,9 +332,10 @@ public class SDKSynchronizer: Synchronizer {
name: .synchronizerConnectionStateChanged, name: .synchronizerConnectionStateChanged,
object: self, object: self,
userInfo: [ userInfo: [
NotificationKeys.previousConnectionState : ConnectionState(previous), NotificationKeys.previousConnectionState: ConnectionState(previous),
NotificationKeys.currentConnectionState : currentState NotificationKeys.currentConnectionState: currentState
]) ]
)
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
self?.connectionState = currentState self?.connectionState = currentState
@ -318,7 +347,13 @@ public class SDKSynchronizer: Synchronizer {
let foundTransactions = userInfo[CompactBlockProcessorNotificationKey.foundTransactions] as? [ConfirmedTransactionEntity] else { let foundTransactions = userInfo[CompactBlockProcessorNotificationKey.foundTransactions] as? [ConfirmedTransactionEntity] else {
return return
} }
NotificationCenter.default.post(name: .synchronizerFoundTransactions, object: self, userInfo: [ NotificationKeys.foundTransactions : foundTransactions]) NotificationCenter.default.post(
name: .synchronizerFoundTransactions,
object: self,
userInfo: [
NotificationKeys.foundTransactions: foundTransactions
]
)
} }
@objc func reorgDetected(_ notification: Notification) { @objc func reorgDetected(_ notification: Notification) {
@ -388,14 +423,16 @@ public class SDKSynchronizer: Synchronizer {
} }
@objc func processorFailed(_ notification: Notification) { @objc func processorFailed(_ notification: Notification) {
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
if let error = notification.userInfo?[CompactBlockProcessorNotificationKey.error] as? Error { if let error = notification.userInfo?[CompactBlockProcessorNotificationKey.error] as? Error {
self.notifyFailure(error) self.notifyFailure(error)
self.status = .error(self.mapError(error)) self.status = .error(self.mapError(error))
} else { } else {
self.notifyFailure(CompactBlockProcessorError.generalError(message: "This is strange. processorFailed Call received no error message")) self.notifyFailure(
CompactBlockProcessorError.generalError(
message: "This is strange. processorFailed Call received no error message")
)
self.status = .error(SynchronizerError.generalError(message: "This is strange. processorFailed Call received no error message")) self.status = .error(SynchronizerError.generalError(message: "This is strange. processorFailed Call received no error message"))
} }
} }
@ -426,14 +463,27 @@ public class SDKSynchronizer: Synchronizer {
} }
// MARK: Synchronizer methods // MARK: Synchronizer methods
// swiftlint:disable type_body_length
public func sendToAddress(spendingKey: String, zatoshi: Int64, toAddress: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void) { public func sendToAddress(
spendingKey: String,
zatoshi: Int64,
toAddress: String,
memo: String?,
from accountIndex: Int,
resultBlock: @escaping (Result<PendingTransactionEntity, Error>
) -> Void) {
initializer.downloadParametersIfNeeded { (downloadResult) in initializer.downloadParametersIfNeeded { (downloadResult) in
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
switch downloadResult { switch downloadResult {
case .success: case .success:
self?.createToAddress(spendingKey: spendingKey, zatoshi: zatoshi, toAddress: toAddress, memo: memo, from: accountIndex, resultBlock: resultBlock) self?.createToAddress(
spendingKey: spendingKey,
zatoshi: zatoshi,
toAddress: toAddress,
memo: memo,
from: accountIndex,
resultBlock: resultBlock
)
case .failure(let error): case .failure(let error):
resultBlock(.failure(SynchronizerError.parameterMissing(underlyingError: error))) resultBlock(.failure(SynchronizerError.parameterMissing(underlyingError: error)))
} }
@ -441,8 +491,13 @@ public class SDKSynchronizer: Synchronizer {
} }
} }
public func shieldFunds(spendingKey: String, transparentSecretKey: String, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void) { public func shieldFunds(
spendingKey: String,
transparentSecretKey: String,
memo: String?,
from accountIndex: Int,
resultBlock: @escaping (Result<PendingTransactionEntity, Error>
) -> Void) {
// let's see if there are funds to shield // let's see if there are funds to shield
let derivationTool = DerivationTool(networkType: self.network.networkType) let derivationTool = DerivationTool(networkType: self.network.networkType)
@ -455,8 +510,8 @@ public class SDKSynchronizer: Synchronizer {
resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds)) resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds))
return return
} }
let vk = try derivationTool.deriveViewingKey(spendingKey: spendingKey) let viewingKey = try derivationTool.deriveViewingKey(spendingKey: spendingKey)
let zAddr = try derivationTool.deriveShieldedAddress(viewingKey: vk) let zAddr = try derivationTool.deriveShieldedAddress(viewingKey: viewingKey)
let shieldingSpend = try transactionManager.initSpend(zatoshi: Int(tBalance.verified), toAddress: zAddr, memo: memo, from: 0) let shieldingSpend = try transactionManager.initSpend(zatoshi: Int(tBalance.verified), toAddress: zAddr, memo: memo, from: 0)
@ -564,7 +619,7 @@ public class SDKSynchronizer: Synchronizer {
return return
} }
initializer.lightWalletService.fetchUTXOs(for: address, height: network.constants.SAPLING_ACTIVATION_HEIGHT, result: { [weak self] r in initializer.lightWalletService.fetchUTXOs(for: address, height: network.constants.saplingActivationHeight, result: { [weak self] r in
guard let self = self else { return } guard let self = self else { return }
switch r { switch r {
case .success(let utxos): case .success(let utxos):
@ -720,7 +775,7 @@ public class SDKSynchronizer: Synchronizer {
let latestHeight = try transactionRepository.lastScannedHeight() let latestHeight = try transactionRepository.lastScannedHeight()
try transactionManager.allPendingTransactions()?.filter( { try transactionManager.allPendingTransactions()?.filter( {
$0.minedHeight > 0 && abs($0.minedHeight - latestHeight) >= ZcashSDK.DEFAULT_STALE_TOLERANCE } $0.minedHeight > 0 && abs($0.minedHeight - latestHeight) >= ZcashSDK.defaultStaleTolerance }
).forEach( { ).forEach( {
try transactionManager.delete(pendingTransaction: $0) try transactionManager.delete(pendingTransaction: $0)
} ) } )

View File

@ -45,8 +45,8 @@ public class SaplingParameterDownloader {
private static func downloadFileWithRequest(_ request: URLRequest, at destination: URL, result: @escaping (Result<URL,Error>) -> Void) { private static func downloadFileWithRequest(_ request: URLRequest, at destination: URL, result: @escaping (Result<URL,Error>) -> Void) {
let task = URLSession.shared.downloadTask(with: request) { (url, _, error) in let task = URLSession.shared.downloadTask(with: request) { (url, _, error) in
if let e = error { if let error = error {
result(.failure(Errors.failed(error: e))) result(.failure(Errors.failed(error: error)))
return return
} else if let localUrl = url { } else if let localUrl = url {
do { do {
@ -110,10 +110,10 @@ public class SaplingParameterDownloader {
} }
public static var spendParamsURLString: String { public static var spendParamsURLString: String {
return ZcashSDK.CLOUD_PARAM_DIR_URL + ZcashSDK.SPEND_PARAM_FILE_NAME return ZcashSDK.cloudParameterURL + ZcashSDK.spendParamFilename
} }
public static var outputParamsURLString: String { public static var outputParamsURLString: String {
return ZcashSDK.CLOUD_PARAM_DIR_URL + ZcashSDK.OUTPUT_PARAM_FILE_NAME return ZcashSDK.cloudParameterURL + ZcashSDK.outputParamFilename
} }
} }

View File

@ -28,14 +28,14 @@ class BlockBatchValidationTests: XCTestCase {
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default))
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.SAPLING_ACTIVATION_HEIGHT, network: network) let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
info.chainName = "main" info.chainName = "main"
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db33f" info.consensusBranchID = "d34db33f"
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
@ -68,14 +68,14 @@ class BlockBatchValidationTests: XCTestCase {
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default))
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.SAPLING_ACTIVATION_HEIGHT, network: network) let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
info.chainName = "test" info.chainName = "test"
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
@ -109,14 +109,14 @@ class BlockBatchValidationTests: XCTestCase {
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default))
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.SAPLING_ACTIVATION_HEIGHT, network: network) let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
info.chainName = "another" info.chainName = "another"
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
@ -149,7 +149,7 @@ class BlockBatchValidationTests: XCTestCase {
let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default)) let service = MockLightWalletService(latestBlockHeight: 1210000, service: LightWalletGRPCService(endpoint: LightWalletEndpointBuilder.default))
let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000) let repository = ZcashConsoleFakeStorage(latestBlockHeight: 1220000)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.SAPLING_ACTIVATION_HEIGHT, network: network) let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = 130000 info.blockHeight = 130000
info.branch = "d34db33f" info.branch = "d34db33f"
@ -171,7 +171,7 @@ class BlockBatchValidationTests: XCTestCase {
operation.errorHandler = { error in operation.errorHandler = { error in
expectation.fulfill() expectation.fulfill()
switch error { switch error {
case CompactBlockProcessorError.saplingActivationMismatch(expected: network.constants.SAPLING_ACTIVATION_HEIGHT, found: BlockHeight(info.saplingActivationHeight)): case CompactBlockProcessorError.saplingActivationMismatch(expected: network.constants.saplingActivationHeight, found: BlockHeight(info.saplingActivationHeight)):
break break
default: default:
XCTFail("Expected CompactBlockProcessorError.saplingActivationMismatch but found \(error)") XCTFail("Expected CompactBlockProcessorError.saplingActivationMismatch but found \(error)")
@ -193,14 +193,14 @@ class BlockBatchValidationTests: XCTestCase {
let expectedResult = FigureNextBatchOperation.NextState.wait(latestHeight: expectedLatestHeight, latestDownloadHeight: expectedLatestHeight) let expectedResult = FigureNextBatchOperation.NextState.wait(latestHeight: expectedLatestHeight, latestDownloadHeight: expectedLatestHeight)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight) let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.SAPLING_ACTIVATION_HEIGHT, network: network) let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: 1210000, saplingActivation: network.constants.saplingActivationHeight, network: network)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = UInt64(expectedLatestHeight) info.blockHeight = UInt64(expectedLatestHeight)
info.branch = "d34db33f" info.branch = "d34db33f"
info.chainName = "main" info.chainName = "main"
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
@ -250,14 +250,14 @@ class BlockBatchValidationTests: XCTestCase {
let expectedResult = FigureNextBatchOperation.NextState.processNewBlocks(range: CompactBlockProcessor.nextBatchBlockRange(latestHeight: expectedLatestHeight, latestDownloadedHeight: expectedStoreLatestHeight, walletBirthday: walletBirthday)) let expectedResult = FigureNextBatchOperation.NextState.processNewBlocks(range: CompactBlockProcessor.nextBatchBlockRange(latestHeight: expectedLatestHeight, latestDownloadedHeight: expectedStoreLatestHeight, walletBirthday: walletBirthday))
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight) let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: walletBirthday, saplingActivation: network.constants.SAPLING_ACTIVATION_HEIGHT, network: network) let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: walletBirthday, saplingActivation: network.constants.saplingActivationHeight, network: network)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = UInt64(expectedLatestHeight) info.blockHeight = UInt64(expectedLatestHeight)
info.branch = "d34db33f" info.branch = "d34db33f"
info.chainName = "main" info.chainName = "main"
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self
@ -307,14 +307,14 @@ class BlockBatchValidationTests: XCTestCase {
let expectedResult = FigureNextBatchOperation.NextState.finishProcessing(height: expectedStoreLatestHeight) let expectedResult = FigureNextBatchOperation.NextState.finishProcessing(height: expectedStoreLatestHeight)
let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight) let repository = ZcashConsoleFakeStorage(latestBlockHeight: expectedStoreLatestHeight)
let downloader = CompactBlockDownloader(service: service, storage: repository) let downloader = CompactBlockDownloader(service: service, storage: repository)
let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: walletBirthday, saplingActivation: network.constants.SAPLING_ACTIVATION_HEIGHT, network: network) let config = CompactBlockProcessor.Configuration(cacheDb: try! __cacheDbURL(), dataDb: try! __dataDbURL(), downloadBatchSize: 100, retries: 5, maxBackoffInterval: 10, rewindDistance: 100, walletBirthday: walletBirthday, saplingActivation: network.constants.saplingActivationHeight, network: network)
var info = LightdInfo() var info = LightdInfo()
info.blockHeight = UInt64(expectedLatestHeight) info.blockHeight = UInt64(expectedLatestHeight)
info.branch = "d34db33f" info.branch = "d34db33f"
info.chainName = "main" info.chainName = "main"
info.buildUser = "test user" info.buildUser = "test user"
info.consensusBranchID = "d34db4d" info.consensusBranchID = "d34db4d"
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
service.mockLightDInfo = info service.mockLightDInfo = info
let mockRust = MockRustBackend.self let mockRust = MockRustBackend.self

View File

@ -40,8 +40,8 @@ class BlockDownloaderTests: XCTestCase {
let expect = XCTestExpectation(description: self.description) let expect = XCTestExpectation(description: self.description)
expect.expectedFulfillmentCount = 3 expect.expectedFulfillmentCount = 3
let lowerRange: BlockHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT + 99 let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange)) let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange))
downloader.downloadBlockRange(range) { (error) in downloader.downloadBlockRange(range) { (error) in
@ -66,8 +66,8 @@ class BlockDownloaderTests: XCTestCase {
func testSmallDownload() { func testSmallDownload() {
let lowerRange: BlockHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT + 99 let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange)) let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange))
var latest: BlockHeight = 0 var latest: BlockHeight = 0
@ -94,12 +94,12 @@ class BlockDownloaderTests: XCTestCase {
} }
func testFailure() { func testFailure() {
let awfulDownloader = CompactBlockDownloader(service: AwfulLightWalletService(latestBlockHeight: self.network.constants.SAPLING_ACTIVATION_HEIGHT + 1000, service: darksideWalletService), storage: ZcashConsoleFakeStorage()) let awfulDownloader = CompactBlockDownloader(service: AwfulLightWalletService(latestBlockHeight: self.network.constants.saplingActivationHeight + 1000, service: darksideWalletService), storage: ZcashConsoleFakeStorage())
let expect = XCTestExpectation(description: self.description) let expect = XCTestExpectation(description: self.description)
expect.expectedFulfillmentCount = 1 expect.expectedFulfillmentCount = 1
let lowerRange: BlockHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT let lowerRange: BlockHeight = self.network.constants.saplingActivationHeight
let upperRange: BlockHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT + 99 let upperRange: BlockHeight = self.network.constants.saplingActivationHeight + 99
let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange)) let range = CompactBlockRange(uncheckedBounds: (lowerRange,upperRange))

View File

@ -55,7 +55,7 @@ class BlockScanOperationTests: XCTestCase {
let latestScannedBlockExpect = XCTestExpectation(description: self.description + "latestScannedHeight") let latestScannedBlockExpect = XCTestExpectation(description: self.description + "latestScannedHeight")
let service = LightWalletGRPCService(endpoint: LightWalletEndpoint(address: "lightwalletd.testnet.electriccoin.co", port: 9067)) let service = LightWalletGRPCService(endpoint: LightWalletEndpoint(address: "lightwalletd.testnet.electriccoin.co", port: 9067))
let blockCount = 100 let blockCount = 100
let range = network.constants.SAPLING_ACTIVATION_HEIGHT ... network.constants.SAPLING_ACTIVATION_HEIGHT + blockCount let range = network.constants.saplingActivationHeight ... network.constants.saplingActivationHeight + blockCount
let downloadOperation = CompactBlockDownloadOperation(downloader: CompactBlockDownloader.sqlDownloader(service: service, at: cacheDbURL)!, range: range) let downloadOperation = CompactBlockDownloadOperation(downloader: CompactBlockDownloader.sqlDownloader(service: service, at: cacheDbURL)!, range: range)
let scanOperation = CompactBlockScanningOperation(rustWelding: rustWelding, cacheDb: cacheDbURL, dataDb: dataDbURL, networkType: network.networkType) let scanOperation = CompactBlockScanningOperation(rustWelding: rustWelding, cacheDb: cacheDbURL, dataDb: dataDbURL, networkType: network.networkType)

View File

@ -10,7 +10,7 @@ import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
class CompactBlockProcessorTests: XCTestCase { class CompactBlockProcessorTests: XCTestCase {
let processorConfig = CompactBlockProcessor.Configuration.standard(for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.SAPLING_ACTIVATION_HEIGHT) let processorConfig = CompactBlockProcessor.Configuration.standard(for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight)
var processor: CompactBlockProcessor! var processor: CompactBlockProcessor!
var downloadStartedExpect: XCTestExpectation! var downloadStartedExpect: XCTestExpectation!
var updatedNotificationExpectation: XCTestExpectation! var updatedNotificationExpectation: XCTestExpectation!
@ -19,7 +19,7 @@ class CompactBlockProcessorTests: XCTestCase {
var startedValidatingNotificationExpectation: XCTestExpectation! var startedValidatingNotificationExpectation: XCTestExpectation!
var idleNotificationExpectation: XCTestExpectation! var idleNotificationExpectation: XCTestExpectation!
let network = ZcashNetworkBuilder.network(for: .testnet) let network = ZcashNetworkBuilder.network(for: .testnet)
let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.SAPLING_ACTIVATION_HEIGHT + 2000 let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000
override func setUpWithError() throws { override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class.
@ -35,7 +35,7 @@ class CompactBlockProcessorTests: XCTestCase {
info.chainName = "test" info.chainName = "test"
info.consensusBranchID = branchID.toString() info.consensusBranchID = branchID.toString()
info.estimatedHeight = UInt64(mockLatestHeight) info.estimatedHeight = UInt64(mockLatestHeight)
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
}) })
let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString)) let storage = CompactBlockStorage.init(connectionProvider: SimpleConnectionProvider(path: processorConfig.cacheDb.absoluteString))
@ -123,15 +123,15 @@ class CompactBlockProcessorTests: XCTestCase {
// test first range // test first range
var latestDownloadedHeight = processorConfig.walletBirthday // this can be either this or Wallet Birthday. var latestDownloadedHeight = processorConfig.walletBirthday // this can be either this or Wallet Birthday.
var latestBlockchainHeight = BlockHeight(network.constants.SAPLING_ACTIVATION_HEIGHT + 1000) var latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000)
var expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight, upper:latestBlockchainHeight)) var expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight, upper:latestBlockchainHeight))
XCTAssertEqual(expectedBatchRange, CompactBlockProcessor.nextBatchBlockRange(latestHeight: latestBlockchainHeight, latestDownloadedHeight: latestDownloadedHeight, walletBirthday: processorConfig.walletBirthday)) XCTAssertEqual(expectedBatchRange, CompactBlockProcessor.nextBatchBlockRange(latestHeight: latestBlockchainHeight, latestDownloadedHeight: latestDownloadedHeight, walletBirthday: processorConfig.walletBirthday))
// Test mid-range // Test mid-range
latestDownloadedHeight = BlockHeight(network.constants.SAPLING_ACTIVATION_HEIGHT + ZcashSDK.DEFAULT_BATCH_SIZE) latestDownloadedHeight = BlockHeight(network.constants.saplingActivationHeight + ZcashSDK.DefaultBatchSize)
latestBlockchainHeight = BlockHeight(network.constants.SAPLING_ACTIVATION_HEIGHT + 1000) latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000)
expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight)) expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight))
@ -139,8 +139,8 @@ class CompactBlockProcessorTests: XCTestCase {
// Test last batch range // Test last batch range
latestDownloadedHeight = BlockHeight(network.constants.SAPLING_ACTIVATION_HEIGHT + 950) latestDownloadedHeight = BlockHeight(network.constants.saplingActivationHeight + 950)
latestBlockchainHeight = BlockHeight(network.constants.SAPLING_ACTIVATION_HEIGHT + 1000) latestBlockchainHeight = BlockHeight(network.constants.saplingActivationHeight + 1000)
expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight)) expectedBatchRange = CompactBlockRange(uncheckedBounds: (lower: latestDownloadedHeight + 1, upper: latestBlockchainHeight))

View File

@ -11,7 +11,7 @@ import XCTest
@testable import ZcashLightClientKit @testable import ZcashLightClientKit
class CompactBlockReorgTests: XCTestCase { class CompactBlockReorgTests: XCTestCase {
let processorConfig = CompactBlockProcessor.Configuration.standard(for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.SAPLING_ACTIVATION_HEIGHT) let processorConfig = CompactBlockProcessor.Configuration.standard(for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight)
var processor: CompactBlockProcessor! var processor: CompactBlockProcessor!
var downloadStartedExpect: XCTestExpectation! var downloadStartedExpect: XCTestExpectation!
var updatedNotificationExpectation: XCTestExpectation! var updatedNotificationExpectation: XCTestExpectation!
@ -21,7 +21,7 @@ class CompactBlockReorgTests: XCTestCase {
var idleNotificationExpectation: XCTestExpectation! var idleNotificationExpectation: XCTestExpectation!
var reorgNotificationExpectation: XCTestExpectation! var reorgNotificationExpectation: XCTestExpectation!
let network = ZcashNetworkBuilder.network(for: .testnet) let network = ZcashNetworkBuilder.network(for: .testnet)
let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.SAPLING_ACTIVATION_HEIGHT + 2000 let mockLatestHeight = ZcashNetworkBuilder.network(for: .testnet).constants.saplingActivationHeight + 2000
override func setUpWithError() throws { override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class. // Put setup code here. This method is called before the invocation of each test method in the class.
@ -38,7 +38,7 @@ class CompactBlockReorgTests: XCTestCase {
info.chainName = "test" info.chainName = "test"
info.consensusBranchID = branchID.toString() info.consensusBranchID = branchID.toString()
info.estimatedHeight = UInt64(mockLatestHeight) info.estimatedHeight = UInt64(mockLatestHeight)
info.saplingActivationHeight = UInt64(network.constants.SAPLING_ACTIVATION_HEIGHT) info.saplingActivationHeight = UInt64(network.constants.saplingActivationHeight)
}) })
try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet) try ZcashRustBackend.initDataDb(dbData: processorConfig.dataDb, networkType: .testnet)
@ -49,7 +49,7 @@ class CompactBlockReorgTests: XCTestCase {
let mockBackend = MockRustBackend.self let mockBackend = MockRustBackend.self
mockBackend.mockValidateCombinedChainFailAfterAttempts = 3 mockBackend.mockValidateCombinedChainFailAfterAttempts = 3
mockBackend.mockValidateCombinedChainKeepFailing = false mockBackend.mockValidateCombinedChainKeepFailing = false
mockBackend.mockValidateCombinedChainFailureHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT + 320 mockBackend.mockValidateCombinedChainFailureHeight = self.network.constants.saplingActivationHeight + 320
processor = CompactBlockProcessor(service: service, processor = CompactBlockProcessor(service: service,
storage: storage, storage: storage,
@ -86,8 +86,8 @@ class CompactBlockReorgTests: XCTestCase {
XCTAssertNotNil(notification.userInfo) XCTAssertNotNil(notification.userInfo)
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight, if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight { let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight {
XCTAssertTrue( reorg == 0 || reorg > self.network.constants.SAPLING_ACTIVATION_HEIGHT) XCTAssertTrue( reorg == 0 || reorg > self.network.constants.saplingActivationHeight)
XCTAssertTrue( rewind == 0 || rewind > self.network.constants.SAPLING_ACTIVATION_HEIGHT) XCTAssertTrue( rewind == 0 || rewind > self.network.constants.saplingActivationHeight)
XCTAssertTrue( rewind <= reorg ) XCTAssertTrue( rewind <= reorg )
reorgNotificationExpectation.fulfill() reorgNotificationExpectation.fulfill()
} else { } else {

View File

@ -20,7 +20,7 @@ class CompactBlockStorageTests: XCTestCase {
func testStoreThousandBlocks() { func testStoreThousandBlocks() {
let initialHeight = try! compactBlockDao.latestHeight() let initialHeight = try! compactBlockDao.latestHeight()
let startHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT let startHeight = self.network.constants.saplingActivationHeight
let blockCount = Int(1_000) let blockCount = Int(1_000)
let finalHeight = startHeight + blockCount let finalHeight = startHeight + blockCount
@ -63,7 +63,7 @@ class CompactBlockStorageTests: XCTestCase {
func testRewindTo() { func testRewindTo() {
let startHeight = self.network.constants.SAPLING_ACTIVATION_HEIGHT let startHeight = self.network.constants.saplingActivationHeight
let blockCount = Int(1_000) let blockCount = Int(1_000)
let finalHeight = startHeight + blockCount let finalHeight = startHeight + blockCount

View File

@ -25,7 +25,7 @@ class DownloadOperationTests: XCTestCase {
let storage = try! TestDbBuilder.inMemoryCompactBlockStorage() let storage = try! TestDbBuilder.inMemoryCompactBlockStorage()
let downloader = CompactBlockDownloader(service: service, storage: storage) let downloader = CompactBlockDownloader(service: service, storage: storage)
let blockCount = 100 let blockCount = 100
let activationHeight = network.constants.SAPLING_ACTIVATION_HEIGHT let activationHeight = network.constants.saplingActivationHeight
let range = activationHeight ... activationHeight + blockCount let range = activationHeight ... activationHeight + blockCount
let downloadOperation = CompactBlockDownloadOperation(downloader: downloader, range: range) let downloadOperation = CompactBlockDownloadOperation(downloader: downloader, range: range)

View File

@ -40,8 +40,8 @@ class LightWalletServiceTests: XCTestCase {
func testHundredBlocks() { func testHundredBlocks() {
let expect = XCTestExpectation(description: self.description) let expect = XCTestExpectation(description: self.description)
let count = 99 let count = 99
let lowerRange: BlockHeight = network.constants.SAPLING_ACTIVATION_HEIGHT let lowerRange: BlockHeight = network.constants.saplingActivationHeight
let upperRange: BlockHeight = network.constants.SAPLING_ACTIVATION_HEIGHT + count let upperRange: BlockHeight = network.constants.saplingActivationHeight + count
let blockRange = lowerRange ... upperRange let blockRange = lowerRange ... upperRange
service.blockRange(blockRange) { (result) in service.blockRange(blockRange) { (result) in
@ -61,8 +61,8 @@ class LightWalletServiceTests: XCTestCase {
} }
func testSyncBlockRange() { func testSyncBlockRange() {
let lowerRange: BlockHeight = network.constants.SAPLING_ACTIVATION_HEIGHT let lowerRange: BlockHeight = network.constants.saplingActivationHeight
let upperRange: BlockHeight = network.constants.SAPLING_ACTIVATION_HEIGHT + 99 let upperRange: BlockHeight = network.constants.saplingActivationHeight + 99
let blockRange = lowerRange ... upperRange let blockRange = lowerRange ... upperRange
do { do {
@ -81,7 +81,7 @@ class LightWalletServiceTests: XCTestCase {
case .failure(let e): case .failure(let e):
XCTFail("error: \(e)") XCTFail("error: \(e)")
case .success(let height): case .success(let height):
XCTAssertTrue(height > self.network.constants.SAPLING_ACTIVATION_HEIGHT) XCTAssertTrue(height > self.network.constants.saplingActivationHeight)
} }
} }

View File

@ -49,7 +49,7 @@ class NetworkUpgradeTests: XCTestCase {
let firstSyncExpectation = XCTestExpectation(description: "first sync") let firstSyncExpectation = XCTestExpectation(description: "first sync")
try coordinator.applyStaged(blockheight: activationHeight - ZcashSDK.DEFAULT_STALE_TOLERANCE) try coordinator.applyStaged(blockheight: activationHeight - ZcashSDK.defaultStaleTolerance)
sleep(5) sleep(5)
try coordinator.sync(completion: { (synchronizer) in try coordinator.sync(completion: { (synchronizer) in

View File

@ -286,8 +286,8 @@ class TestSynchronizerBuilder {
dataDb: initializer.dataDbURL, dataDb: initializer.dataDbURL,
downloadBatchSize: 100, downloadBatchSize: 100,
retries: 5, retries: 5,
maxBackoffInterval: ZcashSDK.DEFAULT_MAX_BACKOFF_INTERVAL, maxBackoffInterval: ZcashSDK.defaultMaxBackOffInterval,
rewindDistance: ZcashSDK.DEFAULT_REWIND_DISTANCE, rewindDistance: ZcashSDK.defaultRewindDistance,
walletBirthday: walletBirthday.height, walletBirthday: walletBirthday.height,
saplingActivation: lowerBoundHeight, saplingActivation: lowerBoundHeight,
network: network) network: network)

View File

@ -202,31 +202,30 @@ class DarksideWalletService: LightWalletService {
class DarksideWalletDConstants: NetworkConstants { class DarksideWalletDConstants: NetworkConstants {
static var SAPLING_ACTIVATION_HEIGHT: BlockHeight {
static var saplingActivationHeight: BlockHeight {
663150 663150
} }
static var DEFAULT_DATA_DB_NAME: String { static var defaultDataDbName: String {
ZcashSDKMainnetConstants.DEFAULT_DATA_DB_NAME ZcashSDKMainnetConstants.defaultDataDbName
} }
static var DEFAULT_CACHES_DB_NAME: String { static var defaultCacheDbName: String {
ZcashSDKMainnetConstants.DEFAULT_CACHES_DB_NAME ZcashSDKMainnetConstants.defaultCacheDbName
} }
static var DEFAULT_PENDING_DB_NAME: String { static var defaultPendingDbName: String {
ZcashSDKMainnetConstants.DEFAULT_PENDING_DB_NAME ZcashSDKMainnetConstants.defaultPendingDbName
} }
static var DEFAULT_DB_NAME_PREFIX: String { static var defaultDbNamePrefix: String {
ZcashSDKMainnetConstants.DEFAULT_DB_NAME_PREFIX ZcashSDKMainnetConstants.defaultDbNamePrefix
} }
static var FEE_CHANGE_HEIGHT: BlockHeight { static var feeChangeHeight: BlockHeight {
ZcashSDKMainnetConstants.FEE_CHANGE_HEIGHT ZcashSDKMainnetConstants.feeChangeHeight
} }
} }
class DarksideWalletDNetwork: ZcashNetwork { class DarksideWalletDNetwork: ZcashNetwork {
var constants: NetworkConstants.Type = DarksideWalletDConstants.self var constants: NetworkConstants.Type = DarksideWalletDConstants.self

View File

@ -79,7 +79,7 @@ class FakeChainBuilder {
try darksideWallet.useDataset(testnetCanopyStartBlock) try darksideWallet.useDataset(testnetCanopyStartBlock)
try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length) try darksideWallet.stageBlocksCreate(from: birthday + 1, count: length)
try darksideWallet.stageTransaction(from: testnetPreCanopyTx, at: networkActivationHeight - ZcashSDK.EXPIRY_OFFSET) try darksideWallet.stageTransaction(from: testnetPreCanopyTx, at: networkActivationHeight - ZcashSDK.expiryOffset)
} }
@ -96,7 +96,7 @@ class FakeChainBuilder {
static func buildChainMixedFunds(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, branchID: String, chainName: String, length: Int) throws { static func buildChainMixedFunds(darksideWallet: DarksideWalletService, birthday: BlockHeight, networkActivationHeight: BlockHeight, branchID: String, chainName: String, length: Int) throws {
try buildChain(darksideWallet: darksideWallet, birthday: birthday, networkActivationHeight: networkActivationHeight, branchID: branchID, chainName: chainName, length: length) try buildChain(darksideWallet: darksideWallet, birthday: birthday, networkActivationHeight: networkActivationHeight, branchID: branchID, chainName: chainName, length: length)
try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + ZcashSDK.EXPIRY_OFFSET) try darksideWallet.stageTransaction(from: testnetPostCanopyTx, at: networkActivationHeight + ZcashSDK.expiryOffset)
} }

View File

@ -144,16 +144,16 @@ class MockTransactionRepository: TransactionRepository {
} }
func mockSent(_ index: Int) -> ConfirmedTransactionEntity { func mockSent(_ index: Int) -> ConfirmedTransactionEntity {
ConfirmedTransaction(toAddress: "some_address", expiryHeight: BlockHeight.max, minedHeight: randomBlockHeight(), noteId: index, blockTimeInSeconds: randomTimeInterval(), transactionIndex: index, raw: Data(), id: index, value: Int.random(in: 1 ... ZcashSDK.ZATOSHI_PER_ZEC), memo: nil, rawTransactionId: Data()) ConfirmedTransaction(toAddress: "some_address", expiryHeight: BlockHeight.max, minedHeight: randomBlockHeight(), noteId: index, blockTimeInSeconds: randomTimeInterval(), transactionIndex: index, raw: Data(), id: index, value: Int.random(in: 1 ... ZcashSDK.zatoshiPerZEC), memo: nil, rawTransactionId: Data())
} }
func mockReceived(_ index: Int) -> ConfirmedTransactionEntity { func mockReceived(_ index: Int) -> ConfirmedTransactionEntity {
ConfirmedTransaction(toAddress: nil, expiryHeight: BlockHeight.max, minedHeight: randomBlockHeight(), noteId: index, blockTimeInSeconds: randomTimeInterval(), transactionIndex: index, raw: Data(), id: index, value: Int.random(in: 1 ... ZcashSDK.ZATOSHI_PER_ZEC), memo: nil, rawTransactionId: Data()) ConfirmedTransaction(toAddress: nil, expiryHeight: BlockHeight.max, minedHeight: randomBlockHeight(), noteId: index, blockTimeInSeconds: randomTimeInterval(), transactionIndex: index, raw: Data(), id: index, value: Int.random(in: 1 ... ZcashSDK.zatoshiPerZEC), memo: nil, rawTransactionId: Data())
} }
func randomBlockHeight() -> BlockHeight { func randomBlockHeight() -> BlockHeight {
BlockHeight.random(in: network.constants.SAPLING_ACTIVATION_HEIGHT ... 1_000_000) BlockHeight.random(in: network.constants.saplingActivationHeight ... 1_000_000)
} }
func randomTimeInterval() -> TimeInterval { func randomTimeInterval() -> TimeInterval {
Double.random(in: Date().timeIntervalSince1970 - 1000000.0 ... Date().timeIntervalSince1970) Double.random(in: Date().timeIntervalSince1970 - 1000000.0 ... Date().timeIntervalSince1970)

View File

@ -322,7 +322,7 @@ class MockRustBackend: ZcashRustBackendWelding {
nil nil
} }
static func decryptAndStoreTransaction(dbData: URL, tx: [UInt8], minedHeight: Int32, networkType: NetworkType) -> Bool { static func decryptAndStoreTransaction(dbData: URL, txBytes: [UInt8], minedHeight: Int32, networkType: NetworkType) -> Bool {
false false
} }