Skip to content

Commit

Permalink
bug fixed
Browse files Browse the repository at this point in the history
  • Loading branch information
polstianka committed Oct 24, 2024
1 parent e664135 commit bcd8b77
Show file tree
Hide file tree
Showing 13 changed files with 55 additions and 72 deletions.
27 changes: 5 additions & 22 deletions apps/wallet/api/src/main/java/com/tonapps/wallet/api/API.kt
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,10 @@ class API(
}
}

fun realtime(accountId: String, testnet: Boolean): Flow<SSEvent> {
fun realtime(accountId: String, testnet: Boolean, onFailure: ((Throwable) -> Unit)?): Flow<SSEvent> {
val endpoint = if (testnet) config.tonapiSSETestnetEndpoint else config.tonapiSSEEndpoint
val url = "$endpoint/sse/traces?account=$accountId"
return tonAPIHttpClient.sse(url)
return tonAPIHttpClient.sse(url, onFailure = onFailure)
}

fun get(url: String): String {
Expand Down Expand Up @@ -456,34 +456,17 @@ class API(
testnet: Boolean
) = getPublicKey(accountId, testnet) ?: EmptyPrivateKeyEd25519.publicKey()

fun accountEvents(accountId: String, testnet: Boolean): Flow<SSEvent> {
val endpoint = if (testnet) {
config.tonapiTestnetHost
} else {
config.tonapiMainnetHost
}
// val mempool = okHttpClient.sse("$endpoint/v2/sse/mempool?accounts=${accountId}")
val tx = tonAPIHttpClient.sse("$endpoint/v2/sse/accounts/transactions?accounts=${accountId}")
// return merge(mempool, tx)
return tx
}

fun newRealtime(accountId: String, testnet: Boolean): Flow<SSEvent> {
val host = if (testnet) "rt-testnet.tonapi.io" else "rt.tonapi.io"
val url = "https://${host}/sse/transactions?account=$accountId"
return tonAPIHttpClient.sse(url)
}

fun tonconnectEvents(
publicKeys: List<String>,
lastEventId: Long? = null
lastEventId: Long? = null,
onFailure: ((Throwable) -> Unit)?
): Flow<SSEvent> {
if (publicKeys.isEmpty()) {
return emptyFlow()
}
val value = publicKeys.joinToString(",")
val url = "${BRIDGE_URL}/events?client_id=$value"
return tonAPIHttpClient.sse(url, lastEventId).filter { it.type == "message" }
return tonAPIHttpClient.sse(url, lastEventId, onFailure).filter { it.type == "message" }
}

fun tonconnectPayload(): String? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
package com.tonapps.wallet.api

import android.net.Uri

sealed class APIException(message: String?, cause: Throwable?): Throwable(message, cause) {

class Emulation(boc: String, cause: Throwable? = null): APIException(
message = "Boc: $boc",
private companion object {

private fun buildMessage(vararg lines: String?): String {
return lines.filterNotNull().joinToString(separator = ";\n")
}
}

class Emulation(boc: String, sourceUri: Uri? = null, cause: Throwable? = null): APIException(
message = buildMessage("Source URI: $sourceUri", "Boc: $boc"),
cause = cause
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tonapps.wallet.data.core.entity

import android.net.Uri
import android.os.Parcelable
import android.util.Log
import com.tonapps.blockchain.ton.TonNetwork
Expand All @@ -15,6 +16,7 @@ import kotlin.time.Duration.Companion.seconds

@Parcelize
data class SignRequestEntity(
val appUri: Uri,
private val fromValue: String?,
private val sourceValue: String?,
val validUntil: Long,
Expand All @@ -33,17 +35,18 @@ data class SignRequestEntity(
}
}

constructor(json: JSONObject) : this(
constructor(json: JSONObject, appUri: Uri) : this(
appUri = appUri,
fromValue = json.optString("from"),
sourceValue = json.optString("source"),
validUntil = parseValidUnit(json),
messages = parseMessages(json.getJSONArray("messages")),
network = parseNetwork(json.opt("network"))
)

constructor(value: String) : this(JSONObject(value))
constructor(value: String, appUri: Uri) : this(JSONObject(value), appUri)

constructor(value: Any) : this(value.toString())
constructor(value: Any, appUri: Uri) : this(value.toString(), appUri)

class Builder {
private var from: AddrStd? = null
Expand All @@ -61,23 +64,24 @@ data class SignRequestEntity(

fun addMessage(message: RawMessageEntity) = apply { messages.add(message) }

fun build(): SignRequestEntity {
fun build(appUri: Uri): SignRequestEntity {
return SignRequestEntity(
fromValue = from?.toAccountId(),
sourceValue = null,
validUntil = validUntil ?: 0,
messages = messages.toList(),
network = network
network = network,
appUri = appUri
)
}
}

companion object {

fun parse(array: JSONArray): List<SignRequestEntity> {
fun parse(array: JSONArray, appUri: Uri): List<SignRequestEntity> {
val requests = mutableListOf<SignRequestEntity>()
for (i in 0 until array.length()) {
requests.add(SignRequestEntity(array.get(i)))
requests.add(SignRequestEntity(array.get(i), appUri))
}
return requests.toList()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,6 @@ data class AppConnectEntity(
localPrivateKey: ByteArray,
body: ByteArray
): ByteArray {
Log.d("AppConnectEntityLog", "encryptMessage: remotePublicKey = ${remotePublicKey.joinToString()}")
Log.d("AppConnectEntityLog", "encryptMessage: localPrivateKey = ${localPrivateKey.joinToString()}")
Log.d("AppConnectEntityLog", "encryptMessage: body = ${body.toString(Charsets.UTF_8)}")
val nonce = CryptoBox.nonce()
val cipher = ByteArray(body.size + Sodium.cryptoBoxMacBytes())
Sodium.cryptoBoxEasy(cipher, body, body.size, nonce, remotePublicKey, localPrivateKey)
Expand All @@ -48,11 +45,8 @@ data class AppConnectEntity(
body: ByteArray
): ByteArray {
val nonce = body.sliceArray(0 until Sodium.cryptoBoxNonceBytes())
Log.d("AppConnectEntityLog", "nonce = ${nonce.joinToString()}")
val cipher = body.sliceArray(Sodium.cryptoBoxNonceBytes() until body.size)
Log.d("AppConnectEntityLog", "cipher = ${cipher.joinToString()}")
val message = ByteArray(cipher.size - Sodium.cryptoBoxMacBytes())
Log.d("AppConnectEntityLog", "message = ${message.joinToString()}")
Sodium.cryptoBoxOpenEasy(message, cipher, cipher.size, nonce, remotePublicKey, localPrivateKey)
return message
}
Expand All @@ -66,17 +60,9 @@ data class AppConnectEntity(
return decryptMessage(clientId.hex(), keyPair.privateKey, body)
}

fun decryptEventMessage(message: String): JSONObject? {
try {
Log.d("AppConnectEntityLog", "decryptEventMessage: message = \"$message\"")
val bytes = message.decodeBase64()
Log.d("AppConnectEntityLog", "decryptEventMessage: bytes = ${bytes.joinToString()}")
val decrypted = decryptMessage(bytes)
Log.d("AppConnectEntityLog", "decryptEventMessage: decrypted = ${decrypted.joinToString()}")
return JSONObject(decrypted.toString(Charsets.UTF_8))
} catch (e: Throwable) {
Log.e("AppConnectEntityLog", "decryptEventMessage: ", e)
return null
}
fun decryptEventMessage(message: String): JSONObject {
val bytes = message.decodeBase64()
val decrypted = decryptMessage(bytes)
return JSONObject(decrypted.toString(Charsets.UTF_8))
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.tonapps.tonkeeper.manager.tonconnect.bridge

import android.util.Log
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tonapps.base64.encodeBase64
import com.tonapps.extensions.optStringCompat
Expand Down Expand Up @@ -84,13 +83,13 @@ internal class Bridge(private val api: API) {
lastEventId: Long,
): Flow<BridgeEvent> {
val publicKeys = connections.map { it.publicKeyHex }
return api.tonconnectEvents(publicKeys, lastEventId)
return api.tonconnectEvents(publicKeys, lastEventId, onFailure = { FirebaseCrashlytics.getInstance().recordException(it) })
.mapNotNull { event ->
val id = event.id?.toLongOrNull() ?: return@mapNotNull null
val from = event.json.optStringCompat("from") ?: return@mapNotNull null
val message = event.json.optStringCompat("message") ?: return@mapNotNull null
val connection = connections.find { it.clientId == from } ?: return@mapNotNull null
val json = connection.decryptEventMessage(message) ?: return@mapNotNull null
val id = event.id?.toLongOrNull() ?: throw IllegalArgumentException("Event \"id\" is missing")
val from = event.json.optStringCompat("from") ?: throw IllegalArgumentException("Event \"from\" is missing")
val message = event.json.optStringCompat("message") ?: throw IllegalArgumentException("Event \"message\" is missing")
val connection = connections.find { it.clientId == from } ?: throw IllegalArgumentException("Connection not found")
val json = connection.decryptEventMessage(message)
val decryptedMessage = BridgeEvent.Message(json)
BridgeEvent(
eventId = id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tonapps.tonkeeper.manager.tx

import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tonapps.blockchain.ton.extensions.base64
import com.tonapps.extensions.MutableEffectFlow
import com.tonapps.extensions.join
Expand Down Expand Up @@ -70,7 +71,8 @@ class TransactionManager(

private fun realtime(wallet: WalletEntity) = api.realtime(
accountId = wallet.accountId,
testnet = wallet.testnet
testnet = wallet.testnet,
onFailure = { FirebaseCrashlytics.getInstance().recordException(it) }
).map { it.data }.map { getTransaction(wallet, it) }

private suspend fun getTransaction(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.tonapps.tonkeeper.ui.screen.battery.recharge

import android.app.Application
import android.net.Uri
import androidx.lifecycle.viewModelScope
import com.tonapps.blockchain.ton.TonNetwork
import com.tonapps.blockchain.ton.TonTransferHelper
Expand Down Expand Up @@ -356,7 +357,7 @@ class BatteryRechargeViewModel(
payloadValue = payload.base64()
))
.setNetwork(network)
.build()
.build(Uri.parse("https://battery.tonkeeper.com/"))

_eventFlow.tryEmit(BatteryRechargeEvent.Sign(request, forceRelayer))
} else {
Expand Down Expand Up @@ -386,7 +387,7 @@ class BatteryRechargeViewModel(
payloadValue = jettonPayload.base64()
))
.setNetwork(network)
.build()
.build(Uri.parse("https://battery.tonkeeper.com/"))

_eventFlow.tryEmit(BatteryRechargeEvent.Sign(request, forceRelayer))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ class DAppScreen(wallet: WalletEntity): WalletContextScreen(R.layout.fragment_da
if (message.method != BridgeMethod.SEND_TRANSACTION) {
return JsonBuilder.responseError(id, BridgeError.METHOD_NOT_SUPPORTED)
}
val signRequests = message.params.map { SignRequestEntity(it) }
val signRequests = message.params.map { SignRequestEntity(it, args.url) }
if (signRequests.size != 1) {
return JsonBuilder.responseError(id, BridgeError.BAD_REQUEST)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ class RootActivity: BaseWalletActivity() {
payloadValue = bin.base64()
))
.setTestnet(wallet.testnet)
.build()
.build(Uri.parse("https://tonkeeper.com/"))

val screen = SendTransactionScreen.newInstance(wallet, request)
add(screen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ class RootViewModel(
private suspend fun signTransaction(tx: RootSignTransaction) {
val eventId = tx.id
try {
val signRequests = tx.params.map { SignRequestEntity(it) }
val signRequests = tx.params.map { SignRequestEntity(it, tx.connection.appUrl) }
if (signRequests.isEmpty()) {
throw IllegalArgumentException("Empty sign requests")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,9 @@ class SendTransactionViewModel(
)
}
} catch (e: Throwable) {

FirebaseCrashlytics.getInstance().recordException(APIException.Emulation(
boc = message.createSignedBody(EmptyPrivateKeyEd25519.invoke(), internalMessage).base64(),
sourceUri = request.appUri,
cause = e
))

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.tonapps.tonkeeper.ui.screen.swap

import android.net.Uri
import com.tonapps.wallet.data.core.entity.SignRequestEntity
import org.json.JSONArray
import uikit.widget.webview.bridge.JsBridge
Expand All @@ -21,7 +22,7 @@ class StonfiBridge2(
close()
return null
} else if (name == "sendTransaction" && args.length() == 1) {
val request = SignRequestEntity(args.getJSONObject(0))
val request = SignRequestEntity(args.getJSONObject(0), Uri.parse("https://ston.fi/"))
return sendTransaction(request)
}
throw IllegalArgumentException("Unknown function: $name")
Expand Down
14 changes: 6 additions & 8 deletions lib/network/src/main/java/com/tonapps/network/OkHttpClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ fun OkHttpClient.sseFactory() = EventSources.createFactory(this)

fun OkHttpClient.sse(
url: String,
lastEventId: Long? = null
lastEventId: Long? = null,
onFailure: ((Throwable) -> Unit)?
): Flow<SSEvent> = callbackFlow {
val listener = object : EventSourceListener() {
override fun onEvent(eventSource: EventSource, id: String?, type: String?, data: String) {
Expand All @@ -120,11 +121,7 @@ fun OkHttpClient.sse(
}

override fun onClosed(eventSource: EventSource) {
this@callbackFlow.close()
}

override fun onOpen(eventSource: EventSource, response: Response) {
super.onOpen(eventSource, response)
this@callbackFlow.close(Throwable("EventSource closed"))
}
}
val builder = requestBuilder(url)
Expand All @@ -138,7 +135,8 @@ fun OkHttpClient.sse(
val request = builder.build()
val events = sseFactory().newEventSource(request, listener)
awaitClose { events.cancel() }
}.cancellable().retry { _ ->
delay(1000)
}.retry { cause ->
onFailure?.invoke(cause)
delay(3000)
true
}.cancellable()

0 comments on commit bcd8b77

Please sign in to comment.