mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-28 20:45:17 +00:00
feat: add batch snode calls and try to poll from all the config namespaces
This commit is contained in:
parent
f573c7deaf
commit
39bd0bba22
@ -6,6 +6,7 @@ import dagger.Provides
|
|||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
|
import org.thoughtcrime.securesms.crypto.KeyPairUtilities
|
||||||
import org.thoughtcrime.securesms.database.ConfigDatabase
|
import org.thoughtcrime.securesms.database.ConfigDatabase
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
@ -23,7 +24,10 @@ object SessionUtilModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
fun provideConfigFactory(@ApplicationContext context: Context, configDatabase: ConfigDatabase): ConfigFactory =
|
fun provideConfigFactory(@ApplicationContext context: Context, configDatabase: ConfigDatabase): ConfigFactory =
|
||||||
ConfigFactory(configDatabase) {
|
ConfigFactory(configDatabase) {
|
||||||
maybeUserEdSecretKey(context)
|
val localUserPublicKey = TextSecurePreferences.getLocalNumber(context)
|
||||||
|
val secretKey = maybeUserEdSecretKey(context)
|
||||||
|
if (localUserPublicKey == null || secretKey == null) null
|
||||||
|
else secretKey to localUserPublicKey
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package org.session.libsession.messaging.sending_receiving.pollers
|
package org.session.libsession.messaging.sending_receiving.pollers
|
||||||
|
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import nl.komponents.kovenant.Deferred
|
import nl.komponents.kovenant.Deferred
|
||||||
import nl.komponents.kovenant.Promise
|
import nl.komponents.kovenant.Promise
|
||||||
@ -14,9 +12,11 @@ import org.session.libsession.messaging.MessagingModuleConfiguration
|
|||||||
import org.session.libsession.messaging.jobs.BatchMessageReceiveJob
|
import org.session.libsession.messaging.jobs.BatchMessageReceiveJob
|
||||||
import org.session.libsession.messaging.jobs.JobQueue
|
import org.session.libsession.messaging.jobs.JobQueue
|
||||||
import org.session.libsession.messaging.jobs.MessageReceiveParameters
|
import org.session.libsession.messaging.jobs.MessageReceiveParameters
|
||||||
|
import org.session.libsession.snode.RawResponse
|
||||||
import org.session.libsession.snode.SnodeAPI
|
import org.session.libsession.snode.SnodeAPI
|
||||||
import org.session.libsession.snode.SnodeModule
|
import org.session.libsession.snode.SnodeModule
|
||||||
import org.session.libsession.utilities.ConfigFactoryProtocol
|
import org.session.libsession.utilities.ConfigFactoryProtocol
|
||||||
|
import org.session.libsignal.protos.SignalServiceProtos
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
import org.session.libsignal.utilities.Snode
|
import org.session.libsignal.utilities.Snode
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
@ -102,28 +102,42 @@ class Poller(private val configFactory: ConfigFactoryProtocol) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processUserConfig(rawMessages: List<Pair<SignalServiceProtos.Envelope, String?>>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private fun poll(snode: Snode, deferred: Deferred<Unit, Exception>): Promise<Unit, Exception> {
|
private fun poll(snode: Snode, deferred: Deferred<Unit, Exception>): Promise<Unit, Exception> {
|
||||||
if (!hasStarted) { return Promise.ofFail(PromiseCanceledException()) }
|
if (!hasStarted) { return Promise.ofFail(PromiseCanceledException()) }
|
||||||
return task {
|
return task {
|
||||||
runBlocking(Dispatchers.IO) {
|
runBlocking(Dispatchers.IO) {
|
||||||
// get user profile namespace
|
val requests = listOfNotNull(
|
||||||
val deferredUserConfig = configFactory.userConfig?.let { currentUserConfig ->
|
// get messages
|
||||||
async {
|
SnodeAPI.buildAuthenticatedRetrieveBatchRequest(snode, userPublicKey),
|
||||||
try {
|
// get user config namespace
|
||||||
val currentNetworkConfig = SnodeAPI.getRawMessages(snode, userPublicKey, namespace = currentUserConfig.configNamespace()).get()
|
configFactory.userConfig?.let { currentUserConfig ->
|
||||||
|
SnodeAPI.buildAuthenticatedRetrieveBatchRequest(
|
||||||
} catch (e: Exception) {
|
snode,
|
||||||
Log.e("Poller", "Error getting current user config from network", e)
|
userPublicKey,
|
||||||
}
|
currentUserConfig.configNamespace()
|
||||||
|
)
|
||||||
|
},
|
||||||
|
// get contact config namespace
|
||||||
|
configFactory.contacts?.let { currentContacts ->
|
||||||
|
SnodeAPI.buildAuthenticatedRetrieveBatchRequest(
|
||||||
|
snode,
|
||||||
|
userPublicKey,
|
||||||
|
currentContacts.configNamespace()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} ?: CompletableDeferred(null)
|
)
|
||||||
|
|
||||||
SnodeAPI.getRawMessages(snode, userPublicKey).bind { rawResponse ->
|
SnodeAPI.getRawBatchResponse(snode, userPublicKey, requests).bind { rawResponses ->
|
||||||
isCaughtUp = true
|
isCaughtUp = true
|
||||||
if (deferred.promise.isDone()) {
|
if (deferred.promise.isDone()) {
|
||||||
return@bind Promise.ofSuccess(Unit)
|
return@bind Promise.ofSuccess(Unit)
|
||||||
} else {
|
} else {
|
||||||
val messages = SnodeAPI.parseRawMessagesResponse(rawResponse, snode, userPublicKey)
|
val messageResponse = (rawResponses["results"] as List<*>).first() as RawResponse
|
||||||
|
val messages = SnodeAPI.parseRawMessagesResponse(messageResponse, snode, userPublicKey)
|
||||||
val parameters = messages.map { (envelope, serverHash) ->
|
val parameters = messages.map { (envelope, serverHash) ->
|
||||||
MessageReceiveParameters(envelope.toByteArray(), serverHash = serverHash)
|
MessageReceiveParameters(envelope.toByteArray(), serverHash = serverHash)
|
||||||
}
|
}
|
||||||
@ -135,7 +149,6 @@ class Poller(private val configFactory: ConfigFactoryProtocol) {
|
|||||||
poll(snode, deferred)
|
poll(snode, deferred)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,6 @@ import org.session.libsignal.utilities.ThreadUtils
|
|||||||
import org.session.libsignal.utilities.prettifiedDescription
|
import org.session.libsignal.utilities.prettifiedDescription
|
||||||
import org.session.libsignal.utilities.retryIfNeeded
|
import org.session.libsignal.utilities.retryIfNeeded
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import java.util.Date
|
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import kotlin.collections.component1
|
import kotlin.collections.component1
|
||||||
import kotlin.collections.component2
|
import kotlin.collections.component2
|
||||||
@ -93,6 +92,12 @@ object SnodeAPI {
|
|||||||
object ValidationFailed : Error("ONS name validation failed.")
|
object ValidationFailed : Error("ONS name validation failed.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Batch
|
||||||
|
data class SnodeBatchRequestInfo(
|
||||||
|
val method: String,
|
||||||
|
val params: Map<String, Any>,
|
||||||
|
) // assume signatures, pubkey and namespaces are attached in parameters if required
|
||||||
|
|
||||||
// Internal API
|
// Internal API
|
||||||
internal fun invoke(
|
internal fun invoke(
|
||||||
method: Snode.Method,
|
method: Snode.Method,
|
||||||
@ -323,7 +328,7 @@ object SnodeAPI {
|
|||||||
Log.e("Loki", "Error getting KeyPair", e)
|
Log.e("Loki", "Error getting KeyPair", e)
|
||||||
return Promise.ofFail(Error.NoKeyPair)
|
return Promise.ofFail(Error.NoKeyPair)
|
||||||
}
|
}
|
||||||
val timestamp = Date().time + SnodeAPI.clockOffset
|
val timestamp = System.currentTimeMillis() + clockOffset
|
||||||
val ed25519PublicKey = userED25519KeyPair.publicKey.asHexString
|
val ed25519PublicKey = userED25519KeyPair.publicKey.asHexString
|
||||||
val signature = ByteArray(Sign.BYTES)
|
val signature = ByteArray(Sign.BYTES)
|
||||||
val verificationData =
|
val verificationData =
|
||||||
@ -351,7 +356,48 @@ object SnodeAPI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make the request
|
// Make the request
|
||||||
return invoke(Snode.Method.GetMessages, snode, parameters, publicKey)
|
return invoke(Snode.Method.Retrieve, snode, parameters, publicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildAuthenticatedRetrieveBatchRequest(snode: Snode, publicKey: String, namespace: Int = 0): SnodeBatchRequestInfo? {
|
||||||
|
val lastHashValue = database.getLastMessageHashValue(snode, publicKey, namespace) ?: ""
|
||||||
|
val params = mutableMapOf<String, Any>(
|
||||||
|
"pubKey" to publicKey,
|
||||||
|
"last_hash" to lastHashValue,
|
||||||
|
)
|
||||||
|
val userEd25519KeyPair = try {
|
||||||
|
MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString
|
||||||
|
val timestamp = System.currentTimeMillis() + clockOffset
|
||||||
|
val signature = ByteArray(Sign.BYTES)
|
||||||
|
val verificationData = "retrieve$namespace$timestamp".toByteArray()
|
||||||
|
try {
|
||||||
|
sodium.cryptoSignDetached(
|
||||||
|
signature,
|
||||||
|
verificationData,
|
||||||
|
verificationData.size.toLong(),
|
||||||
|
userEd25519KeyPair.secretKey.asBytes
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
params["timestamp"] = timestamp
|
||||||
|
// params["pubkey_ed25519"] = ed25519PublicKey
|
||||||
|
params["signature"] = Base64.encodeBytes(signature)
|
||||||
|
return SnodeBatchRequestInfo(
|
||||||
|
Snode.Method.Retrieve.rawValue,
|
||||||
|
params
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRawBatchResponse(snode: Snode, publicKey: String, requests: List<SnodeBatchRequestInfo>): RawResponsePromise {
|
||||||
|
val parameters = mutableMapOf<String, Any>(
|
||||||
|
"requests" to requests
|
||||||
|
)
|
||||||
|
return invoke(Snode.Method.Batch, snode, parameters, publicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMessages(publicKey: String): MessageListPromise {
|
fun getMessages(publicKey: String): MessageListPromise {
|
||||||
|
@ -5,12 +5,13 @@ class Snode(val address: String, val port: Int, val publicKeySet: KeySet?) {
|
|||||||
|
|
||||||
public enum class Method(val rawValue: String) {
|
public enum class Method(val rawValue: String) {
|
||||||
GetSwarm("get_snodes_for_pubkey"),
|
GetSwarm("get_snodes_for_pubkey"),
|
||||||
GetMessages("retrieve"),
|
Retrieve("retrieve"),
|
||||||
SendMessage("store"),
|
SendMessage("store"),
|
||||||
DeleteMessage("delete"),
|
DeleteMessage("delete"),
|
||||||
OxenDaemonRPCCall("oxend_request"),
|
OxenDaemonRPCCall("oxend_request"),
|
||||||
Info("info"),
|
Info("info"),
|
||||||
DeleteAll("delete_all")
|
DeleteAll("delete_all"),
|
||||||
|
Batch("batch")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class KeySet(val ed25519Key: String, val x25519Key: String)
|
data class KeySet(val ed25519Key: String, val x25519Key: String)
|
||||||
|
Loading…
Reference in New Issue
Block a user