Skip to content

Commit

Permalink
fix(dashpay): address various coinjoin issues (#1242)
Browse files Browse the repository at this point in the history
* fix: set account to 0

* fix: return change in credit funding tx's

* chore: cleanup CoinJoinLevelViewModel

* fix: attempt to improve updateBalance

* fix(coinjoin): fix a few problems from refactoring dashj

* build(dashpay): fix firebase app distribution
  • Loading branch information
HashEngineering authored Jan 8, 2024
1 parent fc31944 commit 8ba371f
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 22 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dashpay.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,4 +94,4 @@ jobs:

- name: Staging Build and Firebase Distribution
if: github.event_name == 'push'
run: bundle exec fastlane build_distribute flavor:"staging" type:"release" storepass:"${{ secrets.SIGNING_STORE_PASS }}" versioncode:"${{ env.build_number }}" comment:"Up to date Dash Wallet TestNet build" appid:"1:1039839682638:android:3202b16d460a1a88" testgroup:"qa" SUPPORT_EMAIL:"${{ secrets.INTERNAL_SUPPORT_EMAIL }}"
run: bundle exec fastlane build_distribute flavor:"staging" type:"release" storepass:"${{ secrets.SIGNING_STORE_PASS }}" versioncode:"${{ env.build_number }}" comment:"Up to date Dash Wallet TestNet build" appid:"1:1039839682638:android:bbcfa8c9939ee993ea631f" testgroup:"qa"
14 changes: 7 additions & 7 deletions wallet/src/de/schildbach/wallet/WalletApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -391,23 +391,23 @@ public void setWallet(Wallet newWallet) throws GeneralSecurityException, IOExcep
authKeyTypes
);

authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY_FUNDING);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY_TOPUP);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.INVITATION_FUNDING);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY_FUNDING);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.BLOCKCHAIN_IDENTITY_TOPUP);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.INVITATION_FUNDING);

authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.MASTERNODE_OWNER);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.MASTERNODE_VOTING);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.MASTERNODE_OPERATOR);
authenticationGroupExtension.freshKey(AuthenticationKeyChain.KeyChainType.MASTERNODE_PLATFORM_OPERATOR);
authenticationGroupExtension.setWallet(wallet);
authenticationGroupExtension.setWallet(wallet);
}
}
}
WalletEx walletEx = (WalletEx) wallet;
if (walletEx.getCoinJoin() != null) {
// this wallet is not encrypted yet
walletEx.initializeCoinJoin(null, 0);
}
}
}
}

