Merge pull request #491 from zcash/type_safe_address_api

Add type-safe wrappers for address types.
This commit is contained in:
Kris Nuttycombe 2022-09-07 09:20:18 -06:00 committed by GitHub
commit a8e6b5b5e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
41 changed files with 1030 additions and 683 deletions

View File

@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 52; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@ -15,7 +15,6 @@
0D756A94236C761E009B041B /* GetAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D756A93236C761E009B041B /* GetAddressViewController.swift */; }; 0D756A94236C761E009B041B /* GetAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D756A93236C761E009B041B /* GetAddressViewController.swift */; };
0D76121926B1D66D001CA417 /* Testnet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D76121826B1D66D001CA417 /* Testnet.swift */; }; 0D76121926B1D66D001CA417 /* Testnet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D76121826B1D66D001CA417 /* Testnet.swift */; };
0D7A4A83236CCD88001F4DD8 /* SyncBlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.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 */; }; 0D907F162322CC5900D641FE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F152322CC5900D641FE /* AppDelegate.swift */; };
0D907F182322CC5900D641FE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F172322CC5900D641FE /* ViewController.swift */; }; 0D907F182322CC5900D641FE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F172322CC5900D641FE /* ViewController.swift */; };
0D907F1B2322CC5900D641FE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D907F192322CC5900D641FE /* Main.storyboard */; }; 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 */; }; 0DA1C4C727D11D9500E5006E /* SaplingParametersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D6CE8BC252E3C4A0005D707 /* SaplingParametersViewController.swift */; };
0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1BE47E2581937100F78BE3 /* GetUTXOsViewController.swift */; }; 0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1BE47E2581937100F78BE3 /* GetUTXOsViewController.swift */; };
0DA1C4C927D11D9500E5006E /* DerivationToolViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1BE4502581585C00F78BE3 /* DerivationToolViewController.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 */; }; 0DA1C4CB27D11D9500E5006E /* SampleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D49A18B241698A800CC0649 /* SampleLogger.swift */; };
0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA58B952397F2CB004596EA /* TransactionsDataSource.swift */; }; 0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA58B952397F2CB004596EA /* TransactionsDataSource.swift */; };
0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF53E6623A438F100D7249C /* PaginatedTransactionsViewController.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 = "<group>"; }; 0D76121526B1D5ED001CA417 /* Mainnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mainnet.swift; sourceTree = "<group>"; };
0D76121826B1D66D001CA417 /* Testnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Testnet.swift; sourceTree = "<group>"; }; 0D76121826B1D66D001CA417 /* Testnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Testnet.swift; sourceTree = "<group>"; };
0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBlocksViewController.swift; sourceTree = "<group>"; }; 0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBlocksViewController.swift; sourceTree = "<group>"; };
0D7C85E423AD5A9B006878FC /* SampleStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SampleStorage.swift; sourceTree = "<group>"; };
0D907F122322CC5900D641FE /* ZcashLightClientSample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ZcashLightClientSample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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 = "<group>"; }; 0D907F152322CC5900D641FE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
0D907F172322CC5900D641FE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; }; 0D907F172322CC5900D641FE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@ -248,7 +245,6 @@
0D907F212322CC5B00D641FE /* Info.plist */, 0D907F212322CC5B00D641FE /* Info.plist */,
F94912622790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist */, F94912622790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist */,
0DDFB33D236B844900AED892 /* DemoAppConfig.swift */, 0DDFB33D236B844900AED892 /* DemoAppConfig.swift */,
0D7C85E423AD5A9B006878FC /* SampleStorage.swift */,
0D49A18B241698A800CC0649 /* SampleLogger.swift */, 0D49A18B241698A800CC0649 /* SampleLogger.swift */,
); );
path = ZcashLightClientSample; path = ZcashLightClientSample;
@ -498,6 +494,7 @@
}; };
0DCB58A4237B5B580040096C /* ShellScript */ = { 0DCB58A4237B5B580040096C /* ShellScript */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
@ -534,7 +531,6 @@
0D6CE8BD252E3C4A0005D707 /* SaplingParametersViewController.swift in Sources */, 0D6CE8BD252E3C4A0005D707 /* SaplingParametersViewController.swift in Sources */,
0D1BE47F2581937100F78BE3 /* GetUTXOsViewController.swift in Sources */, 0D1BE47F2581937100F78BE3 /* GetUTXOsViewController.swift in Sources */,
0D1BE4512581585C00F78BE3 /* DerivationToolViewController.swift in Sources */, 0D1BE4512581585C00F78BE3 /* DerivationToolViewController.swift in Sources */,
0D7C85E523AD5A9B006878FC /* SampleStorage.swift in Sources */,
0D49A18C241698A800CC0649 /* SampleLogger.swift in Sources */, 0D49A18C241698A800CC0649 /* SampleLogger.swift in Sources */,
0DA58B962397F2CB004596EA /* TransactionsDataSource.swift in Sources */, 0DA58B962397F2CB004596EA /* TransactionsDataSource.swift in Sources */,
0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */, 0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */,
@ -567,7 +563,6 @@
0DA1C4C727D11D9500E5006E /* SaplingParametersViewController.swift in Sources */, 0DA1C4C727D11D9500E5006E /* SaplingParametersViewController.swift in Sources */,
0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */, 0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */,
0DA1C4C927D11D9500E5006E /* DerivationToolViewController.swift in Sources */, 0DA1C4C927D11D9500E5006E /* DerivationToolViewController.swift in Sources */,
0DA1C4CA27D11D9500E5006E /* SampleStorage.swift in Sources */,
0DA1C4CB27D11D9500E5006E /* SampleLogger.swift in Sources */, 0DA1C4CB27D11D9500E5006E /* SampleLogger.swift in Sources */,
0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */, 0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */,
0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */, 0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */,

View File

@ -138,10 +138,10 @@
}, },
{ {
"package": "libzcashlc", "package": "libzcashlc",
"repositoryURL": "https://github.com/nuttycom/zcash-light-client-ffi.git", "repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state": { "state": {
"branch": "bin/librustzcash_0_7-migrations", "branch": "bin/librustzcash_0_7",
"revision": "dbf7ac51a7de0bbc2c1088973236d68ceb752361", "revision": "823f864a7952073fb9718cf75610691756e34d59",
"version": null "version": null
} }
} }

View File

@ -50,10 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
) )
_ = try! wallet.initialize(with: DemoAppConfig.seed) _ = 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 self.wallet = wallet
return wallet return wallet
} }
@ -141,10 +138,6 @@ extension AppDelegate {
} catch { } catch {
loggerProxy.error("error clearing data db: \(error)") loggerProxy.error("error clearing data db: \(error)")
} }
var storage = SampleStorage.shared
storage!.seed = nil
storage!.privateKey = nil
} }
} }

View File

@ -13,8 +13,6 @@ class DerivationToolViewController: UIViewController {
enum DerivationErrors: Error { enum DerivationErrors: Error {
case couldNotDeriveSpendingKeys(underlyingError: Error) case couldNotDeriveSpendingKeys(underlyingError: Error)
case couldNotDeriveViewingKeys(underlyingError: Error) case couldNotDeriveViewingKeys(underlyingError: Error)
case couldNotDeriveShieldedAddress(underlyingError: Error)
case couldNotDeriveTransparentAddress(underlyingError: Error)
case unknown case unknown
} }
@ -113,15 +111,15 @@ class DerivationToolViewController: UIViewController {
throw DerivationErrors.couldNotDeriveViewingKeys(underlyingError: DerivationErrors.unknown) 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) let transparentAddress = try derivationTool.deriveTransparentAddress(seed: seedBytes)
updateLabels( updateLabels(
spendingKey: spendingKey, spendingKey: spendingKey.stringEncoded,
viewingKey: viewingKey.encoding, viewingKey: viewingKey.stringEncoded,
shieldedAddress: unifiedAddress, shieldedAddress: unifiedAddress.stringEncoded,
transaparentAddress: transparentAddress transaparentAddress: transparentAddress.stringEncoded
) )
} }

View File

