Skip to content
Merged
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
2 changes: 1 addition & 1 deletion DashSyncCurrentCommit
Original file line number Diff line number Diff line change
@@ -1 +1 @@
fdf97505762702eb6290d71c61a93c8f6a57e3ba
753f9446cfd20ae5da996104a5870df839005d4c
43 changes: 30 additions & 13 deletions DashWallet/Sources/Models/CoinJoin/CoinJoinMixingTxSet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@
//

final class CoinJoinMixingTxSet: GroupedTransactions, TransactionWrapper {
override var title: String {
var title: String {
NSLocalizedString("Mixing Transactions", comment: "CoinJoin")
}

override var iconName: String {
var iconName: String {
"tx.item.coinjoin.icon"
}

override var infoText: String {
var infoText: String {
NSLocalizedString("You Dash was mixed using these transactions.", comment: "CoinJoin")
}

override var fiatAmount: String {
var fiatAmount: String {
(try? CurrencyExchanger.shared.convertDash(amount: (UInt64(abs(amount))).dashAmount, to: App.fiatCurrency).formattedFiatAmount) ??
NSLocalizedString("Updating Price", comment: "Updating Price")
}
Expand All @@ -37,15 +37,23 @@ final class CoinJoinMixingTxSet: GroupedTransactions, TransactionWrapper {
private let account = DWEnvironment.sharedInstance().currentAccount
private let amountQueue = DispatchQueue(label: "CoinJoinMixingSet.amount", qos: .utility)
private let amountLock = NSLock()
private let txMapLock = NSLock()
private var _amount: Int64 = 0
override var amount: Int64 {
var amount: Int64 {
return _amount
}

var transactionMap: [Data: Transaction] = [:]
override var transactions: [Transaction] {
private var _transactionMap: [Data: Transaction] = [:]
var transactionMap: [Data: Transaction] {
txMapLock.lock()
defer { txMapLock.unlock() }
return _transactionMap
}
var transactions: [Transaction] {
get {
return transactionMap.values.map { $0 }.sorted { tx1, tx2 in
txMapLock.lock()
defer { txMapLock.unlock() }
return _transactionMap.values.map { $0 }.sorted { tx1, tx2 in
tx1.date > tx2.date
}
}
Expand All @@ -63,27 +71,36 @@ final class CoinJoinMixingTxSet: GroupedTransactions, TransactionWrapper {
@discardableResult
func tryInclude(tx: DSTransaction) -> Bool {
let txHashData = tx.txHashData
let existing = transactionMap[txHashData] as? CoinJoinTransaction

txMapLock.lock()
let existing = _transactionMap[txHashData] as? CoinJoinTransaction

if existing != nil {
transactionMap[txHashData] = CoinJoinTransaction(transaction: tx, type: existing!.type)
_transactionMap[txHashData] = CoinJoinTransaction(transaction: tx, type: existing!.type)
txMapLock.unlock()
// Already included, return true
return true
}

let type = DSCoinJoinWrapper.coinJoinTxType(for: tx, account: account)

if type == CoinJoinTransactionType_None {
if type == CoinJoinTransactionType_None || type == CoinJoinTransactionType_Send {
txMapLock.unlock()
return false
}

if transactions.isEmpty {
let isEmpty = _transactionMap.isEmpty
txMapLock.unlock()

if isEmpty {
groupDay = tx.date
} else if !Calendar.current.isDate(tx.date, inSameDayAs: groupDay) {
return false
}

transactionMap[txHashData] = CoinJoinTransaction(transaction: tx, type: type)
txMapLock.lock()
_transactionMap[txHashData] = CoinJoinTransaction(transaction: tx, type: type)
txMapLock.unlock()

amountQueue.async { [weak self] in
guard let self = self else { return }
Expand Down
2 changes: 0 additions & 2 deletions DashWallet/Sources/Models/CoinJoin/CoinJoinTransaction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ class CoinJoinTransaction: Transaction {
NSLocalizedString("CoinJoin Mixing Fee", comment: "CoinJoin")
case CoinJoinTransactionType_Mixing:
NSLocalizedString("CoinJoin Mixing", comment: "CoinJoin")
case CoinJoinTransactionType_Send:
NSLocalizedString("CoinJoin Send", comment: "CoinJoin")
default:
""
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,19 @@
//

final class FullCrowdNodeSignUpTxSet: GroupedTransactions, TransactionWrapper {
override var title: String {
var title: String {
NSLocalizedString("CrowdNode · Account", comment: "CrowdNode")
}

override var iconName: String {
var iconName: String {
"tx.item.cn.icon"
}

override var infoText: String {
var infoText: String {
NSLocalizedString("Your CrowdNode account was created using these transactions.", comment: "Crowdnode")
}

override var fiatAmount: String {
var fiatAmount: String {
(try? CurrencyExchanger.shared.convertDash(amount: UInt64(abs(amount)).dashAmount, to: App.fiatCurrency).formattedFiatAmount) ??
NSLocalizedString("Updating Price", comment: "Updating Price")
}
Expand All @@ -37,20 +37,31 @@ final class FullCrowdNodeSignUpTxSet: GroupedTransactions, TransactionWrapper {
private let savedAccountAddress = CrowdNodeDefaults.shared.accountAddress
private let januaryFirst2022 = 1640995200.0 // Safe to assume there weren't any CrowdNode accounts before this point
private var matchedFilters: [CoinsToAddressTxFilter] = []
private let txMapLock = NSLock()

var transactionMap: [Data: Transaction] = [:]
override var transactions: [Transaction] {
private var _transactionMap: [Data: Transaction] = [:]
var transactionMap: [Data: Transaction] {
txMapLock.lock()
defer { txMapLock.unlock() }
return _transactionMap
}

var transactions: [Transaction] {
get {
return transactionMap.values.map { $0 }.sorted { tx1, tx2 in
txMapLock.lock()
defer { txMapLock.unlock() }
return _transactionMap.values.map { $0 }.sorted { tx1, tx2 in
tx1.date > tx2.date
}
}
}
private(set) var _amount: Int64 = 0
override var amount: Int64 { _amount }
private var _amount: Int64 = 0
var amount: Int64 { _amount }

var isComplete: Bool {
transactionMap.count == 5
txMapLock.lock()
defer { txMapLock.unlock() }
return _transactionMap.count == 5
}

var welcomeToApiResponse: CoinsToAddressTxFilter? {
Expand Down Expand Up @@ -85,11 +96,14 @@ final class FullCrowdNodeSignUpTxSet: GroupedTransactions, TransactionWrapper {

let txHashData = tx.txHashData

if transactionMap[txHashData] != nil {
transactionMap[txHashData] = Transaction(transaction: tx)
txMapLock.lock()
if _transactionMap[txHashData] != nil {
_transactionMap[txHashData] = Transaction(transaction: tx)
txMapLock.unlock()
// Already included, return true
return true
}
txMapLock.unlock()

var crowdNodeTxFilters = [
CrowdNodeRequest(requestCode: ApiCode.signUp),
Expand All @@ -103,7 +117,10 @@ final class FullCrowdNodeSignUpTxSet: GroupedTransactions, TransactionWrapper {
}

if let matchedFilter = crowdNodeTxFilters.first(where: { $0.matches(tx: tx) }) {
transactionMap[txHashData] = Transaction(transaction: tx)
txMapLock.lock()
_transactionMap[txHashData] = Transaction(transaction: tx)
txMapLock.unlock()

matchedFilters.append(matchedFilter)

let dashAmount = tx.dashAmount
Expand Down
37 changes: 29 additions & 8 deletions DashWallet/Sources/Models/Tx Metadata/DAO/IconBitmapDAO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,21 @@ import Foundation
import SQLite
import Combine

#if !swift(>=5.9)
// Provide `withLock` helper for older Swift toolchains to use scoped locking.
extension NSLock {
/// Executes `body` while holding the lock, ensuring the lock is always released.
/// - Returns: The value returned by `body`.
/// - Throws: Rethrows anything thrown by `body`.
@discardableResult
func withLock<T>(_ body: () throws -> T) rethrows -> T {
lock()
defer { unlock() }
return try body()
}
}
#endif

// MARK: - IconBitmapDAO

protocol IconBitmapDAO {
Expand All @@ -33,6 +48,8 @@ protocol IconBitmapDAO {
class IconBitmapDAOImpl: NSObject, IconBitmapDAO {
private var db: Connection { DatabaseConnection.shared.db }
private var cache: [String: IconBitmap] = [:]
/// Synchronizes access to `cache` to avoid data races when called from multiple threads/queues.
private let cacheLock = NSLock()
private var bitmapsSubject = CurrentValueSubject<[Data: IconBitmap], Never>([:])

static let shared = IconBitmapDAOImpl()
Expand All @@ -54,7 +71,7 @@ class IconBitmapDAOImpl: NSObject, IconBitmapDAO {
IconBitmap.width <- bitmap.width)
try await execute(insert)
let key = bitmap.id.hexEncodedString()
cache[key] = bitmap
cacheLock.withLock { cache[key] = bitmap }
updateBitmapsSubject()
} catch {
print("IconBitmapDAO addBitmap error: \(error)")
Expand All @@ -69,7 +86,7 @@ class IconBitmapDAOImpl: NSObject, IconBitmapDAO {
let bitmap = results.first
if let bitmap = bitmap {
let key = id.hexEncodedString()
cache[key] = bitmap
cacheLock.withLock { cache[key] = bitmap }
}
return bitmap
} catch {
Expand All @@ -87,7 +104,7 @@ class IconBitmapDAOImpl: NSObject, IconBitmapDAO {
do {
let deleteQuery = IconBitmap.table.delete()
try await execute(deleteQuery)
cache.removeAll()
cacheLock.withLock { cache.removeAll() }
updateBitmapsSubject()
} catch {
print("IconBitmapDAO clear error: \(error)")
Expand All @@ -97,10 +114,12 @@ class IconBitmapDAOImpl: NSObject, IconBitmapDAO {
private func loadAllBitmaps() async {
do {
let bitmaps: [IconBitmap] = try await prepare(IconBitmap.table)
cache.removeAll()
for bitmap in bitmaps {
let key = bitmap.id.hexEncodedString()
cache[key] = bitmap
cacheLock.withLock {
cache.removeAll()
for bitmap in bitmaps {
let key = bitmap.id.hexEncodedString()
cache[key] = bitmap
}
}
updateBitmapsSubject()
} catch {
Expand All @@ -109,7 +128,9 @@ class IconBitmapDAOImpl: NSObject, IconBitmapDAO {
}

private func updateBitmapsSubject() {
let bitmapsDict = cache.reduce(into: [Data: IconBitmap]()) { result, item in
let cachedCopy = cacheLock.withLock { cache }

let bitmapsDict = cachedCopy.reduce(into: [Data: IconBitmap]()) { result, item in
if let data = Data(hex: item.key) {
result[data] = item.value
}
Expand Down
14 changes: 7 additions & 7 deletions DashWallet/Sources/Models/Tx/GroupedTransactions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
// limitations under the License.
//

class GroupedTransactions {
var title: String { "" }
var iconName: String { "" }
var infoText: String { "" }
var fiatAmount: String { "" }
var amount: Int64 { 0 }
var transactions: [Transaction] { [] }
protocol GroupedTransactions {
var title: String { get }
var iconName: String { get }
var infoText: String { get }
var fiatAmount: String { get }
var amount: Int64 { get }
var transactions: [Transaction] { get }
}
4 changes: 2 additions & 2 deletions DashWallet/Sources/UI/Home/Views/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,9 @@ final class HomeView: UIView {

if status?.state == .done || completed {
let identity = model.dashPayModel.blockchainIdentity
let notificaitonAmount = model.dashPayModel.unreadNotificationsCount
let notificationAmount = model.dashPayModel.unreadNotificationsCount

delegate?.homeView(self, didUpdateProfile: identity, unreadNotifications: notificaitonAmount)
delegate?.homeView(self, didUpdateProfile: identity, unreadNotifications: notificationAmount)
} else {
delegate?.homeView(self, didUpdateProfile: nil, unreadNotifications: 0)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ struct SettingsMenuContent: View {
title: cjItem.title,
subtitleView: AnyView(CoinJoinSubtitle(cjItem)),
icon: .custom("image.coinjoin.menu"),
badgeText: NSLocalizedString("Beta", comment: ""),
badgeText: nil,
action: cjItem.action
)
} else {
Expand Down
Loading