2021-04-08 10:18:16 -07:00
|
|
|
//
|
|
|
|
// WalletTypes.swift
|
2022-09-30 06:45:51 -07:00
|
|
|
//
|
2021-04-08 10:18:16 -07:00
|
|
|
//
|
|
|
|
// Created by Francisco Gindre on 4/6/21.
|
|
|
|
//
|
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
enum KeyEncodingError: Error {
|
|
|
|
case invalidEncoding
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Something that can be encoded as a String
|
|
|
|
public protocol StringEncoded {
|
|
|
|
var stringEncoded: String { get }
|
|
|
|
}
|
|
|
|
|
2022-10-02 19:11:17 -07:00
|
|
|
public struct UnifiedSpendingKey: Equatable, Undescribable {
|
|
|
|
private(set) var network: NetworkType
|
|
|
|
var bytes: [UInt8]
|
|
|
|
public private(set) var account: UInt32
|
|
|
|
}
|
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
/// 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 {
|
2022-10-03 17:12:10 -07:00
|
|
|
guard DerivationTool(networkType: network).isValidSaplingExtendedSpendingKey(encoding) else {
|
2022-08-20 15:10:22 -07:00
|
|
|
throw KeyEncodingError.invalidEncoding
|
|
|
|
}
|
|
|
|
self.encoding = encoding
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A Transparent Account Private Key
|
|
|
|
public struct TransparentAccountPrivKey: Equatable, Undescribable {
|
|
|
|
var encoding: String
|
|
|
|
}
|
|
|
|
|
2023-01-18 08:09:04 -08:00
|
|
|
/// A ZIP 316 Unified Full Viewing Key.
|
2022-08-20 15:10:22 -07:00
|
|
|
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 {
|
2022-10-03 17:12:10 -07:00
|
|
|
guard DerivationTool.rustwelding.isValidUnifiedFullViewingKey(encoding, networkType: network) else {
|
2022-08-20 15:10:22 -07:00
|
|
|
throw KeyEncodingError.invalidEncoding
|
|
|
|
}
|
|
|
|
|
|
|
|
self.encoding = encoding
|
|
|
|
self.account = account
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public struct SaplingExtendedFullViewingKey: Equatable, StringEncoded, Undescribable {
|
|
|
|
var encoding: String
|
|
|
|
public var stringEncoded: String {
|
|
|
|
encoding
|
|
|
|
}
|
|
|
|
|
|
|
|
/// 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 {
|
2022-10-03 17:12:10 -07:00
|
|
|
guard DerivationTool.rustwelding.isValidSaplingExtendedFullViewingKey(encoding, networkType: network) else {
|
2022-08-20 15:10:22 -07:00
|
|
|
throw KeyEncodingError.invalidEncoding
|
|
|
|
}
|
|
|
|
self.encoding = encoding
|
|
|
|
}
|
2021-04-08 10:18:16 -07:00
|
|
|
}
|
|
|
|
|
2022-09-30 06:45:51 -07:00
|
|
|
public enum AddressType: Equatable {
|
|
|
|
case p2pkh
|
|
|
|
case p2sh
|
|
|
|
case sapling
|
|
|
|
case unified
|
|
|
|
|
|
|
|
var id: UInt32 {
|
|
|
|
switch self {
|
|
|
|
case .p2pkh: return 0
|
|
|
|
case .p2sh: return 1
|
|
|
|
case .sapling: return 2
|
|
|
|
case .unified: return 3
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extension AddressType {
|
|
|
|
static func forId(_ id: UInt32) -> AddressType? {
|
|
|
|
switch id {
|
|
|
|
case 0: return .p2pkh
|
|
|
|
case 1: return .p2sh
|
|
|
|
case 2: return .sapling
|
|
|
|
case 3: return .unified
|
|
|
|
default: return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
/// 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/
|
2022-10-07 07:36:21 -07:00
|
|
|
public struct TransparentAddress: Equatable, StringEncoded, Comparable {
|
2022-08-20 15:10:22 -07:00
|
|
|
var encoding: String
|
2021-04-08 10:18:16 -07:00
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
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 {
|
2022-10-03 17:12:10 -07:00
|
|
|
guard DerivationTool(networkType: network).isValidTransparentAddress(encoding) else {
|
2022-08-20 15:10:22 -07:00
|
|
|
throw KeyEncodingError.invalidEncoding
|
|
|
|
}
|
|
|
|
|
|
|
|
self.encoding = encoding
|
|
|
|
}
|
2022-10-07 07:36:21 -07:00
|
|
|
|
|
|
|
public static func < (lhs: TransparentAddress, rhs: TransparentAddress) -> Bool {
|
|
|
|
return lhs.encoding < rhs.encoding
|
|
|
|
}
|
2022-08-20 15:10:22 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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
|
2022-09-30 06:45:51 -07:00
|
|
|
/// choose `UnifiedAddress` before Sapling or Transparent ones.
|
2022-08-20 15:10:22 -07:00
|
|
|
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 {
|
2022-10-03 17:12:10 -07:00
|
|
|
guard DerivationTool(networkType: network).isValidSaplingAddress(encoding) else {
|
2022-08-20 15:10:22 -07:00
|
|
|
throw KeyEncodingError.invalidEncoding
|
|
|
|
}
|
|
|
|
|
|
|
|
self.encoding = encoding
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public struct UnifiedAddress: Equatable, StringEncoded {
|
2022-09-14 17:40:46 -07:00
|
|
|
public enum Errors: Error {
|
|
|
|
case couldNotExtractTypecodes
|
|
|
|
}
|
|
|
|
|
|
|
|
public enum ReceiverTypecodes: Hashable {
|
|
|
|
case p2pkh
|
|
|
|
case p2sh
|
|
|
|
case sapling
|
|
|
|
case orchard
|
|
|
|
case unknown(UInt32)
|
|
|
|
|
|
|
|
init(typecode: UInt32) {
|
|
|
|
switch typecode {
|
|
|
|
case 0x00:
|
|
|
|
self = .p2pkh
|
|
|
|
case 0x01:
|
|
|
|
self = .p2sh
|
|
|
|
case 0x02:
|
|
|
|
self = .sapling
|
|
|
|
case 0x03:
|
|
|
|
self = .orchard
|
|
|
|
default:
|
|
|
|
self = .unknown(typecode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
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 {
|
2022-10-03 17:12:10 -07:00
|
|
|
guard DerivationTool(networkType: network).isValidUnifiedAddress(encoding) else {
|
2022-08-20 15:10:22 -07:00
|
|
|
throw KeyEncodingError.invalidEncoding
|
|
|
|
}
|
|
|
|
|
|
|
|
self.encoding = encoding
|
|
|
|
}
|
2022-09-14 17:40:46 -07:00
|
|
|
|
|
|
|
/// returns an array of `UnifiedAddress.ReceiverTypecodes` ordered by precedence
|
|
|
|
/// - Throws `UnifiedAddress.Errors.couldNotExtractTypecodes` when the typecodes
|
|
|
|
/// couldn't be extracted
|
|
|
|
public func availableReceiverTypecodes() throws -> [UnifiedAddress.ReceiverTypecodes] {
|
|
|
|
do {
|
2022-10-02 19:11:17 -07:00
|
|
|
return try DerivationTool.receiverTypecodesFromUnifiedAddress(self)
|
2022-09-14 17:40:46 -07:00
|
|
|
} catch {
|
|
|
|
throw Errors.couldNotExtractTypecodes
|
|
|
|
}
|
|
|
|
}
|
2021-04-08 10:18:16 -07:00
|
|
|
}
|
|
|
|
|
2022-08-20 15:10:22 -07:00
|
|
|
/// 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
|
|
|
|
}
|
|
|
|
}
|
2022-09-30 06:45:51 -07:00
|
|
|
|
|
|
|
static func forEncodedAddress(encoded: String) -> (Recipient, NetworkType)? {
|
2023-01-18 08:09:04 -08:00
|
|
|
return DerivationTool.getAddressMetadata(encoded).map { metadata in
|
|
|
|
switch metadata.addressType {
|
2022-09-30 06:45:51 -07:00
|
|
|
case .p2pkh: return (.transparent(TransparentAddress(validatedEncoding: encoded)),
|
2023-01-18 08:09:04 -08:00
|
|
|
metadata.networkType)
|
|
|
|
case .p2sh: return (.transparent(TransparentAddress(validatedEncoding: encoded)), metadata.networkType)
|
|
|
|
case .sapling: return (.sapling(SaplingAddress(validatedEncoding: encoded)), metadata.networkType)
|
|
|
|
case .unified: return (.unified(UnifiedAddress(validatedEncoding: encoded)), metadata.networkType)
|
2022-09-30 06:45:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-20 15:10:22 -07:00
|
|
|
}
|
|
|
|
|
2022-07-15 12:54:25 -07:00
|
|
|
public struct WalletBalance: Equatable {
|
2022-06-22 12:45:37 -07:00
|
|
|
public var verified: Zatoshi
|
|
|
|
public var total: Zatoshi
|
2023-01-18 08:09:04 -08:00
|
|
|
|
|
|
|
public init(verified: Zatoshi, total: Zatoshi) {
|
2022-06-27 08:51:13 -07:00
|
|
|
self.verified = verified
|
|
|
|
self.total = total
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public extension WalletBalance {
|
|
|
|
static var zero: WalletBalance {
|
|
|
|
Self(verified: .zero, total: .zero)
|
|
|
|
}
|
2021-04-08 10:18:16 -07:00
|
|
|
}
|