diff --git a/.github/workflows/ci_relay.yml b/.github/workflows/ci_relay.yml index d0e3c2df9..9f4b0b5f8 100644 --- a/.github/workflows/ci_relay.yml +++ b/.github/workflows/ci_relay.yml @@ -26,6 +26,7 @@ jobs: env: TEST_RELAY_URL: wss://relay.walletconnect.org TEST_PROJECT_ID: ${{ secrets.WC_CLOUD_PROJECT_ID }} + TEST_PROJECT_ID2: ${{ secrets.TEST_PROJECT_ID2 }} NOTIFY_INTEGRATION_TESTS_PROJECT_ID: ${{ secrets.NOTIFY_INTEGRATION_TESTS_PROJECT_ID }} NOTIFY_INTEGRATION_TESTS_SECRET: ${{ secrets.NOTIFY_INTEGRATION_TESTS_SECRET }} with: diff --git a/.github/workflows/ci_scheduled.yml b/.github/workflows/ci_scheduled.yml index e46908687..bb2271157 100644 --- a/.github/workflows/ci_scheduled.yml +++ b/.github/workflows/ci_scheduled.yml @@ -29,6 +29,7 @@ jobs: env: TEST_RELAY_URL: wss://relay.walletconnect.org TEST_PROJECT_ID: ${{ secrets.WC_CLOUD_PROJECT_ID }} + TEST_PROJECT_ID2: ${{ secrets.TEST_PROJECT_ID2 }} with: SECRETS_PROPERTIES: ${{ secrets.SECRETS_PROPERTIES }} uses: ./.github/actions/ci_relay diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 77b94d90f..255ebb040 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -10,4 +10,16 @@ dependencies { implementation("com.google.firebase:firebase-appdistribution-gradle:${libs.versions.firebaseAppDistribution.get()}") testImplementation("junit:junit:${libs.versions.jUnit.get()}") +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "11" + } } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/src/main/kotlin/Versions.kt index bac44a1b5..e8c8b9426 100644 --- a/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/src/main/kotlin/Versions.kt @@ -5,14 +5,14 @@ const val KEY_PUBLISH_ARTIFACT_ID = "PUBLISH_ARTIFACT_ID" const val KEY_SDK_NAME = "SDK_NAME" //Latest versions -const val BOM_VERSION = "1.0.2" -const val FOUNDATION_VERSION = "1.0.2" -const val CORE_VERSION = "1.0.2" -const val SIGN_VERSION = "1.0.2" -const val NOTIFY_VERSION = "1.0.2" -const val WALLETKIT_VERSION = "1.0.2" -const val APPKIT_VERSION = "1.0.2" -const val MODAL_CORE_VERSION = "1.0.2" +const val BOM_VERSION = "1.0.3" +const val FOUNDATION_VERSION = "1.0.3" +const val CORE_VERSION = "1.0.3" +const val SIGN_VERSION = "1.0.3" +const val NOTIFY_VERSION = "1.0.3" +const val WALLETKIT_VERSION = "1.0.3" +const val APPKIT_VERSION = "1.0.3" +const val MODAL_CORE_VERSION = "1.0.3" //Artifact ids const val ANDROID_BOM = "android-bom" diff --git a/core/android/src/debug/kotlin/com/reown/android/di/CoreStorageModule.kt b/core/android/src/debug/kotlin/com/reown/android/di/CoreStorageModule.kt index 1e7722309..ede35c0bb 100644 --- a/core/android/src/debug/kotlin/com/reown/android/di/CoreStorageModule.kt +++ b/core/android/src/debug/kotlin/com/reown/android/di/CoreStorageModule.kt @@ -12,9 +12,9 @@ import org.koin.android.ext.koin.androidContext import org.koin.core.qualifier.named import org.koin.dsl.module -fun coreStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { +fun coreStorageModule(storagePrefix: String = String.Empty, packageName: String) = module { - includes(baseStorageModule(storagePrefix, bundleId)) + includes(baseStorageModule(storagePrefix, packageName)) single(named(AndroidBuildVariantDITags.ANDROID_CORE_DATABASE_DRIVER)) { AndroidSqliteDriver( diff --git a/core/android/src/main/kotlin/com/reown/android/CoreProtocol.kt b/core/android/src/main/kotlin/com/reown/android/CoreProtocol.kt index 4e3996dc0..2b89de32a 100644 --- a/core/android/src/main/kotlin/com/reown/android/CoreProtocol.kt +++ b/core/android/src/main/kotlin/com/reown/android/CoreProtocol.kt @@ -138,7 +138,7 @@ class CoreProtocol(private val koinApp: KoinApplication = wcKoinApp) : CoreInter metaData: Core.Model.AppMetaData, keyServerUrl: String? ) { - val bundleId: String = application.packageName + val packageName: String = application.packageName val relayServerUrl = if (serverUrl.isNullOrEmpty()) "wss://relay.walletconnect.org?projectId=$projectId" else serverUrl with(koinApp) { @@ -146,7 +146,7 @@ class CoreProtocol(private val koinApp: KoinApplication = wcKoinApp) : CoreInter modules( module { single { ProjectId(projectId) } }, module { single(named(AndroidCommonDITags.TELEMETRY_ENABLED)) { TelemetryEnabled(telemetryEnabled) } }, - coreAndroidNetworkModule(relayServerUrl, connectionType, BuildConfig.SDK_VERSION, networkClientTimeout, bundleId), + coreAndroidNetworkModule(relayServerUrl, connectionType, BuildConfig.SDK_VERSION, networkClientTimeout, packageName), coreCommonModule(), coreCryptoModule(), ) @@ -156,7 +156,7 @@ class CoreProtocol(private val koinApp: KoinApplication = wcKoinApp) : CoreInter } modules( - coreStorageModule(bundleId = bundleId), + coreStorageModule(packageName = packageName), module { single(named(AndroidCommonDITags.CLIENT_ID)) { requireNotNull(get().getString(KEY_CLIENT_ID, null)) } }, pushModule(), module { single { relay ?: Relay } }, @@ -181,7 +181,7 @@ class CoreProtocol(private val koinApp: KoinApplication = wcKoinApp) : CoreInter keyServerModule(keyServerUrl), explorerModule(), appKitModule(), - pulseModule(bundleId) + pulseModule(packageName) ) } diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/BaseStorageModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/BaseStorageModule.kt index a1a5e8178..69e83dd3a 100644 --- a/core/android/src/main/kotlin/com/reown/android/internal/common/di/BaseStorageModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/BaseStorageModule.kt @@ -28,7 +28,7 @@ import org.koin.core.scope.Scope import org.koin.dsl.module import com.reown.android.internal.common.scope as wcScope -fun baseStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { +fun baseStorageModule(storagePrefix: String = String.Empty, packageName: String) = module { single, String>>(named(AndroidCommonDITags.COLUMN_ADAPTER_LIST)) { object : ColumnAdapter, String> { override fun decode(databaseValue: String): List = @@ -128,7 +128,7 @@ fun baseStorageModule(storagePrefix: String = String.Empty, bundleId: String) = single { PushMessagesRepository(pushMessageQueries = get()) } - single { EventsRepository(eventQueries = get(), bundleId = bundleId, telemetryEnabled = get(named(AndroidCommonDITags.TELEMETRY_ENABLED))) } + single { EventsRepository(eventQueries = get(), bundleId = packageName, telemetryEnabled = get(named(AndroidCommonDITags.TELEMETRY_ENABLED))) } single { DatabaseConfig(storagePrefix = storagePrefix) } } \ No newline at end of file diff --git a/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreNetworkModule.kt b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreNetworkModule.kt index 52e3ce157..6bd84a750 100644 --- a/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreNetworkModule.kt +++ b/core/android/src/main/kotlin/com/reown/android/internal/common/di/CoreNetworkModule.kt @@ -34,7 +34,7 @@ internal const val KEY_CLIENT_ID = "clientId" @Suppress("LocalVariableName") @JvmSynthetic -fun coreAndroidNetworkModule(serverUrl: String, connectionType: ConnectionType, sdkVersion: String, timeout: NetworkClientTimeout? = null, bundleId: String) = module { +fun coreAndroidNetworkModule(serverUrl: String, connectionType: ConnectionType, sdkVersion: String, timeout: NetworkClientTimeout? = null, packageName: String) = module { val networkClientTimeout = timeout ?: NetworkClientTimeout.getDefaultTimeout() factory(named(AndroidCommonDITags.RELAY_URL)) { val jwt = get().invoke(serverUrl) @@ -42,6 +42,7 @@ fun coreAndroidNetworkModule(serverUrl: String, connectionType: ConnectionType, .buildUpon() .appendQueryParameter("auth", jwt) .appendQueryParameter("ua", get(named(AndroidCommonDITags.USER_AGENT))) + .appendQueryParameter("packageName", packageName) .build() .toString() } @@ -58,7 +59,6 @@ fun coreAndroidNetworkModule(serverUrl: String, connectionType: ConnectionType, Interceptor { chain -> val updatedRequest = chain.request().newBuilder() .addHeader("User-Agent", get(named(AndroidCommonDITags.USER_AGENT))) - .addHeader("Origin", bundleId) .build() chain.proceed(updatedRequest) diff --git a/core/android/src/release/kotlin/com.reown.android/di/CoreStorageModule.kt b/core/android/src/release/kotlin/com.reown.android/di/CoreStorageModule.kt index 1b67f7f08..61cdd59bf 100644 --- a/core/android/src/release/kotlin/com.reown.android/di/CoreStorageModule.kt +++ b/core/android/src/release/kotlin/com.reown.android/di/CoreStorageModule.kt @@ -178,9 +178,9 @@ private fun loadSqlCipherLibrary(context: Context) { } } -fun coreStorageModule(storagePrefix: String = String.Empty, bundleId: String) = module { +fun coreStorageModule(storagePrefix: String = String.Empty, packageName: String) = module { - includes(baseStorageModule(storagePrefix, bundleId), signingModule()) + includes(baseStorageModule(storagePrefix, packageName), signingModule()) single(named(AndroidBuildVariantDITags.ANDROID_CORE_DATABASE_DRIVER)) { AndroidSqliteDriver( diff --git a/foundation/build.gradle.kts b/foundation/build.gradle.kts index 7cea88f35..d21f32532 100644 --- a/foundation/build.gradle.kts +++ b/foundation/build.gradle.kts @@ -28,6 +28,7 @@ tasks.withType { systemProperty("SDK_VERSION", requireNotNull(project.extra.get(KEY_PUBLISH_VERSION))) systemProperty("TEST_RELAY_URL", System.getenv("TEST_RELAY_URL")) systemProperty("TEST_PROJECT_ID", System.getenv("TEST_PROJECT_ID")) + systemProperty("TEST_PROJECT_ID2", System.getenv("TEST_PROJECT_ID2")) } dependencies { diff --git a/foundation/src/main/kotlin/com/reown/foundation/di/FoundationNetworkModule.kt b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationNetworkModule.kt index 0ed01af79..616a69938 100644 --- a/foundation/src/main/kotlin/com/reown/foundation/di/FoundationNetworkModule.kt +++ b/foundation/src/main/kotlin/com/reown/foundation/di/FoundationNetworkModule.kt @@ -15,7 +15,7 @@ import org.koin.core.qualifier.named import org.koin.dsl.module import java.util.concurrent.TimeUnit -fun networkModule(serverUrl: String, sdkVersion: String, jwt: String): Module = module { +fun networkModule(serverUrl: String, sdkVersion: String, jwt: String, packageName: String): Module = module { val DEFAULT_BACKOFF_SECONDS = 5L val TIMEOUT_TIME = 40000L @@ -50,7 +50,7 @@ fun networkModule(serverUrl: String, sdkVersion: String, jwt: String): Module = single(named(FoundationDITags.SCARLET)) { Scarlet.Builder() .backoffStrategy(get()) - .webSocketFactory(get(named(FoundationDITags.OK_HTTP)).newWebSocketFactory("$serverUrl&auth=$jwt")) + .webSocketFactory(get(named(FoundationDITags.OK_HTTP)).newWebSocketFactory("$serverUrl&auth=$jwt&packageName=$packageName")) .addMessageAdapterFactory(get(named(FoundationDITags.MSG_ADAPTER))) .addStreamAdapterFactory(get()) .build() diff --git a/foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt b/foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt index 505e164b4..4e2c9a894 100644 --- a/foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt +++ b/foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt @@ -41,12 +41,106 @@ sealed class TestState { @ExperimentalCoroutinesApi class RelayTest { private val testProjectId: String = requireNotNull(System.getProperty("TEST_PROJECT_ID")) + private val testProjectId2: String = requireNotNull(System.getProperty("TEST_PROJECT_ID2")) private val testRelayUrl: String = requireNotNull(System.getProperty("TEST_RELAY_URL")) - private val serverUrl = "$testRelayUrl?projectId=$testProjectId" + private var serverUrl = "$testRelayUrl?projectId=$testProjectId" private val sdkVersion: String = System.getProperty("SDK_VERSION") + "-relayTest" private val testJob: CompletableJob = SupervisorJob() private val testScope: CoroutineScope = CoroutineScope(testJob + Dispatchers.IO) + + @ExperimentalTime + @Test + fun `Connect with empty packageName when some packageName is already configured in Cloud - successful connection`() { + val testState = MutableStateFlow(TestState.Idle) + val (clientA: RelayInterface, clientB: RelayInterface) = initTwoClients(packageName = "") + + //Await connection + val connectionTime = measureTime { awaitConnection(clientA, clientB) }.inWholeMilliseconds + println("Connection time: $connectionTime ms") + testState.compareAndSet(expect = TestState.Idle, update = TestState.Success) + + //Lock until is finished or timed out + runBlocking { + val start = System.currentTimeMillis() + // Await test finish or check if timeout occurred + while (testState.value is TestState.Idle && !didTimeout(start, 60000L)) { + delay(10) + } + + // Success or fail or idle + when (testState.value) { + is TestState.Success -> return@runBlocking + is TestState.Error -> fail((testState.value as TestState.Error).message) + is TestState.Idle -> fail("Test timeout") + } + } + } + + @ExperimentalTime + @Test + fun `Connect with not whitelisted packageName when some packageName is already configured in Cloud - return an error`() { + val testState = MutableStateFlow(TestState.Idle) + val (clientA: RelayInterface, clientB: RelayInterface) = initTwoClients(packageName = "com.test.failure") + + clientA.eventsFlow.onEach { event -> + when (event) { + is Relay.Model.Event.OnConnectionFailed -> { + if (event.throwable.message?.contains("403") == true) { + testState.compareAndSet(expect = TestState.Idle, update = TestState.Success) + } + } + + else -> {} + } + }.launchIn(testScope) + + //Lock until is finished or timed out + runBlocking { + val start = System.currentTimeMillis() + // Await test finish or check if timeout occurred + while (testState.value is TestState.Idle && !didTimeout(start, 60000L)) { + delay(10) + } + + // Success or fail or idle + when (testState.value) { + is TestState.Success -> return@runBlocking + is TestState.Error -> fail((testState.value as TestState.Error).message) + is TestState.Idle -> fail("Test timeout") + } + } + } + + @ExperimentalTime + @Test + fun `Connect with some packageName when no packageName is configured in Cloud - successful connection`() { + serverUrl = "$testRelayUrl?projectId=$testProjectId2" + val testState = MutableStateFlow(TestState.Idle) + val (clientA: RelayInterface, clientB: RelayInterface) = initTwoClients(packageName = "com.test") + + //Await connection + val connectionTime = measureTime { awaitConnection(clientA, clientB) }.inWholeMilliseconds + println("Connection time: $connectionTime ms") + testState.compareAndSet(expect = TestState.Idle, update = TestState.Success) + + //Lock until is finished or timed out + runBlocking { + val start = System.currentTimeMillis() + // Await test finish or check if timeout occurred + while (testState.value is TestState.Idle && !didTimeout(start, 60000L)) { + delay(10) + } + + // Success or fail or idle + when (testState.value) { + is TestState.Success -> return@runBlocking + is TestState.Error -> fail((testState.value as TestState.Error).message) + is TestState.Idle -> fail("Test timeout") + } + } + } + @ExperimentalTime @Test fun `One client sends unencrypted message, second one receives it`() { @@ -152,13 +246,13 @@ class RelayTest { private fun startLoggingClientEventsFlow(client: RelayInterface, tag: String) = client.eventsFlow.onEach { println("$tag eventsFlow: $it") }.launchIn(testScope) - private fun initTwoClients(): Pair { + private fun initTwoClients(packageName: String = "com.reown.sample.wallet"): Pair { val koinAppA: KoinApplication = KoinApplication.init() .apply { modules(foundationCommonModule(), cryptoModule()) }.also { koinApp -> val jwt = koinApp.koin.get().generateJWT(testRelayUrl) { clientId -> println("ClientA id: $clientId") } - koinApp.modules(networkModule(serverUrl.addUserAgent(sdkVersion), sdkVersion, jwt)) + koinApp.modules(networkModule(serverUrl.addUserAgent(sdkVersion), sdkVersion, jwt, packageName)) } val koinAppB: KoinApplication = KoinApplication.init() @@ -166,7 +260,7 @@ class RelayTest { val jwt = koinApp.koin.get().generateJWT(testRelayUrl) { clientId -> println("ClientB id: $clientId") } - koinApp.modules(networkModule(serverUrl.addUserAgent(sdkVersion), sdkVersion, jwt)) + koinApp.modules(networkModule(serverUrl.addUserAgent(sdkVersion), sdkVersion, jwt, packageName)) } val clientA: BaseRelayClient = koinAppA.koin.get() diff --git a/protocol/notify/src/androidTest/kotlin/com/reown/notify/di/OverrideModule.kt b/protocol/notify/src/androidTest/kotlin/com/reown/notify/di/OverrideModule.kt index 64549d500..066e69159 100644 --- a/protocol/notify/src/androidTest/kotlin/com/reown/notify/di/OverrideModule.kt +++ b/protocol/notify/src/androidTest/kotlin/com/reown/notify/di/OverrideModule.kt @@ -35,6 +35,6 @@ internal fun overrideModule( corePairingModule(pairing, pairingController), coreCryptoModule(sharedPrefsFile, keyStoreAlias), coreJsonRpcModule(), - coreAndroidNetworkModule(relayUrl, connectionType, "test_version", bundleId = bundleId) + coreAndroidNetworkModule(relayUrl, connectionType, "test_version", packageName = bundleId) ) } \ No newline at end of file diff --git a/protocol/sign/src/androidTest/kotlin/com/reown/sign/di/OverrideModule.kt b/protocol/sign/src/androidTest/kotlin/com/reown/sign/di/OverrideModule.kt index d781b64a8..dc49a9a50 100644 --- a/protocol/sign/src/androidTest/kotlin/com/reown/sign/di/OverrideModule.kt +++ b/protocol/sign/src/androidTest/kotlin/com/reown/sign/di/OverrideModule.kt @@ -34,7 +34,7 @@ internal fun overrideModule( coreStorageModule(storagePrefix, bundleId), corePairingModule(pairing, pairingController), coreCryptoModule(sharedPrefsFile, keyStoreAlias), - coreAndroidNetworkModule(relayUrl, connectionType, "test_version", bundleId = bundleId), + coreAndroidNetworkModule(relayUrl, connectionType, "test_version", packageName = bundleId), coreJsonRpcModule() ) } \ No newline at end of file