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
Merged
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ buildscript {
kotlin_version = '1.8.22'
coroutinesVersion = '1.6.4'
ok_http_version = '4.9.1'
dashjVersion = '19.1-CJ-SNAPSHOT'
dashjVersion = '20.0.0-CJ-SNAPSHOT'
hiltVersion = '2.45'
hiltWorkVersion = '1.0.0'
workRuntimeVersion='2.7.1'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ open class DatabaseMigrationTest {
private val service = ServiceName.CrowdNode
}

private val migrations = arrayOf(AppDatabaseMigrations.migration11To17, AppDatabaseMigrations.migration17To18)
private val migrations = arrayOf(
AppDatabaseMigrations.migration11To12,
AppDatabaseMigrations.migration12To17,
AppDatabaseMigrations.migration17To18
)

@Rule
@JvmField
Expand Down
5 changes: 3 additions & 2 deletions wallet/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,9 @@
<string name="coinjoin_advanced_time">Multiple hours</string>
<string name="coinjoin_start">Start Mixing</string>
<string name="coinjoin_stop">Stop Mixing</string>
<string name="coinjoin_progress">Mixing · %1$s of %2$s</string>
<string name="coinjoin_progress">Mixing · %1$s of %2$s (%3$s)</string>
<string name="coinjoin_progress_finished">Finished · %1$s of %2$s</string>
<string name="coinjoin_change_level_confirmation">Are you sure you want to change the privacy level?</string>
<string name="coinjoin_stop_mixing_title">Are you sure you want to stop mixing?</string>
<string name="coinjoin_stop_mixing_message">Any funds that have been mixed will be combined with your un mixed funds</string>
<string name="coinjoin_stop_mixing_message">Any funds that have been mixed will be combined with your unmixed funds</string>
</resources>
2 changes: 1 addition & 1 deletion wallet/schnapps/res/values/values.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<item>µtDASH, no decimal places</item>
</string-array>
<string-array name="preferences_block_explorer_values">
<item>https://insight.bintang.networks.dash.org:3002/insight</item>
<item>https://insight.ouzo.networks.dash.org:3002/insight</item>
</string-array>
<string-array name="preferences_block_explorer_labels">
<item>Insight Devnet Block Explorer</item>
Expand Down
4 changes: 2 additions & 2 deletions wallet/src/de/schildbach/wallet/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
import org.bitcoinj.crypto.ChildNumber;
import org.bitcoinj.params.DevNetParams;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.ScrewDriverDevNetParams;
import org.bitcoinj.params.OuzoDevNetParams;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.utils.MonetaryFormat;
import org.bitcoinj.wallet.DeterministicKeyChain;
Expand Down Expand Up @@ -102,7 +102,7 @@ public final class Constants {
case "devnet": {
// Schnapps Devnet
BIP44_PATH = DeterministicKeyChain.BIP44_ACCOUNT_ZERO_PATH_TESTNET;
NETWORK_PARAMETERS = ScrewDriverDevNetParams.get();
NETWORK_PARAMETERS = OuzoDevNetParams.get();
String devNetName = ((DevNetParams)NETWORK_PARAMETERS).getDevNetName();
devNetName = devNetName.substring(devNetName.indexOf("-") + 1);
DNS_SEED = NETWORK_PARAMETERS.getDnsSeeds();
Expand Down
18 changes: 18 additions & 0 deletions wallet/src/de/schildbach/wallet/data/CoinJoinConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ import android.content.Context
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.core.longPreferencesKey
import androidx.datastore.preferences.core.stringPreferencesKey
import de.schildbach.wallet.service.CoinJoinMode
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import org.dash.wallet.common.WalletDataProvider
import org.dash.wallet.common.data.BaseConfig
import javax.inject.Inject
Expand All @@ -34,10 +39,23 @@ open class CoinJoinConfig @Inject constructor(
) : BaseConfig(context, PREFERENCES_NAME, walletDataProvider) {
companion object {
const val PREFERENCES_NAME = "coinjoin"
val COINJOIN_MODE = stringPreferencesKey("coinjoin_mode")
val COINJOIN_ROUNDS = intPreferencesKey("coinjoin_rounds")
val COINJOIN_SESSIONS = intPreferencesKey("coinjoin_sessions")
val COINJOIN_MULTISESSION = booleanPreferencesKey("coinjoin_multisession")
val COINJOIN_AMOUNT = longPreferencesKey("coinjoin_amount")
val FIRST_TIME_INFO_SHOWN = booleanPreferencesKey("first_time_info_shown")
}

fun observeMode(): Flow<CoinJoinMode> {
return observe(COINJOIN_MODE).filterNotNull().map { mode -> CoinJoinMode.valueOf(mode!!) }
}

suspend fun getMode(): CoinJoinMode {
return get(COINJOIN_MODE).let { CoinJoinMode.valueOf(it!!) }
}

suspend fun setMode(mode: CoinJoinMode) {
set(COINJOIN_MODE, mode.toString())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.

CREDIT_FUNDING_TX_CREATING,
CREDIT_FUNDING_TX_SENDING,
CREDIT_FUNDING_TX_SENT,
Expand Down
6 changes: 4 additions & 2 deletions wallet/src/de/schildbach/wallet/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import de.schildbach.wallet.WalletApplication
import de.schildbach.wallet.data.CoinJoinConfig
import de.schildbach.wallet.payments.ConfirmTransactionLauncher
import de.schildbach.wallet.payments.SendCoinsTaskRunner
import de.schildbach.wallet.security.SecurityFunctions
Expand Down Expand Up @@ -99,9 +100,10 @@ abstract class AppModule {
walletData: WalletDataProvider,
walletApplication: WalletApplication,
securityFunctions: SecurityFunctions,
packageInfoProvider: PackageInfoProvider
packageInfoProvider: PackageInfoProvider,
coinJoinConfig: CoinJoinConfig
): SendPaymentService {
val realService = SendCoinsTaskRunner(walletData, walletApplication, securityFunctions, packageInfoProvider)
val realService = SendCoinsTaskRunner(walletData, walletApplication, securityFunctions, packageInfoProvider, coinJoinConfig)

return if (BuildConfig.FLAVOR.lowercase() == "prod") {
realService
Expand Down
37 changes: 27 additions & 10 deletions wallet/src/de/schildbach/wallet/payments/SendCoinsTaskRunner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ package de.schildbach.wallet.payments

import androidx.annotation.VisibleForTesting
import de.schildbach.wallet.WalletApplication
import de.schildbach.wallet.data.CoinJoinConfig
import de.schildbach.wallet.data.PaymentIntent
import de.schildbach.wallet.payments.parsers.PaymentIntentParser
import de.schildbach.wallet.security.SecurityFunctions
import de.schildbach.wallet.security.SecurityGuard
import de.schildbach.wallet.service.CoinJoinMode
import de.schildbach.wallet.service.PackageInfoProvider
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import okhttp3.CacheControl
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull
Expand All @@ -34,7 +39,6 @@ import okio.IOException
import org.bitcoin.protocols.payments.Protos
import org.bitcoin.protocols.payments.Protos.Payment
import org.bitcoinj.coinjoin.CoinJoinCoinSelector
import org.bitcoinj.coinjoin.UnmixedZeroConfCoinSelector
import org.bitcoinj.core.*
import org.bitcoinj.crypto.IKey
import org.bitcoinj.crypto.KeyCrypterException
Expand All @@ -57,12 +61,25 @@ class SendCoinsTaskRunner @Inject constructor(
private val walletData: WalletDataProvider,
private val walletApplication: WalletApplication,
private val securityFunctions: SecurityFunctions,
private val packageInfoProvider: PackageInfoProvider
private val packageInfoProvider: PackageInfoProvider,
coinJoinConfig: CoinJoinConfig
) : SendPaymentService {
companion object {
private const val WALLET_EXCEPTION_MESSAGE = "this method can't be used before creating the wallet"
private val log = LoggerFactory.getLogger(SendCoinsTaskRunner::class.java)
}
private var coinJoinSend = false
private val coroutineScope = CoroutineScope(Dispatchers.IO)

init {
coinJoinConfig
.observeMode()
.filterNotNull()
.onEach { mode ->
coinJoinSend = mode != CoinJoinMode.NONE
}
.launchIn(coroutineScope)
}
Comment on lines +71 to +82
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.


@Throws(LeftoverBalanceException::class)
override suspend fun sendCoins(
Expand Down Expand Up @@ -233,12 +250,11 @@ class SendCoinsTaskRunner @Inject constructor(
mayEditAmount: Boolean,
paymentIntent: PaymentIntent,
signInputs: Boolean,
forceEnsureMinRequiredFee: Boolean,
coinJoin: Boolean = false
forceEnsureMinRequiredFee: Boolean
): SendRequest {
val wallet = walletData.wallet ?: throw RuntimeException(WALLET_EXCEPTION_MESSAGE)
val sendRequest = paymentIntent.toSendRequest()
sendRequest.coinSelector = getCoinSelector(coinJoin)
sendRequest.coinSelector = getCoinSelector()
sendRequest.useInstantSend = false
sendRequest.feePerKb = Constants.ECONOMIC_FEE
sendRequest.ensureMinRequiredFee = forceEnsureMinRequiredFee
Expand All @@ -256,15 +272,14 @@ class SendCoinsTaskRunner @Inject constructor(
amount: Coin,
coinSelector: CoinSelector? = null,
emptyWallet: Boolean = false,
forceMinFee: Boolean = true,
coinJoin: Boolean = false
forceMinFee: Boolean = true
): SendRequest {
return SendRequest.to(address, amount).apply {
this.feePerKb = Constants.ECONOMIC_FEE
this.ensureMinRequiredFee = forceMinFee
this.emptyWallet = emptyWallet

val selector = coinSelector ?: getCoinSelector(coinJoin)
val selector = coinSelector ?: getCoinSelector()
this.coinSelector = selector

if (selector is ByAddressCoinSelector) {
Expand All @@ -273,10 +288,12 @@ class SendCoinsTaskRunner @Inject constructor(
}
}

private fun getCoinSelector(coinJoin: Boolean) = if (coinJoin) {
private fun getCoinSelector() = if (coinJoinSend) {
// mixed only
CoinJoinCoinSelector(walletData.wallet)
} else {
UnmixedZeroConfCoinSelector(walletData.wallet)
// collect all coins, mixed and unmixed
ZeroConfCoinSelector.get()
}

@Throws(LeftoverBalanceException::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
import org.bitcoinj.utils.MonetaryFormat;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.wallet.DefaultRiskAnalysis;
import org.bitcoinj.wallet.Protos;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.authentication.AuthenticationGroupExtension;
import org.dash.wallet.common.Configuration;
Expand Down Expand Up @@ -165,7 +164,6 @@ public class BlockchainServiceImpl extends LifecycleService implements Blockchai
@Inject PackageInfoProvider packageInfoProvider;
@Inject ConnectivityManager connectivityManager;
@Inject BlockchainStateDataProvider blockchainStateDataProvider;

private BlockStore blockStore;
private BlockStore headerStore;
private File blockChainFile;
Expand Down
Loading
Loading