Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

walletlib + fakewallet updates #574

Merged
merged 15 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions android/fakewallet/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ 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"
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"
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
Expand All @@ -66,6 +84,7 @@ android {

buildFeatures {
viewBinding true
buildConfig true
}
}

Expand Down Expand Up @@ -100,6 +119,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'
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ 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.common.protocol.SessionProperties
import com.solana.mobilewalletadapter.fakewallet.usecase.*
import com.solana.mobilewalletadapter.walletlib.association.AssociationUri
import com.solana.mobilewalletadapter.walletlib.association.LocalAssociationUri
Expand Down Expand Up @@ -56,18 +58,34 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel(
associationUri
)

scenario = associationUri.createScenario(
getApplication<FakeWalletApplication>().applicationContext,
MobileWalletAdapterConfig(
true,
10,
10,
arrayOf(MobileWalletAdapterConfig.LEGACY_TRANSACTION_VERSION, 0),
LOW_POWER_NO_CONNECTION_TIMEOUT_MS,
),
AuthIssuerConfig("fakewallet"),
MobileWalletAdapterScenarioCallbacks()
).also { it.start() }
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<FakeWalletApplication>().applicationContext,
config,
AuthIssuerConfig("fakewallet"),
MobileWalletAdapterScenarioCallbacks(),
associationUri.associationPublicKey,
listOf(SessionProperties.ProtocolVersion.LEGACY),
associationUri.port,
)
} else {
associationUri.createScenario(
getApplication<FakeWalletApplication>().applicationContext,
config,
AuthIssuerConfig("fakewallet"),
MobileWalletAdapterScenarioCallbacks()
)
}.also { it.start() }

return true
}
Expand All @@ -90,12 +108,9 @@ class MobileWalletAdapterViewModel(application: Application) : AndroidViewModel(
val keypair = getApplication<FakeWalletApplication>().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()
}
Expand Down Expand Up @@ -258,7 +273,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!!)
}
Expand All @@ -268,7 +283,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 ->
Expand Down Expand Up @@ -353,14 +368,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,
Copy link
Contributor

Choose a reason for hiding this comment

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

Whats the need for this method if it just returns an AuthorizedAccount constructor?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

convenience

chains: Array<String>? = null, features: Array<String>? = 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")
}
}

Expand Down Expand Up @@ -457,7 +483,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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ public abstract class AssociationUri {
public final byte[] associationPublicKey;

@NonNull
public final List<SessionProperties.ProtocolVersion> supportedProtocolVersions;
public final List<SessionProperties.ProtocolVersion> 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) {
Expand Down Expand Up @@ -68,9 +68,11 @@ private static List<SessionProperties.ProtocolVersion> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,6 @@ public class MobileWalletAdapterConfig {
@Size(min = 1)
public final Object[] supportedTransactionVersions;

@NonNull
public final List<SessionProperties.ProtocolVersion> supportedProtocolVersions;

@NonNull
public final String[] optionalFeatures;

Expand All @@ -49,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");
}
Expand All @@ -59,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<SessionProperties.ProtocolVersion> supportedProtocolVersions) {
@NonNull String[] supportedFeatures) {
this.maxTransactionsPerSigningRequest = maxTransactionsPerSigningRequest;
this.maxMessagesPerSigningRequest = maxMessagesPerSigningRequest;
this.noConnectionWarningTimeoutMs = noConnectionWarningTimeoutMs;
this.supportedProtocolVersions = supportedProtocolVersions;
this.optionalFeatures = supportedFeatures;
this.supportsSignAndSendTransactions = true;

Expand All @@ -82,4 +76,4 @@ public MobileWalletAdapterConfig(@IntRange(from = 0) int maxTransactionsPerSigni
}
}
}
}
}
Loading