Skip to content

Commit

Permalink
fix(dashpay): fix username request voting period bugs (#1318)
Browse files Browse the repository at this point in the history
* fix(dashpay): fix several username request creation and restore bugs

* fix: add ANR monitor

* fix: remove Dispatchers.IO
  • Loading branch information
HashEngineering authored Oct 30, 2024
1 parent 4706eff commit f419160
Show file tree
Hide file tree
Showing 11 changed files with 62 additions and 15 deletions.
4 changes: 4 additions & 0 deletions wallet/src/de/schildbach/wallet/WalletApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@
import de.schildbach.wallet.transactions.WalletMostRecentTransactionsObserver;
import de.schildbach.wallet.security.PinRetryController;
import de.schildbach.wallet.util.AllowLockTimeRiskAnalysis;
import de.schildbach.wallet.util.AnrSupervisor;
import de.schildbach.wallet.util.CrashReporter;
import de.schildbach.wallet.util.LogMarkerFilter;
import de.schildbach.wallet.util.MnemonicCodeExt;
Expand Down Expand Up @@ -188,6 +189,7 @@ public class WalletApplication extends MultiDexApplication
public Activity currentActivity;

private AutoLogout autoLogout;
private AnrSupervisor anrSupervisor;

@Inject
RestartService restartService;
Expand Down Expand Up @@ -326,6 +328,8 @@ private void logState() {
}

resetBlockchainSyncProgress();
anrSupervisor = new AnrSupervisor();
anrSupervisor.start();
}

private void syncExploreData() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ package de.schildbach.wallet.database.entity

import androidx.room.Entity
import androidx.room.PrimaryKey
import de.schildbach.wallet.Constants
import org.bitcoinj.core.NetworkParameters
import java.util.concurrent.TimeUnit