Expand Down
46 changes: 37 additions & 9 deletions wallet/src/de/schildbach/wallet/service/CoinJoinService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,28 @@ class CoinJoinMixingService @Inject constructor(
log.info("coinjoin: mixed balance: ${walletEx.coinJoinBalance.toFriendlyString()}")
val anonBalance = walletEx.getAnonymizableBalance(false, false)
log.info("coinjoin: anonymizable balance {}", anonBalance.toFriendlyString())

val hasPartiallyMixedCoins = (walletEx.denominatedBalance - walletEx.coinJoinBalance).isGreaterThan(Coin.ZERO)
val hasAnonymizableBalance = anonBalance.isGreaterThan(CoinJoin.getSmallestDenomination())
log.info("coinjoin: mixing can occur: $hasAnonymizableBalance")

val canDenominate = if (this::clientManager.isInitialized) {
clientManager.doAutomaticDenominating(true)
} else {
// if the client manager is not running, just say canDenominate is true
// The first round of execution will determine if mixing can happen
log.info("coinjoin: client manager is not running, canDemoninate=true")
true
}

val hasBalanceLeftToMix = when {
hasPartiallyMixedCoins -> true
hasAnonymizableBalance && canDenominate -> true
else -> false
}

log.info("coinjoin: mixing can occur: $hasBalanceLeftToMix = balance: (${anonBalance.isGreaterThan(CoinJoin.getSmallestDenomination())} && canDenominate: $canDenominate) || partially-mixed: $hasPartiallyMixedCoins")
updateState(
config.getMode(),
hasAnonymizableBalance,
hasBalanceLeftToMix,
networkStatus,
isSynced,
blockchainStateProvider.getBlockChain()
Expand All @@ -226,12 +242,12 @@ class CoinJoinMixingService @Inject constructor(
) {
updateMutex.lock()
log.info(
"coinjoin-updateState: ${this.mode}, ${this.hasAnonymizableBalance}, ${this.networkStatus}, ${this.isSynced} ${blockChain != null}"
"coinjoin-old-state: ${this.mode}, ${this.hasAnonymizableBalance}, ${this.networkStatus}, synced: ${this.isSynced} ${blockChain != null}"
)
try {
setBlockchain(blockChain)
log.info(
"coinjoin-updateState: $mode, $hasAnonymizableBalance, $networkStatus, $isSynced, ${blockChain != null}"
"coinjoin-new-state: $mode, $hasAnonymizableBalance, $networkStatus, synced: $isSynced, ${blockChain != null}"
)
this.networkStatus = networkStatus
this.hasAnonymizableBalance = hasAnonymizableBalance
Expand Down Expand Up @@ -413,7 +429,10 @@ class CoinJoinMixingService @Inject constructor(
MixingCompleteListener { _, statusList ->
statusList?.let {
for (status in it) {
if (status != PoolStatus.FINISHED && status != PoolStatus.ERR_NOT_ENOUGH_FUNDS && status != PoolStatus.ERR_NO_INPUTS) {
if (status != PoolStatus.FINISHED &&
status != PoolStatus.ERR_NOT_ENOUGH_FUNDS &&
status != PoolStatus.ERR_NO_INPUTS
) {
coroutineScope.launch { updateMixingState(MixingStatus.ERROR) }
exception = Exception("Mixing stopped before completion ${status.name}")
return@let
Expand Down Expand Up @@ -451,6 +470,7 @@ class CoinJoinMixingService @Inject constructor(

setRequestKeyParameter(requestKeyParameter)
setRequestDecryptedKey(requestDecryptedKey)
start()
}
}

Expand All @@ -464,6 +484,7 @@ class CoinJoinMixingService @Inject constructor(
// run this on a different thread?
val asyncStart = coroutineScope.async(Dispatchers.IO) {
Context.propagate(walletDataProvider.wallet!!.context)
coinJoinManager?.initMasternodeGroup(blockChain)
clientManager.doAutomaticDenominating()
}
val result = asyncStart.await()
Expand All @@ -475,11 +496,10 @@ class CoinJoinMixingService @Inject constructor(
}

private fun stopMixing() {
log.info("Mixing process will stop...")
if (coinJoinManager == null || !this::clientManager.isInitialized) {
return
}

log.info("Mixing process will stop...")
encryptionKey = null

// if mixing is not complete, then tell the future we didn't finish yet
Expand All @@ -489,6 +509,8 @@ class CoinJoinMixingService @Inject constructor(
// remove all listeners
mixingCompleteListeners.forEach { coinJoinManager?.removeMixingCompleteListener(it) }
sessionCompleteListeners.forEach { coinJoinManager?.removeSessionCompleteListener(it) }
clientManager.stopMixing()
coinJoinManager?.stop()
}

private fun setBlockchain(blockChain: AbstractBlockChain?) {
Expand Down Expand Up @@ -525,7 +547,13 @@ class CoinJoinMixingService @Inject constructor(
val anonymizableBalance = wallet.getAnonymizableBalance(false, false)
if (mixedBalance != Coin.ZERO && anonymizableBalance != Coin.ZERO) {
val progress = mixedBalance.value * 100.0 / (mixedBalance.value + anonymizableBalance.value)
log.info("coinjoin: progress {}", progress)
log.info(
"coinjoin: progress {} = 100*{}/({} + {})",
progress,
mixedBalance.value,
mixedBalance.value,
anonymizableBalance.value
)
_progressFlow.emit(progress)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import de.schildbach.wallet.data.CoinJoinConfig
import de.schildbach.wallet.service.CoinJoinMode
import de.schildbach.wallet.service.CoinJoinService
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.filterNotNull
Expand All @@ -35,15 +34,14 @@ import javax.inject.Inject
@HiltViewModel
open class CoinJoinLevelViewModel @Inject constructor(
private val analytics: AnalyticsService,
private val coinJoinService: CoinJoinService,
private val coinJoinConfig: CoinJoinConfig,
private var networkState: NetworkStateInt
) : ViewModel() {

val isMixing: Boolean
get() = _mixingMode.value != CoinJoinMode.NONE

val _mixingMode = MutableStateFlow<CoinJoinMode>(CoinJoinMode.NONE)
private val _mixingMode = MutableStateFlow(CoinJoinMode.NONE)

val mixingMode: Flow<CoinJoinMode>
get() = _mixingMode
Expand Down
11 changes: 9 additions & 2 deletions wallet/src/de/schildbach/wallet/ui/dashpay/PlatformRepo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import de.schildbach.wallet.livedata.SeriousError
import de.schildbach.wallet.livedata.SeriousErrorListener
import de.schildbach.wallet.livedata.Status
import de.schildbach.wallet.security.SecurityGuard
import de.schildbach.wallet.service.CoinJoinMode
import de.schildbach.wallet.service.platform.PlatformService
import io.grpc.StatusRuntimeException
import kotlinx.coroutines.*
Expand Down Expand Up @@ -84,7 +85,8 @@ class PlatformRepo @Inject constructor(
val walletApplication: WalletApplication,
val blockchainIdentityDataDao: BlockchainIdentityConfig,
val appDatabase: AppDatabase,
val platform: PlatformService
val platform: PlatformService,
val coinJoinConfig: CoinJoinConfig
) {

@EntryPoint
Expand Down Expand Up @@ -1019,7 +1021,12 @@ class PlatformRepo @Inject constructor(
// dashj Context does not work with coroutines well, so we need to call Context.propogate
// in each suspend method that uses the dashj Context
Context.propagate(walletApplication.wallet!!.context)
val cftx = blockchainIdentity.createInviteFundingTransaction(Constants.DASH_PAY_FEE, keyParameter, useCoinJoin = false, returnChange = true)
val cftx = blockchainIdentity.createInviteFundingTransaction(
Constants.DASH_PAY_FEE,
keyParameter,
useCoinJoin = coinJoinConfig.getMode() != CoinJoinMode.NONE,
returnChange = true
)
val invitation = Invitation(cftx.creditBurnIdentityIdentifier.toStringBase58(), cftx.txId,
System.currentTimeMillis())
// update database
Expand Down

0 comments on commit 8ba371f

Please sign in to comment.