Skip to content

Commit

Permalink
Merge pull request #1245 from novasamatech/develop
Browse files Browse the repository at this point in the history
8.7.2 Ledger for DApps Browser & Bugfixes
  • Loading branch information
svojsu authored Oct 16, 2024
2 parents 660307f + 5e5a3b5 commit 8a760ad
Show file tree
Hide file tree
Showing 58 changed files with 923 additions and 257 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ extension ICloudBackupServiceFactory: CloudBackupServiceFactoryProtocol {
CloudBackupSecretsExporter(
walletConverter: CloudBackupFileModelConverter(),
cryptoManager: createCryptoManager(),
validator: ICloudBackupValidator(),
keychain: keychain
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,24 @@ enum CloudBackupSecretsExporterError: Error {
case unsupportedWallet(MetaAccountModelType)
case invalidSecret(UInt8)
case brokenSecrets(MetaAccountModel.Id)
case validationFailed
}

final class CloudBackupSecretsExporter {
let walletConverter: CloudBackupFileModelConverting
let cryptoManager: CloudBackupCryptoManagerProtocol
let keychain: KeystoreProtocol
let validator: CloudBackupValidating

init(
walletConverter: CloudBackupFileModelConverting,
cryptoManager: CloudBackupCryptoManagerProtocol,
validator: CloudBackupValidating,
keychain: KeystoreProtocol
) {
self.walletConverter = walletConverter
self.cryptoManager = cryptoManager
self.validator = validator
self.keychain = keychain
}

Expand Down Expand Up @@ -333,12 +337,16 @@ extension CloudBackupSecretsExporter: CloudBackupSecretsExporting {
try createPrivateInfo(from: wallet)
}

let publicData = CloudBackup.PublicData(modifiedAt: modifiedAt, wallets: publicWalletsData)
let privateInfo = CloudBackup.DecryptedFileModel.PrivateData(wallets: Set(privateInfoList))

guard validator.validate(publicData: publicData, matches: privateInfo) else {
throw CloudBackupSecretsExporterError.validationFailed
}

let encodedPrivateInfo = try JSONEncoder().encode(privateInfo)
let encryptedInfo = try cryptoManager.encrypt(data: encodedPrivateInfo, password: password)

let publicData = CloudBackup.PublicData(modifiedAt: modifiedAt, wallets: publicWalletsData)

return .init(publicData: publicData, privateData: encryptedInfo.toHex())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,58 @@ protocol CloudBackupValidating {
) -> Bool
}

final class ICloudBackupValidator {}
final class ICloudBackupValidator {
private func validateSecrets(
privateInfo: CloudBackup.DecryptedFileModel.WalletPrivateInfo
) -> Bool {
privateInfo.substrate?.keypair != nil ||
privateInfo.ethereum?.keypair != nil ||
privateInfo.chainAccounts.contains { $0.keypair != nil }
}

private func validateLedger(privateInfo: CloudBackup.DecryptedFileModel.WalletPrivateInfo) -> Bool {
privateInfo.chainAccounts.contains { $0.derivationPath != nil }
}

private func validateGenericLedger(privateInfo: CloudBackup.DecryptedFileModel.WalletPrivateInfo) -> Bool {
privateInfo.substrate?.derivationPath != nil
}
}

extension ICloudBackupValidator: CloudBackupValidating {
func validate(
publicData _: CloudBackup.PublicData,
matches _: CloudBackup.DecryptedFileModel.PrivateData
publicData: CloudBackup.PublicData,
matches: CloudBackup.DecryptedFileModel.PrivateData
) -> Bool {
// TODO: Implement validation
true
let privateDataDict = matches.wallets.reduce(
into: [MetaAccountModel.Id: CloudBackup.DecryptedFileModel.WalletPrivateInfo]()
) {
$0[$1.walletId] = $1
}

return publicData.wallets.allSatisfy { wallet in
switch wallet.type {
case .secrets:
guard let privateInfo = privateDataDict[wallet.walletId] else {
return false
}

return validateSecrets(privateInfo: privateInfo)
case .ledger:
guard let privateInfo = privateDataDict[wallet.walletId] else {
return false
}

return validateLedger(privateInfo: privateInfo)
case .genericLedger:
guard let privateInfo = privateDataDict[wallet.walletId] else {
return false
}

return validateGenericLedger(privateInfo: privateInfo)
case .watchOnly, .paritySigner, .polkadotVault:
return true
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,18 @@ extension ProxyResolution {
}

let accounts: [AccountId: [MetaChainAccountResponse]]
let proxieds: [AccountId: MetaChainAccountResponse]

init(accounts: [AccountId: [MetaChainAccountResponse]]) {
self.accounts = accounts

proxieds = accounts.reduce(into: [AccountId: MetaChainAccountResponse]()) { accum, keyValue in
guard let account = keyValue.value.first(where: { $0.chainAccount.type == .proxied }) else {
return
}

accum[keyValue.key] = account
}
}

private func buildResult(
Expand Down Expand Up @@ -79,8 +88,10 @@ extension ProxyResolution {
}

let components = try solution.components.map { oldComponent in
let optAccount = accounts[oldComponent.proxyAccountId] ?? proxieds[oldComponent.proxyAccountId]

guard
let account = accounts[oldComponent.proxyAccountId],
let account = optAccount,
let proxyType = oldComponent.applicableTypes.first else {
throw PathFinderError.noAccount
}
Expand Down
25 changes: 17 additions & 8 deletions novawallet/Common/Services/Proxy/ChainProxySyncService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,25 +178,34 @@ final class ChainProxySyncService: ObservableSyncService, ChainProxySyncServiceP
let proxyList = try proxyListWrapper.targetOperation.extractNoCancellableResultData()
let chainMetaAccounts = try metaAccountsWrapper.targetOperation.extractNoCancellableResultData()

let notProxiedAccountIdList: [AccountId] = chainMetaAccounts.compactMap { wallet in
let possibleProxiesList: [AccountId] = chainMetaAccounts.compactMap { wallet in
guard wallet.info.type != .proxied else {
return nil
}

return wallet.info.fetch(for: chainModel.accountRequest())?.accountId
}

let notProxiedAccountIds = Set(notProxiedAccountIdList)
var possibleProxiesIds = Set(possibleProxiesList)
var prevProxiesIds = possibleProxiesIds
var proxies: [ProxiedAccountId: [ProxyAccount]] = [:]

// We only need remote proxieds for proxies we have locally and we don't support delaed proxies
let proxies = proxyList.compactMapValues { accounts in
accounts.filter {
!$0.hasDelay && notProxiedAccountIds.contains($0.accountId)
}
}.filter { !$0.value.isEmpty }
repeat {
// We only need remote proxieds for current proxies and we don't support delayed proxies
proxies = proxyList.compactMapValues { accounts in
accounts.filter {
!$0.hasDelay && possibleProxiesIds.contains($0.accountId)
}
}.filter { !$0.value.isEmpty }

prevProxiesIds = possibleProxiesIds
possibleProxiesIds = possibleProxiesIds.union(Set(proxies.keys))

} while possibleProxiesIds != prevProxiesIds

return proxies
}

proxyListOperation.addDependency(proxyListWrapper.targetOperation)
proxyListOperation.addDependency(metaAccountsWrapper.targetOperation)

Expand Down
58 changes: 44 additions & 14 deletions novawallet/Common/Storage/WalletsUpdateMediator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,44 @@ final class WalletUpdateMediator {
self.operationQueue = operationQueue
}

private static func nonProxiedWalletReachable(
from proxiedWallet: ManagedMetaAccountModel,
wallets: [ManagedMetaAccountModel],
removeIds: Set<MetaAccountModel.Id>
) -> Bool {
var currentProxieds: Set<MetaAccountModel.Id> = [proxiedWallet.info.metaId]
var prevProxieds = currentProxieds
var foundWallets: [MetaAccountModel.Id: ManagedMetaAccountModel] = [proxiedWallet.info.metaId: proxiedWallet]

repeat {
let newReachableWallets: [ManagedMetaAccountModel] = currentProxieds.flatMap { proxiedId in
guard
let proxied = foundWallets[proxiedId],
let chainAccount = proxied.info.chainAccounts.first(where: { $0.proxy != nil }),
let proxy = chainAccount.proxy else {
return [ManagedMetaAccountModel]()
}

return wallets.filter { $0.info.has(accountId: proxy.accountId, chainId: chainAccount.chainId) }
}

if newReachableWallets.contains(
where: { $0.info.type != .proxied && !removeIds.contains($0.info.metaId) }
) {
return true
}

foundWallets = newReachableWallets.reduce(into: foundWallets) {
$0[$1.info.metaId] = $1
}

prevProxieds = currentProxieds
currentProxieds = Set(foundWallets.keys)
} while prevProxieds != currentProxieds

return false
}

private static func includeProxiedsToRemoveSet(
starting removeIds: Set<MetaAccountModel.Id>,
wallets: [ManagedMetaAccountModel]
Expand All @@ -46,20 +84,12 @@ final class WalletUpdateMediator {
// we can have nested proxieds so we make sure to remove them all

repeat {
let newProxiedIdsToRemove = allProxieds.filter { proxiedWallet in
guard
let chainAccount = proxiedWallet.info.chainAccounts.first(where: { $0.proxy != nil }),
let proxy = chainAccount.proxy else {
return false
}

return wallets.allSatisfy { wallet in
guard !newRemovedIds.contains(wallet.identifier) else {
return true
}

return !wallet.info.has(accountId: proxy.accountId, chainId: chainAccount.chainId)
}
let newProxiedIdsToRemove = allProxieds.filter { proxied in
!nonProxiedWalletReachable(
from: proxied,
wallets: wallets,
removeIds: newRemovedIds
)
}.map(\.identifier)

oldRemovedIds = newRemovedIds
Expand Down
2 changes: 1 addition & 1 deletion novawallet/Common/ViewModel/RemoteImageViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ final class RemoteImageSerializer: CacheSerializer {
return uiImage
} else {
let imsvg = SVGKImage(data: data)
return imsvg?.uiImage ?? UIImage()
return imsvg?.uiImage
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,20 @@ final class DAppBrowserSigningState: DAppBrowserBaseState {
super.init(stateMachine: stateMachine)
}

private func provideOperationResponse(with signature: Data, nextState: DAppBrowserStateProtocol) throws {
private func provideOperationResponse(
with signature: Data,
modifiedTransaction: Data?,
nextState: DAppBrowserStateProtocol
) throws {
guard let msgType = signingType.msgType else {
return
}

let identifier = (0 ... UInt32.max).randomElement() ?? 0
let result = PolkadotExtensionSignerResult(
identifier: UInt(identifier),
signature: signature.toHex(includePrefix: true)
signature: signature.toHex(includePrefix: true),
signedTransaction: modifiedTransaction?.toHex(includePrefix: true)
)

try provideResponse(for: msgType, result: result, nextState: nextState)
Expand Down Expand Up @@ -54,7 +59,11 @@ extension DAppBrowserSigningState: DAppBrowserStateProtocol {

if let signature = response.signature {
do {
try provideOperationResponse(with: signature, nextState: nextState)
try provideOperationResponse(
with: signature,
modifiedTransaction: response.modifiedTransaction,
nextState: nextState
)
} catch {
stateMachine?.emit(error: error, nextState: nextState)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ final class DAppEthereumConfirmInteractor: DAppOperationBaseInteractor {
switch result {
case let .success(txHash):
let txHashData = try Data(hexString: txHash)
let response = DAppOperationResponse(signature: txHashData)
let response = DAppOperationResponse(signature: txHashData, modifiedTransaction: nil)
let result: Result<DAppOperationResponse, Error> = .success(response)
self.presenter?.didReceive(responseResult: result, for: self.request)
case let .failure(error):
Expand Down Expand Up @@ -234,7 +234,7 @@ final class DAppEthereumConfirmInteractor: DAppOperationBaseInteractor {
do {
switch result {
case let .success(signedTransaction):
let response = DAppOperationResponse(signature: signedTransaction)
let response = DAppOperationResponse(signature: signedTransaction, modifiedTransaction: nil)
let result: Result<DAppOperationResponse, Error> = .success(response)
self.presenter?.didReceive(responseResult: result, for: self.request)
case let .failure(error):
Expand Down Expand Up @@ -304,7 +304,7 @@ extension DAppEthereumConfirmInteractor: DAppOperationConfirmInteractorInputProt
}

func reject() {
let response = DAppOperationResponse(signature: nil)
let response = DAppOperationResponse(signature: nil, modifiedTransaction: nil)
presenter?.didReceive(responseResult: .success(response), for: request)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ extension DAppEthereumSignBytesInteractor: DAppOperationConfirmInteractorInputPr

do {
let signature = try signingOperation.extractNoCancellableResultData()
let response = DAppOperationResponse(signature: signature)
let response = DAppOperationResponse(signature: signature, modifiedTransaction: nil)

self.presenter?.didReceive(responseResult: .success(response), for: self.request)
} catch {
Expand All @@ -119,7 +119,7 @@ extension DAppEthereumSignBytesInteractor: DAppOperationConfirmInteractorInputPr
}

func reject() {
let response = DAppOperationResponse(signature: nil)
let response = DAppOperationResponse(signature: nil, modifiedTransaction: nil)
presenter?.didReceive(responseResult: .success(response), for: request)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,19 +79,13 @@ extension DAppOperationConfirmInteractor {
)
}

// TODO: Find out whether to return this validation

guard
let specVersion = BigUInt.fromHexString(extrinsic.specVersion) /* ,
codingFactory.specVersion == specVersion */ else {
let specVersion = BigUInt.fromHexString(extrinsic.specVersion) else {
throw DAppOperationConfirmInteractorError.extrinsicBadField(name: "specVersion")
}

// TODO: Find out whether to return this validation

guard
let transactionVersion = BigUInt.fromHexString(extrinsic.transactionVersion) /* ,
codingFactory.txVersion == transactionVersion */ else {
let transactionVersion = BigUInt.fromHexString(extrinsic.transactionVersion) else {
throw DAppOperationConfirmInteractorError.extrinsicBadField(name: "transactionVersion")
}

Expand Down Expand Up @@ -135,6 +129,8 @@ extension DAppOperationConfirmInteractor {
specVersion: UInt32(specVersion),
tip: tip,
transactionVersion: UInt32(transactionVersion),
metadataHash: extrinsic.metadataHash,
withSignedTransaction: extrinsic.withSignedTransaction ?? false,
signedExtensions: expectedSignedExtensions,
version: extrinsic.version
)
Expand Down
Loading

0 comments on commit 8a760ad

Please sign in to comment.