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(dashpay): coinjoin entire wallet balance #1222

Merged
merged 11 commits into from
Nov 21, 2023

Conversation

HashEngineering
Copy link
Collaborator

@HashEngineering HashEngineering commented Oct 30, 2023

Issue being fixed or feature implemented

Related PR's and Dependencies

dashpay/dashj#235

Screenshots / Videos

How Has This Been Tested?

  • QA (Mobile Team)

Checklist:

  • I have performed a self-review of my own code and added comments where necessary
  • I have added or updated relevant unit/integration/functional/e2e tests

@HashEngineering HashEngineering self-assigned this Oct 30, 2023
@HashEngineering HashEngineering changed the title Dashpay coinjoin wallet feat(dashpay): coinjoin entire wallet balance Oct 30, 2023
Comment on lines +71 to +82
private var coinJoinSend = false
private val coroutineScope = CoroutineScope(Dispatchers.IO)

init {
coinJoinConfig
.observeMode()
.filterNotNull()
.onEach { mode ->
coinJoinSend = mode != CoinJoinMode.NONE
}
.launchIn(coroutineScope)
}
Copy link
Collaborator Author

@HashEngineering HashEngineering Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My first thought was to pass a "send with coinjoin" boolean value in many of these functions. But this would require that many ViewModels in many modules have access to the CoinJoinConfig.

My final idea was to load the CoinJoin mode here and no other modules or view models need to know anything about CoinJoin.

@@ -53,11 +54,11 @@ class CoinJoinLevelFragment : Fragment(R.layout.fragment_coinjoin_level) {
lifecycleScope.launch {
if (viewModel.isMixing) {
if (confirmStopMixing()) {
viewModel.stopMixing()
viewModel.setMode(CoinJoinMode.NONE)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting the CoinJoinMode will trigger the CoinJoinService to start or stop mixing.

@@ -100,7 +100,6 @@ data class BlockchainIdentityData(var creationState: CreationState = CreationSta
enum class CreationState {
NONE, // this should always be the first value
UPGRADING_WALLET,
MIXING_FUNDS,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mixing is no longer part of the Username Creation Process, but the UI hasn't been updated to reflect that.

Comment on lines 8 to 13
fun BlockchainServiceImpl.initCoinJoin() {
val scope = CoroutineScope(Executors.newSingleThreadExecutor().asCoroutineDispatcher())
scope.launch {
coinJoinService.configureMixing(true)
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to get rid of this extension. CoinJoinService is largely self-contained and should "configure itself" rather than be told to.

Comment on lines -135 to -149
override suspend fun waitForMixing() {
log.info("Waiting for mixing to complete by watching lock...")
mixingMutex.withLock {}
log.info("Mixing complete according to released lock")
}
override suspend fun waitForMixingWithException() {
waitForMixing()
exception?.let {
log.error("Mixing error: {}", it.message, it)
throw it
}
}
private fun setMixingComplete() {
mixingMutex.unlock()
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since mixing is not part of another process, there is no need for these waiting functions.

Comment on lines 181 to 184
.onEach { balance ->
log.info("coinjoin: total balance: $balance (before distinct)")
}
//.distinctUntilChanged()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These lines are for debugging.

I may remove this observer as it doesn't work as well as I would like. The blockchainStateProvider.observeState() may be sufficient. CoinJoin works on confirmed transactions, which means that having an observer that is triggered every block is ideal.

Sometimes there are a lot of ANR's when running this PR -- maybe its related to this observer. This observer doesn't seem to be triggered with each block, although it seems like it should.

Comment on lines -356 to +461
coroutineScope.launch(Dispatchers.IO) { updateMixingStatus(MixingStatus.FINISHED) }
coroutineScope.launch {
updateProgress()
updateMixingState(MixingStatus.FINISHED)
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed Dispatchers.IO because it seemed to not use the threading pool created above. This resulted in missing Context exceptions.

Comment on lines -123 to +135
Executors.newFixedThreadPool(2).asCoroutineDispatcher(),
Executors.newFixedThreadPool(2, ContextPropagatingThreadFactory("coinjoin-pool")).asCoroutineDispatcher()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ContextPropagatingThreadFactory will call Context.propogate in the created threads and we don't need to worry about missing Context exceptions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's neat.

Comment on lines +127 to 130
// TODO: the coin selector will need to use CoinJoinCoinSelector if CoinJoin is ON
walletDataProvider.observeBalance(coinSelector = MaxOutputAmountCoinSelector())
.distinctUntilChanged()
.onEach(_maxOutputAmount::postValue)
Copy link
Collaborator Author

@HashEngineering HashEngineering Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the UI story for Mixing Information, this will need to be fixed where we select the correct CoinSelector based on the user selected CoinJoinMode.

MaxOutputAmountCoinSelector does not account for CoinJoinMode != NONE

Comment on lines 80 to +82
interface CoinJoinService {
var mode: CoinJoinMode
val mixingStatus: MixingStatus

fun needsToMix(amount: Coin): Boolean
suspend fun configureMixing(
amount: Coin,
requestKeyParameter: RequestKeyParameter,
requestDecryptedKey: RequestDecryptedKey,
restoreFromConfig: Boolean
)

suspend fun prepareAndStartMixing()
suspend fun waitForMixing()
suspend fun waitForMixingWithException()
val mixingProgress: Flow<Double>
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The interface has been simplified to the point where this is used only for viewing progress.

@HashEngineering HashEngineering marked this pull request as ready for review November 20, 2023 21:49
Copy link
Member

@Syn-McJ Syn-McJ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great

Comment on lines -123 to +135
Executors.newFixedThreadPool(2).asCoroutineDispatcher(),
Executors.newFixedThreadPool(2, ContextPropagatingThreadFactory("coinjoin-pool")).asCoroutineDispatcher()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's neat.

updateBalance(balance)
}
}
.launchIn(uiCoroutineScope) // required for observeBalance
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be an oversight in WalletBalanceObserver. We should be able to observe balance in any thread. I'll check it when have a chance.

@HashEngineering HashEngineering merged commit d911db1 into dashpay Nov 21, 2023
1 check passed
@HashEngineering HashEngineering deleted the dashpay-coinjoin-wallet branch December 6, 2023 21:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants