[#461] Adopt a Type-Safe Keys and Addresses API
This PR creates data types for Addresses and Keys so that they are not represented by Strings anymore. This avoids mistakenly use the wrong keys because they are all alike for the type system. New Protocols: ============= StringEncoded -> Protocol that makes a type can be expressed in an string-encoded fashion either for UI or Interchange purposes. Undescribable -> A protocol that implements methods that override default decriptions used by debuggers, loggers and event trackers to avoid types conforming to it to be leaked to logs. Deleted Protocols: ================== UnifiedFullViewingKey --> turned into a struct. UnifiedAddress --> turned into a struct new Error Type: ================ ```` enum KeyEncodingError: Error { case invalidEncoding } ```` This error is thrown when an Address or Key type (addresses are public keys in the end) can be decoded from their String representation, typically upon initialization from a User input. New Types: ========= SaplingExtendedSpendingKey -> Type for Sapling Extended Full Viewing Keys this type will be replaced with Unified Spending Keys soon. SaplingExtendedFullViewingKey -> Extended Full Viewing Key for Sapling. Maintains existing funcionality. Will be probably deprecated in favor of UFVK. TransparentAccountPrivKey -> Private key for transparent account. Used only for shielding operations. Note: this will probably be deprecated soon. UnifiedFullViewingKey -> Replaces the protocol that had the same name. TransparentAddress -> Replaces a type alias with a struct SaplingAddress --> 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. UnifiedAddress -> Represents a UA. String-encodable and Equatable. Use of UAs must be favored instead of individual receivers for different pools. This type can't be decomposed into their Receiver types yet. Recipient -> This represents all valid receiver types to be used as inputs for outgoing transactions. ```` public enum Recipient: Equatable, StringEncoded { case transparent(TransparentAddress) case sapling(SaplingAddress) case unified(UnifiedAddress) ```` The wrapped concrete receiver is a valid receiver type. Deleted Type Aliases: ===================== The following aliases were deleted and turned into types ```` public typealias TransparentAddress = String public typealias SaplingShieldedAddress = String ```` Changes to Derivation Tool ========================== DerivationTool has been changed to accomodate this new types and remove Strings whenever possible. Changes to Synchronizer and CompactBlockProcessor ================================================= Accordingly these to components have been modified to accept the new types intead of strings when possible. Changes to Demo App =================== The demo App has been patch to compile and work with the new types. Developers must consider that the use (and abuse) of forced_try and forced unwrapping is a "license" that maintainers are using for the sake of brevity. We consider that clients of this SDK do know how to handle Errors and Optional and it is not the objective of the demo code to show good practices on those matters. Closes #461
This commit is contained in:
parent
905ee401d1
commit
ed87a2781c
|
@ -3,7 +3,7 @@
|
|||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 52;
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
|
@ -15,7 +15,6 @@
|
|||
0D756A94236C761E009B041B /* GetAddressViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D756A93236C761E009B041B /* GetAddressViewController.swift */; };
|
||||
0D76121926B1D66D001CA417 /* Testnet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D76121826B1D66D001CA417 /* Testnet.swift */; };
|
||||
0D7A4A83236CCD88001F4DD8 /* SyncBlocksViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7A4A82236CCD88001F4DD8 /* SyncBlocksViewController.swift */; };
|
||||
0D7C85E523AD5A9B006878FC /* SampleStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7C85E423AD5A9B006878FC /* SampleStorage.swift */; };
|
||||
0D907F162322CC5900D641FE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F152322CC5900D641FE /* AppDelegate.swift */; };
|
||||
0D907F182322CC5900D641FE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D907F172322CC5900D641FE /* ViewController.swift */; };
|
||||
0D907F1B2322CC5900D641FE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0D907F192322CC5900D641FE /* Main.storyboard */; };
|
||||
|
@ -35,7 +34,6 @@
|
|||
0DA1C4C727D11D9500E5006E /* SaplingParametersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D6CE8BC252E3C4A0005D707 /* SaplingParametersViewController.swift */; };
|
||||
0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1BE47E2581937100F78BE3 /* GetUTXOsViewController.swift */; };
|
||||
0DA1C4C927D11D9500E5006E /* DerivationToolViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D1BE4502581585C00F78BE3 /* DerivationToolViewController.swift */; };
|
||||
0DA1C4CA27D11D9500E5006E /* SampleStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D7C85E423AD5A9B006878FC /* SampleStorage.swift */; };
|
||||
0DA1C4CB27D11D9500E5006E /* SampleLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D49A18B241698A800CC0649 /* SampleLogger.swift */; };
|
||||
0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DA58B952397F2CB004596EA /* TransactionsDataSource.swift */; };
|
||||
0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DF53E6623A438F100D7249C /* PaginatedTransactionsViewController.swift */; };
|
||||
|
@ -83,7 +81,6 @@
|
|||
0D76121526B1D5ED001CA417 /* Mainnet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mainnet.swift; sourceTree = "<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>"; };
|
||||
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; };
|
||||
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>"; };
|
||||
|
@ -248,7 +245,6 @@
|
|||
0D907F212322CC5B00D641FE /* Info.plist */,
|
||||
F94912622790D7C4004BB3DE /* ZcashLightClientSample-Mainnet-Info.plist */,
|
||||
0DDFB33D236B844900AED892 /* DemoAppConfig.swift */,
|
||||
0D7C85E423AD5A9B006878FC /* SampleStorage.swift */,
|
||||
0D49A18B241698A800CC0649 /* SampleLogger.swift */,
|
||||
);
|
||||
path = ZcashLightClientSample;
|
||||
|
@ -498,6 +494,7 @@
|
|||
};
|
||||
0DCB58A4237B5B580040096C /* ShellScript */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
|
@ -534,7 +531,6 @@
|
|||
0D6CE8BD252E3C4A0005D707 /* SaplingParametersViewController.swift in Sources */,
|
||||
0D1BE47F2581937100F78BE3 /* GetUTXOsViewController.swift in Sources */,
|
||||
0D1BE4512581585C00F78BE3 /* DerivationToolViewController.swift in Sources */,
|
||||
0D7C85E523AD5A9B006878FC /* SampleStorage.swift in Sources */,
|
||||
0D49A18C241698A800CC0649 /* SampleLogger.swift in Sources */,
|
||||
0DA58B962397F2CB004596EA /* TransactionsDataSource.swift in Sources */,
|
||||
0DF53E6723A438F100D7249C /* PaginatedTransactionsViewController.swift in Sources */,
|
||||
|
@ -567,7 +563,6 @@
|
|||
0DA1C4C727D11D9500E5006E /* SaplingParametersViewController.swift in Sources */,
|
||||
0DA1C4C827D11D9500E5006E /* GetUTXOsViewController.swift in Sources */,
|
||||
0DA1C4C927D11D9500E5006E /* DerivationToolViewController.swift in Sources */,
|
||||
0DA1C4CA27D11D9500E5006E /* SampleStorage.swift in Sources */,
|
||||
0DA1C4CB27D11D9500E5006E /* SampleLogger.swift in Sources */,
|
||||
0DA1C4CC27D11D9500E5006E /* TransactionsDataSource.swift in Sources */,
|
||||
0DA1C4CD27D11D9500E5006E /* PaginatedTransactionsViewController.swift in Sources */,
|
||||
|
|
|
@ -138,10 +138,10 @@
|
|||
},
|
||||
{
|
||||
"package": "libzcashlc",
|
||||
"repositoryURL": "https://github.com/nuttycom/zcash-light-client-ffi.git",
|
||||
"repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state": {
|
||||
"branch": "bin/librustzcash_0_7-migrations",
|
||||
"revision": "dbf7ac51a7de0bbc2c1088973236d68ceb752361",
|
||||
"branch": "bin/librustzcash_0_7",
|
||||
"revision": "823f864a7952073fb9718cf75610691756e34d59",
|
||||
"version": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,10 +50,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||
)
|
||||
|
||||
_ = try! wallet.initialize(with: DemoAppConfig.seed)
|
||||
var storage = SampleStorage.shared
|
||||
storage!.seed = Data(DemoAppConfig.seed)
|
||||
storage!.privateKey = try! DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
.deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1)[0]
|
||||
|
||||
self.wallet = wallet
|
||||
return wallet
|
||||
}
|
||||
|
@ -141,10 +138,6 @@ extension AppDelegate {
|
|||
} catch {
|
||||
loggerProxy.error("error clearing data db: \(error)")
|
||||
}
|
||||
|
||||
var storage = SampleStorage.shared
|
||||
storage!.seed = nil
|
||||
storage!.privateKey = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,6 @@ class DerivationToolViewController: UIViewController {
|
|||
enum DerivationErrors: Error {
|
||||
case couldNotDeriveSpendingKeys(underlyingError: Error)
|
||||
case couldNotDeriveViewingKeys(underlyingError: Error)
|
||||
case couldNotDeriveShieldedAddress(underlyingError: Error)
|
||||
case couldNotDeriveTransparentAddress(underlyingError: Error)
|
||||
case unknown
|
||||
}
|
||||
|
||||
|
@ -113,15 +111,15 @@ class DerivationToolViewController: UIViewController {
|
|||
throw DerivationErrors.couldNotDeriveViewingKeys(underlyingError: DerivationErrors.unknown)
|
||||
}
|
||||
|
||||
let unifiedAddress = try derivationTool.deriveUnifiedAddress(viewingKey: viewingKey.encoding)
|
||||
let unifiedAddress = try derivationTool.deriveUnifiedAddress(from: viewingKey)
|
||||
|
||||
let transparentAddress = try derivationTool.deriveTransparentAddress(seed: seedBytes)
|
||||
|
||||
updateLabels(
|
||||
spendingKey: spendingKey,
|
||||
viewingKey: viewingKey.encoding,
|
||||
shieldedAddress: unifiedAddress,
|
||||
transaparentAddress: transparentAddress
|
||||
spendingKey: spendingKey.stringEncoded,
|
||||
viewingKey: viewingKey.stringEncoded,
|
||||
shieldedAddress: unifiedAddress.stringEncoded,
|
||||
transaparentAddress: transparentAddress.stringEncoded
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -13,14 +13,22 @@ class GetAddressViewController: UIViewController {
|
|||
@IBOutlet weak var tAddressLabel: UILabel!
|
||||
@IBOutlet weak var spendingKeyLabel: UILabel! // THIS SHOULD BE SUPER SECRET!!!!!
|
||||
|
||||
// THIS SHOULD NEVER BE STORED IN MEMORY
|
||||
// swiftlint:disable:next implicitly_unwrapped_optional
|
||||
var spendingKey: SaplingExtendedSpendingKey!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
unifiedAddressLabel.text = (try? derivationTool.deriveUnifiedAddress(seed: DemoAppConfig.seed, accountIndex: 0)) ?? "No Addresses found"
|
||||
tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed)) ?? "could not derive t-address"
|
||||
spendingKeyLabel.text = SampleStorage.shared.privateKey ?? "No Spending Key found"
|
||||
// swiftlint:disable:next force_try force_unwrapping
|
||||
self.spendingKey = try! derivationTool.deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1).first!
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
// swiftlint:disable:next line_length
|
||||
unifiedAddressLabel.text = (try? derivationTool.deriveUnifiedAddress(seed: DemoAppConfig.seed, accountIndex: 0))?.stringEncoded ?? "No Addresses found"
|
||||
tAddressLabel.text = (try? derivationTool.deriveTransparentAddress(seed: DemoAppConfig.seed))?.stringEncoded ?? "could not derive t-address"
|
||||
spendingKeyLabel.text = self.spendingKey.stringEncoded
|
||||
unifiedAddressLabel.addGestureRecognizer(
|
||||
UITapGestureRecognizer(
|
||||
target: self,
|
||||
|
@ -44,20 +52,12 @@ class GetAddressViewController: UIViewController {
|
|||
)
|
||||
spendingKeyLabel.isUserInteractionEnabled = true
|
||||
loggerProxy.info("Address: \(String(describing: Initializer.shared.getAddress()))")
|
||||
// NOTE: NEVER LOG YOUR PRIVATE KEYS IN REAL LIFE
|
||||
// swiftlint:disable:next print_function_usage
|
||||
print("Spending Key: \(SampleStorage.shared.privateKey ?? "No Spending Key found")")
|
||||
}
|
||||
|
||||
@IBAction func spendingKeyTapped(_ gesture: UIGestureRecognizer) {
|
||||
guard let key = SampleStorage.shared.privateKey else {
|
||||
loggerProxy.warn("nothing to copy")
|
||||
return
|
||||
}
|
||||
|
||||
loggerProxy.event("copied to clipboard")
|
||||
|
||||
UIPasteboard.general.string = key
|
||||
UIPasteboard.general.string = self.spendingKey.stringEncoded
|
||||
let alert = UIAlertController(
|
||||
title: "",
|
||||
message: "Spending Key Copied to clipboard",
|
||||
|
@ -85,7 +85,7 @@ class GetAddressViewController: UIViewController {
|
|||
@IBAction func tAddressTapped(_ gesture: UIGestureRecognizer) {
|
||||
loggerProxy.event("copied to clipboard")
|
||||
UIPasteboard.general.string = try? DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
.deriveTransparentAddress(seed: DemoAppConfig.seed)
|
||||
.deriveTransparentAddress(seed: DemoAppConfig.seed).stringEncoded
|
||||
|
||||
let alert = UIAlertController(
|
||||
title: "",
|
||||
|
|
|
@ -27,7 +27,7 @@ class GetUTXOsViewController: UIViewController {
|
|||
let tAddress = try! DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
.deriveTransparentAddress(seed: DemoAppConfig.seed)
|
||||
|
||||
self.transparentAddressLabel.text = tAddress
|
||||
self.transparentAddressLabel.text = tAddress.stringEncoded
|
||||
|
||||
// swiftlint:disable:next force_try
|
||||
let balance = try! AppDelegate.shared.sharedSynchronizer.getTransparentBalance(accountIndex: 0)
|
||||
|
@ -40,14 +40,12 @@ class GetUTXOsViewController: UIViewController {
|
|||
do {
|
||||
let seed = DemoAppConfig.seed
|
||||
let derivationTool = DerivationTool(networkType: kZcashNetwork.networkType)
|
||||
// swiftlint:disable:next force_unwrapping
|
||||
let spendingKey = try derivationTool.deriveSpendingKeys(seed: seed, numberOfAccounts: 1).first!
|
||||
|
||||
let transparentSecretKey = try derivationTool.deriveTransparentAccountPrivateKey(seed: seed, account: 0)
|
||||
|
||||
KRProgressHUD.showMessage("🛡 Shielding 🛡")
|
||||
|
||||
AppDelegate.shared.sharedSynchronizer.shieldFunds(
|
||||
spendingKey: spendingKey,
|
||||
transparentAccountPrivateKey: transparentSecretKey,
|
||||
memo: "shielding is fun!",
|
||||
from: 0,
|
||||
|
|
|
@ -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() {}
|
||||
}
|
|
@ -150,7 +150,7 @@ class SendViewController: UIViewController {
|
|||
guard let addr = self.addressTextField.text else {
|
||||
return false
|
||||
}
|
||||
return wallet.isValidShieldedAddress(addr) || wallet.isValidTransparentAddress(addr)
|
||||
return wallet.isValidSaplingAddress(addr) || wallet.isValidTransparentAddress(addr)
|
||||
}
|
||||
|
||||
@IBAction func maxFundsValueChanged(_ sender: Any) {
|
||||
|
@ -206,18 +206,19 @@ class SendViewController: UIViewController {
|
|||
loggerProxy.warn("WARNING: Form is invalid")
|
||||
return
|
||||
}
|
||||
|
||||
guard let address = SampleStorage.shared.privateKey else {
|
||||
loggerProxy.error("NO ADDRESS")
|
||||
// swiftlint:disable:next line_length force_try
|
||||
guard let spendingKey = try! DerivationTool(networkType: kZcashNetwork.networkType).deriveSpendingKeys(seed: DemoAppConfig.seed, numberOfAccounts: 1).first else {
|
||||
loggerProxy.error("NO SPENDING KEY")
|
||||
return
|
||||
}
|
||||
|
||||
KRProgressHUD.show()
|
||||
|
||||
synchronizer.sendToAddress(
|
||||
spendingKey: address,
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: zec,
|
||||
toAddress: recipient,
|
||||
// swiftlint:disable:next force_try
|
||||
toAddress: try! Recipient(recipient, network: kZcashNetwork.networkType),
|
||||
memo: !self.memoField.text.isEmpty ? self.memoField.text : nil,
|
||||
from: 0
|
||||
) { [weak self] result in
|
||||
|
|
|
@ -84,10 +84,10 @@
|
|||
},
|
||||
{
|
||||
"package": "libzcashlc",
|
||||
"repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi.git",
|
||||
"repositoryURL": "https://github.com/zcash-hackworks/zcash-light-client-ffi",
|
||||
"state": {
|
||||
"branch": "librustzcash_0_7_build_test",
|
||||
"revision": "5a48541c1da48eb082defaf0fa127657f4f99094",
|
||||
"branch": "bin/librustzcash_0_7",
|
||||
"revision": "823f864a7952073fb9718cf75610691756e34d59",
|
||||
"version": null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ let package = Package(
|
|||
name: "ZcashLightClientKit",
|
||||
platforms: [
|
||||
.iOS(.v13),
|
||||
.macOS(.v10_12)
|
||||
.macOS(.v10_15)
|
||||
],
|
||||
products: [
|
||||
.library(
|
||||
|
@ -16,7 +16,7 @@ let package = Package(
|
|||
dependencies: [
|
||||
.package(url: "https://github.com/grpc/grpc-swift.git", from: "1.8.0"),
|
||||
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.13.0"),
|
||||
.package(name:"libzcashlc", url: "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", branch: "librustzcash_0_7_build_test"),
|
||||
.package(name:"libzcashlc", url: "https://github.com/zcash-hackworks/zcash-light-client-ffi", branch: "bin/librustzcash_0_7"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
|
|
@ -1260,18 +1260,27 @@ extension CompactBlockProcessor {
|
|||
|
||||
extension CompactBlockProcessor {
|
||||
public func getUnifiedAddres(accountIndex: Int) -> UnifiedAddress? {
|
||||
guard let account = try? accountRepository.findBy(account: accountIndex) else {
|
||||
// TODO: perform migrations on the account table to accommodate Unified Address or UFVK to to derive from.
|
||||
guard let address = try? accountRepository.findBy(account: accountIndex)?.address else {
|
||||
return nil
|
||||
}
|
||||
return UnifiedAddressShim(account: account)
|
||||
|
||||
return try? UnifiedAddress(encoding: address, network: self.config.network.networkType)
|
||||
}
|
||||
|
||||
public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? {
|
||||
try? accountRepository.findBy(account: accountIndex)?.address
|
||||
public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? {
|
||||
guard let zAddress = try? accountRepository.findBy(account: accountIndex)?.address else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return try? SaplingAddress(encoding: zAddress, network: self.config.network.networkType)
|
||||
}
|
||||
|
||||
public func getTransparentAddress(accountIndex: Int) -> TransparentAddress? {
|
||||
try? accountRepository.findBy(account: accountIndex)?.transparentAddress
|
||||
guard let tAddress = try? accountRepository.findBy(account: accountIndex)?.transparentAddress else { return nil }
|
||||
|
||||
return TransparentAddress(validatedEncoding: tAddress)
|
||||
|
||||
}
|
||||
|
||||
public func getTransparentBalance(accountIndex: Int) throws -> WalletBalance {
|
||||
|
@ -1282,20 +1291,6 @@ extension CompactBlockProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
private struct UnifiedAddressShim {
|
||||
let account: AccountEntity
|
||||
}
|
||||
|
||||
extension UnifiedAddressShim: UnifiedAddress {
|
||||
var encoding: String {
|
||||
account.transparentAddress
|
||||
}
|
||||
|
||||
var zAddress: SaplingShieldedAddress {
|
||||
account.address
|
||||
}
|
||||
}
|
||||
|
||||
extension CompactBlockProcessor {
|
||||
func refreshUTXOs(tAddress: String, startHeight: BlockHeight, result: @escaping (Result<RefreshedUTXOs, Error>) -> Void) {
|
||||
let dataDb = self.config.dataDb
|
||||
|
|
|
@ -31,17 +31,6 @@ struct Account: AccountEntity, Encodable, Decodable {
|
|||
var transparentAddress: String
|
||||
}
|
||||
|
||||
extension Account: UnifiedAddress {
|
||||
var encoding: String {
|
||||
get {
|
||||
address
|
||||
}
|
||||
set {
|
||||
address = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Account: Hashable {
|
||||
func hash(into hasher: inout Hasher) {
|
||||
hasher.combine(account)
|
||||
|
|
|
@ -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--")
|
||||
}
|
||||
}
|
|
@ -308,10 +308,10 @@ public class Initializer {
|
|||
}
|
||||
|
||||
/**
|
||||
checks if the provided address is a valid shielded zAddress
|
||||
checks if the provided address is a valid sapling address
|
||||
*/
|
||||
public func isValidShieldedAddress(_ address: String) -> Bool {
|
||||
(try? rustBackend.isValidShieldedAddress(address, networkType: network.networkType)) ?? false
|
||||
public func isValidSaplingAddress(_ address: String) -> Bool {
|
||||
(try? rustBackend.isValidSaplingAddress(address, networkType: network.networkType)) ?? false
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,21 +5,194 @@
|
|||
// Created by Francisco Gindre on 4/6/21.
|
||||
//
|
||||
|
||||
enum KeyEncodingError: Error {
|
||||
case invalidEncoding
|
||||
}
|
||||
|
||||
/// Something that can be encoded as a String
|
||||
public protocol StringEncoded {
|
||||
var stringEncoded: String { get }
|
||||
}
|
||||
|
||||
/// Sapling Extended Spending Key
|
||||
public struct SaplingExtendedSpendingKey: Equatable, StringEncoded, Undescribable {
|
||||
var encoding: String
|
||||
|
||||
public var stringEncoded: String {
|
||||
encoding
|
||||
}
|
||||
|
||||
/// Initializes a new Sapling Extended Full Viewing Key from the provided string encoding
|
||||
/// - Parameters:
|
||||
/// - parameter encoding: String encoding of ExtSK
|
||||
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
|
||||
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
|
||||
/// found to be invalid
|
||||
public init(encoding: String, network: NetworkType) throws {
|
||||
guard let valid = try? DerivationTool(networkType: network).isValidSaplingExtendedSpendingKey(encoding), valid else {
|
||||
throw KeyEncodingError.invalidEncoding
|
||||
}
|
||||
self.encoding = encoding
|
||||
}
|
||||
}
|
||||
|
||||
/// A Transparent Account Private Key
|
||||
public struct TransparentAccountPrivKey: Equatable, Undescribable {
|
||||
var encoding: String
|
||||
}
|
||||
|
||||
/**
|
||||
A ZIP 316 Unified Full Viewing Key.
|
||||
*/
|
||||
public protocol UnifiedFullViewingKey {
|
||||
var account: UInt32 { get set }
|
||||
var encoding: String { get set }
|
||||
public struct UnifiedFullViewingKey: Equatable, StringEncoded, Undescribable {
|
||||
var encoding: String
|
||||
|
||||
public var account: UInt32
|
||||
|
||||
public var stringEncoded: String { encoding }
|
||||
|
||||
/// Initializes a new UnifiedFullViewingKey (UFVK) from the provided string encoding
|
||||
/// - Parameters:
|
||||
/// - parameter encoding: String encoding of unified full viewing key
|
||||
/// - parameter account: account number of the given UFVK
|
||||
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
|
||||
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
|
||||
/// found to be invalid
|
||||
public init(encoding: String, account: UInt32, network: NetworkType) throws {
|
||||
guard let valid = try? DerivationTool(networkType: network).isValidExtendedViewingKey(encoding), valid else {
|
||||
throw KeyEncodingError.invalidEncoding
|
||||
}
|
||||
|
||||
self.encoding = encoding
|
||||
self.account = account
|
||||
}
|
||||
}
|
||||
|
||||
public typealias TransparentAddress = String
|
||||
public typealias SaplingShieldedAddress = String
|
||||
public struct SaplingExtendedFullViewingKey: Equatable, StringEncoded, Undescribable {
|
||||
var encoding: String
|
||||
public var stringEncoded: String {
|
||||
encoding
|
||||
}
|
||||
|
||||
public protocol UnifiedAddress {
|
||||
var encoding: String { get }
|
||||
/// Initializes a new Extended Full Viewing key (EFVK) for Sapling from the provided string encoding
|
||||
/// - Parameters:
|
||||
/// - parameter encoding: String encoding of Sapling extended full viewing key
|
||||
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
|
||||
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
|
||||
/// found to be invalid
|
||||
public init(encoding: String, network: NetworkType) throws {
|
||||
guard let valid = try? DerivationTool(networkType: network).isValidExtendedViewingKey(encoding), valid else {
|
||||
throw KeyEncodingError.invalidEncoding
|
||||
}
|
||||
self.encoding = encoding
|
||||
}
|
||||
}
|
||||
|
||||
/// A Transparent Address that can be encoded as a String
|
||||
///
|
||||
/// Transactions sent to this address are totally visible in the public
|
||||
/// ledger. See "Multiple transaction types" in https://z.cash/technology/
|
||||
public struct TransparentAddress: Equatable, StringEncoded {
|
||||
var encoding: String
|
||||
|
||||
public var stringEncoded: String { encoding }
|
||||
|
||||
/// Initializes a new TransparentAddress (t-address) from the provided string encoding
|
||||
///
|
||||
/// - parameter encoding: String encoding of the t-address
|
||||
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
|
||||
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
|
||||
/// found to be invalid
|
||||
public init(encoding: String, network: NetworkType) throws {
|
||||
guard let valid = try? DerivationTool(networkType: network).isValidTransparentAddress(encoding), valid else {
|
||||
throw KeyEncodingError.invalidEncoding
|
||||
}
|
||||
|
||||
self.encoding = encoding
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a Sapling receiver address. Comonly called zAddress.
|
||||
/// This address corresponds to the Zcash Sapling shielded pool.
|
||||
/// Although this it is fully functional, we encourage developers to
|
||||
/// choose `UnifiedAddress` before Sapling or Transparent ones.
|
||||
public struct SaplingAddress: Equatable, StringEncoded {
|
||||
var encoding: String
|
||||
|
||||
public var stringEncoded: String { encoding }
|
||||
|
||||
/// Initializes a new Sapling shielded address (z-address) from the provided string encoding
|
||||
///
|
||||
/// - parameter encoding: String encoding of the z-address
|
||||
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
|
||||
///
|
||||
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
|
||||
/// found to be invalid
|
||||
public init(encoding: String, network: NetworkType) throws {
|
||||
guard let valid = try? DerivationTool(networkType: network).isValidSaplingAddress(encoding), valid else {
|
||||
throw KeyEncodingError.invalidEncoding
|
||||
}
|
||||
|
||||
self.encoding = encoding
|
||||
}
|
||||
}
|
||||
|
||||
public struct UnifiedAddress: Equatable, StringEncoded {
|
||||
var encoding: String
|
||||
|
||||
public var stringEncoded: String { encoding }
|
||||
|
||||
/// Initializes a new Unified Address (UA) from the provided string encoding
|
||||
/// - Parameters:
|
||||
/// - parameter encoding: String encoding of the UA
|
||||
/// - parameter network: `NetworkType` corresponding to the encoding (Mainnet or Testnet)
|
||||
/// - Throws: `KeyEncodingError.invalidEncoding`when the provided encoding is
|
||||
/// found to be invalid
|
||||
public init(encoding: String, network: NetworkType) throws {
|
||||
guard let valid = try? DerivationTool(networkType: network).isValidUnifiedAddress(encoding), valid else {
|
||||
throw KeyEncodingError.invalidEncoding
|
||||
}
|
||||
|
||||
self.encoding = encoding
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a valid recipient of Zcash
|
||||
public enum Recipient: Equatable, StringEncoded {
|
||||
case transparent(TransparentAddress)
|
||||
case sapling(SaplingAddress)
|
||||
case unified(UnifiedAddress)
|
||||
|
||||
public var stringEncoded: String {
|
||||
switch self {
|
||||
case .transparent(let tAddr):
|
||||
return tAddr.stringEncoded
|
||||
case .sapling(let zAddr):
|
||||
return zAddr.stringEncoded
|
||||
case .unified(let uAddr):
|
||||
return uAddr.stringEncoded
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes a `Recipient` with string encoded Zcash address
|
||||
/// - Parameter string: a string encoded Zcash address
|
||||
/// - Parameter network: the `ZcashNetwork.NetworkType` of the recipient
|
||||
/// - Throws: `KeyEncodingError.invalidEncoding` if the received string-encoded address
|
||||
/// can't be initialized as a valid Zcash Address.
|
||||
public init(_ string: String, network: NetworkType) throws {
|
||||
if let unified = try? UnifiedAddress(encoding: string, network: network) {
|
||||
self = .unified(unified)
|
||||
} else if let sapling = try? SaplingAddress(encoding: string, network: network) {
|
||||
self = .sapling(sapling)
|
||||
} else if let transparent = try? TransparentAddress(encoding: string, network: network) {
|
||||
self = .transparent(transparent)
|
||||
} else {
|
||||
throw KeyEncodingError.invalidEncoding
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public struct WalletBalance: Equatable {
|
||||
public var verified: Zatoshi
|
||||
public var total: Zatoshi
|
||||
|
|
|
@ -10,6 +10,9 @@ import Foundation
|
|||
import libzcashlc
|
||||
|
||||
class ZcashRustBackend: ZcashRustBackendWelding {
|
||||
|
||||
static let minimumConfirmations: UInt32 = 10
|
||||
|
||||
static func lastError() -> RustWeldingError? {
|
||||
guard let message = getLastError() else { return nil }
|
||||
|
||||
|
@ -55,7 +58,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
}
|
||||
}
|
||||
|
||||
static func isValidShieldedAddress(_ address: String, networkType: NetworkType) throws -> Bool {
|
||||
static func isValidSaplingAddress(_ address: String, networkType: NetworkType) throws -> Bool {
|
||||
guard !address.containsCStringNullBytesBeforeStringEnding() else {
|
||||
return false
|
||||
}
|
||||
|
@ -85,7 +88,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
return true
|
||||
}
|
||||
|
||||
static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
|
||||
static func isValidSaplingExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
|
||||
guard !key.containsCStringNullBytesBeforeStringEnding() else {
|
||||
return false
|
||||
}
|
||||
|
@ -100,6 +103,35 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
return true
|
||||
}
|
||||
|
||||
static func isValidSaplingExtendedSpendingKey(_ key: String, networkType: NetworkType) throws -> Bool {
|
||||
guard !key.containsCStringNullBytesBeforeStringEnding() else {
|
||||
return false
|
||||
}
|
||||
|
||||
guard zcashlc_is_valid_sapling_extended_spending_key(key, networkType.networkId) else {
|
||||
if let error = lastError() {
|
||||
throw error
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
static func isValidUnifiedAddress(_ address: String, networkType: NetworkType) throws -> Bool {
|
||||
guard !address.containsCStringNullBytesBeforeStringEnding() else {
|
||||
return false
|
||||
}
|
||||
guard zcashlc_is_valid_unified_address([CChar](address.utf8CString), networkType.networkId) else {
|
||||
if let error = lastError() {
|
||||
throw error
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
static func isValidUnifiedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
|
||||
guard !key.containsCStringNullBytesBeforeStringEnding() else {
|
||||
return false
|
||||
|
@ -115,27 +147,39 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
return true
|
||||
}
|
||||
|
||||
static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [String]? {
|
||||
static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [SaplingExtendedSpendingKey]? {
|
||||
let dbData = dbData.osStr()
|
||||
var capacity = UInt(0)
|
||||
let extsksCStr = zcashlc_init_accounts_table(dbData.0, dbData.1, seed, UInt(seed.count), accounts, &capacity, networkType.networkId)
|
||||
if extsksCStr == nil {
|
||||
|
||||
guard let ffiEncodingKeysPtr = zcashlc_init_accounts_table(
|
||||
dbData.0,
|
||||
dbData.1,
|
||||
seed,
|
||||
UInt(seed.count),
|
||||
accounts,
|
||||
networkType.networkId
|
||||
) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap({ cStr -> String? in
|
||||
guard let str = cStr else { return nil }
|
||||
return String(cString: str)
|
||||
let extsks = UnsafeBufferPointer(
|
||||
start: ffiEncodingKeysPtr.pointee.ptr,
|
||||
count: Int(ffiEncodingKeysPtr.pointee.len)
|
||||
).compactMap({ encodedKey -> SaplingExtendedSpendingKey in
|
||||
SaplingExtendedSpendingKey(validatedEncoding: String(cString: encodedKey.encoding))
|
||||
})
|
||||
zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity)
|
||||
zcashlc_free_keys(ffiEncodingKeysPtr)
|
||||
|
||||
return extsks
|
||||
}
|
||||
|
||||
static func initAccountsTable(dbData: URL, ufvks: [UnifiedFullViewingKey], networkType: NetworkType) throws -> Bool {
|
||||
static func initAccountsTable(
|
||||
dbData: URL,
|
||||
ufvks: [UnifiedFullViewingKey],
|
||||
networkType: NetworkType
|
||||
) throws -> Bool {
|
||||
let dbData = dbData.osStr()
|
||||
|
||||
var ffiUfvks: [FFIUnifiedViewingKey] = []
|
||||
var ffiUfvks: [FFIEncodedKey] = []
|
||||
for ufvk in ufvks {
|
||||
guard !ufvk.encoding.containsCStringNullBytesBeforeStringEnding() else {
|
||||
throw RustWeldingError.malformedStringInput
|
||||
|
@ -150,14 +194,22 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
let ufvkPtr = UnsafeMutablePointer<CChar>.allocate(capacity: ufvkCStr.count)
|
||||
ufvkPtr.initialize(from: ufvkCStr, count: ufvkCStr.count)
|
||||
|
||||
ffiUfvks.append(FFIUnifiedViewingKey(account_id: ufvk.account, encoding: ufvkPtr))
|
||||
ffiUfvks.append(
|
||||
FFIEncodedKey(account_id: ufvk.account, encoding: ufvkPtr)
|
||||
)
|
||||
}
|
||||
|
||||
var result = false
|
||||
|
||||
ffiUfvks.withContiguousMutableStorageIfAvailable { pointer in
|
||||
let slice = UnsafeMutablePointer<FFIUVKBoxedSlice>.allocate(capacity: 1)
|
||||
slice.initialize(to: FFIUVKBoxedSlice(ptr: pointer.baseAddress, len: UInt(pointer.count)))
|
||||
let slice = UnsafeMutablePointer<FFIEncodedKeys>.allocate(capacity: 1)
|
||||
|
||||
slice.initialize(
|
||||
to: FFIEncodedKeys(
|
||||
ptr: pointer.baseAddress,
|
||||
len: UInt(pointer.count)
|
||||
)
|
||||
)
|
||||
|
||||
result = zcashlc_init_accounts_table_with_keys(dbData.0, dbData.1, slice, networkType.networkId)
|
||||
slice.deinitialize(count: 1)
|
||||
|
@ -224,7 +276,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
|
||||
static func getVerifiedBalance(dbData: URL, account: Int32, networkType: NetworkType) -> Int64 {
|
||||
let dbData = dbData.osStr()
|
||||
return zcashlc_get_verified_balance(dbData.0, dbData.1, account, networkType.networkId)
|
||||
return zcashlc_get_verified_balance(dbData.0, dbData.1, account, networkType.networkId, minimumConfirmations)
|
||||
}
|
||||
|
||||
static func getVerifiedTransparentBalance(dbData: URL, address: String, networkType: NetworkType) throws -> Int64 {
|
||||
|
@ -238,7 +290,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
|
||||
let dbData = dbData.osStr()
|
||||
|
||||
return zcashlc_get_verified_transparent_balance(dbData.0, dbData.1, [CChar](address.utf8CString), networkType.networkId)
|
||||
return zcashlc_get_verified_transparent_balance(dbData.0, dbData.1, [CChar](address.utf8CString), networkType.networkId, minimumConfirmations)
|
||||
}
|
||||
|
||||
static func getTransparentBalance(dbData: URL, address: String, networkType: NetworkType) throws -> Int64 {
|
||||
|
@ -388,7 +440,8 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
|
||||
outputParamsPath,
|
||||
UInt(outputParamsPath.lengthOfBytes(using: .utf8)),
|
||||
networkType.networkId
|
||||
networkType.networkId,
|
||||
minimumConfirmations
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -397,7 +450,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
dbData: URL,
|
||||
account: Int32,
|
||||
xprv: String,
|
||||
extsk: String,
|
||||
memo: String?,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
|
@ -411,7 +463,6 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
dbData.1,
|
||||
account,
|
||||
[CChar](xprv.utf8CString),
|
||||
[CChar](extsk.utf8CString),
|
||||
[CChar](memoBytes.utf8CString),
|
||||
spendParamsPath,
|
||||
UInt(spendParamsPath.lengthOfBytes(using: .utf8)),
|
||||
|
@ -421,14 +472,14 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
)
|
||||
}
|
||||
|
||||
static func deriveExtendedFullViewingKey(_ spendingKey: String, networkType: NetworkType) throws -> String? {
|
||||
guard !spendingKey.containsCStringNullBytesBeforeStringEnding() else {
|
||||
static func deriveSaplingExtendedFullViewingKey(_ spendingKey: SaplingExtendedSpendingKey, networkType: NetworkType) throws -> SaplingExtendedFullViewingKey? {
|
||||
guard !spendingKey.stringEncoded.containsCStringNullBytesBeforeStringEnding() else {
|
||||
throw RustWeldingError.malformedStringInput
|
||||
}
|
||||
|
||||
guard
|
||||
let extsk = zcashlc_derive_extended_full_viewing_key(
|
||||
[CChar](spendingKey.utf8CString),
|
||||
let extfvk = zcashlc_derive_extended_full_viewing_key(
|
||||
[CChar](spendingKey.stringEncoded.utf8CString),
|
||||
networkType.networkId
|
||||
)
|
||||
else {
|
||||
|
@ -438,20 +489,22 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
return nil
|
||||
}
|
||||
|
||||
let derived = String(validatingUTF8: extsk)
|
||||
defer { zcashlc_string_free(extfvk) }
|
||||
|
||||
zcashlc_string_free(extsk)
|
||||
return derived
|
||||
guard let derived = String(validatingUTF8: extfvk) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return SaplingExtendedFullViewingKey(validatedEncoding: derived)
|
||||
}
|
||||
|
||||
static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? {
|
||||
var capacity = UInt(0)
|
||||
static func deriveSaplingExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedFullViewingKey]? {
|
||||
|
||||
guard
|
||||
let extsksCStr = zcashlc_derive_extended_full_viewing_keys(
|
||||
let ffiEncodedKeysPtr = zcashlc_derive_extended_full_viewing_keys(
|
||||
seed,
|
||||
UInt(seed.count),
|
||||
accounts,
|
||||
&capacity,
|
||||
networkType.networkId
|
||||
)
|
||||
else {
|
||||
|
@ -461,23 +514,26 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
return nil
|
||||
}
|
||||
|
||||
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { cStr -> String? in
|
||||
guard let str = cStr else { return nil }
|
||||
return String(cString: str)
|
||||
defer {
|
||||
zcashlc_free_keys(ffiEncodedKeysPtr)
|
||||
}
|
||||
|
||||
zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity)
|
||||
return extsks
|
||||
let extfvks = UnsafeBufferPointer(
|
||||
start: ffiEncodedKeysPtr.pointee.ptr,
|
||||
count: Int(ffiEncodedKeysPtr.pointee.len)
|
||||
).compactMap { encodedKey -> SaplingExtendedFullViewingKey in
|
||||
SaplingExtendedFullViewingKey(validatedEncoding: String(cString: encodedKey.encoding))
|
||||
}
|
||||
|
||||
return extfvks
|
||||
}
|
||||
|
||||
static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? {
|
||||
var capacity = UInt(0)
|
||||
static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]? {
|
||||
guard
|
||||
let extsksCStr = zcashlc_derive_extended_spending_keys(
|
||||
let ffiEncodedKeysPtr = zcashlc_derive_extended_spending_keys(
|
||||
seed,
|
||||
UInt(seed.count),
|
||||
accounts,
|
||||
&capacity,
|
||||
networkType.networkId
|
||||
)
|
||||
else {
|
||||
|
@ -487,12 +543,12 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
return nil
|
||||
}
|
||||
|
||||
let extsks = UnsafeBufferPointer(start: extsksCStr, count: Int(accounts)).compactMap { cStr -> String? in
|
||||
guard let str = cStr else { return nil }
|
||||
return String(cString: str)
|
||||
defer { zcashlc_free_keys(ffiEncodedKeysPtr) }
|
||||
|
||||
let extsks = UnsafeBufferPointer(start: ffiEncodedKeysPtr.pointee.ptr, count: Int(ffiEncodedKeysPtr.pointee.len)).compactMap {
|
||||
SaplingExtendedSpendingKey(validatedEncoding: String(cString: $0.encoding))
|
||||
}
|
||||
|
||||
zcashlc_vec_string_free(extsksCStr, UInt(accounts), capacity)
|
||||
|
||||
return extsks
|
||||
}
|
||||
|
@ -503,7 +559,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
networkType: NetworkType
|
||||
) throws -> [UnifiedFullViewingKey] {
|
||||
guard
|
||||
let ufvksStruct = zcashlc_derive_unified_full_viewing_keys_from_seed(
|
||||
let ffiEncodedKeysPtr = zcashlc_derive_unified_full_viewing_keys_from_seed(
|
||||
seed,
|
||||
UInt(seed.count),
|
||||
Int32(numberOfAccounts),
|
||||
|
@ -516,9 +572,11 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
throw RustWeldingError.unableToDeriveKeys
|
||||
}
|
||||
|
||||
let ufvksSize = ufvksStruct.pointee.len
|
||||
defer { zcashlc_free_keys(ffiEncodedKeysPtr) }
|
||||
|
||||
guard let ufvksArrayPointer = ufvksStruct.pointee.ptr, ufvksSize > 0 else {
|
||||
let ufvksSize = ffiEncodedKeysPtr.pointee.len
|
||||
|
||||
guard let ufvksArrayPointer = ffiEncodedKeysPtr.pointee.ptr, ufvksSize > 0 else {
|
||||
throw RustWeldingError.unableToDeriveKeys
|
||||
}
|
||||
|
||||
|
@ -531,10 +589,9 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
throw RustWeldingError.unableToDeriveKeys
|
||||
}
|
||||
|
||||
ufvks.append(UFVK(account: UInt32(item), encoding: encoding))
|
||||
ufvks.append(UnifiedFullViewingKey(validatedEncoding: encoding, account: UInt32(item)))
|
||||
}
|
||||
|
||||
zcashlc_free_uvk_array(ufvksStruct)
|
||||
|
||||
return ufvks
|
||||
}
|
||||
|
@ -698,7 +755,7 @@ class ZcashRustBackend: ZcashRustBackendWelding {
|
|||
}
|
||||
}
|
||||
|
||||
private struct UFVK: UnifiedFullViewingKey {
|
||||
private struct UFVK {
|
||||
var account: UInt32
|
||||
var encoding: String
|
||||
}
|
||||
|
@ -728,3 +785,11 @@ extension String {
|
|||
self.utf8CString.firstIndex(of: 0) != (self.utf8CString.count - 1)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fileprivate extension UnifiedFullViewingKey {
|
||||
init(ufvk: UFVK) {
|
||||
self.account = ufvk.account
|
||||
self.encoding = ufvk.encoding
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public enum RustWeldingError: Error {
|
||||
enum RustWeldingError: Error {
|
||||
case genericError(message: String)
|
||||
case dataDbInitFailed(message: String)
|
||||
case dataDbNotEmpty
|
||||
|
@ -18,7 +18,7 @@ public enum RustWeldingError: Error {
|
|||
case unableToDeriveKeys
|
||||
}
|
||||
|
||||
public enum ZcashRustBackendWeldingConstants {
|
||||
enum ZcashRustBackendWeldingConstants {
|
||||
static let validChain: Int32 = -1
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ public enum DbInitResult {
|
|||
case seedRequired
|
||||
}
|
||||
|
||||
public protocol ZcashRustBackendWelding {
|
||||
protocol ZcashRustBackendWelding {
|
||||
/**
|
||||
gets the latest error if available. Clear the existing error
|
||||
*/
|
||||
|
@ -49,10 +49,10 @@ public protocol ZcashRustBackendWelding {
|
|||
static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: NetworkType) throws -> DbInitResult
|
||||
|
||||
/**
|
||||
- Returns: true when the address is valid and shielded. Returns false in any other case
|
||||
- Returns: true when the address is valid. Returns false in any other case
|
||||
- Throws: Error when the provided address belongs to another network
|
||||
*/
|
||||
static func isValidShieldedAddress(_ address: String, networkType: NetworkType) throws -> Bool
|
||||
static func isValidSaplingAddress(_ address: String, networkType: NetworkType) throws -> Bool
|
||||
|
||||
/**
|
||||
- Returns: true when the address is valid and transparent. false in any other case
|
||||
|
@ -60,14 +60,25 @@ public protocol ZcashRustBackendWelding {
|
|||
*/
|
||||
static func isValidTransparentAddress(_ address: String, networkType: NetworkType) throws -> Bool
|
||||
|
||||
/**
|
||||
- Returns: true when the address is valid and transparent. false in any other case
|
||||
- Throws: Error when there's another problem not related to validity of the string in question
|
||||
*/
|
||||
static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool
|
||||
/// 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 a UFVK. false in any other case
|
||||
- Returns: `true` when the Sapling Extended Full Viewing Key is valid. `false` in any other case
|
||||
- Throws: Error when there's another problem not related to validity of the string in question
|
||||
*/
|
||||
static func 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 encoded string is a valid UFVK. false in any other case
|
||||
- Throws: Error when there's another problem not related to validity of the string in question
|
||||
*/
|
||||
static func isValidUnifiedFullViewingKey(_ ufvk: String, networkType: NetworkType) throws -> Bool
|
||||
|
@ -79,7 +90,7 @@ public protocol ZcashRustBackendWelding {
|
|||
- seed: byte array of the zip32 seed
|
||||
- accounts: how many accounts you want to have
|
||||
*/
|
||||
static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [String]?
|
||||
static func initAccountsTable(dbData: URL, seed: [UInt8], accounts: Int32, networkType: NetworkType) -> [SaplingExtendedSpendingKey]?
|
||||
|
||||
/**
|
||||
initialize the accounts table from a set of unified full viewing keys
|
||||
|
@ -302,8 +313,7 @@ public protocol ZcashRustBackendWelding {
|
|||
- dbCache: URL for the Cache DB
|
||||
- dbData: URL for the Data DB
|
||||
- account: the account index that will originate the transaction
|
||||
- xprv: transparent account private key for the shielded funds.
|
||||
- extsk: extended spending key string
|
||||
- xprv: transparent account private key for the transparent funds that will be shielded.
|
||||
- memo: the memo string for this transaction
|
||||
- spendParamsPath: path escaped String for the filesystem locations where the spend parameters are located
|
||||
- outputParamsPath: path escaped String for the filesystem locations where the output parameters are located
|
||||
|
@ -314,7 +324,6 @@ public protocol ZcashRustBackendWelding {
|
|||
dbData: URL,
|
||||
account: Int32,
|
||||
xprv: String,
|
||||
extsk: String,
|
||||
memo: String?,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
|
@ -327,7 +336,7 @@ public protocol ZcashRustBackendWelding {
|
|||
- Returns: the derived key
|
||||
- Throws: RustBackendError if fatal error occurs
|
||||
*/
|
||||
static func deriveExtendedFullViewingKey(_ spendingKey: String, networkType: NetworkType) throws -> String?
|
||||
static func deriveSaplingExtendedFullViewingKey(_ spendingKey: SaplingExtendedSpendingKey, networkType: NetworkType) throws -> SaplingExtendedFullViewingKey?
|
||||
|
||||
/**
|
||||
Derives a set of full viewing keys from a seed
|
||||
|
@ -336,22 +345,22 @@ public protocol ZcashRustBackendWelding {
|
|||
- Returns: an array containing the derived keys
|
||||
- Throws: RustBackendError if fatal error occurs
|
||||
*/
|
||||
static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]?
|
||||
static func deriveSaplingExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedFullViewingKey]?
|
||||
|
||||
/**
|
||||
Derives a set of full viewing keys from a seed
|
||||
Derives a set of Extended Spending Keys from a seed
|
||||
- Parameter seed: a string containing the seed
|
||||
- Parameter accounts: the number of accounts you want to derive from this seed
|
||||
- Returns: an array containing the spending keys
|
||||
- Throws: RustBackendError if fatal error occurs
|
||||
*/
|
||||
static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]?
|
||||
static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]?
|
||||
|
||||
/**
|
||||
Derives a unified address from a seed
|
||||
- Parameter seed: an array of bytes of the seed
|
||||
- Parameter accountIndex: the index of the account you want the address for
|
||||
- Returns: an optional String containing the Shielded address
|
||||
- Returns: an optional String containing Unified Address
|
||||
- Throws: RustBackendError if fatal error occurs
|
||||
*/
|
||||
static func deriveUnifiedAddressFromSeed(seed: [UInt8], accountIndex: Int32, networkType: NetworkType) throws -> String?
|
||||
|
@ -364,12 +373,13 @@ public protocol ZcashRustBackendWelding {
|
|||
*/
|
||||
static func deriveUnifiedAddressFromViewingKey(_ ufvk: String, networkType: NetworkType) throws -> String?
|
||||
|
||||
/**
|
||||
Derives a shielded address from an Extended Full Viewing Key
|
||||
- Parameter seed: an array of bytes of the seed
|
||||
- Returns: an optional String containing the transparent address
|
||||
- Throws: RustBackendError if fatal error occurs
|
||||
*/
|
||||
|
||||
/// Derives a transparent address from seed bytes
|
||||
/// - Parameter seed: an array of bytes of the seed
|
||||
/// - Parameter account: account number
|
||||
/// - Parameter index: diversifier index
|
||||
/// - Returns: an optional String containing the transparent address
|
||||
/// - Throws: RustBackendError if fatal error occurs
|
||||
static func deriveTransparentAddressFromSeed(seed: [UInt8], account: Int, index: Int, networkType: NetworkType) throws -> String?
|
||||
|
||||
/**
|
||||
|
|
|
@ -95,7 +95,7 @@ public protocol Synchronizer {
|
|||
/// Gets the sapling shielded address for the given account.
|
||||
/// - Parameter accountIndex: the optional accountId whose address is of interest. By default, the first account is used.
|
||||
/// - Returns the address or nil if account index is incorrect
|
||||
func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress?
|
||||
func getSaplingAddress(accountIndex: Int) -> SaplingAddress?
|
||||
|
||||
|
||||
/// Gets the unified address for the given account.
|
||||
|
@ -134,25 +134,23 @@ public protocol Synchronizer {
|
|||
/// - Parameter accountIndex: the optional account id to use. By default, the first account is used.
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func sendToAddress(
|
||||
spendingKey: String,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
toAddress: Recipient,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void
|
||||
)
|
||||
|
||||
/// Sends zatoshi.
|
||||
/// - Parameter spendingKey: the key that allows spends to occur.
|
||||
/// Shields transparent funds from the given private key into the best shielded pool of the given account.
|
||||
/// - Parameter transparentAccountPrivateKey: the key that allows to spend transparent funds
|
||||
/// - Parameter memo: the optional memo to include as part of the transaction.
|
||||
/// - Parameter accountIndex: the optional account id that will be used to shield your funds to. By default, the first account is used.
|
||||
func shieldFunds(
|
||||
spendingKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (_ result: Result<PendingTransactionEntity, Error>) -> Void
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
)
|
||||
|
||||
/// Attempts to cancel a transaction that is about to be sent. Typically, cancellation is only
|
||||
|
@ -161,7 +159,6 @@ public protocol Synchronizer {
|
|||
/// - Returns: true when the cancellation request was successful. False when it is too late.
|
||||
func cancelSpend(transaction: PendingTransactionEntity) -> Bool
|
||||
|
||||
|
||||
/// all outbound pending transactions that have been sent but are awaiting confirmations
|
||||
var pendingTransactions: [PendingTransactionEntity] { get }
|
||||
|
||||
|
@ -174,7 +171,6 @@ public protocol Synchronizer {
|
|||
/// all transactions related to receiving funds
|
||||
var receivedTransactions: [ConfirmedTransactionEntity] { get }
|
||||
|
||||
|
||||
/// A repository serving transactions in a paginated manner
|
||||
/// - Parameter kind: Transaction Kind expected from this PaginatedTransactionRepository
|
||||
func paginatedTransactions(of kind: TransactionKind) -> PaginatedTransactionRepository
|
||||
|
@ -203,7 +199,7 @@ public protocol Synchronizer {
|
|||
/// Returns the latests UTXOs for the given address from the specified height on
|
||||
func refreshUTXOs(address: String, from height: BlockHeight, result: @escaping (Result<RefreshedUTXOs, Error>) -> Void)
|
||||
|
||||
/// Returns the last stored unshielded balance
|
||||
/// Returns the last stored transparent balance
|
||||
func getTransparentBalance(accountIndex: Int) throws -> WalletBalance
|
||||
|
||||
|
||||
|
|
|
@ -74,9 +74,10 @@ public extension Notification.Name {
|
|||
static let synchronizerConnectionStateChanged = Notification.Name("SynchronizerConnectionStateChanged")
|
||||
}
|
||||
|
||||
/// Synchronizer implementation for UIKit and iOS 12+
|
||||
/// Synchronizer implementation for UIKit and iOS 13+
|
||||
// swiftlint:disable type_body_length
|
||||
public class SDKSynchronizer: Synchronizer {
|
||||
|
||||
public struct SynchronizerState {
|
||||
public var shieldedBalance: WalletBalance
|
||||
public var transparentBalance: WalletBalance
|
||||
|
@ -466,21 +467,25 @@ public class SDKSynchronizer: Synchronizer {
|
|||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
) {
|
||||
sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: Zatoshi(zatoshi),
|
||||
toAddress: toAddress,
|
||||
memo: memo,
|
||||
from: accountIndex,
|
||||
resultBlock: resultBlock
|
||||
)
|
||||
do {
|
||||
sendToAddress(
|
||||
spendingKey: try SaplingExtendedSpendingKey(encoding: spendingKey, network: network.networkType),
|
||||
zatoshi: Zatoshi(zatoshi),
|
||||
toAddress: try Recipient(toAddress, network: network.networkType),
|
||||
memo: memo,
|
||||
from: accountIndex,
|
||||
resultBlock: resultBlock
|
||||
)
|
||||
} catch {
|
||||
resultBlock(.failure(SynchronizerError.invalidAccount))
|
||||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
public func sendToAddress(
|
||||
spendingKey: String,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
toAddress: Recipient,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
|
@ -492,7 +497,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
self?.createToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: zatoshi,
|
||||
toAddress: toAddress,
|
||||
toAddress: toAddress.stringEncoded,
|
||||
memo: memo,
|
||||
from: accountIndex,
|
||||
resultBlock: resultBlock
|
||||
|
@ -505,8 +510,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
public func shieldFunds(
|
||||
spendingKey: String,
|
||||
transparentAccountPrivateKey: String,
|
||||
transparentAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
resultBlock: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
|
@ -516,20 +520,20 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
do {
|
||||
let tAddr = try derivationTool.deriveTransparentAddressFromAccountPrivateKey(transparentAccountPrivateKey, index: 0) // TODO: FIX
|
||||
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 {
|
||||
resultBlock(.failure(ShieldFundsError.insuficientTransparentFunds))
|
||||
return
|
||||
}
|
||||
let viewingKey = try derivationTool.deriveViewingKey(spendingKey: spendingKey)
|
||||
let uAddr = try derivationTool.deriveUnifiedAddress(viewingKey: viewingKey)
|
||||
|
||||
let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: uAddr, memo: memo, from: 0)
|
||||
// TODO: this should be a UA
|
||||
let zAddr = "fasdfasdfa"
|
||||
|
||||
let shieldingSpend = try transactionManager.initSpend(zatoshi: tBalance.verified, toAddress: zAddr, memo: memo, from: 0)
|
||||
|
||||
transactionManager.encodeShieldingTransaction(
|
||||
spendingKey: spendingKey,
|
||||
xprv: transparentAccountPrivateKey,
|
||||
pendingTransaction: shieldingSpend
|
||||
) { [weak self] result in
|
||||
|
@ -560,7 +564,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func createToAddress(
|
||||
spendingKey: String,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
memo: String?,
|
||||
|
@ -683,8 +687,8 @@ public class SDKSynchronizer: Synchronizer {
|
|||
initializer.getVerifiedBalance(account: accountIndex)
|
||||
}
|
||||
|
||||
public func getShieldedAddress(accountIndex: Int) -> SaplingShieldedAddress? {
|
||||
blockProcessor.getShieldedAddress(accountIndex: accountIndex)
|
||||
public func getSaplingAddress(accountIndex: Int) -> SaplingAddress? {
|
||||
blockProcessor.getSaplingAddress(accountIndex: accountIndex)
|
||||
}
|
||||
|
||||
public func getUnifiedAddress(accountIndex: Int) -> UnifiedAddress? {
|
||||
|
@ -700,7 +704,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
}
|
||||
|
||||
/**
|
||||
Returns the last stored unshielded balance
|
||||
Returns the last stored transparent balance
|
||||
*/
|
||||
public func getTransparentBalance(address: String) throws -> WalletBalance {
|
||||
do {
|
||||
|
|
|
@ -12,7 +12,11 @@ public protocol KeyValidation {
|
|||
|
||||
func isValidTransparentAddress(_ tAddress: String) throws -> Bool
|
||||
|
||||
func isValidShieldedAddress(_ zAddress: String) throws -> Bool
|
||||
func isValidSaplingAddress(_ zAddress: String) throws -> Bool
|
||||
|
||||
func isValidSaplingExtendedSpendingKey(_ extsk: String) throws -> Bool
|
||||
|
||||
func isValidUnifiedAddress(_ unifiedAddress: String) throws -> Bool
|
||||
}
|
||||
|
||||
public protocol KeyDeriving {
|
||||
|
@ -25,7 +29,7 @@ public protocol KeyDeriving {
|
|||
|
||||
- Returns: the viewing keys that correspond to the seed, formatted as Strings.
|
||||
*/
|
||||
func deriveViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String]
|
||||
func deriveUnifiedFullViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey]
|
||||
|
||||
/**
|
||||
Given a spending key, return the associated viewing key.
|
||||
|
@ -34,7 +38,7 @@ public protocol KeyDeriving {
|
|||
|
||||
- Returns: the viewing key that corresponds to the spending key.
|
||||
*/
|
||||
func deriveViewingKey(spendingKey: String) throws -> String
|
||||
func deriveViewingKey(spendingKey: SaplingExtendedSpendingKey) throws -> SaplingExtendedFullViewingKey
|
||||
|
||||
/**
|
||||
Given a seed and a number of accounts, return the associated spending keys.
|
||||
|
@ -45,7 +49,7 @@ public protocol KeyDeriving {
|
|||
|
||||
- Returns: the spending keys that correspond to the seed, formatted as Strings.
|
||||
*/
|
||||
func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String]
|
||||
func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [SaplingExtendedSpendingKey]
|
||||
|
||||
/**
|
||||
Given a seed and account index, return the associated unified address.
|
||||
|
@ -56,45 +60,31 @@ public protocol KeyDeriving {
|
|||
|
||||
- Returns: the address that corresponds to the seed and account index.
|
||||
*/
|
||||
func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> String
|
||||
func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> UnifiedAddress
|
||||
|
||||
/**
|
||||
Given a unified viewing key string, return the associated unified address.
|
||||
|
||||
- Parameter viewingKey: the viewing key to use for deriving the address. The viewing key is tied to
|
||||
a specific account so no account index is required.
|
||||
|
||||
- Returns: the address that corresponds to the viewing key.
|
||||
*/
|
||||
func deriveUnifiedAddress(viewingKey: String) throws -> String
|
||||
/// Given a unified full viewing key string, return the associated unified address.
|
||||
///
|
||||
/// - Parameter ufvk: the viewing key to use for deriving the address. The viewing key is tied to
|
||||
/// a specific account so no account index is required.
|
||||
///
|
||||
/// - Returns: the address that corresponds to the viewing key.
|
||||
func deriveUnifiedAddress(from ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress
|
||||
|
||||
/**
|
||||
Derives a transparent address from seedbytes, specifying account and index
|
||||
*/
|
||||
func deriveTransparentAddress(seed: [UInt8], account: Int, index: Int) throws -> String
|
||||
func deriveTransparentAddress(seed: [UInt8], account: Int, index: Int) throws -> TransparentAddress
|
||||
|
||||
/**
|
||||
Derives the account private key to spend transparent funds from a specific seed and account
|
||||
*/
|
||||
func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int) throws -> String
|
||||
func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int) throws -> TransparentAccountPrivKey
|
||||
|
||||
/**
|
||||
Derives a transparent address from the given transparent account private key
|
||||
*/
|
||||
func deriveTransparentAddressFromAccountPrivateKey(_ xprv: String, index: Int) throws -> String
|
||||
|
||||
func deriveTransparentAddressFromPublicKey(_ pubkey: String) throws -> String
|
||||
|
||||
/**
|
||||
derives unified full viewing keys from seedbytes, specifying a number of accounts
|
||||
- Returns an array of unified viewing key tuples.
|
||||
*/
|
||||
func deriveUnifiedFullViewingKeysFromSeed(_ seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey]
|
||||
|
||||
/**
|
||||
derives a Unified Address from a Unified Full Viewing Key
|
||||
*/
|
||||
func deriveUnifiedAddressFromUnifiedFullViewingKey(_ ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress
|
||||
func deriveTransparentAddressFromAccountPrivateKey(_ xprv: TransparentAccountPrivKey, index: Int) throws -> TransparentAddress
|
||||
}
|
||||
|
||||
public enum KeyDerivationErrors: Error {
|
||||
|
@ -104,6 +94,7 @@ public enum KeyDerivationErrors: Error {
|
|||
}
|
||||
|
||||
public class DerivationTool: KeyDeriving {
|
||||
|
||||
var rustwelding: ZcashRustBackendWelding.Type = ZcashRustBackend.self
|
||||
|
||||
var networkType: NetworkType
|
||||
|
@ -121,7 +112,7 @@ public class DerivationTool: KeyDeriving {
|
|||
|
||||
- Returns: the viewing keys that correspond to the seed, formatted as Strings.
|
||||
*/
|
||||
public func deriveViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String] {
|
||||
public func deriveUnifiedFullViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] {
|
||||
guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else {
|
||||
throw KeyDerivationErrors.invalidInput
|
||||
}
|
||||
|
@ -129,9 +120,9 @@ public class DerivationTool: KeyDeriving {
|
|||
do {
|
||||
let ufvks = try rustwelding.deriveUnifiedFullViewingKeyFromSeed(seed, numberOfAccounts: numberOfAccounts, networkType: networkType)
|
||||
|
||||
var keys: [String] = []
|
||||
var keys: [UnifiedFullViewingKey] = []
|
||||
for ufvk in ufvks {
|
||||
keys.append(ufvk.encoding)
|
||||
keys.append(ufvk)
|
||||
}
|
||||
return keys
|
||||
} catch {
|
||||
|
@ -146,9 +137,9 @@ public class DerivationTool: KeyDeriving {
|
|||
|
||||
- Returns: the viewing key that corresponds to the spending key.
|
||||
*/
|
||||
public func deriveViewingKey(spendingKey: String) throws -> String {
|
||||
public func deriveViewingKey(spendingKey: SaplingExtendedSpendingKey) throws -> SaplingExtendedFullViewingKey {
|
||||
do {
|
||||
guard let key = try rustwelding.deriveExtendedFullViewingKey(spendingKey, networkType: networkType) else {
|
||||
guard let key = try rustwelding.deriveSaplingExtendedFullViewingKey(spendingKey, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return key
|
||||
|
@ -157,88 +148,6 @@ public class DerivationTool: KeyDeriving {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Given a seed and a number of accounts, return the associated spending keys.
|
||||
|
||||
- Parameter seed: the seed from which to derive spending keys.
|
||||
- Parameter numberOfAccounts: the number of accounts to use. Multiple accounts are not fully
|
||||
supported so the default value of 1 is recommended.
|
||||
|
||||
- Returns: the spending keys that correspond to the seed, formatted as Strings.
|
||||
*/
|
||||
public func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [String] {
|
||||
guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else {
|
||||
throw KeyDerivationErrors.invalidInput
|
||||
}
|
||||
do {
|
||||
guard let keys = try rustwelding.deriveExtendedSpendingKeys(seed: seed, accounts: numberOfAccounts, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return keys
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Given a seed and account index, return the associated unified address.
|
||||
|
||||
- Parameter seed: the seed from which to derive the address.
|
||||
- Parameter accountIndex: the index of the account to use for deriving the address. Multiple
|
||||
accounts are not fully supported so the default value of 1 is recommended.
|
||||
|
||||
- Returns: the address that corresponds to the seed and account index.
|
||||
*/
|
||||
public func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> String {
|
||||
guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else {
|
||||
throw KeyDerivationErrors.invalidInput
|
||||
}
|
||||
|
||||
do {
|
||||
guard let address = try rustwelding.deriveUnifiedAddressFromSeed(seed: seed, accountIndex: accountIndex, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return address
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Given a unified viewing key string, return the associated unified address.
|
||||
|
||||
- Parameter viewingKey: the viewing key to use for deriving the address. The viewing key is tied to
|
||||
a specific account so no account index is required.
|
||||
|
||||
- Returns: the address that corresponds to the viewing key.
|
||||
*/
|
||||
public func deriveUnifiedAddress(viewingKey: String) throws -> String {
|
||||
do {
|
||||
guard let zaddr = try rustwelding.deriveUnifiedAddressFromViewingKey(viewingKey, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return zaddr
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
public func deriveTransparentAddress(seed: [UInt8], account: Int = 0, index: Int = 0) throws -> String {
|
||||
do {
|
||||
guard let zaddr = try rustwelding.deriveTransparentAddressFromSeed(
|
||||
seed: seed,
|
||||
account: account,
|
||||
index: index,
|
||||
networkType: networkType
|
||||
) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return zaddr
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
public func deriveUnifiedFullViewingKeysFromSeed(_ seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] {
|
||||
guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else {
|
||||
throw KeyDerivationErrors.invalidInput
|
||||
|
@ -251,36 +160,102 @@ public class DerivationTool: KeyDeriving {
|
|||
}
|
||||
|
||||
/**
|
||||
derives a Unified Address from a Unified Full Viewing Key
|
||||
*/
|
||||
public func deriveUnifiedAddressFromUnifiedFullViewingKey(_ ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress {
|
||||
do {
|
||||
let encoding = try deriveUnifiedAddress(viewingKey: ufvk.encoding)
|
||||
return ConcreteUnifiedAddress(encoding: encoding)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
}
|
||||
Given a seed and a number of accounts, return the associated spending keys.
|
||||
|
||||
public func deriveTransparentAddressFromPublicKey(_ pubkey: String) throws -> String {
|
||||
guard !pubkey.isEmpty else {
|
||||
- Parameter seed: the seed from which to derive spending keys.
|
||||
- Parameter numberOfAccounts: the number of accounts to use. Multiple accounts are not fully
|
||||
supported so the default value of 1 is recommended.
|
||||
|
||||
- Returns: the spending keys that correspond to the seed, formatted as Strings.
|
||||
*/
|
||||
public func deriveSpendingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [SaplingExtendedSpendingKey] {
|
||||
guard numberOfAccounts > 0, let numberOfAccounts = Int32(exactly: numberOfAccounts) else {
|
||||
throw KeyDerivationErrors.invalidInput
|
||||
}
|
||||
|
||||
do {
|
||||
return try rustwelding.derivedTransparentAddressFromPublicKey(pubkey, networkType: networkType)
|
||||
guard let keys = try rustwelding.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: numberOfAccounts, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return keys
|
||||
} 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
|
||||
Given a seed and account index, return the associated unified address.
|
||||
|
||||
- Parameter seed: the seed from which to derive the address.
|
||||
- Parameter accountIndex: the index of the account to use for deriving the address. Multiple
|
||||
accounts are not fully supported so the default value of 1 is recommended.
|
||||
|
||||
- Returns: the address that corresponds to the seed and account index.
|
||||
*/
|
||||
public func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int = 0) throws -> String {
|
||||
public func deriveUnifiedAddress(seed: [UInt8], accountIndex: Int) throws -> UnifiedAddress {
|
||||
guard accountIndex >= 0, let accountIndex = Int32(exactly: accountIndex) else {
|
||||
throw KeyDerivationErrors.invalidInput
|
||||
}
|
||||
|
||||
do {
|
||||
guard let address = try rustwelding.deriveUnifiedAddressFromSeed(seed: seed, accountIndex: accountIndex, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return UnifiedAddress(validatedEncoding: address)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Given a unified viewing key string, return the associated unified address.
|
||||
|
||||
- Parameter viewingKey: the viewing key to use for deriving the address. The viewing key is tied to
|
||||
a specific account so no account index is required.
|
||||
|
||||
- Returns: the address that corresponds to the viewing key.
|
||||
*/
|
||||
public func deriveUnifiedAddress(from ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress {
|
||||
do {
|
||||
guard let stringEncodedUA = try rustwelding.deriveUnifiedAddressFromViewingKey(ufvk.stringEncoded, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return UnifiedAddress(validatedEncoding: stringEncodedUA)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
public func deriveTransparentAddress(seed: [UInt8], account: Int = 0, index: Int = 0) throws -> TransparentAddress {
|
||||
do {
|
||||
guard let taddr = try rustwelding.deriveTransparentAddressFromSeed(
|
||||
seed: seed,
|
||||
account: account,
|
||||
index: index,
|
||||
networkType: networkType
|
||||
) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return TransparentAddress(validatedEncoding: taddr)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// derives a Unified Address from a Unified Full Viewing Key
|
||||
public func deriveUnifiedAddressFromUnifiedFullViewingKey(_ ufvk: UnifiedFullViewingKey) throws -> UnifiedAddress {
|
||||
do {
|
||||
return try deriveUnifiedAddress(from: ufvk)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
}
|
||||
|
||||
/// Derives the transparent funds account private key from the given seed
|
||||
/// - Throws:
|
||||
/// - KeyDerivationErrors.derivationError with the underlying error when it fails
|
||||
/// - KeyDerivationErrors.unableToDerive when there's an unknown error
|
||||
public func deriveTransparentAccountPrivateKey(seed: [UInt8], account: Int = 0) throws -> TransparentAccountPrivKey {
|
||||
do {
|
||||
guard let seedKey = try rustwelding.deriveTransparentAccountPrivateKeyFromSeed(
|
||||
seed: seed,
|
||||
|
@ -289,7 +264,23 @@ public class DerivationTool: KeyDeriving {
|
|||
) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return seedKey
|
||||
return TransparentAccountPrivKey(encoding: seedKey)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Derives the transparent address from an account private key
|
||||
/// - Throws:
|
||||
/// - KeyDerivationErrors.derivationError with the underlying error when it fails
|
||||
/// - KeyDerivationErrors.unableToDerive when there's an unknown error
|
||||
public func deriveTransparentAddressFromAccountPrivateKey(_ xprv: TransparentAccountPrivKey, index: Int) throws -> TransparentAddress {
|
||||
do {
|
||||
guard let tAddr = try rustwelding.deriveTransparentAddressFromAccountPrivateKey(xprv.encoding, index: index, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return TransparentAddress(validatedEncoding: tAddr)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
|
@ -297,9 +288,17 @@ public class DerivationTool: KeyDeriving {
|
|||
}
|
||||
|
||||
extension DerivationTool: KeyValidation {
|
||||
public func isValidUnifiedAddress(_ unifiedAddress: String) throws -> Bool {
|
||||
do {
|
||||
return try rustwelding.isValidUnifiedAddress(unifiedAddress, networkType: networkType)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
public func isValidExtendedViewingKey(_ extvk: String) throws -> Bool {
|
||||
do {
|
||||
return try rustwelding.isValidExtendedFullViewingKey(extvk, networkType: networkType)
|
||||
return try rustwelding.isValidSaplingExtendedFullViewingKey(extvk, networkType: networkType)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
|
@ -313,32 +312,83 @@ extension DerivationTool: KeyValidation {
|
|||
}
|
||||
}
|
||||
|
||||
public func isValidShieldedAddress(_ zAddress: String) throws -> Bool {
|
||||
public func isValidSaplingAddress(_ zAddress: String) throws -> Bool {
|
||||
do {
|
||||
return try rustwelding.isValidShieldedAddress(zAddress, networkType: networkType)
|
||||
return try rustwelding.isValidSaplingAddress(zAddress, networkType: networkType)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Derives the transparent address from an account private key
|
||||
- Throws:
|
||||
- KeyDerivationErrors.derivationError with the underlying error when it fails
|
||||
- KeyDerivationErrors.unableToDerive when there's an unknown error
|
||||
*/
|
||||
public func deriveTransparentAddressFromAccountPrivateKey(_ xprv: String, index: Int) throws -> String {
|
||||
public func isValidSaplingExtendedSpendingKey(_ extsk: String) throws -> Bool {
|
||||
do {
|
||||
guard let tAddr = try rustwelding.deriveTransparentAddressFromAccountPrivateKey(xprv, index: index, networkType: networkType) else {
|
||||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
return tAddr
|
||||
return try rustwelding.isValidSaplingExtendedSpendingKey(extsk, networkType: networkType)
|
||||
} catch {
|
||||
throw KeyDerivationErrors.derivationError(underlyingError: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ConcreteUnifiedAddress: UnifiedAddress {
|
||||
var encoding: String
|
||||
|
||||
extension TransparentAddress {
|
||||
/// This constructor is for internal use for Strings encodings that are assumed to be
|
||||
/// already validated by another function. only for internal use. Unless you are
|
||||
/// constructing an address from a primitive function of the FFI, you probably
|
||||
/// shouldn't be using this.
|
||||
init(validatedEncoding: String) {
|
||||
self.encoding = validatedEncoding
|
||||
}
|
||||
}
|
||||
|
||||
extension SaplingAddress {
|
||||
/// This constructor is for internal use for Strings encodings that are assumed to be
|
||||
/// already validated by another function. only for internal use. Unless you are
|
||||
/// constructing an address from a primitive function of the FFI, you probably
|
||||
/// shouldn't be using this.
|
||||
init(validatedEncoding: String) {
|
||||
self.encoding = validatedEncoding
|
||||
}
|
||||
}
|
||||
|
||||
extension UnifiedAddress {
|
||||
/// This constructor is for internal use for Strings encodings that are assumed to be
|
||||
/// already validated by another function. only for internal use. Unless you are
|
||||
/// constructing an address from a primitive function of the FFI, you probably
|
||||
/// shouldn't be using this..
|
||||
init(validatedEncoding: String) {
|
||||
self.encoding = validatedEncoding
|
||||
}
|
||||
}
|
||||
|
||||
extension UnifiedFullViewingKey {
|
||||
/// This constructor is for internal use for Strings encodings that are assumed to be
|
||||
/// already validated by another function. only for internal use. Unless you are
|
||||
/// constructing an address from a primitive function of the FFI, you probably
|
||||
/// shouldn't be using this.
|
||||
init(validatedEncoding: String, account: UInt32) {
|
||||
self.encoding = validatedEncoding
|
||||
self.account = account
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension SaplingExtendedFullViewingKey {
|
||||
/// This constructor is for internal use for Strings encodings that are assumed to be
|
||||
/// already validated by another function. only for internal use. Unless you are
|
||||
/// constructing an address from a primitive function of the FFI, you probably
|
||||
/// shouldn't be using this.
|
||||
init(validatedEncoding: String) {
|
||||
self.encoding = validatedEncoding
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension SaplingExtendedSpendingKey {
|
||||
/// This constructor is for internal use for Strings encodings that are assumed to be
|
||||
/// already validated by another function. only for internal use. Unless you are
|
||||
/// constructing an address from a primitive function of the FFI, you probably
|
||||
/// shouldn't be using this.
|
||||
init(validatedEncoding: String) {
|
||||
self.encoding = validatedEncoding
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,52 +65,15 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
}
|
||||
|
||||
func encodeShieldingTransaction(
|
||||
spendingKey: String,
|
||||
xprv: String,
|
||||
xprv: TransparentAccountPrivKey,
|
||||
pendingTransaction: PendingTransactionEntity,
|
||||
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
) {
|
||||
queue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
let derivationTool = DerivationTool(networkType: self.network)
|
||||
|
||||
guard
|
||||
let viewingKey = try? derivationTool.deriveViewingKey(spendingKey: spendingKey),
|
||||
let uAddr = try? derivationTool.deriveUnifiedAddress(viewingKey: viewingKey)
|
||||
else {
|
||||
result(
|
||||
.failure(
|
||||
TransactionManagerError.shieldingEncodingFailed(
|
||||
pendingTransaction,
|
||||
reason: "There was an error Deriving your keys"
|
||||
)
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
guard pendingTransaction.toAddress == uAddr else {
|
||||
result(
|
||||
.failure(
|
||||
TransactionManagerError.shieldingEncodingFailed(
|
||||
pendingTransaction,
|
||||
reason: """
|
||||
the recipient address does not match your
|
||||
derived shielded address. Shielding transactions
|
||||
addresses must match the ones derived from your keys.
|
||||
This is a serious error. We are not letting you encode
|
||||
this shielding transaction because it can lead to loss
|
||||
of funds
|
||||
"""
|
||||
)
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
do {
|
||||
let encodedTransaction = try self.encoder.createShieldingTransaction(
|
||||
spendingKey: spendingKey,
|
||||
tAccountPrivateKey: xprv,
|
||||
memo: pendingTransaction.memo?.asZcashTransactionMemo(),
|
||||
from: pendingTransaction.accountIndex
|
||||
|
@ -140,7 +103,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
}
|
||||
|
||||
func encode(
|
||||
spendingKey: String,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
pendingTransaction: PendingTransactionEntity,
|
||||
result: @escaping (Result<PendingTransactionEntity, Error>) -> Void
|
||||
) {
|
||||
|
@ -149,7 +112,7 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
do {
|
||||
let encodedTransaction = try self.encoder.createTransaction(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: pendingTransaction.intValue,
|
||||
zatoshi: pendingTransaction.value,
|
||||
to: pendingTransaction.toAddress,
|
||||
memo: pendingTransaction.memo?.asZcashTransactionMemo(),
|
||||
from: pendingTransaction.accountIndex
|
||||
|
@ -266,10 +229,6 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
|||
.forEach { try self.repository.update($0) }
|
||||
}
|
||||
|
||||
func monitorChanges(byId: Int, observer: Any) {
|
||||
// TODO: Implement this
|
||||
}
|
||||
|
||||
func cancel(pendingTransaction: PendingTransactionEntity) -> Bool {
|
||||
guard let id = pendingTransaction.id else { return false }
|
||||
|
||||
|
|
|
@ -18,47 +18,45 @@ public enum TransactionEncoderError: Error {
|
|||
}
|
||||
|
||||
protocol TransactionEncoder {
|
||||
/**
|
||||
Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
|
||||
doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
|
||||
double-bangs for things).
|
||||
Blocking
|
||||
|
||||
- Parameters:
|
||||
- Parameter spendingKey: a string containing the spending key
|
||||
- Parameter zatoshi: the amount to send in zatoshis
|
||||
- Parameter to: string containing the recipient address
|
||||
- Parameter memo: string containing the memo (optional)
|
||||
- Parameter accountIndex: index of the account that will be used to send the funds
|
||||
|
||||
- Throws: a TransactionEncoderError
|
||||
*/
|
||||
/// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
|
||||
/// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
|
||||
/// double-bangs for things).
|
||||
/// Blocking
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key
|
||||
/// - Parameter zatoshi: the amount to send in `Zatoshi`
|
||||
/// - Parameter to: string containing the recipient address
|
||||
/// - Parameter memo: string containing the memo (optional)
|
||||
/// - Parameter accountIndex: index of the account that will be used to send the funds
|
||||
///
|
||||
/// - Throws: a TransactionEncoderError
|
||||
func createTransaction(
|
||||
spendingKey: String,
|
||||
zatoshi: Int,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int
|
||||
) throws -> EncodedTransaction
|
||||
|
||||
/**
|
||||
Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
|
||||
doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
|
||||
double-bangs for things).
|
||||
Non-blocking
|
||||
|
||||
- Parameters:
|
||||
- Parameter spendingKey: a string containing the spending key
|
||||
- Parameter zatoshi: the amount to send in zatoshis
|
||||
- Parameter to: string containing the recipient address
|
||||
- Parameter memo: string containing the memo (optional)
|
||||
- Parameter accountIndex: index of the account that will be used to send the funds
|
||||
- Parameter result: a non escaping closure that receives a Result containing either an EncodedTransaction or a TransactionEncoderError
|
||||
*/
|
||||
/// Creates a transaction, throwing an exception whenever things are missing. When the provided wallet implementation
|
||||
/// doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using
|
||||
/// double-bangs for things).
|
||||
/// Non-blocking
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - Parameter spendingKey: a `SaplingExtendedSpendingKey` containing the spending key
|
||||
/// - Parameter zatoshi: the amount to send in `Zatoshi`
|
||||
/// - Parameter to: string containing the recipient address
|
||||
/// - Parameter memo: string containing the memo (optional)
|
||||
/// - Parameter accountIndex: index of the account that will be used to send the funds
|
||||
/// - Parameter result: a non escaping closure that receives a Result containing either an EncodedTransaction or a /// TransactionEncoderError
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func createTransaction(
|
||||
spendingKey: String,
|
||||
zatoshi: Int,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
|
@ -70,7 +68,6 @@ protocol TransactionEncoder {
|
|||
Blocking
|
||||
|
||||
- Parameters:
|
||||
- Parameter spendingKey: a string containing the spending key
|
||||
- Parameter tAccountPrivateKey: transparent account private key to spend the UTXOs
|
||||
- Parameter memo: string containing the memo (optional)
|
||||
- Parameter accountIndex: index of the account that will be used to send the funds
|
||||
|
@ -78,34 +75,11 @@ protocol TransactionEncoder {
|
|||
- Throws: a TransactionEncoderError
|
||||
*/
|
||||
func createShieldingTransaction(
|
||||
spendingKey: String,
|
||||
tAccountPrivateKey: String,
|
||||
tAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
from accountIndex: Int
|
||||
) throws -> EncodedTransaction
|
||||
|
||||
/**
|
||||
Creates a transaction that will attempt to shield transparent funds that are present on the cacheDB .throwing an exception whenever things are missing. When the provided wallet implementation doesn't throw an exception, we wrap the issue into a descriptive exception ourselves (rather than using double-bangs for things).
|
||||
|
||||
Non-Blocking
|
||||
|
||||
- Parameters:
|
||||
- Parameter spendingKey: a string containing the spending key
|
||||
- Parameter tAccountPrivateKey: transparent account private key to spend the UTXOs
|
||||
- Parameter memo: string containing the memo (optional)
|
||||
- Parameter accountIndex: index of the account that will be used to send the funds
|
||||
|
||||
- Returns: a TransactionEncoderResultBlock
|
||||
*/
|
||||
|
||||
func createShieldingTransaction(
|
||||
spendingKey: String,
|
||||
tAccountPrivateKey: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
result: @escaping TransactionEncoderResultBlock
|
||||
)
|
||||
|
||||
/**
|
||||
Fetch the Transaction Entity from the encoded representation
|
||||
- Parameter encodedTransaction: The encoded transaction to expand
|
||||
|
|
|
@ -14,17 +14,34 @@ transactions through to completion.
|
|||
*/
|
||||
|
||||
protocol OutboundTransactionManager {
|
||||
func initSpend(zatoshi: Zatoshi, toAddress: String, memo: String?, from accountIndex: Int) throws -> PendingTransactionEntity
|
||||
func initSpend(
|
||||
zatoshi: Zatoshi,
|
||||
toAddress: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int
|
||||
) throws -> PendingTransactionEntity
|
||||
|
||||
func encodeShieldingTransaction(spendingKey: String, xprv: String, pendingTransaction: PendingTransactionEntity, result: @escaping (Result<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 monitorChanges(byId: Int, observer: Any) // check this out. code smell
|
||||
func applyMinedHeight(
|
||||
pendingTransaction: PendingTransactionEntity,
|
||||
minedHeight: BlockHeight
|
||||
) throws -> PendingTransactionEntity
|
||||
|
||||
/**
|
||||
Attempts to Cancel a transaction. Returns true if successful
|
||||
|
|
|
@ -50,8 +50,8 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
}
|
||||
|
||||
func createTransaction(
|
||||
spendingKey: String,
|
||||
zatoshi: Int,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int
|
||||
|
@ -80,8 +80,8 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
func createTransaction(
|
||||
spendingKey: String,
|
||||
zatoshi: Int,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
|
@ -107,7 +107,13 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
func createSpend(spendingKey: String, zatoshi: Int, to address: String, memo: String?, from accountIndex: Int) throws -> Int {
|
||||
func createSpend(
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
zatoshi: Zatoshi,
|
||||
to address: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int
|
||||
) throws -> Int {
|
||||
guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else {
|
||||
throw TransactionEncoderError.missingParams
|
||||
}
|
||||
|
@ -115,9 +121,9 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
let txId = rustBackend.createToAddress(
|
||||
dbData: self.dataDbURL,
|
||||
account: Int32(accountIndex),
|
||||
extsk: spendingKey,
|
||||
extsk: spendingKey.stringEncoded,
|
||||
to: address,
|
||||
value: Int64(zatoshi),
|
||||
value: zatoshi.amount,
|
||||
memo: memo,
|
||||
spendParamsPath: self.spendParamsURL.path,
|
||||
outputParamsPath: self.outputParamsURL.path,
|
||||
|
@ -132,14 +138,12 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
}
|
||||
|
||||
func createShieldingTransaction(
|
||||
spendingKey: String,
|
||||
tAccountPrivateKey: String,
|
||||
tAccountPrivateKey: TransparentAccountPrivKey,
|
||||
memo: String?,
|
||||
from accountIndex: Int
|
||||
) throws -> EncodedTransaction {
|
||||
let txId = try createShieldingSpend(
|
||||
spendingKey: spendingKey,
|
||||
xprv: tAccountPrivateKey,
|
||||
xprv: tAccountPrivateKey.encoding,
|
||||
memo: memo,
|
||||
accountIndex: accountIndex
|
||||
)
|
||||
|
@ -158,19 +162,7 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
}
|
||||
}
|
||||
|
||||
func createShieldingTransaction(
|
||||
spendingKey: String,
|
||||
tAccountPrivateKey: String,
|
||||
memo: String?,
|
||||
from accountIndex: Int,
|
||||
result: @escaping TransactionEncoderResultBlock
|
||||
) {
|
||||
queue.async {
|
||||
result(.failure(RustWeldingError.genericError(message: "not implemented")))
|
||||
}
|
||||
}
|
||||
|
||||
func createShieldingSpend(spendingKey: String, xprv: String, memo: String?, accountIndex: Int) throws -> Int {
|
||||
func createShieldingSpend(xprv: String, memo: String?, accountIndex: Int) throws -> Int {
|
||||
guard ensureParams(spend: self.spendParamsURL, output: self.spendParamsURL) else {
|
||||
throw TransactionEncoderError.missingParams
|
||||
}
|
||||
|
@ -180,7 +172,6 @@ class WalletTransactionEncoder: TransactionEncoder {
|
|||
dbData: self.dataDbURL,
|
||||
account: Int32(accountIndex),
|
||||
xprv: xprv,
|
||||
extsk: spendingKey,
|
||||
memo: memo,
|
||||
spendParamsPath: self.spendParamsURL.path,
|
||||
outputParamsPath: self.outputParamsURL.path,
|
||||
|
|
|
@ -302,7 +302,7 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: coordinator.spendingKeys!.first!,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test transaction",
|
||||
from: 0
|
||||
) { result in
|
||||
|
@ -709,7 +709,7 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: Zatoshi(20000),
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -1069,7 +1069,7 @@ class AdvancedReOrgTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: Zatoshi(20000),
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
|
|
@ -82,7 +82,7 @@ class BalanceTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalance,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -226,7 +226,7 @@ class BalanceTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalanceMinusOne,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -367,7 +367,7 @@ class BalanceTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalanceMinusOne,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -510,7 +510,7 @@ class BalanceTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -681,7 +681,7 @@ class BalanceTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -836,7 +836,7 @@ class BalanceTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKeys,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: memo,
|
||||
from: 0,
|
||||
resultBlock: { sendResult in
|
||||
|
@ -1008,7 +1008,7 @@ class BalanceTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description)",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
|
|
@ -82,7 +82,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -171,7 +171,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -240,7 +240,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -341,7 +341,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
@ -453,7 +453,7 @@ class NetworkUpgradeTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: spendAmount,
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
|
|
@ -97,7 +97,7 @@ class PendingTransactionUpdatesTest: XCTestCase {
|
|||
// swiftlint:disable:next force_unwrapping
|
||||
spendingKey: self.coordinator.spendingKeys!.first!,
|
||||
zatoshi: Zatoshi(20000),
|
||||
toAddress: self.testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "this is a test",
|
||||
from: 0,
|
||||
resultBlock: { result in
|
||||
|
|
|
@ -169,7 +169,7 @@ class RewindRescanTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: coordinator.spendingKey,
|
||||
zatoshi: Zatoshi(1000),
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: nil,
|
||||
from: 0
|
||||
) { result in
|
||||
|
@ -270,7 +270,7 @@ class RewindRescanTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: spendingKey,
|
||||
zatoshi: maxBalance,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test send \(self.description) \(Date().description)",
|
||||
from: 0
|
||||
) { result in
|
||||
|
|
|
@ -84,7 +84,7 @@ class ShieldFundsTests: XCTestCase {
|
|||
///
|
||||
func testShieldFunds() throws {
|
||||
// 1. load the dataset
|
||||
try coordinator.service.useDataset(from: "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/shielding-dataset/shield-funds/1631000.txt")
|
||||
try coordinator.service.useDataset(from: "https://github.com/zcash-hackworks/darksidewalletd-test-data/blob/master/shield-funds/1631000.txt")
|
||||
|
||||
try coordinator.stageBlockCreate(height: birthday + 1, count: 200, nonce: 0)
|
||||
|
||||
|
@ -209,7 +209,6 @@ class ShieldFundsTests: XCTestCase {
|
|||
|
||||
// shield the funds
|
||||
coordinator.synchronizer.shieldFunds(
|
||||
spendingKey: coordinator.spendingKey,
|
||||
transparentAccountPrivateKey: transparentAccountPrivateKey,
|
||||
memo: "shield funds",
|
||||
from: 0
|
||||
|
|
|
@ -104,7 +104,7 @@ class Z2TReceiveTests: XCTestCase {
|
|||
coordinator.synchronizer.sendToAddress(
|
||||
spendingKey: coordinator.spendingKeys!.first!,
|
||||
zatoshi: sendAmount,
|
||||
toAddress: testRecipientAddress,
|
||||
toAddress: try! Recipient(testRecipientAddress, network: self.network.networkType),
|
||||
memo: "test transaction",
|
||||
from: 0
|
||||
) { result in
|
||||
|
|
|
@ -19,9 +19,9 @@ class BlockScanOperationTests: XCTestCase {
|
|||
var cacheDbURL: URL!
|
||||
var dataDbURL: URL!
|
||||
|
||||
var ufvk = UFVFakeKey(
|
||||
account: 0,
|
||||
encoding: "uviewtest1q48t999peecrfkq7ykcxckfkjt77w3lckk5mptlrtuy7xltjnzg8fm5434cxe9p9838ljs24yv83rluhk33ew098dkarapzyj4vk5kfxp5zn2jp3ww74jwd48r05aqjvgqxzx3nqn6zfqh3cmwdtmz0mc5624tvdza55q7mguxrehwcy4y0uktcpp4tkpex4qhazddux4yt6hr0sc9fkqmfr5tyz6ldd7yrq93tyj7446u4kst3vhmd40uga636p56hr0hjfdhgp07qyh90kmsl3qnmld6c8h7u06vekkjywmxv07mqzz9muwcl6weczrn5vf3p27uc9ufrumdp64zdzulzvc373wx3gl0yntntujhcsjhrwk9xwyjpvyuf0s8q3mgjs7uy3pg960w40dthpngcnauhgg9xq8cdcyfkq7ctnngqg4nkp5eh9knd4ckwjyd9czdd240lumul96r2fuerlvjeha6cyn9ftm7gr6xqjmq0zy6tv" // swiftlint:disable:this line_length
|
||||
var ufvk = UnifiedFullViewingKey(
|
||||
validatedEncoding: "uviewtest1q48t999peecrfkq7ykcxckfkjt77w3lckk5mptlrtuy7xltjnzg8fm5434cxe9p9838ljs24yv83rluhk33ew098dkarapzyj4vk5kfxp5zn2jp3ww74jwd48r05aqjvgqxzx3nqn6zfqh3cmwdtmz0mc5624tvdza55q7mguxrehwcy4y0uktcpp4tkpex4qhazddux4yt6hr0sc9fkqmfr5tyz6ldd7yrq93tyj7446u4kst3vhmd40uga636p56hr0hjfdhgp07qyh90kmsl3qnmld6c8h7u06vekkjywmxv07mqzz9muwcl6weczrn5vf3p27uc9ufrumdp64zdzulzvc373wx3gl0yntntujhcsjhrwk9xwyjpvyuf0s8q3mgjs7uy3pg960w40dthpngcnauhgg9xq8cdcyfkq7ctnngqg4nkp5eh9knd4ckwjyd9czdd240lumul96r2fuerlvjeha6cyn9ftm7gr6xqjmq0zy6tv", // swiftlint:disable:this line_length
|
||||
account: 0
|
||||
)
|
||||
|
||||
var walletBirthDay = Checkpoint.birthday(
|
||||
|
@ -268,8 +268,3 @@ extension BlockScanOperationTests: CompactBlockProgressDelegate {
|
|||
func progressUpdated(_ progress: CompactBlockProgress) {
|
||||
}
|
||||
}
|
||||
|
||||
struct UFVFakeKey: UnifiedFullViewingKey {
|
||||
var account: UInt32
|
||||
var encoding: String
|
||||
}
|
||||
|
|
|
@ -53,50 +53,48 @@ class ZcashRustBackendTests: XCTestCase {
|
|||
func testDeriveExtendedSpendingKeys() {
|
||||
let seed = Array("testreferencealicetestreferencealice".utf8)
|
||||
|
||||
var spendingKeys: [String]?
|
||||
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
|
||||
var spendingKeys: [SaplingExtendedSpendingKey]?
|
||||
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
|
||||
|
||||
XCTAssertNotNil(spendingKeys)
|
||||
XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true)
|
||||
XCTAssertEqual(spendingKeys?.count, 1)
|
||||
}
|
||||
|
||||
func testDeriveExtendedFullViewingKeys() {
|
||||
let seed = Array("testreferencealicetestreferencealice".utf8)
|
||||
|
||||
var fullViewingKeys: [String]?
|
||||
var fullViewingKeys: [SaplingExtendedFullViewingKey]?
|
||||
XCTAssertNoThrow(
|
||||
try {
|
||||
fullViewingKeys = try ZcashRustBackend.deriveExtendedFullViewingKeys(
|
||||
fullViewingKeys = try ZcashRustBackend.deriveSaplingExtendedFullViewingKeys(
|
||||
seed: seed,
|
||||
accounts: 1,
|
||||
accounts: 2,
|
||||
networkType: networkType
|
||||
)
|
||||
}()
|
||||
)
|
||||
|
||||
XCTAssertNotNil(fullViewingKeys)
|
||||
XCTAssertFalse(fullViewingKeys?.first?.isEmpty ?? true)
|
||||
XCTAssertEqual(fullViewingKeys?.count, 2)
|
||||
}
|
||||
|
||||
func testDeriveExtendedFullViewingKey() {
|
||||
let seed = Array("testreferencealicetestreferencealice".utf8)
|
||||
var fullViewingKey: String?
|
||||
var fullViewingKey: SaplingExtendedFullViewingKey?
|
||||
|
||||
var spendingKeys: [String]?
|
||||
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
|
||||
var spendingKeys: [SaplingExtendedSpendingKey]?
|
||||
XCTAssertNoThrow(try { spendingKeys = try ZcashRustBackend.deriveSaplingExtendedSpendingKeys(seed: seed, accounts: 1, networkType: networkType) }())
|
||||
|
||||
XCTAssertNotNil(spendingKeys)
|
||||
XCTAssertFalse(spendingKeys?.first?.isEmpty ?? true)
|
||||
|
||||
guard let spendingKey = spendingKeys?.first else {
|
||||
XCTFail("no spending key generated")
|
||||
return
|
||||
}
|
||||
|
||||
XCTAssertNoThrow(try { fullViewingKey = try ZcashRustBackend.deriveExtendedFullViewingKey(spendingKey, networkType: networkType) }())
|
||||
XCTAssertNoThrow(try { fullViewingKey = try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(spendingKey, networkType: networkType) }())
|
||||
|
||||
XCTAssertNotNil(fullViewingKey)
|
||||
XCTAssertFalse(fullViewingKey?.isEmpty ?? true)
|
||||
}
|
||||
|
||||
func testInitAndScanBlocks() {
|
||||
|
@ -164,12 +162,12 @@ class ZcashRustBackendTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
func testIsValidShieldedAddressTrue() {
|
||||
func testIsValidSaplingAddressTrue() {
|
||||
var isValid: Bool?
|
||||
|
||||
XCTAssertNoThrow(
|
||||
try {
|
||||
isValid = try ZcashRustBackend.isValidShieldedAddress(
|
||||
isValid = try ZcashRustBackend.isValidSaplingAddress(
|
||||
"ztestsapling12k9m98wmpjts2m56wc60qzhgsfvlpxcwah268xk5yz4h942sd58jy3jamqyxjwums6hw7kfa4cc",
|
||||
networkType: networkType
|
||||
)
|
||||
|
@ -183,12 +181,12 @@ class ZcashRustBackendTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
func testIsValidShieldedAddressFalse() {
|
||||
func testIsValidSaplingAddressFalse() {
|
||||
var isValid: Bool?
|
||||
|
||||
XCTAssertNoThrow(
|
||||
try {
|
||||
isValid = try ZcashRustBackend.isValidShieldedAddress(
|
||||
isValid = try ZcashRustBackend.isValidSaplingAddress(
|
||||
"tmSwpioc7reeoNrYB9SKpWkurJz3yEj3ee7",
|
||||
networkType: networkType
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -6,24 +6,26 @@
|
|||
//
|
||||
//swiftlint:disable force_unwrapping
|
||||
import XCTest
|
||||
import ZcashLightClientKit
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
class DerivationToolMainnetTests: XCTestCase {
|
||||
var seedPhrase = "still champion voice habit trend flight survey between bitter process artefact blind carbon truly provide dizzy crush flush breeze blouse charge solid fish spread" //TODO: Parameterize this from environment?
|
||||
var seedData: Data = Data(base64Encoded: "9VDVOZZZOWWHpZtq1Ebridp3Qeux5C+HwiRR0g7Oi7HgnMs8Gfln83+/Q1NnvClcaSwM4ADFL1uZHxypEWlWXg==")!
|
||||
let testRecipientAddress = "u1l9f0l4348negsncgr9pxd9d3qaxagmqv3lnexcplmufpq7muffvfaue6ksevfvd7wrz7xrvn95rc5zjtn7ugkmgh5rnxswmcj30y0pw52pn0zjvy38rn2esfgve64rj5pcmazxgpyuj" //TODO: Parameterize this from environment
|
||||
let testRecipientAddress = UnifiedAddress(validatedEncoding: "u1l9f0l4348negsncgr9pxd9d3qaxagmqv3lnexcplmufpq7muffvfaue6ksevfvd7wrz7xrvn95rc5zjtn7ugkmgh5rnxswmcj30y0pw52pn0zjvy38rn2esfgve64rj5pcmazxgpyuj") //TODO: Parameterize this from environment
|
||||
|
||||
let expectedSpendingKey = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv"
|
||||
let expectedSpendingKey = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv")
|
||||
|
||||
let expectedViewingKey = "uview1jpddskrm73gpgrsx00y4dryapkhjlzrm5wfdcue77a26u3e7u28qu0xfsgzwt72rs60rjnwujr93al6sxchste78p8vvrlperlvladfwkyryakdutykdcqgqn9dfn9my6k3aka5ej78leksj6aptqs9yzcysszwzwr6zmrcqycxxlg87ten6ers6urmxthe3pvvh07ga7t4uz92a5y0jgej94a7u9q3nezjqj4zm634x2wc2d8d39nu74jew79phf9u025p82d8qshq0pnzcjcnke0g72gva28qsx0wvtad7qjwld5khgudwlxmx24av2mq4k5k9zypheeppcpnujc9rqpm"
|
||||
let expectedSaplingExtendedViewingKey = "zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy"
|
||||
let expectedViewingKey = UnifiedFullViewingKey(validatedEncoding: "uview17fme6ux853km45g9ep07djpfzeydxxgm22xpmr7arzxyutlusalgpqlx7suga4ahzywfuwz4jclm00u7g8u65qvvdt45kttnfunvschssg3h3g06txs9ja32vx3xa8dej3unnatgzjvd0vumk37t8es3ludldrtse3q6226ws7eq4q0ywz78nudwpepgdn7jmxz8yvp7k6gxkeynkam0f8aqf9qpeaej55zhkw39x7epayhndul0j4xjttdxxlnwcd09nr8svyx8j0zng0w6scx3m5unpkaqxcm3hslhlfg4caz7r8d4xy9wm7klkg79w7j0uyzec5s3yje20eg946r6rmkf532nfydu26s8q9ua7mwxw2j2ag7hfcuu652gw6uta03vlm05zju3a9rwc4h367kqzfqrcz35pdwdk2a7yqnk850un3ujxcvve45ueajgvtr6dj4ufszgqwdy0aedgmkalx2p7qed2suarwkr35dl0c8dnqp3", account: 0)
|
||||
let expectedSaplingExtendedViewingKey = SaplingExtendedFullViewingKey(validatedEncoding: "zxviews1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkysswfhjk79n8l99f2grd26dqg6dy3jcmxsaypxfsu6ara6vsk3x8l544uaksstx9zre879mdg7s9a7zurrx6pf5qg2n323js2s3zlu8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszcq7kwxy")
|
||||
|
||||
let expectedSaplingAddress = SaplingAddress(validatedEncoding: "zs1vp7kvlqr4n9gpehztr76lcn6skkss9p8keqs3nv8avkdtjrcctrvmk9a7u494kluv756jeee5k0")
|
||||
|
||||
let derivationTool = DerivationTool(networkType: NetworkType.mainnet)
|
||||
let expectedTransparentAddress = "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz"
|
||||
let expectedTransparentAddress = TransparentAddress(validatedEncoding: "t1dRJRY7GmyeykJnMH38mdQoaZtFhn1QmGz")
|
||||
func testDeriveViewingKeysFromSeed() throws {
|
||||
let accounts: Int = 1
|
||||
let seedBytes = [UInt8](seedData)
|
||||
let viewingKeys = try derivationTool.deriveViewingKeys(seed: seedBytes, numberOfAccounts: accounts)
|
||||
let viewingKeys = try derivationTool.deriveUnifiedFullViewingKeys(seed: seedBytes, numberOfAccounts: accounts)
|
||||
|
||||
XCTAssertEqual(viewingKeys.count, accounts, "the number of viewing keys have to match the number of account requested to derive")
|
||||
|
||||
|
@ -57,12 +59,12 @@ class DerivationToolMainnetTests: XCTestCase {
|
|||
func testDeriveUnifiedAddressFromSeed() throws {
|
||||
let seedBytes = [UInt8](seedData)
|
||||
|
||||
let shieldedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0)
|
||||
XCTAssertEqual(shieldedAddress, testRecipientAddress)
|
||||
let unifiedAddress = try derivationTool.deriveUnifiedAddress(seed: seedBytes, accountIndex: 0)
|
||||
XCTAssertEqual(unifiedAddress, testRecipientAddress)
|
||||
}
|
||||
|
||||
func testDeriveUnifiedAddressFromViewingKey() throws {
|
||||
XCTAssertEqual(try derivationTool.deriveUnifiedAddress(viewingKey: expectedViewingKey), testRecipientAddress)
|
||||
XCTAssertEqual(try derivationTool.deriveUnifiedAddress(from: expectedViewingKey), testRecipientAddress)
|
||||
}
|
||||
|
||||
func testDeriveTransparentAddressFromSeed() throws {
|
||||
|
@ -76,7 +78,7 @@ class DerivationToolMainnetTests: XCTestCase {
|
|||
}
|
||||
|
||||
func testDeriveTransparentAccountPrivateKeyFromSeed() throws {
|
||||
XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), "xprv9yCTU6giJ1qZ1DLC5rc7KMzwY9s8rSRXYqmoAKffAExpUVUKLhcdvN9ERdxjEW8tQq4pxerLKZE3WcNUKZCeX19rVTxpV2msTyNMNiFT3Nw")
|
||||
XCTAssertEqual(try derivationTool.deriveTransparentAccountPrivateKey(seed: [UInt8](seedData)), TransparentAccountPrivKey(encoding: "xprv9yCTU6giJ1qZ1DLC5rc7KMzwY9s8rSRXYqmoAKffAExpUVUKLhcdvN9ERdxjEW8tQq4pxerLKZE3WcNUKZCeX19rVTxpV2msTyNMNiFT3Nw"))
|
||||
}
|
||||
|
||||
func testDeriveUnifiedKeysFromSeed() throws {
|
||||
|
@ -84,7 +86,7 @@ class DerivationToolMainnetTests: XCTestCase {
|
|||
XCTAssertEqual(unifiedKeys.count, 1)
|
||||
|
||||
XCTAssertEqual(unifiedKeys[0].account, 0)
|
||||
XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey)
|
||||
XCTAssertEqual(unifiedKeys[0], expectedViewingKey)
|
||||
}
|
||||
|
||||
func testDeriveQuiteALotOfUnifiedKeysFromSeed() throws {
|
||||
|
@ -92,11 +94,25 @@ class DerivationToolMainnetTests: XCTestCase {
|
|||
XCTAssertEqual(unifiedKeys.count, 10)
|
||||
|
||||
XCTAssertEqual(unifiedKeys[0].account, 0)
|
||||
XCTAssertEqual(unifiedKeys[0].encoding, expectedViewingKey)
|
||||
XCTAssertEqual(unifiedKeys[0], expectedViewingKey)
|
||||
}
|
||||
|
||||
func testShouldFailOnInvalidChecksumAddresses() throws {
|
||||
let testAddress = "t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1"
|
||||
XCTAssertFalse(try derivationTool.isValidTransparentAddress(testAddress))
|
||||
}
|
||||
|
||||
func testSpendingKeyValidation() throws {
|
||||
XCTAssertTrue(try derivationTool.isValidSaplingExtendedSpendingKey(expectedSpendingKey.stringEncoded))
|
||||
}
|
||||
|
||||
func testSpendingKeyValidationFailsOnInvalidKey() throws {
|
||||
let wrongSpendingKey = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vvZzZzZz"
|
||||
|
||||
XCTAssertFalse(try derivationTool.isValidSaplingExtendedSpendingKey(wrongSpendingKey))
|
||||
}
|
||||
// TODO: Address encoding does not catch this test https://github.com/zcash/ZcashLightClientKit/issues/509
|
||||
// func testSpendingKeyValidationThrowsWhenWrongNetwork() throws {
|
||||
// XCTAssertThrowsError(try derivationTool.isValidExtendedSpendingKey("secret-extended-key-test1qdxykmuaqqqqpqqg3x5c02p4rhw0rtszr8ln4xl7g6wg6qzsqgn445qsu3cq4vd6lk8xce3d4jw7s8ln5yjp6fqv2g0nzue2hc0kv5t004vklvlenncscq9flwh5vf5qnv0hnync72n7gjn70u47765v3kyrxytx50g730svvmhhlazn5rj8mshh470fkrmzg4xarhrqlygg8f486307ujhndwhsw2h7ddzf89k3534aeu0ypz2tjgrzlcqtat380vhe8awm03f58cqe49swv"))
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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"))
|
||||
// }
|
||||
}
|
|
@ -17,7 +17,7 @@ class NullBytesTests: XCTestCase {
|
|||
let validZaddr = "zs1gqtfu59z20s9t20mxlxj86zpw6p69l0ev98uxrmlykf2nchj2dw8ny5e0l22kwmld2afc37gkfp"
|
||||
let zAddrWithNullBytes = "\(validZaddr)\0something else that makes the address invalid"
|
||||
|
||||
XCTAssertFalse(try ZcashRustBackend.isValidShieldedAddress(zAddrWithNullBytes, networkType: networkType))
|
||||
XCTAssertFalse(try ZcashRustBackend.isValidSaplingAddress(zAddrWithNullBytes, networkType: networkType))
|
||||
}
|
||||
|
||||
func testTaddrNullBytes() throws {
|
||||
|
@ -91,13 +91,13 @@ class NullBytesTests: XCTestCase {
|
|||
|
||||
func testderiveExtendedFullViewingKeyWithNullBytes() throws {
|
||||
// swiftlint:disable:next line_length
|
||||
let wrongSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mq\0uy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv" // this spending key corresponds to the "demo app reference seed"
|
||||
let wrongSpendingKeys = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mq\0uy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv") // this spending key corresponds to the "demo app reference seed"
|
||||
|
||||
// swiftlint:disable:next line_length
|
||||
let goodSpendingKeys = "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv"
|
||||
let goodSpendingKeys = SaplingExtendedSpendingKey(validatedEncoding: "secret-extended-key-main1qw28psv0qqqqpqr2ru0kss5equx6h0xjsuk5299xrsgdqnhe0cknkl8uqff34prwkyuegyhh5d4rdr8025nl7e0hm8r2txx3fuea5mquy3wnsr9tlajsg4wwvw0xcfk8357k4h850rgj72kt4rx3fjdz99zs9f4neda35cq8tn3848yyvlg4w38gx75cyv9jdpve77x9eq6rtl6d9qyh8det4edevlnc70tg5kse670x50764gzhy60dta0yv3wsd4fsuaz686lgszc7nc9vv")
|
||||
|
||||
XCTAssertThrowsError(
|
||||
try ZcashRustBackend.deriveExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType),
|
||||
try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(wrongSpendingKeys, networkType: networkType),
|
||||
"Should have thrown an error but didn't! this is dangerous!"
|
||||
) { error in
|
||||
guard let rustError = error as? RustWeldingError else {
|
||||
|
@ -113,7 +113,7 @@ class NullBytesTests: XCTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
XCTAssertNoThrow(try ZcashRustBackend.deriveExtendedFullViewingKey(goodSpendingKeys, networkType: networkType))
|
||||
XCTAssertNoThrow(try ZcashRustBackend.deriveSaplingExtendedFullViewingKey(goodSpendingKeys, networkType: networkType))
|
||||
}
|
||||
|
||||
func testCheckNullBytes() throws {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -77,10 +77,36 @@ extension LightWalletServiceMockResponse {
|
|||
}
|
||||
|
||||
class MockRustBackend: ZcashRustBackendWelding {
|
||||
|
||||
static func initDataDb(dbData: URL, seed: [UInt8]?, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.DbInitResult {
|
||||
.seedRequired
|
||||
}
|
||||
|
||||
static func deriveSaplingAddressFromViewingKey(_ extfvk: ZcashLightClientKit.SaplingExtendedFullViewingKey, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.SaplingAddress {
|
||||
throw RustWeldingError.unableToDeriveKeys
|
||||
}
|
||||
|
||||
static func isValidSaplingExtendedSpendingKey(_ key: String, networkType: ZcashLightClientKit.NetworkType) throws -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
static func deriveSaplingExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: ZcashLightClientKit.NetworkType) throws -> [ZcashLightClientKit.SaplingExtendedFullViewingKey]? {
|
||||
nil
|
||||
}
|
||||
|
||||
static func isValidUnifiedAddress(_ address: String, networkType: ZcashLightClientKit.NetworkType) throws -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
static func deriveSaplingExtendedFullViewingKey(_ spendingKey: SaplingExtendedSpendingKey, networkType: ZcashLightClientKit.NetworkType) throws -> ZcashLightClientKit.SaplingExtendedFullViewingKey? {
|
||||
nil
|
||||
}
|
||||
|
||||
|
||||
public func deriveViewingKeys(seed: [UInt8], numberOfAccounts: Int) throws -> [UnifiedFullViewingKey] {
|
||||
[]
|
||||
}
|
||||
|
||||
static func clearUtxos(dbData: URL, address: String, sinceHeight: BlockHeight, networkType: NetworkType) throws -> Int32 {
|
||||
-1
|
||||
}
|
||||
|
@ -140,7 +166,6 @@ class MockRustBackend: ZcashRustBackendWelding {
|
|||
dbData: URL,
|
||||
account: Int32,
|
||||
xprv: String,
|
||||
extsk: String,
|
||||
memo: String?,
|
||||
spendParamsPath: String,
|
||||
outputParamsPath: String,
|
||||
|
@ -169,7 +194,7 @@ class MockRustBackend: ZcashRustBackendWelding {
|
|||
throw KeyDerivationErrors.unableToDerive
|
||||
}
|
||||
|
||||
static func isValidExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
|
||||
static func isValidSaplingExtendedFullViewingKey(_ key: String, networkType: NetworkType) throws -> Bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -189,11 +214,7 @@ class MockRustBackend: ZcashRustBackendWelding {
|
|||
nil
|
||||
}
|
||||
|
||||
static func deriveExtendedFullViewingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? {
|
||||
nil
|
||||
}
|
||||
|
||||
static func deriveExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [String]? {
|
||||
static func deriveSaplingExtendedSpendingKeys(seed: [UInt8], accounts: Int32, networkType: NetworkType) throws -> [SaplingExtendedSpendingKey]? {
|
||||
nil
|
||||
}
|
||||
|
||||
|
@ -217,7 +238,7 @@ class MockRustBackend: ZcashRustBackendWelding {
|
|||
static var mockAcounts = false
|
||||
static var mockError: RustWeldingError?
|
||||
static var mockLastError: String?
|
||||
static var mockAccounts: [String]?
|
||||
static var mockAccounts: [SaplingExtendedSpendingKey]?
|
||||
static var mockAddresses: [String]?
|
||||
static var mockBalance: Int64?
|
||||
static var mockVerifiedBalance: Int64?
|
||||
|
@ -240,7 +261,7 @@ class MockRustBackend: ZcashRustBackendWelding {
|
|||
mockLastError ?? rustBackend.getLastError()
|
||||
}
|
||||
|
||||
static func isValidShieldedAddress(_ address: String, networkType: NetworkType) throws -> Bool {
|
||||
static func isValidSaplingAddress(_ address: String, networkType: NetworkType) throws -> Bool {
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -37,12 +37,12 @@ class TestCoordinator {
|
|||
|
||||
var completionHandler: ((SDKSynchronizer) -> Void)?
|
||||
var errorHandler: ((Error?) -> Void)?
|
||||
var spendingKey: String
|
||||
var spendingKey: SaplingExtendedSpendingKey
|
||||
var birthday: BlockHeight
|
||||
var channelProvider: ChannelProvider
|
||||
var synchronizer: SDKSynchronizer
|
||||
var service: DarksideWalletService
|
||||
var spendingKeys: [String]?
|
||||
var spendingKeys: [SaplingExtendedSpendingKey]?
|
||||
var databases: TemporaryTestDatabases
|
||||
let network: ZcashNetwork
|
||||
convenience init(
|
||||
|
@ -85,7 +85,7 @@ class TestCoordinator {
|
|||
}
|
||||
|
||||
required init(
|
||||
spendingKey: String,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
unifiedFullViewingKey: UnifiedFullViewingKey,
|
||||
walletBirthday: BlockHeight,
|
||||
channelProvider: ChannelProvider,
|
||||
|
@ -288,13 +288,13 @@ enum TestSynchronizerBuilder {
|
|||
storage: CompactBlockStorage,
|
||||
spendParamsURL: URL,
|
||||
outputParamsURL: URL,
|
||||
spendingKey: String,
|
||||
spendingKey: SaplingExtendedSpendingKey,
|
||||
unifiedFullViewingKey: UnifiedFullViewingKey,
|
||||
walletBirthday: BlockHeight,
|
||||
network: ZcashNetwork,
|
||||
seed: [UInt8]? = nil,
|
||||
loggerProxy: Logger? = nil
|
||||
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
|
||||
) throws -> (spendingKeys: [SaplingExtendedSpendingKey]?, synchronizer: SDKSynchronizer) {
|
||||
let initializer = Initializer(
|
||||
cacheDbURL: cacheDbURL,
|
||||
dataDbURL: dataDbURL,
|
||||
|
@ -334,7 +334,7 @@ enum TestSynchronizerBuilder {
|
|||
walletBirthday: BlockHeight,
|
||||
network: ZcashNetwork,
|
||||
loggerProxy: Logger? = nil
|
||||
) throws -> (spendingKeys: [String]?, synchronizer: SDKSynchronizer) {
|
||||
) throws -> (spendingKeys: [SaplingExtendedSpendingKey]?, synchronizer: SDKSynchronizer) {
|
||||
guard
|
||||
let spendingKey = try DerivationTool(networkType: network.networkType)
|
||||
.deriveSpendingKeys(seed: seedBytes, numberOfAccounts: 1)
|
||||
|
|
Loading…
Reference in New Issue