Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(coinjoin): wifi warning #669

Merged
merged 7 commits into from
Oct 15, 2024
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
24 changes: 12 additions & 12 deletions DashWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10290,7 +10290,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
Expand Down Expand Up @@ -10430,7 +10430,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
Expand Down Expand Up @@ -10604,7 +10604,7 @@
EXCLUDED_ARCHS = "";
IBSC_MODULE = WatchApp_Extension;
INFOPLIST_FILE = WatchApp/Info.plist;
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
Expand All @@ -10626,7 +10626,7 @@
EXCLUDED_ARCHS = "";
IBSC_MODULE = WatchApp_Extension;
INFOPLIST_FILE = WatchApp/Info.plist;
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
Expand All @@ -10651,7 +10651,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
Expand All @@ -10678,7 +10678,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
Expand Down Expand Up @@ -11418,7 +11418,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
Expand Down Expand Up @@ -11587,7 +11587,7 @@
EXCLUDED_ARCHS = "";
IBSC_MODULE = WatchApp_Extension;
INFOPLIST_FILE = WatchApp/Info.plist;
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
Expand All @@ -11612,7 +11612,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
Expand Down Expand Up @@ -11729,7 +11729,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
OTHER_LDFLAGS = (
"$(inherited)",
"-ObjC",
Expand Down Expand Up @@ -11896,7 +11896,7 @@
EXCLUDED_ARCHS = "";
IBSC_MODULE = WatchApp_Extension;
INFOPLIST_FILE = WatchApp/Info.plist;
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
Expand All @@ -11921,7 +11921,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
MARKETING_VERSION = 8.2.0;
MARKETING_VERSION = 8.3.0;
PRODUCT_BUNDLE_IDENTIFIER = org.dashfoundation.dash.watchkitapp.watchkitextension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ extension SyncingActivityMonitor {
state = .noConnection
return
}
updateProgress()
DispatchQueue.main.async {
self.updateProgress()
}
}

private func updateProgress() {
Expand Down
105 changes: 48 additions & 57 deletions DashWallet/Sources/Models/CoinJoin/CoinJoinService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,10 @@ public class CoinJoinServiceWrapper: NSObject {
}
}

