From 3db69e2680cb9e5f966395ef4b9b3cbe3610bce4 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Mon, 16 Oct 2023 18:49:57 -0600 Subject: [PATCH 01/11] walletlib + fakewallet updates --- .../common/ProtocolContract.java | 3 ++ android/fakewallet/build.gradle | 19 ++++++++ .../MobileWalletAdapterViewModel.kt | 36 +++++++++----- .../protocol/MobileWalletAdapterConfig.java | 17 +++++-- .../protocol/MobileWalletAdapterServer.java | 45 +++++++++++++++-- .../walletlib/scenario/AuthorizeRequest.java | 48 +++++++++++++++++-- .../walletlib/scenario/AuthorizedAccount.java | 34 +++++++++++-- .../walletlib/scenario/DeauthorizedEvent.java | 4 +- .../walletlib/scenario/LocalScenario.java | 16 ++++--- .../SignAndSendTransactionsRequest.java | 4 +- .../scenario/SignMessagesRequest.java | 4 +- .../scenario/SignPayloadsRequest.java | 4 +- .../scenario/SignTransactionsRequest.java | 4 +- 13 files changed, 195 insertions(+), 43 deletions(-) diff --git a/android/common/src/main/java/com/solana/mobilewalletadapter/common/ProtocolContract.java b/android/common/src/main/java/com/solana/mobilewalletadapter/common/ProtocolContract.java index f59e0d285..26bc649ac 100644 --- a/android/common/src/main/java/com/solana/mobilewalletadapter/common/ProtocolContract.java +++ b/android/common/src/main/java/com/solana/mobilewalletadapter/common/ProtocolContract.java @@ -73,7 +73,10 @@ public class ProtocolContract { public static final String RESULT_AUTH_TOKEN = "auth_token"; // type: String public static final String RESULT_ACCOUNTS = "accounts"; // type: JSON array of Account public static final String RESULT_ACCOUNTS_ADDRESS = "address"; // type: String (base64-encoded addresses) + public static final String RESULT_ACCOUNTS_DISPLAY_ADDRESS = "display_address"; // type: String + public static final String RESULT_ACCOUNTS_DISPLAY_ADDRESS_FORMAT = "display_address_format"; // type: String public static final String RESULT_ACCOUNTS_LABEL = "label"; // type: String + public static final String RESULT_ACCOUNTS_ICON = "icon"; // type: String public static final String RESULT_ACCOUNTS_CHAINS = "chains"; // type: String // RESULT_ACCOUNTS optionally includes a RESULT_SUPPORTED_FEATURES diff --git a/android/fakewallet/build.gradle b/android/fakewallet/build.gradle index 1969156ef..6de80c8fa 100644 --- a/android/fakewallet/build.gradle +++ b/android/fakewallet/build.gradle @@ -55,6 +55,23 @@ android { } } + flavorDimensions = ["protocol_version"] + + productFlavors { + v1 { + dimension "protocol_version" + applicationId "com.solana.mobilewalletadapter.fakewallet" + buildConfigField "com.solana.mobilewalletadapter.common.protocol.SessionProperties.ProtocolVersion", + "PROTOCOL_VERSION", "com.solana.mobilewalletadapter.common.protocol.SessionProperties.ProtocolVersion.V1" + } + legacy { + dimension "protocol_version" + applicationId "com.solana.mobilewalletadapter.fakewallet.legacy" + buildConfigField "com.solana.mobilewalletadapter.common.protocol.SessionProperties.ProtocolVersion", + "PROTOCOL_VERSION", "com.solana.mobilewalletadapter.common.protocol.SessionProperties.ProtocolVersion.LEGACY" + } + } + compileOptions { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 @@ -66,6 +83,7 @@ android { buildFeatures { viewBinding true + buildConfig true } } @@ -100,6 +118,7 @@ dependencies { implementation 'io.coil-kt:coil-svg:2.4.0' implementation 'org.bouncycastle:bcprov-jdk18on:1.76' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3' + implementation 'io.github.funkatronics:multimult:0.2.0' implementation project(path: ':walletlib') kapt 'androidx.room:room-compiler:2.4.3' } \ No newline at end of file diff --git a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt index 21c6f50c1..d79fa58ce 100644 --- a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt +++ b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt @@ -10,6 +10,7 @@ import android.net.Uri import android.util.Log import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import com.funkatronics.encoders.Base58 import com.solana.mobilewalletadapter.common.ProtocolContract import com.solana.mobilewalletadapter.fakewallet.usecase.* import com.solana.mobilewalletadapter.walletlib.association.AssociationUri @@ -59,11 +60,12 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( scenario = associationUri.createScenario( getApplication().applicationContext, MobileWalletAdapterConfig( - true, 10, 10, arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), LOW_POWER_NO_CONNECTION_TIMEOUT_MS, + arrayOf(ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS), + arrayOf(BuildConfig.PROTOCOL_VERSION) ), AuthIssuerConfig("fakewallet"), MobileWalletAdapterScenarioCallbacks() @@ -90,12 +92,9 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( val keypair = getApplication().keyRepository.generateKeypair() val publicKey = keypair.public as Ed25519PublicKeyParameters Log.d(TAG, "Generated a new keypair (pub=${publicKey.encoded.contentToString()}) for authorize request") - request.request.completeWithAuthorize( - publicKey.encoded, - "fakewallet", - null, - request.sourceVerificationState.authorizationScope.encodeToByteArray() - ) + val accounts = arrayOf(buildAccount(publicKey.encoded, "fakewallet")) + request.request.completeWithAuthorize(accounts, null, + request.sourceVerificationState.authorizationScope.encodeToByteArray()) } else { request.request.completeWithDecline() } @@ -258,7 +257,7 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( return } - Log.d(TAG, "Simulating transactions submitted on cluster=${request.request.cluster}") + Log.d(TAG, "Simulating transactions submitted on cluster=${request.request.chain}") request.request.completeWithSignatures(request.signatures!!) } @@ -268,7 +267,7 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( return } - Log.d(TAG, "Simulating transactions NOT submitted on cluster=${request.request.cluster}") + Log.d(TAG, "Simulating transactions NOT submitted on cluster=${request.request.chain}") val signatures = request.signatures!! val notSubmittedSignatures = Array(signatures.size) { i -> @@ -353,14 +352,25 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( } } - private fun clusterToRpcUri(cluster: String?): Uri { - return when (cluster) { + private fun buildAccount(publicKey: ByteArray, label: String, icon: Uri? = null, + chains: Array? = null, features: Array? = null ) = + AuthorizedAccount( + publicKey, Base58.encodeToString(publicKey), "base58", + label, icon, chains, features + ) + + private fun chainOrClusterToRpcUri(chainOrCluster: String?): Uri { + return when (chainOrCluster) { + ProtocolContract.CHAIN_SOLANA_MAINNET, ProtocolContract.CLUSTER_MAINNET_BETA -> Uri.parse("https://api.mainnet-beta.solana.com") + ProtocolContract.CHAIN_SOLANA_DEVNET, ProtocolContract.CLUSTER_DEVNET -> Uri.parse("https://api.devnet.solana.com") - else -> + ProtocolContract.CHAIN_SOLANA_TESTNET, + ProtocolContract.CLUSTER_TESTNET -> Uri.parse("https://api.testnet.solana.com") + else -> throw IllegalArgumentException("Unsupported chain/cluster: $chainOrCluster") } } @@ -457,7 +467,7 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( override fun onSignAndSendTransactionsRequest(request: SignAndSendTransactionsRequest) { if (verifyPrivilegedMethodSource(request)) { - val endpointUri = clusterToRpcUri(request.cluster) + val endpointUri = chainOrClusterToRpcUri(request.chain) cancelAndReplaceRequest(MobileWalletAdapterServiceRequest.SignAndSendTransactions(request, endpointUri)) } else { request.completeWithDecline() diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java index 0fd4a4d22..4ec1b0489 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java @@ -36,7 +36,7 @@ public class MobileWalletAdapterConfig { public final Object[] supportedTransactionVersions; @NonNull - public final List supportedProtocolVersions; + public final SessionProperties.ProtocolVersion[] supportedProtocolVersions; @NonNull public final String[] optionalFeatures; @@ -51,7 +51,7 @@ public MobileWalletAdapterConfig(boolean supportsSignAndSendTransactions, supportedTransactionVersions, noConnectionWarningTimeoutMs, supportsSignAndSendTransactions ? new String[] { ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS } : new String[] {}, - List.of(SessionProperties.ProtocolVersion.LEGACY)); + new SessionProperties.ProtocolVersion[]{ SessionProperties.ProtocolVersion.LEGACY }); } public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigningRequest, @@ -59,7 +59,7 @@ public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigni @NonNull @Size(min = 1) Object[] supportedTransactionVersions, @IntRange(from = 0) long noConnectionWarningTimeoutMs, @NonNull String[] supportedFeatures, - @NonNull List supportedProtocolVersions) { + @NonNull SessionProperties.ProtocolVersion[] supportedProtocolVersions) { this.maxTransactionsPerSigningRequest = maxTransactionsPerSigningRequest; this.maxMessagesPerSigningRequest = maxMessagesPerSigningRequest; this.noConnectionWarningTimeoutMs = noConnectionWarningTimeoutMs; @@ -86,4 +86,15 @@ public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigni } this.supportsSignAndSendTransactions = supportsSignAndSendTransactions; } + + public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigningRequest, + @IntRange(from = 0) int maxMessagesPerSigningRequest, + @NonNull @Size(min = 1) Object[] supportedTransactionVersions, + @IntRange(from = 0) long noConnectionWarningTimeoutMs, + @NonNull String[] supportedFeatures, + @NonNull List supportedProtocolVersions) { + this(maxTransactionsPerSigningRequest, maxMessagesPerSigningRequest, + supportedTransactionVersions, noConnectionWarningTimeoutMs, + supportedFeatures, supportedProtocolVersions.toArray(new SessionProperties.ProtocolVersion[0])); + } } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java index 67d5b22aa..82c9876e5 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java @@ -16,6 +16,7 @@ import androidx.annotation.Size; import com.solana.mobilewalletadapter.common.ProtocolContract; +import com.solana.mobilewalletadapter.common.util.Identifier; import com.solana.mobilewalletadapter.common.util.JsonPack; import com.solana.mobilewalletadapter.common.util.NotifyOnCompleteFuture; import com.solana.mobilewalletadapter.common.util.NotifyingCompletableFuture; @@ -165,9 +166,16 @@ private void onAuthorizationComplete(@NonNull NotifyOnCompleteFuture mHandler.post(() -> onAuthorizationComplete(f))); mMethodHandlers.authorize(request); } @@ -336,15 +363,25 @@ public static class AuthorizeRequest extends AuthorizationRequest { @Nullable public final String chain; + @Nullable + public final String[] features; + + @Nullable + public final String[] addresses; + private AuthorizeRequest(@Nullable Object id, @Nullable Uri identityUri, @Nullable Uri iconUri, @Nullable String identityName, @Nullable String chain, + @Nullable String[] features, + @Nullable String[] addresses, @Nullable String authToken) { super(id, identityUri, iconUri, identityName, authToken); this.chain = chain; - this.cluster = chain; + this.cluster = null; + this.features = features; + this.addresses = addresses; } @Override diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java index f415d5aa4..8e2e3b125 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java @@ -10,6 +10,8 @@ import androidx.annotation.Nullable; import androidx.annotation.Size; +import com.solana.mobilewalletadapter.common.ProtocolContract; +import com.solana.mobilewalletadapter.common.util.Identifier; import com.solana.mobilewalletadapter.common.util.NotifyingCompletableFuture; import com.solana.mobilewalletadapter.walletlib.protocol.MobileWalletAdapterServer; @@ -29,16 +31,36 @@ public class AuthorizeRequest @NonNull protected final String mChain; + @Nullable + protected final String[] mFeatures; + + @Nullable + protected final String[] mAddresses; + + @Nullable + protected final String mAuthToken; + /*package*/ AuthorizeRequest(@NonNull NotifyingCompletableFuture request, @Nullable String identityName, @Nullable Uri identityUri, @Nullable Uri iconUri, - @NonNull String chain) { + @NonNull String chain, + @Nullable String[] features, + @Nullable String[] addresses, + @Nullable String authToken) { super(request); mIdentityName = identityName; mIdentityUri = identityUri; mIconUri = iconUri; - mChain = chain; + mFeatures = features; + mAddresses = addresses; + mAuthToken = authToken; + + if (Identifier.isValidIdentifier(chain)) { + mChain = chain; + } else { + throw new IllegalArgumentException("Provided chain must be a valid chain identifier"); + } } @Nullable @@ -58,7 +80,16 @@ public Uri getIconRelativeUri() { @NonNull @Deprecated public String getCluster() { - return mChain; + switch (mChain) { + case ProtocolContract.CHAIN_SOLANA_MAINNET: + return ProtocolContract.CLUSTER_MAINNET_BETA; + case ProtocolContract.CHAIN_SOLANA_TESTNET: + return ProtocolContract.CLUSTER_TESTNET; + case ProtocolContract.CHAIN_SOLANA_DEVNET: + return ProtocolContract.CLUSTER_DEVNET; + default: + return mChain; + } } @NonNull @@ -66,13 +97,22 @@ public String getChain() { return mChain; } + @Nullable + public String[] getFeatures() { return mFeatures; } + + @Nullable + public String[] getAddresses() { return mAddresses; } + + @Nullable + public String getAuthToken() { return mAuthToken; } + @Deprecated public void completeWithAuthorize(@NonNull byte[] publicKey, @Nullable String accountLabel, @Nullable Uri walletUriBase, @Nullable byte[] scope) { AuthorizedAccount[] accounts = new AuthorizedAccount[] { - new AuthorizedAccount(publicKey, accountLabel, null, null)}; + new AuthorizedAccount(publicKey, accountLabel, null, null, null)}; mRequest.complete(new Result(accounts, walletUriBase, scope)); } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizedAccount.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizedAccount.java index 1b5365719..32b6d6bbf 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizedAccount.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizedAccount.java @@ -4,6 +4,8 @@ package com.solana.mobilewalletadapter.walletlib.scenario; +import android.net.Uri; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -13,18 +15,44 @@ public class AuthorizedAccount { @NonNull public final byte[] publicKey; @Nullable + public final String displayAddress; + @Nullable + public final String displayAddressFormat; + @Nullable public final String accountLabel; @Nullable + public final Uri icon; + @Nullable public final String[] chains; @Nullable public final String[] features; public AuthorizedAccount(@NonNull byte[] publicKey, - @Nullable String accountLabel, - @Nullable String[] chains, - @Nullable String[] features) { + @Nullable String accountLabel, + @Nullable Uri icon, + @Nullable String[] chains, + @Nullable String[] features) { + this.publicKey = publicKey; + this.displayAddress = null; + this.displayAddressFormat = null; + this.accountLabel = accountLabel; + this.icon = icon; + this.chains = chains; + this.features = features; + } + + public AuthorizedAccount(@NonNull byte[] publicKey, + @Nullable String displayAddress, + @Nullable String displayAddressFormat, + @Nullable String accountLabel, + @Nullable Uri icon, + @Nullable String[] chains, + @Nullable String[] features) { this.publicKey = publicKey; + this.displayAddress = displayAddress; + this.displayAddressFormat = displayAddressFormat; this.accountLabel = accountLabel; + this.icon = icon; this.chains = chains; this.features = features; } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/DeauthorizedEvent.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/DeauthorizedEvent.java index 339e15e9c..fef58ec4b 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/DeauthorizedEvent.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/DeauthorizedEvent.java @@ -16,9 +16,9 @@ public class DeauthorizedEvent extends BaseVerifiableIdentityRequest supportedProtocolVersions = + Arrays.asList(mobileWalletAdapterConfig.supportedProtocolVersions); SessionProperties.ProtocolVersion maxSupportedProtocolVersion = SessionProperties.ProtocolVersion.LEGACY; for (SessionProperties.ProtocolVersion version : requestedProtocolVersions) { if (version.ordinal() > maxSupportedProtocolVersion.ordinal() - && mobileWalletAdapterConfig.supportedProtocolVersions.contains(version)) { + && supportedProtocolVersions.contains(version)) { maxSupportedProtocolVersion = version; } } @@ -260,7 +263,8 @@ public void authorize(@NonNull MobileWalletAdapterServer.AuthorizeRequest reques })); mIoHandler.post(() -> mCallbacks.onAuthorizeRequest(new AuthorizeRequest( - future, request.identityName, request.identityUri, request.iconUri, chain))); + future, request.identityName, request.identityUri, request.iconUri, chain, + request.features, request.addresses, request.authToken))); } private void doReauthorize(@NonNull MobileWalletAdapterServer.AuthorizeRequest request) { @@ -417,7 +421,7 @@ public void deauthorize(@NonNull MobileWalletAdapterServer.DeauthorizeRequest re if (authRecord != null) { mIoHandler.post(() -> mCallbacks.onDeauthorizedEvent(new DeauthorizedEvent( request, authRecord.identity.getName(), authRecord.identity.getUri(), - authRecord.identity.getRelativeIconUri(), authRecord.cluster, + authRecord.identity.getRelativeIconUri(), authRecord.chain, authRecord.scope))); } else { // No auth token was found. Just complete successfully, to avoid disclosing whether @@ -441,7 +445,7 @@ public void signTransactions(@NonNull MobileWalletAdapterServer.SignTransactions mIoHandler.post(() -> mCallbacks.onSignTransactionsRequest(new SignTransactionsRequest( request, authRecord.identity.getName(), authRecord.identity.getUri(), authRecord.identity.getRelativeIconUri(), authRecord.scope, - authRecord.publicKey, authRecord.cluster))); + authRecord.publicKey, authRecord.chain))); } @Override @@ -460,7 +464,7 @@ public void signMessages(@NonNull MobileWalletAdapterServer.SignMessagesRequest final SignMessagesRequest smr = new SignMessagesRequest(request, authRecord.identity.getName(), authRecord.identity.getUri(), authRecord.identity.getRelativeIconUri(), authRecord.scope, - authRecord.publicKey, authRecord.cluster); + authRecord.publicKey, authRecord.chain); mIoHandler.post(() -> mCallbacks.onSignMessagesRequest(smr)); } catch (IllegalArgumentException e) { mIoHandler.post(() -> request.completeExceptionally( @@ -484,7 +488,7 @@ public void signAndSendTransactions( mIoHandler.post(() -> mCallbacks.onSignAndSendTransactionsRequest( new SignAndSendTransactionsRequest(request, authRecord.identity.getName(), authRecord.identity.getUri(), authRecord.identity.getRelativeIconUri(), - authRecord.scope, authRecord.publicKey, authRecord.cluster))); + authRecord.scope, authRecord.publicKey, authRecord.chain))); } }; diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignAndSendTransactionsRequest.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignAndSendTransactionsRequest.java index 1326dba78..55779007f 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignAndSendTransactionsRequest.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignAndSendTransactionsRequest.java @@ -24,8 +24,8 @@ public class SignAndSendTransactionsRequest @Nullable Uri iconUri, @NonNull byte[] authorizationScope, @NonNull byte[] publicKey, - @NonNull String cluster) { - super(request, identityName, identityUri, iconUri, cluster, authorizationScope); + @NonNull String chain) { + super(request, identityName, identityUri, iconUri, chain, authorizationScope); mPublicKey = publicKey; } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignMessagesRequest.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignMessagesRequest.java index b760b1850..80c28452f 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignMessagesRequest.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignMessagesRequest.java @@ -20,8 +20,8 @@ public class SignMessagesRequest extends SignPayloadsRequest { @Nullable Uri iconUri, @NonNull byte[] authorizationScope, @NonNull byte[] authorizedPublicKey, - @NonNull String cluster) { - super(request, identityName, identityUri, iconUri, authorizationScope, authorizedPublicKey, cluster); + @NonNull String chain) { + super(request, identityName, identityUri, iconUri, authorizationScope, authorizedPublicKey, chain); // TODO(#44): support multiple addresses // this check is temporary; it will become a wallet competency to evaluate the set of diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignPayloadsRequest.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignPayloadsRequest.java index 92404e806..c21056e27 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignPayloadsRequest.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/SignPayloadsRequest.java @@ -23,8 +23,8 @@ protected SignPayloadsRequest(@NonNull MobileWalletAdapterServer.SignRequest Date: Mon, 16 Oct 2023 21:16:13 -0600 Subject: [PATCH 02/11] fix test commands --- .github/workflows/android.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index d81e407cc..9fc8c6e18 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -156,8 +156,8 @@ jobs: disable-animations: true script: | adb shell am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS - ./gradlew :fakewallet:connectedDebugAndroidTest - ./gradlew :fakewallet:installDebug :fakedapp:connectedDebugAndroidTest + ./gradlew :fakewallet:connectedV1DebugAndroidTest + ./gradlew :fakewallet:installV1Debug :fakedapp:connectedDebugAndroidTest - name: Archive fakewallet test results if: ${{ success() || failure() }} uses: actions/upload-artifact@v3 From 00953915bdcee302cd030c14f54aa5c9640ff749 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Tue, 17 Oct 2023 14:45:14 -0600 Subject: [PATCH 03/11] remove auth token from consumer auth request --- .../walletlib/scenario/AuthorizeRequest.java | 10 +--------- .../walletlib/scenario/LocalScenario.java | 2 +- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java index 8e2e3b125..07376fb6b 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/AuthorizeRequest.java @@ -37,24 +37,19 @@ public class AuthorizeRequest @Nullable protected final String[] mAddresses; - @Nullable - protected final String mAuthToken; - /*package*/ AuthorizeRequest(@NonNull NotifyingCompletableFuture request, @Nullable String identityName, @Nullable Uri identityUri, @Nullable Uri iconUri, @NonNull String chain, @Nullable String[] features, - @Nullable String[] addresses, - @Nullable String authToken) { + @Nullable String[] addresses) { super(request); mIdentityName = identityName; mIdentityUri = identityUri; mIconUri = iconUri; mFeatures = features; mAddresses = addresses; - mAuthToken = authToken; if (Identifier.isValidIdentifier(chain)) { mChain = chain; @@ -103,9 +98,6 @@ public String getChain() { @Nullable public String[] getAddresses() { return mAddresses; } - @Nullable - public String getAuthToken() { return mAuthToken; } - @Deprecated public void completeWithAuthorize(@NonNull byte[] publicKey, @Nullable String accountLabel, diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java index 30792e493..6590d6a89 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java @@ -264,7 +264,7 @@ public void authorize(@NonNull MobileWalletAdapterServer.AuthorizeRequest reques mIoHandler.post(() -> mCallbacks.onAuthorizeRequest(new AuthorizeRequest( future, request.identityName, request.identityUri, request.iconUri, chain, - request.features, request.addresses, request.authToken))); + request.features, request.addresses))); } private void doReauthorize(@NonNull MobileWalletAdapterServer.AuthorizeRequest request) { From 7c147eaeb0ec47e2fbe5c0f650fd0636af9037b4 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Wed, 18 Oct 2023 13:31:10 -0600 Subject: [PATCH 04/11] hide protocol version from consumers --- .../common/protocol/SessionProperties.java | 6 ++- .../MobileWalletAdapterViewModel.kt | 46 +++++++++++++------ .../walletlib/association/AssociationUri.java | 12 +++-- .../association/LocalAssociationUri.java | 3 +- .../protocol/MobileWalletAdapterConfig.java | 21 +-------- .../protocol/MobileWalletAdapterSession.java | 13 +++++- .../walletlib/scenario/LocalScenario.java | 29 ++++-------- .../LocalWebSocketServerScenario.java | 14 +++--- .../walletlib/scenario/Scenario.java | 5 +- .../LocalWebSocketServerScenarioTest.java | 14 +++--- 10 files changed, 85 insertions(+), 78 deletions(-) diff --git a/android/common/src/main/java/com/solana/mobilewalletadapter/common/protocol/SessionProperties.java b/android/common/src/main/java/com/solana/mobilewalletadapter/common/protocol/SessionProperties.java index f824255fd..2e1a1c6d1 100644 --- a/android/common/src/main/java/com/solana/mobilewalletadapter/common/protocol/SessionProperties.java +++ b/android/common/src/main/java/com/solana/mobilewalletadapter/common/protocol/SessionProperties.java @@ -50,13 +50,15 @@ public String toString() { } } - public static ProtocolVersion from(String versionString) { + public static ProtocolVersion from(String versionString) throws IllegalArgumentException { switch (versionString.toLowerCase()) { case "v1": case "1": return V1; - default: + case "legacy": return LEGACY; + default: + throw new IllegalArgumentException("Unknown/unsupported version: " + versionString); } } } diff --git a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt index d79fa58ce..a9cb91fcb 100644 --- a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt +++ b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt @@ -12,6 +12,7 @@ import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope import com.funkatronics.encoders.Base58 import com.solana.mobilewalletadapter.common.ProtocolContract +import com.solana.mobilewalletadapter.common.protocol.SessionProperties import com.solana.mobilewalletadapter.fakewallet.usecase.* import com.solana.mobilewalletadapter.walletlib.association.AssociationUri import com.solana.mobilewalletadapter.walletlib.association.LocalAssociationUri @@ -57,19 +58,38 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( associationUri ) - scenario = associationUri.createScenario( - getApplication().applicationContext, - MobileWalletAdapterConfig( - 10, - 10, - arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), - LOW_POWER_NO_CONNECTION_TIMEOUT_MS, - arrayOf(ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS), - arrayOf(BuildConfig.PROTOCOL_VERSION) - ), - AuthIssuerConfig("fakewallet"), - MobileWalletAdapterScenarioCallbacks() - ).also { it.start() } + scenario = if (BuildConfig.PROTOCOL_VERSION == SessionProperties.ProtocolVersion.LEGACY) { + // manually create the scenario here so we can override the association protocol version + // this forces ProtocolVersion.LEGACY to simulate a wallet using walletlib 1.x (for testing) + LocalWebSocketServerScenario( + getApplication().applicationContext, + MobileWalletAdapterConfig( + 10, + 10, + arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), + LOW_POWER_NO_CONNECTION_TIMEOUT_MS, + arrayOf(ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS) + ), + AuthIssuerConfig("fakewallet"), + MobileWalletAdapterScenarioCallbacks(), + associationUri.associationPublicKey, + listOf(SessionProperties.ProtocolVersion.LEGACY), + associationUri.port, + ) + } else { + associationUri.createScenario( + getApplication().applicationContext, + MobileWalletAdapterConfig( + 10, + 10, + arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), + LOW_POWER_NO_CONNECTION_TIMEOUT_MS, + arrayOf(ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS) + ), + AuthIssuerConfig("fakewallet"), + MobileWalletAdapterScenarioCallbacks() + ) + }.also { it.start() } return true } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/AssociationUri.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/AssociationUri.java index e94d5eaff..7dfca014e 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/AssociationUri.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/AssociationUri.java @@ -28,13 +28,13 @@ public abstract class AssociationUri { public final byte[] associationPublicKey; @NonNull - public final List supportedProtocolVersions; + public final List associationProtocolVersions; protected AssociationUri(@NonNull Uri uri) { this.uri = uri; validate(uri); associationPublicKey = parseAssociationToken(uri); - supportedProtocolVersions = parseSupportedProtocolVersions(uri); + associationProtocolVersions = parseSupportedProtocolVersions(uri); } private static void validate(@NonNull Uri uri) { @@ -68,9 +68,11 @@ private static List parseSupportedProtocolVer AssociationContract.PARAMETER_PROTOCOL_VERSION)) { try { supportedVersions.add(SessionProperties.ProtocolVersion.from(supportVersionStr)); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("port parameter must be a number", e); - } + } catch (IllegalArgumentException ignored) {} + } + + if (supportedVersions.isEmpty()) { + supportedVersions.add(SessionProperties.ProtocolVersion.LEGACY); } return supportedVersions; diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/LocalAssociationUri.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/LocalAssociationUri.java index 083d42425..e1c04ce6f 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/LocalAssociationUri.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/association/LocalAssociationUri.java @@ -42,7 +42,8 @@ public LocalWebSocketServerScenario createScenario(@NonNull Context context, @NonNull Scenario.Callbacks callbacks) { if (callbacks instanceof LocalScenario.Callbacks) { return new LocalWebSocketServerScenario(context, mobileWalletAdapterConfig, - authIssuerConfig, (LocalScenario.Callbacks) callbacks, associationPublicKey, port, supportedProtocolVersions); + authIssuerConfig, (LocalScenario.Callbacks) callbacks, + associationPublicKey, associationProtocolVersions, port); } else { throw new IllegalArgumentException("callbacks must implement " + LocalScenario.Callbacks.class.getName()); } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java index 4ec1b0489..f130ac9e2 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java @@ -35,9 +35,6 @@ public class MobileWalletAdapterConfig { @Size(min = 1) public final Object[] supportedTransactionVersions; - @NonNull - public final SessionProperties.ProtocolVersion[] supportedProtocolVersions; - @NonNull public final String[] optionalFeatures; @@ -50,20 +47,17 @@ public MobileWalletAdapterConfig(boolean supportsSignAndSendTransactions, this(maxTransactionsPerSigningRequest, maxMessagesPerSigningRequest, supportedTransactionVersions, noConnectionWarningTimeoutMs, supportsSignAndSendTransactions ? new String[] { - ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS } : new String[] {}, - new SessionProperties.ProtocolVersion[]{ SessionProperties.ProtocolVersion.LEGACY }); + ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS } : new String[] {}); } public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigningRequest, @IntRange(from = 0) int maxMessagesPerSigningRequest, @NonNull @Size(min = 1) Object[] supportedTransactionVersions, @IntRange(from = 0) long noConnectionWarningTimeoutMs, - @NonNull String[] supportedFeatures, - @NonNull SessionProperties.ProtocolVersion[] supportedProtocolVersions) { + @NonNull String[] supportedFeatures) { this.maxTransactionsPerSigningRequest = maxTransactionsPerSigningRequest; this.maxMessagesPerSigningRequest = maxMessagesPerSigningRequest; this.noConnectionWarningTimeoutMs = noConnectionWarningTimeoutMs; - this.supportedProtocolVersions = supportedProtocolVersions; this.optionalFeatures = supportedFeatures; for (Object o : supportedTransactionVersions) { @@ -86,15 +80,4 @@ public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigni } this.supportsSignAndSendTransactions = supportsSignAndSendTransactions; } - - public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigningRequest, - @IntRange(from = 0) int maxMessagesPerSigningRequest, - @NonNull @Size(min = 1) Object[] supportedTransactionVersions, - @IntRange(from = 0) long noConnectionWarningTimeoutMs, - @NonNull String[] supportedFeatures, - @NonNull List supportedProtocolVersions) { - this(maxTransactionsPerSigningRequest, maxMessagesPerSigningRequest, - supportedTransactionVersions, noConnectionWarningTimeoutMs, - supportedFeatures, supportedProtocolVersions.toArray(new SessionProperties.ProtocolVersion[0])); - } } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterSession.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterSession.java index 78eebccee..a242b9f7b 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterSession.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterSession.java @@ -34,12 +34,23 @@ public class MobileWalletAdapterSession extends MobileWalletAdapterSessionCommon @NonNull private final ECPublicKey mAssociationPublicKey; + @NonNull + private final SessionProperties mSessionProperties; + public MobileWalletAdapterSession(@NonNull Scenario scenario, @NonNull MessageReceiver decryptedPayloadReceiver, @Nullable StateCallbacks stateCallbacks) { super(decryptedPayloadReceiver, stateCallbacks); mScenario = scenario; mAssociationPublicKey = ECDSAKeys.decodeP256PublicKey(scenario.getAssociationPublicKey()); + + SessionProperties.ProtocolVersion maxSupportedProtocolVersion = SessionProperties.ProtocolVersion.LEGACY; + for (SessionProperties.ProtocolVersion version : scenario.getAssociationProtocolVersions()) { + if (version.ordinal() > maxSupportedProtocolVersion.ordinal()) { + maxSupportedProtocolVersion = version; + } + } + mSessionProperties = new SessionProperties(maxSupportedProtocolVersion); } @NonNull @@ -50,7 +61,7 @@ protected ECPublicKey getAssociationPublicKey() { @NonNull @Override - protected SessionProperties getSessionProperties() { return mScenario.getSessionProperties(); } + protected SessionProperties getSessionProperties() { return mSessionProperties; } @Override protected void handleSessionEstablishmentMessage(@NonNull byte[] payload) diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java index 6590d6a89..67e4dc448 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java @@ -28,8 +28,6 @@ import com.solana.mobilewalletadapter.walletlib.protocol.MobileWalletAdapterSession; import com.solana.mobilewalletadapter.walletlib.util.LooperThread; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; @@ -42,11 +40,10 @@ public abstract class LocalScenario implements Scenario { private static final String TAG = LocalScenario.class.getSimpleName(); - @NonNull - final public SessionProperties sessionProperties; - @NonNull final public byte[] associationPublicKey; + @NonNull + final public List associationProtocolVersions; @NonNull protected final MobileWalletAdapterConfig mMobileWalletAdapterConfig; @@ -77,7 +74,7 @@ protected LocalScenario(@NonNull Context context, @NonNull Callbacks callbacks, @NonNull byte[] associationPublicKey) { this(context, mobileWalletAdapterConfig, authIssuerConfig, callbacks, - associationPublicKey, new DevicePowerConfigProvider(context), new ArrayList<>()); + associationPublicKey, new DevicePowerConfigProvider(context), List.of()); } /*package*/ LocalScenario(@NonNull Context context, @@ -86,23 +83,12 @@ protected LocalScenario(@NonNull Context context, @NonNull Callbacks callbacks, @NonNull byte[] associationPublicKey, @NonNull PowerConfigProvider powerConfigProvider, - @NonNull List requestedProtocolVersions) { + @NonNull List associationProtocolVersions) { mCallbacks = callbacks; mMobileWalletAdapterConfig = mobileWalletAdapterConfig; + this.associationProtocolVersions = associationProtocolVersions; this.associationPublicKey = associationPublicKey; - List supportedProtocolVersions = - Arrays.asList(mobileWalletAdapterConfig.supportedProtocolVersions); - SessionProperties.ProtocolVersion maxSupportedProtocolVersion = - SessionProperties.ProtocolVersion.LEGACY; - for (SessionProperties.ProtocolVersion version : requestedProtocolVersions) { - if (version.ordinal() > maxSupportedProtocolVersion.ordinal() - && supportedProtocolVersions.contains(version)) { - maxSupportedProtocolVersion = version; - } - } - this.sessionProperties = new SessionProperties(maxSupportedProtocolVersion); - final LooperThread t = new LooperThread(); t.start(); mIoLooper = t.getLooper(); // blocks until Looper is available @@ -118,9 +104,10 @@ public byte[] getAssociationPublicKey() { return associationPublicKey; } - @Nullable @Override - public SessionProperties getSessionProperties() { return sessionProperties; } + public List getAssociationProtocolVersions() { + return associationProtocolVersions; + } @Override protected void finalize() { diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenario.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenario.java index 6274c9eec..886e8104e 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenario.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenario.java @@ -32,7 +32,7 @@ public LocalWebSocketServerScenario(@NonNull Context context, @NonNull byte[] associationPublicKey, @WebSocketsTransportContract.LocalPortRange int port) { this(context, mobileWalletAdapterConfig, authIssuerConfig, callbacks, - associationPublicKey, port, new DevicePowerConfigProvider(context), new ArrayList<>()); + associationPublicKey, List.of(), port); } public LocalWebSocketServerScenario(@NonNull Context context, @@ -40,10 +40,11 @@ public LocalWebSocketServerScenario(@NonNull Context context, @NonNull AuthIssuerConfig authIssuerConfig, @NonNull LocalScenario.Callbacks callbacks, @NonNull byte[] associationPublicKey, - @WebSocketsTransportContract.LocalPortRange int port, - @NonNull List requestedProtocolVersions) { + @NonNull List associationProtocolVersions, + @WebSocketsTransportContract.LocalPortRange int port) { this(context, mobileWalletAdapterConfig, authIssuerConfig, callbacks, - associationPublicKey, port, new DevicePowerConfigProvider(context), requestedProtocolVersions); + associationPublicKey, port, new DevicePowerConfigProvider(context), + associationProtocolVersions); } /*package*/ LocalWebSocketServerScenario(@NonNull Context context, @@ -53,8 +54,9 @@ public LocalWebSocketServerScenario(@NonNull Context context, @NonNull byte[] associationPublicKey, @WebSocketsTransportContract.LocalPortRange int port, PowerConfigProvider powerConfigProvider, - @NonNull List requestedProtocolVersions) { - super(context, mobileWalletAdapterConfig, authIssuerConfig, callbacks, associationPublicKey, powerConfigProvider, requestedProtocolVersions); + @NonNull List associationProtocolVersions) { + super(context, mobileWalletAdapterConfig, authIssuerConfig, callbacks, associationPublicKey, + powerConfigProvider, associationProtocolVersions); this.port = port; this.mWebSocketServer = new LocalWebSocketServer(this, mWebSocketServerCallbacks); } diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/Scenario.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/Scenario.java index 2d3870f19..d9de54daa 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/Scenario.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/Scenario.java @@ -9,11 +9,12 @@ import com.solana.mobilewalletadapter.common.protocol.MessageReceiver; import com.solana.mobilewalletadapter.common.protocol.SessionProperties; -public interface Scenario { +import java.util.List; - SessionProperties getSessionProperties(); +public interface Scenario { byte[] getAssociationPublicKey(); + List getAssociationProtocolVersions(); MessageReceiver createMessageReceiver(); diff --git a/android/walletlib/src/test/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenarioTest.java b/android/walletlib/src/test/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenarioTest.java index eb2488a8a..8d0c08ce3 100644 --- a/android/walletlib/src/test/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenarioTest.java +++ b/android/walletlib/src/test/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalWebSocketServerScenarioTest.java @@ -43,8 +43,7 @@ public void testLowPowerNoConnectionCallbackIsNotCalled() throws InterruptedExce 1, new Object[] { "legacy" }, noConnectionTimeout, - new String[] {}, - List.of(SessionProperties.ProtocolVersion.LEGACY) + new String[] {} ); CountDownLatch latch = new CountDownLatch(1); @@ -62,8 +61,8 @@ public void onLowPowerAndNoConnection() { List.of(SessionProperties.ProtocolVersion.LEGACY); // when - new LocalWebSocketServerScenario(context, config, authConfig, - lowPowerNoConnectionCallback, publicKey, port, powerConfig, supportedVersions).start(); + new LocalWebSocketServerScenario(context, config, authConfig, lowPowerNoConnectionCallback, + publicKey, port, powerConfig, supportedVersions).start(); boolean lowPowerNoConnectionCallbackFired = latch.await(200, TimeUnit.MILLISECONDS); // then @@ -86,8 +85,7 @@ public void testLowPowerNoConnectionCallbackIsCalled() throws InterruptedExcepti 1, new Object[] { "legacy" }, noConnectionTimeout, - new String[] {}, - List.of(SessionProperties.ProtocolVersion.LEGACY) + new String[] {} ); CountDownLatch latch = new CountDownLatch(1); @@ -105,8 +103,8 @@ public void onLowPowerAndNoConnection() { List.of(SessionProperties.ProtocolVersion.LEGACY); // when - new LocalWebSocketServerScenario(context, config, authConfig, - lowPowerNoConnectionCallback, publicKey, port, powerConfig, supportedVersions).start(); + new LocalWebSocketServerScenario(context, config, authConfig, lowPowerNoConnectionCallback, + publicKey, port, powerConfig, supportedVersions).start(); boolean lowPowerNoConnectionCallbackFired = latch.await(200, TimeUnit.MILLISECONDS); // then From 5389720385b331b575e56463589856149ac8fe87 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Wed, 18 Oct 2023 15:51:44 -0600 Subject: [PATCH 05/11] change legacy flavor app name for easier differentiation --- android/fakewallet/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/android/fakewallet/build.gradle b/android/fakewallet/build.gradle index 6de80c8fa..64fff4a3b 100644 --- a/android/fakewallet/build.gradle +++ b/android/fakewallet/build.gradle @@ -67,6 +67,7 @@ android { legacy { dimension "protocol_version" applicationId "com.solana.mobilewalletadapter.fakewallet.legacy" + resValue "string", "app_name", "Fake Wallet App (Legacy)" buildConfigField "com.solana.mobilewalletadapter.common.protocol.SessionProperties.ProtocolVersion", "PROTOCOL_VERSION", "com.solana.mobilewalletadapter.common.protocol.SessionProperties.ProtocolVersion.LEGACY" } From 0bf0b1114eea61ccb9b6d543b4b1ac684d76824a Mon Sep 17 00:00:00 2001 From: funkatronics Date: Wed, 18 Oct 2023 16:05:07 -0600 Subject: [PATCH 06/11] couple small fixes --- .../walletlib/protocol/MobileWalletAdapterServer.java | 3 ++- .../mobilewalletadapter/walletlib/scenario/LocalScenario.java | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java index 82c9876e5..30fce2be9 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java @@ -327,7 +327,8 @@ private void handleAuthorize(@Nullable Object id, @Nullable Object params) throw final String cluster = o.optString(ProtocolContract.PARAMETER_CLUSTER); final String chainParam = o.optString(ProtocolContract.PARAMETER_CHAIN); final String chain = !chainParam.isEmpty() ? chainParam - : !cluster.isEmpty() ? Identifier.clusterToChainIdentifier(cluster) : null; + : !cluster.isEmpty() ? Identifier.clusterToChainIdentifier(cluster) + : ProtocolContract.CHAIN_SOLANA_MAINNET; final String authTokenParam = o.optString(ProtocolContract.PARAMETER_AUTH_TOKEN); final String authToken = authTokenParam.isEmpty() ? null : authTokenParam; diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java index 67e4dc448..53785cd14 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java @@ -105,6 +105,7 @@ public byte[] getAssociationPublicKey() { } @Override + @NonNull public List getAssociationProtocolVersions() { return associationProtocolVersions; } From b7628df9bc4b7d52e9d0493bde50d821425acae1 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Fri, 20 Oct 2023 09:16:16 -0600 Subject: [PATCH 07/11] resolve merge conflict --- .../walletlib/protocol/MobileWalletAdapterConfig.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java index 16ef0cbcf..c1f68d4af 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterConfig.java @@ -46,8 +46,7 @@ public MobileWalletAdapterConfig(boolean supportsSignAndSendTransactions, @IntRange(from = 0) long noConnectionWarningTimeoutMs) { this(maxTransactionsPerSigningRequest, maxMessagesPerSigningRequest, supportedTransactionVersions, noConnectionWarningTimeoutMs, - new String[] { ProtocolContract.FEATURE_ID_SIGN_TRANSACTIONS }, - List.of(SessionProperties.ProtocolVersion.LEGACY)); + new String[] { ProtocolContract.FEATURE_ID_SIGN_TRANSACTIONS }); if (!supportsSignAndSendTransactions) throw new IllegalArgumentException("signAndSendTransactions is required in MWA 2.0"); } @@ -56,12 +55,10 @@ public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigni @IntRange(from = 0) int maxMessagesPerSigningRequest, @NonNull @Size(min = 1) Object[] supportedTransactionVersions, @IntRange(from = 0) long noConnectionWarningTimeoutMs, - @NonNull String[] supportedFeatures, - @NonNull List supportedProtocolVersions) { + @NonNull String[] supportedFeatures) { this.maxTransactionsPerSigningRequest = maxTransactionsPerSigningRequest; this.maxMessagesPerSigningRequest = maxMessagesPerSigningRequest; this.noConnectionWarningTimeoutMs = noConnectionWarningTimeoutMs; - this.supportedProtocolVersions = supportedProtocolVersions; this.optionalFeatures = supportedFeatures; this.supportsSignAndSendTransactions = true; @@ -79,4 +76,4 @@ public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigni } } } -} +} \ No newline at end of file From e578f22424576f9a25fdd6a0a2f40479833416e6 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Fri, 20 Oct 2023 12:49:45 -0600 Subject: [PATCH 08/11] update fakewallet --- .../fakewallet/MobileWalletAdapterViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt index a9cb91fcb..a4236b02c 100644 --- a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt +++ b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt @@ -68,7 +68,7 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( 10, arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), LOW_POWER_NO_CONNECTION_TIMEOUT_MS, - arrayOf(ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS) + arrayOf(ProtocolContract.FEATURE_ID_SIGN_TRANSACTIONS) ), AuthIssuerConfig("fakewallet"), MobileWalletAdapterScenarioCallbacks(), From cff8deae4d08c99fecf38fd7daa1075826425d93 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Fri, 20 Oct 2023 12:55:42 -0600 Subject: [PATCH 09/11] update fakewallet --- .../fakewallet/MobileWalletAdapterViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt index a4236b02c..19820f439 100644 --- a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt +++ b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt @@ -84,7 +84,7 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( 10, arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), LOW_POWER_NO_CONNECTION_TIMEOUT_MS, - arrayOf(ProtocolContract.FEATURE_ID_SIGN_AND_SEND_TRANSACTIONS) + arrayOf(ProtocolContract.FEATURE_ID_SIGN_TRANSACTIONS) ), AuthIssuerConfig("fakewallet"), MobileWalletAdapterScenarioCallbacks() From df863d023a2735234a6ba9eb878a4986baa7d484 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Fri, 20 Oct 2023 12:56:43 -0600 Subject: [PATCH 10/11] some cleanup --- .../MobileWalletAdapterViewModel.kt | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt index 19820f439..f791fb8e5 100644 --- a/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt +++ b/android/fakewallet/src/main/java/com/solana/mobilewalletadapter/fakewallet/MobileWalletAdapterViewModel.kt @@ -58,18 +58,20 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( associationUri ) + val config = MobileWalletAdapterConfig( + 10, + 10, + arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), + LOW_POWER_NO_CONNECTION_TIMEOUT_MS, + arrayOf(ProtocolContract.FEATURE_ID_SIGN_TRANSACTIONS) + ) + scenario = if (BuildConfig.PROTOCOL_VERSION == SessionProperties.ProtocolVersion.LEGACY) { // manually create the scenario here so we can override the association protocol version // this forces ProtocolVersion.LEGACY to simulate a wallet using walletlib 1.x (for testing) LocalWebSocketServerScenario( getApplication().applicationContext, - MobileWalletAdapterConfig( - 10, - 10, - arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), - LOW_POWER_NO_CONNECTION_TIMEOUT_MS, - arrayOf(ProtocolContract.FEATURE_ID_SIGN_TRANSACTIONS) - ), + config, AuthIssuerConfig("fakewallet"), MobileWalletAdapterScenarioCallbacks(), associationUri.associationPublicKey, @@ -79,13 +81,7 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel( } else { associationUri.createScenario( getApplication().applicationContext, - MobileWalletAdapterConfig( - 10, - 10, - arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0), - LOW_POWER_NO_CONNECTION_TIMEOUT_MS, - arrayOf(ProtocolContract.FEATURE_ID_SIGN_TRANSACTIONS) - ), + config, AuthIssuerConfig("fakewallet"), MobileWalletAdapterScenarioCallbacks() ) From 4349086122d2b0368634eaf614ee4304b2818c76 Mon Sep 17 00:00:00 2001 From: funkatronics Date: Mon, 23 Oct 2023 12:42:42 -0600 Subject: [PATCH 11/11] clean up reauth, fixes bug with reauth chain --- .../protocol/MobileWalletAdapterServer.java | 313 ++++++------------ .../walletlib/scenario/LocalScenario.java | 69 ---- 2 files changed, 97 insertions(+), 285 deletions(-) diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java index 819deec88..65d2a8795 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/protocol/MobileWalletAdapterServer.java @@ -47,9 +47,6 @@ public interface MethodHandlers { void signTransactions(@NonNull SignTransactionsRequest request); void signMessages(@NonNull SignMessagesRequest request); void signAndSendTransactions(@NonNull SignAndSendTransactionsRequest request); - - @Deprecated - void reauthorize(@NonNull ReauthorizeRequest request); } public MobileWalletAdapterServer(@NonNull MobileWalletAdapterConfig config, @@ -123,12 +120,81 @@ private static String safeGetMessage(@Nullable Throwable t) { } // ============================================================================================= - // authorize/reauthorize shared types and methods + // authorize // ============================================================================================= - // NOTE: future may be either AuthorizeRequest or ReauthorizeRequest + private void handleAuthorize(@Nullable Object id, @Nullable Object params) throws IOException { + if (!(params instanceof JSONObject)) { + handleRpcError(id, ERROR_INVALID_PARAMS, "params must be either a JSONObject", null); + return; + } + + final JSONObject o = (JSONObject) params; + + final JSONObject ident = o.optJSONObject(ProtocolContract.PARAMETER_IDENTITY); + final Uri identityUri; + final Uri iconUri; + final String identityName; + if (ident != null) { + identityUri = ident.has(ProtocolContract.PARAMETER_IDENTITY_URI) ? + Uri.parse(ident.optString(ProtocolContract.PARAMETER_IDENTITY_URI)) : null; + if (identityUri != null && (!identityUri.isAbsolute() || !identityUri.isHierarchical())) { + handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, identity.uri must be an absolute, hierarchical URI", null); + return; + } + iconUri = ident.has(ProtocolContract.PARAMETER_IDENTITY_ICON) ? + Uri.parse(ident.optString(ProtocolContract.PARAMETER_IDENTITY_ICON)) : null; + if (iconUri != null && !iconUri.isRelative()) { + handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, identity.icon must be a relative URI", null); + return; + } + identityName = ident.has(ProtocolContract.PARAMETER_IDENTITY_NAME) ? + ident.optString(ProtocolContract.PARAMETER_IDENTITY_NAME) : null; + if (identityName != null && identityName.isEmpty()) { + handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, identity.name must be a non-empty string", null); + return; + } + } else { + identityUri = null; + iconUri = null; + identityName = null; + } + + final String authTokenParam = o.optString(ProtocolContract.PARAMETER_AUTH_TOKEN); + final String authToken = authTokenParam.isEmpty() ? null : authTokenParam; + + final String cluster = o.optString(ProtocolContract.PARAMETER_CLUSTER); + final String chainParam = o.optString(ProtocolContract.PARAMETER_CHAIN); + final String chain = !chainParam.isEmpty() ? chainParam + : !cluster.isEmpty() ? Identifier.clusterToChainIdentifier(cluster) + : authToken == null ? ProtocolContract.CHAIN_SOLANA_MAINNET : null; + + final String[] features; + try { + final JSONArray featuresArr = o.optJSONArray(ProtocolContract.PARAMETER_FEATURES); + features = featuresArr != null ? JsonPack.unpackStrings(featuresArr) : null; + } catch (JSONException e) { + handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, features must be a JSONArray of strings", null); + return; + } + + final String[] addresses; + try { + final JSONArray addressesArr = o.optJSONArray(ProtocolContract.PARAMETER_ADDRESSES); + addresses = addressesArr != null ? JsonPack.unpackStrings(addressesArr) : null; + } catch (JSONException e) { + handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, addresses must be a JSONArray of strings", null); + return; + } + + final AuthorizeRequest request = + new AuthorizeRequest(id, identityUri, iconUri, identityName, chain, features, addresses, authToken); + request.notifyOnComplete((f) -> mHandler.post(() -> onAuthorizationComplete(f))); + mMethodHandlers.authorize(request); + } + private void onAuthorizationComplete(@NonNull NotifyOnCompleteFuture future) { - final AuthorizationRequest request = (AuthorizationRequest) future; + final AuthorizeRequest request = (AuthorizeRequest) future; try { final AuthorizationResult result; @@ -136,13 +202,9 @@ private void onAuthorizationComplete(@NonNull NotifyOnCompleteFuture { + public static class AuthorizeRequest extends RequestFuture { + @Nullable public final Uri identityUri; @Nullable @@ -199,17 +262,31 @@ public static abstract class AuthorizationRequest extends RequestFuture mHandler.post(() -> onAuthorizationComplete(f))); - mMethodHandlers.authorize(request); - } - - public static class AuthorizeRequest extends AuthorizationRequest { - - @Nullable @Deprecated - public final String cluster; - @Nullable - public final String chain; - - @Nullable - public final String[] features; - - @Nullable - public final String[] addresses; - - private AuthorizeRequest(@Nullable Object id, - @Nullable Uri identityUri, - @Nullable Uri iconUri, - @Nullable String identityName, - @Nullable String chain, - @Nullable String[] features, - @Nullable String[] addresses, - @Nullable String authToken) { - super(id, identityUri, iconUri, identityName, authToken); - this.chain = chain; - this.cluster = null; - this.features = features; - this.addresses = addresses; - } - - @Override - public boolean complete(@Nullable AuthorizationResult result) { - if (result == null) { - throw new IllegalArgumentException("A non-null result must be provided"); - } - return super.complete(result); - } - - @NonNull - @Override - public String toString() { - return "AuthorizeRequest{" + - "chain='" + chain + '\'' + - '/' + super.toString() + - '}'; - } - } - - // ============================================================================================= - // reauthorize - // ============================================================================================= - - @Deprecated - private void handleReauthorize(@Nullable Object id, @Nullable Object params) throws IOException { - if (!(params instanceof JSONObject)) { - handleRpcError(id, ERROR_INVALID_PARAMS, "params must be either a JSONObject", null); - return; - } - - final JSONObject o = (JSONObject) params; - - final JSONObject ident = o.optJSONObject(ProtocolContract.PARAMETER_IDENTITY); - final Uri identityUri; - final Uri iconUri; - final String identityName; - if (ident != null) { - identityUri = ident.has(ProtocolContract.PARAMETER_IDENTITY_URI) ? - Uri.parse(ident.optString(ProtocolContract.PARAMETER_IDENTITY_URI)) : null; - if (identityUri != null && (!identityUri.isAbsolute() || !identityUri.isHierarchical())) { - handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, identity.uri must be an absolute, hierarchical URI", null); - return; - } - iconUri = ident.has(ProtocolContract.PARAMETER_IDENTITY_ICON) ? - Uri.parse(ident.optString(ProtocolContract.PARAMETER_IDENTITY_ICON)) : null; - if (iconUri != null && !iconUri.isRelative()) { - handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, identity.icon must be a relative URI", null); - return; - } - identityName = ident.has(ProtocolContract.PARAMETER_IDENTITY_NAME) ? - ident.optString(ProtocolContract.PARAMETER_IDENTITY_NAME) : null; - if (identityName != null && identityName.isEmpty()) { - handleRpcError(id, ERROR_INVALID_PARAMS, "When specified, identity.name must be a non-empty string", null); - return; - } - } else { - identityUri = null; - iconUri = null; - identityName = null; - } - - final String authToken = o.optString(ProtocolContract.PARAMETER_AUTH_TOKEN); - if (authToken.isEmpty()) { - handleRpcError(id, ERROR_INVALID_PARAMS, "auth_token must be a non-empty string", null); - return; - } - - final ReauthorizeRequest request = new ReauthorizeRequest(id, identityUri, iconUri, identityName, authToken); - request.notifyOnComplete((f) -> mHandler.post(() -> onAuthorizationComplete(f))); - mMethodHandlers.reauthorize(request); - } - - public static class ReauthorizeRequest extends AuthorizationRequest { - @NonNull - public final String authToken; - - private ReauthorizeRequest(@Nullable Object id, - @Nullable Uri identityUri, - @Nullable Uri iconUri, - @Nullable String identityName, - @NonNull String authToken) { - super(id, identityUri, iconUri, identityName, authToken); - this.authToken = authToken; - } - - @NonNull - @Override - public String toString() { - return "ReauthorizeRequest{" + - "authToken=" + - '/' + super.toString() + - '}'; - } - } - // ============================================================================================= // deauthorize // ============================================================================================= diff --git a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java index 53785cd14..a8141b380 100644 --- a/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java +++ b/android/walletlib/src/main/java/com/solana/mobilewalletadapter/walletlib/scenario/LocalScenario.java @@ -325,75 +325,6 @@ private void doReauthorize(@NonNull MobileWalletAdapterServer.AuthorizeRequest r authRecord.chain, authRecord.scope))); } - @Override - public void reauthorize(@NonNull MobileWalletAdapterServer.ReauthorizeRequest request) { - // Clear the active auth token immediately upon beginning reauthorization; it will be - // set to valid only on successful completion - synchronized (mLock) { - mActiveAuthorization = null; - } - - final AuthRecord authRecord = mAuthRepository.fromAuthToken(request.authToken); - if (authRecord == null) { - mIoHandler.post(() -> request.completeExceptionally( - new MobileWalletAdapterServer.AuthorizationNotValidException( - "auth_token not valid for this request"))); - return; - } - - final NotifyingCompletableFuture future = new NotifyingCompletableFuture<>(); - future.notifyOnComplete(f -> mIoHandler.post(() -> { // Note: run in IO thread context - try { - final Boolean reauthorize = f.get(); // won't block - if (!reauthorize) { - mIoHandler.post(() -> request.completeExceptionally( - new MobileWalletAdapterServer.RequestDeclinedException( - "app declined reauthorization request"))); - mAuthRepository.revoke(authRecord); - return; - } - - final AuthRecord reissuedAuthRecord = mAuthRepository.reissue(authRecord); - if (reissuedAuthRecord == null) { - // No need to explicitly revoke the old auth token; that is part of the - // reissue method contract - mIoHandler.post(() -> request.completeExceptionally( - new MobileWalletAdapterServer.RequestDeclinedException( - "auth_token not valid for reissue"))); - return; - } - synchronized (mLock) { - mActiveAuthorization = reissuedAuthRecord; - } - - final String authToken; - if (reissuedAuthRecord == authRecord) { - // Reissued same auth record; don't regenerate the token - authToken = request.authToken; - } else { - authToken = mAuthRepository.toAuthToken(reissuedAuthRecord); - } - - mIoHandler.post(() -> request.complete( - new MobileWalletAdapterServer.AuthorizationResult( - authToken, authRecord.publicKey, authRecord.accountLabel, - authRecord.walletUriBase))); - } catch (ExecutionException e) { - final Throwable cause = e.getCause(); - assert(cause instanceof Exception); // expected to always be an Exception - request.completeExceptionally((Exception)cause); - } catch (InterruptedException e) { - throw new RuntimeException("Unexpected interruption while waiting for reauthorization", e); - } catch (CancellationException e) { - request.cancel(true); - } - })); - - mIoHandler.post(() -> mCallbacks.onReauthorizeRequest(new ReauthorizeRequest( - future, request.identityName, request.identityUri, request.iconUri, - authRecord.chain, authRecord.scope))); - } - @Override public void deauthorize(@NonNull MobileWalletAdapterServer.DeauthorizeRequest request) { final AuthRecord authRecord = mAuthRepository.fromAuthToken(request.authToken);