Skip to content

fix: fix username creation bugs and remove some ANR's #1350

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

Merged
merged 17 commits into from
Feb 25, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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 build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ buildscript {
coroutinesVersion = '1.6.4'
ok_http_version = '4.9.1'
dashjVersion = '21.1.6'
dppVersion = "1.7.2"
dppVersion = "1.7.3"
hiltVersion = '2.51'
hiltCompilerVersion = '1.2.0'
hiltWorkVersion = '1.0.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,6 @@ interface WalletDataProvider {
fun checkSendingConditions(address: Address?, amount: Coin)

fun observeMostRecentTransaction(): Flow<Transaction>
fun observeMixedBalance(): Flow<Coin>
fun observeTotalBalance(): Flow<Coin>
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ open class WalletUIConfig @Inject constructor(
val SHOW_TAP_TO_HIDE_HINT = booleanPreferencesKey("show_tap_to_hide_balance_hint")
val SELECTED_CURRENCY = stringPreferencesKey("exchange_currency")
val EXCHANGE_CURRENCY_DETECTED = booleanPreferencesKey("exchange_currency_detected")
val LAST_TOTAL_BALANCE = longPreferencesKey("last_total_balance")
val LAST_MIXED_BALANCE = longPreferencesKey("last_mixed_balance")
Comment on lines +66 to +67
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Save the last calculated values so that they can be emitted first when the app starts

}

suspend fun getExchangeCurrencyCode(): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ class DashDirectViewModel @Inject constructor(
.launchIn(viewModelScope)

walletDataProvider
.observeBalance()
.observeTotalBalance()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

observeBalance -> observeTotalBalance, make it explicity getting the total balance, not necessarily what is spendable.

This balance should be based on the CoinJoin Mode, but that will be handled later.

.distinctUntilChanged()
.onEach(_balance::postValue)
.launchIn(viewModelScope)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ class TransferDashViewModel @Inject constructor(
init {
getUserAccountAddress()
getUserData()
walletDataProvider.observeBalance()
walletDataProvider.observeTotalBalance()
.onEach(_dashBalanceInWalletState::postValue)
.launchIn(viewModelScope)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ class CrowdNodeViewModel @Inject constructor(
(crowdNodeBalance.value?.balance?.isLessThan(CrowdNodeConstants.MINIMUM_DASH_DEPOSIT) ?: true)

init {
walletDataProvider.observeBalance()
walletDataProvider.observeTotalBalance()
.distinctUntilChanged()
.onEach {
_dashBalance.postValue(it)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ class CrowdNodeViewModelTest {
}

private val walletData = mock<WalletDataProvider> {
on { observeBalance() } doReturn MutableStateFlow(balance)
on { observeTotalBalance() } doReturn MutableStateFlow(balance)
on {
freshReceiveAddress()
} doReturn Address.fromBase58(TestNet3Params.get(), "ydW78zVxRgNhANX2qtG4saSCC5ejNQjw2U")
Expand Down
3 changes: 2 additions & 1 deletion wallet/res/layout/wallet_transactions_fragment.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
android:gravity="center_horizontal"
android:textAlignment="gravity"
android:text="@string/wallet_transactions_fragment_empty_text"
android:textSize="@dimen/font_size_small" />
android:textSize="@dimen/font_size_small"
android:visibility="invisible"/>

<LinearLayout
android:layout_width="match_parent"
Expand Down
8 changes: 7 additions & 1 deletion wallet/src/de/schildbach/wallet/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public final class Constants {
public static boolean SUPPORTS_PLATFORM;
// TODO: remove all references to this when invites are enabled and functional
public static boolean SUPPORTS_INVITES;
// TODO: remove all references to this when transaction metadata is saved on platform
public static final boolean SUPPORTS_TXMETADATA;

public static final EnumSet<MasternodeSync.SYNC_FLAGS> SYNC_FLAGS = MasternodeSync.SYNC_DEFAULT_SPV;
public static final EnumSet<MasternodeSync.VERIFY_FLAGS> VERIFY_FLAGS = MasternodeSync.VERIFY_DEFAULT_SPV;
Expand Down Expand Up @@ -92,6 +94,7 @@ public final class Constants {
org.dash.wallet.common.util.Constants.INSTANCE.setEXPLORE_GC_FILE_PATH("explore/explore.db");
SUPPORTS_PLATFORM = !is32Bit;
SUPPORTS_INVITES = false;
SUPPORTS_TXMETADATA = false;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This will disable publishing tx-metadata to platform, which is an unfinished feature.

SYNC_FLAGS.add(MasternodeSync.SYNC_FLAGS.SYNC_HEADERS_MN_LIST_FIRST);
if (SUPPORTS_PLATFORM) {
SYNC_FLAGS.add(MasternodeSync.SYNC_FLAGS.SYNC_BLOCKS_AFTER_PREPROCESSING);
Expand All @@ -110,6 +113,7 @@ public final class Constants {
WALLET_NAME_CURRENCY_CODE = "tdash";
SUPPORTS_PLATFORM = !is32Bit;
SUPPORTS_INVITES = false;
SUPPORTS_TXMETADATA = false;
SYNC_FLAGS.add(MasternodeSync.SYNC_FLAGS.SYNC_HEADERS_MN_LIST_FIRST);
if (SUPPORTS_PLATFORM) {
SYNC_FLAGS.add(MasternodeSync.SYNC_FLAGS.SYNC_BLOCKS_AFTER_PREPROCESSING);
Expand All @@ -130,7 +134,9 @@ public final class Constants {
FEE_NETWORK_SUFFIX = "-testnet"; // use the same fee file as testnet
WALLET_NAME_CURRENCY_CODE = "tdash";
org.dash.wallet.common.util.Constants.EXPLORE_GC_FILE_PATH = "explore/explore-devnet.db";
SUPPORTS_PLATFORM = true;
SUPPORTS_PLATFORM = !is32Bit;
SUPPORTS_INVITES = false;
SUPPORTS_TXMETADATA = false;
Comment on lines +137 to +139
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We haven't made devnet builds in a long time, but let's update the feature flags.

SYNC_FLAGS.add(MasternodeSync.SYNC_FLAGS.SYNC_HEADERS_MN_LIST_FIRST);
SYNC_FLAGS.add(MasternodeSync.SYNC_FLAGS.SYNC_BLOCKS_AFTER_PREPROCESSING);
org.dash.wallet.common.util.Constants.FAUCET_URL = String.format("http://faucet.%s.networks.dash.org/", devNetName);
Expand Down
44 changes: 34 additions & 10 deletions wallet/src/de/schildbach/wallet/WalletApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,21 @@
import org.bitcoinj.core.TransactionBag;
import org.bitcoinj.core.VerificationException;
import org.bitcoinj.crypto.LinuxSecureRandom;
import org.bitcoinj.manager.DashSystem;
import org.bitcoinj.utils.Threading;
import org.bitcoinj.core.VersionMessage;
import org.bitcoinj.crypto.IKey;
import org.bitcoinj.wallet.AuthenticationKeyChain;
import org.bitcoinj.wallet.CoinSelector;
import org.bitcoinj.wallet.Protos;
import org.bitcoinj.wallet.UnreadableWalletException;
import org.bitcoinj.wallet.Wallet;
import org.bitcoinj.wallet.WalletEx;
import org.bitcoinj.wallet.WalletExtension;
import org.bitcoinj.wallet.WalletProtobufSerializer;
import org.bitcoinj.wallet.authentication.AuthenticationGroupExtension;
import org.bitcoinj.wallet.authentication.AuthenticationKeyUsage;
import org.dash.wallet.common.AutoLogoutTimerHandler;
import org.dash.wallet.common.Configuration;
import org.dash.wallet.common.InteractionAwareActivity;
import org.dash.wallet.common.WalletDataProvider;
import org.dash.wallet.common.data.WalletUIConfig;
import org.dash.wallet.common.services.LeftoverBalanceException;
import org.dash.wallet.common.services.TransactionMetadataProvider;
import org.dash.wallet.common.services.analytics.AnalyticsService;
Expand All @@ -91,17 +88,18 @@

import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import de.schildbach.wallet.data.CoinJoinConfig;
import de.schildbach.wallet.service.BlockchainStateDataProvider;
import de.schildbach.wallet.service.DashSystemService;
import de.schildbach.wallet.service.PackageInfoProvider;
import de.schildbach.wallet.service.WalletFactory;
import de.schildbach.wallet.transactions.MasternodeObserver;
import de.schildbach.wallet.transactions.WalletBalanceObserver;
import de.schildbach.wallet.ui.buy_sell.LiquidClient;
import org.dash.wallet.integrations.uphold.api.UpholdClient;
import org.dash.wallet.integrations.uphold.data.UpholdConstants;
import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConfig;
import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeBalanceCondition;
import org.dash.wallet.integrations.crowdnode.utils.CrowdNodeConfig;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -118,7 +116,6 @@
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import javax.inject.Inject;
Expand All @@ -130,8 +127,6 @@
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.rolling.RollingFileAppender;
import dagger.hilt.android.HiltAndroidApp;
import org.dash.wallet.common.data.entity.BlockchainState;
import de.schildbach.wallet.database.dao.BlockchainStateDao;
import de.schildbach.wallet.security.SecurityGuard;
import de.schildbach.wallet.service.BlockchainService;
import de.schildbach.wallet.service.BlockchainServiceImpl;
Expand Down Expand Up @@ -216,6 +211,9 @@ public class WalletApplication extends MultiDexApplication
WalletFactory walletFactory;
@Inject
DashSystemService dashSystemService;
@Inject
WalletUIConfig walletUIConfig;
private WalletBalanceObserver walletBalanceObserver;

@Override
protected void attachBaseContext(Context base) {
Expand Down Expand Up @@ -580,6 +578,9 @@ private void afterLoadWallet() {
// make sure there is at least one recent backup
if (!getFileStreamPath(Constants.Files.WALLET_KEY_BACKUP_PROTOBUF).exists())
backupWallet();

// setup WalletBalanceObserver
walletBalanceObserver = new WalletBalanceObserver(wallet, walletUIConfig);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

this is the "global" wallet observer.

}

private void deleteBlockchainFiles() {
Expand Down Expand Up @@ -1041,6 +1042,8 @@ public void finalizeWipe() {
// wallet must be null for the OnboardingActivity flow
log.info("removing wallet from memory during wipe");
wallet = null;
walletBalanceObserver.close();
walletBalanceObserver = null;
Comment on lines +1045 to +1046
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

when the wallet is replaced or reset, we should also close out the balance observer.

if (afterWipeFunction != null)
afterWipeFunction.invoke();
afterWipeFunction = null;
Expand Down Expand Up @@ -1103,7 +1106,28 @@ public Coin getWalletBalance() {
return Coin.ZERO;
}

return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
//return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
return walletBalanceObserver.getTotalBalance().getValue();
}

@NonNull
@Override
public Flow<Coin> observeTotalBalance() {
if (wallet == null) {
return FlowKt.emptyFlow();
}

return walletBalanceObserver.getTotalBalance();
}

@NonNull
@Override
public Flow<Coin> observeMixedBalance() {
if (wallet == null) {
return FlowKt.emptyFlow();
}

return walletBalanceObserver.getMixedBalance();
}

@NonNull
Expand All @@ -1116,7 +1140,7 @@ public Flow<Coin> observeBalance(
return FlowKt.emptyFlow();
}

return new WalletBalanceObserver(wallet, balanceType, coinSelector).observe();
return walletBalanceObserver.observe(balanceType, coinSelector);
}

@NonNull
Expand Down
42 changes: 32 additions & 10 deletions wallet/src/de/schildbach/wallet/service/BlockchainServiceImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.withContext
import org.bitcoinj.core.Address
import org.bitcoinj.core.Block
Expand Down Expand Up @@ -169,6 +170,7 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
private val serviceJob = SupervisorJob()
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceJob)
private val onCreateCompleted = CompletableDeferred<Unit>()
private var checkMutex = Mutex(false)

@Inject lateinit var application: WalletApplication

Expand Down Expand Up @@ -215,7 +217,6 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
private var peerGroup: PeerGroup? = null
private val handler = Handler()
private val delayHandler = Handler()
private val metadataHandler = Handler()
private var wakeLock: PowerManager.WakeLock? = null
private var peerConnectivityListener: PeerConnectivityListener? = null
private var nm: NotificationManager? = null
Expand All @@ -241,6 +242,8 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
private var syncPercentage = 0 // 0 to 100%
private var mixingStatus = MixingStatus.NOT_STARTED
private var mixingProgress = 0.0
private var balance = Coin.ZERO
private var mixedBalance = Coin.ZERO
private var foregroundService = ForegroundService.NONE

// Risk Analyser for Transactions that is PeerGroup Aware
Expand All @@ -254,10 +257,8 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
CrowdNodeDepositReceivedResponse(Constants.NETWORK_PARAMETERS)
private var apiConfirmationHandler: CrowdNodeAPIConfirmationHandler? = null
private fun handleMetadata(tx: Transaction) {
metadataHandler.post {
transactionMetadataProvider.syncTransactionBlocking(
tx
)
serviceScope.launch {
transactionMetadataProvider.syncTransaction(tx)
Comment on lines -257 to +261
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

use coroutine instead of a Handler()

}
}

Expand Down Expand Up @@ -683,7 +684,12 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
serviceScope.launch {
// make sure that onCreate is finished
onCreateCompleted.await()
checkService()
checkMutex.lock()
try {
checkService()
} finally {
checkMutex.unlock()
}
}
}

Expand All @@ -708,7 +714,7 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
packageInfoProvider.packageInfo
)
}
org.bitcoinj.core.Context.propagate(wallet.context)
propagateContext()
dashSystemService.system.initDashSync(getDir("masternode", MODE_PRIVATE).absolutePath)
log.info("starting peergroup")
peerGroup = PeerGroup(Constants.NETWORK_PARAMETERS, blockChain, headerChain)
Expand Down Expand Up @@ -1012,6 +1018,9 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
}

private fun propagateContext() {
if (application.wallet?.context != Constants.CONTEXT) {
log.warn("wallet context does not equal Constants.CONTEXT")
}
org.bitcoinj.core.Context.propagate(Constants.CONTEXT)
}

Expand Down Expand Up @@ -1125,21 +1134,31 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
coinJoinService.observeMixingProgress().observe(this@BlockchainServiceImpl) { mixingProgress ->
handleBlockchainStateNotification(blockchainState, mixingStatus, mixingProgress)
}

application.observeTotalBalance().observe(this@BlockchainServiceImpl) {
balance = it
handleBlockchainStateNotification(blockchainState, mixingStatus, mixingProgress)
}

application.observeMixedBalance().observe(this@BlockchainServiceImpl) {
mixedBalance = it
handleBlockchainStateNotification(blockchainState, mixingStatus, mixingProgress)
}
Comment on lines +1137 to +1146
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

balance updates will trigger notification updates


onCreateCompleted.complete(Unit) // Signal completion of onCreate
log.info(".onCreate() finished")
}
}

private fun createCoinJoinNotification(): Notification {
val mixedBalance = (application.wallet as WalletEx?)!!.coinJoinBalance
val totalBalance = application.wallet!!.balance
val notificationIntent = createIntent(this)
val pendingIntent = PendingIntent.getActivity(
this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val decimalFormat = DecimalFormat("0.000")
val statusStringId = when (mixingStatus) {
MixingStatus.NOT_STARTED -> R.string.coinjoin_not_started
MixingStatus.MIXING -> R.string.coinjoin_mixing
MixingStatus.PAUSED -> R.string.coinjoin_paused
MixingStatus.FINISHED -> R.string.coinjoin_progress_finished
Expand All @@ -1150,7 +1169,7 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
getString(statusStringId),
mixingProgress,
decimalFormat.format(mixedBalance.toBigDecimal()),
decimalFormat.format(totalBalance.toBigDecimal())
decimalFormat.format(balance.toBigDecimal())
)
return NotificationCompat.Builder(
this,
Expand Down Expand Up @@ -1300,6 +1319,7 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
serviceScope.launch {
try {
onCreateCompleted.await() // wait until onCreate is finished
checkMutex.lock()
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

There was a crash from check() running at the same time as onDestroy()

WalletApplication.scheduleStartBlockchainService(this@BlockchainServiceImpl) //disconnect feature
application.wallet!!.removeChangeEventListener(walletEventListener)
application.wallet!!.removeCoinsSentEventListener(walletEventListener)
Expand Down Expand Up @@ -1330,6 +1350,7 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
throw RuntimeException(x)
}
if (!deleteWalletFileOnShutdown) {
propagateContext()
application.saveWallet()
}
if (wakeLock!!.isHeld) {
Expand All @@ -1353,6 +1374,7 @@ class BlockchainServiceImpl : LifecycleService(), BlockchainService {
} finally {
log.info("serviceJob cancelled after " + (System.currentTimeMillis() - serviceCreatedAt) / 1000 / 60 + " minutes")
serviceJob.cancel()
checkMutex.unlock()
cleanupDeferred?.complete(Unit)
}
}
Expand Down
Loading
Loading