From 2416714e4de7917834e8b068cd99d4150c25767d Mon Sep 17 00:00:00 2001 From: Simon McLoughlin Date: Tue, 13 Aug 2024 15:59:08 +0100 Subject: [PATCH] - convert walletmetadata and list to classes so we can use pass by reference - update caching/naming/deleting logic to use references to arrays in a generic way so that we can share bits of similar logic - add some ledger specific helpers - add new tests --- .../Models/WalletMetadata.swift | 43 +++++-- .../Services/WalletCacheService.swift | 107 ++++++++++++------ .../Models/TzKTTransactionTests.swift | 2 +- .../Services/WalletCacheServiceTests.swift | 50 +++++--- 4 files changed, 146 insertions(+), 56 deletions(-) diff --git a/Sources/KukaiCoreSwift/Models/WalletMetadata.swift b/Sources/KukaiCoreSwift/Models/WalletMetadata.swift index 2f80775d..2b4a8164 100644 --- a/Sources/KukaiCoreSwift/Models/WalletMetadata.swift +++ b/Sources/KukaiCoreSwift/Models/WalletMetadata.swift @@ -8,7 +8,7 @@ import Foundation /// Container to store groups of WalletMetadata based on type -public struct WalletMetadataList: Codable, Hashable { +public class WalletMetadataList: Codable, Hashable { public var socialWallets: [WalletMetadata] public var hdWallets: [WalletMetadata] public var linearWallets: [WalletMetadata] @@ -85,7 +85,7 @@ public struct WalletMetadataList: Codable, Hashable { return nil } - public mutating func update(address: String, with newMetadata: WalletMetadata) -> Bool { + public func update(address: String, with newMetadata: WalletMetadata) -> Bool { for (index, metadata) in socialWallets.enumerated() { if metadata.address == address { socialWallets[index] = newMetadata; return true } } @@ -113,8 +113,8 @@ public struct WalletMetadataList: Codable, Hashable { return false } - public mutating func set(mainnetDomain: TezosDomainsReverseRecord?, ghostnetDomain: TezosDomainsReverseRecord?, forAddress address: String) -> Bool { - var meta = metadata(forAddress: address) + public func set(mainnetDomain: TezosDomainsReverseRecord?, ghostnetDomain: TezosDomainsReverseRecord?, forAddress address: String) -> Bool { + let meta = metadata(forAddress: address) if let mainnet = mainnetDomain { meta?.mainnetDomains = [mainnet] @@ -131,8 +131,8 @@ public struct WalletMetadataList: Codable, Hashable { return false } - public mutating func set(nickname: String?, forAddress address: String) -> Bool { - var meta = metadata(forAddress: address) + public func set(nickname: String?, forAddress address: String) -> Bool { + let meta = metadata(forAddress: address) meta?.walletNickname = nickname if let meta = meta, update(address: address, with: meta) { @@ -142,8 +142,8 @@ public struct WalletMetadataList: Codable, Hashable { return false } - public mutating func set(hdWalletGroupName: String, forAddress address: String) -> Bool { - var meta = metadata(forAddress: address) + public func set(hdWalletGroupName: String, forAddress address: String) -> Bool { + let meta = metadata(forAddress: address) meta?.hdWalletGroupName = hdWalletGroupName if let meta = meta, update(address: address, with: meta) { @@ -224,6 +224,22 @@ public struct WalletMetadataList: Codable, Hashable { return temp } + + public static func == (lhs: WalletMetadataList, rhs: WalletMetadataList) -> Bool { + return lhs.socialWallets == rhs.socialWallets && + lhs.hdWallets == rhs.hdWallets && + lhs.linearWallets == rhs.linearWallets && + lhs.ledgerWallets == rhs.ledgerWallets && + lhs.watchWallets == rhs.watchWallets + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(socialWallets) + hasher.combine(hdWallets) + hasher.combine(linearWallets) + hasher.combine(ledgerWallets) + hasher.combine(watchWallets) + } } @@ -231,7 +247,7 @@ public struct WalletMetadataList: Codable, Hashable { /// Object to store UI related info about wallets, seperated from the wallet object itself to avoid issues merging together -public struct WalletMetadata: Codable, Hashable { +public class WalletMetadata: Codable, Hashable { public var address: String public var hdWalletGroupName: String? public var walletNickname: String? @@ -246,6 +262,7 @@ public struct WalletMetadata: Codable, Hashable { public var isWatchOnly: Bool public var bas58EncodedPublicKey: String public var backedUp: Bool + public var customDerivationPath: String? public func hasMainnetDomain() -> Bool { return (mainnetDomains ?? []).count > 0 @@ -287,7 +304,12 @@ public struct WalletMetadata: Codable, Hashable { } } - public init(address: String, hdWalletGroupName: String?, walletNickname: String? = nil, socialUsername: String? = nil, socialUserId: String? = nil, mainnetDomains: [TezosDomainsReverseRecord]? = nil, ghostnetDomains: [TezosDomainsReverseRecord]? = nil, socialType: TorusAuthProvider? = nil, type: WalletType, children: [WalletMetadata], isChild: Bool, isWatchOnly: Bool, bas58EncodedPublicKey: String, backedUp: Bool) { + public func childCountExcludingCustomDerivationPaths() -> Int { + let excluded = children.filter { $0.customDerivationPath == nil } + return excluded.count + } + + public init(address: String, hdWalletGroupName: String?, walletNickname: String? = nil, socialUsername: String? = nil, socialUserId: String? = nil, mainnetDomains: [TezosDomainsReverseRecord]? = nil, ghostnetDomains: [TezosDomainsReverseRecord]? = nil, socialType: TorusAuthProvider? = nil, type: WalletType, children: [WalletMetadata], isChild: Bool, isWatchOnly: Bool, bas58EncodedPublicKey: String, backedUp: Bool, customDerivationPath: String?) { self.address = address self.hdWalletGroupName = hdWalletGroupName self.walletNickname = walletNickname @@ -302,6 +324,7 @@ public struct WalletMetadata: Codable, Hashable { self.isWatchOnly = isWatchOnly self.bas58EncodedPublicKey = bas58EncodedPublicKey self.backedUp = backedUp + self.customDerivationPath = customDerivationPath } public static func == (lhs: WalletMetadata, rhs: WalletMetadata) -> Bool { diff --git a/Sources/KukaiCoreSwift/Services/WalletCacheService.swift b/Sources/KukaiCoreSwift/Services/WalletCacheService.swift index 97c67494..8b960024 100644 --- a/Sources/KukaiCoreSwift/Services/WalletCacheService.swift +++ b/Sources/KukaiCoreSwift/Services/WalletCacheService.swift @@ -85,7 +85,7 @@ public class WalletCacheService { - Parameter childOfIndex: An optional `Int` to denote the index of the HD wallet that this wallet is a child of - Returns: Bool, indicating if the storage was successful or not */ - public func cache(wallet: T, childOfIndex: Int?, backedUp: Bool) throws { + public func cache(wallet: T, childOfIndex: Int?, backedUp: Bool, customDerivationPath: String? = nil) throws { guard let existingWallets = readWalletsFromDiskAndDecrypt() else { Logger.walletCache.error("cache - Unable to cache wallet, as can't decrypt existing wallets") throw WalletCacheError.unableToDecrypt @@ -99,40 +99,57 @@ public class WalletCacheService { var newWallets = existingWallets newWallets[wallet.address] = wallet - var newMetadata = readMetadataFromDiskAndDecrypt() + let newMetadata = readMetadataFromDiskAndDecrypt() + var array = metadataArray(forType: wallet.type, fromMeta: newMetadata) + if let index = childOfIndex { - if index >= newMetadata.hdWallets.count { - Logger.walletCache.error("WalletCacheService metadata insertion issue. Requested to add to HDWallet at index \"\(index)\", when there are currently only \"\(newMetadata.hdWallets.count)\" items") + + // If child index is present, update the correct sub array to include this new item, checking forst that we have the correct details + if index >= array.count { + Logger.walletCache.error("WalletCacheService metadata insertion issue. Requested to add at index \"\(index)\", when there are currently only \"\(array.count)\" items") throw WalletCacheError.requestedIndexTooHigh } - newMetadata.hdWallets[index].children.append(WalletMetadata(address: wallet.address, hdWalletGroupName: nil, walletNickname: nil, socialUsername: nil, type: wallet.type, children: [], isChild: true, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp)) + array[index].children.append(WalletMetadata(address: wallet.address, hdWalletGroupName: nil, walletNickname: nil, socialUsername: nil, type: wallet.type, children: [], isChild: true, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp, customDerivationPath: customDerivationPath)) - } else if let _ = wallet as? HDWallet { + } else if wallet.type == .hd || wallet.type == .ledger { + + // If its HD or Ledger (also a HD), these wallets display grouped together with a custom name. Compute the new default name based off existing data and then add + var groupNameStart = "" + switch wallet.type { + case .hd: + groupNameStart = "HD Wallet " + case .ledger: + groupNameStart = "Ledger Wallet " + case .social, .regular, .regularShifted: + groupNameStart = "" + } var newNumber = 0 - if let lastDefaultName = newMetadata.hdWallets.reversed().first(where: { $0.hdWalletGroupName?.prefix(10) == "HD Wallet " }) { - let numberOnly = lastDefaultName.hdWalletGroupName?.replacingOccurrences(of: "HD Wallet ", with: "") + if let lastDefaultName = array.reversed().first(where: { $0.hdWalletGroupName?.prefix(groupNameStart.count) ?? " " == groupNameStart }) { + let numberOnly = lastDefaultName.hdWalletGroupName?.replacingOccurrences(of: groupNameStart, with: "") newNumber = (Int(numberOnly ?? "0") ?? 0) + 1 } if newNumber == 0 { - newNumber = newMetadata.hdWallets.count + 1 + newNumber = array.count + 1 } - newMetadata.hdWallets.append(WalletMetadata(address: wallet.address, hdWalletGroupName: "HD Wallet \(newNumber)", walletNickname: nil, socialUsername: nil, socialType: nil, type: wallet.type, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp)) + array.append(WalletMetadata(address: wallet.address, hdWalletGroupName: "\(groupNameStart)\(newNumber)", walletNickname: nil, socialUsername: nil, socialType: nil, type: wallet.type, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp, customDerivationPath: customDerivationPath)) } else if let torusWallet = wallet as? TorusWallet { - newMetadata.socialWallets.append(WalletMetadata(address: wallet.address, hdWalletGroupName: nil, walletNickname: nil, socialUsername: torusWallet.socialUsername, socialUserId: torusWallet.socialUserId, socialType: torusWallet.authProvider, type: wallet.type, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp)) - } else if let _ = wallet as? LedgerWallet { - newMetadata.ledgerWallets.append(WalletMetadata(address: wallet.address, hdWalletGroupName: nil, walletNickname: nil, socialUsername: nil, socialType: nil, type: wallet.type, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp)) + // If social, cast and fetch special attributes + array.append(WalletMetadata(address: wallet.address, hdWalletGroupName: nil, walletNickname: nil, socialUsername: torusWallet.socialUsername, socialUserId: torusWallet.socialUserId, socialType: torusWallet.authProvider, type: wallet.type, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp, customDerivationPath: customDerivationPath)) } else { - newMetadata.linearWallets.append(WalletMetadata(address: wallet.address, hdWalletGroupName: nil, walletNickname: nil, socialUsername: nil, socialType: nil, type: wallet.type, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp)) + + // Else, add basic wallet to the list its supposed to go to + array.append(WalletMetadata(address: wallet.address, hdWalletGroupName: nil, walletNickname: nil, socialUsername: nil, socialType: nil, type: wallet.type, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: wallet.publicKeyBase58encoded(), backedUp: backedUp, customDerivationPath: customDerivationPath)) } - + // Update wallet metadata array, and then commit to disk + updateMetadataArray(forType: wallet.type, withNewArray: array, frorMeta: newMetadata) if encryptAndWriteWalletsToDisk(wallets: newWallets) && encryptAndWriteMetadataToDisk(newMetadata) == false { throw WalletCacheError.unableToEncryptAndWrite } else { @@ -140,6 +157,38 @@ public class WalletCacheService { } } + /// Helper method to return the appropriate sub array for the type, to reduce code compelxity + private func metadataArray(forType: WalletType, fromMeta: WalletMetadataList) -> [WalletMetadata] { + switch forType { + case .regular: + return fromMeta.linearWallets + case .regularShifted: + return fromMeta.linearWallets + case .hd: + return fromMeta.hdWallets + case .social: + return fromMeta.socialWallets + case .ledger: + return fromMeta.ledgerWallets + } + } + + /// Helper method to take ina new sub array and update and existing reference, to reduce code complexity + private func updateMetadataArray(forType: WalletType, withNewArray: [WalletMetadata], frorMeta: WalletMetadataList) { + switch forType { + case .regular: + frorMeta.linearWallets = withNewArray + case .regularShifted: + frorMeta.linearWallets = withNewArray + case .hd: + frorMeta.hdWallets = withNewArray + case .social: + frorMeta.socialWallets = withNewArray + case .ledger: + frorMeta.ledgerWallets = withNewArray + } + } + private func removeNewAddressFromWatchListIfExists(_ address: String, list: WalletMetadataList) { if let _ = list.watchWallets.first(where: { $0.address == address }) { let _ = deleteWatchWallet(address: address) @@ -150,7 +199,7 @@ public class WalletCacheService { Cahce a watch wallet metadata obj, only. Metadata cahcing handled via wallet cache method */ public func cacheWatchWallet(metadata: WalletMetadata) throws { - var list = readMetadataFromDiskAndDecrypt() + let list = readMetadataFromDiskAndDecrypt() if let _ = list.addresses().first(where: { $0 == metadata.address }) { Logger.walletCache.error("cacheWatchWallet - Unable to cache wallet, wallet already exists") @@ -177,37 +226,31 @@ public class WalletCacheService { } var newWallets = existingWallets + let type = existingWallets[withAddress]?.type ?? .hd newWallets.removeValue(forKey: withAddress) - var newMetadata = readMetadataFromDiskAndDecrypt() + let newMetadata = readMetadataFromDiskAndDecrypt() + var array = metadataArray(forType: type, fromMeta: newMetadata) + if let hdWalletIndex = parentIndex { - guard hdWalletIndex < newMetadata.hdWallets.count, let childIndex = newMetadata.hdWallets[hdWalletIndex].children.firstIndex(where: { $0.address == withAddress }) else { + guard hdWalletIndex < array.count, let childIndex = array[hdWalletIndex].children.firstIndex(where: { $0.address == withAddress }) else { Logger.walletCache.error("Unable to locate wallet") return false } - let _ = newMetadata.hdWallets[hdWalletIndex].children.remove(at: childIndex) + let _ = array[hdWalletIndex].children.remove(at: childIndex) } else { - if let index = newMetadata.hdWallets.firstIndex(where: { $0.address == withAddress }) { + if let index = array.firstIndex(where: { $0.address == withAddress }) { // Children will be removed from metadata automatically, as they are contained inside the parent, however they won't from the encrypted cache // Remove them from encrypted first, then parent from metadata - let children = newMetadata.hdWallets[index].children + let children = array[index].children for child in children { newWallets.removeValue(forKey: child.address) } - let _ = newMetadata.hdWallets.remove(at: index) - - } else if let index = newMetadata.socialWallets.firstIndex(where: { $0.address == withAddress }) { - let _ = newMetadata.socialWallets.remove(at: index) - - } else if let index = newMetadata.linearWallets.firstIndex(where: { $0.address == withAddress }) { - let _ = newMetadata.linearWallets.remove(at: index) - - } else if let index = newMetadata.ledgerWallets.firstIndex(where: { $0.address == withAddress }) { - let _ = newMetadata.ledgerWallets.remove(at: index) + let _ = array.remove(at: index) } else { Logger.walletCache.error("Unable to locate wallet") @@ -222,7 +265,7 @@ public class WalletCacheService { Clear a watch wallet meatadata obj from the metadata cache only, does not affect actual wallet cache */ public func deleteWatchWallet(address: String) -> Bool { - var list = readMetadataFromDiskAndDecrypt() + let list = readMetadataFromDiskAndDecrypt() list.watchWallets.removeAll(where: { $0.address == address }) return encryptAndWriteMetadataToDisk(list) diff --git a/Tests/KukaiCoreSwiftTests/Models/TzKTTransactionTests.swift b/Tests/KukaiCoreSwiftTests/Models/TzKTTransactionTests.swift index 3c8ec052..98e9b31d 100644 --- a/Tests/KukaiCoreSwiftTests/Models/TzKTTransactionTests.swift +++ b/Tests/KukaiCoreSwiftTests/Models/TzKTTransactionTests.swift @@ -78,7 +78,7 @@ final class TzKTTransactionTests: XCTestCase { } func testPlaceholders() { - let source = WalletMetadata(address: "tz1abc", hdWalletGroupName: nil, type: .hd, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true) + let source = WalletMetadata(address: "tz1abc", hdWalletGroupName: nil, type: .hd, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) let placeholder1 = TzKTTransaction.placeholder(withStatus: .unconfirmed, id: 567, opHash: "abc123", type: .transaction, counter: 0, fromWallet: source, newDelegate: TzKTAddress(alias: "Baking Benjamins", address: "tz1YgDUQV2eXm8pUWNz3S5aWP86iFzNp4jnD")) let placeholder2 = TzKTTransaction.placeholder(withStatus: .unconfirmed, id: 456, opHash: "def456", type: .transaction, counter: 1, fromWallet: source, destination: TzKTAddress(alias: nil, address: "tz1def"), xtzAmount: .init(fromNormalisedAmount: 4.17, decimalPlaces: 6), parameters: nil, primaryToken: nil, baker: nil, kind: nil) diff --git a/Tests/KukaiCoreSwiftTests/Services/WalletCacheServiceTests.swift b/Tests/KukaiCoreSwiftTests/Services/WalletCacheServiceTests.swift index 58ebf093..2147414f 100644 --- a/Tests/KukaiCoreSwiftTests/Services/WalletCacheServiceTests.swift +++ b/Tests/KukaiCoreSwiftTests/Services/WalletCacheServiceTests.swift @@ -211,7 +211,6 @@ class WalletCacheServiceTests: XCTestCase { XCTFail("Should not error: \(error)") } - // Update one and check let _ = list.set(hdWalletGroupName: "Blah 2", forAddress: hdWallet2.address) let _ = walletCacheService.encryptAndWriteMetadataToDisk(list) @@ -250,7 +249,7 @@ class WalletCacheServiceTests: XCTestCase { func testMetadata() { let mainentDomain = [TezosDomainsReverseRecord(id: "123", address: "tz1abc123", owner: "tz1abc123", expiresAtUtc: nil, domain: TezosDomainsDomain(name: "blah.tez", address: "tz1abc123"))] let ghostnetDomain = [TezosDomainsReverseRecord(id: "123", address: "tz1abc123", owner: "tz1abc123", expiresAtUtc: nil, domain: TezosDomainsDomain(name: "blah.gho", address: "tz1abc123"))] - let metadata1 = WalletMetadata(address: "tz1abc123", hdWalletGroupName: nil, mainnetDomains: mainentDomain, ghostnetDomains: ghostnetDomain, type: .hd, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true) + let metadata1 = WalletMetadata(address: "tz1abc123", hdWalletGroupName: nil, mainnetDomains: mainentDomain, ghostnetDomains: ghostnetDomain, type: .hd, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) XCTAssert(metadata1.hasMainnetDomain()) XCTAssert(metadata1.hasGhostnetDomain()) @@ -265,7 +264,7 @@ class WalletCacheServiceTests: XCTestCase { - let metadata2 = WalletMetadata(address: "tz1def456", hdWalletGroupName: nil, type: .hd, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true) + let metadata2 = WalletMetadata(address: "tz1def456", hdWalletGroupName: nil, type: .hd, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) XCTAssert(!metadata2.hasMainnetDomain()) XCTAssert(!metadata2.hasGhostnetDomain()) @@ -274,23 +273,23 @@ class WalletCacheServiceTests: XCTestCase { func testMetadataList() { let mainentDomain = TezosDomainsReverseRecord(id: "123", address: "tz1abc123", owner: "tz1abc123", expiresAtUtc: nil, domain: TezosDomainsDomain(name: "blah.tez", address: "tz1abc123")) let ghostnetDomain = TezosDomainsReverseRecord(id: "123", address: "tz1abc123", owner: "tz1abc123", expiresAtUtc: nil, domain: TezosDomainsDomain(name: "blah.gho", address: "tz1abc123")) - let child = WalletMetadata(address: "tz1child", hdWalletGroupName: nil, type: .hd, children: [], isChild: true, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true) - let updatedWatch = WalletMetadata(address: "tz1jkl", hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "blah", backedUp: true) + let child = WalletMetadata(address: "tz1child", hdWalletGroupName: nil, type: .hd, children: [], isChild: true, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) + let updatedWatch = WalletMetadata(address: "tz1jkl", hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "blah", backedUp: true, customDerivationPath: nil) let hd: [WalletMetadata] = [ - WalletMetadata(address: "tz1abc123", hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [child], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true) + WalletMetadata(address: "tz1abc123", hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [child], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) ] let social: [WalletMetadata] = [ - WalletMetadata(address: "tz1def", hdWalletGroupName: nil, socialUsername: "test@gmail.com", socialType: .google, type: .social, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true) + WalletMetadata(address: "tz1def", hdWalletGroupName: nil, socialUsername: "test@gmail.com", socialType: .google, type: .social, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) ] let linear: [WalletMetadata] = [ - WalletMetadata(address: "tz1ghi", hdWalletGroupName: nil, type: .regular, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true) + WalletMetadata(address: "tz1ghi", hdWalletGroupName: nil, type: .regular, children: [], isChild: false, isWatchOnly: false, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) ] let watch: [WalletMetadata] = [ - WalletMetadata(address: "tz1jkl", hdWalletGroupName: nil, type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true) + WalletMetadata(address: "tz1jkl", hdWalletGroupName: nil, type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) ] - var list = WalletMetadataList(socialWallets: social, hdWallets: hd, linearWallets: linear, ledgerWallets: [], watchWallets: watch) + let list = WalletMetadataList(socialWallets: social, hdWallets: hd, linearWallets: linear, ledgerWallets: [], watchWallets: watch) let addresses = list.addresses() XCTAssert(addresses == ["tz1def", "tz1abc123", "tz1child", "tz1ghi", "tz1jkl"], addresses.description) @@ -334,7 +333,7 @@ class WalletCacheServiceTests: XCTestCase { func testWatchWallet() { XCTAssert(walletCacheService.deleteAllCacheAndKeys()) - let watchWallet = WalletMetadata(address: "tz1jkl", hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true) + let watchWallet = WalletMetadata(address: "tz1jkl", hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) do { try walletCacheService.cacheWatchWallet(metadata: watchWallet) @@ -355,7 +354,7 @@ class WalletCacheServiceTests: XCTestCase { XCTFail("Should not error: \(error)") } - let watchWallet = WalletMetadata(address: MockConstants.defaultLinearWallet.address, hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true) + let watchWallet = WalletMetadata(address: MockConstants.defaultLinearWallet.address, hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) do { try walletCacheService.cacheWatchWallet(metadata: watchWallet) @@ -369,7 +368,7 @@ class WalletCacheServiceTests: XCTestCase { XCTAssert(walletCacheService.deleteAllCacheAndKeys()) // Add watch and confirm - let watchWallet = WalletMetadata(address: MockConstants.defaultLinearWallet.address, hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true) + let watchWallet = WalletMetadata(address: MockConstants.defaultLinearWallet.address, hdWalletGroupName: nil, mainnetDomains: [], ghostnetDomains: [], type: .hd, children: [], isChild: false, isWatchOnly: true, bas58EncodedPublicKey: "", backedUp: true, customDerivationPath: nil) do { try walletCacheService.cacheWatchWallet(metadata: watchWallet) @@ -391,4 +390,29 @@ class WalletCacheServiceTests: XCTestCase { XCTFail("Should not error: \(error)") } } + + func testLedgerWalletCaching() { + XCTAssert(walletCacheService.deleteAllCacheAndKeys()) + + let ledgerWallet = LedgerWallet(address: "tz1abc", publicKey: "edpks1234", derivationPath: HD.defaultDerivationPath, curve: .ed25519, ledgerUUID: "blah1")! + let ledgerWalletChild1 = LedgerWallet(address: "tz1def", publicKey: "edpks1234", derivationPath: HD.defaultDerivationPath, curve: .ed25519, ledgerUUID: "blah2")! + let ledgerWalletChild2 = LedgerWallet(address: "tz1ghi", publicKey: "edpks1234", derivationPath: HD.defaultDerivationPath, curve: .ed25519, ledgerUUID: "blah3")! + let ledgerWalletChild3 = LedgerWallet(address: "tz1jkl", publicKey: "edpks1234", derivationPath: "m/44'/1729'/147'/62'", curve: .ed25519, ledgerUUID: "blah4")! + + do { + try walletCacheService.cache(wallet: ledgerWallet, childOfIndex: nil, backedUp: true) + try walletCacheService.cache(wallet: ledgerWalletChild1, childOfIndex: 0, backedUp: true) + try walletCacheService.cache(wallet: ledgerWalletChild2, childOfIndex: 0, backedUp: true) + try walletCacheService.cache(wallet: ledgerWalletChild3, childOfIndex: 0, backedUp: true, customDerivationPath: ledgerWalletChild3.derivationPath) + + let list = walletCacheService.readMetadataFromDiskAndDecrypt() + let ledgers = list.ledgerWallets + let excludedCount = ledgers[0].childCountExcludingCustomDerivationPaths() + XCTAssert(ledgers.count == 1, ledgers.count.description) + XCTAssert(ledgers[0].children.count == 3, ledgers[0].children.count.description) + XCTAssert(excludedCount == 2, excludedCount.description) + } catch let error { + XCTFail("Should not error: \(error)") + } + } }