diff --git a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java index a5c5697b3d..1048853f0b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java +++ b/app/src/main/java/org/thoughtcrime/securesms/ApplicationContext.java @@ -115,8 +115,6 @@ import org.session.libsignal.service.loki.protocol.mentions.MentionsManager; import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol; import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol; import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocolDelegate; -import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink; -import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol; import java.io.File; import java.io.FileInputStream; @@ -204,18 +202,11 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc SnodeAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); MentionsManager.Companion.configureIfNeeded(userPublicKey, threadDB, userDB); SessionMetaProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); - SyncMessagesProtocol.Companion.configureIfNeeded(apiDB, userPublicKey); } - org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.Companion.configureIfNeeded(apiDB); SessionManagementProtocol.Companion.configureIfNeeded(sessionResetImpl, sskDatabase, this); setUpP2PAPIIfNeeded(); PushNotificationAPI.Companion.configureIfNeeded(BuildConfig.DEBUG); - if (setUpStorageAPIIfNeeded()) { - if (userPublicKey != null) { - Set deviceLinks = DatabaseFactory.getLokiAPIDatabase(this).getDeviceLinks(userPublicKey); - FileServerAPI.shared.setDeviceLinks(deviceLinks); - } - } + setUpStorageAPIIfNeeded(); resubmitProfilePictureIfNeeded(); publicChatManager = new PublicChatManager(this); updateOpenGroupProfilePicturesIfNeeded(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java b/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java index 825f9f0392..2b4b820ce8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/TypingStatusSender.java @@ -12,7 +12,6 @@ import org.thoughtcrime.securesms.jobs.TypingSendJob; import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.session.libsession.messaging.threads.recipients.Recipient; import org.session.libsession.utilities.Util; -import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; import java.util.HashMap; import java.util.Map; @@ -87,12 +86,8 @@ public class TypingStatusSender { if (recipient != null && !SessionMetaProtocol.shouldSendTypingIndicator(recipient.getAddress())) { return; } // Loki - Take into account multi device if (recipient == null) { return; } - Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(recipient.getAddress().serialize()); - for (String device : linkedDevices) { - Recipient deviceAsRecipient = Recipient.from(context, Address.Companion.fromSerialized(device), false); - long deviceThreadID = threadDatabase.getOrCreateThreadIdFor(deviceAsRecipient); - ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(deviceThreadID, typingStarted)); - } + long threadID = threadDatabase.getOrCreateThreadIdFor(recipient); + ApplicationContext.getInstance(context).getJobManager().add(new TypingSendJob(threadID, typingStarted)); } private class StartRunnable implements Runnable { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 010eef08a1..6796031ddf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -92,7 +92,6 @@ import org.session.libsignal.service.loki.api.opengroups.PublicChat; import org.session.libsignal.service.loki.protocol.mentions.Mention; import org.session.libsignal.service.loki.protocol.mentions.MentionsManager; import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol; -import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; import org.session.libsignal.service.loki.utilities.HexEncodingKt; import org.session.libsignal.service.loki.utilities.PublicKeyValidation; import org.thoughtcrime.securesms.ApplicationContext; @@ -2620,10 +2619,9 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity // region Loki private void updateTitleTextView(Recipient recipient) { String userPublicKey = TextSecurePreferences.getLocalNumber(this); - Set allUserDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); if (recipient == null) { titleTextView.setText("Compose"); - } else if (allUserDevices.contains(recipient.getAddress().toString().toLowerCase())) { + } else if (recipient.getAddress().toString().toLowerCase() == userPublicKey) { titleTextView.setText("Note to Self"); } else { boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java index cda9f4116e..0a05f7bb2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupMessageProcessor.java @@ -36,7 +36,6 @@ import org.session.libsignal.service.api.messages.SignalServiceContent; import org.session.libsignal.service.api.messages.SignalServiceDataMessage; import org.session.libsignal.service.api.messages.SignalServiceGroup; import org.session.libsignal.service.api.messages.SignalServiceGroup.Type; -import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; import java.util.Collections; import java.util.HashSet; @@ -141,9 +140,7 @@ public class GroupMessageProcessor { if (group.getGroupType() == SignalServiceGroup.GroupType.SIGNAL) { // Loki - Only update the group if the group admin sent the message - String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); - if (masterDevice == null) { masterDevice = content.getSender(); } - if (!groupRecord.getAdmins().contains(Address.Companion.fromSerialized(masterDevice))) { + if (!groupRecord.getAdmins().contains(Address.Companion.fromSerialized(content.getSender()))) { Log.d("Loki", "Received a group update message from a non-admin user for: " + id +"; ignoring."); return null; } @@ -219,9 +216,7 @@ public class GroupMessageProcessor { @NonNull SignalServiceGroup group, @NonNull GroupRecord record) { - String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); - if (masterDevice == null) { masterDevice = content.getSender(); } - if (record.getMembers().contains(Address.Companion.fromSerialized(masterDevice))) { + if (record.getMembers().contains(Address.Companion.fromSerialized(content.getSender()))) { ApplicationContext.getInstance(context) .getJobManager() .add(new PushGroupUpdateJob(content.getSender(), group.getGroupId())); @@ -242,10 +237,8 @@ public class GroupMessageProcessor { GroupContext.Builder builder = createGroupContext(group); builder.setType(GroupContext.Type.QUIT); - String masterDevice = MultiDeviceProtocol.shared.getMasterDevice(content.getSender()); - if (masterDevice == null) { masterDevice = content.getSender(); } - if (members.contains(Address.Companion.fromExternal(context, masterDevice))) { - database.removeMember(id, Address.Companion.fromExternal(context, masterDevice)); + if (members.contains(Address.Companion.fromExternal(context, content.getSender()))) { + database.removeMember(id, Address.Companion.fromExternal(context, content.getSender())); if (outgoing) database.setActive(id, false); return storeMessage(context, content, group, builder.build(), outgoing); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 4510b1eefb..107b88938b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -269,54 +269,34 @@ public class PushDecryptJob extends BaseJob implements InjectableType { SignalServiceDataMessage message = content.getDataMessage().get(); boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent(); - if (message.isDeviceUnlinkingRequest()) { - throw new UnsupportedOperationException("Device link operations are not supported!"); - } else { - - if (message.getClosedGroupUpdateV2().isPresent()) { - ClosedGroupsProtocolV2.handleMessage(context, message.getClosedGroupUpdateV2().get(), message.getTimestamp(), envelope.getSource(), content.getSender()); - } - if (message.isEndSession()) { - handleEndSessionMessage(content, smsMessageId); - } else if (message.isGroupUpdate()) { - handleGroupMessage(content, message, smsMessageId); - } else if (message.isExpirationUpdate()) { - handleExpirationUpdate(content, message, smsMessageId); - } else if (isMediaMessage) { - handleMediaMessage(content, message, smsMessageId, Optional.absent()); - } else if (message.getBody().isPresent()) { - handleTextMessage(content, message, smsMessageId, Optional.absent()); - } + if (message.getClosedGroupUpdateV2().isPresent()) { + ClosedGroupsProtocolV2.handleMessage(context, message.getClosedGroupUpdateV2().get(), message.getTimestamp(), envelope.getSource(), content.getSender()); + } + if (message.isEndSession()) { + handleEndSessionMessage(content, smsMessageId); + } else if (message.isGroupUpdate()) { + handleGroupMessage(content, message, smsMessageId); + } else if (message.isExpirationUpdate()) { + handleExpirationUpdate(content, message, smsMessageId); + } else if (isMediaMessage) { + handleMediaMessage(content, message, smsMessageId, Optional.absent()); + } else if (message.getBody().isPresent()) { + handleTextMessage(content, message, smsMessageId, Optional.absent()); + } - if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) { - handleUnknownGroupMessage(content, message.getGroupInfo().get()); - } + if (message.getGroupInfo().isPresent() && groupDatabase.isUnknownGroup(GroupUtil.getEncodedId(message.getGroupInfo().get()))) { + handleUnknownGroupMessage(content, message.getGroupInfo().get()); + } - if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { - SessionMetaProtocol.handleProfileKeyUpdate(context, content); - } + if (message.getProfileKey().isPresent() && message.getProfileKey().get().length == 32) { + SessionMetaProtocol.handleProfileKeyUpdate(context, content); + } - if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.Companion.fromSerialized(content.getSender()))) { - handleNeedsDeliveryReceipt(content, message); - } + if (SessionMetaProtocol.shouldSendDeliveryReceipt(message, Address.Companion.fromSerialized(content.getSender()))) { + handleNeedsDeliveryReceipt(content, message); } } else if (content.getSyncMessage().isPresent()) { throw new UnsupportedOperationException("Device link operations are not supported!"); - -// TextSecurePreferences.setMultiDevice(context, true); -// -// SignalServiceSyncMessage syncMessage = content.getSyncMessage().get(); -// -// if (syncMessage.getSent().isPresent()) handleSynchronizeSentMessage(content, syncMessage.getSent().get()); -// else if (syncMessage.getRequest().isPresent()) handleSynchronizeRequestMessage(syncMessage.getRequest().get()); -// else if (syncMessage.getRead().isPresent()) handleSynchronizeReadMessage(syncMessage.getRead().get(), content.getTimestamp()); -// else if (syncMessage.getVerified().isPresent()) handleSynchronizeVerifiedMessage(syncMessage.getVerified().get()); -// else if (syncMessage.getStickerPackOperations().isPresent()) handleSynchronizeStickerPackOperation(syncMessage.getStickerPackOperations().get()); -// else if (syncMessage.getContacts().isPresent()) SyncMessagesProtocol.handleContactSyncMessage(context, content, syncMessage.getContacts().get()); -// else if (syncMessage.getGroups().isPresent()) SyncMessagesProtocol.handleClosedGroupSyncMessage(context, content, syncMessage.getGroups().get()); -// else if (syncMessage.getOpenGroups().isPresent()) SyncMessagesProtocol.handleOpenGroupSyncMessage(context, content, syncMessage.getOpenGroups().get()); -// else if (syncMessage.getBlockedList().isPresent()) SyncMessagesProtocol.handleBlockedContactsSyncMessage(context, content, syncMessage.getBlockedList().get()); -// else Log.w(TAG, "Contains no known sync types..."); } else if (content.getReceiptMessage().isPresent()) { SignalServiceReceiptMessage message = content.getReceiptMessage().get(); @@ -1250,21 +1230,10 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } else { String publicKey = message.getDestination().get(); String userPublicKey = TextSecurePreferences.getLocalNumber(context); - Set allUserDevices = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - if (allUserDevices.contains(publicKey)) { + if (publicKey.equals(userPublicKey)) { return Recipient.from(context, Address.Companion.fromSerialized(userPublicKey), false); } else { - try { - // TODO: Burn this with fire when we can - PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); - String masterPublicKey = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); - if (masterPublicKey == null) { - masterPublicKey = publicKey; - } - return Recipient.from(context, Address.Companion.fromSerialized(masterPublicKey), false); - } catch (Exception e) { - return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); - } + return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); } } } @@ -1282,21 +1251,10 @@ public class PushDecryptJob extends BaseJob implements InjectableType { return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); } else { String userPublicKey = TextSecurePreferences.getLocalNumber(context); - Set allUserDevices = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey); - if (allUserDevices.contains(publicKey)) { + if (publicKey.equals(userPublicKey)) { return Recipient.from(context, Address.Companion.fromSerialized(userPublicKey), false); } else { - try { - // TODO: Burn this with fire when we can - PromiseUtilities.timeout(FileServerAPI.shared.getDeviceLinks(publicKey, false), 6000).get(); - String masterPublicKey = org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol.shared.getMasterDevice(publicKey); - if (masterPublicKey == null) { - masterPublicKey = publicKey; - } - return Recipient.from(context, Address.Companion.fromSerialized(masterPublicKey), false); - } catch (Exception e) { - return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); - } + return Recipient.from(context, Address.Companion.fromSerialized(publicKey), false); } } } @@ -1330,9 +1288,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { Recipient sender = Recipient.from(context, Address.Companion.fromSerialized(content.getSender()), false); - if (content.getDeviceLink().isPresent()) { - return false; - } else if (content.getDataMessage().isPresent()) { + if (content.getDataMessage().isPresent()) { SignalServiceDataMessage message = content.getDataMessage().get(); Recipient conversation = getMessageDestination(content, message); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt index 4c7ad6e8c1..a0dc10c34d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/SelectContactsLoader.kt @@ -9,9 +9,9 @@ class SelectContactsLoader(context: Context, val usersToExclude: Set) : override fun loadInBackground(): List { val contacts = ContactUtilities.getAllContacts(context) return contacts.filter { contact -> - !contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave && !usersToExclude.contains(contact.recipient.address.toString()) + !contact.isGroupRecipient && !usersToExclude.contains(contact.address.toString()) }.map { - it.recipient.address.toString() + it.address.toString() } } } \ No newline at end of file diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt index 35d0f56a71..6e46808d41 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/fragments/ContactSelectionListLoader.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.loki.fragments import android.content.Context import network.loki.messenger.R -import org.thoughtcrime.securesms.loki.utilities.Contact import org.thoughtcrime.securesms.loki.utilities.ContactUtilities import org.session.libsession.messaging.threads.recipients.Recipient import org.thoughtcrime.securesms.util.AsyncLoader @@ -28,9 +27,9 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St override fun loadInBackground(): List { val contacts = ContactUtilities.getAllContacts(context).filter { if (filter.isNullOrEmpty()) return@filter true - it.recipient.toShortString().contains(filter.trim(), true) || it.recipient.address.serialize().contains(filter.trim(), true) + it.toShortString().contains(filter.trim(), true) || it.address.serialize().contains(filter.trim(), true) }.sortedBy { - it.recipient.toShortString() + it.toShortString() } val list = mutableListOf() if (isFlagSet(DisplayMode.FLAG_CLOSED_GROUPS)) { @@ -45,27 +44,27 @@ class ContactSelectionListLoader(context: Context, val mode: Int, val filter: St return list } - private fun getContacts(contacts: List): List { + private fun getContacts(contacts: List): List { return getItems(contacts, context.getString(R.string.fragment_contact_selection_contacts_title)) { - !it.recipient.isGroupRecipient && !it.isOurDevice && !it.isSlave + !it.isGroupRecipient } } - private fun getClosedGroups(contacts: List): List { + private fun getClosedGroups(contacts: List): List { return getItems(contacts, context.getString(R.string.fragment_contact_selection_closed_groups_title)) { - it.recipient.address.isClosedGroup + it.address.isClosedGroup } } - private fun getOpenGroups(contacts: List): List { + private fun getOpenGroups(contacts: List): List { return getItems(contacts, context.getString(R.string.fragment_contact_selection_open_groups_title)) { - it.recipient.address.isOpenGroup + it.address.isOpenGroup } } - private fun getItems(contacts: List, title: String, contactFilter: (Contact) -> Boolean): List { + private fun getItems(contacts: List, title: String, contactFilter: (Recipient) -> Boolean): List { val items = contacts.filter(contactFilter).map { - ContactSelectionListItem.Contact(it.recipient) + ContactSelectionListItem.Contact(it) } if (items.isEmpty()) return listOf() val header = ContactSelectionListItem.Header(title) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt index a63360fdaa..d31c4c9d02 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/utilities/ContactUtilities.kt @@ -3,49 +3,19 @@ package org.thoughtcrime.securesms.loki.utilities import android.content.Context import org.thoughtcrime.securesms.database.DatabaseFactory import org.session.libsession.messaging.threads.recipients.Recipient -import org.session.libsession.utilities.TextSecurePreferences -import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol - -data class Contact( - val recipient: Recipient, - val isSlave: Boolean, - val isOurDevice: Boolean -) { - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other?.javaClass != javaClass) return false - other as Contact - return recipient == other.recipient - } - - override fun hashCode(): Int { - return recipient.hashCode() - } -} object ContactUtilities { @JvmStatic - fun getAllContacts(context: Context): Set { + fun getAllContacts(context: Context): Set { val threadDatabase = DatabaseFactory.getThreadDatabase(context) - val userPublicKey = TextSecurePreferences.getLocalNumber(context)!! - val lokiAPIDatabase = DatabaseFactory.getLokiAPIDatabase(context) - val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey) val cursor = threadDatabase.conversationList - val result = mutableSetOf() + val result = mutableSetOf() threadDatabase.readerFor(cursor).use { reader -> while (reader.next != null) { val thread = reader.current val recipient = thread.recipient - val publicKey = recipient.address.serialize() - val isUserDevice = userDevices.contains(publicKey) - var isSlave = false - if (!recipient.isGroupRecipient) { - val deviceLinks = lokiAPIDatabase.getDeviceLinks(publicKey) - isSlave = deviceLinks.find { it.slavePublicKey == publicKey } != null - } - result.add(Contact(recipient, isSlave, isUserDevice)) + result.add(recipient) } } return result diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java index ccfbb6dfdd..fd2c2be969 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/MarkReadReceiver.java @@ -21,7 +21,6 @@ import org.thoughtcrime.securesms.jobs.SendReadReceiptJob; import org.session.libsignal.utilities.logging.Log; import org.thoughtcrime.securesms.loki.protocol.SessionMetaProtocol; import org.thoughtcrime.securesms.service.ExpiringMessageManager; -import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol; import java.util.LinkedList; import java.util.List; @@ -85,13 +84,9 @@ public class MarkReadReceiver extends BroadcastReceiver { // Loki - Check whether we want to send a read receipt to this user if (!SessionMetaProtocol.shouldSendReadReceipt(address)) { continue; } // Loki - Take into account multi device - Set linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(address.serialize()); - for (String device : linkedDevices) { - Address deviceAsAddress = Address.Companion.fromExternal(context, device); - ApplicationContext.getInstance(context) - .getJobManager() - .add(new SendReadReceiptJob(deviceAsAddress, timestamps)); - } + ApplicationContext.getInstance(context) + .getJobManager() + .add(new SendReadReceiptJob(address, timestamps)); } }