@Entity(tableName = "username_requests")
data class UsernameRequest(
Expand All @@ -34,6 +37,8 @@ data class UsernameRequest(
val isApproved: Boolean
) {
companion object {
val VOTING_PERIOD_MILLIS = if (Constants.NETWORK_PARAMETERS.id == NetworkParameters.ID_MAINNET) TimeUnit.DAYS.toMillis(14) else TimeUnit.MINUTES.toMillis(90)
val SUBMIT_PERIOD_MILLIS = VOTING_PERIOD_MILLIS / 2
fun getRequestId(identity: String, username: String): String {
return String.format("%s-%s", identity, username)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ import org.dashj.platform.contracts.wallet.TxMetadataItem
import org.dashj.platform.dashpay.ContactRequest
import org.dashj.platform.dashpay.UsernameRequestStatus
import org.dashj.platform.dpp.identifier.Identifier
import org.dashj.platform.dpp.voting.ContestedDocumentResourceVotePoll
import org.dashj.platform.sdk.PlatformValue
import org.dashj.platform.sdk.platform.DomainDocument
import org.dashj.platform.wallet.IdentityVerify
import org.slf4j.Logger
Expand Down Expand Up @@ -225,7 +227,7 @@ class PlatformSynchronizationService @Inject constructor(
if (blockchainIdentityData.creationState < BlockchainIdentityData.CreationState.DONE) {
// Is the Voting Period complete?
if (blockchainIdentityData.creationState == BlockchainIdentityData.CreationState.VOTING) {
val timeWindow = if (Constants.NETWORK_PARAMETERS.id == NetworkParameters.ID_MAINNET) TimeUnit.DAYS.toMillis(14) else TimeUnit.MINUTES.toMillis(90)
val timeWindow = UsernameRequest.VOTING_PERIOD_MILLIS
if (System.currentTimeMillis() - blockchainIdentityData.votingPeriodStart!! >= timeWindow) {
val resource = platformRepo.getUsername(blockchainIdentityData.username!!)
if (resource.status == Status.SUCCESS) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,12 @@ class CreateIdentityService : LifecycleService() {
if (blockchainIdentityData.creationStateErrorMessage?.contains("preorderDocument was not found with a salted domain hash") == true) {
blockchainIdentityData.creationState = CreationState.PREORDER_REGISTERING
platformRepo.updateBlockchainIdentityData(blockchainIdentityData)
} else if (retryWithNewUserName) {
// lets rewind the state to allow for a new username registration or request
// it may have failed later in the process
if (blockchainIdentityData.creationState > CreationState.USERNAME_REGISTERING) {
blockchainIdentityData.creationState = CreationState.USERNAME_REGISTERING
}
}
}
}
Expand Down Expand Up @@ -919,7 +925,18 @@ class CreateIdentityService : LifecycleService() {
false
)
)
val usernameInfo = blockchainIdentity.usernameStatuses[blockchainIdentity.currentUsername!!]!!
// what if usernameInfo would have been null, we should create it.

var usernameInfo = blockchainIdentity.usernameStatuses[blockchainIdentity.currentUsername!!]
if (usernameInfo == null) {
usernameInfo = UsernameInfo(
null,
UsernameStatus.CONFIRMED,
blockchainIdentity.currentUsername!!,
UsernameRequestStatus.VOTING
)
blockchainIdentity.usernameStatuses[blockchainIdentity.currentUsername!!] = usernameInfo
}

// determine when voting started by finding the minimum timestamp
val earliestCreatedAt = voteContenders.map.values.minOf {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ class HistoryHeaderAdapter(

if (blockchainIdentityData.creationStateErrorMessage != null) {
if (blockchainIdentityData.creationState == BlockchainIdentityData.CreationState.USERNAME_REGISTERING &&
blockchainIdentityData.creationStateErrorMessage.contains("Document transitions with duplicate unique properties")) {
(blockchainIdentityData.creationStateErrorMessage.contains("Document transitions with duplicate unique properties") ||
blockchainIdentityData.creationStateErrorMessage.contains("Document Contest for vote_poll ContestedDocumentResourceVotePoll")) ||
blockchainIdentityData.creationStateErrorMessage.contains(Regex("does not have .* as a contender"))
) {
binding.identityCreation.title.text = binding.root.context.getString(R.string.processing_username_unavailable_title)
binding.identityCreation.subtitle.visibility = View.VISIBLE
binding.identityCreation.icon.setImageResource(R.drawable.ic_username_unavailable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.annotation.SuppressLint
import android.app.Application
import androidx.work.*
import de.schildbach.wallet.Constants
import de.schildbach.wallet.database.entity.UsernameRequest
import org.bitcoinj.core.NetworkParameters
import org.slf4j.LoggerFactory
import java.text.DateFormat
Expand Down Expand Up @@ -49,11 +50,7 @@ class GetUsernameVotingResultOperation(val application: Application) {
@SuppressLint("EnqueueWork")
fun create(username: String, identityId: String, votingStartedAt: Long): WorkContinuation {
log.info("scheduling work to check username voting status")
val delay = System.currentTimeMillis() - if (Constants.NETWORK_PARAMETERS.id != NetworkParameters.ID_MAINNET) {
TimeUnit.MINUTES.toMillis(90)
} else {
TimeUnit.DAYS.toMillis(14)
} + votingStartedAt + TimeUnit.MINUTES.toMillis(2)
val delay = System.currentTimeMillis() - UsernameRequest.VOTING_PERIOD_MILLIS + votingStartedAt + TimeUnit.MINUTES.toMillis(2)
log.info("scheduling work to check username voting status on {}",
DateFormat.getDateInstance(DateFormat.FULL).format(
Date(System.currentTimeMillis() + delay)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,8 @@ class WalletTransactionsFragment : Fragment(R.layout.wallet_transactions_fragmen
private fun openIdentityCreation() {
viewModel.blockchainIdentity.value?.let { blockchainIdentityData ->
if (blockchainIdentityData.creationStateErrorMessage != null) {
if (blockchainIdentityData.creationState == BlockchainIdentityData.CreationState.USERNAME_REGISTERING) {
if (blockchainIdentityData.creationState == BlockchainIdentityData.CreationState.USERNAME_REGISTERING ||
blockchainIdentityData.creationState == BlockchainIdentityData.CreationState.REQUESTED_NAME_CHECKING) {
startActivity(CreateUsernameActivity.createIntentReuseTransaction(requireActivity(), blockchainIdentityData))
} else {
Toast.makeText(requireContext(), blockchainIdentityData.creationStateErrorMessage, Toast.LENGTH_LONG).show()
Expand Down
3 changes: 2 additions & 1 deletion wallet/src/de/schildbach/wallet/ui/more/MoreFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import de.schildbach.wallet.Constants
import de.schildbach.wallet.WalletApplication
import de.schildbach.wallet.database.entity.BlockchainIdentityData
import de.schildbach.wallet.database.entity.DashPayProfile
import de.schildbach.wallet.database.entity.UsernameRequest
import de.schildbach.wallet.livedata.Status
import de.schildbach.wallet.service.PackageInfoProvider
import de.schildbach.wallet.ui.CreateUsernameActivity
Expand Down Expand Up @@ -237,7 +238,7 @@ class MoreFragment : Fragment(R.layout.fragment_more) {
binding.joinDashpayContainer.visibility = View.GONE
binding.requestedUsernameContainer.visibility = View.VISIBLE
val votingPeriod = it.votingPeriodStart?.let { startTime ->
val endTime = startTime + if (Constants.NETWORK_PARAMETERS.id == NetworkParameters.ID_MAINNET) TimeUnit.DAYS.toMillis(14) else TimeUnit.MINUTES.toMillis(90)
val endTime = startTime + UsernameRequest.VOTING_PERIOD_MILLIS
val dateFormat = DateFormat.getMediumDateFormat(requireContext())
String.format("%s", dateFormat.format(endTime))
} ?: "Voting Period not found"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import de.schildbach.wallet.service.CoinJoinMode
import de.schildbach.wallet.ui.dashpay.CreateIdentityService
import de.schildbach.wallet.ui.dashpay.PlatformRepo
import de.schildbach.wallet.ui.dashpay.work.BroadcastIdentityVerifyOperation
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
Expand All @@ -54,6 +55,7 @@ import org.dashj.platform.dashpay.UsernameRequestStatus
import org.dashj.platform.dpp.identifier.Identifier
import org.dashj.platform.sdk.platform.DomainDocument
import org.dashj.platform.sdk.platform.Names
import org.slf4j.LoggerFactory
import javax.inject.Inject
import kotlin.math.max

Expand All @@ -78,13 +80,16 @@ data class RequestUserNameUIState(
@HiltViewModel
class RequestUserNameViewModel @Inject constructor(
val walletApplication: WalletApplication,
val identityConfig: BlockchainIdentityConfig,
private val identityConfig: BlockchainIdentityConfig,
val walletData: WalletDataProvider,
val platformRepo: PlatformRepo,
val usernameRequestDao: UsernameRequestDao,
val coinJoinConfig: CoinJoinConfig,
val analytics: AnalyticsService
) : ViewModel() {
companion object {
private val log = LoggerFactory.getLogger(RequestUserNameViewModel::class.java)
}
private val _uiState = MutableStateFlow(RequestUserNameUIState())
val uiState: StateFlow<RequestUserNameUIState> = _uiState.asStateFlow()

Expand Down Expand Up @@ -136,6 +141,7 @@ class RequestUserNameViewModel @Inject constructor(
identityBalance = identity?.let { identity ->
platformRepo.getIdentityBalance(Identifier.from(identity.userId)).balance
} ?: 0
log.info("identity balance: {}", identityBalance)
if (requestedUserName == null) {
requestedUserName = identityConfig.get(USERNAME)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.google.android.material.textfield.TextInputLayout
import dagger.hilt.android.AndroidEntryPoint
import de.schildbach.wallet.Constants
import de.schildbach.wallet.database.entity.BlockchainIdentityData
import de.schildbach.wallet.database.entity.UsernameRequest
import de.schildbach.wallet.ui.dashpay.DashPayViewModel
import de.schildbach.wallet_test.R
import de.schildbach.wallet_test.databinding.FragmentRequestUsernameBinding
Expand Down Expand Up @@ -135,8 +136,10 @@ class RequestUsernameFragment : Fragment(R.layout.fragment_request_username) {
binding.walletBalanceContainer.isVisible = !it.enoughBalance
if (it.usernameContestable || it.usernameContested) {
val startDate = Date(it.votingPeriodStart)
val endDate = Date(startDate.time + if (Constants.NETWORK_PARAMETERS.id == NetworkParameters.ID_MAINNET) TimeUnit.DAYS.toMillis(14) else TimeUnit.MINUTES.toMillis(90))
if (it.votingPeriodStart == -1L && System.currentTimeMillis() - it.votingPeriodStart > TimeUnit.DAYS.toMillis(7)) {
val endDate = Date(startDate.time + UsernameRequest.VOTING_PERIOD_MILLIS)
if (it.votingPeriodStart == -1L && System.currentTimeMillis() - it.votingPeriodStart > UsernameRequest.VOTING_PERIOD_MILLIS) {
binding.votingPeriodContainer.isVisible = false
} else if (it.votingPeriodStart == -1L && System.currentTimeMillis() - it.votingPeriodStart > UsernameRequest.SUBMIT_PERIOD_MILLIS) {
binding.votingPeriodContainer.isVisible = false
} else {
val dateFormat = DateFormat.getMediumDateFormat(context)
Expand All @@ -162,6 +165,12 @@ class RequestUsernameFragment : Fragment(R.layout.fragment_request_username) {
binding.checkAvailable.setImageResource(getCheckMarkImage(false, false))
binding.votingPeriodContainer.isVisible = false
}
it.usernameContestable && (it.votingPeriodStart == -1L && System.currentTimeMillis() - it.votingPeriodStart > UsernameRequest.VOTING_PERIOD_MILLIS) -> {
// the submission period has ended, let us just say the username is taken
binding.usernameAvailableMessage.text = getString(R.string.request_username_taken)
binding.checkAvailable.setImageResource(getCheckMarkImage(false, false))
binding.votingPeriodContainer.isVisible = false
}
it.usernameContestable -> {
// voting period container will be visible
binding.usernameAvailableContainer.isVisible = false
Expand Down Expand Up @@ -197,7 +206,8 @@ class RequestUsernameFragment : Fragment(R.layout.fragment_request_username) {
return@observe
}
if (it?.creationStateErrorMessage != null) {
requireActivity().finish()
//why are we closing, we should allow the user to chose a new name
//requireActivity().finish()
} else if ((it?.creationState?.ordinal ?: 0) > BlockchainIdentityData.CreationState.NONE.ordinal) {
// completeUsername = it.username ?: ""
// showCompleteState()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import de.schildbach.wallet.Constants
import de.schildbach.wallet.database.entity.UsernameRequest
import de.schildbach.wallet_test.R
import de.schildbach.wallet_test.databinding.FragmentVotingRequestDetailsBinding
import org.bitcoinj.core.NetworkParameters
Expand Down Expand Up @@ -58,7 +59,7 @@ class VotingRequestDetailsFragment : Fragment(R.layout.fragment_voting_request_d
binding.identity.text = myUsernameRequest?.identity
var isVotingOver = false
val votingResults = myUsernameRequest?.createdAt?.let { startTime ->
val endTime = startTime + if (Constants.NETWORK_PARAMETERS.id == NetworkParameters.ID_MAINNET) TimeUnit.DAYS.toMillis(14) else TimeUnit.MINUTES.toMillis(90)
val endTime = startTime + UsernameRequest.VOTING_PERIOD_MILLIS
val dateFormat = DateFormat.getMediumDateFormat(requireContext())
isVotingOver = endTime < System.currentTimeMillis()
if (isVotingOver) {
Expand Down

0 comments on commit f419160

Please sign in to comment.