diff --git a/Cargo.lock b/Cargo.lock index 1204ba1cc..78a6faa11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2528,7 +2528,7 @@ dependencies = [ [[package]] name = "sargon" -version = "0.6.41" +version = "0.6.42" dependencies = [ "actix-rt", "aes-gcm", diff --git a/Cargo.toml b/Cargo.toml index f0c45b27e..89df947a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sargon" -version = "0.6.41" +version = "0.6.42" edition = "2021" build = "build.rs" @@ -141,6 +141,7 @@ k256 = { git = "https://github.com/RustCrypto/elliptic-curves", rev = "e158ce5cf bip39 = { git = "https://github.com/rust-bitcoin/rust-bip39", rev = "a30760beac21d595b2bda376df4f4e6bf029bcc5", features = [ "serde", "zeroize", + "french", ] } # assert-json-diff = "2.0.2" diff --git a/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/BIP39/BIP39Language+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/BIP39/BIP39Language+Wrap+Functions.swift new file mode 100644 index 000000000..86d2ac2b9 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/BIP39/BIP39Language+Wrap+Functions.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension BIP39Language { + public func wordlist() -> [BIP39Word] { + bip39LanguageWordlist(language: self) + } +} diff --git a/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/BIP39/BIP39WordCount+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/BIP39/BIP39WordCount+Wrap+Functions.swift new file mode 100644 index 000000000..ed54e9644 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/BIP39/BIP39WordCount+Wrap+Functions.swift @@ -0,0 +1,15 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension BIP39WordCount { + public static var allCases: [Self] { + bip39WordCountAll() + } +} diff --git a/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/DerivationPath+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/DerivationPath+Wrap+Functions.swift index 239d80819..25d80d04e 100644 --- a/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/DerivationPath+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/Crypto/Derivation/DerivationPath+Wrap+Functions.swift @@ -9,11 +9,16 @@ import Foundation import SargonUniFFI extension DerivationPath { - public var path: HdPath { + public var path: HDPath { derivationPathToHdPath(path: self) } public func toString() -> String { derivationPathToString(path: self) } + + public init(string: String) throws { + self = try newDerivationPathFromString(string: string) + } + } diff --git a/apple/Sources/Sargon/Extensions/Methods/Crypto/Hash/Hash+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Crypto/Hash/Hash+Wrap+Functions.swift index b24a69958..576da92e7 100644 --- a/apple/Sources/Sargon/Extensions/Methods/Crypto/Hash/Hash+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/Crypto/Hash/Hash+Wrap+Functions.swift @@ -8,17 +8,15 @@ extension DataProtocol { } extension Hash { - public func hash() -> Self { - data.hash() - } -} - -extension Hash { - public var data: Data { - bytes.data - } - public var bytes: Exactly32Bytes { hashGetBytes(hash: self) } + + public init(string: String) throws { + self = try newHashFromString(string: string) + } + + public init(bytes32: Exactly32Bytes) { + self = newHashFromBytes(bytes: bytes32) + } } diff --git a/apple/Sources/Sargon/Extensions/Methods/Crypto/Keys/PrivateHD+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Crypto/Keys/PrivateHD+Wrap+Functions.swift new file mode 100644 index 000000000..a5a424582 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Methods/Crypto/Keys/PrivateHD+Wrap+Functions.swift @@ -0,0 +1,32 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension PrivateHierarchicalDeterministicFactorSource { + + public static func olympia( + mnemonicWithPassphrase: MnemonicWithPassphrase + ) -> Self { + newPrivateHdFactorSourceOlympiaFromMnemonicWithPassphrase( + mnemonicWithPassphrase: mnemonicWithPassphrase, + walletClientModel: .iphone + ) + } + + public static func babylon( + isMainBDFS: Bool, + mnemonicWithPassphrase: MnemonicWithPassphrase + ) -> Self { + newPrivateHdFactorSourceBabylonFromMnemonicWithPassphrase( + isMain: isMainBDFS, + mnemonicWithPassphrase: mnemonicWithPassphrase, + walletClientModel: .iphone + ) + } +} diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/AppPreferences+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/AppPreferences+Wrap+Functions.swift new file mode 100644 index 000000000..4e220f281 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/AppPreferences+Wrap+Functions.swift @@ -0,0 +1,13 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension AppPreferences { + public static let `default`: Self = newAppPreferencesDefault() +} diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/DeviceFactorSource+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/DeviceFactorSource+Wrap+Functions.swift new file mode 100644 index 000000000..ab6f73997 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/DeviceFactorSource+Wrap+Functions.swift @@ -0,0 +1,37 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension DeviceFactorSource { + + public static func olympia( + mnemonicWithPassphrase: MnemonicWithPassphrase + ) -> Self { + newDeviceFactorSourceOlympia( + mnemonicWithPassphrase: mnemonicWithPassphrase, + walletClientModel: .iphone + ) + } + + public static func babylon( + mnemonicWithPassphrase: MnemonicWithPassphrase, + isMain: Bool + ) -> Self { + newDeviceFactorSourceBabylon( + isMain: isMain, + mnemonicWithPassphrase: mnemonicWithPassphrase, + walletClientModel: .iphone + ) + } + + + public var isMainBDFS: Bool { + deviceFactorSourceIsMainBdfs(deviceFactorSource: self) + } +} diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/FactorSource+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/FactorSource+Wrap+Functions.swift index e9903f7b4..132e6ed74 100644 --- a/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/FactorSource+Wrap+Functions.swift +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/FactorSource+Wrap+Functions.swift @@ -12,4 +12,11 @@ extension FactorSource { public func toString() -> String { factorSourceToString(factorSource: self) } + + public var supportsOlympia: Bool { + factorSourceSupportsOlympia(factorSource: self) + } + public var supportsBabylon: Bool { + factorSourceSupportsBabylon(factorSource: self) + } } diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/FactorSourceKind+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/FactorSourceKind+Wrap+Functions.swift new file mode 100644 index 000000000..52b352552 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/FactorSourceKind+Wrap+Functions.swift @@ -0,0 +1,21 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension FactorSourceKind { + + public init(string: String) throws { + self = try newFactorSourceKindFromString(string: string) + } + + public func toString() -> String { + factorSourceKindToString(kind: self) + } +} + diff --git a/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/LedgerHardwareWalletModel+Wrap+Functions.swift b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/LedgerHardwareWalletModel+Wrap+Functions.swift new file mode 100644 index 000000000..3a779b6d5 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Methods/Profile/Factor/LedgerHardwareWalletModel+Wrap+Functions.swift @@ -0,0 +1,19 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension LedgerHardwareWalletModel { + public init(string: String) throws { + self = try newLedgerHwWalletModelFromString(string: string) + } + + public func toString() -> String { + ledgerHwWalletModelToString(model: self) + } +} diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/BIP39Language+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/BIP39Language+SampleValues.swift new file mode 100644 index 000000000..435b2dc32 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/BIP39Language+SampleValues.swift @@ -0,0 +1,8 @@ +import SargonUniFFI + +#if DEBUG +extension BIP39Language { + public static let sample: Self = newBip39LanguageSample() + public static let sampleOther: Self = newBip39LanguageSampleOther() +} +#endif // DEBUG diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/Mnemonic+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/Mnemonic+SampleValues.swift new file mode 100644 index 000000000..3ebf7ca3f --- /dev/null +++ b/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/Mnemonic+SampleValues.swift @@ -0,0 +1,10 @@ +import SargonUniFFI + +#if DEBUG +extension Mnemonic { + public static let sample: Self = newMnemonicSample() + public static let sampleOther: Self = newMnemonicSampleOther() + + public static let sample24ZooVote: Self = try! Self(phrase: "zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo vote") +} +#endif // DEBUG diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/MnemonicWithPassphrase+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/MnemonicWithPassphrase+SampleValues.swift similarity index 100% rename from apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/MnemonicWithPassphrase+SampleValues.swift rename to apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/BIP39/MnemonicWithPassphrase+SampleValues.swift diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/Mnemonic+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/Mnemonic+SampleValues.swift deleted file mode 100644 index d99563c83..000000000 --- a/apple/Sources/Sargon/Extensions/SampleValues/Crypto/Derivation/Mnemonic+SampleValues.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SargonUniFFI - -#if DEBUG -extension Mnemonic { - public static var sample: Self { - newMnemonicSample() - } - - public static var sampleOther: Self { - newMnemonicSampleOther() - } -} -#endif // DEBUG diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Profile/AppPreferences+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Profile/AppPreferences+SampleValues.swift new file mode 100644 index 000000000..5c288886f --- /dev/null +++ b/apple/Sources/Sargon/Extensions/SampleValues/Profile/AppPreferences+SampleValues.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-19. +// + +import Foundation +import SargonUniFFI + +#if DEBUG +extension AppPreferences { + public static let sample: Self = newAppPreferencesSample() + public static let sampleOther: Self = newAppPreferencesSampleOther() +} +#endif // DEBUG diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Profile/Factor/FactorSource/FactorSourceKind+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Profile/Factor/FactorSource/FactorSourceKind+SampleValues.swift new file mode 100644 index 000000000..5ebc69310 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/SampleValues/Profile/Factor/FactorSource/FactorSourceKind+SampleValues.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +#if DEBUG +extension FactorSourceKind { + public static let sample: Self = newFactorSourceKindSample() + public static let sampleOther: Self = newFactorSourceKindSampleOther() +} +#endif // DEBUG diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Profile/Factor/FactorSource/PrivateHierarchicalDeterministicFactorSource+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Profile/Factor/FactorSource/PrivateHierarchicalDeterministicFactorSource+SampleValues.swift new file mode 100644 index 000000000..0790c6f9d --- /dev/null +++ b/apple/Sources/Sargon/Extensions/SampleValues/Profile/Factor/FactorSource/PrivateHierarchicalDeterministicFactorSource+SampleValues.swift @@ -0,0 +1,8 @@ +import SargonUniFFI + +#if DEBUG +extension PrivateHierarchicalDeterministicFactorSource { + public static let sample: Self = newPrivateHdFactorSourceSample() + public static let sampleOther: Self = newPrivateHdFactorSourceSampleOther() +} +#endif // DEBUG diff --git a/apple/Sources/Sargon/Extensions/SampleValues/Profile/LedgerHardwareWalletModel+SampleValues.swift b/apple/Sources/Sargon/Extensions/SampleValues/Profile/LedgerHardwareWalletModel+SampleValues.swift new file mode 100644 index 000000000..efe40e4d3 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/SampleValues/Profile/LedgerHardwareWalletModel+SampleValues.swift @@ -0,0 +1,16 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +#if DEBUG +extension LedgerHardwareWalletModel { + public static let sample: Self = newLedgerHwWalletModelSample() + public static let sampleOther: Self = newLedgerHwWalletModelSampleOther() +} +#endif // DEBUG diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift new file mode 100644 index 000000000..8220271db --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39Language+Swiftified.swift @@ -0,0 +1,12 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +public typealias BIP39Language = Bip39Language +extension BIP39Language: SargonModel {} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift new file mode 100644 index 000000000..eea350c67 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39/BIP39WordCount+Swiftified.swift @@ -0,0 +1,58 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +public typealias BIP39WordCount = Bip39WordCount + +extension BIP39WordCount: SargonModel { + public static let sample: Self = .twentyFour + public static let sampleOther: Self = .twelve +} + +extension BIP39WordCount { + public init?(wordCount: Int) { + self.init(rawValue: UInt8(wordCount)) + } +} + +// MARK: Identifiable +extension BIP39WordCount: Identifiable { + public typealias ID = RawValue + public var id: ID { + rawValue + } +} + +// MARK: CaseIterable +extension BIP39WordCount: CaseIterable { + public typealias AllCases = [Self] +} + +// MARK: Comparable +extension BIP39WordCount: Comparable { + public static func < (lhs: Self, rhs: Self) -> Bool { + lhs.rawValue < rhs.rawValue + } +} + +extension BIP39WordCount { + public mutating func increaseBy3() { + guard self < .twentyFour else { + return assertionFailure("At max word count (24)") + } + self = .init(rawValue: rawValue + 3)! + } + + public mutating func decreaseBy3() { + guard self > .twelve else { + return assertionFailure("At min word count (12)") + } + self = .init(rawValue: rawValue - 3)! + } +} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Language+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Language+Swiftified.swift deleted file mode 100644 index d6be023e4..000000000 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/BIP39Language+Swiftified.swift +++ /dev/null @@ -1,3 +0,0 @@ -import SargonUniFFI - -public typealias BIP39Language = Bip39Language diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/CAP26Path+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/CAP26Path+Swiftified.swift index 13f89e004..3aec0276b 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/CAP26Path+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/CAP26Path+Swiftified.swift @@ -3,7 +3,7 @@ import SargonUniFFI public typealias CAP26Path = Cap26Path extension CAP26Path: SargonModel, CAP26PathProtocol { - public var path: HdPath { + public var path: HDPath { asDerivationPath.path } diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/DerivationPath+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/DerivationPath+Swiftified.swift index 1f03c772f..c7744fcb8 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/DerivationPath+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Derivation/DerivationPath+Swiftified.swift @@ -13,14 +13,51 @@ extension DerivationPath: CustomStringConvertible { public var description: String { toString() } - +} + +extension DerivationPath: DerivationPathProtocol { + public var asDerivationPath: DerivationPath { self } +} + +public typealias HDPath = HdPath + +extension DerivationPath { /// Returns the index, non hardened, so `3H` returns `3`. public var nonHardenedIndex: HDPathValue { let component = self.path.components.last! // safe to unwrap, we disallow empty paths. return component.nonHardenedValue } + + public var curve: SLIP10Curve { + switch self { + case .bip44Like: .secp256k1 + case .cap26: .curve25519 + } + } + + public static func forEntity( + kind: EntityKind, + networkID: NetworkID, + index: HDPathValue + ) -> Self { + switch kind { + case .account: + AccountPath( + networkID: networkID, + keyKind: .transactionSigning, + index: index + ).asDerivationPath + case .persona: + IdentityPath( + networkID: networkID, + keyKind: .transactionSigning, + index: index + ).asDerivationPath + } + } } + extension HdPathComponent { public var nonHardenedValue: HDPathValue { hdPathComponentGetNonHardenedValue(component: self) diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Hash/Hash+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Hash/Hash+Swiftified.swift index 848f3fc43..0f2267a01 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Hash/Hash+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Crypto/Hash/Hash+Swiftified.swift @@ -8,6 +8,14 @@ extension Hash { public var hex: String { data.hex } + + public func hash() -> Self { + data.hash() + } + + public var data: Data { + bytes.data + } } extension Hash: CustomStringConvertible { diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/AppPreferences+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/AppPreferences+Swiftified.swift new file mode 100644 index 000000000..f4f0fd13a --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/AppPreferences+Swiftified.swift @@ -0,0 +1,11 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension AppPreferences: SargonModel {} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/DeviceFactorSource+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/DeviceFactorSource+Swiftified.swift index d65745e9b..59a8c4af0 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/DeviceFactorSource+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/DeviceFactorSource+Swiftified.swift @@ -23,4 +23,7 @@ extension DeviceFactorSource: FactorSourceProtocol { public var factorSourceKind: FactorSourceKind { .device } + + public var supportsOlympia: Bool { asGeneral.supportsOlympia } + public var supportsBabylon: Bool { asGeneral.supportsBabylon } } diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSource+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSource+Swiftified.swift index 9c9f11abb..a4d6a5235 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSource+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSource+Swiftified.swift @@ -32,4 +32,6 @@ extension FactorSource: FactorSourceProtocol { } public var asGeneral: FactorSource { self } + + } diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceKind+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceKind+Swiftified.swift new file mode 100644 index 000000000..835f2f2d5 --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceKind+Swiftified.swift @@ -0,0 +1,27 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension FactorSourceKind: SargonModel {} +extension FactorSourceKind: CustomStringConvertible { + public var description: String { + toString() + } +} + +extension FactorSourceKind { + + public var rawValue: String { + toString() + } + + public init?(rawValue: String) { + try? self.init(string: rawValue) + } +} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceProtocol.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceProtocol.swift index a519856b7..c5dfd7f47 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceProtocol.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/FactorSourceProtocol.swift @@ -8,8 +8,10 @@ import Foundation import SargonUniFFI -public protocol FactorSourceProtocol { +public protocol FactorSourceProtocol: SargonModel { var factorSourceID: FactorSourceID { get } var factorSourceKind: FactorSourceKind { get } var asGeneral: FactorSource { get } + var supportsOlympia: Bool { get } + var supportsBabylon: Bool { get } } diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/LedgerHardwareWalletFactorSource+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/LedgerHardwareWalletFactorSource+Swiftified.swift index 2c31d0cdf..0ec6f1e03 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/LedgerHardwareWalletFactorSource+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/LedgerHardwareWalletFactorSource+Swiftified.swift @@ -23,4 +23,7 @@ extension LedgerHardwareWalletFactorSource: FactorSourceProtocol { public var factorSourceKind: FactorSourceKind { .ledgerHqHardwareWallet } + + public var supportsOlympia: Bool { asGeneral.supportsOlympia } + public var supportsBabylon: Bool { asGeneral.supportsBabylon } } diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/PrivateHierarchicalDeterministicFactorSource+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/PrivateHierarchicalDeterministicFactorSource+Swiftified.swift new file mode 100644 index 000000000..d3b3b6a8b --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/FactorSource/PrivateHierarchicalDeterministicFactorSource+Swiftified.swift @@ -0,0 +1,34 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation +import SargonUniFFI + +extension PrivateHierarchicalDeterministicFactorSource: SargonModel {} +extension PrivateHierarchicalDeterministicFactorSource: FactorSourceProtocol { + public var factorSourceID: FactorSourceID { + factorSource.factorSourceID + } + + public var factorSourceKind: FactorSourceKind { + factorSource.factorSourceKind + } + + public var asGeneral: FactorSource { + factorSource.asGeneral + } + + public var supportsOlympia: Bool { + factorSource.supportsOlympia + } + + public var supportsBabylon: Bool { + factorSource.supportsBabylon + } + + +} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/LedgerHardwareWalletModel+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/LedgerHardwareWalletModel+Swiftified.swift new file mode 100644 index 000000000..f97c21e0d --- /dev/null +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Factor/LedgerHardwareWalletModel+Swiftified.swift @@ -0,0 +1,20 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import Foundation + +extension LedgerHardwareWalletModel: SargonModel {} +extension LedgerHardwareWalletModel: CustomStringConvertible { + public var description: String { + toString() + } +} +extension LedgerHardwareWalletModel { + public var rawValue: String { + toString() + } +} diff --git a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Gateway/Gateway+Swiftified.swift b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Gateway/Gateway+Swiftified.swift index aa9b08da7..7c40433a0 100644 --- a/apple/Sources/Sargon/Extensions/Swiftified/Profile/Gateway/Gateway+Swiftified.swift +++ b/apple/Sources/Sargon/Extensions/Swiftified/Profile/Gateway/Gateway+Swiftified.swift @@ -14,3 +14,37 @@ extension Gateway: Identifiable { } } + +extension Gateway { + public var networkID: NetworkID { + network.id + } +} + +extension Gateway { + + public static var nebunet: Self { + Self.forNetwork(id: .nebunet) + } + + public static var kisharnet: Self { + Self.forNetwork(id: .kisharnet) + } + + public static var ansharnet: Self { + Self.forNetwork(id: .ansharnet) + } + + public static var hammunet: Self { + Self.forNetwork(id: .hammunet) + } + + public static var enkinet: Self { + Self.forNetwork(id: .enkinet) + } + + public static var mardunet: Self { + Self.forNetwork(id: .mardunet) + } + +} diff --git a/apple/Sources/Sargon/Protocols/CAP26PathProtocol.swift b/apple/Sources/Sargon/Protocols/CAP26PathProtocol.swift index 2f73bbaae..f5e0ee8be 100644 --- a/apple/Sources/Sargon/Protocols/CAP26PathProtocol.swift +++ b/apple/Sources/Sargon/Protocols/CAP26PathProtocol.swift @@ -4,16 +4,6 @@ public protocol DerivationPathProtocol: HDPathProtocol { var asDerivationPath: DerivationPath { get } } -extension DerivationPath: DerivationPathProtocol { - public init(string: String) throws { - if let cap26 = try? CAP26Path(string: string) { - self = .cap26(value: cap26) - } else { - self = try .bip44Like(value: .init(string: string)) - } - } - public var asDerivationPath: DerivationPath { self } -} public protocol CAP26PathProtocol: DerivationPathProtocol { var asGeneral: CAP26Path { get } diff --git a/apple/Tests/TestCases/Crypto/AccountPathTests.swift b/apple/Tests/TestCases/Crypto/AccountPathTests.swift index 1560510be..0f6b9b095 100644 --- a/apple/Tests/TestCases/Crypto/AccountPathTests.swift +++ b/apple/Tests/TestCases/Crypto/AccountPathTests.swift @@ -4,7 +4,7 @@ import Sargon import SargonUniFFI import XCTest -final class AccountPathTests: HDPathProtocolTests { +final class AccountPathTests: HDPathProtocolTest { func test_sample_description() { XCTAssertNoDifference(SUT.sample.description, "m/44H/1022H/1H/525H/1460H/0H") } diff --git a/apple/Tests/TestCases/Crypto/BIP39/BIP39LanguageTests.swift b/apple/Tests/TestCases/Crypto/BIP39/BIP39LanguageTests.swift new file mode 100644 index 000000000..feaca0dfd --- /dev/null +++ b/apple/Tests/TestCases/Crypto/BIP39/BIP39LanguageTests.swift @@ -0,0 +1,11 @@ +import CustomDump +import Foundation +import Sargon +import SargonUniFFI +import XCTest + +final class BIP39LanguageTests: Test { + func test_wordlist() { + XCTAssertEqual(SUT.english.wordlist().first!.word, "abandon") + } +} diff --git a/apple/Tests/TestCases/Crypto/BIP39/BIP39WordCountTests.swift b/apple/Tests/TestCases/Crypto/BIP39/BIP39WordCountTests.swift new file mode 100644 index 000000000..e396200d9 --- /dev/null +++ b/apple/Tests/TestCases/Crypto/BIP39/BIP39WordCountTests.swift @@ -0,0 +1,54 @@ +import CustomDump +import Foundation +import Sargon +import SargonUniFFI +import XCTest + +final class BIP39WordCountTests: Test { + func test_all_cases() { + XCTAssertEqual(SUT.allCases, [.twentyFour, .twentyOne, .eighteen, .fifteen, .twelve]) + } + + func test_id_is_raw_value() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.id, sut.rawValue) + } + SUT.allCases.forEach(doTest) + } + + func test_init_raw_value() { + func doTest(_ sut: SUT) { + XCTAssertEqual(SUT.init(rawValue: sut.rawValue), sut) + } + SUT.allCases.forEach(doTest) + } + + func test_init_wordCount() { + func doTest(_ sut: SUT) { + XCTAssertEqual(SUT.init(wordCount: Int(sut.rawValue)), sut) + } + SUT.allCases.forEach(doTest) + } + + func test_comparable_less_than() { + XCTAssertLessThan(SUT.eighteen, SUT.twentyOne) + XCTAssertLessThan(SUT.eighteen, SUT.twentyFour) + } + + func test_comparable_greater_than() { + XCTAssertGreaterThan(SUT.eighteen, SUT.fifteen) + XCTAssertGreaterThan(SUT.fifteen, SUT.twelve) + } + + func test_decrease() { + var sut = SUT.fifteen + sut.decreaseBy3() + XCTAssertEqual(sut, .twelve) + } + + func test_increase() { + var sut = SUT.twentyOne + sut.increaseBy3() + XCTAssertEqual(sut, .twentyFour) + } +} diff --git a/apple/Tests/TestCases/Crypto/MnemonicTests.swift b/apple/Tests/TestCases/Crypto/BIP39/MnemonicTests.swift similarity index 100% rename from apple/Tests/TestCases/Crypto/MnemonicTests.swift rename to apple/Tests/TestCases/Crypto/BIP39/MnemonicTests.swift diff --git a/apple/Tests/TestCases/Crypto/MnemonicWithPassphraseTests.swift b/apple/Tests/TestCases/Crypto/BIP39/MnemonicWithPassphraseTests.swift similarity index 100% rename from apple/Tests/TestCases/Crypto/MnemonicWithPassphraseTests.swift rename to apple/Tests/TestCases/Crypto/BIP39/MnemonicWithPassphraseTests.swift diff --git a/apple/Tests/TestCases/Crypto/BIP44LikePathTests.swift b/apple/Tests/TestCases/Crypto/BIP44LikePathTests.swift index ab21521b9..d197a118d 100644 --- a/apple/Tests/TestCases/Crypto/BIP44LikePathTests.swift +++ b/apple/Tests/TestCases/Crypto/BIP44LikePathTests.swift @@ -4,7 +4,7 @@ import Sargon import SargonUniFFI import XCTest -final class BIP44LikePathTests: HDPathProtocolTests { +final class BIP44LikePathTests: HDPathProtocolTest { func test_sample_description() { XCTAssertNoDifference(SUT.sample.description, "m/44H/1022H/0H/0/0H") } diff --git a/apple/Tests/TestCases/Crypto/CAP26PathTests.swift b/apple/Tests/TestCases/Crypto/CAP26PathTests.swift index cbd5e686b..7b9d7872f 100644 --- a/apple/Tests/TestCases/Crypto/CAP26PathTests.swift +++ b/apple/Tests/TestCases/Crypto/CAP26PathTests.swift @@ -4,7 +4,7 @@ import Sargon import SargonUniFFI import XCTest -final class CAP26PathTests: HDPathProtocolTests { +final class CAP26PathTests: HDPathProtocolTest { func test_sample_description() { XCTAssertNoDifference(SUT.sample.description, "m/44H/1022H/1H/525H/1460H/0H") } diff --git a/apple/Tests/TestCases/Crypto/DerivationPathTests.swift b/apple/Tests/TestCases/Crypto/DerivationPathTests.swift index 0b710675f..08510aa2f 100644 --- a/apple/Tests/TestCases/Crypto/DerivationPathTests.swift +++ b/apple/Tests/TestCases/Crypto/DerivationPathTests.swift @@ -4,7 +4,7 @@ import Sargon import SargonUniFFI import XCTest -final class DerivationPathTests: HDPathProtocolTests { +final class DerivationPathTests: HDPathProtocolTest { func test_sample_description() { XCTAssertNoDifference(SUT.sample.description, "m/44H/1022H/1H/525H/1460H/0H") } @@ -18,8 +18,16 @@ final class DerivationPathTests: HDPathProtocolTests { ) } + + func test_get_hd_path() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.path.components.count, sut.toString().matches(of: "/").count) + } + SUT.allCases.forEach(doTest) + } func test_cap26_account_hd_path() { + let accountPath = AccountPath.sample XCTAssertEqual( @@ -75,4 +83,14 @@ final class DerivationPathTests: HDPathProtocolTests { func test_as_general_is_identity() { XCTAssertEqual(SUT.sample.asDerivationPath, SUT.sample) } + + func test_curve() { + XCTAssertEqual(SUT.sample.curve, .curve25519) + XCTAssertEqual(SUT.bip44Like(value: .sample).curve, .secp256k1) + } + + func test_for_entity() { + XCTAssertEqual(SUT.forEntity(kind: .account, networkID: .mainnet, index: 9).toString(), "m/44H/1022H/1H/525H/1460H/9H") + XCTAssertEqual(SUT.forEntity(kind: .persona, networkID: .stokenet, index: 42).toString(), "m/44H/1022H/2H/618H/1460H/42H") + } } diff --git a/apple/Tests/TestCases/Crypto/HashTests.swift b/apple/Tests/TestCases/Crypto/HashTests.swift index b1a99fd6b..119541e86 100644 --- a/apple/Tests/TestCases/Crypto/HashTests.swift +++ b/apple/Tests/TestCases/Crypto/HashTests.swift @@ -17,4 +17,14 @@ final class HashTests: Test { func test_hash_of_hash() { XCTAssertEqual(Data("Hello Radix".utf8).hash().hash().bytes, "0c18fa9b3e94d9b879d631e791ee0699ad2f98d914f16a35a70f6312abe4474a") } + + func test_from_bytes32() { + let bytes32: Exactly32Bytes = "48f1bd08444b5e713db9e14caac2faae71836786ac94d645b00679728202a935" + XCTAssertEqual(SUT(bytes32: bytes32).bytes, bytes32) + } + + func test_from_string() throws { + let hex = "48f1bd08444b5e713db9e14caac2faae71836786ac94d645b00679728202a935" + try XCTAssertEqual(SUT(string: hex).hex, hex) + } } diff --git a/apple/Tests/TestCases/Crypto/IdentityPathTests.swift b/apple/Tests/TestCases/Crypto/IdentityPathTests.swift index 92e375d7d..755f67007 100644 --- a/apple/Tests/TestCases/Crypto/IdentityPathTests.swift +++ b/apple/Tests/TestCases/Crypto/IdentityPathTests.swift @@ -4,7 +4,7 @@ import Sargon import SargonUniFFI import XCTest -final class IdentityPathTests: HDPathProtocolTests { +final class IdentityPathTests: HDPathProtocolTest { func test_sample_description() { XCTAssertNoDifference(SUT.sample.description, "m/44H/1022H/1H/618H/1460H/0H") } diff --git a/apple/Tests/TestCases/Profile/AppPreferencesTests.swift b/apple/Tests/TestCases/Profile/AppPreferencesTests.swift new file mode 100644 index 000000000..f7b310e2c --- /dev/null +++ b/apple/Tests/TestCases/Profile/AppPreferencesTests.swift @@ -0,0 +1,18 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import CustomDump +import Foundation +import Sargon +import SargonUniFFI +import XCTest + +final class AppPreferencesTests: Test { + func test_default_guarantee_is_99() { + XCTAssertEqual(SUT.default.transaction.defaultDepositGuarantee, 0.99) + } +} diff --git a/apple/Tests/TestCases/Profile/FactorSource/DeviceFactorSourceTests.swift b/apple/Tests/TestCases/Profile/FactorSource/DeviceFactorSourceTests.swift index b18f0663d..5e4e2c764 100644 --- a/apple/Tests/TestCases/Profile/FactorSource/DeviceFactorSourceTests.swift +++ b/apple/Tests/TestCases/Profile/FactorSource/DeviceFactorSourceTests.swift @@ -4,7 +4,7 @@ import Sargon import SargonUniFFI import XCTest -final class DeviceFactorSourceTests: Test { +final class DeviceFactorSourceTests: FactorSourceTest { func test_id_of_device() { XCTAssertEqual(SUT.sample.id.description, FactorSourceID.hash(value: SUT.sample.id).description) } @@ -24,4 +24,26 @@ final class DeviceFactorSourceTests: Test { func test_as_general() { XCTAssertEqual(SUT.sample.asGeneral, FactorSource.device(value: SUT.sample)) } + + func test_new_babylon_is_main_true() { + let sut = SUT.babylon(mnemonicWithPassphrase: .sample, isMain: true) + XCTAssertTrue(sut.isMainBDFS) + } + + func test_new_babylon_is_main_false() { + let sut = SUT.babylon(mnemonicWithPassphrase: .sample, isMain: false) + XCTAssertFalse(sut.isMainBDFS) + } + + func test_new_babylon() { + let sut = SUT.babylon(mnemonicWithPassphrase: .sample, isMain: true) + XCTAssertTrue(sut.supportsBabylon) + XCTAssertFalse(sut.supportsOlympia) + } + + func test_new_olympia() { + let sut = SUT.olympia(mnemonicWithPassphrase: .sample) + XCTAssertTrue(sut.supportsOlympia) + XCTAssertFalse(sut.supportsBabylon) + } } diff --git a/apple/Tests/TestCases/Profile/FactorSource/LedgerHardwareWalletFactorSourceTests.swift b/apple/Tests/TestCases/Profile/FactorSource/LedgerHardwareWalletFactorSourceTests.swift index 634ff57dc..9021a6375 100644 --- a/apple/Tests/TestCases/Profile/FactorSource/LedgerHardwareWalletFactorSourceTests.swift +++ b/apple/Tests/TestCases/Profile/FactorSource/LedgerHardwareWalletFactorSourceTests.swift @@ -11,7 +11,7 @@ import Sargon import SargonUniFFI import XCTest -final class LedgerHardwareWalletFactorSourceTests: Test { +final class LedgerHardwareWalletFactorSourceTests: FactorSourceTest { func test_id_of_ledger() { XCTAssertEqual(SUT.sample.id.description, FactorSourceID.hash(value: SUT.sample.id).description) } @@ -31,4 +31,18 @@ final class LedgerHardwareWalletFactorSourceTests: Test { + + func test_new_babylon() { + let sut = SUT.babylon(isMainBDFS: true, mnemonicWithPassphrase: .sample) + XCTAssertTrue(sut.supportsBabylon) + } + + func test_new_olympia() { + let sut = SUT.olympia(mnemonicWithPassphrase: .sample) + XCTAssertTrue(sut.supportsOlympia) + } + + func test_kind_is_device() { + XCTAssertEqual(SUT.olympia(mnemonicWithPassphrase: .sample).factorSourceKind, .device) + XCTAssertEqual(SUT.babylon(isMainBDFS: true, mnemonicWithPassphrase: .sample).factorSourceKind, .device) + } + + func test_is_main_bdfs_true() { + let sut = SUT.babylon(isMainBDFS: true, mnemonicWithPassphrase: .sample) + XCTAssertTrue(sut.factorSource.isMainBDFS) + } + + func test_is_main_bdfs_false() { + let sut = SUT.babylon(isMainBDFS: false, mnemonicWithPassphrase: .sample) + XCTAssertFalse(sut.factorSource.isMainBDFS) + } +} diff --git a/apple/Tests/TestCases/Profile/FactorSourceKindTests.swift b/apple/Tests/TestCases/Profile/FactorSourceKindTests.swift new file mode 100644 index 000000000..4e4f82fa2 --- /dev/null +++ b/apple/Tests/TestCases/Profile/FactorSourceKindTests.swift @@ -0,0 +1,34 @@ +import CustomDump +import Foundation +import Sargon +import SargonUniFFI +import XCTest + +final class FactorSourceKindTests: Test { + + func test_description_is_to_string() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.description, sut.toString()) + } + SUT.allCases.forEach(doTest) + } + + + func test_rawValue_is_to_string() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.rawValue, sut.toString()) + } + SUT.allCases.forEach(doTest) + } + + + func test_string_roundtrip() { + func doTest(_ sut: SUT) { + XCTAssertEqual( + SUT(rawValue: sut.rawValue)!, + sut + ) + } + SUT.allCases.forEach(doTest) + } +} diff --git a/apple/Tests/TestCases/Profile/Gateways/GatewayTests.swift b/apple/Tests/TestCases/Profile/Gateways/GatewayTests.swift index 269a6e227..088c12ff1 100644 --- a/apple/Tests/TestCases/Profile/Gateways/GatewayTests.swift +++ b/apple/Tests/TestCases/Profile/Gateways/GatewayTests.swift @@ -5,28 +5,53 @@ import SargonUniFFI import XCTest final class GatewayTests: Test { - func test_is_wellknown() { - XCTAssertTrue(SUT.mainnet.isWellknown) - XCTAssertTrue(SUT.stokenet.isWellknown) - XCTAssertFalse(SUT.forNetwork(id: .hammunet).isWellknown) - } - - func test_new_url_network_id() throws { - XCTAssertEqual( - SUT.mainnet, - try SUT( - url: "https://mainnet.radixdlt.com", - networkID: .mainnet - ) - ) - } - - func test_id() throws { - XCTAssertEqual(SUT.sample.id, SUT.sample.getID()) - } - - func test_description() throws { - XCTAssertEqual(SUT.sample.description, SUT.sample.toString()) - } - + func test_is_wellknown() { + XCTAssertTrue(SUT.mainnet.isWellknown) + XCTAssertTrue(SUT.stokenet.isWellknown) + XCTAssertFalse(SUT.forNetwork(id: .hammunet).isWellknown) + } + + func test_new_url_network_id() throws { + XCTAssertEqual( + SUT.mainnet, + try SUT( + url: "https://mainnet.radixdlt.com", + networkID: .mainnet + ) + ) + } + + func test_id() throws { + XCTAssertEqual(SUT.sample.id, SUT.sample.getID()) + } + + func test_description() throws { + XCTAssertEqual(SUT.sample.description, SUT.sample.toString()) + } + + func test_network_id_of_nebunet() { + XCTAssertEqual(SUT.nebunet.networkID, .nebunet) + } + + func test_network_id_of_kisharnet() { + XCTAssertEqual(SUT.kisharnet.networkID, .kisharnet) + } + + func test_network_id_of_ansharnet() { + XCTAssertEqual(SUT.ansharnet.networkID, .ansharnet) + } + + func test_network_id_of_hammunet() { + XCTAssertEqual(SUT.hammunet.networkID, .hammunet) + } + + func test_network_id_of_enkinet() { + XCTAssertEqual(SUT.enkinet.networkID, .enkinet) + } + + func test_network_id_of_mardunet() { + XCTAssertEqual(SUT.mardunet.networkID, .mardunet) + } + + } diff --git a/apple/Tests/TestCases/Profile/LedgerHardwareWalletModelTests.swift b/apple/Tests/TestCases/Profile/LedgerHardwareWalletModelTests.swift new file mode 100644 index 000000000..f90b79864 --- /dev/null +++ b/apple/Tests/TestCases/Profile/LedgerHardwareWalletModelTests.swift @@ -0,0 +1,41 @@ +// +// File.swift +// +// +// Created by Alexander Cyon on 2024-04-22. +// + +import CustomDump +import Foundation +import Sargon +import SargonUniFFI +import XCTest + +final class LedgerHardwareWalletModelTests: Test { + + func test_description_is_to_string() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.description, sut.toString()) + } + SUT.allCases.forEach(doTest) + } + + + func test_rawValue_is_to_string() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.rawValue, sut.toString()) + } + SUT.allCases.forEach(doTest) + } + + + func test_string_roundtrip() throws { + func doTest(_ sut: SUT) throws { + XCTAssertEqual( + try SUT(string: sut.rawValue), + sut + ) + } + try SUT.allCases.forEach(doTest) + } +} diff --git a/apple/Tests/Utils/AddressProtocolTests.swift b/apple/Tests/Utils/AddressProtocolTest.swift similarity index 100% rename from apple/Tests/Utils/AddressProtocolTests.swift rename to apple/Tests/Utils/AddressProtocolTest.swift diff --git a/apple/Tests/Utils/BinaryProtocolTests.swift b/apple/Tests/Utils/BinaryProtocolTest.swift similarity index 100% rename from apple/Tests/Utils/BinaryProtocolTests.swift rename to apple/Tests/Utils/BinaryProtocolTest.swift diff --git a/apple/Tests/Utils/EntityProtocolTests.swift b/apple/Tests/Utils/EntityProtocolTest.swift similarity index 100% rename from apple/Tests/Utils/EntityProtocolTests.swift rename to apple/Tests/Utils/EntityProtocolTest.swift diff --git a/apple/Tests/Utils/FactorSourceTest.swift b/apple/Tests/Utils/FactorSourceTest.swift new file mode 100644 index 000000000..d31d430fe --- /dev/null +++ b/apple/Tests/Utils/FactorSourceTest.swift @@ -0,0 +1,22 @@ +import CustomDump +import Foundation +import Sargon +import SargonUniFFI +import XCTest + +class FactorSourceTest: Test { + + func test_as_general_factorSourceID() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.asGeneral.factorSourceID, sut.factorSourceID) + } + SUT.allCases.forEach(doTest) + } + + func test_as_general_factorSourceKind() { + func doTest(_ sut: SUT) { + XCTAssertEqual(sut.asGeneral.factorSourceKind, sut.factorSourceKind) + } + SUT.allCases.forEach(doTest) + } +} diff --git a/apple/Tests/Utils/HDPathProtocolTests.swift b/apple/Tests/Utils/HDPathProtocolTest.swift similarity index 76% rename from apple/Tests/Utils/HDPathProtocolTests.swift rename to apple/Tests/Utils/HDPathProtocolTest.swift index a5ef46676..028977d42 100644 --- a/apple/Tests/Utils/HDPathProtocolTests.swift +++ b/apple/Tests/Utils/HDPathProtocolTest.swift @@ -4,7 +4,7 @@ import Sargon import SargonUniFFI import XCTest -class HDPathProtocolTests: Test { +class HDPathProtocolTest: Test { func test_string_roundtrip() throws { try SUT.allCases.forEach { XCTAssertNoDifference(try SUT(string: $0.description), $0) diff --git a/apple/Tests/Utils/IdentifiableByStringProtocolTests.swift b/apple/Tests/Utils/IdentifiableByStringProtocolTest.swift similarity index 100% rename from apple/Tests/Utils/IdentifiableByStringProtocolTests.swift rename to apple/Tests/Utils/IdentifiableByStringProtocolTest.swift diff --git a/apple/Tests/Utils/PersonaDataEntryTests.swift b/apple/Tests/Utils/PersonaDataEntryTest.swift similarity index 100% rename from apple/Tests/Utils/PersonaDataEntryTests.swift rename to apple/Tests/Utils/PersonaDataEntryTest.swift diff --git a/apple/Tests/Utils/TransactionHashProtocolTests.swift b/apple/Tests/Utils/TransactionHashProtocolTest.swift similarity index 100% rename from apple/Tests/Utils/TransactionHashProtocolTests.swift rename to apple/Tests/Utils/TransactionHashProtocolTest.swift diff --git a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/PrivateHierarchicalDeterministicFactorSource.kt b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/PrivateHierarchicalDeterministicFactorSource.kt index dcdf76693..b05985b63 100644 --- a/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/PrivateHierarchicalDeterministicFactorSource.kt +++ b/jvm/sargon-android/src/main/java/com/radixdlt/sargon/extensions/PrivateHierarchicalDeterministicFactorSource.kt @@ -3,10 +3,11 @@ package com.radixdlt.sargon.extensions import com.radixdlt.sargon.NonEmptyMax32Bytes import com.radixdlt.sargon.PrivateHierarchicalDeterministicFactorSource import com.radixdlt.sargon.WalletClientModel -import com.radixdlt.sargon.newPrivateHdFactorSource +import com.radixdlt.sargon.newPrivateHdFactorSourceBabylon @Throws(SargonException::class) fun PrivateHierarchicalDeterministicFactorSource.Companion.init( + isMainBDFS: Boolean, entropy: NonEmptyMax32Bytes, walletClientModel: WalletClientModel -) = newPrivateHdFactorSource(entropy = entropy, walletClientModel = walletClientModel) \ No newline at end of file +) = newPrivateHdFactorSourceBabylon(isMain = isMainBDFS, entropy = entropy, walletClientModel = walletClientModel) \ No newline at end of file diff --git a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/ProfileTest.kt b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/ProfileTest.kt index b3f6f7638..6519c1403 100644 --- a/jvm/sargon-android/src/test/java/com/radixdlt/sargon/ProfileTest.kt +++ b/jvm/sargon-android/src/test/java/com/radixdlt/sargon/ProfileTest.kt @@ -18,6 +18,7 @@ class ProfileTest: SampleTestable { @Test fun testInit() { val hdFactorSource = PrivateHierarchicalDeterministicFactorSource.init( + isMainBDFS = true, entropy = NonEmptyMax32Bytes(bagOfBytes = randomBagOfBytes(byteCount = 32)), walletClientModel = WalletClientModel.ANDROID ) diff --git a/src/core/error/common_error.rs b/src/core/error/common_error.rs index b76ee2145..bdec5d1fc 100644 --- a/src/core/error/common_error.rs +++ b/src/core/error/common_error.rs @@ -445,6 +445,12 @@ pub enum CommonError { #[error("Invalid AES Sealedbox, too few bytes expected at least: {expected_at_least}, found: {found}.")] InvalidAESBytesTooShort { expected_at_least: u64, found: u64 } = 10124, + + #[error("Invalid Factor Source kind, bad value: {bad_value}")] + InvalidFactorSourceKind { bad_value: String } = 10125, + + #[error("Invalid LedgerHardwareWalletModel, bad value: {bad_value}")] + InvalidLedgerHardwareWalletModel { bad_value: String } = 10126, } #[uniffi::export] diff --git a/src/core/hash.rs b/src/core/hash.rs index 6ea466bca..e273ca912 100644 --- a/src/core/hash.rs +++ b/src/core/hash.rs @@ -62,8 +62,17 @@ impl AsRef<[u8]> for Hash { self.secret_magic.0.as_ref() } } + +impl From for Hash { + /// Instantiates a new `Hash` from the `Exactly32Bytes` + fn from(value: Exactly32Bytes) -> Self { + let scrypto = ScryptoHash(*value.bytes()); + Self::from(scrypto) + } +} + impl From for Exactly32Bytes { - /// Instantiates a new `ExactlyNBytes` from the `Hash` (N bytes). + /// Instantiates a new `Exactly32Bytes` from the `Hash` fn from(value: Hash) -> Self { Self::from(&value.into_bytes()) } @@ -125,6 +134,12 @@ mod tests { assert_eq!(SUT::sample_other(), SUT::sample_other()); } + #[test] + fn test_from_exactly32() { + let bytes = Exactly32Bytes::sample(); + assert_eq!(SUT::from(bytes.clone()).bytes(), bytes.to_vec()) + } + #[test] fn inequality() { assert_ne!(SUT::sample(), SUT::sample_other()); diff --git a/src/core/hash_uniffi_fn.rs b/src/core/hash_uniffi_fn.rs index 726c16b74..2505a3bf7 100644 --- a/src/core/hash_uniffi_fn.rs +++ b/src/core/hash_uniffi_fn.rs @@ -15,6 +15,16 @@ pub fn new_hash_sample_other() -> Hash { Hash::sample_other() } +#[uniffi::export] +pub fn new_hash_from_bytes(bytes: Exactly32Bytes) -> Hash { + Hash::from(bytes) +} + +#[uniffi::export] +pub fn new_hash_from_string(string: String) -> Result { + Exactly32Bytes::from_str(&string).map(Hash::from) +} + #[cfg(test)] mod tests { use super::*; @@ -44,4 +54,24 @@ mod tests { "48f1bd08444b5e713db9e14caac2faae71836786ac94d645b00679728202a935" ); } + + #[test] + fn test_from_str() { + let sut = new_hash_from_string( + "48f1bd08444b5e713db9e14caac2faae71836786ac94d645b00679728202a935" + .to_owned(), + ) + .unwrap(); + assert_eq!(sut, SUT::sample()) + } + + #[test] + fn test_from_bytes() { + let bytes = Exactly32Bytes::from_str( + "48f1bd08444b5e713db9e14caac2faae71836786ac94d645b00679728202a935", + ) + .unwrap(); + let sut = new_hash_from_bytes(bytes); + assert_eq!(sut, SUT::sample()) + } } diff --git a/src/hierarchical_deterministic/bip39/bip39_word/bip39_language.rs b/src/hierarchical_deterministic/bip39/bip39_word/bip39_language.rs index e42b0b769..4bbed8550 100644 --- a/src/hierarchical_deterministic/bip39/bip39_word/bip39_language.rs +++ b/src/hierarchical_deterministic/bip39/bip39_word/bip39_language.rs @@ -1,3 +1,5 @@ +use crate::prelude::*; + /// Language to be used for the mnemonic phrase. /// /// The English language is always available, other languages are enabled using @@ -17,6 +19,9 @@ pub enum BIP39Language { /// The English language. English, + + /// The French language. + French, } impl Default for BIP39Language { @@ -25,11 +30,35 @@ impl Default for BIP39Language { } } +impl HasSampleValues for BIP39Language { + fn sample() -> Self { + Self::English + } + + fn sample_other() -> Self { + Self::French + } +} + +impl BIP39Language { + pub fn wordlist(&self) -> Vec { + let language: bip39::Language = (*self).into(); + let word_list = language.word_list(); + + word_list + .iter() + .map(|w| BIP39Word::new(w, *self)) + .collect::, CommonError>>() + .expect("Crate bip39 generated words unknown to us.") + } +} + impl From for BIP39Language { fn from(value: bip39::Language) -> Self { use bip39::Language::*; match value { English => Self::English, + French => Self::French, } } } @@ -38,30 +67,63 @@ impl From for bip39::Language { use bip39::Language::*; match value { BIP39Language::English => English, + BIP39Language::French => French, } } } #[cfg(test)] mod tests { - use super::BIP39Language; + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = BIP39Language; + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + + #[test] + fn inequality() { + assert_ne!(SUT::sample(), SUT::sample_other()); + } #[test] fn default_is_english() { - assert_eq!(BIP39Language::default(), BIP39Language::English); + assert_eq!(SUT::default(), SUT::English); + } + + #[test] + fn test_wordlist_english() { + let wordlist = SUT::English.wordlist(); + assert_eq!(wordlist.into_iter().last().unwrap().word, "zoo"); + } + + #[test] + fn test_wordlist_french() { + let wordlist = SUT::French.wordlist(); + assert_eq!(wordlist.into_iter().last().unwrap().word, "zoologie"); } #[test] - fn into() { - assert_eq!(BIP39Language::English, bip39::Language::English.into()); + fn to_from_bip39_english() { + assert_eq!(SUT::English, bip39::Language::English.into()); assert_eq!( - bip39::Language::from(BIP39Language::English), + bip39::Language::from(SUT::English), bip39::Language::English ); } + #[test] + fn to_from_bip39_french() { + assert_eq!(SUT::French, bip39::Language::French.into()); + assert_eq!(bip39::Language::from(SUT::French), bip39::Language::French); + } + #[test] fn display() { - assert_eq!(format!("{}", BIP39Language::English), "English"); + assert_eq!(format!("{}", SUT::English), "English"); } } diff --git a/src/hierarchical_deterministic/bip39/bip39_word/bip39_language_uniffi_fn.rs b/src/hierarchical_deterministic/bip39/bip39_word/bip39_language_uniffi_fn.rs new file mode 100644 index 000000000..12223e72a --- /dev/null +++ b/src/hierarchical_deterministic/bip39/bip39_word/bip39_language_uniffi_fn.rs @@ -0,0 +1,47 @@ +use crate::prelude::*; + +#[uniffi::export] +pub fn new_bip39_language_sample() -> BIP39Language { + BIP39Language::sample() +} + +#[uniffi::export] +pub fn new_bip39_language_sample_other() -> BIP39Language { + BIP39Language::sample_other() +} + +#[uniffi::export] +pub fn bip39_language_wordlist(language: &BIP39Language) -> Vec { + language.wordlist() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = BIP39Language; + + #[test] + fn hash_of_samples() { + assert_eq!( + HashSet::::from_iter([ + new_bip39_language_sample(), + new_bip39_language_sample_other(), + // duplicates should get removed + new_bip39_language_sample(), + new_bip39_language_sample_other(), + ]) + .len(), + 2 + ); + } + + #[test] + fn test_word_list() { + assert_eq!( + bip39_language_wordlist(&SUT::sample()), + SUT::sample().wordlist() + ) + } +} diff --git a/src/hierarchical_deterministic/bip39/bip39_word/mod.rs b/src/hierarchical_deterministic/bip39/bip39_word/mod.rs index 6238abd93..b8667effc 100644 --- a/src/hierarchical_deterministic/bip39/bip39_word/mod.rs +++ b/src/hierarchical_deterministic/bip39/bip39_word/mod.rs @@ -1,7 +1,9 @@ mod bip39_language; +mod bip39_language_uniffi_fn; mod bip39_word; mod u11; pub use bip39_language::*; +pub use bip39_language_uniffi_fn::*; pub use bip39_word::*; pub use u11::*; diff --git a/src/hierarchical_deterministic/bip39/bip39_word_count.rs b/src/hierarchical_deterministic/bip39/bip39_word_count.rs index e15d71fd4..9c384e8b0 100644 --- a/src/hierarchical_deterministic/bip39/bip39_word_count.rs +++ b/src/hierarchical_deterministic/bip39/bip39_word_count.rs @@ -14,6 +14,7 @@ use crate::prelude::*; Eq, Hash, PartialOrd, + enum_iterator::Sequence, Ord, uniffi::Enum, )] @@ -42,6 +43,11 @@ impl std::fmt::Display for BIP39WordCount { } impl BIP39WordCount { + /// Returns collection of all word counts + pub fn all() -> Vec { + all::().collect() + } + /// The raw representation of the word count as a number. pub fn discriminant(&self) -> u8 { *self as u8 @@ -71,46 +77,60 @@ impl Default for BIP39WordCount { #[cfg(test)] mod tests { - use crate::prelude::*; + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = BIP39WordCount; #[test] fn default_is_24() { - assert_eq!(BIP39WordCount::default(), BIP39WordCount::TwentyFour); + assert_eq!(SUT::default(), SUT::TwentyFour); + } + + #[test] + fn test_all() { + assert_eq!( + SUT::all(), + vec![ + SUT::TwentyFour, + SUT::TwentyOne, + SUT::Eighteen, + SUT::Fifteen, + SUT::Twelve, + ] + ) } #[test] fn discriminant() { - assert_eq!(BIP39WordCount::TwentyFour.discriminant(), 24); - assert_eq!(BIP39WordCount::TwentyOne.discriminant(), 21); - assert_eq!(BIP39WordCount::Eighteen.discriminant(), 18); - assert_eq!(BIP39WordCount::Fifteen.discriminant(), 15); - assert_eq!(BIP39WordCount::Twelve.discriminant(), 12); + assert_eq!(SUT::TwentyFour.discriminant(), 24); + assert_eq!(SUT::TwentyOne.discriminant(), 21); + assert_eq!(SUT::Eighteen.discriminant(), 18); + assert_eq!(SUT::Fifteen.discriminant(), 15); + assert_eq!(SUT::Twelve.discriminant(), 12); } #[test] fn format() { - assert_eq!(format!("{}", BIP39WordCount::TwentyFour), "24 words"); - assert_eq!(format!("{}", BIP39WordCount::Twelve), "12 words"); + assert_eq!(format!("{}", SUT::TwentyFour), "24 words"); + assert_eq!(format!("{}", SUT::Twelve), "12 words"); } #[test] fn equality() { - assert_eq!(BIP39WordCount::TwentyFour, BIP39WordCount::TwentyFour); - assert_eq!(BIP39WordCount::Twelve, BIP39WordCount::Twelve); + assert_eq!(SUT::TwentyFour, SUT::TwentyFour); + assert_eq!(SUT::Twelve, SUT::Twelve); } #[test] fn inequality() { - assert_ne!(BIP39WordCount::TwentyFour, BIP39WordCount::Twelve); + assert_ne!(SUT::TwentyFour, SUT::Twelve); } #[test] fn hash() { assert_eq!( - BTreeSet::from_iter( - [BIP39WordCount::TwentyFour, BIP39WordCount::TwentyFour] - .into_iter() - ) - .len(), + BTreeSet::from_iter([SUT::TwentyFour, SUT::TwentyFour].into_iter()) + .len(), 1 ); } @@ -118,26 +138,20 @@ mod tests { #[test] fn invalid_word_count_error() { assert_eq!( - BIP39WordCount::from_count(23), + SUT::from_count(23), Err(CommonError::InvalidBIP39WordCount { bad_value: 23 }) ) } #[test] fn ord() { - assert!(BIP39WordCount::Twelve < BIP39WordCount::TwentyFour); + assert!(SUT::Twelve < SUT::TwentyFour); } #[test] fn json_roundtrip() { - assert_json_value_eq_after_roundtrip( - &BIP39WordCount::TwentyFour, - json!(24), - ); - assert_json_value_ne_after_roundtrip( - &BIP39WordCount::TwentyFour, - json!(12), - ); - assert_json_roundtrip(&BIP39WordCount::TwentyFour); + assert_json_value_eq_after_roundtrip(&SUT::TwentyFour, json!(24)); + assert_json_value_ne_after_roundtrip(&SUT::TwentyFour, json!(12)); + assert_json_roundtrip(&SUT::TwentyFour); } } diff --git a/src/hierarchical_deterministic/bip39/bip39_word_count_uniffi_fn.rs b/src/hierarchical_deterministic/bip39/bip39_word_count_uniffi_fn.rs new file mode 100644 index 000000000..a4a56cd40 --- /dev/null +++ b/src/hierarchical_deterministic/bip39/bip39_word_count_uniffi_fn.rs @@ -0,0 +1,16 @@ +use crate::prelude::*; + +#[uniffi::export] +pub fn bip39_word_count_all() -> Vec { + BIP39WordCount::all() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_bip39_word_count_all() { + assert_eq!(bip39_word_count_all().len(), 5); + } +} diff --git a/src/hierarchical_deterministic/bip39/mod.rs b/src/hierarchical_deterministic/bip39/mod.rs index 6908390c9..12377c8f1 100644 --- a/src/hierarchical_deterministic/bip39/mod.rs +++ b/src/hierarchical_deterministic/bip39/mod.rs @@ -3,6 +3,7 @@ mod bip39_passphrase; mod bip39_seed; mod bip39_word; mod bip39_word_count; +mod bip39_word_count_uniffi_fn; mod mnemonic; mod mnemonic_uniffi_fn; @@ -11,5 +12,6 @@ pub use bip39_passphrase::*; pub use bip39_seed::*; pub use bip39_word::*; pub use bip39_word_count::*; +pub use bip39_word_count_uniffi_fn::*; pub use mnemonic::*; pub use mnemonic_uniffi_fn::*; diff --git a/src/hierarchical_deterministic/derivation/derivation_path.rs b/src/hierarchical_deterministic/derivation/derivation_path.rs index ccf61c17d..2c92d3849 100644 --- a/src/hierarchical_deterministic/derivation/derivation_path.rs +++ b/src/hierarchical_deterministic/derivation/derivation_path.rs @@ -31,6 +31,13 @@ impl TryFrom<&HDPath> for DerivationPath { } } +impl FromStr for DerivationPath { + type Err = CommonError; + fn from_str(s: &str) -> Result { + HDPath::from_str(s).and_then(|p| Self::try_from(&p)) + } +} + impl<'de> serde::Deserialize<'de> for DerivationPath { /// Tries to deserializes a JSON string as a bech32 address into an `AccountAddress`. #[cfg(not(tarpaulin_include))] // false negative @@ -337,6 +344,18 @@ mod tests { ); } + #[test] + fn test_from_str_bip44() { + let s = "m/44H/1022H/0H/0/0H"; + assert_eq!(SUT::from_str(s).unwrap(), BIP44LikePath::sample().into()) + } + + #[test] + fn test_from_str_cap26_account_path() { + let s = "m/44H/1022H/1H/525H/1460H/0H"; + assert_eq!(SUT::from_str(s).unwrap(), AccountPath::sample().into()) + } + #[test] fn display() { let model = SUT::sample(); diff --git a/src/hierarchical_deterministic/derivation/derivation_path_uniffi_fn.rs b/src/hierarchical_deterministic/derivation/derivation_path_uniffi_fn.rs index 06853fe55..2096e6890 100644 --- a/src/hierarchical_deterministic/derivation/derivation_path_uniffi_fn.rs +++ b/src/hierarchical_deterministic/derivation/derivation_path_uniffi_fn.rs @@ -11,8 +11,10 @@ pub fn new_derivation_path_sample_other() -> DerivationPath { } #[uniffi::export] -pub fn derivation_path_to_string(path: &DerivationPath) -> String { - path.to_string() +pub fn new_derivation_path_from_string( + string: String, +) -> Result { + DerivationPath::from_str(&string) } #[uniffi::export] @@ -20,6 +22,11 @@ pub fn derivation_path_to_hd_path(path: &DerivationPath) -> HDPath { path.hd_path().clone() } +#[uniffi::export] +pub fn derivation_path_to_string(path: &DerivationPath) -> String { + path.to_string() +} + #[cfg(test)] mod tests { use super::*; @@ -27,6 +34,15 @@ mod tests { #[allow(clippy::upper_case_acronyms)] type SUT = DerivationPath; + #[test] + fn test_from_str_cap26_account_path() { + let s = "m/44H/1022H/1H/525H/1460H/0H"; + assert_eq!( + new_derivation_path_from_string(s.to_owned()).unwrap(), + SUT::from(AccountPath::sample()) + ) + } + #[test] fn hash_of_samples() { assert_eq!( diff --git a/src/profile/v100/app_preferences/app_preferences.rs b/src/profile/v100/app_preferences/app_preferences.rs index f37a9a2a8..414ecfb43 100644 --- a/src/profile/v100/app_preferences/app_preferences.rs +++ b/src/profile/v100/app_preferences/app_preferences.rs @@ -57,16 +57,6 @@ impl AppPreferences { } } -#[uniffi::export] -pub fn new_app_preferences_sample() -> AppPreferences { - AppPreferences::sample() -} - -#[uniffi::export] -pub fn new_app_preferences_sample_other() -> AppPreferences { - AppPreferences::sample_other() -} - impl AppPreferences { pub fn new( display: AppDisplay, @@ -111,53 +101,50 @@ impl HasSampleValues for AppPreferences { #[cfg(test)] mod tests { - use crate::prelude::*; + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = AppPreferences; #[test] fn equality() { - assert_eq!(AppPreferences::sample(), AppPreferences::sample()); - assert_eq!( - AppPreferences::sample_other(), - AppPreferences::sample_other() - ); + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); } #[test] fn inequality() { - assert_ne!(AppPreferences::sample(), AppPreferences::sample_other()); + assert_ne!(SUT::sample(), SUT::sample_other()); } #[test] fn get_display() { - assert_eq!(AppPreferences::sample().display, AppDisplay::sample()) + assert_eq!(SUT::sample().display, AppDisplay::sample()) } #[test] fn get_gateways() { - assert_eq!(AppPreferences::sample().gateways, Gateways::sample()) + assert_eq!(SUT::sample().gateways, Gateways::sample()) } #[test] fn get_p2p_links() { - assert_eq!(AppPreferences::sample().p2p_links, P2PLinks::sample()) + assert_eq!(SUT::sample().p2p_links, P2PLinks::sample()) } #[test] fn get_security() { - assert_eq!(AppPreferences::sample().security, Security::sample()) + assert_eq!(SUT::sample().security, Security::sample()) } #[test] fn get_transaction() { - assert_eq!( - AppPreferences::sample().transaction, - TransactionPreferences::sample() - ) + assert_eq!(SUT::sample().transaction, TransactionPreferences::sample()) } #[test] fn json_roundtrip() { - let sut = AppPreferences::sample(); + let sut = SUT::sample(); assert_eq_after_json_roundtrip( &sut, r#" @@ -218,22 +205,3 @@ mod tests { ) } } - -#[cfg(test)] -mod uniffi_tests { - use crate::{ - new_app_preferences_sample, new_app_preferences_sample_other, - HasSampleValues, - }; - - use super::AppPreferences; - - #[test] - fn equality_samples() { - assert_eq!(AppPreferences::sample(), new_app_preferences_sample()); - assert_eq!( - AppPreferences::sample_other(), - new_app_preferences_sample_other() - ); - } -} diff --git a/src/profile/v100/app_preferences/app_preferences_uniffi_fn.rs b/src/profile/v100/app_preferences/app_preferences_uniffi_fn.rs new file mode 100644 index 000000000..d78dfe1d6 --- /dev/null +++ b/src/profile/v100/app_preferences/app_preferences_uniffi_fn.rs @@ -0,0 +1,36 @@ +use crate::prelude::*; + +#[uniffi::export] +pub fn new_app_preferences_sample() -> AppPreferences { + AppPreferences::sample() +} + +#[uniffi::export] +pub fn new_app_preferences_sample_other() -> AppPreferences { + AppPreferences::sample_other() +} + +#[uniffi::export] +pub fn new_app_preferences_default() -> AppPreferences { + AppPreferences::default() +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = AppPreferences; + + #[test] + fn equality_samples() { + assert_eq!(SUT::sample(), new_app_preferences_sample()); + assert_eq!(SUT::sample_other(), new_app_preferences_sample_other()); + } + + #[test] + fn test_default() { + assert_eq!(new_app_preferences_default(), AppPreferences::default()); + } +} diff --git a/src/profile/v100/app_preferences/mod.rs b/src/profile/v100/app_preferences/mod.rs index c2dac6310..f00e086e9 100644 --- a/src/profile/v100/app_preferences/mod.rs +++ b/src/profile/v100/app_preferences/mod.rs @@ -1,5 +1,6 @@ mod app_display_settings; mod app_preferences; +mod app_preferences_uniffi_fn; mod gateways; mod p2p_links; mod security; @@ -7,6 +8,7 @@ mod transaction_preferences; pub use app_display_settings::*; pub use app_preferences::*; +pub use app_preferences_uniffi_fn::*; pub use gateways::*; pub use p2p_links::*; pub use security::*; diff --git a/src/profile/v100/app_preferences/transaction_preferences.rs b/src/profile/v100/app_preferences/transaction_preferences.rs index bf54d5948..e41c5905f 100644 --- a/src/profile/v100/app_preferences/transaction_preferences.rs +++ b/src/profile/v100/app_preferences/transaction_preferences.rs @@ -32,10 +32,10 @@ impl TransactionPreferences { } impl Default for TransactionPreferences { - /// By default `1.0` is used. + /// By default `0.99` is used. fn default() -> Self { Self { - default_deposit_guarantee: Decimal192::one(), + default_deposit_guarantee: Decimal192::try_from(0.99).unwrap(), } } } @@ -96,12 +96,12 @@ mod tests { } #[test] - fn default_is_1() { + fn default_is_99_percent() { assert_eq!( TransactionPreferences::default() .default_deposit_guarantee .to_string(), - "1" + "0.99" ); } } diff --git a/src/profile/v100/factors/factor_source.rs b/src/profile/v100/factors/factor_source.rs index d58e21dc0..d9c184014 100644 --- a/src/profile/v100/factors/factor_source.rs +++ b/src/profile/v100/factors/factor_source.rs @@ -28,6 +28,13 @@ pub enum FactorSource { } impl BaseIsFactorSource for FactorSource { + fn common_properties(&self) -> FactorSourceCommon { + match self { + FactorSource::Device { value } => value.common_properties(), + FactorSource::Ledger { value } => value.common_properties(), + } + } + fn factor_source_kind(&self) -> FactorSourceKind { match self { FactorSource::Device { value } => value.factor_source_kind(), @@ -139,23 +146,39 @@ impl FactorSource { #[cfg(test)] mod tests { - use crate::prelude::*; + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = FactorSource; #[test] fn equality() { - assert_eq!(FactorSource::sample(), FactorSource::sample()); - assert_eq!(FactorSource::sample_other(), FactorSource::sample_other()); + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); } #[test] fn inequality() { - assert_ne!(FactorSource::sample(), FactorSource::sample_other()); + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn device_common_properties() { + assert_eq!( + SUT::sample().common_properties(), + DeviceFactorSource::sample_babylon().common + ); + + assert_eq!( + SUT::sample_other().common_properties(), + LedgerHardwareWalletFactorSource::sample().common + ) } #[test] fn factor_source_id_device() { assert_eq!( - FactorSource::sample_device().factor_source_id(), + SUT::sample_device().factor_source_id(), DeviceFactorSource::sample().factor_source_id() ); } @@ -163,7 +186,7 @@ mod tests { #[test] fn factor_source_id_ledger() { assert_eq!( - FactorSource::sample_ledger().factor_source_id(), + SUT::sample_ledger().factor_source_id(), LedgerHardwareWalletFactorSource::sample().factor_source_id() ); } @@ -171,7 +194,7 @@ mod tests { #[test] fn factor_source_kind_device() { assert_eq!( - FactorSource::sample_device().factor_source_kind(), + SUT::sample_device().factor_source_kind(), FactorSourceKind::Device ); } @@ -179,17 +202,17 @@ mod tests { #[test] fn factor_source_kind_ledger() { assert_eq!( - FactorSource::sample_ledger().factor_source_kind(), + SUT::sample_ledger().factor_source_kind(), FactorSourceKind::LedgerHQHardwareWallet ); } #[test] fn into_from_device() { - let factor_source: FactorSource = DeviceFactorSource::sample().into(); + let factor_source: SUT = DeviceFactorSource::sample().into(); assert_eq!( factor_source, - FactorSource::Device { + SUT::Device { value: DeviceFactorSource::sample() } ); @@ -197,11 +220,11 @@ mod tests { #[test] fn into_from_ledger() { - let factor_source: FactorSource = + let factor_source: SUT = LedgerHardwareWalletFactorSource::sample().into(); assert_eq!( factor_source, - FactorSource::Ledger { + SUT::Ledger { value: LedgerHardwareWalletFactorSource::sample() } ); @@ -209,7 +232,7 @@ mod tests { #[test] fn json_roundtrip_device() { - let model = FactorSource::sample_device(); + let model = SUT::sample_device(); assert_eq_after_json_roundtrip( &model, r#" @@ -242,7 +265,7 @@ mod tests { #[test] fn json_roundtrip_ledger() { - let model = FactorSource::sample_ledger(); + let model = SUT::sample_ledger(); assert_eq_after_json_roundtrip( &model, r#" diff --git a/src/profile/v100/factors/factor_source_common.rs b/src/profile/v100/factors/factor_source_common.rs index 304150d08..7f14e791f 100644 --- a/src/profile/v100/factors/factor_source_common.rs +++ b/src/profile/v100/factors/factor_source_common.rs @@ -69,6 +69,10 @@ impl FactorSourceCommon { Self::with_values(crypto_parameters, date, date, flags) } + pub fn new_olympia() -> Self { + Self::new(FactorSourceCryptoParameters::olympia(), Vec::new()) + } + pub fn new_bdfs(is_main: bool) -> Self { Self::new( FactorSourceCryptoParameters::babylon(), @@ -88,6 +92,10 @@ impl FactorSourceCommon { self.crypto_parameters.supports_babylon() } + pub fn supports_olympia(&self) -> bool { + self.crypto_parameters.supports_olympia() + } + /// Checks if its Main Babylon Device Factor Source (BDFS). pub fn is_main_bdfs(&self) -> bool { self.supports_babylon() && self.flags.contains(&FactorSourceFlag::Main) diff --git a/src/profile/v100/factors/factor_source_crypto_parameters.rs b/src/profile/v100/factors/factor_source_crypto_parameters.rs index 68efbaeda..e91c87807 100644 --- a/src/profile/v100/factors/factor_source_crypto_parameters.rs +++ b/src/profile/v100/factors/factor_source_crypto_parameters.rs @@ -74,6 +74,13 @@ impl FactorSourceCryptoParameters { .expect("Valid Babylon and Olympia parameters") } + pub fn supports_olympia(&self) -> bool { + self.supported_curves.contains(&SLIP10Curve::Secp256k1) + && self + .supported_derivation_path_schemes + .contains(&DerivationPathScheme::Bip44Olympia) + } + pub fn supports_babylon(&self) -> bool { self.supported_curves.contains(&SLIP10Curve::Curve25519) && self @@ -157,6 +164,24 @@ mod tests { ); } + #[test] + fn babylon_olympia_compat_supports_olympia_and_babylon() { + assert!(SUT::babylon_olympia_compatible().supports_babylon()); + assert!(SUT::babylon_olympia_compatible().supports_olympia()); + } + + #[test] + fn olympia_supports_olympia_but_not_babylon() { + assert!(SUT::olympia().supports_olympia()); + assert!(!SUT::olympia().supports_babylon()); + } + + #[test] + fn babylon_supports_babylon_but_not_olympia() { + assert!(SUT::babylon().supports_babylon()); + assert!(!SUT::babylon().supports_olympia()); + } + #[test] fn babylon_olympia_compat_has_cap26_as_first_derivation_path_scheme() { assert_eq!( diff --git a/src/profile/v100/factors/factor_source_kind.rs b/src/profile/v100/factors/factor_source_kind.rs index ef4246459..fbbfba0e7 100644 --- a/src/profile/v100/factors/factor_source_kind.rs +++ b/src/profile/v100/factors/factor_source_kind.rs @@ -82,6 +82,16 @@ impl FactorSourceKind { } } +impl HasSampleValues for FactorSourceKind { + fn sample() -> Self { + Self::Device + } + + fn sample_other() -> Self { + Self::LedgerHQHardwareWallet + } +} + impl std::fmt::Display for FactorSourceKind { #[cfg(not(tarpaulin_include))] // false negative fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -89,88 +99,105 @@ impl std::fmt::Display for FactorSourceKind { } } +impl FromStr for FactorSourceKind { + type Err = CommonError; + fn from_str(s: &str) -> Result { + let value = serde_json::Value::String(s.to_owned()); + serde_json::from_value(value).map_err(|_| { + CommonError::InvalidFactorSourceKind { + bad_value: s.to_owned(), + } + }) + } +} + #[cfg(test)] mod tests { - use crate::prelude::*; + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = FactorSourceKind; #[test] - fn equality() { - assert_eq!(FactorSourceKind::Device, FactorSourceKind::Device); + fn string_roundtrip() { + use FactorSourceKind::*; + let eq = |f: SUT, s| { + assert_eq!(f.to_string(), s); + assert_eq!(SUT::from_str(s).unwrap(), f); + }; + + eq(Device, "device"); + eq(LedgerHQHardwareWallet, "ledgerHQHardwareWallet"); + eq(OffDeviceMnemonic, "offDeviceMnemonic"); + eq(TrustedContact, "trustedContact"); + eq(SecurityQuestions, "securityQuestions"); + } + + #[test] + fn from_str_err() { + let s = "invalid factor source kind!"; assert_eq!( - FactorSourceKind::LedgerHQHardwareWallet, - FactorSourceKind::LedgerHQHardwareWallet + SUT::from_str(s), + Err(CommonError::InvalidFactorSourceKind { + bad_value: s.to_owned(), + }) ); } + + #[test] + fn equality() { + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); + } + #[test] fn inequality() { - assert_ne!( - FactorSourceKind::Device, - FactorSourceKind::LedgerHQHardwareWallet - ); + assert_ne!(SUT::sample(), SUT::sample_other()); } #[test] fn hash() { assert_eq!( - BTreeSet::from_iter( - [FactorSourceKind::Device, FactorSourceKind::Device] - .into_iter() - ) - .len(), + BTreeSet::from_iter([SUT::Device, SUT::Device].into_iter()).len(), 1 ); } #[test] fn ord() { - assert!(FactorSourceKind::Device < FactorSourceKind::TrustedContact); + assert!(SUT::Device < SUT::TrustedContact); } #[test] fn discriminant() { - assert_eq!(FactorSourceKind::Device.discriminant(), "device"); - assert_eq!( - FactorSourceKind::SecurityQuestions.discriminant(), - "securityQuestions" - ); + assert_eq!(SUT::Device.discriminant(), "device"); + assert_eq!(SUT::SecurityQuestions.discriminant(), "securityQuestions"); assert_eq!( - FactorSourceKind::LedgerHQHardwareWallet.discriminant(), + SUT::LedgerHQHardwareWallet.discriminant(), "ledgerHQHardwareWallet" ); - assert_eq!( - FactorSourceKind::OffDeviceMnemonic.discriminant(), - "offDeviceMnemonic" - ); + assert_eq!(SUT::OffDeviceMnemonic.discriminant(), "offDeviceMnemonic"); - assert_eq!( - FactorSourceKind::TrustedContact.discriminant(), - "trustedContact" - ); + assert_eq!(SUT::TrustedContact.discriminant(), "trustedContact"); } #[test] fn display() { + assert_eq!(format!("{}", SUT::Device.discriminant()), "device"); assert_eq!( - format!("{}", FactorSourceKind::Device.discriminant()), - "device" - ); - assert_eq!( - format!( - "{}", - FactorSourceKind::LedgerHQHardwareWallet.discriminant() - ), + format!("{}", SUT::LedgerHQHardwareWallet.discriminant()), "ledgerHQHardwareWallet" ); assert_eq!( - format!("{}", FactorSourceKind::SecurityQuestions.discriminant()), + format!("{}", SUT::SecurityQuestions.discriminant()), "securityQuestions" ); assert_eq!( - format!("{}", FactorSourceKind::OffDeviceMnemonic.discriminant()), + format!("{}", SUT::OffDeviceMnemonic.discriminant()), "offDeviceMnemonic" ); assert_eq!( - format!("{}", FactorSourceKind::TrustedContact.discriminant()), + format!("{}", SUT::TrustedContact.discriminant()), "trustedContact" ); } @@ -178,25 +205,22 @@ mod tests { #[test] fn json_roundtrip() { assert_json_value_eq_after_roundtrip( - &FactorSourceKind::TrustedContact, + &SUT::TrustedContact, json!("trustedContact"), ); + assert_json_value_eq_after_roundtrip(&SUT::Device, json!("device")); assert_json_value_eq_after_roundtrip( - &FactorSourceKind::Device, - json!("device"), - ); - assert_json_value_eq_after_roundtrip( - &FactorSourceKind::SecurityQuestions, + &SUT::SecurityQuestions, json!("securityQuestions"), ); assert_json_value_eq_after_roundtrip( - &FactorSourceKind::LedgerHQHardwareWallet, + &SUT::LedgerHQHardwareWallet, json!("ledgerHQHardwareWallet"), ); assert_json_value_eq_after_roundtrip( - &FactorSourceKind::OffDeviceMnemonic, + &SUT::OffDeviceMnemonic, json!("offDeviceMnemonic"), ); - assert_json_roundtrip(&FactorSourceKind::Device); + assert_json_roundtrip(&SUT::Device); } } diff --git a/src/profile/v100/factors/factor_source_kind_uniffi_fn.rs b/src/profile/v100/factors/factor_source_kind_uniffi_fn.rs new file mode 100644 index 000000000..3d16e99fb --- /dev/null +++ b/src/profile/v100/factors/factor_source_kind_uniffi_fn.rs @@ -0,0 +1,54 @@ +use crate::prelude::*; + +#[uniffi::export] +pub fn new_factor_source_kind_sample() -> FactorSourceKind { + FactorSourceKind::sample() +} + +#[uniffi::export] +pub fn new_factor_source_kind_sample_other() -> FactorSourceKind { + FactorSourceKind::sample_other() +} + +#[uniffi::export] +pub fn new_factor_source_kind_from_string( + string: String, +) -> Result { + FactorSourceKind::from_str(&string) +} + +#[uniffi::export] +pub fn factor_source_kind_to_string(kind: FactorSourceKind) -> String { + kind.to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = FactorSourceKind; + + #[test] + fn hash_of_samples() { + assert_eq!( + HashSet::::from_iter([ + new_factor_source_kind_sample(), + new_factor_source_kind_sample_other(), + // duplicates should get removed + new_factor_source_kind_sample(), + new_factor_source_kind_sample_other(), + ]) + .len(), + 2 + ); + } + + #[test] + fn string_roundtrip() { + let sut = SUT::sample(); + let str = factor_source_kind_to_string(sut); + let from_str = new_factor_source_kind_from_string(str).unwrap(); + assert_eq!(sut, from_str); + } +} diff --git a/src/profile/v100/factors/factor_source_uniffi_fn.rs b/src/profile/v100/factors/factor_source_uniffi_fn.rs index 8c14a8a5f..671daac00 100644 --- a/src/profile/v100/factors/factor_source_uniffi_fn.rs +++ b/src/profile/v100/factors/factor_source_uniffi_fn.rs @@ -5,6 +5,16 @@ pub fn factor_source_to_string(factor_source: &FactorSource) -> String { factor_source.to_string() } +#[uniffi::export] +pub fn factor_source_supports_olympia(factor_source: &FactorSource) -> bool { + factor_source.supports_olympia() +} + +#[uniffi::export] +pub fn factor_source_supports_babylon(factor_source: &FactorSource) -> bool { + factor_source.supports_babylon() +} + #[uniffi::export] pub fn new_factor_source_sample() -> FactorSource { FactorSource::sample() diff --git a/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source.rs b/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source.rs index 8c08fb860..31d20930a 100644 --- a/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source.rs +++ b/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source.rs @@ -49,6 +49,10 @@ impl IsFactorSource for DeviceFactorSource { } } impl BaseIsFactorSource for DeviceFactorSource { + fn common_properties(&self) -> FactorSourceCommon { + self.common.clone() + } + fn factor_source_kind(&self) -> FactorSourceKind { self.id.kind } @@ -88,6 +92,25 @@ impl DeviceFactorSource { ) } + pub fn olympia( + mnemonic_with_passphrase: &MnemonicWithPassphrase, + wallet_client_model: WalletClientModel, + ) -> Self { + let id = FactorSourceIDFromHash::from_mnemonic_with_passphrase( + FactorSourceKind::Device, + mnemonic_with_passphrase, + ); + + Self::new( + id, + FactorSourceCommon::new_olympia(), + DeviceFactorSourceHint::unknown_model_of_client( + mnemonic_with_passphrase.mnemonic.word_count, + wallet_client_model, + ), + ) + } + /// Checks if its Main Babylon Device Factor Source (BDFS). pub fn is_main_bdfs(&self) -> bool { self.common.is_main_bdfs() diff --git a/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source_uniffi_fn.rs b/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source_uniffi_fn.rs index f4443d4dc..3a851715f 100644 --- a/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source_uniffi_fn.rs +++ b/src/profile/v100/factors/factor_sources/device_factor_source/device_factor_source_uniffi_fn.rs @@ -10,6 +10,34 @@ pub fn new_device_factor_source_sample_other() -> DeviceFactorSource { DeviceFactorSource::sample_other() } +#[uniffi::export] +pub fn new_device_factor_source_babylon( + is_main: bool, + mnemonic_with_passphrase: &MnemonicWithPassphrase, + wallet_client_model: WalletClientModel, +) -> DeviceFactorSource { + DeviceFactorSource::babylon( + is_main, + mnemonic_with_passphrase, + wallet_client_model, + ) +} + +#[uniffi::export] +pub fn new_device_factor_source_olympia( + mnemonic_with_passphrase: &MnemonicWithPassphrase, + wallet_client_model: WalletClientModel, +) -> DeviceFactorSource { + DeviceFactorSource::olympia(mnemonic_with_passphrase, wallet_client_model) +} + +#[uniffi::export] +pub fn device_factor_source_is_main_bdfs( + device_factor_source: &DeviceFactorSource, +) -> bool { + device_factor_source.is_main_bdfs() +} + #[cfg(test)] mod tests { use super::*; @@ -17,6 +45,51 @@ mod tests { #[allow(clippy::upper_case_acronyms)] type SUT = DeviceFactorSource; + #[test] + fn test_new_olympia() { + let olympia = new_device_factor_source_olympia( + &MnemonicWithPassphrase::sample(), + WalletClientModel::Iphone, + ); + + assert!(factor_source_supports_olympia(&olympia.clone().into())); + assert!(!factor_source_supports_babylon(&olympia.into())); + } + + #[test] + fn test_new_babylon() { + let babylon = new_device_factor_source_babylon( + true, + &MnemonicWithPassphrase::sample(), + WalletClientModel::Iphone, + ); + + assert!(factor_source_supports_babylon(&babylon.clone().into())); + assert!(!factor_source_supports_olympia(&babylon.into())); + } + + #[test] + fn test_new_babylon_not_main() { + let babylon = new_device_factor_source_babylon( + false, + &MnemonicWithPassphrase::sample(), + WalletClientModel::Iphone, + ); + + assert!(!device_factor_source_is_main_bdfs(&babylon)); + } + + #[test] + fn test_new_babylon_is_main() { + let babylon = new_device_factor_source_babylon( + true, + &MnemonicWithPassphrase::sample(), + WalletClientModel::Iphone, + ); + + assert!(device_factor_source_is_main_bdfs(&babylon)); + } + #[test] fn hash_of_samples() { assert_eq!( diff --git a/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_factor_source.rs b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_factor_source.rs index 5422f2bd7..ed97da30b 100644 --- a/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_factor_source.rs +++ b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_factor_source.rs @@ -77,6 +77,10 @@ impl IsFactorSource for LedgerHardwareWalletFactorSource { } } impl BaseIsFactorSource for LedgerHardwareWalletFactorSource { + fn common_properties(&self) -> FactorSourceCommon { + self.common.clone() + } + fn factor_source_kind(&self) -> FactorSourceKind { self.id.kind } diff --git a/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_model.rs b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_model.rs index 654d1259e..b0a82c16f 100644 --- a/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_model.rs +++ b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_model.rs @@ -13,7 +13,6 @@ use crate::prelude::*; Hash, PartialOrd, Ord, - derive_more::Display, uniffi::Enum, )] #[serde(rename_all = "camelCase")] @@ -25,64 +24,95 @@ pub enum LedgerHardwareWalletModel { NanoX, } +impl FromStr for LedgerHardwareWalletModel { + type Err = CommonError; + fn from_str(s: &str) -> Result { + Self::new_from_json_string(s).map_err(|_| { + CommonError::InvalidLedgerHardwareWalletModel { + bad_value: s.to_owned(), + } + }) + } +} + +impl std::fmt::Display for LedgerHardwareWalletModel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.to_json_string()) + } +} + +impl JsonStringSerializing for LedgerHardwareWalletModel {} // to raw String +impl JsonStringDeserializing for LedgerHardwareWalletModel {} // from raw String + +impl HasSampleValues for LedgerHardwareWalletModel { + fn sample() -> Self { + Self::NanoSPlus + } + + fn sample_other() -> Self { + Self::NanoX + } +} + #[cfg(test)] mod tests { use crate::prelude::*; + #[allow(clippy::upper_case_acronyms)] + type SUT = LedgerHardwareWalletModel; + #[test] fn equality() { - assert_eq!( - LedgerHardwareWalletModel::NanoS, - LedgerHardwareWalletModel::NanoS - ); - assert_eq!( - LedgerHardwareWalletModel::NanoX, - LedgerHardwareWalletModel::NanoX - ); + assert_eq!(SUT::sample(), SUT::sample()); + assert_eq!(SUT::sample_other(), SUT::sample_other()); } + #[test] fn inequality() { - assert_ne!( - LedgerHardwareWalletModel::NanoS, - LedgerHardwareWalletModel::NanoX + assert_ne!(SUT::sample(), SUT::sample_other()); + } + + #[test] + fn string_roundtrip() { + use LedgerHardwareWalletModel::*; + let eq = |f: SUT, s| { + assert_eq!(f.to_string(), s); + assert_eq!(SUT::from_str(s).unwrap(), f); + }; + + eq(NanoSPlus, "nanoS+"); + eq(NanoX, "nanoX"); + eq(NanoS, "nanoS"); + } + + #[test] + fn from_str_err() { + let s = "invalid ledger hardware model kind!"; + assert_eq!( + SUT::from_str(s), + Err(CommonError::InvalidLedgerHardwareWalletModel { + bad_value: s.to_owned(), + }) ); } #[test] fn hash() { assert_eq!( - BTreeSet::from_iter( - [ - LedgerHardwareWalletModel::NanoS, - LedgerHardwareWalletModel::NanoS - ] - .into_iter() - ) - .len(), + BTreeSet::from_iter([SUT::NanoS, SUT::NanoS].into_iter()).len(), 1 ); } #[test] fn ord() { - assert!( - LedgerHardwareWalletModel::NanoS < LedgerHardwareWalletModel::NanoX - ); + assert!(SUT::NanoS < SUT::NanoX); } #[test] fn json_roundtrip() { - assert_json_value_eq_after_roundtrip( - &LedgerHardwareWalletModel::NanoS, - json!("nanoS"), - ); - assert_json_value_eq_after_roundtrip( - &LedgerHardwareWalletModel::NanoSPlus, - json!("nanoS+"), - ); - assert_json_value_eq_after_roundtrip( - &LedgerHardwareWalletModel::NanoX, - json!("nanoX"), - ); + assert_json_value_eq_after_roundtrip(&SUT::NanoS, json!("nanoS")); + assert_json_value_eq_after_roundtrip(&SUT::NanoSPlus, json!("nanoS+")); + assert_json_value_eq_after_roundtrip(&SUT::NanoX, json!("nanoX")); } } diff --git a/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_model_uniffi_fn.rs b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_model_uniffi_fn.rs new file mode 100644 index 000000000..9931c24c7 --- /dev/null +++ b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/ledger_hardware_wallet_model_uniffi_fn.rs @@ -0,0 +1,56 @@ +use crate::prelude::*; + +#[uniffi::export] +pub fn ledger_hw_wallet_model_to_string( + model: LedgerHardwareWalletModel, +) -> String { + model.to_string() +} + +#[uniffi::export] +pub fn new_ledger_hw_wallet_model_from_string( + string: String, +) -> Result { + LedgerHardwareWalletModel::from_str(&string) +} + +#[uniffi::export] +pub fn new_ledger_hw_wallet_model_sample() -> LedgerHardwareWalletModel { + LedgerHardwareWalletModel::sample() +} + +#[uniffi::export] +pub fn new_ledger_hw_wallet_model_sample_other() -> LedgerHardwareWalletModel { + LedgerHardwareWalletModel::sample_other() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[allow(clippy::upper_case_acronyms)] + type SUT = LedgerHardwareWalletModel; + + #[test] + fn hash_of_samples() { + assert_eq!( + HashSet::::from_iter([ + new_ledger_hw_wallet_model_sample(), + new_ledger_hw_wallet_model_sample_other(), + // duplicates should get removed + new_ledger_hw_wallet_model_sample(), + new_ledger_hw_wallet_model_sample_other(), + ]) + .len(), + 2 + ); + } + + #[test] + fn string_roundtrip() { + let sut = SUT::sample(); + let str = ledger_hw_wallet_model_to_string(sut); + let from_str = new_ledger_hw_wallet_model_from_string(str).unwrap(); + assert_eq!(sut, from_str); + } +} diff --git a/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/mod.rs b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/mod.rs index aa6f44961..13c0edf8c 100644 --- a/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/mod.rs +++ b/src/profile/v100/factors/factor_sources/ledger_hardware_wallet_factor_source/mod.rs @@ -2,8 +2,10 @@ mod ledger_hardware_wallet_factor_source; mod ledger_hardware_wallet_factor_source_uniffi_fn; mod ledger_hardware_wallet_hint; mod ledger_hardware_wallet_model; +mod ledger_hardware_wallet_model_uniffi_fn; pub use ledger_hardware_wallet_factor_source::*; pub use ledger_hardware_wallet_factor_source_uniffi_fn::*; pub use ledger_hardware_wallet_hint::*; pub use ledger_hardware_wallet_model::*; +pub use ledger_hardware_wallet_model_uniffi_fn::*; diff --git a/src/profile/v100/factors/factor_sources/mod.rs b/src/profile/v100/factors/factor_sources/mod.rs index ea5f3c1be..f622c6044 100644 --- a/src/profile/v100/factors/factor_sources/mod.rs +++ b/src/profile/v100/factors/factor_sources/mod.rs @@ -2,8 +2,10 @@ mod device_factor_source; mod factor_sources; mod ledger_hardware_wallet_factor_source; mod private_hierarchical_deterministic_factor_source; +mod private_hierarchical_deterministic_factor_source_uniffi_fn; pub use device_factor_source::*; pub use factor_sources::*; pub use ledger_hardware_wallet_factor_source::*; pub use private_hierarchical_deterministic_factor_source::*; +pub use private_hierarchical_deterministic_factor_source_uniffi_fn::*; diff --git a/src/profile/v100/factors/factor_sources/private_hierarchical_deterministic_factor_source.rs b/src/profile/v100/factors/factor_sources/private_hierarchical_deterministic_factor_source.rs index 62f3e6a77..457a08a82 100644 --- a/src/profile/v100/factors/factor_sources/private_hierarchical_deterministic_factor_source.rs +++ b/src/profile/v100/factors/factor_sources/private_hierarchical_deterministic_factor_source.rs @@ -7,20 +7,6 @@ pub struct PrivateHierarchicalDeterministicFactorSource { pub factor_source: DeviceFactorSource, } -#[uniffi::export] -pub fn new_private_hd_factor_source( - entropy: NonEmptyMax32Bytes, - wallet_client_model: WalletClientModel, -) -> Result { - BIP39Entropy::try_from(entropy).map(|entropy| { - PrivateHierarchicalDeterministicFactorSource::new_with_entropy( - entropy, - BIP39Passphrase::default(), - wallet_client_model, - ) - }) -} - impl PrivateHierarchicalDeterministicFactorSource { pub fn new( mnemonic_with_passphrase: MnemonicWithPassphrase, @@ -40,19 +26,32 @@ impl PrivateHierarchicalDeterministicFactorSource { } } - fn new_with_mnemonic_with_passphrase( + pub fn new_olympia_with_mnemonic_with_passphrase( + mnemonic_with_passphrase: MnemonicWithPassphrase, + wallet_client_model: WalletClientModel, + ) -> Self { + let device_factor_source = DeviceFactorSource::olympia( + &mnemonic_with_passphrase, + wallet_client_model, + ); + Self::new(mnemonic_with_passphrase, device_factor_source) + } + + pub fn new_babylon_with_mnemonic_with_passphrase( + is_main: bool, mnemonic_with_passphrase: MnemonicWithPassphrase, wallet_client_model: WalletClientModel, ) -> Self { let bdfs = DeviceFactorSource::babylon( - true, + is_main, &mnemonic_with_passphrase, wallet_client_model, ); Self::new(mnemonic_with_passphrase, bdfs) } - pub fn new_with_entropy( + pub fn new_babylon_with_entropy( + is_main: bool, entropy: BIP39Entropy, passphrase: BIP39Passphrase, wallet_client_model: WalletClientModel, @@ -60,16 +59,21 @@ impl PrivateHierarchicalDeterministicFactorSource { let mnemonic = Mnemonic::from_entropy(entropy); let mnemonic_with_passphrase = MnemonicWithPassphrase::with_passphrase(mnemonic, passphrase); - Self::new_with_mnemonic_with_passphrase( + Self::new_babylon_with_mnemonic_with_passphrase( + is_main, mnemonic_with_passphrase, wallet_client_model, ) } - pub fn generate_new(wallet_client_model: WalletClientModel) -> Self { + pub fn generate_new_babylon( + is_main: bool, + wallet_client_model: WalletClientModel, + ) -> Self { let mnemonic = Mnemonic::generate_new(); let mnemonic_with_passphrase = MnemonicWithPassphrase::new(mnemonic); - Self::new_with_mnemonic_with_passphrase( + Self::new_babylon_with_mnemonic_with_passphrase( + is_main, mnemonic_with_passphrase, wallet_client_model, ) @@ -140,7 +144,9 @@ mod tests { fn hash() { let n = 100; let set = (0..n) - .map(|_| SUT::generate_new(WalletClientModel::Unknown)) + .map(|_| { + SUT::generate_new_babylon(true, WalletClientModel::Unknown) + }) .collect::>(); assert_eq!(set.len(), n); } @@ -152,21 +158,3 @@ mod tests { assert_ne!(sut, SUT::sample()); } } - -#[cfg(test)] -mod uniffi_tests { - use super::*; - - #[allow(clippy::upper_case_acronyms)] - type SUT = PrivateHierarchicalDeterministicFactorSource; - - #[test] - fn new_uses_empty_bip39_passphrase() { - let private: SUT = new_private_hd_factor_source( - Entropy32Bytes::new([0xff; 32]).into(), - WalletClientModel::Unknown, - ) - .unwrap(); - assert_eq!(private.mnemonic_with_passphrase.passphrase.0, ""); - } -} diff --git a/src/profile/v100/factors/factor_sources/private_hierarchical_deterministic_factor_source_uniffi_fn.rs b/src/profile/v100/factors/factor_sources/private_hierarchical_deterministic_factor_source_uniffi_fn.rs new file mode 100644 index 000000000..913dc33e6 --- /dev/null +++ b/src/profile/v100/factors/factor_sources/private_hierarchical_deterministic_factor_source_uniffi_fn.rs @@ -0,0 +1,117 @@ +use crate::prelude::*; + +#[uniffi::export] +pub fn new_private_hd_factor_source_babylon( + is_main: bool, + entropy: NonEmptyMax32Bytes, + wallet_client_model: WalletClientModel, +) -> Result { + BIP39Entropy::try_from(entropy).map(|entropy| { + PrivateHierarchicalDeterministicFactorSource::new_babylon_with_entropy( + is_main, + entropy, + BIP39Passphrase::default(), + wallet_client_model, + ) + }) +} + +#[uniffi::export] +pub fn new_private_hd_factor_source_babylon_from_mnemonic_with_passphrase( + is_main: bool, + mnemonic_with_passphrase: MnemonicWithPassphrase, + wallet_client_model: WalletClientModel, +) -> PrivateHierarchicalDeterministicFactorSource { + PrivateHierarchicalDeterministicFactorSource::new_babylon_with_mnemonic_with_passphrase(is_main, mnemonic_with_passphrase, wallet_client_model) +} + +#[uniffi::export] +pub fn new_private_hd_factor_source_olympia_from_mnemonic_with_passphrase( + mnemonic_with_passphrase: MnemonicWithPassphrase, + wallet_client_model: WalletClientModel, +) -> PrivateHierarchicalDeterministicFactorSource { + PrivateHierarchicalDeterministicFactorSource::new_olympia_with_mnemonic_with_passphrase(mnemonic_with_passphrase, wallet_client_model) +} + +#[uniffi::export] +pub fn new_private_hd_factor_source_sample( +) -> PrivateHierarchicalDeterministicFactorSource { + PrivateHierarchicalDeterministicFactorSource::sample() +} + +#[uniffi::export] +pub fn new_private_hd_factor_source_sample_other( +) -> PrivateHierarchicalDeterministicFactorSource { + PrivateHierarchicalDeterministicFactorSource::sample_other() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hash_of_samples() { + assert_eq!( + HashSet::::from_iter([ + new_private_hd_factor_source_sample(), + new_private_hd_factor_source_sample_other(), + // duplicates should get removed + new_private_hd_factor_source_sample(), + new_private_hd_factor_source_sample_other(), + ]) + .len(), + 2 + ); + } + + #[allow(clippy::upper_case_acronyms)] + type SUT = PrivateHierarchicalDeterministicFactorSource; + + #[test] + fn new_uses_empty_bip39_passphrase() { + let private: SUT = new_private_hd_factor_source_babylon( + true, + Entropy32Bytes::new([0xff; 32]).into(), + WalletClientModel::Unknown, + ) + .unwrap(); + assert_eq!(private.mnemonic_with_passphrase.passphrase.0, ""); + } + + #[test] + fn test_new_private_hd_factor_source_babylon_from_mnemonic_with_passphrase() + { + let sut = + new_private_hd_factor_source_babylon_from_mnemonic_with_passphrase( + true, + MnemonicWithPassphrase::sample(), + WalletClientModel::Android, + ); + assert!(&sut.factor_source.supports_babylon()); + assert!(!&sut.factor_source.supports_olympia()); + } + + #[test] + fn test_new_private_hd_factor_source_babylon_from_mnemonic_with_passphrase_is_main_true( + ) { + let sut = + new_private_hd_factor_source_babylon_from_mnemonic_with_passphrase( + true, + MnemonicWithPassphrase::sample(), + WalletClientModel::Android, + ); + assert!(sut.factor_source.is_main_bdfs()); + } + + #[test] + fn test_new_private_hd_factor_source_olympia_from_mnemonic_with_passphrase() + { + let sut = + new_private_hd_factor_source_olympia_from_mnemonic_with_passphrase( + MnemonicWithPassphrase::sample(), + WalletClientModel::Android, + ); + assert!(&sut.factor_source.supports_olympia()); + assert!(!&sut.factor_source.supports_babylon()); + } +} diff --git a/src/profile/v100/factors/is_factor_source.rs b/src/profile/v100/factors/is_factor_source.rs index cf994b2c9..b35d57b30 100644 --- a/src/profile/v100/factors/is_factor_source.rs +++ b/src/profile/v100/factors/is_factor_source.rs @@ -5,6 +5,14 @@ pub trait BaseIsFactorSource: { fn factor_source_kind(&self) -> FactorSourceKind; fn factor_source_id(&self) -> FactorSourceID; + + fn common_properties(&self) -> FactorSourceCommon; + fn supports_babylon(&self) -> bool { + self.common_properties().supports_babylon() + } + fn supports_olympia(&self) -> bool { + self.common_properties().supports_olympia() + } } pub trait IsFactorSource: BaseIsFactorSource { diff --git a/src/profile/v100/factors/mod.rs b/src/profile/v100/factors/mod.rs index 8dc7c58c8..bb8123c56 100644 --- a/src/profile/v100/factors/mod.rs +++ b/src/profile/v100/factors/mod.rs @@ -10,6 +10,7 @@ mod factor_source_id_from_hash; mod factor_source_id_from_hash_uniffi_fn; mod factor_source_id_uniffi_fn; mod factor_source_kind; +mod factor_source_kind_uniffi_fn; mod factor_source_uniffi_fn; mod factor_sources; mod hd_transaction_signing_factor_instance; @@ -28,6 +29,7 @@ pub use factor_source_id_from_hash::*; pub use factor_source_id_from_hash_uniffi_fn::*; pub use factor_source_id_uniffi_fn::*; pub use factor_source_kind::*; +pub use factor_source_kind_uniffi_fn::*; pub use factor_source_uniffi_fn::*; pub use factor_sources::*; pub use hd_transaction_signing_factor_instance::*; diff --git a/src/profile/v100/profile.rs b/src/profile/v100/profile.rs index 34ff785bd..393bbec09 100644 --- a/src/profile/v100/profile.rs +++ b/src/profile/v100/profile.rs @@ -394,7 +394,8 @@ mod tests { let set = (0..n) .map(|_| { SUT::new( - PrivateHierarchicalDeterministicFactorSource::generate_new( + PrivateHierarchicalDeterministicFactorSource::generate_new_babylon( + true, WalletClientModel::Unknown, ) .factor_source, diff --git a/src/profile/v100/profile_uniffi_fn.rs b/src/profile/v100/profile_uniffi_fn.rs index 70aa7fb7e..210a43f93 100644 --- a/src/profile/v100/profile_uniffi_fn.rs +++ b/src/profile/v100/profile_uniffi_fn.rs @@ -75,7 +75,7 @@ mod uniffi_tests { #[test] fn to_string_and_debug_string() { - assert_eq!(profile_to_string(&SUT::sample()).len(), 4447); + assert_eq!(profile_to_string(&SUT::sample()).len(), 4444); assert_eq!(profile_to_debug_string(&SUT::sample()).len(), 28088); assert_ne!( profile_to_debug_string(&SUT::sample()), diff --git a/src/wallet/wallet.rs b/src/wallet/wallet.rs index 56e4143a0..bfff0809e 100644 --- a/src/wallet/wallet.rs +++ b/src/wallet/wallet.rs @@ -89,7 +89,8 @@ impl Wallet { let entropy = BIP39Entropy::try_from(entropy)?; let private_hd_factor_source = - PrivateHierarchicalDeterministicFactorSource::new_with_entropy( + PrivateHierarchicalDeterministicFactorSource::new_babylon_with_entropy( + true, entropy, BIP39Passphrase::default(), wallet_client_model, diff --git a/src/wallet/wallet_accounts.rs b/src/wallet/wallet_accounts.rs index 174cd24bb..f3247e652 100644 --- a/src/wallet/wallet_accounts.rs +++ b/src/wallet/wallet_accounts.rs @@ -268,9 +268,11 @@ mod tests { #[test] pub fn add_private_device_factor_source_successful() { let profile = Profile::sample(); - let new = PrivateHierarchicalDeterministicFactorSource::generate_new( - WalletClientModel::Unknown, - ); + let new = + PrivateHierarchicalDeterministicFactorSource::generate_new_babylon( + true, + WalletClientModel::Unknown, + ); let (wallet, storage) = Wallet::ephemeral(profile.clone()); assert_eq!( profile @@ -297,9 +299,11 @@ mod tests { pub fn add_private_device_factor_source_ok_storage_when_save_to_profile_fails_then_deleted_from_storage( ) { let profile = Profile::sample(); - let new = PrivateHierarchicalDeterministicFactorSource::generate_new( - WalletClientModel::Unknown, - ); + let new = + PrivateHierarchicalDeterministicFactorSource::generate_new_babylon( + true, + WalletClientModel::Unknown, + ); assert_eq!( profile diff --git a/src/wrapped_radix_engine_toolkit/high_level/manifest_building/metadata.rs b/src/wrapped_radix_engine_toolkit/high_level/manifest_building/metadata.rs index cae36692a..77228646d 100644 --- a/src/wrapped_radix_engine_toolkit/high_level/manifest_building/metadata.rs +++ b/src/wrapped_radix_engine_toolkit/high_level/manifest_building/metadata.rs @@ -60,6 +60,7 @@ mod tests { use MetadataKey::*; let eq = |v: MetadataKey, e| { assert_eq!(v.to_string(), e); + assert_eq!(MetadataKey::from_str(e).unwrap(), v); }; eq(AccountType, "account_type");