diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.pbxproj b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.pbxproj index e494dd8c..7959044a 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.pbxproj +++ b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 52; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -15,7 +15,6 @@ 0D756A94236C761E009B041B /* GetAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D756A93236C761E009B041B /* GetAddressViewController.swift */; }; 0D76121926B1D66D001CA417 /* Testnet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D76121826B1D66D001CA417 /* Testnet.swift */; }; 0D7A4A83236CCD88001F4DD8 /* SyncBlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */; }; - 0D7C85E523AD5A9B006878FC /* SampleStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7C85E423AD5A9B006878FC /* SampleStorage.swift */; }; 0D907F162322CC5900D641FE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F152322CC5900D641FE /* AppDelegate.swift */; }; 0D907F182322CC5900D641FE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F172322CC5900D641FE /* ViewController.swift */; }; 0D907F1B2322CC5900D641FE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D907F192322CC5900D641FE /* Main.storyboard */; }; @@ -35,7 +34,6 @@ 0DA1C4C727D11D9500E5006E /* SaplingParametersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D6CE8BC252E3C4A0005D707 /* SaplingParametersViewController.swift */; }; 0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1BE47E2581937100F78BE3 /* GetUTXOsViewController.swift */; }; 0DA1C4C927D11D9500E5006E /* DerivationToolViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1BE4502581585C00F78BE3 /* DerivationToolViewController.swift */; }; - 0DA1C4CA27D11D9500E5006E /* SampleStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7C85E423AD5A9B006878FC /* SampleStorage.swift */; }; 0DA1C4CB27D11D9500E5006E /* SampleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D49A18B241698A800CC0649 /* SampleLogger.swift */; }; 0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA58B952397F2CB004596EA /* TransactionsDataSource.swift */; }; 0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF53E6623A438F100D7249C /* PaginatedTransactionsViewController.swift */; }; @@ -83,7 +81,6 @@ 0D76121526B1D5ED001CA417 /* Mainnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mainnet.swift; sourceTree = ""; }; 0D76121826B1D66D001CA417 /* Testnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Testnet.swift; sourceTree = ""; }; 0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBlocksViewController.swift; sourceTree = ""; }; - 0D7C85E423AD5A9B006878FC /* SampleStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleStorage.swift; sourceTree = ""; }; 0D907F122322CC5900D641FE /* ZcashLightClientSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZcashLightClientSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 0D907F152322CC5900D641FE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 0D907F172322CC5900D641FE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; @@ -248,7 +245,6 @@ 0D907F212322CC5B00D641FE /* Info.plist */, F94912622790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist */, 0DDFB33D236B844900AED892 /* DemoAppConfig.swift */, - 0D7C85E423AD5A9B006878FC /* SampleStorage.swift */, 0D49A18B241698A800CC0649 /* SampleLogger.swift */, ); path = ZcashLightClientSample; @@ -498,6 +494,7 @@ }; 0DCB58A4237B5B580040096C /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -534,7 +531,6 @@ 0D6CE8BD252E3C4A0005D707 /* SaplingParametersViewController.swift in Sources */, 0D1BE47F2581937100F78BE3 /* GetUTXOsViewController.swift in Sources */, 0D1BE4512581585C00F78BE3 /* DerivationToolViewController.swift in Sources */, - 0D7C85E523AD5A9B006878FC /* SampleStorage.swift in Sources */, 0D49A18C241698A800CC0649 /* SampleLogger.swift in Sources */, 0DA58B962397F2CB004596EA /* TransactionsDataSource.swift in Sources */, 0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */, @@ -567,7 +563,6 @@ 0DA1C4C727D11D9500E5006E /* SaplingParametersViewController.swift in Sources */, 0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */, 0DA1C4C927D11D9500E5006E /* DerivationToolViewController.swift in Sources */, - 0DA1C4CA27D11D9500E5006E /* SampleStorage.swift in Sources */, 0DA1C4CB27D11D9500E5006E /* SampleLogger.swift in Sources */, 0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */, 0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */, diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index d6c4612a..54fef930 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/ZcashLightClientSample/ZcashLightClientSample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -138,10 +138,10 @@ }, { "package": "libzcashlc", - "repositoryURL": "https://github.com/nuttycom/zcash-light-client-ffi.git", + "repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi", "state": { - "branch": "bin/librustzcash_0_7-migrations", - "revision": "dbf7ac51a7de0bbc2c1088973236d68ceb752361", + "branch": "bin/librustzcash_0_7", + "revision": "823f864a7952073fb9718cf75610691756e34d59", "version": null } } diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/AppDelegate.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/AppDelegate.swift index 45c0686d..1ff3a5ae 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/AppDelegate.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/AppDelegate.swift @@ -50,10 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { ) _ = try! wallet.initialize(with: DemoAppConfig.seed) - var storage = SampleStorage.shared - storage!.seed = Data(DemoAppConfig.seed) - storage!.privateKey = try! DerivationTool(networkType: kZcashNetwork.networkType) - .deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1)[0] + self.wallet = wallet return wallet } @@ -141,10 +138,6 @@ extension AppDelegate { } catch { loggerProxy.error("error clearing data db: \(error)") } - - var storage = SampleStorage.shared - storage!.seed = nil - storage!.privateKey = nil } } diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Derivation Tool/DerivationToolViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Derivation Tool/DerivationToolViewController.swift index 178e8558..b890f327 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Derivation Tool/DerivationToolViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Derivation Tool/DerivationToolViewController.swift @@ -13,8 +13,6 @@ class DerivationToolViewController: UIViewController { enum DerivationErrors: Error { case couldNotDeriveSpendingKeys(underlyingError: Error) case couldNotDeriveViewingKeys(underlyingError: Error) - case couldNotDeriveShieldedAddress(underlyingError: Error) - case couldNotDeriveTransparentAddress(underlyingError: Error) case unknown } @@ -113,15 +111,15 @@ class DerivationToolViewController: UIViewController { throw DerivationErrors.couldNotDeriveViewingKeys(underlyingError: DerivationErrors.unknown) } - let unifiedAddress = try derivationTool.deriveUnifiedAddress(viewingKey: viewingKey.encoding) + let unifiedAddress = try derivationTool.deriveUnifiedAddress(from: viewingKey) let transparentAddress = try derivationTool.deriveTransparentAddress(seed: seedBytes) updateLabels( - spendingKey: spendingKey, - viewingKey: viewingKey.encoding, - shieldedAddress: unifiedAddress, - transaparentAddress: transparentAddress + spendingKey: spendingKey.stringEncoded, + viewingKey: viewingKey.stringEncoded, + shieldedAddress: unifiedAddress.stringEncoded, + transaparentAddress: transparentAddress.stringEncoded ) } diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Get Address/GetAddressViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Get Address/GetAddressViewController.swift index e8a2c17c..d7b3bb79 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Get Address/GetAddressViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Get Address/GetAddressViewController.swift @@ -12,15 +12,23 @@ class GetAddressViewController: UIViewController { @IBOutlet weak var unifiedAddressLabel: UILabel! @IBOutlet weak var tAddressLabel: UILabel! @IBOutlet weak var spendingKeyLabel: UILabel! // THIS SHOULD BE SUPER SECRET!!!!! - + + // THIS SHOULD NEVER BE STORED IN MEMORY + // swiftlint:disable:next implicitly_unwrapped_optional + var spendingKey: SaplingExtendedSpendingKey! + override func viewDidLoad() { super.viewDidLoad() let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType) + + // swiftlint:disable:next force_try force_unwrapping + self.spendingKey = try! derivationTool.deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1).first! + // Do any additional setup after loading the view. - - unifiedAddressLabel.text = (try? derivationTool.deriveUnifiedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found" - tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed)) ?? "could not derive t-address" - spendingKeyLabel.text = SampleStorage.shared.privateKey ?? "No Spending Key found" + // swiftlint:disable:next line_length + unifiedAddressLabel.text = (try? derivationTool.deriveUnifiedAddress(seed: DemoAppConfig.seed, accountIndex: 0))?.stringEncoded ?? "No Addresses found" + tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed))?.stringEncoded ?? "could not derive t-address" + spendingKeyLabel.text = self.spendingKey.stringEncoded unifiedAddressLabel.addGestureRecognizer( UITapGestureRecognizer( target: self, @@ -44,20 +52,12 @@ class GetAddressViewController: UIViewController { ) spendingKeyLabel.isUserInteractionEnabled = true loggerProxy.info("Address: \(String(describing: Initializer.shared.getAddress()))") - // NOTE: NEVER LOG YOUR PRIVATE KEYS IN REAL LIFE - // swiftlint:disable:next print_function_usage - print("Spending Key: \(SampleStorage.shared.privateKey ?? "No Spending Key found")") } @IBAction func spendingKeyTapped(_ gesture: UIGestureRecognizer) { - guard let key = SampleStorage.shared.privateKey else { - loggerProxy.warn("nothing to copy") - return - } - loggerProxy.event("copied to clipboard") - UIPasteboard.general.string = key + UIPasteboard.general.string = self.spendingKey.stringEncoded let alert = UIAlertController( title: "", message: "Spending Key Copied to clipboard", @@ -85,7 +85,7 @@ class GetAddressViewController: UIViewController { @IBAction func tAddressTapped(_ gesture: UIGestureRecognizer) { loggerProxy.event("copied to clipboard") UIPasteboard.general.string = try? DerivationTool(networkType: kZcashNetwork.networkType) - .deriveTransparentAddress(seed: DemoAppConfig.seed) + .deriveTransparentAddress(seed: DemoAppConfig.seed).stringEncoded let alert = UIAlertController( title: "", diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift index 8413ed73..052261db 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Get UTXOs/GetUTXOsViewController.swift @@ -27,7 +27,7 @@ class GetUTXOsViewController: UIViewController { let tAddress = try! DerivationTool(networkType: kZcashNetwork.networkType) .deriveTransparentAddress(seed: DemoAppConfig.seed) - self.transparentAddressLabel.text = tAddress + self.transparentAddressLabel.text = tAddress.stringEncoded // swiftlint:disable:next force_try let balance = try! AppDelegate.shared.sharedSynchronizer.getTransparentBalance(accountIndex: 0) @@ -40,14 +40,12 @@ class GetUTXOsViewController: UIViewController { do { let seed = DemoAppConfig.seed let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType) - // swiftlint:disable:next force_unwrapping - let spendingKey = try derivationTool.deriveSpendingKeys(seed: seed, numberOfAccounts: 1).first! + let transparentSecretKey = try derivationTool.deriveTransparentAccountPrivateKey(seed: seed, account: 0) KRProgressHUD.showMessage("🛡 Shielding 🛡") AppDelegate.shared.sharedSynchronizer.shieldFunds( - spendingKey: spendingKey, transparentAccountPrivateKey: transparentSecretKey, memo: "shielding is fun!", from: 0, diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/SampleStorage.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/SampleStorage.swift deleted file mode 100644 index 51fada7c..00000000 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/SampleStorage.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// SampleStorage.swift -// ZcashLightClientSample -// -// Created by Francisco Gindre on 12/20/19. -// Copyright © 2019 Electric Coin Company. All rights reserved. -// - -import Foundation - -protocol WalletStorage { - var seed: Data? { get set } - var privateKey: String? { get set } -} - -/** -DO NOT STORE YOUR KEYS ON USER DEFAULTS!!! -USE KEYCHAIN OR ANY OTHER SECURE STORAGE MECHANISM OF YOUR CHOICE -USE AT YOUR OWN RISK -*/ -class SampleStorage: WalletStorage { - // swiftlint:disable:next implicitly_unwrapped_optional - static var shared: WalletStorage! { - _shared - } - - private static var _shared = SampleStorage() - - private let keySeed = "cash.z.wallet.sdk.demoapp.SEED" - private let keyPK = "cash.z.wallet.sdk.demoapp.PK" - - var seed: Data? { - get { - // swiftlint:disable:next force_cast - UserDefaults.standard.value(forKey: keySeed) as! Data? - } - set { - UserDefaults.standard.set(newValue, forKey: keySeed) - } - } - - var privateKey: String? { - get { - // swiftlint:disable:next force_cast - UserDefaults.standard.value(forKey: keyPK) as! String? - } - set { - UserDefaults.standard.set(newValue, forKey: keyPK) - } - } - - private init() {} -} diff --git a/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift b/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift index cef06377..e023b50f 100644 --- a/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift +++ b/Example/ZcashLightClientSample/ZcashLightClientSample/Send/SendViewController.swift @@ -150,7 +150,7 @@ class SendViewController: UIViewController { guard let addr = self.addressTextField.text else { return false } - return wallet.isValidShieldedAddress(addr) || wallet.isValidTransparentAddress(addr) + return wallet.isValidSaplingAddress(addr) || wallet.isValidTransparentAddress(addr) } @IBAction func maxFundsValueChanged(_ sender: Any) { @@ -206,18 +206,19 @@ class SendViewController: UIViewController { loggerProxy.warn("WARNING: Form is invalid") return } - - guard let address = SampleStorage.shared.privateKey else { - loggerProxy.error("NO ADDRESS") + // swiftlint:disable:next line_length force_try + guard let spendingKey = try! DerivationTool(networkType: kZcashNetwork.networkType).deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1).first else { + loggerProxy.error("NO SPENDING KEY") return } KRProgressHUD.show() - + synchronizer.sendToAddress( - spendingKey: address, + spendingKey: spendingKey, zatoshi: zec, - toAddress: recipient, + // swiftlint:disable:next force_try + toAddress: try! Recipient(recipient, network: kZcashNetwork.networkType), memo: !self.memoField.text.isEmpty ? self.memoField.text : nil, from: 0 ) { [weak self] result in diff --git a/Package.resolved b/Package.resolved index 4fa9241e..ae06ee39 100644 --- a/Package.resolved +++ b/Package.resolved @@ -84,10 +84,10 @@ }, { "package": "libzcashlc", - "repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", + "repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi", "state": { - "branch": "librustzcash_0_7_build_test", - "revision": "5a48541c1da48eb082defaf0fa127657f4f99094", + "branch": "bin/librustzcash_0_7", + "revision": "823f864a7952073fb9718cf75610691756e34d59", "version": null } } diff --git a/Package.swift b/Package.swift index 55769504..ec104b74 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ let package = Package( dependencies: [ .package(url: "https://github.com/grpc/grpc-swift.git", from: "1.8.0"), .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0"), - .package(name:"libzcashlc", url: "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", branch: "librustzcash_0_7_build_test"), + .package(name:"libzcashlc", url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", branch: "bin/librustzcash_0_7"), ], targets: [ .target( diff --git a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift index d43a2ac7..7ff5de68 100644 --- a/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift +++ b/Sources/ZcashLightClientKit/Block/Processor/CompactBlockProcessor.swift @@ -1260,18 +1260,27 @@ extension CompactBlockProcessor { extension CompactBlockProcessor { public func getUnifiedAddres(accountIndex: Int) -> UnifiedAddress? { - guard let account = try? accountRepository.findBy(account: accountIndex) else { + // TODO: perform migrations on the account table to accommodate Unified Address or UFVK to to derive from. + guard let address = try? accountRepository.findBy(account: accountIndex)?.address else { return nil } - return UnifiedAddressShim(account: account) + + return try? UnifiedAddress(encoding: address, network: self.config.network.networkType) } - public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? { - try? accountRepository.findBy(account: accountIndex)?.address + public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? { + guard let zAddress = try? accountRepository.findBy(account: accountIndex)?.address else { + return nil + } + + return try? SaplingAddress(encoding: zAddress, network: self.config.network.networkType) } public func getTransparentAddress(accountIndex: Int) -> TransparentAddress? { - try? accountRepository.findBy(account: accountIndex)?.transparentAddress + guard let tAddress = try? accountRepository.findBy(account: accountIndex)?.transparentAddress else { return nil } + + return TransparentAddress(validatedEncoding: tAddress) + } public func getTransparentBalance(accountIndex: Int) throws -> WalletBalance { @@ -1282,20 +1291,6 @@ extension CompactBlockProcessor { } } -private struct UnifiedAddressShim { - let account: AccountEntity -} - -extension UnifiedAddressShim: UnifiedAddress { - var encoding: String { - account.transparentAddress - } - - var zAddress: SaplingShieldedAddress { - account.address - } -} - extension CompactBlockProcessor { func refreshUTXOs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result) -> Void) { let dataDb = self.config.dataDb diff --git a/Sources/ZcashLightClientKit/Entity/AccountEntity.swift b/Sources/ZcashLightClientKit/Entity/AccountEntity.swift index 2e77fd47..1036f1d3 100644 --- a/Sources/ZcashLightClientKit/Entity/AccountEntity.swift +++ b/Sources/ZcashLightClientKit/Entity/AccountEntity.swift @@ -31,17 +31,6 @@ struct Account: AccountEntity, Encodable, Decodable { var transparentAddress: String } -extension Account: UnifiedAddress { - var encoding: String { - get { - address - } - set { - address = newValue - } - } -} - extension Account: Hashable { func hash(into hasher: inout Hasher) { hasher.combine(account) diff --git a/Sources/ZcashLightClientKit/Extensions/Undescribable.swift b/Sources/ZcashLightClientKit/Extensions/Undescribable.swift new file mode 100644 index 00000000..09c5e8d0 --- /dev/null +++ b/Sources/ZcashLightClientKit/Extensions/Undescribable.swift @@ -0,0 +1,23 @@ +// +// undescribable.swift +// +// +// Created by Francisco Gindre on 8/30/22. +// + +public protocol Undescribable: CustomStringConvertible, CustomDebugStringConvertible, CustomLeafReflectable {} + + +extension Undescribable { + public var description: String { + return "--redacted--" + } + + public var debugDescription: String { + return "--redacted--" + } + + public var customMirror: Mirror { + return Mirror(reflecting: "--redacted--") + } +} diff --git a/Sources/ZcashLightClientKit/Initializer.swift b/Sources/ZcashLightClientKit/Initializer.swift index d9764fce..da2fa7fb 100644 --- a/Sources/ZcashLightClientKit/Initializer.swift +++ b/Sources/ZcashLightClientKit/Initializer.swift @@ -308,10 +308,10 @@ public class Initializer { } /** - checks if the provided address is a valid shielded zAddress + checks if the provided address is a valid sapling address */ - public func isValidShieldedAddress(_ address: String) -> Bool { - (try? rustBackend.isValidShieldedAddress(address, networkType: network.networkType)) ?? false + public func isValidSaplingAddress(_ address: String) -> Bool { + (try? rustBackend.isValidSaplingAddress(address, networkType: network.networkType)) ?? false } /** diff --git a/Sources/ZcashLightClientKit/Model/WalletTypes.swift b/Sources/ZcashLightClientKit/Model/WalletTypes.swift index 73803dd6..ddc18313 100644 --- a/Sources/ZcashLightClientKit/Model/WalletTypes.swift +++ b/Sources/ZcashLightClientKit/Model/WalletTypes.swift @@ -5,21 +5,194 @@ // Created by Francisco Gindre on 4/6/21. // +enum KeyEncodingError: Error { + case invalidEncoding +} + +/// Something that can be encoded as a String +public protocol StringEncoded { + var stringEncoded: String { get } +} + +/// Sapling Extended Spending Key +public struct SaplingExtendedSpendingKey: Equatable, StringEncoded, Undescribable { + var encoding: String + + public var stringEncoded: String { + encoding + } + + /// Initializes a new Sapling Extended Full Viewing Key from the provided string encoding + /// - Parameters: + /// - parameter encoding: String encoding of ExtSK + /// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet) + /// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is + /// found to be invalid + public init(encoding: String, network: NetworkType) throws { + guard let valid = try? DerivationTool(networkType: network).isValidSaplingExtendedSpendingKey(encoding), valid else { + throw KeyEncodingError.invalidEncoding + } + self.encoding = encoding + } +} + +/// A Transparent Account Private Key +public struct TransparentAccountPrivKey: Equatable, Undescribable { + var encoding: String +} + /** A ZIP 316 Unified Full Viewing Key. */ -public protocol UnifiedFullViewingKey { - var account: UInt32 { get set } - var encoding: String { get set } +public struct UnifiedFullViewingKey: Equatable, StringEncoded, Undescribable { + var encoding: String + + public var account: UInt32 + + public var stringEncoded: String { encoding } + + /// Initializes a new UnifiedFullViewingKey (UFVK) from the provided string encoding + /// - Parameters: + /// - parameter encoding: String encoding of unified full viewing key + /// - parameter account: account number of the given UFVK + /// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet) + /// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is + /// found to be invalid + public init(encoding: String, account: UInt32, network: NetworkType) throws { + guard let valid = try? DerivationTool(networkType: network).isValidExtendedViewingKey(encoding), valid else { + throw KeyEncodingError.invalidEncoding + } + + self.encoding = encoding + self.account = account + } } -public typealias TransparentAddress = String -public typealias SaplingShieldedAddress = String +public struct SaplingExtendedFullViewingKey: Equatable, StringEncoded, Undescribable { + var encoding: String + public var stringEncoded: String { + encoding + } -public protocol UnifiedAddress { - var encoding: String { get } + /// Initializes a new Extended Full Viewing key (EFVK) for Sapling from the provided string encoding + /// - Parameters: + /// - parameter encoding: String encoding of Sapling extended full viewing key + /// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet) + /// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is + /// found to be invalid + public init(encoding: String, network: NetworkType) throws { + guard let valid = try? DerivationTool(networkType: network).isValidExtendedViewingKey(encoding), valid else { + throw KeyEncodingError.invalidEncoding + } + self.encoding = encoding + } } +/// A Transparent Address that can be encoded as a String +/// +/// Transactions sent to this address are totally visible in the public +/// ledger. See "Multiple transaction types" in https://z.cash/technology/ +public struct TransparentAddress: Equatable, StringEncoded { + var encoding: String + + public var stringEncoded: String { encoding } + + /// Initializes a new TransparentAddress (t-address) from the provided string encoding + /// + /// - parameter encoding: String encoding of the t-address + /// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet) + /// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is + /// found to be invalid + public init(encoding: String, network: NetworkType) throws { + guard let valid = try? DerivationTool(networkType: network).isValidTransparentAddress(encoding), valid else { + throw KeyEncodingError.invalidEncoding + } + + self.encoding = encoding + } +} + +/// Represents a Sapling receiver address. Comonly called zAddress. +/// This address corresponds to the Zcash Sapling shielded pool. +/// Although this it is fully functional, we encourage developers to +/// choose `UnifiedAddress` before Sapling or Transparent ones. +public struct SaplingAddress: Equatable, StringEncoded { + var encoding: String + + public var stringEncoded: String { encoding } + + /// Initializes a new Sapling shielded address (z-address) from the provided string encoding + /// + /// - parameter encoding: String encoding of the z-address + /// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet) + /// + /// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is + /// found to be invalid + public init(encoding: String, network: NetworkType) throws { + guard let valid = try? DerivationTool(networkType: network).isValidSaplingAddress(encoding), valid else { + throw KeyEncodingError.invalidEncoding + } + + self.encoding = encoding + } +} + +public struct UnifiedAddress: Equatable, StringEncoded { + var encoding: String + + public var stringEncoded: String { encoding } + + /// Initializes a new Unified Address (UA) from the provided string encoding + /// - Parameters: + /// - parameter encoding: String encoding of the UA + /// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet) + /// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is + /// found to be invalid + public init(encoding: String, network: NetworkType) throws { + guard let valid = try? DerivationTool(networkType: network).isValidUnifiedAddress(encoding), valid else { + throw KeyEncodingError.invalidEncoding + } + + self.encoding = encoding + } +} + +/// Represents a valid recipient of Zcash +public enum Recipient: Equatable, StringEncoded { + case transparent(TransparentAddress) + case sapling(SaplingAddress) + case unified(UnifiedAddress) + + public var stringEncoded: String { + switch self { + case .transparent(let tAddr): + return tAddr.stringEncoded + case .sapling(let zAddr): + return zAddr.stringEncoded + case .unified(let uAddr): + return uAddr.stringEncoded + } + } + + /// Initializes a `Recipient` with string encoded Zcash address + /// - Parameter string: a string encoded Zcash address + /// - Parameter network: the `ZcashNetwork.NetworkType` of the recipient + /// - Throws: `KeyEncodingError.invalidEncoding` if the received string-encoded address + /// can't be initialized as a valid Zcash Address. + public init(_ string: String, network: NetworkType) throws { + if let unified = try? UnifiedAddress(encoding: string, network: network) { + self = .unified(unified) + } else if let sapling = try? SaplingAddress(encoding: string, network: network) { + self = .sapling(sapling) + } else if let transparent = try? TransparentAddress(encoding: string, network: network) { + self = .transparent(transparent) + } else { + throw KeyEncodingError.invalidEncoding + } + } +} + + public struct WalletBalance: Equatable { public var verified: Zatoshi public var total: Zatoshi diff --git a/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift b/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift index 18784c5b..4037a63f 100644 --- a/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift +++ b/Sources/ZcashLightClientKit/Rust/ZcashRustBackend.swift @@ -10,6 +10,9 @@ import Foundation import libzcashlc class ZcashRustBackend: ZcashRustBackendWelding { + + static let minimumConfirmations: UInt32 = 10 + static func lastError() -> RustWeldingError? { guard let message = getLastError() else { return nil } @@ -55,7 +58,7 @@ class ZcashRustBackend: ZcashRustBackendWelding { } } - static func isValidShieldedAddress(_ address: String, networkType: NetworkType) throws -> Bool { + static func isValidSaplingAddress(_ address: String, networkType: NetworkType) throws -> Bool { guard !address.containsCStringNullBytesBeforeStringEnding() else { return false } @@ -85,7 +88,7 @@ class ZcashRustBackend: ZcashRustBackendWelding { return true } - static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool { + static func isValidSaplingExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool { guard !key.containsCStringNullBytesBeforeStringEnding() else { return false } @@ -100,6 +103,35 @@ class ZcashRustBackend: ZcashRustBackendWelding { return true } + static func isValidSaplingExtendedSpendingKey(_ key: String, networkType: NetworkType) throws -> Bool { + guard !key.containsCStringNullBytesBeforeStringEnding() else { + return false + } + + guard zcashlc_is_valid_sapling_extended_spending_key([CChar](key.utf8CString), networkType.networkId) else { + if let error = lastError() { + throw error + } + return false + } + + return true + } + + static func isValidUnifiedAddress(_ address: String, networkType: NetworkType) throws -> Bool { + guard !address.containsCStringNullBytesBeforeStringEnding() else { + return false + } + guard zcashlc_is_valid_unified_address([CChar](address.utf8CString), networkType.networkId) else { + if let error = lastError() { + throw error + } + return false + } + + return true + } + static func isValidUnifiedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool { guard !key.containsCStringNullBytesBeforeStringEnding() else { return false @@ -115,27 +147,39 @@ class ZcashRustBackend: ZcashRustBackendWelding { return true } - static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [String]? { + static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [SaplingExtendedSpendingKey]? { let dbData = dbData.osStr() - var capacity = UInt(0) - let extsksCStr = zcashlc_init_accounts_table(dbData.0, dbData.1, seed, UInt(seed.count), accounts, &capacity, networkType.networkId) - if extsksCStr == nil { + + guard let ffiEncodingKeysPtr = zcashlc_init_accounts_table( + dbData.0, + dbData.1, + seed, + UInt(seed.count), + accounts, + networkType.networkId + ) else { return nil } - let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap({ cStr -> String? in - guard let str = cStr else { return nil } - return String(cString: str) + let extsks = UnsafeBufferPointer( + start: ffiEncodingKeysPtr.pointee.ptr, + count: Int(ffiEncodingKeysPtr.pointee.len) + ).compactMap({ encodedKey -> SaplingExtendedSpendingKey in + SaplingExtendedSpendingKey(validatedEncoding: String(cString: encodedKey.encoding)) }) - zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity) + zcashlc_free_keys(ffiEncodingKeysPtr) return extsks } - static func initAccountsTable(dbData: URL, ufvks: [UnifiedFullViewingKey], networkType: NetworkType) throws -> Bool { + static func initAccountsTable( + dbData: URL, + ufvks: [UnifiedFullViewingKey], + networkType: NetworkType + ) throws -> Bool { let dbData = dbData.osStr() - var ffiUfvks: [FFIUnifiedViewingKey] = [] + var ffiUfvks: [FFIEncodedKey] = [] for ufvk in ufvks { guard !ufvk.encoding.containsCStringNullBytesBeforeStringEnding() else { throw RustWeldingError.malformedStringInput @@ -149,15 +193,23 @@ class ZcashRustBackend: ZcashRustBackendWelding { let ufvkPtr = UnsafeMutablePointer.allocate(capacity: ufvkCStr.count) ufvkPtr.initialize(from: ufvkCStr, count: ufvkCStr.count) - - ffiUfvks.append(FFIUnifiedViewingKey(account_id: ufvk.account, encoding: ufvkPtr)) + + ffiUfvks.append( + FFIEncodedKey(account_id: ufvk.account, encoding: ufvkPtr) + ) } var result = false ffiUfvks.withContiguousMutableStorageIfAvailable { pointer in - let slice = UnsafeMutablePointer.allocate(capacity: 1) - slice.initialize(to: FFIUVKBoxedSlice(ptr: pointer.baseAddress, len: UInt(pointer.count))) + let slice = UnsafeMutablePointer.allocate(capacity: 1) + + slice.initialize( + to: FFIEncodedKeys( + ptr: pointer.baseAddress, + len: UInt(pointer.count) + ) + ) result = zcashlc_init_accounts_table_with_keys(dbData.0, dbData.1, slice, networkType.networkId) slice.deinitialize(count: 1) @@ -224,7 +276,7 @@ class ZcashRustBackend: ZcashRustBackendWelding { static func getVerifiedBalance(dbData: URL, account: Int32, networkType: NetworkType) -> Int64 { let dbData = dbData.osStr() - return zcashlc_get_verified_balance(dbData.0, dbData.1, account, networkType.networkId) + return zcashlc_get_verified_balance(dbData.0, dbData.1, account, networkType.networkId, minimumConfirmations) } static func getVerifiedTransparentBalance(dbData: URL, address: String, networkType: NetworkType) throws -> Int64 { @@ -238,7 +290,7 @@ class ZcashRustBackend: ZcashRustBackendWelding { let dbData = dbData.osStr() - return zcashlc_get_verified_transparent_balance(dbData.0, dbData.1, [CChar](address.utf8CString), networkType.networkId) + return zcashlc_get_verified_transparent_balance(dbData.0, dbData.1, [CChar](address.utf8CString), networkType.networkId, minimumConfirmations) } static func getTransparentBalance(dbData: URL, address: String, networkType: NetworkType) throws -> Int64 { @@ -388,7 +440,8 @@ class ZcashRustBackend: ZcashRustBackendWelding { UInt(spendParamsPath.lengthOfBytes(using: .utf8)), outputParamsPath, UInt(outputParamsPath.lengthOfBytes(using: .utf8)), - networkType.networkId + networkType.networkId, + minimumConfirmations ) } @@ -397,7 +450,6 @@ class ZcashRustBackend: ZcashRustBackendWelding { dbData: URL, account: Int32, xprv: String, - extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String, @@ -411,7 +463,6 @@ class ZcashRustBackend: ZcashRustBackendWelding { dbData.1, account, [CChar](xprv.utf8CString), - [CChar](extsk.utf8CString), [CChar](memoBytes.utf8CString), spendParamsPath, UInt(spendParamsPath.lengthOfBytes(using: .utf8)), @@ -421,14 +472,14 @@ class ZcashRustBackend: ZcashRustBackendWelding { ) } - static func deriveExtendedFullViewingKey(_ spendingKey: String, networkType: NetworkType) throws -> String? { - guard !spendingKey.containsCStringNullBytesBeforeStringEnding() else { + static func deriveSaplingExtendedFullViewingKey(_ spendingKey: SaplingExtendedSpendingKey, networkType: NetworkType) throws -> SaplingExtendedFullViewingKey? { + guard !spendingKey.stringEncoded.containsCStringNullBytesBeforeStringEnding() else { throw RustWeldingError.malformedStringInput } guard - let extsk = zcashlc_derive_extended_full_viewing_key( - [CChar](spendingKey.utf8CString), + let extfvk = zcashlc_derive_extended_full_viewing_key( + [CChar](spendingKey.stringEncoded.utf8CString), networkType.networkId ) else { @@ -437,21 +488,23 @@ class ZcashRustBackend: ZcashRustBackendWelding { } return nil } - - let derived = String(validatingUTF8: extsk) - - zcashlc_string_free(extsk) - return derived + + defer { zcashlc_string_free(extfvk) } + + guard let derived = String(validatingUTF8: extfvk) else { + return nil + } + + return SaplingExtendedFullViewingKey(validatedEncoding: derived) } - static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { - var capacity = UInt(0) + static func deriveSaplingExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedFullViewingKey]? { + guard - let extsksCStr = zcashlc_derive_extended_full_viewing_keys( + let ffiEncodedKeysPtr = zcashlc_derive_extended_full_viewing_keys( seed, UInt(seed.count), accounts, - &capacity, networkType.networkId ) else { @@ -460,24 +513,27 @@ class ZcashRustBackend: ZcashRustBackendWelding { } return nil } + + defer { + zcashlc_free_keys(ffiEncodedKeysPtr) + } - let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { cStr -> String? in - guard let str = cStr else { return nil } - return String(cString: str) + let extfvks = UnsafeBufferPointer( + start: ffiEncodedKeysPtr.pointee.ptr, + count: Int(ffiEncodedKeysPtr.pointee.len) + ).compactMap { encodedKey -> SaplingExtendedFullViewingKey in + SaplingExtendedFullViewingKey(validatedEncoding: String(cString: encodedKey.encoding)) } - zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity) - return extsks + return extfvks } - static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { - var capacity = UInt(0) + static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]? { guard - let extsksCStr = zcashlc_derive_extended_spending_keys( + let ffiEncodedKeysPtr = zcashlc_derive_extended_spending_keys( seed, UInt(seed.count), accounts, - &capacity, networkType.networkId ) else { @@ -486,13 +542,13 @@ class ZcashRustBackend: ZcashRustBackendWelding { } return nil } - - let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { cStr -> String? in - guard let str = cStr else { return nil } - return String(cString: str) + + defer { zcashlc_free_keys(ffiEncodedKeysPtr) } + + let extsks = UnsafeBufferPointer(start: ffiEncodedKeysPtr.pointee.ptr, count: Int(ffiEncodedKeysPtr.pointee.len)).compactMap { + SaplingExtendedSpendingKey(validatedEncoding: String(cString: $0.encoding)) } - zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity) return extsks } @@ -503,7 +559,7 @@ class ZcashRustBackend: ZcashRustBackendWelding { networkType: NetworkType ) throws -> [UnifiedFullViewingKey] { guard - let ufvksStruct = zcashlc_derive_unified_full_viewing_keys_from_seed( + let ffiEncodedKeysPtr = zcashlc_derive_unified_full_viewing_keys_from_seed( seed, UInt(seed.count), Int32(numberOfAccounts), @@ -515,10 +571,12 @@ class ZcashRustBackend: ZcashRustBackendWelding { } throw RustWeldingError.unableToDeriveKeys } - - let ufvksSize = ufvksStruct.pointee.len - guard let ufvksArrayPointer = ufvksStruct.pointee.ptr, ufvksSize > 0 else { + defer { zcashlc_free_keys(ffiEncodedKeysPtr) } + + let ufvksSize = ffiEncodedKeysPtr.pointee.len + + guard let ufvksArrayPointer = ffiEncodedKeysPtr.pointee.ptr, ufvksSize > 0 else { throw RustWeldingError.unableToDeriveKeys } @@ -531,10 +589,9 @@ class ZcashRustBackend: ZcashRustBackendWelding { throw RustWeldingError.unableToDeriveKeys } - ufvks.append(UFVK(account: UInt32(item), encoding: encoding)) + ufvks.append(UnifiedFullViewingKey(validatedEncoding: encoding, account: UInt32(item))) } - - zcashlc_free_uvk_array(ufvksStruct) + return ufvks } @@ -698,11 +755,6 @@ class ZcashRustBackend: ZcashRustBackendWelding { } } -private struct UFVK: UnifiedFullViewingKey { - var account: UInt32 - var encoding: String -} - private extension ZcashRustBackend { static func throwDataDbError(_ error: RustWeldingError) -> Error { if case RustWeldingError.genericError(let message) = error, message.contains("is not empty") { diff --git a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift index 9c8db26c..bede117b 100644 --- a/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift +++ b/Sources/ZcashLightClientKit/Rust/ZcashRustBackendWelding.swift @@ -8,7 +8,7 @@ import Foundation -public enum RustWeldingError: Error { +enum RustWeldingError: Error { case genericError(message: String) case dataDbInitFailed(message: String) case dataDbNotEmpty @@ -18,7 +18,7 @@ public enum RustWeldingError: Error { case unableToDeriveKeys } -public enum ZcashRustBackendWeldingConstants { +enum ZcashRustBackendWeldingConstants { static let validChain: Int32 = -1 } @@ -31,7 +31,7 @@ public enum DbInitResult { case seedRequired } -public protocol ZcashRustBackendWelding { +protocol ZcashRustBackendWelding { /** gets the latest error if available. Clear the existing error */ @@ -49,25 +49,36 @@ public protocol ZcashRustBackendWelding { static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: NetworkType) throws -> DbInitResult /** - - Returns: true when the address is valid and shielded. Returns false in any other case + - Returns: true when the address is valid. Returns false in any other case - Throws: Error when the provided address belongs to another network */ - static func isValidShieldedAddress(_ address: String, networkType: NetworkType) throws -> Bool + static func isValidSaplingAddress(_ address: String, networkType: NetworkType) throws -> Bool /** - Returns: true when the address is valid and transparent. false in any other case - Throws: Error when the provided address belongs to another network */ static func isValidTransparentAddress(_ address: String, networkType: NetworkType) throws -> Bool + + /// validates whether a string encoded address is a valid Unified Address. + /// - Returns: true when the address is valid and transparent. false in any other case + /// - Throws: Error when the provided address belongs to another network + static func isValidUnifiedAddress(_ address: String, networkType: NetworkType) throws -> Bool /** - - Returns: true when the address is valid and transparent. false in any other case + - Returns: `true` when the Sapling Extended Full Viewing Key is valid. `false` in any other case - Throws: Error when there's another problem not related to validity of the string in question */ - static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool - + static func isValidSaplingExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool + + /// - Returns: `true` when the Sapling Extended Spending Key is valid, false in any other case. + /// - Throws: Error when the key is semantically valid but it belongs to another network + /// - parameter key: String encoded Extendeed Spending Key + /// - parameter networkType: `NetworkType` signaling testnet or mainnet + static func isValidSaplingExtendedSpendingKey(_ key: String, networkType: NetworkType) throws -> Bool + /** - - Returns: true when the address is valid and a UFVK. false in any other case + - Returns: true when the encoded string is a valid UFVK. false in any other case - Throws: Error when there's another problem not related to validity of the string in question */ static func isValidUnifiedFullViewingKey(_ ufvk: String, networkType: NetworkType) throws -> Bool @@ -79,7 +90,7 @@ public protocol ZcashRustBackendWelding { - seed: byte array of the zip32 seed - accounts: how many accounts you want to have */ - static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [String]? + static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [SaplingExtendedSpendingKey]? /** initialize the accounts table from a set of unified full viewing keys @@ -302,8 +313,7 @@ public protocol ZcashRustBackendWelding { - dbCache: URL for the Cache DB - dbData: URL for the Data DB - account: the account index that will originate the transaction - - xprv: transparent account private key for the shielded funds. - - extsk: extended spending key string + - xprv: transparent account private key for the transparent funds that will be shielded. - memo: the memo string for this transaction - 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 @@ -314,7 +324,6 @@ public protocol ZcashRustBackendWelding { dbData: URL, account: Int32, xprv: String, - extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String, @@ -327,8 +336,8 @@ public protocol ZcashRustBackendWelding { - Returns: the derived key - Throws: RustBackendError if fatal error occurs */ - static func deriveExtendedFullViewingKey(_ spendingKey: String, networkType: NetworkType) throws -> String? - + static func deriveSaplingExtendedFullViewingKey(_ spendingKey: SaplingExtendedSpendingKey, networkType: NetworkType) throws -> SaplingExtendedFullViewingKey? + /** Derives a set of full viewing keys from a seed - Parameter spendingKey: a string containing the spending key @@ -336,22 +345,22 @@ public protocol ZcashRustBackendWelding { - Returns: an array containing the derived keys - Throws: RustBackendError if fatal error occurs */ - static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? + static func deriveSaplingExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedFullViewingKey]? /** - Derives a set of full viewing keys from a seed + Derives a set of Extended Spending Keys from a seed - Parameter seed: a string containing the seed - Parameter accounts: the number of accounts you want to derive from this seed - Returns: an array containing the spending keys - Throws: RustBackendError if fatal error occurs */ - static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? + static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]? /** Derives a unified address from a seed - Parameter seed: an array of bytes of the seed - Parameter accountIndex: the index of the account you want the address for - - Returns: an optional String containing the Shielded address + - Returns: an optional String containing Unified Address - Throws: RustBackendError if fatal error occurs */ static func deriveUnifiedAddressFromSeed(seed: [UInt8], accountIndex: Int32, networkType: NetworkType) throws -> String? @@ -363,13 +372,14 @@ public protocol ZcashRustBackendWelding { - Throws: RustBackendError if fatal error occurs */ static func deriveUnifiedAddressFromViewingKey(_ ufvk: String, networkType: NetworkType) throws -> String? - - /** - Derives a shielded address from an Extended Full Viewing Key - - Parameter seed: an array of bytes of the seed - - Returns: an optional String containing the transparent address - - Throws: RustBackendError if fatal error occurs - */ + + + /// Derives a transparent address from seed bytes + /// - Parameter seed: an array of bytes of the seed + /// - Parameter account: account number + /// - Parameter index: diversifier index + /// - Returns: an optional String containing the transparent address + /// - Throws: RustBackendError if fatal error occurs static func deriveTransparentAddressFromSeed(seed: [UInt8], account: Int, index: Int, networkType: NetworkType) throws -> String? /** diff --git a/Sources/ZcashLightClientKit/Synchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer.swift index 9b28635a..b338682b 100644 --- a/Sources/ZcashLightClientKit/Synchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer.swift @@ -95,7 +95,7 @@ public protocol Synchronizer { /// Gets the sapling shielded address for the given account. /// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used. /// - Returns the address or nil if account index is incorrect - func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? + func getSaplingAddress(accountIndex: Int) -> SaplingAddress? /// Gets the unified address for the given account. @@ -134,25 +134,23 @@ public protocol Synchronizer { /// - Parameter accountIndex: the optional account id to use. By default, the first account is used. // swiftlint:disable:next function_parameter_count func sendToAddress( - spendingKey: String, + spendingKey: SaplingExtendedSpendingKey, zatoshi: Zatoshi, - toAddress: String, + toAddress: Recipient, memo: String?, from accountIndex: Int, resultBlock: @escaping (_ result: Result) -> Void ) - /// Sends zatoshi. - /// - Parameter spendingKey: the key that allows spends to occur. + /// Shields transparent funds from the given private key into the best shielded pool of the given account. /// - Parameter transparentAccountPrivateKey: the key that allows to spend transparent funds /// - Parameter memo: the optional memo to include as part of the transaction. /// - Parameter accountIndex: the optional account id that will be used to shield your funds to. By default, the first account is used. func shieldFunds( - spendingKey: String, - transparentAccountPrivateKey: String, + transparentAccountPrivateKey: TransparentAccountPrivKey, memo: String?, from accountIndex: Int, - resultBlock: @escaping (_ result: Result) -> Void + resultBlock: @escaping (Result) -> Void ) /// Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only @@ -160,7 +158,6 @@ public protocol Synchronizer { /// - Parameter transaction: the transaction to cancel. /// - Returns: true when the cancellation request was successful. False when it is too late. func cancelSpend(transaction: PendingTransactionEntity) -> Bool - /// all outbound pending transactions that have been sent but are awaiting confirmations var pendingTransactions: [PendingTransactionEntity] { get } @@ -174,7 +171,6 @@ public protocol Synchronizer { /// all transactions related to receiving funds var receivedTransactions: [ConfirmedTransactionEntity] { get } - /// A repository serving transactions in a paginated manner /// - Parameter kind: Transaction Kind expected from this PaginatedTransactionRepository func paginatedTransactions(of kind: TransactionKind) -> PaginatedTransactionRepository @@ -203,7 +199,7 @@ public protocol Synchronizer { /// Returns the latests UTXOs for the given address from the specified height on func refreshUTXOs(address: String, from height: BlockHeight, result: @escaping (Result) -> Void) - /// Returns the last stored unshielded balance + /// Returns the last stored transparent balance func getTransparentBalance(accountIndex: Int) throws -> WalletBalance diff --git a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift index e2ef3cd2..ff6123ca 100644 --- a/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift +++ b/Sources/ZcashLightClientKit/Synchronizer/SDKSynchronizer.swift @@ -74,9 +74,10 @@ public extension Notification.Name { static let synchronizerConnectionStateChanged = Notification.Name("SynchronizerConnectionStateChanged") } -/// Synchronizer implementation for UIKit and iOS 12+ +/// Synchronizer implementation for UIKit and iOS 13+ // swiftlint:disable type_body_length public class SDKSynchronizer: Synchronizer { + public struct SynchronizerState { public var shieldedBalance: WalletBalance public var transparentBalance: WalletBalance @@ -466,21 +467,25 @@ public class SDKSynchronizer: Synchronizer { from accountIndex: Int, resultBlock: @escaping (Result) -> Void ) { - sendToAddress( - spendingKey: spendingKey, - zatoshi: Zatoshi(zatoshi), - toAddress: toAddress, - memo: memo, - from: accountIndex, - resultBlock: resultBlock - ) + do { + sendToAddress( + spendingKey: try SaplingExtendedSpendingKey(encoding: spendingKey, network: network.networkType), + zatoshi: Zatoshi(zatoshi), + toAddress: try Recipient(toAddress, network: network.networkType), + memo: memo, + from: accountIndex, + resultBlock: resultBlock + ) + } catch { + resultBlock(.failure(SynchronizerError.invalidAccount)) + } } // swiftlint:disable:next function_parameter_count public func sendToAddress( - spendingKey: String, + spendingKey: SaplingExtendedSpendingKey, zatoshi: Zatoshi, - toAddress: String, + toAddress: Recipient, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result) -> Void @@ -492,7 +497,7 @@ public class SDKSynchronizer: Synchronizer { self?.createToAddress( spendingKey: spendingKey, zatoshi: zatoshi, - toAddress: toAddress, + toAddress: toAddress.stringEncoded, memo: memo, from: accountIndex, resultBlock: resultBlock @@ -505,8 +510,7 @@ public class SDKSynchronizer: Synchronizer { } public func shieldFunds( - spendingKey: String, - transparentAccountPrivateKey: String, + transparentAccountPrivateKey: TransparentAccountPrivKey, memo: String?, from accountIndex: Int, resultBlock: @escaping (Result) -> Void @@ -515,21 +519,26 @@ public class SDKSynchronizer: Synchronizer { let derivationTool = DerivationTool(networkType: self.network.networkType) do { - let tAddr = try derivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, index: 0) // TODO: FIX - let tBalance = try utxoRepository.balance(address: tAddr, latestHeight: self.latestDownloadedHeight()) + let tAddr = try derivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, index: 0) + + let tBalance = try utxoRepository.balance(address: tAddr.stringEncoded, latestHeight: self.latestDownloadedHeight()) - // Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet. + // Verify that at least there are funds for the fee. Ideally this logic will be improved by the shielding wallet. guard tBalance.verified >= self.network.constants.defaultFee(for: self.latestScannedHeight) else { resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds)) return } - let viewingKey = try derivationTool.deriveViewingKey(spendingKey: spendingKey) - let uAddr = try derivationTool.deriveUnifiedAddress(viewingKey: viewingKey) + + // FIXME: Define who's the recipient of a shielding transaction #521 + // https://github.com/zcash/ZcashLightClientKit/issues/521 + guard let uAddr = self.getUnifiedAddress(accountIndex: accountIndex) else { + resultBlock(.failure(ShieldFundsError.shieldingFailed(underlyingError: KeyEncodingError.invalidEncoding))) + return + } - let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: uAddr, memo: memo, from: 0) + let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: uAddr.stringEncoded, memo: memo, from: accountIndex) transactionManager.encodeShieldingTransaction( - spendingKey: spendingKey, xprv: transparentAccountPrivateKey, pendingTransaction: shieldingSpend ) { [weak self] result in @@ -560,7 +569,7 @@ public class SDKSynchronizer: Synchronizer { // swiftlint:disable:next function_parameter_count func createToAddress( - spendingKey: String, + spendingKey: SaplingExtendedSpendingKey, zatoshi: Zatoshi, toAddress: String, memo: String?, @@ -690,8 +699,8 @@ public class SDKSynchronizer: Synchronizer { initializer.getVerifiedBalance(account: accountIndex) } - public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? { - blockProcessor.getShieldedAddress(accountIndex: accountIndex) + public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? { + blockProcessor.getSaplingAddress(accountIndex: accountIndex) } public func getUnifiedAddress(accountIndex: Int) -> UnifiedAddress? { @@ -707,7 +716,7 @@ public class SDKSynchronizer: Synchronizer { } /** - Returns the last stored unshielded balance + Returns the last stored transparent balance */ public func getTransparentBalance(address: String) throws -> WalletBalance { do { diff --git a/Sources/ZcashLightClientKit/Tool/DerivationTool.swift b/Sources/ZcashLightClientKit/Tool/DerivationTool.swift index 34e1cd56..301bb734 100644 --- a/Sources/ZcashLightClientKit/Tool/DerivationTool.swift +++ b/Sources/ZcashLightClientKit/Tool/DerivationTool.swift @@ -12,7 +12,11 @@ public protocol KeyValidation { func isValidTransparentAddress(_ tAddress: String) throws -> Bool - func isValidShieldedAddress(_ zAddress: String) throws -> Bool + func isValidSaplingAddress(_ zAddress: String) throws -> Bool + + func isValidSaplingExtendedSpendingKey(_ extsk: String) throws -> Bool + + func isValidUnifiedAddress(_ unifiedAddress: String) throws -> Bool } public protocol KeyDeriving { @@ -25,7 +29,7 @@ public protocol KeyDeriving { - Returns: the viewing keys that correspond to the seed, formatted as Strings. */ - func deriveViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String] + func deriveUnifiedFullViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] /** Given a spending key, return the associated viewing key. @@ -34,7 +38,7 @@ public protocol KeyDeriving { - Returns: the viewing key that corresponds to the spending key. */ - func deriveViewingKey(spendingKey: String) throws -> String + func deriveViewingKey(spendingKey: SaplingExtendedSpendingKey) throws -> SaplingExtendedFullViewingKey /** Given a seed and a number of accounts, return the associated spending keys. @@ -45,7 +49,7 @@ public protocol KeyDeriving { - Returns: the spending keys that correspond to the seed, formatted as Strings. */ - func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String] + func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [SaplingExtendedSpendingKey] /** Given a seed and account index, return the associated unified address. @@ -56,45 +60,31 @@ public protocol KeyDeriving { - Returns: the address that corresponds to the seed and account index. */ - func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> String + func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> UnifiedAddress - /** - Given a unified viewing key string, return the associated unified address. - - - Parameter viewingKey: the viewing key to use for deriving the address. The viewing key is tied to - a specific account so no account index is required. - - - Returns: the address that corresponds to the viewing key. - */ - func deriveUnifiedAddress(viewingKey: String) throws -> String + + /// Given a unified full viewing key string, return the associated unified address. + /// + /// - Parameter ufvk: the viewing key to use for deriving the address. The viewing key is tied to + /// a specific account so no account index is required. + /// + /// - Returns: the address that corresponds to the viewing key. + func deriveUnifiedAddress(from ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress /** Derives a transparent address from seedbytes, specifying account and index */ - func deriveTransparentAddress(seed: [UInt8], account: Int, index: Int) throws -> String + func deriveTransparentAddress(seed: [UInt8], account: Int, index: Int) throws -> TransparentAddress /** Derives the account private key to spend transparent funds from a specific seed and account */ - func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int) throws -> String + func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int) throws -> TransparentAccountPrivKey /** Derives a transparent address from the given transparent account private key */ - func deriveTransparentAddressFromAccountPrivateKey(_ xprv: String, index: Int) throws -> String - - func deriveTransparentAddressFromPublicKey(_ pubkey: String) throws -> String - - /** - derives unified full viewing keys from seedbytes, specifying a number of accounts - - Returns an array of unified viewing key tuples. - */ - func deriveUnifiedFullViewingKeysFromSeed(_ seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] - - /** - derives a Unified Address from a Unified Full Viewing Key - */ - func deriveUnifiedAddressFromUnifiedFullViewingKey(_ ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress + func deriveTransparentAddressFromAccountPrivateKey(_ xprv: TransparentAccountPrivKey, index: Int) throws -> TransparentAddress } public enum KeyDerivationErrors: Error { @@ -104,6 +94,7 @@ public enum KeyDerivationErrors: Error { } public class DerivationTool: KeyDeriving { + var rustwelding: ZcashRustBackendWelding.Type = ZcashRustBackend.self var networkType: NetworkType @@ -121,7 +112,7 @@ public class DerivationTool: KeyDeriving { - Returns: the viewing keys that correspond to the seed, formatted as Strings. */ - public func deriveViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String] { + public func deriveUnifiedFullViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] { guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else { throw KeyDerivationErrors.invalidInput } @@ -129,16 +120,16 @@ public class DerivationTool: KeyDeriving { do { let ufvks = try rustwelding.deriveUnifiedFullViewingKeyFromSeed(seed, numberOfAccounts: numberOfAccounts, networkType: networkType) - var keys: [String] = [] + var keys: [UnifiedFullViewingKey] = [] for ufvk in ufvks { - keys.append(ufvk.encoding) + keys.append(ufvk) } return keys } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } } - + /** Given a spending key, return the associated viewing key. @@ -146,9 +137,9 @@ public class DerivationTool: KeyDeriving { - Returns: the viewing key that corresponds to the spending key. */ - public func deriveViewingKey(spendingKey: String) throws -> String { + public func deriveViewingKey(spendingKey: SaplingExtendedSpendingKey) throws -> SaplingExtendedFullViewingKey { do { - guard let key = try rustwelding.deriveExtendedFullViewingKey(spendingKey, networkType: networkType) else { + guard let key = try rustwelding.deriveSaplingExtendedFullViewingKey(spendingKey, networkType: networkType) else { throw KeyDerivationErrors.unableToDerive } return key @@ -156,6 +147,17 @@ public class DerivationTool: KeyDeriving { throw KeyDerivationErrors.derivationError(underlyingError: error) } } + + public func deriveUnifiedFullViewingKeysFromSeed(_ seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] { + guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else { + throw KeyDerivationErrors.invalidInput + } + do { + return try rustwelding.deriveUnifiedFullViewingKeyFromSeed(seed, numberOfAccounts: numberOfAccounts, networkType: networkType) + } catch { + throw KeyDerivationErrors.derivationError(underlyingError: error) + } + } /** Given a seed and a number of accounts, return the associated spending keys. @@ -166,12 +168,12 @@ public class DerivationTool: KeyDeriving { - Returns: the spending keys that correspond to the seed, formatted as Strings. */ - public func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String] { + public func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [SaplingExtendedSpendingKey] { guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else { throw KeyDerivationErrors.invalidInput } do { - guard let keys = try rustwelding.deriveExtendedSpendingKeys(seed: seed, accounts: numberOfAccounts, networkType: networkType) else { + guard let keys = try rustwelding.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: numberOfAccounts, networkType: networkType) else { throw KeyDerivationErrors.unableToDerive } return keys @@ -189,7 +191,7 @@ public class DerivationTool: KeyDeriving { - Returns: the address that corresponds to the seed and account index. */ - public func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> String { + public func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> UnifiedAddress { guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else { throw KeyDerivationErrors.invalidInput } @@ -198,7 +200,7 @@ public class DerivationTool: KeyDeriving { guard let address = try rustwelding.deriveUnifiedAddressFromSeed(seed: seed, accountIndex: accountIndex, networkType: networkType) else { throw KeyDerivationErrors.unableToDerive } - return address + return UnifiedAddress(validatedEncoding: address) } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } @@ -212,20 +214,20 @@ public class DerivationTool: KeyDeriving { - Returns: the address that corresponds to the viewing key. */ - public func deriveUnifiedAddress(viewingKey: String) throws -> String { + public func deriveUnifiedAddress(from ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress { do { - guard let zaddr = try rustwelding.deriveUnifiedAddressFromViewingKey(viewingKey, networkType: networkType) else { + guard let stringEncodedUA = try rustwelding.deriveUnifiedAddressFromViewingKey(ufvk.stringEncoded, networkType: networkType) else { throw KeyDerivationErrors.unableToDerive } - return zaddr + return UnifiedAddress(validatedEncoding: stringEncodedUA) } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } } - public func deriveTransparentAddress(seed: [UInt8], account: Int = 0, index: Int = 0) throws -> String { + public func deriveTransparentAddress(seed: [UInt8], account: Int = 0, index: Int = 0) throws -> TransparentAddress { do { - guard let zaddr = try rustwelding.deriveTransparentAddressFromSeed( + guard let taddr = try rustwelding.deriveTransparentAddressFromSeed( seed: seed, account: account, index: index, @@ -233,54 +235,27 @@ public class DerivationTool: KeyDeriving { ) else { throw KeyDerivationErrors.unableToDerive } - return zaddr + return TransparentAddress(validatedEncoding: taddr) } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } } - public func deriveUnifiedFullViewingKeysFromSeed(_ seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] { - guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else { - throw KeyDerivationErrors.invalidInput - } - do { - return try rustwelding.deriveUnifiedFullViewingKeyFromSeed(seed, numberOfAccounts: numberOfAccounts, networkType: networkType) - } catch { - throw KeyDerivationErrors.derivationError(underlyingError: error) - } - } - - /** - derives a Unified Address from a Unified Full Viewing Key - */ + + /// derives a Unified Address from a Unified Full Viewing Key public func deriveUnifiedAddressFromUnifiedFullViewingKey(_ ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress { do { - let encoding = try deriveUnifiedAddress(viewingKey: ufvk.encoding) - return ConcreteUnifiedAddress(encoding: encoding) + return try deriveUnifiedAddress(from: ufvk) } catch { throw KeyDerivationErrors.unableToDerive } } - - public func deriveTransparentAddressFromPublicKey(_ pubkey: String) throws -> String { - guard !pubkey.isEmpty else { - throw KeyDerivationErrors.invalidInput - } - - do { - return try rustwelding.derivedTransparentAddressFromPublicKey(pubkey, networkType: networkType) - } catch { - throw KeyDerivationErrors.derivationError(underlyingError: error) - } - } - - /** - Derives the transparent funds account private key from the given seed - - Throws: - - KeyDerivationErrors.derivationError with the underlying error when it fails - - KeyDerivationErrors.unableToDerive when there's an unknown error - */ - public func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int = 0) throws -> String { + + /// Derives the transparent funds account private key from the given seed + /// - Throws: + /// - KeyDerivationErrors.derivationError with the underlying error when it fails + /// - KeyDerivationErrors.unableToDerive when there's an unknown error + public func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int = 0) throws -> TransparentAccountPrivKey { do { guard let seedKey = try rustwelding.deriveTransparentAccountPrivateKeyFromSeed( seed: seed, @@ -289,7 +264,23 @@ public class DerivationTool: KeyDeriving { ) else { throw KeyDerivationErrors.unableToDerive } - return seedKey + return TransparentAccountPrivKey(encoding: seedKey) + } catch { + throw KeyDerivationErrors.derivationError(underlyingError: error) + } + } + + + /// Derives the transparent address from an account private key + /// - Throws: + /// - KeyDerivationErrors.derivationError with the underlying error when it fails + /// - KeyDerivationErrors.unableToDerive when there's an unknown error + public func deriveTransparentAddressFromAccountPrivateKey(_ xprv: TransparentAccountPrivKey, index: Int) throws -> TransparentAddress { + do { + guard let tAddr = try rustwelding.deriveTransparentAddressFromAccountPrivateKey(xprv.encoding, index: index, networkType: networkType) else { + throw KeyDerivationErrors.unableToDerive + } + return TransparentAddress(validatedEncoding: tAddr) } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } @@ -297,9 +288,17 @@ public class DerivationTool: KeyDeriving { } extension DerivationTool: KeyValidation { + public func isValidUnifiedAddress(_ unifiedAddress: String) throws -> Bool { + do { + return try rustwelding.isValidUnifiedAddress(unifiedAddress, networkType: networkType) + } catch { + throw KeyDerivationErrors.derivationError(underlyingError: error) + } + } + public func isValidExtendedViewingKey(_ extvk: String) throws -> Bool { do { - return try rustwelding.isValidExtendedFullViewingKey(extvk, networkType: networkType) + return try rustwelding.isValidSaplingExtendedFullViewingKey(extvk, networkType: networkType) } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } @@ -313,32 +312,83 @@ extension DerivationTool: KeyValidation { } } - public func isValidShieldedAddress(_ zAddress: String) throws -> Bool { + public func isValidSaplingAddress(_ zAddress: String) throws -> Bool { do { - return try rustwelding.isValidShieldedAddress(zAddress, networkType: networkType) + return try rustwelding.isValidSaplingAddress(zAddress, networkType: networkType) } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } } - - /** - Derives the transparent address from an account private key - - Throws: - - KeyDerivationErrors.derivationError with the underlying error when it fails - - KeyDerivationErrors.unableToDerive when there's an unknown error - */ - public func deriveTransparentAddressFromAccountPrivateKey(_ xprv: String, index: Int) throws -> String { + + public func isValidSaplingExtendedSpendingKey(_ extsk: String) throws -> Bool { do { - guard let tAddr = try rustwelding.deriveTransparentAddressFromAccountPrivateKey(xprv, index: index, networkType: networkType) else { - throw KeyDerivationErrors.unableToDerive - } - return tAddr + return try rustwelding.isValidSaplingExtendedSpendingKey(extsk, networkType: networkType) } catch { throw KeyDerivationErrors.derivationError(underlyingError: error) } } } -private struct ConcreteUnifiedAddress: UnifiedAddress { - var encoding: String + +extension TransparentAddress { + /// This constructor is for internal use for Strings encodings that are assumed to be + /// already validated by another function. only for internal use. Unless you are + /// constructing an address from a primitive function of the FFI, you probably + /// shouldn't be using this. + init(validatedEncoding: String) { + self.encoding = validatedEncoding + } +} + +extension SaplingAddress { + /// This constructor is for internal use for Strings encodings that are assumed to be + /// already validated by another function. only for internal use. Unless you are + /// constructing an address from a primitive function of the FFI, you probably + /// shouldn't be using this. + init(validatedEncoding: String) { + self.encoding = validatedEncoding + } + } + +extension UnifiedAddress { + /// This constructor is for internal use for Strings encodings that are assumed to be + /// already validated by another function. only for internal use. Unless you are + /// constructing an address from a primitive function of the FFI, you probably + /// shouldn't be using this.. + init(validatedEncoding: String) { + self.encoding = validatedEncoding + } +} + +extension UnifiedFullViewingKey { + /// This constructor is for internal use for Strings encodings that are assumed to be + /// already validated by another function. only for internal use. Unless you are + /// constructing an address from a primitive function of the FFI, you probably + /// shouldn't be using this. + init(validatedEncoding: String, account: UInt32) { + self.encoding = validatedEncoding + self.account = account + } +} + + +extension SaplingExtendedFullViewingKey { + /// This constructor is for internal use for Strings encodings that are assumed to be + /// already validated by another function. only for internal use. Unless you are + /// constructing an address from a primitive function of the FFI, you probably + /// shouldn't be using this. + init(validatedEncoding: String) { + self.encoding = validatedEncoding + } +} + + +extension SaplingExtendedSpendingKey { + /// This constructor is for internal use for Strings encodings that are assumed to be + /// already validated by another function. only for internal use. Unless you are + /// constructing an address from a primitive function of the FFI, you probably + /// shouldn't be using this. + init(validatedEncoding: String) { + self.encoding = validatedEncoding + } } diff --git a/Sources/ZcashLightClientKit/Transaction/PersistentTransactionManager.swift b/Sources/ZcashLightClientKit/Transaction/PersistentTransactionManager.swift index c5084d2c..54d940fb 100644 --- a/Sources/ZcashLightClientKit/Transaction/PersistentTransactionManager.swift +++ b/Sources/ZcashLightClientKit/Transaction/PersistentTransactionManager.swift @@ -65,52 +65,15 @@ class PersistentTransactionManager: OutboundTransactionManager { } func encodeShieldingTransaction( - spendingKey: String, - xprv: String, + xprv: TransparentAccountPrivKey, pendingTransaction: PendingTransactionEntity, result: @escaping (Result) -> Void ) { queue.async { [weak self] in guard let self = self else { return } - let derivationTool = DerivationTool(networkType: self.network) - - guard - let viewingKey = try? derivationTool.deriveViewingKey(spendingKey: spendingKey), - let uAddr = try? derivationTool.deriveUnifiedAddress(viewingKey: viewingKey) - else { - result( - .failure( - TransactionManagerError.shieldingEncodingFailed( - pendingTransaction, - reason: "There was an error Deriving your keys" - ) - ) - ) - return - } - - guard pendingTransaction.toAddress == uAddr else { - result( - .failure( - TransactionManagerError.shieldingEncodingFailed( - 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 - } do { let encodedTransaction = try self.encoder.createShieldingTransaction( - spendingKey: spendingKey, tAccountPrivateKey: xprv, memo: pendingTransaction.memo?.asZcashTransactionMemo(), from: pendingTransaction.accountIndex @@ -140,7 +103,7 @@ class PersistentTransactionManager: OutboundTransactionManager { } func encode( - spendingKey: String, + spendingKey: SaplingExtendedSpendingKey, pendingTransaction: PendingTransactionEntity, result: @escaping (Result) -> Void ) { @@ -149,7 +112,7 @@ class PersistentTransactionManager: OutboundTransactionManager { do { let encodedTransaction = try self.encoder.createTransaction( spendingKey: spendingKey, - zatoshi: pendingTransaction.intValue, + zatoshi: pendingTransaction.value, to: pendingTransaction.toAddress, memo: pendingTransaction.memo?.asZcashTransactionMemo(), from: pendingTransaction.accountIndex @@ -266,10 +229,6 @@ class PersistentTransactionManager: OutboundTransactionManager { .forEach { try self.repository.update($0) } } - func monitorChanges(byId: Int, observer: Any) { - // TODO: Implement this - } - func cancel(pendingTransaction: PendingTransactionEntity) -> Bool { guard let id = pendingTransaction.id else { return false } diff --git a/Sources/ZcashLightClientKit/Transaction/TransactionEncoder.swift b/Sources/ZcashLightClientKit/Transaction/TransactionEncoder.swift index fa3695ff..e71f99a5 100644 --- a/Sources/ZcashLightClientKit/Transaction/TransactionEncoder.swift +++ b/Sources/ZcashLightClientKit/Transaction/TransactionEncoder.swift @@ -18,47 +18,45 @@ public enum TransactionEncoderError: Error { } protocol TransactionEncoder { - /** - Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation - doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using - double-bangs for things). - Blocking - - - Parameters: - - Parameter spendingKey: a string containing the spending key - - Parameter zatoshi: the amount to send in zatoshis - - Parameter to: string containing the recipient address - - Parameter memo: string containing the memo (optional) - - Parameter accountIndex: index of the account that will be used to send the funds - - - Throws: a TransactionEncoderError - */ + + /// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation + /// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using + /// double-bangs for things). + /// Blocking + /// + /// - Parameters: + /// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key + /// - Parameter zatoshi: the amount to send in `Zatoshi` + /// - Parameter to: string containing the recipient address + /// - Parameter memo: string containing the memo (optional) + /// - Parameter accountIndex: index of the account that will be used to send the funds + /// + /// - Throws: a TransactionEncoderError func createTransaction( - spendingKey: String, - zatoshi: Int, + spendingKey: SaplingExtendedSpendingKey, + zatoshi: Zatoshi, to address: String, memo: String?, from accountIndex: Int ) throws -> EncodedTransaction - /** - Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation - doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using - double-bangs for things). - Non-blocking - - - Parameters: - - Parameter spendingKey: a string containing the spending key - - Parameter zatoshi: the amount to send in zatoshis - - Parameter to: string containing the recipient address - - Parameter memo: string containing the memo (optional) - - 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 - */ + + /// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation + /// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using + /// double-bangs for things). + /// Non-blocking + /// + /// - Parameters: + /// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key + /// - Parameter zatoshi: the amount to send in `Zatoshi` + /// - Parameter to: string containing the recipient address + /// - Parameter memo: string containing the memo (optional) + /// - 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 // swiftlint:disable:next function_parameter_count func createTransaction( - spendingKey: String, - zatoshi: Int, + spendingKey: SaplingExtendedSpendingKey, + zatoshi: Zatoshi, to address: String, memo: String?, from accountIndex: Int, @@ -70,7 +68,6 @@ protocol TransactionEncoder { Blocking - Parameters: - - Parameter spendingKey: a string containing the spending key - Parameter tAccountPrivateKey: transparent account private key to spend the UTXOs - Parameter memo: string containing the memo (optional) - Parameter accountIndex: index of the account that will be used to send the funds @@ -78,34 +75,11 @@ protocol TransactionEncoder { - Throws: a TransactionEncoderError */ func createShieldingTransaction( - spendingKey: String, - tAccountPrivateKey: String, + tAccountPrivateKey: TransparentAccountPrivKey, 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 doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using double-bangs for things). - - Non-Blocking - - - Parameters: - - Parameter spendingKey: a string containing the spending key - - Parameter tAccountPrivateKey: transparent account private key to spend the UTXOs - - Parameter memo: string containing the memo (optional) - - Parameter accountIndex: index of the account that will be used to send the funds - - - Returns: a TransactionEncoderResultBlock - */ - - func createShieldingTransaction( - spendingKey: String, - tAccountPrivateKey: String, - memo: String?, - from accountIndex: Int, - result: @escaping TransactionEncoderResultBlock - ) - /** Fetch the Transaction Entity from the encoded representation - Parameter encodedTransaction: The encoded transaction to expand diff --git a/Sources/ZcashLightClientKit/Transaction/TransactionManager.swift b/Sources/ZcashLightClientKit/Transaction/TransactionManager.swift index fb78c9b8..a9cc4277 100644 --- a/Sources/ZcashLightClientKit/Transaction/TransactionManager.swift +++ b/Sources/ZcashLightClientKit/Transaction/TransactionManager.swift @@ -14,17 +14,34 @@ transactions through to completion. */ protocol OutboundTransactionManager { - func initSpend(zatoshi: Zatoshi, toAddress: String, memo: String?, from accountIndex: Int) throws -> PendingTransactionEntity + func initSpend( + zatoshi: Zatoshi, + toAddress: String, + memo: String?, + from accountIndex: Int + ) throws -> PendingTransactionEntity - func encodeShieldingTransaction(spendingKey: String, xprv: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result) -> Void) + func encodeShieldingTransaction( + xprv: TransparentAccountPrivKey, + pendingTransaction: PendingTransactionEntity, + result: @escaping (Result) -> Void + ) - func encode(spendingKey: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result) -> Void) + func encode( + spendingKey: SaplingExtendedSpendingKey, + pendingTransaction: PendingTransactionEntity, + result: @escaping (Result + ) -> Void) - func submit(pendingTransaction: PendingTransactionEntity, result: @escaping (Result) -> Void) + func submit( + pendingTransaction: PendingTransactionEntity, + result: @escaping (Result) -> Void + ) - func applyMinedHeight(pendingTransaction: PendingTransactionEntity, minedHeight: BlockHeight) throws -> PendingTransactionEntity - - func monitorChanges(byId: Int, observer: Any) // check this out. code smell + func applyMinedHeight( + pendingTransaction: PendingTransactionEntity, + minedHeight: BlockHeight + ) throws -> PendingTransactionEntity /** Attempts to Cancel a transaction. Returns true if successful diff --git a/Sources/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift b/Sources/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift index d0074ae5..9f020410 100644 --- a/Sources/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift +++ b/Sources/ZcashLightClientKit/Transaction/WalletTransactionEncoder.swift @@ -50,8 +50,8 @@ class WalletTransactionEncoder: TransactionEncoder { } func createTransaction( - spendingKey: String, - zatoshi: Int, + spendingKey: SaplingExtendedSpendingKey, + zatoshi: Zatoshi, to address: String, memo: String?, from accountIndex: Int @@ -80,8 +80,8 @@ class WalletTransactionEncoder: TransactionEncoder { // swiftlint:disable:next function_parameter_count func createTransaction( - spendingKey: String, - zatoshi: Int, + spendingKey: SaplingExtendedSpendingKey, + zatoshi: Zatoshi, to address: String, memo: String?, from accountIndex: Int, @@ -107,7 +107,13 @@ class WalletTransactionEncoder: TransactionEncoder { } } - func createSpend(spendingKey: String, zatoshi: Int, to address: String, memo: String?, from accountIndex: Int) throws -> Int { + func createSpend( + spendingKey: SaplingExtendedSpendingKey, + zatoshi: Zatoshi, + to address: String, + memo: String?, + from accountIndex: Int + ) throws -> Int { guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else { throw TransactionEncoderError.missingParams } @@ -115,9 +121,9 @@ class WalletTransactionEncoder: TransactionEncoder { let txId = rustBackend.createToAddress( dbData: self.dataDbURL, account: Int32(accountIndex), - extsk: spendingKey, + extsk: spendingKey.stringEncoded, to: address, - value: Int64(zatoshi), + value: zatoshi.amount, memo: memo, spendParamsPath: self.spendParamsURL.path, outputParamsPath: self.outputParamsURL.path, @@ -132,14 +138,12 @@ class WalletTransactionEncoder: TransactionEncoder { } func createShieldingTransaction( - spendingKey: String, - tAccountPrivateKey: String, + tAccountPrivateKey: TransparentAccountPrivKey, memo: String?, from accountIndex: Int ) throws -> EncodedTransaction { let txId = try createShieldingSpend( - spendingKey: spendingKey, - xprv: tAccountPrivateKey, + xprv: tAccountPrivateKey.encoding, memo: memo, accountIndex: accountIndex ) @@ -158,19 +162,7 @@ class WalletTransactionEncoder: TransactionEncoder { } } - func createShieldingTransaction( - spendingKey: String, - tAccountPrivateKey: String, - memo: String?, - from accountIndex: Int, - result: @escaping TransactionEncoderResultBlock - ) { - queue.async { - result(.failure(RustWeldingError.genericError(message: "not implemented"))) - } - } - - func createShieldingSpend(spendingKey: String, xprv: String, memo: String?, accountIndex: Int) throws -> Int { + func createShieldingSpend(xprv: String, memo: String?, accountIndex: Int) throws -> Int { guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else { throw TransactionEncoderError.missingParams } @@ -180,7 +172,6 @@ class WalletTransactionEncoder: TransactionEncoder { dbData: self.dataDbURL, account: Int32(accountIndex), xprv: xprv, - extsk: spendingKey, memo: memo, spendParamsPath: self.spendParamsURL.path, outputParamsPath: self.outputParamsURL.path, diff --git a/Tests/DarksideTests/AdvancedReOrgTests.swift b/Tests/DarksideTests/AdvancedReOrgTests.swift index 7b0e2050..a2d8e11e 100644 --- a/Tests/DarksideTests/AdvancedReOrgTests.swift +++ b/Tests/DarksideTests/AdvancedReOrgTests.swift @@ -302,7 +302,7 @@ class AdvancedReOrgTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: coordinator.spendingKeys!.first!, zatoshi: sendAmount, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test transaction", from: 0 ) { result in @@ -709,7 +709,7 @@ class AdvancedReOrgTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: Zatoshi(20000), - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in @@ -1069,7 +1069,7 @@ class AdvancedReOrgTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: Zatoshi(20000), - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in diff --git a/Tests/DarksideTests/BalanceTests.swift b/Tests/DarksideTests/BalanceTests.swift index 409f3f7e..2c0db15e 100644 --- a/Tests/DarksideTests/BalanceTests.swift +++ b/Tests/DarksideTests/BalanceTests.swift @@ -82,7 +82,7 @@ class BalanceTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKey, zatoshi: maxBalance, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test send \(self.description) \(Date().description)", from: 0, resultBlock: { result in @@ -226,7 +226,7 @@ class BalanceTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKey, zatoshi: maxBalanceMinusOne, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test send \(self.description) \(Date().description)", from: 0, resultBlock: { result in @@ -367,7 +367,7 @@ class BalanceTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKey, zatoshi: maxBalanceMinusOne, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test send \(self.description) \(Date().description)", from: 0, resultBlock: { result in @@ -510,7 +510,7 @@ class BalanceTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKey, zatoshi: sendAmount, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test send \(self.description) \(Date().description)", from: 0, resultBlock: { result in @@ -681,7 +681,7 @@ class BalanceTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKey, zatoshi: sendAmount, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test send \(self.description) \(Date().description)", from: 0, resultBlock: { result in @@ -836,7 +836,7 @@ class BalanceTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKeys, zatoshi: sendAmount, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: memo, from: 0, resultBlock: { sendResult in @@ -1008,7 +1008,7 @@ class BalanceTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKey, zatoshi: sendAmount, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test send \(self.description)", from: 0, resultBlock: { result in diff --git a/Tests/DarksideTests/NetworkUpgradeTests.swift b/Tests/DarksideTests/NetworkUpgradeTests.swift index ac77b350..af87fc3b 100644 --- a/Tests/DarksideTests/NetworkUpgradeTests.swift +++ b/Tests/DarksideTests/NetworkUpgradeTests.swift @@ -82,7 +82,7 @@ class NetworkUpgradeTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in @@ -171,7 +171,7 @@ class NetworkUpgradeTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in @@ -240,7 +240,7 @@ class NetworkUpgradeTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in @@ -341,7 +341,7 @@ class NetworkUpgradeTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in @@ -453,7 +453,7 @@ class NetworkUpgradeTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: spendAmount, - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in diff --git a/Tests/DarksideTests/PendingTransactionUpdatesTest.swift b/Tests/DarksideTests/PendingTransactionUpdatesTest.swift index 7bba41df..b4fa0e78 100644 --- a/Tests/DarksideTests/PendingTransactionUpdatesTest.swift +++ b/Tests/DarksideTests/PendingTransactionUpdatesTest.swift @@ -97,7 +97,7 @@ class PendingTransactionUpdatesTest: XCTestCase { // swiftlint:disable:next force_unwrapping spendingKey: self.coordinator.spendingKeys!.first!, zatoshi: Zatoshi(20000), - toAddress: self.testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "this is a test", from: 0, resultBlock: { result in diff --git a/Tests/DarksideTests/RewindRescanTests.swift b/Tests/DarksideTests/RewindRescanTests.swift index 316084e4..c64fe284 100644 --- a/Tests/DarksideTests/RewindRescanTests.swift +++ b/Tests/DarksideTests/RewindRescanTests.swift @@ -169,7 +169,7 @@ class RewindRescanTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: coordinator.spendingKey, zatoshi: Zatoshi(1000), - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: nil, from: 0 ) { result in @@ -270,7 +270,7 @@ class RewindRescanTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: spendingKey, zatoshi: maxBalance, - toAddress: testRecipientAddress, + toAddress: try Recipient(testRecipientAddress, network: self.network.networkType), memo: "test send \(self.description) \(Date().description)", from: 0 ) { result in diff --git a/Tests/DarksideTests/ShieldFundsTests.swift b/Tests/DarksideTests/ShieldFundsTests.swift index 07e7e2ae..02e0e6cc 100644 --- a/Tests/DarksideTests/ShieldFundsTests.swift +++ b/Tests/DarksideTests/ShieldFundsTests.swift @@ -84,7 +84,7 @@ class ShieldFundsTests: XCTestCase { /// func testShieldFunds() throws { // 1. load the dataset - try coordinator.service.useDataset(from: "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/shielding-dataset/shield-funds/1631000.txt") + try coordinator.service.useDataset(from: "https://github.com/zcash-hackworks/darksidewalletd-test-data/blob/master/shield-funds/1631000.txt") try coordinator.stageBlockCreate(height: birthday + 1, count: 200, nonce: 0) @@ -209,7 +209,6 @@ class ShieldFundsTests: XCTestCase { // shield the funds coordinator.synchronizer.shieldFunds( - spendingKey: coordinator.spendingKey, transparentAccountPrivateKey: transparentAccountPrivateKey, memo: "shield funds", from: 0 diff --git a/Tests/DarksideTests/Z2TReceiveTests.swift b/Tests/DarksideTests/Z2TReceiveTests.swift index f673b497..6f746a4c 100644 --- a/Tests/DarksideTests/Z2TReceiveTests.swift +++ b/Tests/DarksideTests/Z2TReceiveTests.swift @@ -104,7 +104,7 @@ class Z2TReceiveTests: XCTestCase { coordinator.synchronizer.sendToAddress( spendingKey: coordinator.spendingKeys!.first!, zatoshi: sendAmount, - toAddress: testRecipientAddress, + toAddress: try! Recipient(testRecipientAddress, network: self.network.networkType), memo: "test transaction", from: 0 ) { result in diff --git a/Tests/NetworkTests/BlockScanOperationTests.swift b/Tests/NetworkTests/BlockScanOperationTests.swift index 5d834f23..f63493cd 100644 --- a/Tests/NetworkTests/BlockScanOperationTests.swift +++ b/Tests/NetworkTests/BlockScanOperationTests.swift @@ -19,9 +19,9 @@ class BlockScanOperationTests: XCTestCase { var cacheDbURL: URL! var dataDbURL: URL! - var ufvk = UFVFakeKey( - account: 0, - encoding: "uviewtest1q48t999peecrfkq7ykcxckfkjt77w3lckk5mptlrtuy7xltjnzg8fm5434cxe9p9838ljs24yv83rluhk33ew098dkarapzyj4vk5kfxp5zn2jp3ww74jwd48r05aqjvgqxzx3nqn6zfqh3cmwdtmz0mc5624tvdza55q7mguxrehwcy4y0uktcpp4tkpex4qhazddux4yt6hr0sc9fkqmfr5tyz6ldd7yrq93tyj7446u4kst3vhmd40uga636p56hr0hjfdhgp07qyh90kmsl3qnmld6c8h7u06vekkjywmxv07mqzz9muwcl6weczrn5vf3p27uc9ufrumdp64zdzulzvc373wx3gl0yntntujhcsjhrwk9xwyjpvyuf0s8q3mgjs7uy3pg960w40dthpngcnauhgg9xq8cdcyfkq7ctnngqg4nkp5eh9knd4ckwjyd9czdd240lumul96r2fuerlvjeha6cyn9ftm7gr6xqjmq0zy6tv" // swiftlint:disable:this line_length + var ufvk = UnifiedFullViewingKey( + validatedEncoding: "uviewtest1q48t999peecrfkq7ykcxckfkjt77w3lckk5mptlrtuy7xltjnzg8fm5434cxe9p9838ljs24yv83rluhk33ew098dkarapzyj4vk5kfxp5zn2jp3ww74jwd48r05aqjvgqxzx3nqn6zfqh3cmwdtmz0mc5624tvdza55q7mguxrehwcy4y0uktcpp4tkpex4qhazddux4yt6hr0sc9fkqmfr5tyz6ldd7yrq93tyj7446u4kst3vhmd40uga636p56hr0hjfdhgp07qyh90kmsl3qnmld6c8h7u06vekkjywmxv07mqzz9muwcl6weczrn5vf3p27uc9ufrumdp64zdzulzvc373wx3gl0yntntujhcsjhrwk9xwyjpvyuf0s8q3mgjs7uy3pg960w40dthpngcnauhgg9xq8cdcyfkq7ctnngqg4nkp5eh9knd4ckwjyd9czdd240lumul96r2fuerlvjeha6cyn9ftm7gr6xqjmq0zy6tv", // swiftlint:disable:this line_length + account: 0 ) var walletBirthDay = Checkpoint.birthday( @@ -268,8 +268,3 @@ extension BlockScanOperationTests: CompactBlockProgressDelegate { func progressUpdated(_ progress: CompactBlockProgress) { } } - -struct UFVFakeKey: UnifiedFullViewingKey { - var account: UInt32 - var encoding: String -} diff --git a/Tests/NetworkTests/ZcashRustBackendTests.swift b/Tests/NetworkTests/ZcashRustBackendTests.swift index b05985b9..26d8a254 100644 --- a/Tests/NetworkTests/ZcashRustBackendTests.swift +++ b/Tests/NetworkTests/ZcashRustBackendTests.swift @@ -53,50 +53,48 @@ class ZcashRustBackendTests: XCTestCase { func testDeriveExtendedSpendingKeys() { let seed = Array("testreferencealicetestreferencealice".utf8) - var spendingKeys: [String]? - XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) + var spendingKeys: [SaplingExtendedSpendingKey]? + XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) XCTAssertNotNil(spendingKeys) - XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true) + XCTAssertEqual(spendingKeys?.count, 1) } func testDeriveExtendedFullViewingKeys() { let seed = Array("testreferencealicetestreferencealice".utf8) - var fullViewingKeys: [String]? + var fullViewingKeys: [SaplingExtendedFullViewingKey]? XCTAssertNoThrow( try { - fullViewingKeys = try ZcashRustBackend.deriveExtendedFullViewingKeys( + fullViewingKeys = try ZcashRustBackend.deriveSaplingExtendedFullViewingKeys( seed: seed, - accounts: 1, + accounts: 2, networkType: networkType ) }() ) XCTAssertNotNil(fullViewingKeys) - XCTAssertFalse(fullViewingKeys?.first?.isEmpty ?? true) + XCTAssertEqual(fullViewingKeys?.count, 2) } func testDeriveExtendedFullViewingKey() { let seed = Array("testreferencealicetestreferencealice".utf8) - var fullViewingKey: String? + var fullViewingKey: SaplingExtendedFullViewingKey? - var spendingKeys: [String]? - XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) + var spendingKeys: [SaplingExtendedSpendingKey]? + XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) XCTAssertNotNil(spendingKeys) - XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true) guard let spendingKey = spendingKeys?.first else { XCTFail("no spending key generated") return } - XCTAssertNoThrow(try { fullViewingKey = try ZcashRustBackend.deriveExtendedFullViewingKey(spendingKey, networkType: networkType) }()) + XCTAssertNoThrow(try { fullViewingKey = try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(spendingKey, networkType: networkType) }()) XCTAssertNotNil(fullViewingKey) - XCTAssertFalse(fullViewingKey?.isEmpty ?? true) } func testInitAndScanBlocks() { @@ -164,12 +162,12 @@ class ZcashRustBackendTests: XCTestCase { } } - func testIsValidShieldedAddressTrue() { + func testIsValidSaplingAddressTrue() { var isValid: Bool? XCTAssertNoThrow( try { - isValid = try ZcashRustBackend.isValidShieldedAddress( + isValid = try ZcashRustBackend.isValidSaplingAddress( "ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc", networkType: networkType ) @@ -183,12 +181,12 @@ class ZcashRustBackendTests: XCTestCase { } } - func testIsValidShieldedAddressFalse() { + func testIsValidSaplingAddressFalse() { var isValid: Bool? XCTAssertNoThrow( try { - isValid = try ZcashRustBackend.isValidShieldedAddress( + isValid = try ZcashRustBackend.isValidSaplingAddress( "tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7", networkType: networkType ) diff --git a/Tests/OfflineTests/DerivationToolTests/DerivatioToolTestnetTests.swift b/Tests/OfflineTests/DerivationToolTests/DerivatioToolTestnetTests.swift deleted file mode 100644 index 9d6a55d6..00000000 --- a/Tests/OfflineTests/DerivationToolTests/DerivatioToolTestnetTests.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// DerivatioToolTestnetTests.swift -// ZcashLightClientKit-Unit-DerivationToolTests -// -// Created by Francisco Gindre on 7/26/21. -// -// swift-format-ignore-file - -import XCTest -@testable import ZcashLightClientKit - -class DerivatioToolTestnetTests: XCTestCase { - var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" //TODO: Parameterize this from environment? - var seedData: Data = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")! - let testRecipientAddress = "utest1uqmec4a2njqz2z2rwppchsd06qe7a0jh4jmsqr0yy99m9er9646zlxunf3v8qr0hncgv86e8a62vxy0qa32qzetmj8s57yudmyx9zav6f52nurclsqjkqtjtpz6vg679p6wkczpl2wu" //TODO: Parameterize this from environment - - let expectedSpendingKey = "secret-extended-key-test1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6lk8xce3d4jw7s8ln5yjp6fqv2g0nzue2hc0kv5t004vklvlenncscq9flwh5vf5qnv0hnync72n7gjn70u47765v3kyrxytx50g730svvmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqe49swv" - - let expectedViewingKey = "uviewtest16dqd5q7zd3xfxlcm2jm5k95zd92ed3qcm5jr9uq6yl5y2h6vuwfpxlnndckv5hpwsajgvq26xgdcdns8mqclecl0zh4sph45t4ncfnhcsus0k6sashfknsp9ltxrxlf096ljkwmp7psh3z2vmcd3rcc72qaujsl2y23ajexhr7qza29u9k95frs8qqgvy83rgymt7mmw09a02a5ytjpa502tshlsgl2j736jlfuzt27gezlajrs2tw9c99uqxrj5sx942vdr7w6yk2ltz96yq7n96fd9nr48c59dh9znqtwtm0nt9tmt7vzwhdwzt00tgp57mn85hpe6w00upmjv52kct9y" - let expectedSaplingExtendedViewingKey = "zxviewtestsapling1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6l5smlqrckkl2x5rnrauzc4gp665q3zyw0qf2sfdsx5wpp832htfavqk72uchuuvq2dpmgk8jfaza5t5l56u66fpx0sr8ewp9s3wj2txavmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqgegsaj" - - let derivationTool = DerivationTool(networkType: NetworkType.testnet) - let expectedTransparentAddress = "tmXuTnE11JojToagTqxXUn6KvdxDE3iLKbp" - func testDeriveViewingKeysFromSeed() throws { - let accounts: Int = 1 - let seedBytes = [UInt8](seedData) - let viewingKeys = try derivationTool.deriveViewingKeys(seed: seedBytes, numberOfAccounts: accounts) - - XCTAssertEqual(viewingKeys.count, accounts, "the number of viewing keys have to match the number of account requested to derive") - - guard let viewingKey = viewingKeys.first else { - XCTFail("no viewing key generated") - return - } - XCTAssertEqual(expectedViewingKey, viewingKey) - - } - - func testDeriveViewingKeyFromSpendingKeys() throws { - XCTAssertEqual(expectedSaplingExtendedViewingKey, try derivationTool.deriveViewingKey(spendingKey: expectedSpendingKey)) - } - - func testDeriveSpendingKeysFromSeed() throws { - let accounts: Int = 1 - let seedBytes = [UInt8](seedData) - - let spendingKeys = try derivationTool.deriveSpendingKeys(seed: seedBytes, numberOfAccounts: accounts) - XCTAssertEqual(spendingKeys.count, accounts, "the number of viewing keys have to match the number of account requested to derive") - - guard let spendingKey = spendingKeys.first else { - XCTFail("no viewing key generated") - return - } - XCTAssertEqual(expectedSpendingKey, spendingKey) - - } - - func testDeriveUnifiedAddressFromSeed() throws { - let seedBytes = [UInt8](seedData) - - let shieldedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0) - XCTAssertEqual(shieldedAddress, testRecipientAddress) - } - - func testDeriveUnifiedAddressFromViewingKey() throws { - XCTAssertEqual(try derivationTool.deriveUnifiedAddress(viewingKey: expectedViewingKey), testRecipientAddress) - } - - func testDeriveTransparentAddressFromSeed() throws { - XCTAssertEqual(try derivationTool.deriveTransparentAddress(seed: [UInt8](seedData)), expectedTransparentAddress) - } - - func testIsValidViewingKey() throws { - XCTAssertTrue(try derivationTool.isValidExtendedViewingKey(self.expectedSaplingExtendedViewingKey)) - - XCTAssertFalse(try derivationTool.isValidExtendedViewingKey("zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy")) - } - - func testDeriveTransparentAccountPrivateKeyFromSeed() throws { - XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), "xprv9yURYog8Ds8XB36PVzPadbVaCPwVm4CZVMejW9bPPTqBCY8oLssPbe1MhJhPzSbVeg7cWZtuXxuUy2urADuAJUaN27c5f9nErx68SQokG1b") - } - - func testDeriveUnifiedKeysFromSeed() throws { - let unifiedKeys = try derivationTool.deriveUnifiedFullViewingKeysFromSeed([UInt8](seedData), numberOfAccounts: 1) - XCTAssertEqual(unifiedKeys.count, 1) - - XCTAssertEqual(unifiedKeys[0].account, 0) - XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey) - } - - func testDeriveQuiteALotOfUnifiedKeysFromSeed() throws { - let unifiedKeys = try derivationTool.deriveUnifiedFullViewingKeysFromSeed([UInt8](seedData), numberOfAccounts: 10) - XCTAssertEqual(unifiedKeys.count, 10) - - XCTAssertEqual(unifiedKeys[0].account, 0) - XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey) - } -} diff --git a/Tests/OfflineTests/DerivationToolTests/DerivationToolMainnetTests.swift b/Tests/OfflineTests/DerivationToolTests/DerivationToolMainnetTests.swift index 333ee57d..c16c1118 100644 --- a/Tests/OfflineTests/DerivationToolTests/DerivationToolMainnetTests.swift +++ b/Tests/OfflineTests/DerivationToolTests/DerivationToolMainnetTests.swift @@ -6,24 +6,26 @@ // //swiftlint:disable force_unwrapping import XCTest -import ZcashLightClientKit +@testable import ZcashLightClientKit class DerivationToolMainnetTests: XCTestCase { var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" //TODO: Parameterize this from environment? var seedData: Data = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")! - let testRecipientAddress = "u1l9f0l4348negsncgr9pxd9d3qaxagmqv3lnexcplmufpq7muffvfaue6ksevfvd7wrz7xrvn95rc5zjtn7ugkmgh5rnxswmcj30y0pw52pn0zjvy38rn2esfgve64rj5pcmazxgpyuj" //TODO: Parameterize this from environment + let testRecipientAddress = UnifiedAddress(validatedEncoding: "u1l9f0l4348negsncgr9pxd9d3qaxagmqv3lnexcplmufpq7muffvfaue6ksevfvd7wrz7xrvn95rc5zjtn7ugkmgh5rnxswmcj30y0pw52pn0zjvy38rn2esfgve64rj5pcmazxgpyuj") //TODO: Parameterize this from environment - let expectedSpendingKey = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" + let expectedSpendingKey = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv") - let expectedViewingKey = "uview1jpddskrm73gpgrsx00y4dryapkhjlzrm5wfdcue77a26u3e7u28qu0xfsgzwt72rs60rjnwujr93al6sxchste78p8vvrlperlvladfwkyryakdutykdcqgqn9dfn9my6k3aka5ej78leksj6aptqs9yzcysszwzwr6zmrcqycxxlg87ten6ers6urmxthe3pvvh07ga7t4uz92a5y0jgej94a7u9q3nezjqj4zm634x2wc2d8d39nu74jew79phf9u025p82d8qshq0pnzcjcnke0g72gva28qsx0wvtad7qjwld5khgudwlxmx24av2mq4k5k9zypheeppcpnujc9rqpm" - let expectedSaplingExtendedViewingKey = "zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy" + let expectedViewingKey = UnifiedFullViewingKey(validatedEncoding: "uview17fme6ux853km45g9ep07djpfzeydxxgm22xpmr7arzxyutlusalgpqlx7suga4ahzywfuwz4jclm00u7g8u65qvvdt45kttnfunvschssg3h3g06txs9ja32vx3xa8dej3unnatgzjvd0vumk37t8es3ludldrtse3q6226ws7eq4q0ywz78nudwpepgdn7jmxz8yvp7k6gxkeynkam0f8aqf9qpeaej55zhkw39x7epayhndul0j4xjttdxxlnwcd09nr8svyx8j0zng0w6scx3m5unpkaqxcm3hslhlfg4caz7r8d4xy9wm7klkg79w7j0uyzec5s3yje20eg946r6rmkf532nfydu26s8q9ua7mwxw2j2ag7hfcuu652gw6uta03vlm05zju3a9rwc4h367kqzfqrcz35pdwdk2a7yqnk850un3ujxcvve45ueajgvtr6dj4ufszgqwdy0aedgmkalx2p7qed2suarwkr35dl0c8dnqp3", account: 0) + let expectedSaplingExtendedViewingKey = SaplingExtendedFullViewingKey(validatedEncoding: "zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy") + + let expectedSaplingAddress = SaplingAddress(validatedEncoding: "zs1vp7kvlqr4n9gpehztr76lcn6skkss9p8keqs3nv8avkdtjrcctrvmk9a7u494kluv756jeee5k0") let derivationTool = DerivationTool(networkType: NetworkType.mainnet) - let expectedTransparentAddress = "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz" + let expectedTransparentAddress = TransparentAddress(validatedEncoding: "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz") func testDeriveViewingKeysFromSeed() throws { let accounts: Int = 1 let seedBytes = [UInt8](seedData) - let viewingKeys = try derivationTool.deriveViewingKeys(seed: seedBytes, numberOfAccounts: accounts) + let viewingKeys = try derivationTool.deriveUnifiedFullViewingKeys(seed: seedBytes, numberOfAccounts: accounts) XCTAssertEqual(viewingKeys.count, accounts, "the number of viewing keys have to match the number of account requested to derive") @@ -57,12 +59,12 @@ class DerivationToolMainnetTests: XCTestCase { func testDeriveUnifiedAddressFromSeed() throws { let seedBytes = [UInt8](seedData) - let shieldedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0) - XCTAssertEqual(shieldedAddress, testRecipientAddress) + let unifiedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0) + XCTAssertEqual(unifiedAddress, testRecipientAddress) } func testDeriveUnifiedAddressFromViewingKey() throws { - XCTAssertEqual(try derivationTool.deriveUnifiedAddress(viewingKey: expectedViewingKey), testRecipientAddress) + XCTAssertEqual(try derivationTool.deriveUnifiedAddress(from: expectedViewingKey), testRecipientAddress) } func testDeriveTransparentAddressFromSeed() throws { @@ -76,7 +78,7 @@ class DerivationToolMainnetTests: XCTestCase { } func testDeriveTransparentAccountPrivateKeyFromSeed() throws { - XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), "xprv9yCTU6giJ1qZ1DLC5rc7KMzwY9s8rSRXYqmoAKffAExpUVUKLhcdvN9ERdxjEW8tQq4pxerLKZE3WcNUKZCeX19rVTxpV2msTyNMNiFT3Nw") + XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), TransparentAccountPrivKey(encoding: "xprv9yCTU6giJ1qZ1DLC5rc7KMzwY9s8rSRXYqmoAKffAExpUVUKLhcdvN9ERdxjEW8tQq4pxerLKZE3WcNUKZCeX19rVTxpV2msTyNMNiFT3Nw")) } func testDeriveUnifiedKeysFromSeed() throws { @@ -84,7 +86,7 @@ class DerivationToolMainnetTests: XCTestCase { XCTAssertEqual(unifiedKeys.count, 1) XCTAssertEqual(unifiedKeys[0].account, 0) - XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey) + XCTAssertEqual(unifiedKeys[0], expectedViewingKey) } func testDeriveQuiteALotOfUnifiedKeysFromSeed() throws { @@ -92,11 +94,25 @@ class DerivationToolMainnetTests: XCTestCase { XCTAssertEqual(unifiedKeys.count, 10) XCTAssertEqual(unifiedKeys[0].account, 0) - XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey) + XCTAssertEqual(unifiedKeys[0], expectedViewingKey) } func testShouldFailOnInvalidChecksumAddresses() throws { let testAddress = "t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1" XCTAssertFalse(try derivationTool.isValidTransparentAddress(testAddress)) } + + func testSpendingKeyValidation() throws { + XCTAssertTrue(try derivationTool.isValidSaplingExtendedSpendingKey(expectedSpendingKey.stringEncoded)) + } + + func testSpendingKeyValidationFailsOnInvalidKey() throws { + let wrongSpendingKey = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vvZzZzZz" + + XCTAssertFalse(try derivationTool.isValidSaplingExtendedSpendingKey(wrongSpendingKey)) + } + // TODO: Address encoding does not catch this test https://github.com/zcash/ZcashLightClientKit/issues/509 +// func testSpendingKeyValidationThrowsWhenWrongNetwork() throws { +// XCTAssertThrowsError(try derivationTool.isValidExtendedSpendingKey("secret-extended-key-test1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6lk8xce3d4jw7s8ln5yjp6fqv2g0nzue2hc0kv5t004vklvlenncscq9flwh5vf5qnv0hnync72n7gjn70u47765v3kyrxytx50g730svvmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqe49swv")) +// } } diff --git a/Tests/OfflineTests/DerivationToolTests/DerivationToolTestnetTests.swift b/Tests/OfflineTests/DerivationToolTests/DerivationToolTestnetTests.swift new file mode 100644 index 00000000..98223ca6 --- /dev/null +++ b/Tests/OfflineTests/DerivationToolTests/DerivationToolTestnetTests.swift @@ -0,0 +1,116 @@ +// +// DerivatioToolTestnetTests.swift +// ZcashLightClientKit-Unit-DerivationToolTests +// +// Created by Francisco Gindre on 7/26/21. +// +// swift-format-ignore-file + +import XCTest +@testable import ZcashLightClientKit + +class DerivationToolTestnetTests: XCTestCase { + var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" //TODO: Parameterize this from environment? + var seedData: Data = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")! + let testRecipientAddress = UnifiedAddress(validatedEncoding: "utest1uqmec4a2njqz2z2rwppchsd06qe7a0jh4jmsqr0yy99m9er9646zlxunf3v8qr0hncgv86e8a62vxy0qa32qzetmj8s57yudmyx9zav6f52nurclsqjkqtjtpz6vg679p6wkczpl2wu") //TODO: Parameterize this from environment + + let expectedSpendingKey = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-test1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6lk8xce3d4jw7s8ln5yjp6fqv2g0nzue2hc0kv5t004vklvlenncscq9flwh5vf5qnv0hnync72n7gjn70u47765v3kyrxytx50g730svvmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqe49swv") + + let expectedViewingKey = UnifiedFullViewingKey(validatedEncoding: "uviewtest12tkgzhaevmw78us4xj2cx6ehxjgpp5da2qwrjqvytztejqfjdmy3e6nryqggtwrjum5cefuuuky8rscuw5dynmjec2tx3kkupqexw4va879pf874kvp6r8kjeza26gysxllaqwl67hm9u0jjke06zc93asrpw4wmy3g0lr9r5cy9pz49q2g7y7wm2pls5akmzhuvqr7khftk93aa2kpvwp7n3sjtmef28mxg3n2rpctsjlgsrhc29g6r23qc0u4tzd8rz8vqq4j7jxummdts8zx0jatzw4l2tl7r3egxhlw587rtkjx0y6dvw4hf4vjprn0qv3hs0sulmavk84ajeewn7argyerpr4essqvgfd0d24jpz6phxlasnd58qazh9d3yc6ad3hc5atp0pkvlq053zga65gscp0pv2plhqj9y2tcmx43thw5g4v8z3unytkc2dhyttuhmnlh5dyz4rmhgfkc96tp8z8rpfe35whjvky0jagz5n7qx", account: 0) + + let expectedSaplingExtendedViewingKey = SaplingExtendedFullViewingKey(validatedEncoding: "zxviewtestsapling1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6l5smlqrckkl2x5rnrauzc4gp665q3zyw0qf2sfdsx5wpp832htfavqk72uchuuvq2dpmgk8jfaza5t5l56u66fpx0sr8ewp9s3wj2txavmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqgegsaj") + + let expectedSaplingAddress = SaplingAddress(validatedEncoding: "ztestsapling1475xtm56czrzmleqzzlu4cxvjjfsy2p6rv78q07232cpsx5ee52k0mn5jyndq09mampkgvrxnwg") + + let derivationTool = DerivationTool(networkType: NetworkType.testnet) + let expectedTransparentAddress = TransparentAddress(validatedEncoding: "tmXuTnE11JojToagTqxXUn6KvdxDE3iLKbp") + func testDeriveViewingKeysFromSeed() throws { + let accounts: Int = 1 + let seedBytes = [UInt8](seedData) + let viewingKeys = try derivationTool.deriveUnifiedFullViewingKeys(seed: seedBytes, numberOfAccounts: accounts) + + XCTAssertEqual(viewingKeys.count, accounts, "the number of viewing keys have to match the number of account requested to derive") + + guard let viewingKey = viewingKeys.first else { + XCTFail("no viewing key generated") + return + } + + XCTAssertEqual(expectedViewingKey, viewingKey) + } + + func testDeriveViewingKeyFromSpendingKeys() throws { + XCTAssertEqual(expectedSaplingExtendedViewingKey, try derivationTool.deriveViewingKey(spendingKey: expectedSpendingKey)) + } + + func testDeriveSpendingKeysFromSeed() throws { + let accounts: Int = 1 + let seedBytes = [UInt8](seedData) + + let spendingKeys = try derivationTool.deriveSpendingKeys(seed: seedBytes, numberOfAccounts: accounts) + XCTAssertEqual(spendingKeys.count, accounts, "the number of viewing keys have to match the number of account requested to derive") + + guard let spendingKey = spendingKeys.first else { + XCTFail("no viewing key generated") + return + } + XCTAssertEqual(expectedSpendingKey, spendingKey) + + } + + func testDeriveUnifiedAddressFromSeed() throws { + let seedBytes = [UInt8](seedData) + + let unifiedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0) + XCTAssertEqual(unifiedAddress, testRecipientAddress) + } + + func testDeriveUnifiedAddressFromViewingKey() throws { + XCTAssertEqual(try derivationTool.deriveUnifiedAddress(from: expectedViewingKey), testRecipientAddress) + } + + func testDeriveTransparentAddressFromSeed() throws { + XCTAssertEqual(try derivationTool.deriveTransparentAddress(seed: [UInt8](seedData)), expectedTransparentAddress) + } + + func testIsValidViewingKey() throws { + XCTAssertTrue(try derivationTool.isValidExtendedViewingKey(self.expectedSaplingExtendedViewingKey.stringEncoded)) + + XCTAssertFalse(try derivationTool.isValidExtendedViewingKey("zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy")) + } + + func testDeriveTransparentAccountPrivateKeyFromSeed() throws { + XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), TransparentAccountPrivKey(encoding: "xprv9yURYog8Ds8XB36PVzPadbVaCPwVm4CZVMejW9bPPTqBCY8oLssPbe1MhJhPzSbVeg7cWZtuXxuUy2urADuAJUaN27c5f9nErx68SQokG1b")) + } + + func testDeriveUnifiedKeysFromSeed() throws { + let unifiedKeys = try derivationTool.deriveUnifiedFullViewingKeysFromSeed([UInt8](seedData), numberOfAccounts: 1) + XCTAssertEqual(unifiedKeys.count, 1) + + XCTAssertEqual(unifiedKeys[0].account, 0) + XCTAssertEqual(unifiedKeys[0], expectedViewingKey) + } + + func testDeriveQuiteALotOfUnifiedKeysFromSeed() throws { + let unifiedKeys = try derivationTool.deriveUnifiedFullViewingKeysFromSeed([UInt8](seedData), numberOfAccounts: 10) + XCTAssertEqual(unifiedKeys.count, 10) + + XCTAssertEqual(unifiedKeys[0].account, 0) + XCTAssertEqual(unifiedKeys[0], expectedViewingKey) + } + + func testSpendingKeyValidation() throws { + XCTAssertTrue(try derivationTool.isValidSaplingExtendedSpendingKey(expectedSpendingKey.stringEncoded)) + } + + func testSpendingKeyValidationFailsOnInvalidKey() throws { + let wrongSpendingKey = "secret-extended-key-test1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vvZzZzZz" + + XCTAssertFalse(try derivationTool.isValidSaplingExtendedSpendingKey(wrongSpendingKey)) + } + + // TODO: Address encoding does not catch this test https://github.com/zcash/ZcashLightClientKit/issues/509 +// func testSpendingKeyValidationThrowsWhenWrongNetwork() throws { +// XCTAssertThrowsError(try derivationTool.isValidExtendedSpendingKey("secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv")) +// } +} diff --git a/Tests/OfflineTests/NullBytesTests.swift b/Tests/OfflineTests/NullBytesTests.swift index 5a4e8e41..3b751a35 100644 --- a/Tests/OfflineTests/NullBytesTests.swift +++ b/Tests/OfflineTests/NullBytesTests.swift @@ -17,7 +17,7 @@ class NullBytesTests: XCTestCase { let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp" let zAddrWithNullBytes = "\(validZaddr)\0something else that makes the address invalid" - XCTAssertFalse(try ZcashRustBackend.isValidShieldedAddress(zAddrWithNullBytes, networkType: networkType)) + XCTAssertFalse(try ZcashRustBackend.isValidSaplingAddress(zAddrWithNullBytes, networkType: networkType)) } func testTaddrNullBytes() throws { @@ -91,13 +91,13 @@ class NullBytesTests: XCTestCase { func testderiveExtendedFullViewingKeyWithNullBytes() throws { // swiftlint:disable:next line_length - let wrongSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mq\0uy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" // this spending key corresponds to the "demo app reference seed" + let wrongSpendingKeys = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mq\0uy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv") // this spending key corresponds to the "demo app reference seed" // swiftlint:disable:next line_length - let goodSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" + let goodSpendingKeys = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv") XCTAssertThrowsError( - try ZcashRustBackend.deriveExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType), + try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType), "Should have thrown an error but didn't! this is dangerous!" ) { error in guard let rustError = error as? RustWeldingError else { @@ -113,7 +113,7 @@ class NullBytesTests: XCTestCase { } } - XCTAssertNoThrow(try ZcashRustBackend.deriveExtendedFullViewingKey(goodSpendingKeys, networkType: networkType)) + XCTAssertNoThrow(try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(goodSpendingKeys, networkType: networkType)) } func testCheckNullBytes() throws { diff --git a/Tests/OfflineTests/RecipientTests.swift b/Tests/OfflineTests/RecipientTests.swift new file mode 100644 index 00000000..40ba4b1e --- /dev/null +++ b/Tests/OfflineTests/RecipientTests.swift @@ -0,0 +1,34 @@ +// +// RecipientTests.swift +// OfflineTests +// +// Created by Francisco Gindre on 8/31/22. +// + +import XCTest +@testable import ZcashLightClientKit +final class RecipientTests: XCTestCase { + let uaString = "u1l9f0l4348negsncgr9pxd9d3qaxagmqv3lnexcplmufpq7muffvfaue6ksevfvd7wrz7xrvn95rc5zjtn7ugkmgh5rnxswmcj30y0pw52pn0zjvy38rn2esfgve64rj5pcmazxgpyuj" + + let saplingString = "zs1vp7kvlqr4n9gpehztr76lcn6skkss9p8keqs3nv8avkdtjrcctrvmk9a7u494kluv756jeee5k0" + + let transparentString = "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz" + + func testUnifiedRecipient() throws { + let expectedUnifiedAddress = UnifiedAddress(validatedEncoding: uaString) + + XCTAssertEqual(try Recipient(uaString, network: .mainnet), .unified(expectedUnifiedAddress)) + } + + func testSaplingRecipient() throws { + let expectedSaplingAddress = SaplingAddress(validatedEncoding: saplingString) + + XCTAssertEqual(try Recipient(saplingString, network: .mainnet), .sapling(expectedSaplingAddress)) + } + + func testTransparentRecipient() throws { + let expectedTransparentAddress = TransparentAddress(validatedEncoding: transparentString) + + XCTAssertEqual(try Recipient(transparentString, network: .mainnet), .transparent(expectedTransparentAddress)) + } +} diff --git a/Tests/OfflineTests/UndescribableTests.swift b/Tests/OfflineTests/UndescribableTests.swift new file mode 100644 index 00000000..76b56e7c --- /dev/null +++ b/Tests/OfflineTests/UndescribableTests.swift @@ -0,0 +1,96 @@ +// +// UndescribableTests.swift +// +// +// Created by Francisco Gindre on 8/30/22. +// + +import XCTest +@testable import ZcashLightClientKit + +struct SomeStructure: Undescribable { + var info: String +} + +enum SomeError: Error { + case sensitiveThingFailed(SomeStructure) +} + +struct EnclosingStruct { + var someStructure: SomeStructure +} + +final class UndescribableTests: XCTestCase { + func testDescriptionIsRedacted() throws { + let info = "important info" + let someStructure = SomeStructure(info: info) + + let description = String(describing: someStructure) + XCTAssertFalse(description.contains(info)) + XCTAssertEqual(description, "--redacted--") + } + + func testDumpIsRedacted() { + let info = "important info" + let someStructure = SomeStructure(info: info) + var stream = "" + dump(someStructure, to: &stream, indent: 0) + XCTAssertFalse(stream.contains(info)) + XCTAssertEqual(stream, "- --redacted--\n") + } + + func testPrintIsRedacted() { + let info = "important info" + let someStructure = SomeStructure(info: info) + var stream = "" + + print(someStructure, to: &stream) + XCTAssertFalse(stream.contains(info)) + XCTAssertEqual(stream, "--redacted--\n") + } + + func testMirroringIsRedacted() { + let info = "important info" + let someStructure = SomeStructure(info: info) + var s = "" + debugPrint(someStructure, to: &s) + XCTAssertFalse(s.contains(info)) + XCTAssertEqual(s, "--redacted--\n") + } + + func testLocalizedErrorIsRedacted() { + let info = "importantInfo" + let description = "\(SomeError.sensitiveThingFailed(SomeStructure(info: info)))" + XCTAssertFalse(description.contains(info)) + XCTAssertEqual(description, "sensitiveThingFailed(--redacted--)") + } + + func testNestedStructuresCantDescribeUndescribable() { + let info = "important info" + let nested = EnclosingStruct(someStructure: SomeStructure(info: info)) + + var dumpStream = "" + dump(nested, to: &dumpStream) + XCTAssertFalse(dumpStream.contains(info)) + + var debugStream = "" + + debugPrint(nested, to: &debugStream) + XCTAssertFalse(debugStream.contains(info)) + var printStream = "" + + print(nested, to: &printStream) + XCTAssertFalse(printStream.contains(info)) + + XCTAssertFalse(String(describing: nested).contains(info)) + } + + func testSpendingKeyCantBeDescribed() { + let key = SaplingExtendedFullViewingKey(validatedEncoding: "zxviewtestsapling1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6l5smlqrckkl2x5rnrauzc4gp665q3zyw0qf2sfdsx5wpp832htfavqk72uchuuvq2dpmgk8jfaza5t5l56u66fpx0sr8ewp9s3wj2txavmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqgegsaj") + + var s = "" + debugPrint(key, to: &s) + + XCTAssertEqual(s, "--redacted--\n") + } +} diff --git a/Tests/TestUtils/Stubs.swift b/Tests/TestUtils/Stubs.swift index 5f0ba6ec..4b327157 100644 --- a/Tests/TestUtils/Stubs.swift +++ b/Tests/TestUtils/Stubs.swift @@ -81,10 +81,36 @@ extension LightWalletServiceMockResponse { } class MockRustBackend: ZcashRustBackendWelding { + static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.DbInitResult { .seedRequired } + static func deriveSaplingAddressFromViewingKey(_ extfvk: ZcashLightClientKit.SaplingExtendedFullViewingKey, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.SaplingAddress { + throw RustWeldingError.unableToDeriveKeys + } + + static func isValidSaplingExtendedSpendingKey(_ key: String, networkType: ZcashLightClientKit.NetworkType) throws -> Bool { + false + } + + static func deriveSaplingExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: ZcashLightClientKit.NetworkType) throws -> [ZcashLightClientKit.SaplingExtendedFullViewingKey]? { + nil + } + + static func isValidUnifiedAddress(_ address: String, networkType: ZcashLightClientKit.NetworkType) throws -> Bool { + false + } + + static func deriveSaplingExtendedFullViewingKey(_ spendingKey: SaplingExtendedSpendingKey, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.SaplingExtendedFullViewingKey? { + nil + } + + + public func deriveViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] { + [] + } + static func clearUtxos(dbData: URL, address: String, sinceHeight: BlockHeight, networkType: NetworkType) throws -> Int32 { -1 } @@ -144,7 +170,6 @@ class MockRustBackend: ZcashRustBackendWelding { dbData: URL, account: Int32, xprv: String, - extsk: String, memo: String?, spendParamsPath: String, outputParamsPath: String, @@ -173,7 +198,7 @@ class MockRustBackend: ZcashRustBackendWelding { throw KeyDerivationErrors.unableToDerive } - static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool { + static func isValidSaplingExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool { false } @@ -193,11 +218,7 @@ class MockRustBackend: ZcashRustBackendWelding { nil } - static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { - nil - } - - static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { + static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]? { nil } @@ -221,7 +242,7 @@ class MockRustBackend: ZcashRustBackendWelding { static var mockAcounts = false static var mockError: RustWeldingError? static var mockLastError: String? - static var mockAccounts: [String]? + static var mockAccounts: [SaplingExtendedSpendingKey]? static var mockAddresses: [String]? static var mockBalance: Int64? static var mockVerifiedBalance: Int64? @@ -244,7 +265,7 @@ class MockRustBackend: ZcashRustBackendWelding { mockLastError ?? rustBackend.getLastError() } - static func isValidShieldedAddress(_ address: String, networkType: NetworkType) throws -> Bool { + static func isValidSaplingAddress(_ address: String, networkType: NetworkType) throws -> Bool { true } @@ -258,7 +279,7 @@ class MockRustBackend: ZcashRustBackendWelding { } } - static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [String]? { + static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [SaplingExtendedSpendingKey]? { mockAccounts ?? rustBackend.initAccountsTable(dbData: dbData, seed: seed, accounts: accounts, networkType: networkType) } diff --git a/Tests/TestUtils/TestCoordinator.swift b/Tests/TestUtils/TestCoordinator.swift index 40901c4c..7f4b9894 100644 --- a/Tests/TestUtils/TestCoordinator.swift +++ b/Tests/TestUtils/TestCoordinator.swift @@ -37,12 +37,12 @@ class TestCoordinator { var completionHandler: ((SDKSynchronizer) -> Void)? var errorHandler: ((Error?) -> Void)? - var spendingKey: String + var spendingKey: SaplingExtendedSpendingKey var birthday: BlockHeight var channelProvider: ChannelProvider var synchronizer: SDKSynchronizer var service: DarksideWalletService - var spendingKeys: [String]? + var spendingKeys: [SaplingExtendedSpendingKey]? var databases: TemporaryTestDatabases let network: ZcashNetwork convenience init( @@ -85,7 +85,7 @@ class TestCoordinator { } required init( - spendingKey: String, + spendingKey: SaplingExtendedSpendingKey, unifiedFullViewingKey: UnifiedFullViewingKey, walletBirthday: BlockHeight, channelProvider: ChannelProvider, @@ -288,13 +288,13 @@ enum TestSynchronizerBuilder { storage: CompactBlockStorage, spendParamsURL: URL, outputParamsURL: URL, - spendingKey: String, + spendingKey: SaplingExtendedSpendingKey, unifiedFullViewingKey: UnifiedFullViewingKey, walletBirthday: BlockHeight, network: ZcashNetwork, seed: [UInt8]? = nil, loggerProxy: Logger? = nil - ) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { + ) throws -> (spendingKeys: [SaplingExtendedSpendingKey]?, synchronizer: SDKSynchronizer) { let initializer = Initializer( cacheDbURL: cacheDbURL, dataDbURL: dataDbURL, @@ -334,7 +334,7 @@ enum TestSynchronizerBuilder { walletBirthday: BlockHeight, network: ZcashNetwork, loggerProxy: Logger? = nil - ) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { + ) throws -> (spendingKeys: [SaplingExtendedSpendingKey]?, synchronizer: SDKSynchronizer) { guard let spendingKey = try DerivationTool(networkType: network.networkType) .deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1)