refactor: fixing compile issues and namespace references throughout. figuring out config sig and base issues

This commit is contained in:
0x330a
2023-09-18 12:24:06 +10:00
parent b5b248a6ec
commit 43e72550f9
15 changed files with 86 additions and 89 deletions

View File

@@ -2,7 +2,7 @@ package org.session.libsession.database
import android.content.Context
import android.net.Uri
import network.loki.messenger.libsession_util.ConfigBase
import network.loki.messenger.libsession_util.Config
import org.session.libsession.messaging.BlindedIdMapping
import org.session.libsession.messaging.calls.CallMessageType
import org.session.libsession.messaging.contacts.Contact
@@ -237,7 +237,7 @@ interface StorageProtocol {
fun blockedContacts(): List<Recipient>
// Shared configs
fun notifyConfigUpdates(forConfigObject: ConfigBase)
fun notifyConfigUpdates(forConfigObject: Config)
fun conversationInConfig(publicKey: String?, groupPublicKey: String?, openGroupId: String?, visibleOnly: Boolean): Boolean
fun canPerformConfigChange(variant: String, publicKey: String, changeTimestampMs: Long): Boolean
fun isCheckingCommunityRequests(): Boolean

View File

@@ -1,20 +1,20 @@
package org.session.libsession.messaging.jobs
import network.loki.messenger.libsession_util.Config
import network.loki.messenger.libsession_util.ConfigBase
import network.loki.messenger.libsession_util.ConfigBase.Companion.protoKindFor
import nl.komponents.kovenant.functional.bind
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.Destination
import org.session.libsession.messaging.messages.control.SharedConfigurationMessage
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.utilities.Data
import org.session.libsession.snode.RawResponse
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.snode.SnodeAPI.SnodeBatchRequestInfo
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.ConfigFactoryProtocol
import org.session.libsignal.utilities.Log
import java.util.concurrent.atomic.AtomicBoolean
typealias ConfigPair<T> = List<Pair<T, ConfigBase>>
class InvalidDestination: Exception("Trying to push configs somewhere other than our swarm or a closed group")
class InvalidContactDestination: Exception("Trying to push to non-user config swarm")
// only contact (self) and closed group destinations will be supported
data class ConfigurationSyncJob(val destination: Destination): Job {
@@ -26,61 +26,47 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
val shouldRunAgain = AtomicBoolean(false)
data class SyncInformation(val configs: ConfigPair<SnodeBatchRequestInfo>, val toDelete: List<String>)
data class ConfigMessageInformation(val batch: SnodeBatchRequestInfo, val config: Config, val seqNo: Long?) // seqNo will be null for keys type
data class SyncInformation(val configs: List<ConfigMessageInformation>, val toDelete: List<String>)
private fun destinationConfigs(delegate: JobDelegate, configFactoryProtocol: ConfigFactoryProtocol): SyncInformation {
TODO()
}
override suspend fun execute(dispatcherName: String) {
val storage = MessagingModuleConfiguration.shared.storage
val forcedConfig = TextSecurePreferences.hasForcedNewConfig(MessagingModuleConfiguration.shared.context)
val currentTime = SnodeAPI.nowWithOffset
val userPublicKey = storage.getUserPublicKey()
val delegate = delegate ?: return Log.e("ConfigurationSyncJob", "No Delegate")
if ((destination is Destination.Contact && destination.publicKey != userPublicKey)) {
Log.w(TAG, "No need to run config sync job, TODO")
return delegate.handleJobSucceeded(this, dispatcherName)
if (destination is Destination.Contact && destination.publicKey != userPublicKey) {
return delegate.handleJobFailedPermanently(this, dispatcherName, InvalidContactDestination())
} else if (destination !is Destination.ClosedGroup) {
return delegate.handleJobFailedPermanently(this, dispatcherName, InvalidDestination())
}
// configFactory singleton instance will come in handy for modifying hashes and fetching configs for namespace etc
val configFactory = MessagingModuleConfiguration.shared.configFactory
// **** start user ****
// get latest states, filter out configs that don't need push
val configsRequiringPush = configFactory.getUserConfigs().filter { config -> config.needsPush() }
// don't run anything if we don't need to push anything
if (configsRequiringPush.isEmpty()) return delegate.handleJobSucceeded(this, dispatcherName)
// need to get the current hashes before we call `push()`
val toDeleteHashes = mutableListOf<String>()
// **** end user ****
// allow null results here so the list index matches configsRequiringPush
val sentTimestamp: Long = SnodeAPI.nowWithOffset
val batchObjects: List<Pair<SharedConfigurationMessage, SnodeBatchRequestInfo>?> = configsRequiringPush.map { config ->
val (data, seqNo, obsoleteHashes) = config.push()
toDeleteHashes += obsoleteHashes
SharedConfigurationMessage(config.protoKindFor(), data, seqNo) to config
}.map { (message, config) ->
// return a list of batch request objects
val snodeMessage = MessageSender.buildWrappedMessageToSnode(destination, message, true)
val authenticated = SnodeAPI.buildAuthenticatedStoreBatchInfo(
config.configNamespace(),
snodeMessage
) ?: return@map null // this entry will be null otherwise
message to authenticated // to keep track of seqNo for calling confirmPushed later
}
val (batchObjects, toDeleteHashes) = destinationConfigs(delegate, configFactory)
val toDeleteRequest = toDeleteHashes.let { toDeleteFromAllNamespaces ->
if (toDeleteFromAllNamespaces.isEmpty()) null
else SnodeAPI.buildAuthenticatedDeleteBatchInfo(destination.destinationPublicKey(), toDeleteFromAllNamespaces)
}
if (batchObjects.any { it == null }) {
// stop running here, something like a signing error occurred
return delegate.handleJobFailedPermanently(this, dispatcherName, NullPointerException("One or more requests had a null batch request info"))
}
val allRequests = mutableListOf<SnodeBatchRequestInfo>()
allRequests += batchObjects.requireNoNulls().map { (_, request) -> request }
allRequests += batchObjects.map { (request) -> request }
// add in the deletion if we have any hashes
if (toDeleteRequest != null) {
allRequests += toDeleteRequest
@@ -118,23 +104,26 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
} ?: emptySet()
// at this point responseList index should line up with configsRequiringPush index
configsRequiringPush.forEachIndexed { index, config ->
val (toPushMessage, _) = batchObjects[index]!!
batchObjects.forEachIndexed { index, (message, config, seqNo) ->
val response = responseList[index]
val responseBody = response["body"] as? RawResponse
val insertHash = responseBody?.get("hash") as? String ?: run {
Log.w(TAG, "No hash returned for the configuration in namespace ${config.configNamespace()}")
Log.w(TAG, "No hash returned for the configuration in namespace ${config.namespace()}")
return@forEachIndexed
}
Log.d(TAG, "Hash ${insertHash.take(4)} returned from store request for new config")
// confirm pushed seqno
val thisSeqNo = toPushMessage.seqNo
config.confirmPushed(thisSeqNo, insertHash)
if (config is ConfigBase) {
seqNo?.let {
config.confirmPushed(it, insertHash)
}
}
Log.d(TAG, "Successfully removed the deleted hashes from ${config.javaClass.simpleName}")
// dump and write config after successful
if (config.needsDump()) { // usually this will be true?
configFactory.persist(config, toPushMessage.sentTimestamp ?: sentTimestamp)
if (config is ConfigBase && config.needsDump()) { // usually this will be true?
configFactory.persist(config, (message.params["timestamp"] as String).toLong())
}
}
} catch (e: Exception) {
@@ -152,10 +141,6 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair()
}
private fun syncGroupConfigs(delegate: JobDelegate) {
}
fun Destination.destinationPublicKey(): String = when (this) {
is Destination.Contact -> publicKey
is Destination.ClosedGroup -> publicKey

View File

@@ -197,10 +197,10 @@ object MessageSender {
val forkInfo = SnodeAPI.forkInfo
val namespaces: List<Int> = when {
destination is Destination.LegacyClosedGroup
&& forkInfo.defaultRequiresAuth() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP)
&& forkInfo.defaultRequiresAuth() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP())
destination is Destination.LegacyClosedGroup
&& forkInfo.hasNamespaces() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP, Namespace.DEFAULT)
else -> listOf(Namespace.DEFAULT)
&& forkInfo.hasNamespaces() -> listOf(Namespace.UNAUTHENTICATED_CLOSED_GROUP(), Namespace.DEFAULT())
else -> listOf(Namespace.DEFAULT())
}
namespaces.map { namespace ->
if (destination is Destination.ClosedGroup) {

View File

@@ -104,28 +104,28 @@ class ClosedGroupPoller(private val executor: CoroutineScope,
val messagePoll = SnodeAPI.buildAuthenticatedRetrieveBatchRequest(
snode,
closedGroupSessionId.hexString(),
Namespace.DEFAULT,
Namespace.DEFAULT(),
maxSize = null,
group.signingKey()
) ?: return null
val infoPoll = SnodeAPI.buildAuthenticatedRetrieveBatchRequest(
snode,
closedGroupSessionId.hexString(),
info.configNamespace(),
info.namespace(),
maxSize = null,
group.signingKey()
) ?: return null
val membersPoll = SnodeAPI.buildAuthenticatedRetrieveBatchRequest(
snode,
closedGroupSessionId.hexString(),
members.configNamespace(),
members.namespace(),
maxSize = null,
group.signingKey()
) ?: return null
val keysPoll = SnodeAPI.buildAuthenticatedRetrieveBatchRequest(
snode,
closedGroupSessionId.hexString(),
GroupKeysConfig.storageNamespace(),
keys.namespace(),
maxSize = null,
group.signingKey()
) ?: return null

View File

@@ -108,13 +108,13 @@ class LegacyClosedGroupPollerV2 {
if (!isPolling(groupPublicKey)) { throw PollingCanceledException() }
val currentForkInfo = SnodeAPI.forkInfo
when {
currentForkInfo.defaultRequiresAuth() -> SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.UNAUTHENTICATED_CLOSED_GROUP)
.map { SnodeAPI.parseRawMessagesResponse(it, snode, groupPublicKey, Namespace.UNAUTHENTICATED_CLOSED_GROUP) }
currentForkInfo.defaultRequiresAuth() -> SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.UNAUTHENTICATED_CLOSED_GROUP())
.map { SnodeAPI.parseRawMessagesResponse(it, snode, groupPublicKey, Namespace.UNAUTHENTICATED_CLOSED_GROUP()) }
currentForkInfo.hasNamespaces() -> task {
val unAuthed = SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.UNAUTHENTICATED_CLOSED_GROUP)
.map { SnodeAPI.parseRawMessagesResponse(it, snode, groupPublicKey, Namespace.UNAUTHENTICATED_CLOSED_GROUP) }
val default = SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.DEFAULT)
.map { SnodeAPI.parseRawMessagesResponse(it, snode, groupPublicKey, Namespace.DEFAULT) }
val unAuthed = SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.UNAUTHENTICATED_CLOSED_GROUP())
.map { SnodeAPI.parseRawMessagesResponse(it, snode, groupPublicKey, Namespace.UNAUTHENTICATED_CLOSED_GROUP()) }
val default = SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.DEFAULT())
.map { SnodeAPI.parseRawMessagesResponse(it, snode, groupPublicKey, Namespace.DEFAULT()) }
val unAuthedResult = unAuthed.get()
val defaultResult = default.get()
val format = DateFormat.getTimeInstance()
@@ -123,7 +123,7 @@ class LegacyClosedGroupPollerV2 {
}
unAuthedResult + defaultResult
}
else -> SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.DEFAULT)
else -> SnodeAPI.getRawMessages(snode, groupPublicKey, requiresAuth = false, namespace = Namespace.DEFAULT())
.map { SnodeAPI.parseRawMessagesResponse(it, snode, groupPublicKey) }
}
}