@ -12,15 +12,23 @@ class GetAddressViewController: UIViewController {
@IBOutlet weak var unifiedAddressLabel: UILabel! @IBOutlet weak var unifiedAddressLabel: UILabel!
@IBOutlet weak var tAddressLabel: UILabel! @IBOutlet weak var tAddressLabel: UILabel!
@IBOutlet weak var spendingKeyLabel: UILabel! // THIS SHOULD BE SUPER SECRET!!!!! @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() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType) 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. // Do any additional setup after loading the view.
// swiftlint:disable:next line_length
unifiedAddressLabel.text = (try? derivationTool.deriveUnifiedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found" unifiedAddressLabel.text = (try? derivationTool.deriveUnifiedAddress(seed: DemoAppConfig.seed, accountIndex: 0))?.stringEncoded ?? "No Addresses found"
tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed)) ?? "could not derive t-address" tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed))?.stringEncoded ?? "could not derive t-address"
spendingKeyLabel.text = SampleStorage.shared.privateKey ?? "No Spending Key found" spendingKeyLabel.text = self.spendingKey.stringEncoded
unifiedAddressLabel.addGestureRecognizer( unifiedAddressLabel.addGestureRecognizer(
UITapGestureRecognizer( UITapGestureRecognizer(
target: self, target: self,
@ -44,20 +52,12 @@ class GetAddressViewController: UIViewController {
) )
spendingKeyLabel.isUserInteractionEnabled = true spendingKeyLabel.isUserInteractionEnabled = true
loggerProxy.info("Address: \(String(describing: Initializer.shared.getAddress()))") 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) { @IBAction func spendingKeyTapped(_ gesture: UIGestureRecognizer) {
guard let key = SampleStorage.shared.privateKey else {
loggerProxy.warn("nothing to copy")
return
}
loggerProxy.event("copied to clipboard") loggerProxy.event("copied to clipboard")
UIPasteboard.general.string = key UIPasteboard.general.string = self.spendingKey.stringEncoded
let alert = UIAlertController( let alert = UIAlertController(
title: "", title: "",
message: "Spending Key Copied to clipboard", message: "Spending Key Copied to clipboard",
@ -85,7 +85,7 @@ class GetAddressViewController: UIViewController {
@IBAction func tAddressTapped(_ gesture: UIGestureRecognizer) { @IBAction func tAddressTapped(_ gesture: UIGestureRecognizer) {
loggerProxy.event("copied to clipboard") loggerProxy.event("copied to clipboard")
UIPasteboard.general.string = try? DerivationTool(networkType: kZcashNetwork.networkType) UIPasteboard.general.string = try? DerivationTool(networkType: kZcashNetwork.networkType)
.deriveTransparentAddress(seed: DemoAppConfig.seed) .deriveTransparentAddress(seed: DemoAppConfig.seed).stringEncoded
let alert = UIAlertController( let alert = UIAlertController(
title: "", title: "",

View File

@ -27,7 +27,7 @@ class GetUTXOsViewController: UIViewController {
let tAddress = try! DerivationTool(networkType: kZcashNetwork.networkType) let tAddress = try! DerivationTool(networkType: kZcashNetwork.networkType)
.deriveTransparentAddress(seed: DemoAppConfig.seed) .deriveTransparentAddress(seed: DemoAppConfig.seed)
self.transparentAddressLabel.text = tAddress self.transparentAddressLabel.text = tAddress.stringEncoded
// swiftlint:disable:next force_try // swiftlint:disable:next force_try
let balance = try! AppDelegate.shared.sharedSynchronizer.getTransparentBalance(accountIndex: 0) let balance = try! AppDelegate.shared.sharedSynchronizer.getTransparentBalance(accountIndex: 0)
@ -40,14 +40,12 @@ class GetUTXOsViewController: UIViewController {
do { do {
let seed = DemoAppConfig.seed let seed = DemoAppConfig.seed
let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType) 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) let transparentSecretKey = try derivationTool.deriveTransparentAccountPrivateKey(seed: seed, account: 0)
KRProgressHUD.showMessage("🛡 Shielding 🛡") KRProgressHUD.showMessage("🛡 Shielding 🛡")
AppDelegate.shared.sharedSynchronizer.shieldFunds( AppDelegate.shared.sharedSynchronizer.shieldFunds(
spendingKey: spendingKey,
transparentAccountPrivateKey: transparentSecretKey, transparentAccountPrivateKey: transparentSecretKey,
memo: "shielding is fun!", memo: "shielding is fun!",
from: 0, from: 0,

View File

@ -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() {}
}

View File

@ -150,7 +150,7 @@ class SendViewController: UIViewController {
guard let addr = self.addressTextField.text else { guard let addr = self.addressTextField.text else {
return false return false
} }
return wallet.isValidShieldedAddress(addr) || wallet.isValidTransparentAddress(addr) return wallet.isValidSaplingAddress(addr) || wallet.isValidTransparentAddress(addr)
} }
@IBAction func maxFundsValueChanged(_ sender: Any) { @IBAction func maxFundsValueChanged(_ sender: Any) {
@ -206,18 +206,19 @@ class SendViewController: UIViewController {
loggerProxy.warn("WARNING: Form is invalid") loggerProxy.warn("WARNING: Form is invalid")
return return
} }
// swiftlint:disable:next line_length force_try
guard let address = SampleStorage.shared.privateKey else { guard let spendingKey = try! DerivationTool(networkType: kZcashNetwork.networkType).deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1).first else {
loggerProxy.error("NO ADDRESS") loggerProxy.error("NO SPENDING KEY")
return return
} }
KRProgressHUD.show() KRProgressHUD.show()
synchronizer.sendToAddress( synchronizer.sendToAddress(
spendingKey: address, spendingKey: spendingKey,
zatoshi: zec, 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, memo: !self.memoField.text.isEmpty ? self.memoField.text : nil,
from: 0 from: 0
) { [weak self] result in ) { [weak self] result in

View File

@ -84,10 +84,10 @@
}, },
{ {
"package": "libzcashlc", "package": "libzcashlc",
"repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", "repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi",
"state": { "state": {
"branch": "librustzcash_0_7_build_test", "branch": "bin/librustzcash_0_7",
"revision": "5a48541c1da48eb082defaf0fa127657f4f99094", "revision": "823f864a7952073fb9718cf75610691756e34d59",
"version": null "version": null
} }
} }

View File

@ -5,7 +5,7 @@ let package = Package(
name: "ZcashLightClientKit", name: "ZcashLightClientKit",
platforms: [ platforms: [
.iOS(.v13), .iOS(.v13),
.macOS(.v10_12) .macOS(.v10_15)
], ],
products: [ products: [
.library( .library(
@ -16,7 +16,7 @@ let package = Package(
dependencies: [ dependencies: [
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.8.0"), .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(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: [ targets: [
.target( .target(

View File

@ -1260,18 +1260,27 @@ extension CompactBlockProcessor {
extension CompactBlockProcessor { extension CompactBlockProcessor {
public func getUnifiedAddres(accountIndex: Int) -> UnifiedAddress? { 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 nil
} }
return UnifiedAddressShim(account: account)
return try? UnifiedAddress(encoding: address, network: self.config.network.networkType)
} }
public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? { public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? {
try? accountRepository.findBy(account: accountIndex)?.address 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? { 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 { 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 { extension CompactBlockProcessor {
func refreshUTXOs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result<RefreshedUTXOs, Error>) -> Void) { func refreshUTXOs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result<RefreshedUTXOs, Error>) -> Void) {
let dataDb = self.config.dataDb let dataDb = self.config.dataDb

View File

@ -31,17 +31,6 @@ struct Account: AccountEntity, Encodable, Decodable {
var transparentAddress: String var transparentAddress: String
} }
extension Account: UnifiedAddress {
var encoding: String {
get {
address
}
set {
address = newValue
}
}
}
extension Account: Hashable { extension Account: Hashable {
func hash(into hasher: inout Hasher) { func hash(into hasher: inout Hasher) {
hasher.combine(account) hasher.combine(account)

View File

@ -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--")
}
}

View File

@ -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 { public func isValidSaplingAddress(_ address: String) -> Bool {
(try? rustBackend.isValidShieldedAddress(address, networkType: network.networkType)) ?? false (try? rustBackend.isValidSaplingAddress(address, networkType: network.networkType)) ?? false
} }
/** /**

View File

@ -5,21 +5,194 @@
// Created by Francisco Gindre on 4/6/21. // 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. A ZIP 316 Unified Full Viewing Key.
*/ */
public protocol UnifiedFullViewingKey { public struct UnifiedFullViewingKey: Equatable, StringEncoded, Undescribable {
var account: UInt32 { get set } var encoding: String
var encoding: String { get set }
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 struct SaplingExtendedFullViewingKey: Equatable, StringEncoded, Undescribable {
public typealias SaplingShieldedAddress = String var encoding: String
public var stringEncoded: String {
encoding
}
public protocol UnifiedAddress { /// Initializes a new Extended Full Viewing key (EFVK) for Sapling from the provided string encoding
var encoding: String { get } /// - 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 struct WalletBalance: Equatable {
public var verified: Zatoshi public var verified: Zatoshi
public var total: Zatoshi public var total: Zatoshi

View File

@ -10,6 +10,9 @@ import Foundation
import libzcashlc import libzcashlc
class ZcashRustBackend: ZcashRustBackendWelding { class ZcashRustBackend: ZcashRustBackendWelding {
static let minimumConfirmations: UInt32 = 10
static func lastError() -> RustWeldingError? { static func lastError() -> RustWeldingError? {
guard let message = getLastError() else { return nil } 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 { guard !address.containsCStringNullBytesBeforeStringEnding() else {
return false return false
} }
@ -85,7 +88,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return true return true
} }
static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool { static func isValidSaplingExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
guard !key.containsCStringNullBytesBeforeStringEnding() else { guard !key.containsCStringNullBytesBeforeStringEnding() else {
return false return false
} }
@ -100,6 +103,35 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return true 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 { static func isValidUnifiedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
guard !key.containsCStringNullBytesBeforeStringEnding() else { guard !key.containsCStringNullBytesBeforeStringEnding() else {
return false return false
@ -115,27 +147,39 @@ class ZcashRustBackend: ZcashRustBackendWelding {
return true 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() 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) guard let ffiEncodingKeysPtr = zcashlc_init_accounts_table(
if extsksCStr == nil { dbData.0,
dbData.1,
seed,
UInt(seed.count),
accounts,
networkType.networkId
) else {
return nil return nil
} }
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap({ cStr -> String? in let extsks = UnsafeBufferPointer(
guard let str = cStr else { return nil } start: ffiEncodingKeysPtr.pointee.ptr,
return String(cString: str) 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 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() let dbData = dbData.osStr()
var ffiUfvks: [FFIUnifiedViewingKey] = [] var ffiUfvks: [FFIEncodedKey] = []
for ufvk in ufvks { for ufvk in ufvks {
guard !ufvk.encoding.containsCStringNullBytesBeforeStringEnding() else { guard !ufvk.encoding.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.malformedStringInput throw RustWeldingError.malformedStringInput
@ -149,15 +193,23 @@ class ZcashRustBackend: ZcashRustBackendWelding {
let ufvkPtr = UnsafeMutablePointer<CChar>.allocate(capacity: ufvkCStr.count) let ufvkPtr = UnsafeMutablePointer<CChar>.allocate(capacity: ufvkCStr.count)
ufvkPtr.initialize(from: ufvkCStr, count: 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 var result = false
ffiUfvks.withContiguousMutableStorageIfAvailable { pointer in ffiUfvks.withContiguousMutableStorageIfAvailable { pointer in
let slice = UnsafeMutablePointer<FFIUVKBoxedSlice>.allocate(capacity: 1) let slice = UnsafeMutablePointer<FFIEncodedKeys>.allocate(capacity: 1)
slice.initialize(to: FFIUVKBoxedSlice(ptr: pointer.baseAddress, len: UInt(pointer.count)))
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) result = zcashlc_init_accounts_table_with_keys(dbData.0, dbData.1, slice, networkType.networkId)
slice.deinitialize(count: 1) slice.deinitialize(count: 1)
@ -224,7 +276,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
static func getVerifiedBalance(dbData: URL, account: Int32, networkType: NetworkType) -> Int64 { static func getVerifiedBalance(dbData: URL, account: Int32, networkType: NetworkType) -> Int64 {
let dbData = dbData.osStr() 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 { static func getVerifiedTransparentBalance(dbData: URL, address: String, networkType: NetworkType) throws -> Int64 {
@ -238,7 +290,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
let dbData = dbData.osStr() 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 { static func getTransparentBalance(dbData: URL, address: String, networkType: NetworkType) throws -> Int64 {
@ -388,7 +440,8 @@ class ZcashRustBackend: ZcashRustBackendWelding {
UInt(spendParamsPath.lengthOfBytes(using: .utf8)), UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
outputParamsPath, outputParamsPath,
UInt(outputParamsPath.lengthOfBytes(using: .utf8)), UInt(outputParamsPath.lengthOfBytes(using: .utf8)),
networkType.networkId networkType.networkId,
minimumConfirmations
) )
} }
@ -397,7 +450,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
dbData: URL, dbData: URL,
account: Int32, account: Int32,
xprv: String, xprv: String,
extsk: String,
memo: String?, memo: String?,
spendParamsPath: String, spendParamsPath: String,
outputParamsPath: String, outputParamsPath: String,
@ -411,7 +463,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
dbData.1, dbData.1,
account, account,
[CChar](xprv.utf8CString), [CChar](xprv.utf8CString),
[CChar](extsk.utf8CString),
[CChar](memoBytes.utf8CString), [CChar](memoBytes.utf8CString),
spendParamsPath, spendParamsPath,
UInt(spendParamsPath.lengthOfBytes(using: .utf8)), UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
@ -421,14 +472,14 @@ class ZcashRustBackend: ZcashRustBackendWelding {
) )
} }
static func deriveExtendedFullViewingKey(_ spendingKey: String, networkType: NetworkType) throws -> String? { static func deriveSaplingExtendedFullViewingKey(_ spendingKey: SaplingExtendedSpendingKey, networkType: NetworkType) throws -> SaplingExtendedFullViewingKey? {
guard !spendingKey.containsCStringNullBytesBeforeStringEnding() else { guard !spendingKey.stringEncoded.containsCStringNullBytesBeforeStringEnding() else {
throw RustWeldingError.malformedStringInput throw RustWeldingError.malformedStringInput
} }
guard guard
let extsk = zcashlc_derive_extended_full_viewing_key( let extfvk = zcashlc_derive_extended_full_viewing_key(
[CChar](spendingKey.utf8CString), [CChar](spendingKey.stringEncoded.utf8CString),
networkType.networkId networkType.networkId
) )
else { else {
@ -437,21 +488,23 @@ class ZcashRustBackend: ZcashRustBackendWelding {
} }
return nil return nil
} }
let derived = String(validatingUTF8: extsk) defer { zcashlc_string_free(extfvk) }
zcashlc_string_free(extsk) guard let derived = String(validatingUTF8: extfvk) else {
return derived return nil
}
return SaplingExtendedFullViewingKey(validatedEncoding: derived)
} }
static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { static func deriveSaplingExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedFullViewingKey]? {
var capacity = UInt(0)
guard guard
let extsksCStr = zcashlc_derive_extended_full_viewing_keys( let ffiEncodedKeysPtr = zcashlc_derive_extended_full_viewing_keys(
seed, seed,
UInt(seed.count), UInt(seed.count),
accounts, accounts,
&capacity,
networkType.networkId networkType.networkId
) )
else { else {
@ -460,24 +513,27 @@ class ZcashRustBackend: ZcashRustBackendWelding {
} }
return nil return nil
} }
defer {
zcashlc_free_keys(ffiEncodedKeysPtr)
}
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { cStr -> String? in let extfvks = UnsafeBufferPointer(
guard let str = cStr else { return nil } start: ffiEncodedKeysPtr.pointee.ptr,
return String(cString: str) count: Int(ffiEncodedKeysPtr.pointee.len)
).compactMap { encodedKey -> SaplingExtendedFullViewingKey in
SaplingExtendedFullViewingKey(validatedEncoding: String(cString: encodedKey.encoding))
} }
zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity) return extfvks
return extsks
} }
static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]? {
var capacity = UInt(0)
guard guard
let extsksCStr = zcashlc_derive_extended_spending_keys( let ffiEncodedKeysPtr = zcashlc_derive_extended_spending_keys(
seed, seed,
UInt(seed.count), UInt(seed.count),
accounts, accounts,
&capacity,
networkType.networkId networkType.networkId
) )
else { else {
@ -486,13 +542,13 @@ class ZcashRustBackend: ZcashRustBackendWelding {
} }
return nil return nil
} }
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { cStr -> String? in defer { zcashlc_free_keys(ffiEncodedKeysPtr) }
guard let str = cStr else { return nil }
return String(cString: str) 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 return extsks
} }
@ -503,7 +559,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
networkType: NetworkType networkType: NetworkType
) throws -> [UnifiedFullViewingKey] { ) throws -> [UnifiedFullViewingKey] {
guard guard
let ufvksStruct = zcashlc_derive_unified_full_viewing_keys_from_seed( let ffiEncodedKeysPtr = zcashlc_derive_unified_full_viewing_keys_from_seed(
seed, seed,
UInt(seed.count), UInt(seed.count),
Int32(numberOfAccounts), Int32(numberOfAccounts),
@ -515,10 +571,12 @@ class ZcashRustBackend: ZcashRustBackendWelding {
} }
throw RustWeldingError.unableToDeriveKeys 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 throw RustWeldingError.unableToDeriveKeys
} }
@ -531,10 +589,9 @@ class ZcashRustBackend: ZcashRustBackendWelding {
throw RustWeldingError.unableToDeriveKeys 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 return ufvks
} }
@ -698,11 +755,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
} }
} }
private struct UFVK: UnifiedFullViewingKey {
var account: UInt32
var encoding: String
}
private extension ZcashRustBackend { private extension ZcashRustBackend {
static func throwDataDbError(_ error: RustWeldingError) -> Error { static func throwDataDbError(_ error: RustWeldingError) -> Error {
if case RustWeldingError.genericError(let message) = error, message.contains("is not empty") { if case RustWeldingError.genericError(let message) = error, message.contains("is not empty") {

View File

@ -8,7 +8,7 @@
import Foundation import Foundation
public enum RustWeldingError: Error { enum RustWeldingError: Error {
case genericError(message: String) case genericError(message: String)
case dataDbInitFailed(message: String) case dataDbInitFailed(message: String)
case dataDbNotEmpty case dataDbNotEmpty
@ -18,7 +18,7 @@ public enum RustWeldingError: Error {
case unableToDeriveKeys case unableToDeriveKeys
} }
public enum ZcashRustBackendWeldingConstants { enum ZcashRustBackendWeldingConstants {
static let validChain: Int32 = -1 static let validChain: Int32 = -1
} }
@ -31,7 +31,7 @@ public enum DbInitResult {
case seedRequired case seedRequired
} }
public protocol ZcashRustBackendWelding { protocol ZcashRustBackendWelding {
/** /**
gets the latest error if available. Clear the existing error 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 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 - 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 - Returns: true when the address is valid and transparent. false in any other case
- Throws: Error when the provided address belongs to another network - Throws: Error when the provided address belongs to another network
*/ */
static func isValidTransparentAddress(_ address: String, networkType: NetworkType) throws -> Bool 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 - 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 - 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 static func isValidUnifiedFullViewingKey(_ ufvk: String, networkType: NetworkType) throws -> Bool
@ -79,7 +90,7 @@ public protocol ZcashRustBackendWelding {
- seed: byte array of the zip32 seed - seed: byte array of the zip32 seed
- accounts: how many accounts you want to have - 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 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 - dbCache: URL for the Cache DB
- dbData: URL for the Data DB - dbData: URL for the Data DB
- account: the account index that will originate the transaction - account: the account index that will originate the transaction
- xprv: transparent account private key for the shielded funds. - xprv: transparent account private key for the transparent funds that will be shielded.
- extsk: extended spending key string
- memo: the memo string for this transaction - memo: the memo string for this transaction
- spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located - spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located
- outputParamsPath: path escaped String for the filesystem locations where the output parameters are located - outputParamsPath: path escaped String for the filesystem locations where the output parameters are located
@ -314,7 +324,6 @@ public protocol ZcashRustBackendWelding {
dbData: URL, dbData: URL,
account: Int32, account: Int32,
xprv: String, xprv: String,
extsk: String,
memo: String?, memo: String?,
spendParamsPath: String, spendParamsPath: String,
outputParamsPath: String, outputParamsPath: String,
@ -327,8 +336,8 @@ public protocol ZcashRustBackendWelding {
- Returns: the derived key - Returns: the derived key
- Throws: RustBackendError if fatal error occurs - 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 Derives a set of full viewing keys from a seed
- Parameter spendingKey: a string containing the spending key - Parameter spendingKey: a string containing the spending key
@ -336,22 +345,22 @@ public protocol ZcashRustBackendWelding {
- Returns: an array containing the derived keys - Returns: an array containing the derived keys
- Throws: RustBackendError if fatal error occurs - 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 seed: a string containing the seed
- Parameter accounts: the number of accounts you want to derive from this seed - Parameter accounts: the number of accounts you want to derive from this seed
- Returns: an array containing the spending keys - Returns: an array containing the spending keys
- Throws: RustBackendError if fatal error occurs - 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 Derives a unified address from a seed
- Parameter seed: an array of bytes of the seed - Parameter seed: an array of bytes of the seed
- Parameter accountIndex: the index of the account you want the address for - 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 - Throws: RustBackendError if fatal error occurs
*/ */
static func deriveUnifiedAddressFromSeed(seed: [UInt8], accountIndex: Int32, networkType: NetworkType) throws -> String? static func deriveUnifiedAddressFromSeed(seed: [UInt8], accountIndex: Int32, networkType: NetworkType) throws -> String?
@ -363,13 +372,14 @@ public protocol ZcashRustBackendWelding {
- Throws: RustBackendError if fatal error occurs - Throws: RustBackendError if fatal error occurs
*/ */
static func deriveUnifiedAddressFromViewingKey(_ ufvk: String, networkType: NetworkType) throws -> String? static func deriveUnifiedAddressFromViewingKey(_ ufvk: String, networkType: NetworkType) throws -> String?
/**
Derives a shielded address from an Extended Full Viewing Key /// Derives a transparent address from seed bytes
- Parameter seed: an array of bytes of the seed /// - Parameter seed: an array of bytes of the seed
- Returns: an optional String containing the transparent address /// - Parameter account: account number
- Throws: RustBackendError if fatal error occurs /// - 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? static func deriveTransparentAddressFromSeed(seed: [UInt8], account: Int, index: Int, networkType: NetworkType) throws -> String?
/** /**

View File

@ -95,7 +95,7 @@ public protocol Synchronizer {
/// Gets the sapling shielded address for the given account. /// 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. /// - 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 /// - 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. /// 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. /// - Parameter accountIndex: the optional account id to use. By default, the first account is used.
// swiftlint:disable:next function_parameter_count // swiftlint:disable:next function_parameter_count
func sendToAddress( func sendToAddress(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
zatoshi: Zatoshi, zatoshi: Zatoshi,
toAddress: String, toAddress: Recipient,
memo: String?, memo: String?,
from accountIndex: Int, from accountIndex: Int,
resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void
) )
/// Sends zatoshi. /// Shields transparent funds from the given private key into the best shielded pool of the given account.
/// - Parameter spendingKey: the key that allows spends to occur.
/// - Parameter transparentAccountPrivateKey: the key that allows to spend transparent funds /// - Parameter transparentAccountPrivateKey: the key that allows to spend transparent funds
/// - Parameter memo: the optional memo to include as part of the transaction. /// - 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. /// - Parameter accountIndex: the optional account id that will be used to shield your funds to. By default, the first account is used.
func shieldFunds( func shieldFunds(
spendingKey: String, transparentAccountPrivateKey: TransparentAccountPrivKey,
transparentAccountPrivateKey: String,
memo: String?, memo: String?,
from accountIndex: Int, from accountIndex: Int,
resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
) )
/// Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only /// 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. /// - Parameter transaction: the transaction to cancel.
/// - Returns: true when the cancellation request was successful. False when it is too late. /// - Returns: true when the cancellation request was successful. False when it is too late.
func cancelSpend(transaction: PendingTransactionEntity) -> Bool func cancelSpend(transaction: PendingTransactionEntity) -> Bool
/// all outbound pending transactions that have been sent but are awaiting confirmations /// all outbound pending transactions that have been sent but are awaiting confirmations
var pendingTransactions: [PendingTransactionEntity] { get } var pendingTransactions: [PendingTransactionEntity] { get }
@ -174,7 +171,6 @@ public protocol Synchronizer {
/// all transactions related to receiving funds /// all transactions related to receiving funds
var receivedTransactions: [ConfirmedTransactionEntity] { get } var receivedTransactions: [ConfirmedTransactionEntity] { get }
/// A repository serving transactions in a paginated manner /// A repository serving transactions in a paginated manner
/// - Parameter kind: Transaction Kind expected from this PaginatedTransactionRepository /// - Parameter kind: Transaction Kind expected from this PaginatedTransactionRepository
func paginatedTransactions(of kind: TransactionKind) -> 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 /// Returns the latests UTXOs for the given address from the specified height on
func refreshUTXOs(address: String, from height: BlockHeight, result: @escaping (Result<RefreshedUTXOs, Error>) -> Void) func refreshUTXOs(address: String, from height: BlockHeight, result: @escaping (Result<RefreshedUTXOs, Error>) -> Void)
/// Returns the last stored unshielded balance /// Returns the last stored transparent balance
func getTransparentBalance(accountIndex: Int) throws -> WalletBalance func getTransparentBalance(accountIndex: Int) throws -> WalletBalance

View File

@ -74,9 +74,10 @@ public extension Notification.Name {
static let synchronizerConnectionStateChanged = Notification.Name("SynchronizerConnectionStateChanged") 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 // swiftlint:disable type_body_length
public class SDKSynchronizer: Synchronizer { public class SDKSynchronizer: Synchronizer {
public struct SynchronizerState { public struct SynchronizerState {
public var shieldedBalance: WalletBalance public var shieldedBalance: WalletBalance
public var transparentBalance: WalletBalance public var transparentBalance: WalletBalance
@ -466,21 +467,25 @@ public class SDKSynchronizer: Synchronizer {
from accountIndex: Int, from accountIndex: Int,
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
) { ) {
sendToAddress( do {
spendingKey: spendingKey, sendToAddress(
zatoshi: Zatoshi(zatoshi), spendingKey: try SaplingExtendedSpendingKey(encoding: spendingKey, network: network.networkType),
toAddress: toAddress, zatoshi: Zatoshi(zatoshi),
memo: memo, toAddress: try Recipient(toAddress, network: network.networkType),
from: accountIndex, memo: memo,
resultBlock: resultBlock from: accountIndex,
) resultBlock: resultBlock
)
} catch {
resultBlock(.failure(SynchronizerError.invalidAccount))
}
} }
// swiftlint:disable:next function_parameter_count // swiftlint:disable:next function_parameter_count
public func sendToAddress( public func sendToAddress(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
zatoshi: Zatoshi, zatoshi: Zatoshi,
toAddress: String, toAddress: Recipient,
memo: String?, memo: String?,
from accountIndex: Int, from accountIndex: Int,
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
@ -492,7 +497,7 @@ public class SDKSynchronizer: Synchronizer {
self?.createToAddress( self?.createToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: zatoshi, zatoshi: zatoshi,
toAddress: toAddress, toAddress: toAddress.stringEncoded,
memo: memo, memo: memo,
from: accountIndex, from: accountIndex,
resultBlock: resultBlock resultBlock: resultBlock
@ -505,8 +510,7 @@ public class SDKSynchronizer: Synchronizer {
} }
public func shieldFunds( public func shieldFunds(
spendingKey: String, transparentAccountPrivateKey: TransparentAccountPrivKey,
transparentAccountPrivateKey: String,
memo: String?, memo: String?,
from accountIndex: Int, from accountIndex: Int,
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
@ -515,21 +519,26 @@ public class SDKSynchronizer: Synchronizer {
let derivationTool = DerivationTool(networkType: self.network.networkType) let derivationTool = DerivationTool(networkType: self.network.networkType)
do { do {
let tAddr = try derivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, index: 0) // TODO: FIX let tAddr = try derivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, index: 0)
let tBalance = try utxoRepository.balance(address: tAddr, latestHeight: self.latestDownloadedHeight())
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 { guard tBalance.verified >= self.network.constants.defaultFee(for: self.latestScannedHeight) else {
resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds)) resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds))
return 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( transactionManager.encodeShieldingTransaction(
spendingKey: spendingKey,
xprv: transparentAccountPrivateKey, xprv: transparentAccountPrivateKey,
pendingTransaction: shieldingSpend pendingTransaction: shieldingSpend
) { [weak self] result in ) { [weak self] result in
@ -560,7 +569,7 @@ public class SDKSynchronizer: Synchronizer {
// swiftlint:disable:next function_parameter_count // swiftlint:disable:next function_parameter_count
func createToAddress( func createToAddress(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
zatoshi: Zatoshi, zatoshi: Zatoshi,
toAddress: String, toAddress: String,
memo: String?, memo: String?,
@ -683,8 +692,8 @@ public class SDKSynchronizer: Synchronizer {
initializer.getVerifiedBalance(account: accountIndex) initializer.getVerifiedBalance(account: accountIndex)
} }
public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? { public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? {
blockProcessor.getShieldedAddress(accountIndex: accountIndex) blockProcessor.getSaplingAddress(accountIndex: accountIndex)
} }
public func getUnifiedAddress(accountIndex: Int) -> UnifiedAddress? { public func getUnifiedAddress(accountIndex: Int) -> UnifiedAddress? {
@ -700,7 +709,7 @@ public class SDKSynchronizer: Synchronizer {
} }
/** /**
Returns the last stored unshielded balance Returns the last stored transparent balance
*/ */
public func getTransparentBalance(address: String) throws -> WalletBalance { public func getTransparentBalance(address: String) throws -> WalletBalance {
do { do {

View File

@ -12,7 +12,11 @@ public protocol KeyValidation {
func isValidTransparentAddress(_ tAddress: String) throws -> Bool 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 { public protocol KeyDeriving {
@ -25,7 +29,7 @@ public protocol KeyDeriving {
- Returns: the viewing keys that correspond to the seed, formatted as Strings. - 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. 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. - 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. 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. - 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. 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. - 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. /// Given a unified full 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 /// - 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. /// a specific account so no account index is required.
///
- Returns: the address that corresponds to the viewing key. /// - Returns: the address that corresponds to the viewing key.
*/ func deriveUnifiedAddress(from ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress
func deriveUnifiedAddress(viewingKey: String) throws -> String
/** /**
Derives a transparent address from seedbytes, specifying account and index 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 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 Derives a transparent address from the given transparent account private key
*/ */
func deriveTransparentAddressFromAccountPrivateKey(_ xprv: String, index: Int) throws -> String func deriveTransparentAddressFromAccountPrivateKey(_ xprv: TransparentAccountPrivKey, index: Int) throws -> TransparentAddress
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
} }
public enum KeyDerivationErrors: Error { public enum KeyDerivationErrors: Error {
@ -104,6 +94,7 @@ public enum KeyDerivationErrors: Error {
} }
public class DerivationTool: KeyDeriving { public class DerivationTool: KeyDeriving {
var rustwelding: ZcashRustBackendWelding.Type = ZcashRustBackend.self var rustwelding: ZcashRustBackendWelding.Type = ZcashRustBackend.self
var networkType: NetworkType var networkType: NetworkType
@ -121,7 +112,7 @@ public class DerivationTool: KeyDeriving {
- Returns: the viewing keys that correspond to the seed, formatted as Strings. - 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 { guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else {
throw KeyDerivationErrors.invalidInput throw KeyDerivationErrors.invalidInput
} }
@ -129,16 +120,16 @@ public class DerivationTool: KeyDeriving {
do { do {
let ufvks = try rustwelding.deriveUnifiedFullViewingKeyFromSeed(seed, numberOfAccounts: numberOfAccounts, networkType: networkType) let ufvks = try rustwelding.deriveUnifiedFullViewingKeyFromSeed(seed, numberOfAccounts: numberOfAccounts, networkType: networkType)
var keys: [String] = [] var keys: [UnifiedFullViewingKey] = []
for ufvk in ufvks { for ufvk in ufvks {
keys.append(ufvk.encoding) keys.append(ufvk)
} }
return keys return keys
} catch { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) throw KeyDerivationErrors.derivationError(underlyingError: error)
} }
} }
/** /**
Given a spending key, return the associated viewing key. 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. - 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 { do {
guard let key = try rustwelding.deriveExtendedFullViewingKey(spendingKey, networkType: networkType) else { guard let key = try rustwelding.deriveSaplingExtendedFullViewingKey(spendingKey, networkType: networkType) else {
throw KeyDerivationErrors.unableToDerive throw KeyDerivationErrors.unableToDerive
} }
return key return key
@ -156,6 +147,17 @@ public class DerivationTool: KeyDeriving {
throw KeyDerivationErrors.derivationError(underlyingError: error) 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. 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. - 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 { guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else {
throw KeyDerivationErrors.invalidInput throw KeyDerivationErrors.invalidInput
} }
do { 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 throw KeyDerivationErrors.unableToDerive
} }
return keys return keys
@ -189,7 +191,7 @@ public class DerivationTool: KeyDeriving {
- Returns: the address that corresponds to the seed and account index. - 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 { guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else {
throw KeyDerivationErrors.invalidInput throw KeyDerivationErrors.invalidInput
} }
@ -198,7 +200,7 @@ public class DerivationTool: KeyDeriving {
guard let address = try rustwelding.deriveUnifiedAddressFromSeed(seed: seed, accountIndex: accountIndex, networkType: networkType) else { guard let address = try rustwelding.deriveUnifiedAddressFromSeed(seed: seed, accountIndex: accountIndex, networkType: networkType) else {
throw KeyDerivationErrors.unableToDerive throw KeyDerivationErrors.unableToDerive
} }
return address return UnifiedAddress(validatedEncoding: address)
} catch { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) throw KeyDerivationErrors.derivationError(underlyingError: error)
} }
@ -212,20 +214,20 @@ public class DerivationTool: KeyDeriving {
- Returns: the address that corresponds to the viewing key. - 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 { 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 throw KeyDerivationErrors.unableToDerive
} }
return zaddr return UnifiedAddress(validatedEncoding: stringEncodedUA)
} catch { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) 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 { do {
guard let zaddr = try rustwelding.deriveTransparentAddressFromSeed( guard let taddr = try rustwelding.deriveTransparentAddressFromSeed(
seed: seed, seed: seed,
account: account, account: account,
index: index, index: index,
@ -233,54 +235,27 @@ public class DerivationTool: KeyDeriving {
) else { ) else {
throw KeyDerivationErrors.unableToDerive throw KeyDerivationErrors.unableToDerive
} }
return zaddr return TransparentAddress(validatedEncoding: taddr)
} catch { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) throw KeyDerivationErrors.derivationError(underlyingError: error)
} }
} }
public func deriveUnifiedFullViewingKeysFromSeed(_ seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] {
guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else { /// derives a Unified Address from a Unified Full Viewing Key
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
*/
public func deriveUnifiedAddressFromUnifiedFullViewingKey(_ ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress { public func deriveUnifiedAddressFromUnifiedFullViewingKey(_ ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress {
do { do {
let encoding = try deriveUnifiedAddress(viewingKey: ufvk.encoding) return try deriveUnifiedAddress(from: ufvk)
return ConcreteUnifiedAddress(encoding: encoding)
} catch { } catch {
throw KeyDerivationErrors.unableToDerive throw KeyDerivationErrors.unableToDerive
} }
} }
public func deriveTransparentAddressFromPublicKey(_ pubkey: String) throws -> String { /// Derives the transparent funds account private key from the given seed
guard !pubkey.isEmpty else { /// - Throws:
throw KeyDerivationErrors.invalidInput /// - 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 {
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 {
do { do {
guard let seedKey = try rustwelding.deriveTransparentAccountPrivateKeyFromSeed( guard let seedKey = try rustwelding.deriveTransparentAccountPrivateKeyFromSeed(
seed: seed, seed: seed,
@ -289,7 +264,23 @@ public class DerivationTool: KeyDeriving {
) else { ) else {
throw KeyDerivationErrors.unableToDerive 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 { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) throw KeyDerivationErrors.derivationError(underlyingError: error)
} }
@ -297,9 +288,17 @@ public class DerivationTool: KeyDeriving {
} }
extension DerivationTool: KeyValidation { 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 { public func isValidExtendedViewingKey(_ extvk: String) throws -> Bool {
do { do {
return try rustwelding.isValidExtendedFullViewingKey(extvk, networkType: networkType) return try rustwelding.isValidSaplingExtendedFullViewingKey(extvk, networkType: networkType)
} catch { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) 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 { do {
return try rustwelding.isValidShieldedAddress(zAddress, networkType: networkType) return try rustwelding.isValidSaplingAddress(zAddress, networkType: networkType)
} catch { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) throw KeyDerivationErrors.derivationError(underlyingError: error)
} }
} }
/** public func isValidSaplingExtendedSpendingKey(_ extsk: String) throws -> Bool {
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 {
do { do {
guard let tAddr = try rustwelding.deriveTransparentAddressFromAccountPrivateKey(xprv, index: index, networkType: networkType) else { return try rustwelding.isValidSaplingExtendedSpendingKey(extsk, networkType: networkType)
throw KeyDerivationErrors.unableToDerive
}
return tAddr
} catch { } catch {
throw KeyDerivationErrors.derivationError(underlyingError: error) 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
}
} }

View File

@ -65,52 +65,15 @@ class PersistentTransactionManager: OutboundTransactionManager {
} }
func encodeShieldingTransaction( func encodeShieldingTransaction(
spendingKey: String, xprv: TransparentAccountPrivKey,
xprv: String,
pendingTransaction: PendingTransactionEntity, pendingTransaction: PendingTransactionEntity,
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void result: @escaping (Result<PendingTransactionEntity, Error>) -> Void
) { ) {
queue.async { [weak self] in queue.async { [weak self] in
guard let self = self else { return } guard let self = self else { return }
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 { do {
let encodedTransaction = try self.encoder.createShieldingTransaction( let encodedTransaction = try self.encoder.createShieldingTransaction(
spendingKey: spendingKey,
tAccountPrivateKey: xprv, tAccountPrivateKey: xprv,
memo: pendingTransaction.memo?.asZcashTransactionMemo(), memo: pendingTransaction.memo?.asZcashTransactionMemo(),
from: pendingTransaction.accountIndex from: pendingTransaction.accountIndex
@ -140,7 +103,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
} }
func encode( func encode(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
pendingTransaction: PendingTransactionEntity, pendingTransaction: PendingTransactionEntity,
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void result: @escaping (Result<PendingTransactionEntity, Error>) -> Void
) { ) {
@ -149,7 +112,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
do { do {
let encodedTransaction = try self.encoder.createTransaction( let encodedTransaction = try self.encoder.createTransaction(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: pendingTransaction.intValue, zatoshi: pendingTransaction.value,
to: pendingTransaction.toAddress, to: pendingTransaction.toAddress,
memo: pendingTransaction.memo?.asZcashTransactionMemo(), memo: pendingTransaction.memo?.asZcashTransactionMemo(),
from: pendingTransaction.accountIndex from: pendingTransaction.accountIndex
@ -266,10 +229,6 @@ class PersistentTransactionManager: OutboundTransactionManager {
.forEach { try self.repository.update($0) } .forEach { try self.repository.update($0) }
} }
func monitorChanges(byId: Int, observer: Any) {
// TODO: Implement this
}
func cancel(pendingTransaction: PendingTransactionEntity) -> Bool { func cancel(pendingTransaction: PendingTransactionEntity) -> Bool {
guard let id = pendingTransaction.id else { return false } guard let id = pendingTransaction.id else { return false }

View File

@ -18,47 +18,45 @@ public enum TransactionEncoderError: Error {
} }
protocol TransactionEncoder { protocol TransactionEncoder {
/**
Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation /// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using /// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
double-bangs for things). /// double-bangs for things).
Blocking /// Blocking
///
- Parameters: /// - Parameters:
- Parameter spendingKey: a string containing the spending key /// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key
- Parameter zatoshi: the amount to send in zatoshis /// - Parameter zatoshi: the amount to send in `Zatoshi`
- Parameter to: string containing the recipient address /// - Parameter to: string containing the recipient address
- Parameter memo: string containing the memo (optional) /// - Parameter memo: string containing the memo (optional)
- Parameter accountIndex: index of the account that will be used to send the funds /// - Parameter accountIndex: index of the account that will be used to send the funds
///
- Throws: a TransactionEncoderError /// - Throws: a TransactionEncoderError
*/
func createTransaction( func createTransaction(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
zatoshi: Int, zatoshi: Zatoshi,
to address: String, to address: String,
memo: String?, memo: String?,
from accountIndex: Int from accountIndex: Int
) throws -> EncodedTransaction ) throws -> EncodedTransaction
/**
Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation /// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using /// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
double-bangs for things). /// double-bangs for things).
Non-blocking /// Non-blocking
///
- Parameters: /// - Parameters:
- Parameter spendingKey: a string containing the spending key /// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key
- Parameter zatoshi: the amount to send in zatoshis /// - Parameter zatoshi: the amount to send in `Zatoshi`
- Parameter to: string containing the recipient address /// - Parameter to: string containing the recipient address
- Parameter memo: string containing the memo (optional) /// - Parameter memo: string containing the memo (optional)
- Parameter accountIndex: index of the account that will be used to send the funds /// - Parameter accountIndex: index of the account that will be used to send the funds
- Parameter result: a non escaping closure that receives a Result containing either an EncodedTransaction or a TransactionEncoderError /// - Parameter result: a non escaping closure that receives a Result containing either an EncodedTransaction or a /// TransactionEncoderError
*/
// swiftlint:disable:next function_parameter_count // swiftlint:disable:next function_parameter_count
func createTransaction( func createTransaction(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
zatoshi: Int, zatoshi: Zatoshi,
to address: String, to address: String,
memo: String?, memo: String?,
from accountIndex: Int, from accountIndex: Int,
@ -70,7 +68,6 @@ protocol TransactionEncoder {
Blocking Blocking
- Parameters: - Parameters:
- Parameter spendingKey: a string containing the spending key
- Parameter tAccountPrivateKey: transparent account private key to spend the UTXOs - Parameter tAccountPrivateKey: transparent account private key to spend the UTXOs
- Parameter memo: string containing the memo (optional) - Parameter memo: string containing the memo (optional)
- Parameter accountIndex: index of the account that will be used to send the funds - Parameter accountIndex: index of the account that will be used to send the funds
@ -78,34 +75,11 @@ protocol TransactionEncoder {
- Throws: a TransactionEncoderError - Throws: a TransactionEncoderError
*/ */
func createShieldingTransaction( func createShieldingTransaction(
spendingKey: String, tAccountPrivateKey: TransparentAccountPrivKey,
tAccountPrivateKey: String,
memo: String?, memo: String?,
from accountIndex: Int from accountIndex: Int
) throws -> EncodedTransaction ) 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 Fetch the Transaction Entity from the encoded representation
- Parameter encodedTransaction: The encoded transaction to expand - Parameter encodedTransaction: The encoded transaction to expand

View File

@ -14,17 +14,34 @@ transactions through to completion.
*/ */
protocol OutboundTransactionManager { 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<PendingTransactionEntity, Error>) -> Void) func encodeShieldingTransaction(
xprv: TransparentAccountPrivKey,
pendingTransaction: PendingTransactionEntity,
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void
)
func encode(spendingKey: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) func encode(
spendingKey: SaplingExtendedSpendingKey,
pendingTransaction: PendingTransactionEntity,
result: @escaping (Result<PendingTransactionEntity, Error>
) -> Void)
func submit(pendingTransaction: PendingTransactionEntity, result: @escaping (Result<PendingTransactionEntity, Error>) -> Void) func submit(
pendingTransaction: PendingTransactionEntity,
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void
)
func applyMinedHeight(pendingTransaction: PendingTransactionEntity, minedHeight: BlockHeight) throws -> PendingTransactionEntity func applyMinedHeight(
pendingTransaction: PendingTransactionEntity,
func monitorChanges(byId: Int, observer: Any) // check this out. code smell minedHeight: BlockHeight
) throws -> PendingTransactionEntity
/** /**
Attempts to Cancel a transaction. Returns true if successful Attempts to Cancel a transaction. Returns true if successful

View File

@ -50,8 +50,8 @@ class WalletTransactionEncoder: TransactionEncoder {
} }
func createTransaction( func createTransaction(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
zatoshi: Int, zatoshi: Zatoshi,
to address: String, to address: String,
memo: String?, memo: String?,
from accountIndex: Int from accountIndex: Int
@ -80,8 +80,8 @@ class WalletTransactionEncoder: TransactionEncoder {
// swiftlint:disable:next function_parameter_count // swiftlint:disable:next function_parameter_count
func createTransaction( func createTransaction(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
zatoshi: Int, zatoshi: Zatoshi,
to address: String, to address: String,
memo: String?, memo: String?,
from accountIndex: Int, 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 { guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else {
throw TransactionEncoderError.missingParams throw TransactionEncoderError.missingParams
} }
@ -115,9 +121,9 @@ class WalletTransactionEncoder: TransactionEncoder {
let txId = rustBackend.createToAddress( let txId = rustBackend.createToAddress(
dbData: self.dataDbURL, dbData: self.dataDbURL,
account: Int32(accountIndex), account: Int32(accountIndex),
extsk: spendingKey, extsk: spendingKey.stringEncoded,
to: address, to: address,
value: Int64(zatoshi), value: zatoshi.amount,
memo: memo, memo: memo,
spendParamsPath: self.spendParamsURL.path, spendParamsPath: self.spendParamsURL.path,
outputParamsPath: self.outputParamsURL.path, outputParamsPath: self.outputParamsURL.path,
@ -132,14 +138,12 @@ class WalletTransactionEncoder: TransactionEncoder {
} }
func createShieldingTransaction( func createShieldingTransaction(
spendingKey: String, tAccountPrivateKey: TransparentAccountPrivKey,
tAccountPrivateKey: String,
memo: String?, memo: String?,
from accountIndex: Int from accountIndex: Int
) throws -> EncodedTransaction { ) throws -> EncodedTransaction {
let txId = try createShieldingSpend( let txId = try createShieldingSpend(
spendingKey: spendingKey, xprv: tAccountPrivateKey.encoding,
xprv: tAccountPrivateKey,
memo: memo, memo: memo,
accountIndex: accountIndex accountIndex: accountIndex
) )
@ -158,19 +162,7 @@ class WalletTransactionEncoder: TransactionEncoder {
} }
} }
func createShieldingTransaction( func createShieldingSpend(xprv: String, memo: String?, accountIndex: Int) throws -> Int {
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 {
guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else { guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else {
throw TransactionEncoderError.missingParams throw TransactionEncoderError.missingParams
} }
@ -180,7 +172,6 @@ class WalletTransactionEncoder: TransactionEncoder {
dbData: self.dataDbURL, dbData: self.dataDbURL,
account: Int32(accountIndex), account: Int32(accountIndex),
xprv: xprv, xprv: xprv,
extsk: spendingKey,
memo: memo, memo: memo,
spendParamsPath: self.spendParamsURL.path, spendParamsPath: self.spendParamsURL.path,
outputParamsPath: self.outputParamsURL.path, outputParamsPath: self.outputParamsURL.path,

View File

@ -302,7 +302,7 @@ class AdvancedReOrgTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: coordinator.spendingKeys!.first!, spendingKey: coordinator.spendingKeys!.first!,
zatoshi: sendAmount, zatoshi: sendAmount,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test transaction", memo: "test transaction",
from: 0 from: 0
) { result in ) { result in
@ -709,7 +709,7 @@ class AdvancedReOrgTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: Zatoshi(20000), zatoshi: Zatoshi(20000),
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -1069,7 +1069,7 @@ class AdvancedReOrgTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: Zatoshi(20000), zatoshi: Zatoshi(20000),
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in

View File

@ -82,7 +82,7 @@ class BalanceTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: maxBalance, zatoshi: maxBalance,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test send \(self.description) \(Date().description)", memo: "test send \(self.description) \(Date().description)",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -226,7 +226,7 @@ class BalanceTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: maxBalanceMinusOne, zatoshi: maxBalanceMinusOne,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test send \(self.description) \(Date().description)", memo: "test send \(self.description) \(Date().description)",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -367,7 +367,7 @@ class BalanceTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: maxBalanceMinusOne, zatoshi: maxBalanceMinusOne,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test send \(self.description) \(Date().description)", memo: "test send \(self.description) \(Date().description)",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -510,7 +510,7 @@ class BalanceTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: sendAmount, zatoshi: sendAmount,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test send \(self.description) \(Date().description)", memo: "test send \(self.description) \(Date().description)",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -681,7 +681,7 @@ class BalanceTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: sendAmount, zatoshi: sendAmount,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test send \(self.description) \(Date().description)", memo: "test send \(self.description) \(Date().description)",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -836,7 +836,7 @@ class BalanceTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKeys, spendingKey: spendingKeys,
zatoshi: sendAmount, zatoshi: sendAmount,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: memo, memo: memo,
from: 0, from: 0,
resultBlock: { sendResult in resultBlock: { sendResult in
@ -1008,7 +1008,7 @@ class BalanceTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: sendAmount, zatoshi: sendAmount,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test send \(self.description)", memo: "test send \(self.description)",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in

View File

@ -82,7 +82,7 @@ class NetworkUpgradeTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: spendAmount, zatoshi: spendAmount,
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -171,7 +171,7 @@ class NetworkUpgradeTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: spendAmount, zatoshi: spendAmount,
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -240,7 +240,7 @@ class NetworkUpgradeTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: spendAmount, zatoshi: spendAmount,
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -341,7 +341,7 @@ class NetworkUpgradeTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: spendAmount, zatoshi: spendAmount,
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in
@ -453,7 +453,7 @@ class NetworkUpgradeTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: spendAmount, zatoshi: spendAmount,
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in

View File

@ -97,7 +97,7 @@ class PendingTransactionUpdatesTest: XCTestCase {
// swiftlint:disable:next force_unwrapping // swiftlint:disable:next force_unwrapping
spendingKey: self.coordinator.spendingKeys!.first!, spendingKey: self.coordinator.spendingKeys!.first!,
zatoshi: Zatoshi(20000), zatoshi: Zatoshi(20000),
toAddress: self.testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "this is a test", memo: "this is a test",
from: 0, from: 0,
resultBlock: { result in resultBlock: { result in

View File

@ -169,7 +169,7 @@ class RewindRescanTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: coordinator.spendingKey, spendingKey: coordinator.spendingKey,
zatoshi: Zatoshi(1000), zatoshi: Zatoshi(1000),
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: nil, memo: nil,
from: 0 from: 0
) { result in ) { result in
@ -270,7 +270,7 @@ class RewindRescanTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: spendingKey, spendingKey: spendingKey,
zatoshi: maxBalance, zatoshi: maxBalance,
toAddress: testRecipientAddress, toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test send \(self.description) \(Date().description)", memo: "test send \(self.description) \(Date().description)",
from: 0 from: 0
) { result in ) { result in

View File

@ -84,7 +84,7 @@ class ShieldFundsTests: XCTestCase {
/// ///
func testShieldFunds() throws { func testShieldFunds() throws {
// 1. load the dataset // 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) try coordinator.stageBlockCreate(height: birthday + 1, count: 200, nonce: 0)
@ -209,7 +209,6 @@ class ShieldFundsTests: XCTestCase {
// shield the funds // shield the funds
coordinator.synchronizer.shieldFunds( coordinator.synchronizer.shieldFunds(
spendingKey: coordinator.spendingKey,
transparentAccountPrivateKey: transparentAccountPrivateKey, transparentAccountPrivateKey: transparentAccountPrivateKey,
memo: "shield funds", memo: "shield funds",
from: 0 from: 0

View File

@ -104,7 +104,7 @@ class Z2TReceiveTests: XCTestCase {
coordinator.synchronizer.sendToAddress( coordinator.synchronizer.sendToAddress(
spendingKey: coordinator.spendingKeys!.first!, spendingKey: coordinator.spendingKeys!.first!,
zatoshi: sendAmount, zatoshi: sendAmount,
toAddress: testRecipientAddress, toAddress: try! Recipient(testRecipientAddress, network: self.network.networkType),
memo: "test transaction", memo: "test transaction",
from: 0 from: 0
) { result in ) { result in

View File

@ -19,9 +19,9 @@ class BlockScanOperationTests: XCTestCase {
var cacheDbURL: URL! var cacheDbURL: URL!
var dataDbURL: URL! var dataDbURL: URL!
var ufvk = UFVFakeKey( var ufvk = UnifiedFullViewingKey(
account: 0, validatedEncoding: "uviewtest1q48t999peecrfkq7ykcxckfkjt77w3lckk5mptlrtuy7xltjnzg8fm5434cxe9p9838ljs24yv83rluhk33ew098dkarapzyj4vk5kfxp5zn2jp3ww74jwd48r05aqjvgqxzx3nqn6zfqh3cmwdtmz0mc5624tvdza55q7mguxrehwcy4y0uktcpp4tkpex4qhazddux4yt6hr0sc9fkqmfr5tyz6ldd7yrq93tyj7446u4kst3vhmd40uga636p56hr0hjfdhgp07qyh90kmsl3qnmld6c8h7u06vekkjywmxv07mqzz9muwcl6weczrn5vf3p27uc9ufrumdp64zdzulzvc373wx3gl0yntntujhcsjhrwk9xwyjpvyuf0s8q3mgjs7uy3pg960w40dthpngcnauhgg9xq8cdcyfkq7ctnngqg4nkp5eh9knd4ckwjyd9czdd240lumul96r2fuerlvjeha6cyn9ftm7gr6xqjmq0zy6tv", // swiftlint:disable:this line_length
encoding: "uviewtest1q48t999peecrfkq7ykcxckfkjt77w3lckk5mptlrtuy7xltjnzg8fm5434cxe9p9838ljs24yv83rluhk33ew098dkarapzyj4vk5kfxp5zn2jp3ww74jwd48r05aqjvgqxzx3nqn6zfqh3cmwdtmz0mc5624tvdza55q7mguxrehwcy4y0uktcpp4tkpex4qhazddux4yt6hr0sc9fkqmfr5tyz6ldd7yrq93tyj7446u4kst3vhmd40uga636p56hr0hjfdhgp07qyh90kmsl3qnmld6c8h7u06vekkjywmxv07mqzz9muwcl6weczrn5vf3p27uc9ufrumdp64zdzulzvc373wx3gl0yntntujhcsjhrwk9xwyjpvyuf0s8q3mgjs7uy3pg960w40dthpngcnauhgg9xq8cdcyfkq7ctnngqg4nkp5eh9knd4ckwjyd9czdd240lumul96r2fuerlvjeha6cyn9ftm7gr6xqjmq0zy6tv" // swiftlint:disable:this line_length account: 0
) )
var walletBirthDay = Checkpoint.birthday( var walletBirthDay = Checkpoint.birthday(
@ -268,8 +268,3 @@ extension BlockScanOperationTests: CompactBlockProgressDelegate {
func progressUpdated(_ progress: CompactBlockProgress) { func progressUpdated(_ progress: CompactBlockProgress) {
} }
} }
struct UFVFakeKey: UnifiedFullViewingKey {
var account: UInt32
var encoding: String
}

View File

@ -53,50 +53,48 @@ class ZcashRustBackendTests: XCTestCase {
func testDeriveExtendedSpendingKeys() { func testDeriveExtendedSpendingKeys() {
let seed = Array("testreferencealicetestreferencealice".utf8) let seed = Array("testreferencealicetestreferencealice".utf8)
var spendingKeys: [String]? var spendingKeys: [SaplingExtendedSpendingKey]?
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
XCTAssertNotNil(spendingKeys) XCTAssertNotNil(spendingKeys)
XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true) XCTAssertEqual(spendingKeys?.count, 1)
} }
func testDeriveExtendedFullViewingKeys() { func testDeriveExtendedFullViewingKeys() {
let seed = Array("testreferencealicetestreferencealice".utf8) let seed = Array("testreferencealicetestreferencealice".utf8)
var fullViewingKeys: [String]? var fullViewingKeys: [SaplingExtendedFullViewingKey]?
XCTAssertNoThrow( XCTAssertNoThrow(
try { try {
fullViewingKeys = try ZcashRustBackend.deriveExtendedFullViewingKeys( fullViewingKeys = try ZcashRustBackend.deriveSaplingExtendedFullViewingKeys(
seed: seed, seed: seed,
accounts: 1, accounts: 2,
networkType: networkType networkType: networkType
) )
}() }()
) )
XCTAssertNotNil(fullViewingKeys) XCTAssertNotNil(fullViewingKeys)
XCTAssertFalse(fullViewingKeys?.first?.isEmpty ?? true) XCTAssertEqual(fullViewingKeys?.count, 2)
} }
func testDeriveExtendedFullViewingKey() { func testDeriveExtendedFullViewingKey() {
let seed = Array("testreferencealicetestreferencealice".utf8) let seed = Array("testreferencealicetestreferencealice".utf8)
var fullViewingKey: String? var fullViewingKey: SaplingExtendedFullViewingKey?
var spendingKeys: [String]? var spendingKeys: [SaplingExtendedSpendingKey]?
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }()) XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
XCTAssertNotNil(spendingKeys) XCTAssertNotNil(spendingKeys)
XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true)
guard let spendingKey = spendingKeys?.first else { guard let spendingKey = spendingKeys?.first else {
XCTFail("no spending key generated") XCTFail("no spending key generated")
return return
} }
XCTAssertNoThrow(try { fullViewingKey = try ZcashRustBackend.deriveExtendedFullViewingKey(spendingKey, networkType: networkType) }()) XCTAssertNoThrow(try { fullViewingKey = try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(spendingKey, networkType: networkType) }())
XCTAssertNotNil(fullViewingKey) XCTAssertNotNil(fullViewingKey)
XCTAssertFalse(fullViewingKey?.isEmpty ?? true)
} }
func testInitAndScanBlocks() { func testInitAndScanBlocks() {
@ -164,12 +162,12 @@ class ZcashRustBackendTests: XCTestCase {
} }
} }
func testIsValidShieldedAddressTrue() { func testIsValidSaplingAddressTrue() {
var isValid: Bool? var isValid: Bool?
XCTAssertNoThrow( XCTAssertNoThrow(
try { try {
isValid = try ZcashRustBackend.isValidShieldedAddress( isValid = try ZcashRustBackend.isValidSaplingAddress(
"ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc", "ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc",
networkType: networkType networkType: networkType
) )
@ -183,12 +181,12 @@ class ZcashRustBackendTests: XCTestCase {
} }
} }
func testIsValidShieldedAddressFalse() { func testIsValidSaplingAddressFalse() {
var isValid: Bool? var isValid: Bool?
XCTAssertNoThrow( XCTAssertNoThrow(
try { try {
isValid = try ZcashRustBackend.isValidShieldedAddress( isValid = try ZcashRustBackend.isValidSaplingAddress(
"tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7", "tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7",
networkType: networkType networkType: networkType
) )

View File

@ -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)
}
}

View File

@ -6,24 +6,26 @@
// //
//swiftlint:disable force_unwrapping //swiftlint:disable force_unwrapping
import XCTest import XCTest
import ZcashLightClientKit @testable import ZcashLightClientKit
class DerivationToolMainnetTests: XCTestCase { 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 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==")! 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 expectedViewingKey = UnifiedFullViewingKey(validatedEncoding: "uview17fme6ux853km45g9ep07djpfzeydxxgm22xpmr7arzxyutlusalgpqlx7suga4ahzywfuwz4jclm00u7g8u65qvvdt45kttnfunvschssg3h3g06txs9ja32vx3xa8dej3unnatgzjvd0vumk37t8es3ludldrtse3q6226ws7eq4q0ywz78nudwpepgdn7jmxz8yvp7k6gxkeynkam0f8aqf9qpeaej55zhkw39x7epayhndul0j4xjttdxxlnwcd09nr8svyx8j0zng0w6scx3m5unpkaqxcm3hslhlfg4caz7r8d4xy9wm7klkg79w7j0uyzec5s3yje20eg946r6rmkf532nfydu26s8q9ua7mwxw2j2ag7hfcuu652gw6uta03vlm05zju3a9rwc4h367kqzfqrcz35pdwdk2a7yqnk850un3ujxcvve45ueajgvtr6dj4ufszgqwdy0aedgmkalx2p7qed2suarwkr35dl0c8dnqp3", account: 0)
let expectedSaplingExtendedViewingKey = "zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy" let expectedSaplingExtendedViewingKey = SaplingExtendedFullViewingKey(validatedEncoding: "zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy")
let expectedSaplingAddress = SaplingAddress(validatedEncoding: "zs1vp7kvlqr4n9gpehztr76lcn6skkss9p8keqs3nv8avkdtjrcctrvmk9a7u494kluv756jeee5k0")
let derivationTool = DerivationTool(networkType: NetworkType.mainnet) let derivationTool = DerivationTool(networkType: NetworkType.mainnet)
let expectedTransparentAddress = "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz" let expectedTransparentAddress = TransparentAddress(validatedEncoding: "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz")
func testDeriveViewingKeysFromSeed() throws { func testDeriveViewingKeysFromSeed() throws {
let accounts: Int = 1 let accounts: Int = 1
let seedBytes = [UInt8](seedData) 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") 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 { func testDeriveUnifiedAddressFromSeed() throws {
let seedBytes = [UInt8](seedData) let seedBytes = [UInt8](seedData)
let shieldedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0) let unifiedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0)
XCTAssertEqual(shieldedAddress, testRecipientAddress) XCTAssertEqual(unifiedAddress, testRecipientAddress)
} }
func testDeriveUnifiedAddressFromViewingKey() throws { func testDeriveUnifiedAddressFromViewingKey() throws {
XCTAssertEqual(try derivationTool.deriveUnifiedAddress(viewingKey: expectedViewingKey), testRecipientAddress) XCTAssertEqual(try derivationTool.deriveUnifiedAddress(from: expectedViewingKey), testRecipientAddress)
} }
func testDeriveTransparentAddressFromSeed() throws { func testDeriveTransparentAddressFromSeed() throws {
@ -76,7 +78,7 @@ class DerivationToolMainnetTests: XCTestCase {
} }
func testDeriveTransparentAccountPrivateKeyFromSeed() throws { func testDeriveTransparentAccountPrivateKeyFromSeed() throws {
XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), "xprv9yCTU6giJ1qZ1DLC5rc7KMzwY9s8rSRXYqmoAKffAExpUVUKLhcdvN9ERdxjEW8tQq4pxerLKZE3WcNUKZCeX19rVTxpV2msTyNMNiFT3Nw") XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), TransparentAccountPrivKey(encoding: "xprv9yCTU6giJ1qZ1DLC5rc7KMzwY9s8rSRXYqmoAKffAExpUVUKLhcdvN9ERdxjEW8tQq4pxerLKZE3WcNUKZCeX19rVTxpV2msTyNMNiFT3Nw"))
} }
func testDeriveUnifiedKeysFromSeed() throws { func testDeriveUnifiedKeysFromSeed() throws {
@ -84,7 +86,7 @@ class DerivationToolMainnetTests: XCTestCase {
XCTAssertEqual(unifiedKeys.count, 1) XCTAssertEqual(unifiedKeys.count, 1)
XCTAssertEqual(unifiedKeys[0].account, 0) XCTAssertEqual(unifiedKeys[0].account, 0)
XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey) XCTAssertEqual(unifiedKeys[0], expectedViewingKey)
} }
func testDeriveQuiteALotOfUnifiedKeysFromSeed() throws { func testDeriveQuiteALotOfUnifiedKeysFromSeed() throws {
@ -92,11 +94,25 @@ class DerivationToolMainnetTests: XCTestCase {
XCTAssertEqual(unifiedKeys.count, 10) XCTAssertEqual(unifiedKeys.count, 10)
XCTAssertEqual(unifiedKeys[0].account, 0) XCTAssertEqual(unifiedKeys[0].account, 0)
XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey) XCTAssertEqual(unifiedKeys[0], expectedViewingKey)
} }
func testShouldFailOnInvalidChecksumAddresses() throws { func testShouldFailOnInvalidChecksumAddresses() throws {
let testAddress = "t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1" let testAddress = "t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1"
XCTAssertFalse(try derivationTool.isValidTransparentAddress(testAddress)) 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"))
// }
} }

View File

@ -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"))
// }
}

View File

@ -17,7 +17,7 @@ class NullBytesTests: XCTestCase {
let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp" let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp"
let zAddrWithNullBytes = "\(validZaddr)\0something else that makes the address invalid" 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 { func testTaddrNullBytes() throws {
@ -91,13 +91,13 @@ class NullBytesTests: XCTestCase {
func testderiveExtendedFullViewingKeyWithNullBytes() throws { func testderiveExtendedFullViewingKeyWithNullBytes() throws {
// swiftlint:disable:next line_length // 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 // swiftlint:disable:next line_length
let goodSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" let goodSpendingKeys = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv")
XCTAssertThrowsError( XCTAssertThrowsError(
try ZcashRustBackend.deriveExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType), try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType),
"Should have thrown an error but didn't! this is dangerous!" "Should have thrown an error but didn't! this is dangerous!"
) { error in ) { error in
guard let rustError = error as? RustWeldingError else { 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 { func testCheckNullBytes() throws {

View File

@ -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))
}
}

View File

@ -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")
}
}

View File

@ -77,10 +77,36 @@ extension LightWalletServiceMockResponse {
} }
class MockRustBackend: ZcashRustBackendWelding { class MockRustBackend: ZcashRustBackendWelding {
static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.DbInitResult { static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.DbInitResult {
.seedRequired .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 { static func clearUtxos(dbData: URL, address: String, sinceHeight: BlockHeight, networkType: NetworkType) throws -> Int32 {
-1 -1
} }
@ -140,7 +166,6 @@ class MockRustBackend: ZcashRustBackendWelding {
dbData: URL, dbData: URL,
account: Int32, account: Int32,
xprv: String, xprv: String,
extsk: String,
memo: String?, memo: String?,
spendParamsPath: String, spendParamsPath: String,
outputParamsPath: String, outputParamsPath: String,
@ -169,7 +194,7 @@ class MockRustBackend: ZcashRustBackendWelding {
throw KeyDerivationErrors.unableToDerive throw KeyDerivationErrors.unableToDerive
} }
static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool { static func isValidSaplingExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
false false
} }
@ -189,11 +214,7 @@ class MockRustBackend: ZcashRustBackendWelding {
nil nil
} }
static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? { static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]? {
nil
}
static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? {
nil nil
} }
@ -217,7 +238,7 @@ class MockRustBackend: ZcashRustBackendWelding {
static var mockAcounts = false static var mockAcounts = false
static var mockError: RustWeldingError? static var mockError: RustWeldingError?
static var mockLastError: String? static var mockLastError: String?
static var mockAccounts: [String]? static var mockAccounts: [SaplingExtendedSpendingKey]?
static var mockAddresses: [String]? static var mockAddresses: [String]?
static var mockBalance: Int64? static var mockBalance: Int64?
static var mockVerifiedBalance: Int64? static var mockVerifiedBalance: Int64?
@ -240,7 +261,7 @@ class MockRustBackend: ZcashRustBackendWelding {
mockLastError ?? rustBackend.getLastError() mockLastError ?? rustBackend.getLastError()
} }
static func isValidShieldedAddress(_ address: String, networkType: NetworkType) throws -> Bool { static func isValidSaplingAddress(_ address: String, networkType: NetworkType) throws -> Bool {
true true
} }
@ -254,7 +275,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) mockAccounts ?? rustBackend.initAccountsTable(dbData: dbData, seed: seed, accounts: accounts, networkType: networkType)
} }

View File

@ -37,12 +37,12 @@ class TestCoordinator {
var completionHandler: ((SDKSynchronizer) -> Void)? var completionHandler: ((SDKSynchronizer) -> Void)?
var errorHandler: ((Error?) -> Void)? var errorHandler: ((Error?) -> Void)?
var spendingKey: String var spendingKey: SaplingExtendedSpendingKey
var birthday: BlockHeight var birthday: BlockHeight
var channelProvider: ChannelProvider var channelProvider: ChannelProvider
var synchronizer: SDKSynchronizer var synchronizer: SDKSynchronizer
var service: DarksideWalletService var service: DarksideWalletService
var spendingKeys: [String]? var spendingKeys: [SaplingExtendedSpendingKey]?
var databases: TemporaryTestDatabases var databases: TemporaryTestDatabases
let network: ZcashNetwork let network: ZcashNetwork
convenience init( convenience init(
@ -85,7 +85,7 @@ class TestCoordinator {
} }
required init( required init(
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
unifiedFullViewingKey: UnifiedFullViewingKey, unifiedFullViewingKey: UnifiedFullViewingKey,
walletBirthday: BlockHeight, walletBirthday: BlockHeight,
channelProvider: ChannelProvider, channelProvider: ChannelProvider,
@ -288,13 +288,13 @@ enum TestSynchronizerBuilder {
storage: CompactBlockStorage, storage: CompactBlockStorage,
spendParamsURL: URL, spendParamsURL: URL,
outputParamsURL: URL, outputParamsURL: URL,
spendingKey: String, spendingKey: SaplingExtendedSpendingKey,
unifiedFullViewingKey: UnifiedFullViewingKey, unifiedFullViewingKey: UnifiedFullViewingKey,
walletBirthday: BlockHeight, walletBirthday: BlockHeight,
network: ZcashNetwork, network: ZcashNetwork,
seed: [UInt8]? = nil, seed: [UInt8]? = nil,
loggerProxy: Logger? = nil loggerProxy: Logger? = nil
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { ) throws -> (spendingKeys: [SaplingExtendedSpendingKey]?, synchronizer: SDKSynchronizer) {
let initializer = Initializer( let initializer = Initializer(
cacheDbURL: cacheDbURL, cacheDbURL: cacheDbURL,
dataDbURL: dataDbURL, dataDbURL: dataDbURL,
@ -334,7 +334,7 @@ enum TestSynchronizerBuilder {
walletBirthday: BlockHeight, walletBirthday: BlockHeight,
network: ZcashNetwork, network: ZcashNetwork,
loggerProxy: Logger? = nil loggerProxy: Logger? = nil
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) { ) throws -> (spendingKeys: [SaplingExtendedSpendingKey]?, synchronizer: SDKSynchronizer) {
guard guard
let spendingKey = try DerivationTool(networkType: network.networkType) let spendingKey = try DerivationTool(networkType: network.networkType)
.deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1) .deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1)