class CoinJoinService: NSObject {
class CoinJoinService: NSObject, NetworkReachabilityHandling {
var networkStatusDidChange: ((NetworkStatus) -> ())?
var reachabilityObserver: Any!

static let shared: CoinJoinService = {
return CoinJoinService()
}()
Expand All @@ -85,7 +88,6 @@ class CoinJoinService: NSObject {
private let updateMixingStateMutex = NSLock()
private var coinJoinManager: DSCoinJoinManager? = nil
private var hasAnonymizableBalance: Bool = false
private var networkStatus: NetworkStatus = .online
private var timeSkew: TimeInterval = 0
private var workingChain: ChainType

Expand All @@ -95,42 +97,50 @@ class CoinJoinService: NSObject {
}
}

private var savedMode: Int {
private var currentMode: CoinJoinMode {
get {
return UserDefaults.standard.integer(forKey: chainModeKey)
let current = CoinJoinMode(rawValue: UserDefaults.standard.integer(forKey: chainModeKey)) ?? .none

if self.mode != current {
self.mode = current
}

return current
}
set(value) { UserDefaults.standard.set(value, forKey: chainModeKey) }
}

@Published private(set) var mode: CoinJoinMode = .none {
didSet {
savedMode = mode.rawValue
set(value) {
self.mode = value
UserDefaults.standard.set(value.rawValue, forKey: chainModeKey)
}
}

@Published private(set) var mode: CoinJoinMode = .none
@Published var mixingState: MixingStatus = .notStarted
@Published private(set) var progress = CoinJoinProgress(progress: 0.0, totalBalance: 0, coinJoinBalance: 0)
@Published private(set) var activeSessions: Int = 0
@Published private(set) var networkStatus: NetworkStatus = .online

override init() {
workingChain = DWEnvironment.sharedInstance().currentChain.chainType
super.init()

networkStatusDidChange = { [weak self] state in
self?.updateNetworkState(newState: state)
}
NotificationCenter.default.publisher(for: NSNotification.Name.DWCurrentNetworkDidChange)
.sink { [weak self] _ in
DSLogger.log("[SW] CoinJoin: change of network to \(DWEnvironment.sharedInstance().currentChain.chainType.tag), resetting")
DSLogger.log("CoinJoin: change of network to \(DWEnvironment.sharedInstance().currentChain.chainType.tag), resetting")
self?.workingChain = DWEnvironment.sharedInstance().currentChain.chainType
self?.restoreMode()
}
.store(in: &permanentBag)

restoreMode()
startNetworkMonitoring()
}

func updateMode(mode: CoinJoinMode) async {
func updateMode(mode: CoinJoinMode, force: Bool = false) async {
self.coinJoinManager?.updateOptions(withEnabled: mode != .none)

if mode != .none && self.mode == .none {
if mode != .none && (force || self.currentMode == .none) {
configureMixing()
configureObservers()
} else if mode == .none {
Expand All @@ -139,8 +149,7 @@ class CoinJoinService: NSObject {

let account = DWEnvironment.sharedInstance().currentAccount
updateBalance(balance: account.balance)
let currentTimeSkew = await getCurrentTimeSkew()
updateState(mode: mode, timeSkew: currentTimeSkew, hasAnonymizableBalance: self.hasAnonymizableBalance, networkStatus: self.networkStatus, chain: DWEnvironment.sharedInstance().currentChain)
updateState(mode: mode, timeSkew: self.timeSkew, hasAnonymizableBalance: self.hasAnonymizableBalance, networkStatus: self.networkStatus, chain: DWEnvironment.sharedInstance().currentChain)
}

func updateTimeSkew(timeSkew: TimeInterval) {
Expand All @@ -159,7 +168,7 @@ class CoinJoinService: NSObject {
guard let coinJoinManager = self.coinJoinManager else { return }

if !coinJoinManager.startMixing() {
DSLogger.log("[SW] CoinJoin: Mixing has been started already.")
DSLogger.log("CoinJoin: Mixing has been started already.")
} else {
coinJoinManager.refreshUnusedKeys()
coinJoinManager.initMasternodeGroup()
Expand All @@ -172,7 +181,7 @@ class CoinJoinService: NSObject {

let account = DWEnvironment.sharedInstance().currentAccount
let rounds: Int32
switch mode {
switch currentMode {
case .none:
return
case .intermediate:
Expand Down Expand Up @@ -209,14 +218,14 @@ class CoinJoinService: NSObject {
guard let coinJoinManager = self.coinJoinManager else { return }

coinJoinManager.updateOptions(withAmount: balance)
DSLogger.log("[SW] CoinJoin: total balance: \(balance)")
DSLogger.log("CoinJoin: total balance: \(balance)")
let canDenominate = coinJoinManager.doAutomaticDenominating(withDryRun: true)

let coinJoinBalance = coinJoinManager.getBalance()
DSLogger.log("[SW] CoinJoin: mixed balance: \(coinJoinBalance.anonymized)")
DSLogger.log("CoinJoin: mixed balance: \(coinJoinBalance.anonymized)")

let anonBalance = coinJoinManager.getAnonymizableBalance(withSkipDenominated: false, skipUnconfirmed: false)
DSLogger.log("[SW] CoinJoin: anonymizable balance \(anonBalance)")
DSLogger.log("CoinJoin: anonymizable balance \(anonBalance)")

let smallestDenomination = coinJoinManager.getSmallestDenomination()
let hasPartiallyMixedCoins = (coinJoinBalance.denominatedTrusted - coinJoinBalance.anonymized) > 0
Expand All @@ -231,10 +240,10 @@ class CoinJoinService: NSObject {
hasBalanceLeftToMix = false
}

DSLogger.log("[SW] CoinJoin: can mix balance: \(hasBalanceLeftToMix) = balance: (\(anonBalance > smallestDenomination) && canDenominate: \(canDenominate)) || partially-mixed: \(hasPartiallyMixedCoins)")
DSLogger.log("CoinJoin: can mix balance: \(hasBalanceLeftToMix) = balance: (\(anonBalance > smallestDenomination) && canDenominate: \(canDenominate)) || partially-mixed: \(hasPartiallyMixedCoins)")

updateState(
mode: self.mode,
mode: self.currentMode,
timeSkew: self.timeSkew,
hasAnonymizableBalance: hasBalanceLeftToMix,
networkStatus: self.networkStatus,
Expand All @@ -259,11 +268,11 @@ class CoinJoinService: NSObject {
}

synchronized(self.updateMutex) {
DSLogger.log("[SW] CoinJoin: \(mode), \(timeSkew) s, \(hasAnonymizableBalance), \(networkStatus), synced: \(SyncingActivityMonitor.shared.state == .syncDone)")
DSLogger.log("CoinJoin: \(mode), \(timeSkew) s, \(hasAnonymizableBalance), \(networkStatus), synced: \(SyncingActivityMonitor.shared.state == .syncDone)")

self.networkStatus = networkStatus
self.hasAnonymizableBalance = hasAnonymizableBalance
self.mode = mode
self.currentMode = mode
self.timeSkew = timeSkew

if mode == .none || !isInsideTimeSkewBounds(timeSkew: timeSkew) || DWGlobalOptions.sharedInstance().isResyncingWallet {
Expand Down Expand Up @@ -293,12 +302,7 @@ class CoinJoinService: NSObject {
}

let previousMixingStatus = self.mixingState
DSLogger.log("[SW] CoinJoin: \(previousMixingStatus) -> \(state)")

if previousMixingStatus == .paused && state != .paused {
DSLogger.log("[SW] CoinJoin: moving from paused to \(state)")
}

DSLogger.log("CoinJoin: \(previousMixingStatus) -> \(state)")
self.mixingState = state

if state == .mixing && previousMixingStatus != .mixing {
Expand All @@ -314,7 +318,7 @@ class CoinJoinService: NSObject {

private func updateTimeSkewInternal(timeSkew: TimeInterval) {
let chain = DWEnvironment.sharedInstance().currentChain
updateState(mode: self.mode,
updateState(mode: self.currentMode,
timeSkew: timeSkew,
hasAnonymizableBalance: self.hasAnonymizableBalance,
networkStatus: self.networkStatus,
Expand All @@ -323,7 +327,7 @@ class CoinJoinService: NSObject {

private func getCurrentTimeSkew() async -> TimeInterval {
do {
return await TimeInterval(try TimeUtils.getTimeSkew())
return try await TimeUtils.getTimeSkew()
} catch {
DSLogger.log("[SW] CoinJoin: getTimeSkew problem: \(error)")
return 0.0
Expand All @@ -334,14 +338,9 @@ class CoinJoinService: NSObject {
self.stopMixing()
self.coinJoinManager = nil
self.hasAnonymizableBalance = false
let savedMode = self.savedMode
self.mode = .none
let restoredMode = CoinJoinMode(rawValue: savedMode) ?? .none

if restoredMode != .none {
Task {
await updateMode(mode: restoredMode)
}
Task {
await updateMode(mode: self.currentMode, force: true)
self.updateTimeSkewInternal(timeSkew: await getCurrentTimeSkew())
}
}

Expand Down Expand Up @@ -387,6 +386,11 @@ class CoinJoinService: NSObject {
SyncingActivityMonitor.shared.add(observer: self)
}

private func updateNetworkState(newState: NetworkStatus) {
self.networkStatus = newState
self.updateState(mode: mode, timeSkew: timeSkew, hasAnonymizableBalance: self.hasAnonymizableBalance, networkStatus: self.networkStatus, chain: DWEnvironment.sharedInstance().currentChain)
}

private func removeObservers() {
cancellableBag.forEach { $0.cancel() }
SyncingActivityMonitor.shared.remove(observer: self)
Expand All @@ -400,13 +404,9 @@ class CoinJoinService: NSObject {
}

extension CoinJoinService: DSCoinJoinManagerDelegate {
func sessionStarted(withId baseId: Int32, clientSessionId clientId: UInt256, denomination denom: UInt32, poolState state: PoolState, poolMessage message: PoolMessage, ipAddress address: UInt128, isJoined joined: Bool) {
updateActiveSessions()
}
func sessionStarted(withId baseId: Int32, clientSessionId clientId: UInt256, denomination denom: UInt32, poolState state: PoolState, poolMessage message: PoolMessage, ipAddress address: UInt128, isJoined joined: Bool) { }

func sessionComplete(withId baseId: Int32, clientSessionId clientId: UInt256, denomination denom: UInt32, poolState state: PoolState, poolMessage message: PoolMessage, ipAddress address: UInt128, isJoined joined: Bool) {
updateActiveSessions()
}
func sessionComplete(withId baseId: Int32, clientSessionId clientId: UInt256, denomination denom: UInt32, poolState state: PoolState, poolMessage message: PoolMessage, ipAddress address: UInt128, isJoined joined: Bool) { }

func mixingStarted() { }

Expand All @@ -429,23 +429,14 @@ extension CoinJoinService: DSCoinJoinManagerDelegate {
func transactionProcessed(withId txId: UInt256, type: CoinJoinTransactionType) {
self.updateProgress()
}

private func updateActiveSessions() {
guard let coinJoinManager = self.coinJoinManager else { return }

let activeSessions = coinJoinManager.getActiveSessionCount()
self.activeSessions = Int(activeSessions)

DSLogger.log("[SW] CoinJoin: Active sessions: \(activeSessions)")
}
}

extension CoinJoinService: SyncingActivityMonitorObserver {
func syncingActivityMonitorProgressDidChange(_ progress: Double) { }

func syncingActivityMonitorStateDidChange(previousState: SyncingActivityMonitor.State, state: SyncingActivityMonitor.State) {
self.updateState(
mode: self.mode,
mode: self.currentMode,
timeSkew: self.timeSkew,
hasAnonymizableBalance: self.hasAnonymizableBalance,
networkStatus: self.networkStatus,
Expand Down
Loading