Skip to content

Commit

Permalink
AND-79 Fix WC crash loop (#146)
Browse files Browse the repository at this point in the history
* Check WC ID proof request nullability

* Handle unexpected WC session request handling errors

* Await the WC connection before sending responses

Fix trying to send a response for a pending request when there is no
connection yet.

* Update the changelog
  • Loading branch information
Radiokot authored Oct 8, 2024
1 parent c4719b3 commit a9ef461
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

- Inability to configure a validator closed for delegation
- Incorrect state of the account tokens page when there are no tokens
- Crash caused by a malformed WalletConnect verifiable presentation request

## [1.2.0] - 2024-08-27

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,15 @@ class WalletConnectVerifiablePresentationRequestHandler(
try {
val wrappedParams =
App.appCore.gson.fromJson(params, WalletConnectParamsWrapper::class.java)
this.identityProofRequest = UnqualifiedRequest.fromJson(wrappedParams.paramsJson)
this.identityProofRequest = UnqualifiedRequest.fromJson(wrappedParams.paramsJson).also {
// Manual checks required because the JSON mapper doesn't care.
checkNotNull(it.credentialStatements) {
"Credential statements can't be null"
}
checkNotNull(it.challenge) {
"Challenge can't be null"
}
}
} catch (e: Exception) {
onInvalidRequest("Failed to parse identity proof request parameters: $params", e)
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import java.math.BigInteger

Expand Down Expand Up @@ -117,6 +118,7 @@ private constructor(
private lateinit var sessionRequestAccount: Account
private lateinit var sessionRequestAppMetadata: AppMetadata
private val handledRequests = mutableSetOf<Long>()
private val connectionAvailabilityFlow = MutableStateFlow(false)

enum class ProofProvableState {
Provable,
Expand Down Expand Up @@ -307,6 +309,11 @@ private constructor(
mutableStateFlow.tryEmit(State.WaitingForSessionRequest)
}

override fun onConnectionStateChange(state: Sign.Model.ConnectionState) {
defaultWalletDelegate.onConnectionStateChange(state)
connectionAvailabilityFlow.tryEmit(state.isAvailable)
}

override fun onSessionProposal(sessionProposal: Sign.Model.SessionProposal) =
viewModelScope.launch {
defaultWalletDelegate.onSessionProposal(sessionProposal)
Expand Down Expand Up @@ -673,46 +680,70 @@ private constructor(

this@WalletConnectViewModel.sessionRequestAccount = account

when (method) {
REQUEST_METHOD_SIGN_AND_SEND_TRANSACTION ->
signTransactionRequestHandler.start(
params = params,
account = sessionRequestAccount,
appMetadata = sessionRequestAppMetadata,
)
if (method !in allowedRequestMethods) {
val unsupportedMethodMessage =
"Received an unsupported WalletConnect method request: $method"

REQUEST_METHOD_SIGN_MESSAGE ->
signMessageRequestHandler.start(
params = params,
account = sessionRequestAccount,
appMetadata = sessionRequestAppMetadata,
)
Log.e(unsupportedMethodMessage)

respondError(message = unsupportedMethodMessage)

REQUEST_METHOD_VERIFIABLE_PRESENTATION -> {
verifiablePresentationRequestHandler.start(
params = params,
account = account,
availableAccounts = getAvailableAccounts(),
appMetadata = sessionRequestAppMetadata,
mutableEventsFlow.tryEmit(
Event.ShowFloatingError(
Error.InvalidRequest
)
}
)

else -> {
val unsupportedMethodMessage =
"Received an unsupported WalletConnect method request: $method"
onSessionRequestHandlingFinished()

Log.e(unsupportedMethodMessage)
return@launch
}

respondError(message = unsupportedMethodMessage)
// Even though handlers contain specific error handling,
// the global try-catch prevents the crash loop
// if there is an unexpected error.
try {
when (method) {
REQUEST_METHOD_SIGN_AND_SEND_TRANSACTION ->
signTransactionRequestHandler.start(
params = params,
account = sessionRequestAccount,
appMetadata = sessionRequestAppMetadata,
)

mutableEventsFlow.tryEmit(
Event.ShowFloatingError(
Error.InvalidRequest
REQUEST_METHOD_SIGN_MESSAGE ->
signMessageRequestHandler.start(
params = params,
account = sessionRequestAccount,
appMetadata = sessionRequestAppMetadata,
)
)

onSessionRequestHandlingFinished()
REQUEST_METHOD_VERIFIABLE_PRESENTATION -> {
verifiablePresentationRequestHandler.start(
params = params,
account = account,
availableAccounts = getAvailableAccounts(),
appMetadata = sessionRequestAppMetadata,
)
}

else ->
error("Missing a handler for the allowed method '$method'")
}
} catch (error: Exception) {
val unexpectedErrorMessage = "Unexpected error occurred: $error"

Log.e(unexpectedErrorMessage, error)

respondError(unexpectedErrorMessage)

mutableEventsFlow.tryEmit(
Event.ShowFloatingError(
Error.InvalidRequest
)
)

onSessionRequestHandlingFinished()
}
}

Expand Down Expand Up @@ -940,9 +971,12 @@ private constructor(
false
}

private fun respondSuccess(result: String) {
private fun respondSuccess(result: String) = viewModelScope.launch {
handledRequests += sessionRequestId

// Await the connection.
connectionAvailabilityFlow.first { it }

SignClient.respond(
Sign.Params.Response(
sessionTopic = sessionRequestTopic,
Expand All @@ -966,9 +1000,12 @@ private constructor(
message: String,
sessionRequestId: Long = this.sessionRequestId,
sessionRequestTopic: String = this.sessionRequestTopic,
) {
) = viewModelScope.launch {
handledRequests += sessionRequestId

// Await the connection.
connectionAvailabilityFlow.first { it }

SignClient.respond(
Sign.Params.Response(
sessionTopic = sessionRequestTopic,
Expand Down

0 comments on commit a9ef461

Please sign in to comment.