Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 20 additions & 32 deletions Adamant/Modules/Login/LoginViewController+Pinpad.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,16 @@ extension LoginViewController {

Task {
do {
let result = try await accountService.loginWithStoredAccount()
handleSavedAccountLoginResult(result)
try await accountService.loginWithStoredAccount()
dialogService.dismissProgress()

guard let presenter = presentingViewController else {
return
}

presenter.dismiss(animated: true, completion: nil)

presentUpdateV12AlertIfNeeded(presenter: presenter)
} catch {
dialogService.showRichError(error: error)

Expand All @@ -80,36 +88,16 @@ extension LoginViewController {
}
}

private func handleSavedAccountLoginResult(_ result: AccountServiceResult) {
switch result {
case .success(_, let alert):
dialogService.dismissProgress()

let alertVc: UIAlertController?
if let alert = alert {
alertVc = UIAlertController(title: alert.title, message: alert.message, preferredStyleSafe: .alert, source: nil)
alertVc!.addAction(UIAlertAction(title: String.adamant.alert.ok, style: .default))
} else {
alertVc = nil
}

guard let presenter = presentingViewController else {
return
}
presenter.dismiss(animated: true, completion: nil)

if let alertVc = alertVc {
alertVc.modalPresentationStyle = .overFullScreen
presenter.present(alertVc, animated: true, completion: nil)
}

case .failure(let error):
dialogService.showRichError(error: error)

if let pinpad = presentedViewController as? PinpadViewController {
pinpad.clearPin()
}
}
private func presentUpdateV12AlertIfNeeded(presenter: UIViewController) {
let alertVc = UIAlertController(
title: String.adamant.accountService.updateAlertTitleV12,
message: String.adamant.accountService.updateAlertMessageV12,
preferredStyleSafe: .alert,
source: nil
)
alertVc.addAction(UIAlertAction(title: String.adamant.alert.ok, style: .default))
alertVc.modalPresentationStyle = .overFullScreen
presenter.present(alertVc, animated: true, completion: nil)
}
}

Expand Down
55 changes: 15 additions & 40 deletions Adamant/Modules/Login/LoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -492,64 +492,39 @@ extension LoginViewController {
dialogService.showWarning(withMessage: AccountServiceError.invalidPassphrase.localized)
return
}

dialogService.showProgress(withMessage: String.adamant.login.loggingInProgressMessage, userInteractionEnable: false)


Task {
let result = await apiService.getAccount(byPassphrase: passphrase)

switch result {
case .success:
loginIntoExistingAccount(passphrase: passphrase)

case .failure(let error):
dialogService.showRichError(error: error)
}
await loginIntoExistingAccount(passphrase: passphrase)
}
}

func generateNewPassphrase() {
let passphrase = (try? Mnemonic.generate().joined(separator: " ")) ?? .empty

hideNewPassphrase = false

form.rowBy(tag: Rows.saveYourPassphraseAlert.tag)?.evaluateHidden()

if let row: PassphraseRow = form.rowBy(tag: Rows.newPassphrase.tag) {
row.value = passphrase
row.updateCell()
row.evaluateHidden()
}

if let row = form.rowBy(tag: Rows.generateNewPassphraseButton.tag), let indexPath = row.indexPath {
tableView.scrollToRow(at: indexPath, at: .none, animated: true)
}
}

@MainActor
private func loginIntoExistingAccount(passphrase: String) {
Task {
do {
let result = try await accountService.loginWith(passphrase: passphrase, password: .empty)

if let nav = navigationController {
nav.popViewController(animated: true)
} else {
dismiss(animated: true, completion: nil)
}

dialogService.dismissProgress()

if case .success(_, let alert) = result,
let alert = alert
{
dialogService.showAlert(title: alert.title, message: alert.message, style: UIAlertController.Style.alert, actions: nil, from: nil)
}
} catch {
dialogService.dismissProgress()
dialogService.showRichError(error: error)
private func loginIntoExistingAccount(passphrase: String) async {
try? await accountService.loginWith(passphrase: passphrase, password: .empty)

if let nav = navigationController {
nav.popViewController(animated: true)
} else {
dismiss(animated: true, completion: nil)
}
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions Adamant/ServiceProtocols/AccountService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -161,16 +161,16 @@ protocol AccountService: AnyObject, Sendable {
func update(_ completion: (@Sendable (AccountServiceResult) -> Void)?)

/// Login into Adamant using passphrase.
func loginWith(passphrase: String, password: String) async throws -> AccountServiceResult
func loginWith(passphrase: String, password: String) async throws

/// Login into Adamant using previously logged account
func loginWithStoredAccount() async throws -> AccountServiceResult
func loginWithStoredAccount() async throws

/// Logout
@MainActor func logout() async

/// Reload current wallets state
func reloadWallets() async
//func reloadWallets() async

// MARK: Stay in functions

Expand Down
98 changes: 41 additions & 57 deletions Adamant/Services/AdamantAccountService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ final class AdamantAccountService: AccountService, @unchecked Sendable {
}.store(in: &subscriptions)

setupSecureStore()
}
}
}

// MARK: - Saved data
Expand Down Expand Up @@ -296,7 +296,7 @@ extension AdamantAccountService {
extension AdamantAccountService {
// MARK: Passphrase
@MainActor
func loginWith(passphrase: String, password: String) async throws -> AccountServiceResult {
func loginWith(passphrase: String, password: String) async throws {
guard AdamantUtilities.validateAdamantPassphrase(passphrase: passphrase) else {
throw AccountServiceError.invalidPassphrase
}
Expand All @@ -305,7 +305,7 @@ extension AdamantAccountService {
throw AccountServiceError.internalError(message: "Failed to generate keypair for passphrase", error: nil)
}

let account = try await loginWith(keypair: keypair)
try await loginWith(keypair: keypair)

// MARK: Drop saved accs
if let storedPassphrase = self.getSavedPassphrase(),
Expand All @@ -320,16 +320,11 @@ extension AdamantAccountService {
dropSavedAccount()
}

// Update and initiate wallet services
self.passphrase = passphrase

_ = await initWallets()

return .success(account: account, alert: nil)
}

// MARK: Pincode
func loginWith(pincode: String) async throws -> AccountServiceResult {
func loginWith(pincode: String) async throws {
guard let storePin = SecureStore.get(.pin) else {
throw AccountServiceError.invalidPassphrase
}
Expand All @@ -338,12 +333,12 @@ extension AdamantAccountService {
throw AccountServiceError.invalidPassphrase
}

return try await loginWithStoredAccount()
try await loginWithStoredAccount()
}

// MARK: Biometry
@MainActor
func loginWithStoredAccount() async throws -> AccountServiceResult {
func loginWithStoredAccount() async throws {
if let passphrase = getSavedPassphrase() {
let account = try await loginWith(passphrase: passphrase, password: .empty)
return account
Expand All @@ -367,68 +362,59 @@ extension AdamantAccountService {
wallet.core.setInitiationFailed(reason: .adamant.accountService.reloginToInitiateWallets)
}

return .success(account: account, alert: alert)
}

throw AccountServiceError.invalidPassphrase
}

// MARK: Keypair
private func loginWith(keypair: Keypair) async throws -> AdamantAccount {
private func loginWith(keypair: Keypair) async throws {
switch state {
case .isLoggingIn:
throw AccountServiceError.internalError(message: "Service is busy", error: nil)
// Logout first
case .updating, .loggedIn:
await logout()
// Go login
case .notLogged:
break
case .isLoggingIn:
throw AccountServiceError.internalError(message: "Service is busy", error: nil)

// Logout first
case .updating, .loggedIn:
await logout()

// Go login
case .notLogged:
break
}

state = .isLoggingIn

let address = adamantCore.getAddressFromPublicKey(keypair.publicKey)
self.keypair = keypair
//self.address = address

let userInfo = [AdamantUserInfoKey.AccountService.loggedAccountAddress: address]
NotificationCenter.default.post(
name: Notification.Name.AdamantAccountService.userLoggedIn,
object: self,
userInfo: userInfo
)

self.state = .loggedIn
do {
let account = try await apiService.getAccount(byPublicKey: keypair.publicKey).get()
self.account = account
self.keypair = keypair
markBalanceAsFresh()

let userInfo = [AdamantUserInfoKey.AccountService.loggedAccountAddress: account.address]

NotificationCenter.default.post(
name: Notification.Name.AdamantAccountService.userLoggedIn,
object: self,
userInfo: userInfo
)

self.state = .loggedIn
return account
} catch let error as ApiServiceError {
self.state = .notLogged

switch error {
case .accountNotFound:
throw AccountServiceError.wrongPassphrase

default:
throw AccountServiceError.apiError(error: error)
Task.detached(priority: .medium) { [weak self] in
guard let self else { return }
do {
let fetchedAccount = try await self.apiService.getAccount(byPublicKey: keypair.publicKey).get()
self.account = fetchedAccount
markBalanceAsFresh()
await initWallets()
} catch {
throw AccountServiceError.internalError(message: "Failed to fetch account in background", error: error)
}
}
} catch {
throw AccountServiceError.internalError(message: error.localizedDescription, error: error)
}
}

func reloadWallets() async {
_ = await initWallets()
}

func initWallets() async -> [WalletAccount?] {
func initWallets() async {
guard let passphrase = passphrase else {
print("No passphrase found")
return []
return
}

return await withTaskGroup(of: WalletAccount?.self) { group in
Expand All @@ -447,8 +433,6 @@ extension AdamantAccountService {
for await wallet in group {
wallets.append(wallet)
}

return wallets
}
}
}
Expand Down
14 changes: 14 additions & 0 deletions CommonKit/Sources/CommonKit/Core/NativeAdamantCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import CryptoSwift
import Foundation
import BigInt

/*
* Native Adamanat Core
Expand Down Expand Up @@ -210,6 +211,19 @@ public final class NativeAdamantCore: AdamantCore {

return seed.sha256()
}

// MARK: Address
public func getAddressFromPublicKey(_ publicKeyHex: String) -> String {
let publicKey = Data(hex: publicKeyHex)
let hash = publicKey.sha256()

let reversedBytes = (0..<8).map { i in
hash[7 - i]
}
let number = BigUInt(Data(reversedBytes))

return "U" + number.description
}

public init() {}
}
Expand Down
2 changes: 2 additions & 0 deletions CommonKit/Sources/CommonKit/Protocols/AdamantCore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public protocol AdamantCore: AnyObject, Sendable {
senderPublicKey senderKeyHex: String,
privateKey privateKeyHex: String
) -> Data?

func getAddressFromPublicKey(_ publicKeyHex: String) -> String
}

public protocol SignableTransaction {
Expand Down