Skip to content

Commit

Permalink
Merge pull request #684 from nova-wallet/develop
Browse files Browse the repository at this point in the history
6.3.0 Community requested features
  • Loading branch information
ERussel authored May 29, 2023
2 parents 96b3dd6 + 054866f commit c18fd0c
Show file tree
Hide file tree
Showing 116 changed files with 3,239 additions and 437 deletions.
2 changes: 1 addition & 1 deletion Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: 7401829850c56fe153f46655dbfad25e61b9927c

COCOAPODS: 1.11.3
COCOAPODS: 1.12.1
Binary file modified docs/Nova_GitHub.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
178 changes: 174 additions & 4 deletions novawallet.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "approve-with-pin.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "authentication.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 61 additions & 14 deletions novawallet/Common/Crypto/SigningWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,61 +6,108 @@ import SubstrateSdk
enum SigningWrapperError: Error {
case missingSelectedAccount
case missingSecretKey
case pinCheckNotPassed
}

final class SigningWrapper: SigningWrapperProtocol {
final class SigningWrapper: SigningWrapperProtocol, AuthorizationPresentable {
let keystore: KeystoreProtocol
let metaId: String
let accountId: AccountId?
let isEthereumBased: Bool
let cryptoType: MultiassetCryptoType
let publicKeyData: Data

@available(*, deprecated, message: "Use init(keystore:metaId:accountId:cryptoType:) instead")
init(keystore: KeystoreProtocol, settings _: SettingsManagerProtocol) {
self.keystore = keystore
metaId = ""
accountId = nil
cryptoType = .sr25519
isEthereumBased = false
publicKeyData = Data(repeating: 0, count: 32)
}
let settingsManager: SettingsManagerProtocol

init(
keystore: KeystoreProtocol,
metaId: String,
accountId: AccountId?,
isEthereumBased: Bool,
cryptoType: MultiassetCryptoType,
publicKeyData: Data
publicKeyData: Data,
settingsManager: SettingsManagerProtocol
) {
self.keystore = keystore
self.metaId = metaId
self.accountId = accountId
self.cryptoType = cryptoType
self.isEthereumBased = isEthereumBased
self.publicKeyData = publicKeyData
self.settingsManager = settingsManager
}

init(keystore: KeystoreProtocol, metaId: String, accountResponse: ChainAccountResponse) {
init(
keystore: KeystoreProtocol,
metaId: String,
accountResponse: ChainAccountResponse,
settingsManager: SettingsManagerProtocol
) {
self.keystore = keystore
self.metaId = metaId
accountId = accountResponse.isChainAccount ? accountResponse.accountId : nil
isEthereumBased = accountResponse.isEthereumBased
cryptoType = accountResponse.cryptoType
publicKeyData = accountResponse.publicKey
self.settingsManager = settingsManager
}

init(keystore: KeystoreProtocol, ethereumAccountResponse: MetaEthereumAccountResponse) {
init(
keystore: KeystoreProtocol,
ethereumAccountResponse: MetaEthereumAccountResponse,
settingsManager: SettingsManagerProtocol
) {
self.keystore = keystore
metaId = ethereumAccountResponse.metaId
accountId = ethereumAccountResponse.isChainAccount ? ethereumAccountResponse.address : nil
isEthereumBased = true
cryptoType = MultiassetCryptoType.ethereumEcdsa
publicKeyData = ethereumAccountResponse.publicKey
self.settingsManager = settingsManager
}

func sign(_ originalData: Data) throws -> IRSignatureProtocol {
if settingsManager.pinConfirmationEnabled == true {
let signingResult = signAfterAutorization(originalData)
switch signingResult {
case let .success(signature):
return signature
case let .failure(error):
throw error
}
} else {
return try _sign(originalData)
}
}

private func signAfterAutorization(_ originalData: Data) -> Result<IRSignatureProtocol, Error> {
let semaphore = DispatchSemaphore(value: 0)
var signResult: Result<IRSignatureProtocol, Error>?

DispatchQueue.main.async {
self.authorize(animated: true, cancellable: true) { [weak self] completed in
defer {
semaphore.signal()
}
guard let self = self else {
return
}
if completed {
do {
let sign = try self._sign(originalData)
signResult = .success(sign)
} catch {
signResult = .failure(error)
}
}
}
}

semaphore.wait()

return signResult ?? .failure(SigningWrapperError.pinCheckNotPassed)
}

private func _sign(_ originalData: Data) throws -> IRSignatureProtocol {
let tag: String = isEthereumBased ?
KeystoreTagV2.ethereumSecretKeyTagForMetaId(metaId, accountId: accountId) :
KeystoreTagV2.substrateSecretKeyTagForMetaId(metaId, accountId: accountId)
Expand Down
19 changes: 15 additions & 4 deletions novawallet/Common/Crypto/SigningWrapperFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@ protocol SigningWrapperFactoryProtocol {

final class SigningWrapperFactory: SigningWrapperFactoryProtocol {
let keystore: KeystoreProtocol

let uiPresenter: TransactionSigningPresenting
let settingsManager: SettingsManagerProtocol

init(
uiPresenter: TransactionSigningPresenting = TransactionSigningPresenter(),
keystore: KeystoreProtocol = Keychain()
keystore: KeystoreProtocol = Keychain(),
settingsManager: SettingsManagerProtocol = SettingsManager.shared
) {
self.uiPresenter = uiPresenter
self.keystore = keystore
self.settingsManager = settingsManager
}

func createSigningWrapper(
Expand All @@ -33,7 +35,12 @@ final class SigningWrapperFactory: SigningWrapperFactoryProtocol {
) -> SigningWrapperProtocol {
switch accountResponse.type {
case .secrets:
return SigningWrapper(keystore: keystore, metaId: metaId, accountResponse: accountResponse)
return SigningWrapper(
keystore: keystore,
metaId: metaId,
accountResponse: accountResponse,
settingsManager: settingsManager
)
case .watchOnly:
return NoKeysSigningWrapper()
case .paritySigner:
Expand All @@ -56,7 +63,11 @@ final class SigningWrapperFactory: SigningWrapperFactoryProtocol {
) -> SigningWrapperProtocol {
switch ethereumAccountResponse.type {
case .secrets:
return SigningWrapper(keystore: keystore, ethereumAccountResponse: ethereumAccountResponse)
return SigningWrapper(
keystore: keystore,
ethereumAccountResponse: ethereumAccountResponse,
settingsManager: settingsManager
)
case .watchOnly:
return NoKeysSigningWrapper()
case .paritySigner:
Expand Down
1 change: 1 addition & 0 deletions novawallet/Common/Extension/Foundation/String+split.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ extension String {
case slash = "/"
case colon = ":"
case hashtag = "#"
case space = " "
}

enum CompoundSeparator: String {
Expand Down
15 changes: 15 additions & 0 deletions novawallet/Common/Extension/SettingsExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum SettingsKey: String {
case governanceDelegateInfoSeen
case skippedUpdateVersion
case skippedAddDelegationTracksHint
case pinConfirmationEnabled
}

extension SettingsManagerProtocol {
Expand All @@ -32,6 +33,20 @@ extension SettingsManagerProtocol {
}
}

var pinConfirmationEnabled: Bool? {
get {
bool(for: SettingsKey.pinConfirmationEnabled.rawValue)
}

set {
if let existingValue = newValue {
set(value: existingValue, for: SettingsKey.pinConfirmationEnabled.rawValue)
} else {
removeValue(for: SettingsKey.pinConfirmationEnabled.rawValue)
}
}
}

var crowdloanChainId: String? {
get {
string(for: SettingsKey.crowdloadChainId.rawValue)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import UIKit

extension UILayoutPriority {
static let high = UILayoutPriority(700)
}
4 changes: 4 additions & 0 deletions novawallet/Common/Helpers/GenericLens.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
struct GenericLens<Whole, Part> {
let get: (Whole) -> Part
let set: (Part, Whole) -> Whole
}
61 changes: 61 additions & 0 deletions novawallet/Common/Helpers/SearchOperationFactory.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Foundation
import RobinHood

enum SearchOperationFactory {
private static func pointsForWord(title: String, word: String) -> UInt {
if word.caseInsensitiveCompare(title) == .orderedSame {
return 1000
} else if title.range(of: word, options: .caseInsensitive) != nil {
return 1
} else {
return 0
}
}

private static func pointsForPhrase(title: String, phrase: String) -> UInt {
let pattern = phrase.replacingOccurrences(of: " ", with: ".*")
guard let regex = try? NSRegularExpression(pattern: pattern) else {
return 0
}
let match = regex.firstMatch(
in: title,
range: NSRange(title.startIndex..., in: title)
)
return match != nil ? 1 : 0
}

static func searchOperation<Model, Key>(
text: String,
in models: [Model],
keyExtractor: @escaping (Model) -> Key,
searchKeysExtractor: @escaping (Key) -> [String]
) -> BaseOperation<[Model]> where Key: Hashable {
ClosureOperation {
guard !text.isEmpty else {
return models
}
let calculatePoints = text.split(
by: .space,
maxSplits: 1
).count > 1 ? self.pointsForPhrase : self.pointsForWord

let weights = models.reduce(into: [Key: UInt]()) { result, item in
let key = keyExtractor(item)
let searchWords = searchKeysExtractor(key)
result[key] = searchWords.reduce(0) {
$0 + calculatePoints($1, text)
}
}

let result = models
.filter {
weights[keyExtractor($0), default: 0] > 0
}
.sorted {
weights[keyExtractor($0), default: 0] > weights[keyExtractor($1), default: 0]
}

return result
}
}
}
26 changes: 14 additions & 12 deletions novawallet/Common/LocalAuthentication/BiometryAuth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ enum AvailableBiometryType {

protocol BiometryAuthProtocol {
var availableBiometryType: AvailableBiometryType { get }
var supportedBiometryType: AvailableBiometryType { get }

func authenticate(
localizedReason: String,
completionQueue: DispatchQueue,
Expand All @@ -24,19 +26,19 @@ class BiometryAuth: BiometryAuthProtocol {
let available = context.canEvaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, error: nil)
guard available else { return .none }

if #available(iOS 11, *) {
switch context.biometryType {
case .touchID:
return .touchId
case .faceID:
return .faceId
case .none:
return .none
@unknown default:
return .none
}
} else {
return supportedBiometryType
}

var supportedBiometryType: AvailableBiometryType {
switch context.biometryType {
case .touchID:
return .touchId
case .faceID:
return .faceId
case .none:
return .none
@unknown default:
return .none
}
}

Expand Down
16 changes: 16 additions & 0 deletions novawallet/Common/LocalAuthentication/NoBiometryAuth.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Foundation

final class NoBiometryAuth: BiometryAuthProtocol {
var availableBiometryType: AvailableBiometryType { .none }
var supportedBiometryType: AvailableBiometryType { .none }

func authenticate(
localizedReason _: String,
completionQueue: DispatchQueue,
completionBlock: @escaping (Bool) -> Void
) {
dispatchInQueueWhenPossible(completionQueue) {
completionBlock(false)
}
}
}
Loading

0 comments on commit c18fd0c

Please sign in to comment.