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

feat: TVF #46

Open
wants to merge 16 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ internal fun JsonRpcHistoryRecord.toWCResponse(result: JsonRpcResponse, params:

@JvmSynthetic
internal fun IrnParams.toRelay(): Relay.Model.IrnParams =
Relay.Model.IrnParams(tag.id, ttl.seconds, prompt)
Relay.Model.IrnParams(tag.id, ttl.seconds, correlationId, rpcMethods, chainId, txHashes, contractAddresses, prompt)

internal fun Subscription.toWCRequest(clientJsonRpc: ClientJsonRpc, params: ClientParams, transportType: TransportType): WCRequest =
WCRequest(topic, clientJsonRpc.id, clientJsonRpc.method, params, decryptedMessage, publishedAt, encryptedMessage, attestation, transportType)
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,13 @@ package com.reown.android.internal.common.model

import com.reown.foundation.common.model.Ttl

data class IrnParams(val tag: Tags, val ttl: Ttl, val prompt: Boolean = false)
data class IrnParams(
val tag: Tags,
val ttl: Ttl,
val correlationId: Long? = null,
val rpcMethods: List<String>? = null,
val chainId: String? = null,
val txHashes: List<String>? = null,
val contractAddresses: List<String>? = null,
val prompt: Boolean = false
jakubuid marked this conversation as resolved.
Show resolved Hide resolved
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package com.reown.android.internal.common.model
enum class Tags(val id: Int) {
UNSUPPORTED_METHOD(0),

//Pairing Tags
PAIRING_DELETE(1000),
PAIRING_DELETE_RESPONSE(1001),

PAIRING_PING(1002),
PAIRING_PING_RESPONSE(1003),

//Sign Tags
SESSION_PROPOSE(1100),
SESSION_PROPOSE_RESPONSE_APPROVE(1101),
SESSION_PROPOSE_RESPONSE_REJECT(1120),
Expand Down Expand Up @@ -47,6 +49,7 @@ enum class Tags(val id: Int) {
SESSION_REQUEST_LINK_MODE(1125),
SESSION_REQUEST_LINK_MODE_RESPONSE(1126),

//Notify Tags
NOTIFY_SUBSCRIBE(4000),
NOTIFY_SUBSCRIBE_RESPONSE(4001),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ internal class PairingEngine(
jsonRpcInteractor.clientSyncJsonRpc
.filter { request -> request.method !in setOfRegisteredMethods }
.onEach { request ->
val irnParams = IrnParams(Tags.UNSUPPORTED_METHOD, Ttl(dayInSeconds))
val irnParams = IrnParams(Tags.UNSUPPORTED_METHOD, Ttl(dayInSeconds), correlationId = request.id)
jsonRpcInteractor.respondWithError(request, Invalid.MethodUnsupported(request.method), irnParams)
}.map { request ->
SDKError(Exception(Invalid.MethodUnsupported(request.method).message))
Expand Down Expand Up @@ -237,10 +237,9 @@ internal class PairingEngine(
return onFailure(CannotFindSequenceForTopic("$NO_SEQUENCE_FOR_TOPIC_MESSAGE$topic"))
}

val pairing = pairingRepository.getPairingOrNullByTopic(Topic(topic))
val deleteParams = PairingParams.DeleteParams(6000, "User disconnected")
val pairingDelete = PairingRpc.PairingDelete(params = deleteParams)
val irnParams = IrnParams(Tags.PAIRING_DELETE, Ttl(dayInSeconds))
val irnParams = IrnParams(Tags.PAIRING_DELETE, Ttl(dayInSeconds), correlationId = pairingDelete.id)
logger.log("Sending Pairing disconnect")
jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, pairingDelete,
onSuccess = {
Expand All @@ -264,7 +263,7 @@ internal class PairingEngine(
fun ping(topic: String, onSuccess: (String) -> Unit, onFailure: (Throwable) -> Unit) {
if (isPairingValid(topic)) {
val pingPayload = PairingRpc.PairingPing(params = PairingParams.PingParams())
val irnParams = IrnParams(Tags.PAIRING_PING, Ttl(thirtySeconds))
val irnParams = IrnParams(Tags.PAIRING_PING, Ttl(thirtySeconds), correlationId = pingPayload.id)

jsonRpcInteractor.publishJsonRpcRequest(Topic(topic), irnParams, pingPayload,
onSuccess = { onPingSuccess(pingPayload, onSuccess, topic, onFailure) },
Expand Down Expand Up @@ -379,9 +378,8 @@ internal class PairingEngine(

@Deprecated(message = "This method has been deprecated. It will be removed soon.")
private suspend fun onPairingDelete(request: WCRequest, params: PairingParams.DeleteParams) {
val irnParams = IrnParams(Tags.PAIRING_DELETE_RESPONSE, Ttl(dayInSeconds))
val irnParams = IrnParams(Tags.PAIRING_DELETE_RESPONSE, Ttl(dayInSeconds), correlationId = request.id)
try {
val pairing = pairingRepository.getPairingOrNullByTopic(request.topic)
if (!isPairingValid(request.topic.value)) {
jsonRpcInteractor.respondWithError(request, Uncategorized.NoMatchingTopic("Pairing", request.topic.value), irnParams)
return
Expand All @@ -401,7 +399,7 @@ internal class PairingEngine(

@Deprecated(message = "Ping method has been deprecated. It will be removed soon.")
private fun onPing(request: WCRequest) {
val irnParams = IrnParams(Tags.PAIRING_PING, Ttl(thirtySeconds))
val irnParams = IrnParams(Tags.PAIRING_PING, Ttl(thirtySeconds), correlationId = request.id)
jsonRpcInteractor.respondWithSuccess(request, irnParams)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ internal class RelayerInteractorTest {
}

private fun publishJsonRpcRequests() {
val irnParamsVO = IrnParams(Tags.SESSION_PING, Ttl(300))
val irnParamsVO = IrnParams(Tags.SESSION_PING, Ttl(300), correlationId = 1234L, prompt = true)
sut.publishJsonRpcRequest(
topicVO,
irnParamsVO,
Expand Down Expand Up @@ -193,7 +193,7 @@ internal class RelayerInteractorTest {
fun `RespondWithParams publishes result with params and request id on request topic`() {
val params: ClientParams = mockk()
val result = JsonRpcResponse.JsonRpcResult(request.id, result = params)
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300))
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300), correlationId = 1234L, prompt = true)
mockRelayPublishSuccess()
sut.respondWithParams(request, params, irnParams, onSuccess = {}, onFailure = {})
verify { sut.publishJsonRpcResponse(topic = topicVO, response = result, params = irnParams, onSuccess = any(), onFailure = any()) }
Expand All @@ -202,7 +202,7 @@ internal class RelayerInteractorTest {
@Test
fun `RespondWithSuccess publishes result as true with request id on request topic`() {
val result = JsonRpcResponse.JsonRpcResult(request.id, result = true)
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300))
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300), correlationId = 1234L, prompt = true)
mockRelayPublishSuccess()
sut.respondWithSuccess(request, irnParams)
verify { sut.publishJsonRpcResponse(topic = topicVO, response = result, params = irnParams, onSuccess = any(), onFailure = any()) }
Expand All @@ -212,7 +212,7 @@ internal class RelayerInteractorTest {
fun `RespondWithError publishes result as error with request id on request topic`() {
val error = JsonRpcResponse.Error(peerError.code, peerError.message)
val result = JsonRpcResponse.JsonRpcError(request.id, error = error)
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300))
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300), correlationId = 1234L, prompt = true)
mockRelayPublishSuccess()
sut.respondWithError(request, peerError, irnParams)
verify { sut.publishJsonRpcResponse(topic = topicVO, response = result, params = irnParams, onSuccess = any(), onFailure = any()) }
Expand All @@ -221,7 +221,7 @@ internal class RelayerInteractorTest {
@Test
fun `OnFailure callback called when respondWithError encounters error`() {
mockRelayPublishFailure()
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300))
val irnParams = IrnParams(Tags.SESSION_PING, Ttl(300), correlationId = 1234L, prompt = true)
sut.respondWithError(request = request, error = peerError, irnParams = irnParams, onFailure = onFailure)
verify { onFailure(any()) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import com.reown.foundation.common.model.Topic
import com.reown.foundation.common.model.Ttl
import com.reown.foundation.common.toRelay
import com.reown.foundation.common.toRelayEvent
import com.reown.foundation.di.FoundationDITags
import com.reown.foundation.di.foundationCommonModule
import com.reown.foundation.network.data.service.RelayService
import com.reown.foundation.network.model.Relay
import com.reown.foundation.network.model.RelayDTO
import com.reown.foundation.util.Logger
import com.reown.foundation.util.scope
import com.reown.util.generateClientToServerId
import com.squareup.moshi.Moshi
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -38,6 +40,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.supervisorScope
import kotlinx.coroutines.withTimeout
import org.koin.core.KoinApplication
import org.koin.core.qualifier.named

sealed class ConnectionState {
data object Open : ConnectionState()
Expand Down Expand Up @@ -119,11 +122,12 @@ abstract class BaseRelayClient : RelayInterface {
) {
connectAndCallRelay(
onConnected = {
val (tag, ttl, prompt) = params
val publishParams = RelayDTO.Publish.Request.Params(Topic(topic), message, Ttl(ttl), tag, prompt)
val publishRequest = RelayDTO.Publish.Request(id = id ?: generateClientToServerId(), params = publishParams)
observePublishResult(publishRequest.id, onResult)
relayService.publishRequest(publishRequest)
with(params) {
val publishParams = RelayDTO.Publish.Request.Params(Topic(topic), message, Ttl(ttl), tag, prompt, correlationId, rpcMethods, chainId, txHashes, contractAddresses)
val publishRequest = RelayDTO.Publish.Request(id = id ?: generateClientToServerId(), params = publishParams)
observePublishResult(publishRequest.id, onResult)
relayService.publishRequest(publishRequest)
}
},
onFailure = { onResult(Result.failure(it)) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ object Relay {

data class ShutdownReason(val code: Int, val reason: String) : Model()

data class IrnParams(val tag: Int, val ttl: Long, val prompt: Boolean = false) : Model()
data class IrnParams(
val tag: Int,
val ttl: Long,
val correlationId: Long?,
val rpcMethods: List<String>? = null,
val chainId: String? = null,
val txHashes: List<String>? = null,
val contractAddresses: List<String>? = null,
val prompt: Boolean = false
) : Model()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ sealed class RelayDTO {
val tag: Int,
@Json(name = "prompt")
val prompt: Boolean?,
@Json(name = "correlationId")
val correlationId: Long?,
@Json(name = "rpcMethods")
val rpcMethods: List<String>?,
@Json(name = "chainId")
val chainId: String?,
@Json(name = "txHashes")
val txHashes: List<String>?,
@Json(name = "contractAddresses")
val contractAddresses: List<String>?,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class BaseRelayClientTest {

val topic = "testTopic"
val message = "testMessage"
val params = Relay.Model.IrnParams(1, 60, true)
val params = Relay.Model.IrnParams(1, 60, correlationId = 1234L, prompt = true)
val ack = RelayDTO.Publish.Result.Acknowledgement(123L, result = true)

val publishRequestSlot = slot<RelayDTO.Publish.Request>()
Expand All @@ -95,7 +95,7 @@ class BaseRelayClientTest {
fun `test publish success`() = testScope.runTest {
val topic = "testTopic"
val message = "testMessage"
val params = Relay.Model.IrnParams(1, 60, true)
val params = Relay.Model.IrnParams(1, 60, correlationId = 1234L, prompt = true)
val ack = RelayDTO.Publish.Result.Acknowledgement(123L, result = true)

coEvery { relayServiceMock.publishRequest(any()) } returns Unit
Expand All @@ -118,10 +118,10 @@ class BaseRelayClientTest {


@Test
fun `test publish error due to time out`() = testScope.runTest() {
fun `test publish error due to time out`() = testScope.runTest {
val topic = "testTopic"
val message = "testMessage"
val params = Relay.Model.IrnParams(1, 60, true)
val params = Relay.Model.IrnParams(1, 60, correlationId = 1234L, prompt = true)

coEvery { relayServiceMock.publishRequest(any()) } returns Unit
coEvery { relayServiceMock.observePublishAcknowledgement() } returns flow { delay(15000L) }
Expand Down
75 changes: 72 additions & 3 deletions foundation/src/test/kotlin/com/reown/foundation/RelayTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class RelayTest {

@ExperimentalTime
@Test
fun `One client sends unencrypted message, second one receives it`() {
fun `One client sends message, second one receives it`() {
val testState = MutableStateFlow<TestState>(TestState.Idle)
val testTopic = Random.nextBytes(32).bytesToHex()
val testMessage = "testMessage"
Expand Down Expand Up @@ -184,7 +184,76 @@ class RelayTest {
)
}

clientA.publish(testTopic, testMessage, Relay.Model.IrnParams(1114, 300)) { result ->
clientA.publish(testTopic, testMessage, Relay.Model.IrnParams(1114, 300, correlationId = 1234L, prompt = true)) { result ->
result.fold(
onSuccess = { println("ClientA publish on topic: $testTopic; message: $testMessage") },
onFailure = { error ->
println("ClientA failed to publish on topic: $testTopic. Message: ${error.message}")
}
)
}

//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 message with tvf, second one receives it`() {
val testState = MutableStateFlow<TestState>(TestState.Idle)
val testTopic = Random.nextBytes(32).bytesToHex()
val testMessage = "testMessage"
val (clientA: RelayInterface, clientB: RelayInterface) = initTwoClients()

// Listen to incoming messages/requests
clientB.subscriptionRequest.onEach {
println("ClientB subscriptionRequest: $it")
assertEquals(testMessage, it.params.subscriptionData.message)
testState.compareAndSet(expect = TestState.Idle, update = TestState.Success)
}.launchIn(testScope)

//Await connection
measureAwaitingForConnection(clientA, clientB)

//Subscribe to topic
clientB.subscribe(testTopic) { result ->
result.fold(
onSuccess = {
println("ClientB subscribe on topic: $testTopic")
},
onFailure = { error ->
println("ClientB failed to subscribe on topic: $testTopic. Message: ${error.message}")
}
)
}

clientA.publish(
testTopic,
testMessage,
Relay.Model.IrnParams(
1114,
300,
correlationId = 1234L,
prompt = true,
chainId = "test",
contractAddresses = listOf("address"),
txHashes = listOf("hash"),
rpcMethods = listOf("method")
)
) { result ->
result.fold(
onSuccess = { println("ClientA publish on topic: $testTopic; message: $testMessage") },
onFailure = { error ->
Expand Down Expand Up @@ -223,7 +292,7 @@ class RelayTest {
measureAwaitingForConnection(clientA, clientB)

//Publish message
clientA.publish(testTopic, testMessage, Relay.Model.IrnParams(1114, ttl)) { result ->
clientA.publish(testTopic, testMessage, Relay.Model.IrnParams(1114, ttl, correlationId = 1234L, prompt = true)) { result ->
result.fold(
onSuccess = {
testState.compareAndSet(expect = TestState.Idle, update = TestState.Error("ClientA publish on topic: $testTopic with ttl: $ttl"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ internal sealed class SignRpc : JsonRpcClientSync<SignParams> {
override val method: String = JsonRpcMethod.WC_SESSION_REQUEST,
@Json(name = "params")
override val params: SignParams.SessionRequestParams
) : SignRpc()
) : SignRpc() {
val rpcMethod = params.request.method
val rpcParams = params.request.params
}

@JsonClass(generateAdapter = true)
internal data class SessionDelete(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,11 @@ internal sealed class SignParams : CoreSignParams() {
val request: SessionRequestVO,
@Json(name = "chainId")
val chainId: String,
) : SignParams()
) : SignParams() {
val expiry = request.expiryTimestamp
val rpcMethod = request.method
val rpcParams = request.params
}

@JsonClass(generateAdapter = true)
internal data class EventParams(
Expand Down
Loading