View File

@@ -180,7 +180,7 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti
hashesToExtend += config.currentHashes()
SnodeAPI.buildAuthenticatedRetrieveBatchRequest(
snode, userPublicKey,
config.configNamespace(),
config.namespace(),
maxSize = -8
)
}.forEach { request ->
@@ -211,10 +211,10 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti
// in case we had null configs, the array won't be fully populated
// index of the sparse array key iterator should be the request index, with the key being the namespace
listOfNotNull(
configFactory.user?.configNamespace(),
configFactory.contacts?.configNamespace(),
configFactory.userGroups?.configNamespace(),
configFactory.convoVolatile?.configNamespace()
configFactory.user?.namespace(),
configFactory.contacts?.namespace(),
configFactory.userGroups?.namespace(),
configFactory.convoVolatile?.namespace()
).map {
it to requestSparseArray.indexOfKey(it)
}.filter { (_, i) -> i >= 0 }.forEach { (key, requestIndex) ->
@@ -228,7 +228,7 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti
Log.e("Loki", "Batch sub-request didn't contain a body")
return@forEach
}
if (key == Namespace.DEFAULT) {
if (key == Namespace.DEFAULT()) {
return@forEach // continue, skip default namespace
} else {
when (ConfigBase.kindFor(key)) {
@@ -242,7 +242,7 @@ class Poller(private val configFactory: ConfigFactoryProtocol, debounceTimer: Ti
}
// the first response will be the personal messages (we want these to be processed after config messages)
val personalResponseIndex = requestSparseArray.indexOfKey(Namespace.DEFAULT)
val personalResponseIndex = requestSparseArray.indexOfKey(Namespace.DEFAULT())
if (personalResponseIndex >= 0) {
responseList.getOrNull(personalResponseIndex)?.let { rawResponse ->
if (rawResponse["code"] as? Int != 200) {

View File

@@ -793,14 +793,14 @@ object SnodeAPI {
retryIfNeeded(maxRetryCount) {
getNetworkTime(snode).bind { (_, timestamp) ->
val signature = ByteArray(Sign.BYTES)
val verificationData = (Snode.Method.DeleteAll.rawValue + Namespace.ALL + timestamp.toString()).toByteArray()
val verificationData = (Snode.Method.DeleteAll.rawValue + Namespace.ALL() + timestamp.toString()).toByteArray()
sodium.cryptoSignDetached(signature, verificationData, verificationData.size.toLong(), userED25519KeyPair.secretKey.asBytes)
val deleteMessageParams = mapOf(
"pubkey" to userPublicKey,
"pubkey_ed25519" to userED25519KeyPair.publicKey.asHexString,
"timestamp" to timestamp,
"signature" to Base64.encodeBytes(signature),
"namespace" to Namespace.ALL,
"namespace" to Namespace.ALL(),
)
invoke(Snode.Method.DeleteAll, snode, deleteMessageParams, userPublicKey).map {
rawResponse -> parseDeletions(userPublicKey, timestamp, rawResponse)

View File

@@ -1,5 +1,6 @@
package org.session.libsession.utilities
import network.loki.messenger.libsession_util.Config
import network.loki.messenger.libsession_util.ConfigBase
import network.loki.messenger.libsession_util.Contacts
import network.loki.messenger.libsession_util.ConversationVolatileConfig
@@ -21,7 +22,7 @@ interface ConfigFactoryProtocol {
fun getGroupKeysConfig(groupSessionId: SessionId): GroupKeysConfig?
fun getUserConfigs(): List<ConfigBase>
fun persist(forConfigObject: ConfigBase, timestamp: Long)
fun persist(forConfigObject: Config, timestamp: Long)
fun conversationInConfig(publicKey: String?, groupPublicKey: String?, openGroupId: String?, visibleOnly: Boolean): Boolean
fun canPerformChange(variant: String, publicKey: String, changeTimestampMs: Long): Boolean
@@ -33,5 +34,5 @@ interface ConfigFactoryProtocol {
}
interface ConfigFactoryUpdateListener {
fun notifyUpdates(forConfigObject: ConfigBase)
fun notifyUpdates(forConfigObject: Config)
}