// networkJobs provides functions to perform network jobs // An example is downloading a user's inbox messages // All of these jobs are run in the background by backgroundJobs (see backgroundJobs.go) package networkJobs //TODO: Add all of these jobs to backgroundJobs.go //TODO: Implement these network jobs: // Download message funded statuses for hosting // Download message funded statuses for moderation // Download message funded statuses for reading (this is to make sure messages we have received are funded) // Download Host identity funded statuses // Download Mate identity/profile funded statuses for hosting // Download Mate identity/profile funded statuses for moderation // Download Mate identity/profile funded statuses for browsing // Download Mate identity/profile funded statuses for contacts/chat recipients // Download report funded statuses for hosting // Download report funded statuses for moderation // Broadcast my Mate profile // Broadcast my Host profile // Broadcast my Moderator profile/reviews // Broadcast my Mate messages // Broadcast my Moderator messages // Update Existing Mate User Profiles // -We need a way for a client to user know if another user has changed their profile to no longer fulfill our downloads criteria // -We need to download updated profiles for specific identities, without providing a Criteria in the request // -We should download the newest viewable profile for all Mate users whose stored profile fulfills our criteria. // -If the user's newer downloaded profile does not fulfill our criteria, we should delete their old profiles. //TODO: More jobs //TODO: Add limits to the number of hosts per job, and the number of items per request // Otherwise, some of these jobs could take a very long time to complete, due to the number of hosts being contacted. //TODO: The Mate outlier lists can be reduced by removing all of the mate outliers whose profiles/viewable statuses will be downloaded by other jobs. import "seekia/internal/byteRange" import "seekia/internal/badgerDatabase" import "seekia/internal/contentMetadata" import "seekia/internal/desires/myMateDesires" import "seekia/internal/helpers" import "seekia/internal/logger" import "seekia/internal/messaging/myChatMessages" import "seekia/internal/messaging/myInbox" import "seekia/internal/messaging/peerChatKeys" import "seekia/internal/moderation/myReviews" import "seekia/internal/myBlockedUsers" import "seekia/internal/myContacts" import "seekia/internal/myIdentity" import "seekia/internal/myLikedUsers" import "seekia/internal/myRanges" import "seekia/internal/mySettings" import "seekia/internal/network/fundedStatus" import "seekia/internal/network/mateCriteria" import "seekia/internal/network/myMateCriteria" import "seekia/internal/network/queryHosts" import "seekia/internal/profiles/calculatedAttributes" import "seekia/internal/profiles/profileStorage" import "seekia/internal/profiles/readProfiles" import "seekia/internal/profiles/viewableProfiles" import "errors" // This function will download and update the network-wide parameters files func DownloadParameters(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadParameters called with invalid networkType: " + networkTypeString) } err := queryHosts.DownloadParametersFromHosts(true, networkType, 5) if (err != nil) { return err } return nil } // This function will download all of the network's newest viewable host/moderator profiles // -This is used by all users to download host profiles // -Users will only connect to Viewable hosts, whom are not banned // -This is used by moderators/hosts to download all moderator profiles // All hosts/moderators must download all newest viewable Moderator profiles func DownloadAllNewestViewableUserProfiles(profileType string, networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadAllNewestViewableUserProfiles called with invalid networkType: " + networkTypeString) } // This functions is not used for Mate profiles // Mate profiles are always downloaded based on a range, whereas in this function, we download all newest viewable profiles. if (profileType != "Host" && profileType != "Moderator"){ return errors.New("DownloadAllNewestViewableUserProfiles called with invalid profileType: " + profileType) } checkIfProfileShouldBeDownloaded := func(_ [28]byte, profileAuthor [16]byte, profileCreationTime int64)(bool, error){ // Check to see if our newest profile is older than the received profile newestViewableProfileExists, _, _, _, storedProfileCreationTime, _, err := viewableProfiles.GetNewestViewableUserProfile(profileAuthor, networkType, true, false, true) if (err != nil) { return false, err } if (newestViewableProfileExists == false){ // We do not have any viewable profiles for this host // Download this profile return true, nil } if (profileCreationTime <= storedProfileCreationTime){ // Host we are retrieving from has a profile that is not newer than our existing profile // We can skip this profile return false, nil } // This profile is newer than our existing newest viewable profile // We download it. return true, nil } minimumRange, maximumRange := byteRange.GetMinimumMaximumIdentityHashBounds() emptyList := make([][16]byte, 0) err := queryHosts.DownloadProfilesFromHosts(false, networkType, profileType, minimumRange, maximumRange, emptyList, nil, true, true, false, checkIfProfileShouldBeDownloaded, 10) if (err != nil) { return err } return nil } // This function will download newest viewable mate profiles that fulfill user's downloads criteria // It is used by users who are downloading profiles to find matches. func DownloadMateProfilesToBrowse(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMateProfilesToBrowse called with invalid networkType: " + networkTypeString) } minimumRange, maximumRange := byteRange.GetMinimumMaximumIdentityHashBounds() getMyCriteria := func()([]byte, error){ exists, myCriteria, err := myMateCriteria.GetMyMateDownloadsCriteria() if (err != nil) { return nil, err } if (exists == false){ return nil, nil } return myCriteria, nil } myCriteria, err := getMyCriteria() if (err != nil) { return err } getDownloadAllBool := func()(bool, error){ //TODO: Fix below desiresPruningMode := false if (desiresPruningMode == true){ return true, nil } return false, nil } downloadAllBool, err := getDownloadAllBool() if (err != nil) { return err } emptyList := make([][16]byte, 0) checkIfProfileShouldBeDownloaded := func(_ [28]byte, profileAuthor [16]byte, profileCreationTime int64)(bool, error){ // This function is only called if profile hash is not already downloaded // We check if we have a profile that is newer (and viewable) // If we have a newer viewable profile, we will skip downloading this profile // We wont check, but if we have a newer profile that is not known to be viewable, we will still download this profile // We will delete this older profile if and when the newer profile becomes viewable profileExists, _, _, _, storedProfileCreationTime, _, err := viewableProfiles.GetNewestViewableUserProfile(profileAuthor, networkType, true, false, true) if (err != nil) { return false, err } if (profileExists == false){ return true, nil } if (storedProfileCreationTime >= profileCreationTime){ // The profile the host is offering is not newer than our existing // We will skip it return false, nil } // Profile is newer than the profile we already have, or we dont have any profile for this identity // We will download it return true, nil } err = queryHosts.DownloadProfilesFromHosts(false, networkType, "Mate", minimumRange, maximumRange, emptyList, myCriteria, true, true, downloadAllBool, checkIfProfileShouldBeDownloaded, 10) if (err != nil) { return err } return nil } // This function will download the newest viewable profiles for a user's mate outliers // Outliers are either contacts, likes, or chat recipients. // We download them so the user's client will have their profiles downloaded, even if the users do not fulfill our criteria // This is done so that a user's client has the profiles for users within their contacts and users whom they are chatting with // These are downloaded one-by-one to prevent hosts from learning a user's contacts/likes/chat recipients func DownloadMateOutlierProfiles(outlierType string, networkType byte, missingOrExisting string)error{ if (outlierType != "Contacts" && outlierType != "Likes" && outlierType != "ChatRecipients"){ return errors.New("DownloadMateOutlierProfiles called with invalid outlierType: " + outlierType) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMateOutlierProfiles called with invalid networkType: " + networkTypeString) } if (missingOrExisting != "Missing" && missingOrExisting != "Existing"){ return errors.New("DownloadMateOutlierProfiles called with invalid missingOrExisting: " + missingOrExisting) } //Outputs: // -[][16]byte: List of identities // -error getMyOutlierIdentitiesList := func()([][16]byte, error){ if (outlierType == "Contacts"){ myContactIdentityHashesList, err := myContacts.GetMyContactsList("Mate") if (err != nil) { return nil, err } return myContactIdentityHashesList, nil } if (outlierType == "Likes"){ myLikedUsersList, err := myLikedUsers.GetMyLikedUsersList() if (err != nil) { return nil, err } return myLikedUsersList, nil } myIdentityExists, chatRecipientIdentityHashesList, err := myChatMessages.GetAllMyNonBlockedChatRecipients("Mate", networkType) if (err != nil) { return nil, err } if (myIdentityExists == false){ // Nothing to do, mate identity does not exist emptyList := make([][16]byte, 0) return emptyList, nil } return chatRecipientIdentityHashesList, nil } myOutlierIdentitiesList, err := getMyOutlierIdentitiesList() if (err != nil) { return err } if (len(myOutlierIdentitiesList) == 0){ return nil } helpers.RandomizeListOrder(myOutlierIdentitiesList) // We will attempt to retrieve the newest viewable profiles for the identities for _, userIdentityHash := range myOutlierIdentitiesList{ statusIsKnown, identityIsFunded, err := fundedStatus.GetIdentityIsFundedStatus(userIdentityHash, networkType) if (err != nil) { return err } if (statusIsKnown == true && identityIsFunded == false){ // The user's identity not funded // This means that none of their profiles will be hosted by the network // We will not try to download their profile, because it will not exist on the network continue } // We check to see if the user's active chat keys exist // We do this because even if a user's profile exists, it may not contain the user's newest chat keys keysAreMissing, err := peerChatKeys.CheckIfUsersChatKeysAreMissing(userIdentityHash, networkType) if (err != nil) { return err } if (keysAreMissing == true){ if (missingOrExisting == "Existing"){ continue } } profileExists, _, _, _, _, _, err := viewableProfiles.GetNewestViewableUserProfile(userIdentityHash, networkType, true, false, true) if (err != nil) { return err } if (profileExists == false){ if (missingOrExisting == "Existing"){ continue } } else { if (missingOrExisting == "Missing"){ continue } } checkIfProfileShouldBeDownloaded := func(_ [28]byte, profileAuthor [16]byte, profileCreationTime int64)(bool, error){ // We check to see if this profile is newer than our stored newest viewable profile for the identity // If it is, we download the profile profileExists, _, _, _, existingProfileCreationTime, _, err := viewableProfiles.GetNewestViewableUserProfile(profileAuthor, networkType, true, false, true) if (err != nil) { return false, err } if (profileExists == false){ return true, nil } if (profileCreationTime <= existingProfileCreationTime){ return false, nil } return true, nil } identitiesList := [][16]byte{userIdentityHash} minimumRange, maximumRange := byteRange.GetMinimumMaximumIdentityHashBounds() err = queryHosts.DownloadProfilesFromHosts(true, networkType, "Mate", minimumRange, maximumRange, identitiesList, nil, true, true, false, checkIfProfileShouldBeDownloaded, 1) if (err != nil) { return err } } return nil } // This function will download profiles within our host range // This should be called if a user is in Host mode. func DownloadProfilesToHost(profileType string, networkType byte)error{ if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){ return errors.New("DownloadProfilesToHost called with invalid profileType: " + profileType) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadProfilesToHost called with invalid networkType: " + networkTypeString) } hostModeEnabled, hostingAny, myIdentitiesToHostRangeStart, myIdentitiesToHostRangeEnd, err := myRanges.GetMyIdentitiesToHostRange(profileType) if (err != nil) { return err } if (hostModeEnabled == false){ // Host mode must have been turned off recently. err := logger.AddLogEntry("Network", "DownloadProfilesToHost called when Host mode is disabled.") if (err != nil) { return err } return nil } if (hostingAny == false) { // No profiles to host of this profileType. Nothing to do. return nil } checkIfProfileShouldBeDownloaded := func(profileHash [28]byte, profileAuthor [16]byte, profileCreationTime int64)(bool, error){ //TODO // First we check the moderator consensus of the identity // If consensus is ban, we will only download profile if time it has been banned is not older than maximum time to store banned content // Then we check to see if we have profile metadata // If we don't, we download the profile. // At this point, we have the profile metadata but not the profile // That means we thought it was worth deleting at one point (or it was downloaded for mate/moderator reasons) // Then we check the moderator consensus of profile // If consensus is ban, we will only download profile if time it has been banned is not older than maximum time to store banned content // Otherwise, we will not download profile // At this point, consensus for profile is approve/none and identity is not banned // We then check to see if we have a newer profile by the same identity // If there is no newer profile, we download the profile // At this point, there is a newer profile for the identity // We only want to download this older profile if it can be used by moderators to determine if the identity should be banned // We dont want this to be abused, if an attacker created a report for many old profiles that existed on the network, they could cause hosts to download all those old profiles // So, if profile has been approved for a long enough period of time, it does not need to be hosted anymore // We cannot trust the creationTimes for reviews, reports, or profiles, so each host keeps track of the time that a consensus has existed based on the reviews it has downloaded // We use verdictHistory for this // If offered profile is approved, we check to see if profile has been approved for at least the maximum time to store old approved profiles // If it has been approved for at least that long, we will not download the profile // If it has been approved but not for that long, we will download the profile, just so that moderators can determine if consensus is wrong before profile is dropped from network // At this point, consensus for profile is none, and it is not the user's newest profile // We check to see if any ban reviews and reports exist for the profile // If there are any ban reviews or reports, we check to see how long time that consensus has been none. // If that time is older than maximum time to store banned and reported no consensus old content, we will not download it // If that time has not passed, we will download the profile // At this point, the profile has no ban reviews/reports and consensus is none // We will check if profile's no consensus time is older than maximum time to store unreported and unbanned no consensus old content // If the profile is older than that time, we will not download it // If the profile is newer than that time, we will download it // This is quite complicated and could be simplified // Basically, we want to profiles on the network for long enough so the moderators can review them and ban any moderators who wrongly banned/approved them return true, nil } getGetViewableProfilesOnlyBool := func()(bool, error){ if (profileType == "Host"){ // Hosts must download all peer host profiles, regardless of viewable status // This is because (a) malicious moderator(s) could ban all Host profiles and cripple the network // Host profiles have no images, so they are much less legally risky to download anyway. //TODO: Make it clear in the GUI that hosts will host all unviewable host profiles, even if HostUnviewableProfiles is disabled. return false, nil } exists, hostUnviewableStatus, err := mySettings.GetSetting("HostUnviewableProfilesOnOffStatus") if (err != nil) { return false, err } if (exists == true && hostUnviewableStatus == "On") { return false, nil } return true, nil } getViewableProfilesOnlyBool, err := getGetViewableProfilesOnlyBool() if (err != nil) { return err } emptyList := make([][16]byte, 0) err = queryHosts.DownloadProfilesFromHosts(true, networkType, profileType, myIdentitiesToHostRangeStart, myIdentitiesToHostRangeEnd, emptyList, nil, false, getViewableProfilesOnlyBool, false, checkIfProfileShouldBeDownloaded, 10) if (err != nil) { return err } return nil } // This function will download profiles within our moderation range func DownloadProfilesToModerate(profileType string, networkType byte)error{ if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){ return errors.New("DownloadProfilesToModerate called with invalid profileType: " + profileType) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadProfilesToModerate called with invalid networkType: " + networkTypeString) } moderatorModeEnabled, moderatingAny, myIdentitiesToModerateRangeStart, myIdentitiesToModerateRangeEnd, err := myRanges.GetMyIdentitiesToModerateRange(profileType) if (err != nil) { return err } if (moderatorModeEnabled == false){ // This should not happen unless moderator mode was turned off recently err := logger.AddLogEntry("Network", "DownloadProfilesToModerate called when moderator mode is disabled.") if (err != nil) { return err } return nil } if (moderatingAny == false){ // We are not moderating any profiles return nil } checkIfProfileShouldBeDownloaded := func(profileHash [28]byte, profileIdentityHash [16]byte, profileCreationTime int64)(bool, error){ //TODO: See if we have already reviewed the profile, if yes, then we don't need to download it. // Also, if we have the profile's metadata, and it is not the user's newest profile, and it has no ban reviews, we don't need to download it. // We must have the profile metadata downloaded because it is the only way we can be sure the profile has no ban reviews return true, nil } emptyList := make([][16]byte, 0) err = queryHosts.DownloadProfilesFromHosts(true, networkType, profileType, myIdentitiesToModerateRangeStart, myIdentitiesToModerateRangeEnd, emptyList, nil, false, false, false, checkIfProfileShouldBeDownloaded, 10) if (err != nil) { return err } return nil } // This function will download messages in our inboxes // Each inbox has to be queried seperately from a different host to prevent linking the inboxes together func DownloadMyInboxMessages(identityType string, networkType byte)error{ if (identityType != "Mate" && identityType != "Moderator"){ return errors.New("DownloadMyInboxMessages called with invalid identityType: " + identityType) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMyInboxMessages called with invalid networkType: " + networkTypeString) } myIdentityFound, myIdentityHash, err := myIdentity.GetMyIdentityHash(identityType) if (err != nil) { return err } if (myIdentityFound == false){ return nil } myInboxesList, err := myInbox.GetAllMyActiveInboxes(myIdentityHash, networkType) if (err != nil) { return err } helpers.RandomizeListOrder(myInboxesList) minimumInboxRange, maximumInboxRange := byteRange.GetMinimumMaximumInboxBounds() for _, inbox := range myInboxesList{ inboxAsList := [][10]byte{inbox} checkIfMessageShouldBeDownloaded := func(messageHash [26]byte)(bool, error){ metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(messageHash) if (err != nil) { return false, err } if (metadataExists == false){ return true, nil } // We already downloaded the message at some point // First we see if the message inbox/networkType matches what we requested if (messageNetworkType != networkType || messageInbox != inbox){ // The host is offering us a message we know does not belong to the inbox/networkType we requested // The host is malicious. // We download the message so they do not learn that we downloaded this message at one point (to prevent fingerprinting) // We should have already checked for this within queryHosts.DownloadMessagesFromHosts // The only way we would not have known is if we downloaded the message recently err := logger.AddLogEntry("Network", "checkIfMessageShouldBeDownloaded called when inbox/networkType does not match message we have metadata for.") if (err != nil) { return false, err } return true, nil } // Now we check to see if we have already imported, deleted, or failed to decrypt the message isDeleted, err := myChatMessages.CheckIfMessageIsDeleted(messageHash) if (err != nil) { return false, err } if (isDeleted == true){ return false, nil } isUndecryptable, err := myChatMessages.CheckIfMessageIsUndecryptable(messageHash) if (err != nil) { return false, err } if (isUndecryptable == true){ return false, nil } isImported, err := myChatMessages.CheckIfMessageIsImported(messageHash, identityType, messageNetworkType) if (err != nil) { return false, err } if (isImported == true){ return false, nil } return true, nil } err := queryHosts.DownloadMessagesFromHosts(false, networkType, minimumInboxRange, maximumInboxRange, inboxAsList, false, false, checkIfMessageShouldBeDownloaded, 1) if (err != nil) { return err } } return nil } // This function will download messages within our hosting range // This should be called periodically if we are in Host mode func DownloadMessagesToHost(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMessagesToHost called with invalid networkType: " + networkTypeString) } hostModeEnabled, hostingAny, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToHostRange() if (err != nil) { return err } if (hostModeEnabled == false){ // Host mode is disabled. Nothing to do. // This should not happen unless host mode was turned off recently err := logger.AddLogEntry("Network", "DownloadMessagesToHost called when Host mode is disabled.") if (err != nil) { return err } return nil } if (hostingAny == false){ // Not hosting any messages. Nothing to do. return nil } // We are not retrieving based on a list of inboxes inboxesToRetrieveList := make([][10]byte, 0) getGetViewableMessagesOnlyBool := func()(bool, error){ exists, hostUnviewableStatus, err := mySettings.GetSetting("HostUnviewableMessagesOnOffStatus") if (err != nil) { return false, err } if (exists == true && hostUnviewableStatus == "On") { return false, nil } return true, nil } getViewableMessagesOnlyBool, err := getGetViewableMessagesOnlyBool() if (err != nil) { return err } checkIfMessageShouldBeDownloaded := func(inputMessageHash [26]byte)(bool, error){ //TODO: // This should be similar to the function in DownloadProfilesToHost return true, nil } err = queryHosts.DownloadMessagesFromHosts(true, networkType, myInboxRangeStart, myInboxRangeEnd, inboxesToRetrieveList, getViewableMessagesOnlyBool, false, checkIfMessageShouldBeDownloaded, 5) if (err != nil) { return err } return nil } // This function will download messages within our moderation range // This should be called periodically if the user is in Moderator mode. func DownloadMessagesToModerate(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMessagesToModerate called with invalid networkType: " + networkTypeString) } moderatorModeEnabled, moderatingAny, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToModerateRange() if (err != nil) { return err } if (moderatorModeEnabled == false){ // User must have disabled moderator mode recently err := logger.AddLogEntry("Network", "DownloadMessagesToModerate called when user is not in Moderator mode.") if (err != nil) { return err } return nil } if (moderatingAny == false){ // We are not moderating any messages. return nil } checkIfMessageShouldBeDownloaded := func(inputMessageHash [26]byte)(bool, error){ // This function is only called if we don't already have the message downloaded metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(inputMessageHash) if (err != nil) { return false, err } if (metadataExists == false){ return true, nil } if (messageNetworkType != networkType){ // The host is offering us a message we know does not belong to the networkType we requested // The host is malicious. // We download the message so they do not learn that we downloaded this message at one point (to prevent fingerprinting) return true, nil } isWithinRequestRange, err := byteRange.CheckIfInboxIsWithinRange(myInboxRangeStart, myInboxRangeEnd, messageInbox) if (err != nil) { return false, err } if (isWithinRequestRange == false){ // The host is offering us a message we know does not belong to the inbox range we requested // The host is malicious. // We download the message so they do not learn that we downloaded this message at one point (to prevent fingerprinting) return true, nil } myIdentityExists, messageMetadataIsKnown, iHaveReviewed, _, err := myReviews.GetMyNewestMessageModerationVerdict(inputMessageHash) if (err != nil) { return false, err } if (myIdentityExists == false){ // Without an identity, we would only use moderator mode to browse content // We should therefore download the message return true, nil } if (messageMetadataIsKnown == false){ // We must have just deleted our message metadata //TODO: Log this return true, nil } if (iHaveReviewed == true){ // We can skip messages that the user has already reviewed //TODO: Create a mode where this behavior can be disabled // Some moderators may want to keep those messages so they can reference them again, until they // expire from the network, or they have been sufficiently banned return false, nil } return true, nil } emptyList := make([][10]byte, 0) err = queryHosts.DownloadMessagesFromHosts(true, networkType, myInboxRangeStart, myInboxRangeEnd, emptyList, false, true, checkIfMessageShouldBeDownloaded, 5) if (err != nil) { return err } return nil } // This function will download all reviews banning moderator identities. // This is necessary if Moderator/Host mode is enabled. // These reviews determine which moderators are banned, so they are needed to determine the moderation verdict for all identities/content. // Thus, they must be downloaded by all hosts and moderators. func DownloadModeratorIdentityBanningReviews(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadModeratorIdentityBanningReviews called with invalid networkType: " + networkTypeString) } minimumRange, maximumRange := byteRange.GetMinimumMaximumIdentityHashBounds() emptyListA := make([][16]byte, 0) emptyListB := make([][16]byte, 0) checkIfReviewShouldBeDownloadedFunction := func(_ [29]byte, reviewedHash []byte)(bool, error){ reviewedType, err := helpers.GetReviewedTypeFromReviewedHash(reviewedHash) if (err != nil) { return false, err } if (reviewedType != "Identity" && reviewedType != "Profile" && reviewedType != "Attribute"){ return false, errors.New("DownloadIdentityReviewsFromHosts not checking that identity reviewed hash is valid.") } if (reviewedType == "Identity"){ return true, nil } return false, nil } err := queryHosts.DownloadIdentityReviewsFromHosts(false, networkType, "Moderator", minimumRange, maximumRange, emptyListA, emptyListB, checkIfReviewShouldBeDownloadedFunction, 5) if (err != nil) { return err } return nil } // This function will download reviews for identities/profiles/attributes within our Host range // It is called periodically if Host mode is enabled. func DownloadIdentityReviewsToHost(identityTypeToRetrieve string, networkType byte)error{ if (identityTypeToRetrieve != "Mate" && identityTypeToRetrieve != "Host" && identityTypeToRetrieve != "Moderator"){ return errors.New("DownloadIdentityReviewsToHost called with invalid identityTypeToRetrieve: " + identityTypeToRetrieve) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadIdentityReviewsToHost called with invalid networkType: " + networkTypeString) } hostModeEnabled, hostingAnyIdentities, myIdentityRangeStart, myIdentityRangeEnd, err := myRanges.GetMyIdentitiesToHostRange(identityTypeToRetrieve) if (err != nil) { return err } if (hostModeEnabled == false){ // Host mode is disabled. Nothing to do. // This should not happen unless host mode was turned off recently err := logger.AddLogEntry("Network", "DownloadIdentityReviewsToHost called when Host mode is disabled.") if (err != nil) { return err } return nil } if (hostingAnyIdentities == false){ // We are not hosting any identity reviews of the provided identityType return nil } emptyListA := make([][16]byte, 0) emptyListB := make([][16]byte, 0) checkIfReviewShouldBeDownloadedFunction := func(_ [29]byte, _ []byte)(bool, error){ //TODO: // We want to defend against downloading reviews that we will delete immediately. // Don't download reviews by moderators who have been banned for a long enough period of time // Don't download reviews for content/identities that have been expired for long enough // Any hosts who offers us content that should no longer exist on the network would be a malicious host. return true, nil } err = queryHosts.DownloadIdentityReviewsFromHosts(false, networkType, identityTypeToRetrieve, myIdentityRangeStart, myIdentityRangeEnd, emptyListA, emptyListB, checkIfReviewShouldBeDownloadedFunction, 5) if (err != nil) { return err } return nil } // This function will download reviews for messages within our Host range // It is called periodically if Host mode is enabled. func DownloadMessageReviewsToHost(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMessageReviewsToHost called with invalid networkType: " + networkTypeString) } hostModeEnabled, hostingAnyInboxes, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToHostRange() if (err != nil) { return err } if (hostModeEnabled == false){ // Host mode is disabled. Nothing to do. // This should not happen unless host mode was turned off recently err := logger.AddLogEntry("Network", "DownloadMessageReviewsToHost called when Host mode is disabled.") if (err != nil) { return err } return nil } if (hostingAnyInboxes == false){ // We are not hosting any message reviews return nil } emptyListA := make([][26]byte, 0) emptyListB := make([][16]byte, 0) emptyMap := make(map[[26]byte][10]byte) checkIfReviewShouldBeDownloadedFunction := func(reviewHash [29]byte, messageHash [26]byte)(bool, error){ //TODO: // We want to defend against downloading reviews that we will delete immediately. // Don't download reviews by moderators who have been banned for a long enough period of time // Don't download reviews for messages that have been expired for long enough // Any hosts who offers us content that should no longer exist on the network would be a malicious host. return true, nil } err = queryHosts.DownloadMessageReviewsFromHosts(false, networkType, myInboxRangeStart, myInboxRangeEnd, emptyListA, emptyListB, emptyMap, checkIfReviewShouldBeDownloadedFunction, 5) if (err != nil) { return err } return nil } // This function will download reviews for identities/profiles/attributes within our moderation range // This will be called periodically if Moderator mode is enabled. func DownloadIdentityReviewsForModeration(identityTypeToRetrieve string, networkType byte)error{ if (identityTypeToRetrieve != "Mate" && identityTypeToRetrieve != "Host" && identityTypeToRetrieve != "Moderator"){ return errors.New("DownloadIdentityReviewsForModeration called with invalid identityTypeToRetrieve: " + identityTypeToRetrieve) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadIdentityReviewsForModeration called with invalid networkType: " + networkTypeString) } moderatorModeEnabled, moderatingAnyIdentities, myIdentityRangeStart, myIdentityRangeEnd, err := myRanges.GetMyIdentitiesToModerateRange(identityTypeToRetrieve) if (err != nil) { return err } if (moderatorModeEnabled == false){ // Moderator mode must have been disabled recently err := logger.AddLogEntry("Network", "DownloadIdentityReviewsForModeration called when user is not in Moderator mode.") if (err != nil) { return err } return nil } if (moderatingAnyIdentities == false){ // We are not moderating any identities of the provided identityType // We don't need to download any reviews for the identities return nil } emptyListA := make([][16]byte, 0) emptyListB := make([][16]byte, 0) checkIfReviewShouldBeDownloadedFunction := func(reviewHash [29]byte, reviewedHash []byte)(bool, error){ //TODO // see DownloadIdentityReviewsToHost for some ideas on what restrictions to add return true, nil } err = queryHosts.DownloadIdentityReviewsFromHosts(false, networkType, identityTypeToRetrieve, myIdentityRangeStart, myIdentityRangeEnd, emptyListA, emptyListB, checkIfReviewShouldBeDownloadedFunction, 5) if (err != nil) { return err } return nil } // This function will download reviews for messages within our moderation range // This will be called periodically if Moderator mode is enabled. func DownloadMessageReviewsForModeration(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMessageReviewsForModeration called with invalid networkType: " + networkTypeString) } moderatorModeEnabled, moderatingAnyInboxes, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToModerateRange() if (err != nil) { return err } if (moderatorModeEnabled == false){ // Moderator mode must have been disabled recently err := logger.AddLogEntry("Network", "DownloadMessageReviewsForModeration called when user is not in Moderator mode.") if (err != nil) { return err } return nil } if (moderatingAnyInboxes == false){ // We are not moderating any message inboxes // We don't need to download any message reviews return nil } emptyListA := make([][26]byte, 0) emptyListB := make([][16]byte, 0) emptyMap := make(map[[26]byte][10]byte) checkIfReviewShouldBeDownloadedFunction := func(reviewHash [29]byte, reviewedMessageHash [26]byte)(bool, error){ //TODO // see DownloadMessageReviewsToHost for some ideas on what restrictions to add return true, nil } err = queryHosts.DownloadMessageReviewsFromHosts(false, networkType, myInboxRangeStart, myInboxRangeEnd, emptyListA, emptyListB, emptyMap, checkIfReviewShouldBeDownloadedFunction, 5) if (err != nil) { return err } return nil } // This function will download reports for identities/profiles/attributes within our host range // It must be called if Host mode is enabled func DownloadIdentityReportsToHost(identityTypeToRetrieve string, networkType byte)error{ if (identityTypeToRetrieve != "Mate" && identityTypeToRetrieve != "Host" && identityTypeToRetrieve != "Moderator"){ return errors.New("DownloadIdentityReportsToHost called with invalid identityTypeToRetrieve: " + identityTypeToRetrieve) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadIdentityReportsToHost called with invalid networkType: " + networkTypeString) } hostModeEnabled, hostingAnyIdentities, myIdentityRangeStart, myIdentityRangeEnd, err := myRanges.GetMyIdentitiesToHostRange(identityTypeToRetrieve) if (err != nil) { return err } if (hostModeEnabled == false){ // Host mode is disabled. Nothing to do. // This should not happen unless host mode was turned off recently err := logger.AddLogEntry("Network", "DownloadIdentityReportsToHost called when Host mode is disabled.") if (err != nil) { return err } return nil } if (hostingAnyIdentities == false){ return nil } checkIfReportShouldBeDownloaded := func(reportHash [30]byte, reportedHash []byte)(bool, error){ //TODO // see DownloadReviewsToHost for some ideas on what restrictions to add return true, nil } emptyList := make([][16]byte, 0) err = queryHosts.DownloadIdentityReportsFromHosts(true, networkType, identityTypeToRetrieve, myIdentityRangeStart, myIdentityRangeEnd, emptyList, checkIfReportShouldBeDownloaded, 5) if (err != nil) { return err } return nil } // This function will download reports for messages within our host range // It must be called if Host mode is enabled func DownloadMessageReportsToHost(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMessageReportsToHost called with invalid networkType: " + networkTypeString) } hostModeEnabled, hostingAnyInboxes, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToHostRange() if (err != nil) { return err } if (hostModeEnabled == false){ // Host mode is disabled. Nothing to do. // This should not happen unless host mode was turned off recently err := logger.AddLogEntry("Network", "DownloadMessageReportsToHost called when Host mode is disabled.") if (err != nil) { return err } return nil } if (hostingAnyInboxes == false){ return nil } checkIfReportShouldBeDownloaded := func(reportHash [30]byte, reportedMessageHash [26]byte)(bool, error){ //TODO // see DownloadMessageReportsToHost for some ideas on what restrictions to add return true, nil } emptyList := make([][26]byte, 0) emptyMap := make(map[[26]byte][10]byte) err = queryHosts.DownloadMessageReportsFromHosts(true, networkType, myInboxRangeStart, myInboxRangeEnd, emptyList, emptyMap, checkIfReportShouldBeDownloaded, 5) if (err != nil) { return err } return nil } // This function will download reports for identities/profiles/attributes within our moderation range // It is called periodically if Moderator mode is enabled func DownloadIdentityReportsForModeration(identityTypeToRetrieve string, networkType byte)error{ if (identityTypeToRetrieve != "Mate" && identityTypeToRetrieve != "Host" && identityTypeToRetrieve != "Moderator"){ return errors.New("DownloadIdentityReportsForModeration called with invalid identityTypeToRetrieve: " + identityTypeToRetrieve) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadIdentityReportsForModeration called with invalid networkType: " + networkTypeString) } moderatorModeEnabled, moderatingAny, myRangeStart, myRangeEnd, err := myRanges.GetMyIdentitiesToModerateRange(identityTypeToRetrieve) if (err != nil) { return err } if (moderatorModeEnabled == false){ // Moderator mode must have been disabled recently err := logger.AddLogEntry("Network", "DownloadIdentityReportsForModeration called when user is not in Moderator mode.") if (err != nil) { return err } return nil } if (moderatingAny == false){ // Not moderating any identities of provided identityType // We don't need to download reports return nil } checkIfReportShouldBeDownloaded := func(reportHash [30]byte, reportedHash []byte)(bool, error){ //TODO // see DownloadIdentityReviewsToHost for some ideas on what restrictions to add return true, nil } emptyList := make([][16]byte, 0) err = queryHosts.DownloadIdentityReportsFromHosts(true, networkType, identityTypeToRetrieve, myRangeStart, myRangeEnd, emptyList, checkIfReportShouldBeDownloaded, 5) if (err != nil) { return err } return nil } // This function will download reports for messages/profiles/identities within our moderation range // It is called periodically if Moderator mode is enabled func DownloadMessageReportsForModeration(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMessageReportsForModeration called with invalid networkType: " + networkTypeString) } moderatorModeEnabled, moderatingAny, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToModerateRange() if (err != nil) { return err } if (moderatorModeEnabled == false){ // Moderator mode must have been disabled recently err := logger.AddLogEntry("Network", "DownloadMessageReportsForModeration called when user is not in Moderator mode.") if (err != nil) { return err } return nil } if (moderatingAny == false){ // Not moderating any messages // We don't need to download any message reports return nil } checkIfReportShouldBeDownloaded := func(reportHash [30]byte, reportedMessageHash [26]byte)(bool, error){ //TODO // see DownloadMessageReviewsToHost for some ideas on what restrictions to add return true, nil } emptyList := make([][26]byte, 0) emptyMap := make(map[[26]byte][10]byte) err = queryHosts.DownloadMessageReportsFromHosts(true, networkType, myInboxRangeStart, myInboxRangeEnd, emptyList, emptyMap, checkIfReportShouldBeDownloaded, 5) if (err != nil) { return err } return nil } // This function will download trusted viewable statuses for downloaded host identities/profiles. // Downloading these statuses is not needed if a user is able to determine these statuses themselves // // Hosts who are hosting host identities will download all host profile reviews, so they can calculate the viewable status themselves // Moderators who are moderating Host identities can also calculate the viewable status for host identities/profiles within their moderation range func DownloadHostViewableStatuses(networkType byte, knownOrUnknown string)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadHostViewableStatuses called with invalid networkType: " + networkTypeString) } if (knownOrUnknown != "Known" && knownOrUnknown != "Unknown"){ return errors.New("DownloadHostViewableStatuses called with invalid knownOrUnknown: " + knownOrUnknown) } hostModeEnabled, hostingAny, _, _, err := myRanges.GetMyIdentitiesToHostRange("Host") if (err != nil) { return err } if (hostModeEnabled == true && hostingAny == true){ // We are already hosting all reviews for host identities, so we can calculate the viewable status of host profiles ourselves // Thus, we don't need to download any trusted viewable statuses for host identitites/profiles return nil } allHostProfileIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Host") if (err != nil) { return err } getRelevantHostProfileIdentityHashesList := func()([][16]byte, error){ moderatorModeEnabled, moderatingAny, identityRangeStart, identityRangeEnd, err := myRanges.GetMyIdentitiesToModerateRange("Host") if (err != nil) { return nil, err } if (moderatorModeEnabled == false || moderatingAny == false){ // We are not moderating any host identities // We do not need to reduce the list at all return allHostProfileIdentityHashesList, nil } relevantHostProfileIdentityHashesList := make([][16]byte, 0) for _, hostIdentityHash := range allHostProfileIdentityHashesList{ isWithinMyRange, err := byteRange.CheckIfIdentityHashIsWithinRange(identityRangeStart, identityRangeEnd, hostIdentityHash) if (err != nil) { return nil, err } if (isWithinMyRange == true){ // We don't need to download this host's viewable status, because we can calculate it ourselves. continue } relevantHostProfileIdentityHashesList = append(relevantHostProfileIdentityHashesList, hostIdentityHash) } return relevantHostProfileIdentityHashesList, nil } relevantHostProfileIdentityHashesList, err := getRelevantHostProfileIdentityHashesList() if (err != nil) { return err } // Map Structure: Profile Hash -> Author identity hash profileHashesToRetrieveMap := make(map[[28]byte][16]byte) for _, identityHash := range relevantHostProfileIdentityHashesList{ exists, profileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(identityHash) if (err != nil) { return err } if (exists == false){ // Entry must have been deleted. Will be fixed automatically continue } if (len(profileHashesList) == 0){ continue } for _, profileHash := range profileHashesList{ profileMetadataExists, _, profileNetworkType, profileAuthor, _, profileIsDisabled, _, _, err := contentMetadata.GetProfileMetadata(profileHash) if (err != nil) { return err } if (profileMetadataExists == false){ // Profile is not downloaded, skip it. continue } if (profileNetworkType != networkType){ continue } if (profileAuthor != identityHash){ return errors.New("Database corrupt: Host identity profile hashes list contains profile of different identity") } if (profileIsDisabled == true){ continue } profileHashesToRetrieveMap[profileHash] = identityHash } } err = queryHosts.DownloadViewableStatusesFromHosts(false, networkType, knownOrUnknown, relevantHostProfileIdentityHashesList, profileHashesToRetrieveMap, 5) if (err != nil) { return err } return nil } // This function will download trusted viewable statuses for downloaded moderator profiles whose viewable statuses we cannot calculate ourselves // // We only need to do this is Moderator mode is enabled. // This is only needed so moderators can view Moderator profiles whose viewable status we are not aware of and be sure they are viewable // If Host mode is enabled, all of the moderator profiles we download, we will be able to determine the viewable status of. // Mate mode will not download any moderator profiles. // // We don't need to download viewable statuses for moderator identities // This is because in Moderator/Host mode, we should be downloading all moderator identity reviews // Thus, we should be able to determine their viewable statuses ourselves // func DownloadModeratorProfileViewableStatuses(networkType byte, knownOrUnknown string)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadModeratorProfileViewableStatuses called with invalid networkType: " + networkTypeString) } if (knownOrUnknown != "Known" && knownOrUnknown != "Unknown"){ return errors.New("DownloadModeratorProfileViewableStatuses called with invalid knownOrUnknown: " + knownOrUnknown) } moderatorModeEnabled, moderatingAnyModeratorIdentities, myIdentityRangeStart, myIdentityRangeEnd, err := myRanges.GetMyIdentitiesToModerateRange("Moderator") if (err != nil) { return err } if (moderatorModeEnabled == false){ // Moderator mode must have been disabled recently err := logger.AddLogEntry("Network", "DownloadModeratorProfileViewableStatuses called when user is not in Moderator mode.") if (err != nil) { return err } return nil } if (moderatingAnyModeratorIdentities == true){ minimumIdentityBound, maximumIdentityBound := byteRange.GetMinimumMaximumIdentityHashBounds() if (myIdentityRangeStart == minimumIdentityBound && myIdentityRangeEnd == maximumIdentityBound){ // We are moderating all Moderator identities, so we should be able to determine the viewable status for all moderator profiles // We don't need to download any moderator profile trusted viewable statuses return nil } } // Now we see if we are hosting all moderator identities hostModeEnabled, hostingAny, _, _, err := myRanges.GetMyIdentitiesToHostRange("Moderator") if (err != nil) { return err } if (hostModeEnabled == true && hostingAny == true){ // We are hosting all moderator identities // Thus, we are downloading all reviews for all moderator profiles // We don't need to download the viewable statuses for any moderator profiles return nil } allModeratorProfileIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Moderator") if (err != nil) { return err } // Map Structure: Profile Hash -> Author identity hash profileHashesToRetrieveMap := make(map[[28]byte][16]byte) for _, moderatorIdentityHash := range allModeratorProfileIdentityHashesList{ if (moderatingAnyModeratorIdentities == true){ // We see if moderator's identity is within our range // If it is, we don't have to get the status for profiles authored by this identity, because we can determine the viewable statuses ourselves isWithinRange, err := byteRange.CheckIfIdentityHashIsWithinRange(myIdentityRangeStart, myIdentityRangeEnd, moderatorIdentityHash) if (err != nil) { return err } if (isWithinRange == true){ continue } } exists, profileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(moderatorIdentityHash) if (err != nil) { return err } if (exists == false){ // Entry must have been deleted. Will be fixed automatically continue } if (len(profileHashesList) == 0){ continue } for _, profileHash := range profileHashesList{ profileMetadataExists, _, profileNetworkType, profileAuthor, _, profileIsDisabled, _, _, err := contentMetadata.GetProfileMetadata(profileHash) if (err != nil) { return err } if (profileMetadataExists == false){ // Profile is not downloaded, skip it. continue } if (profileNetworkType != networkType){ continue } if (profileAuthor != moderatorIdentityHash){ return errors.New("Database corrupt: Moderator identity profile hashes list contains profile of different identity") } if (profileIsDisabled == true){ continue } profileHashesToRetrieveMap[profileHash] = moderatorIdentityHash } } emptyList := make([][16]byte, 0) err = queryHosts.DownloadViewableStatusesFromHosts(false, networkType, knownOrUnknown, emptyList, profileHashesToRetrieveMap, 5) if (err != nil) { return err } return nil } // This function will download viewable statuses for mate identities/profiles we are browsing // If we are not in DesiresPruningMode, we will download the statuses for all downloaded profiles/identities which fulfill our criteria // If we are in DesiresPruningMode, we have to download the statuses for our matches profiles/identities one-at-a-time func DownloadMateViewableStatusesForBrowsing(networkType byte, knownOrUnknown string)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMateViewableStatusesForBrowsing called with invalid networkType: " + networkTypeString) } if (knownOrUnknown != "Known" && knownOrUnknown != "Unknown"){ return errors.New("DownloadMateViewableStatusesForBrowsing called with invalid knownOrUnknown: " + knownOrUnknown) } //TODO: Fix desiresPruningMode := false if (desiresPruningMode == true){ // We will retrieve consensus statuses for all users who could potentially be matches one-by-one // A user could potentially be a match if their newest profile fulfills our desires // Their newest profile may not be viewable, so we still need to retrieve statuses for all of their profiles which fulfill our desires myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Mate") if (err != nil) { return err } mateIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Mate") if (err != nil) { return err } for _, userIdentityHash := range mateIdentityHashesList{ if (myIdentityExists == true && userIdentityHash == myIdentityHash){ continue } userIsBlocked, _, _, _, err := myBlockedUsers.CheckIfUserIsBlocked(userIdentityHash) if (err != nil) { return err } if (userIsBlocked == true){ // We don't need to get the viewable statuses for blocked users continue } profileExists, profileVersion, newestProfileHash, _, _, newestRawProfileMap, err := profileStorage.GetNewestUserProfile(userIdentityHash, networkType) if (err != nil) { return err } if (profileExists == false){ // This user has no profiles, so we do not need to retrieve any viewable statuses for this user // The user's profile was probably deleted in the background continue } userIsDisabled, _, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(newestRawProfileMap, "Disabled") if (err != nil) { return err } if (userIsDisabled == true){ // The user will never be a match, because their newest profile is disabled // Thus, we do not need to retrieve viewable statuses for the user. continue } getAnyProfileAttributeFunction, err := calculatedAttributes.GetRetrieveAnyProfileAttributeIncludingCalculatedFunction(profileVersion, newestRawProfileMap) if (err != nil){ return err } profilePassesMyDesires, err := myMateDesires.CheckIfMateProfilePassesAllMyDesires(false, "", getAnyProfileAttributeFunction) if (err != nil) { return err } if (profilePassesMyDesires == false){ // The user's newest profile does not fulfill our desires // We don't need to get this profile's viewable status, because it does not fulfill our desires // We don't need to get any viewable statuses for this user, unless they are an outlier continue } anyExist, profileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(userIdentityHash) if (err != nil) { return err } if (anyExist == false){ // This user has no profiles continue } profileHashesToGetStatusesOfList := [][28]byte{newestProfileHash} for _, profileHash := range profileHashesList{ if (profileHash == newestProfileHash){ // We are already getting this profile's status continue } profileExists, profileBytes, err := badgerDatabase.GetUserProfile("Mate", profileHash) if (err != nil) { return err } if (profileExists == false){ continue } ableToRead, profileVersion, profileNetworkType, profileAuthor, _, profileIsDisabled, rawProfileMap, err := readProfiles.ReadProfile(false, profileBytes) if (err != nil) { return err } if (ableToRead == false){ return errors.New("Database corrupt: Contains malformed profile.") } if (profileNetworkType != networkType){ // Profile belongs to a different network type. continue } if (profileAuthor != userIdentityHash){ return errors.New("Database corrupt: Mate identity profile hashes list contains profile of different identity") } if (profileIsDisabled == true){ // We don't need to retrieve status for disabled profiles. // These profiles will always be viewable, unless their author is banned. continue } getAnyProfileAttributeFunction, err := calculatedAttributes.GetRetrieveAnyProfileAttributeIncludingCalculatedFunction(profileVersion, rawProfileMap) if (err != nil){ return err } profilePassesMyDesires, err := myMateDesires.CheckIfMateProfilePassesAllMyDesires(false, "", getAnyProfileAttributeFunction) if (err != nil) { return err } if (profilePassesMyDesires == false){ // We don't need to get this profile's viewable status, because it does not fulfill our desires // Thus, we will not show it to the user unless the author is an outlier // Outlier viewable statuses are retrieved in a different job continue } profileHashesToGetStatusesOfList = append(profileHashesToGetStatusesOfList, profileHash) } // Map Structure: Profile Hash -> User Identity Hash profileHashesToRetrieveMap := make(map[[28]byte][16]byte) for _, profileHash := range profileHashesToGetStatusesOfList{ profileHashesToRetrieveMap[profileHash] = userIdentityHash } identityHashList := [][16]byte{userIdentityHash} err = queryHosts.DownloadViewableStatusesFromHosts(false, networkType, knownOrUnknown, identityHashList, profileHashesToRetrieveMap, 1) if (err != nil) { return err } } return nil } // We will retrieve statuses for all identities whose newest profile fulfills our downloads criteria, and their profiles that fulfill our criteria myCriteriaExists, myCriteria, err := myMateCriteria.GetMyMateDownloadsCriteria() if (err != nil) { return err } allMateProfileIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes("Mate") if (err != nil) { return err } helpers.RandomizeListOrder(allMateProfileIdentityHashesList) identityHashesToRetrieveList := make([][16]byte, 0) // Map Structure: Profile Hash -> User Identity Hash profileHashesToRetrieveMap := make(map[[28]byte][16]byte) for _, userIdentityHash := range allMateProfileIdentityHashesList{ statusIsKnown, identityIsFunded, err := fundedStatus.GetIdentityIsFundedStatus(userIdentityHash, networkType) if (err != nil) { return err } if (statusIsKnown == true && identityIsFunded == false){ // The user's identity is not funded // This means that none of their profiles will be hosted by the network // We won't try to retrieve their viewable status, because their profile/identity will not exist on the network continue } profileExists, profileVersion, newestProfileHash, _, _, newestRawProfileMap, err := profileStorage.GetNewestUserProfile(userIdentityHash, networkType) if (err != nil) { return err } if (profileExists == false){ continue } userIsDisabled, _, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(newestRawProfileMap, "Disabled") if (err != nil) { return err } if (userIsDisabled == true){ // The user will never be a match, because their newest profile is disabled // Thus, we do not need to retrieve viewable statuses for the user. continue } if (myCriteriaExists == true){ criteriaIsValid, fulfillsCriteria, err := mateCriteria.CheckIfMateProfileFulfillsCriteria(true, profileVersion, newestRawProfileMap, myCriteria) if (err != nil) { return err } if (criteriaIsValid == false){ return errors.New("GetMyMateDownloadsCriteria returning invalid criteria.") } if (fulfillsCriteria == false){ // This user's newest profile does not fulfill our criteria. Skip them. // DatabaseJobs will delete all of their profiles (unless they are an outlier) continue } } anyExist, profileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(userIdentityHash) if (err != nil) { return err } if (anyExist == false){ // User has no profiles // They must have been deleted after earlier check continue } for _, profileHash := range profileHashesList{ if (profileHash == newestProfileHash){ // We already checked and know that this profile fulfills our criteria profileHashesToRetrieveMap[profileHash] = userIdentityHash continue } profileExists, profileBytes, err := badgerDatabase.GetUserProfile("Mate", profileHash) if (err != nil) { return err } if (profileExists == false){ continue } ableToRead, profileVersion, profileNetworkType, profileAuthor, _, profileIsDisabled, rawProfileMap, err := readProfiles.ReadProfile(false, profileBytes) if (err != nil) { return err } if (ableToRead == false){ return errors.New("Database corrupt: Contains malformed profile.") } if (profileNetworkType != networkType){ // Profile belongs to a different network type. continue } if (profileAuthor != userIdentityHash){ return errors.New("Database corrupt: Mate identity profile hashes list contains profile of different identity") } if (profileIsDisabled == true){ // We don't need to retrieve status for disabled profiles. They are always approved, unless identity is banned. continue } if (myCriteriaExists == true){ criteriaIsValid, fulfillsCriteria, err := mateCriteria.CheckIfMateProfileFulfillsCriteria(true, profileVersion, rawProfileMap, myCriteria) if (err != nil) { return err } if (criteriaIsValid == false){ return errors.New("GetMyMateDownloadsCriteria returning invalid criteria.") } if (fulfillsCriteria == false){ // This profile does not fulfill our criteria // We don't need to get its viewable status, because it will never be shown to the user // The exception is if the profile is an outlier, in which case we will retrieve its status using DownloadMateOutlierViewableStatuses // Another reason we skip the profile is that requesting its status would pose a risk of exposing our older criteria to the host continue } } profileHashesToRetrieveMap[profileHash] = userIdentityHash } identityHashesToRetrieveList = append(identityHashesToRetrieveList, userIdentityHash) } if (len(identityHashesToRetrieveList) == 0){ // No users fulfill our criteria. Nothing to do. return nil } err = queryHosts.DownloadViewableStatusesFromHosts(false, networkType, knownOrUnknown, identityHashesToRetrieveList, profileHashesToRetrieveMap, 5) if (err != nil) { return err } return nil } // This function will download trusted viewable statuses for Mate users whom are our outliers // Outliers are either contacts, likes, or chat recipients. // We download them so the user's client will have their downloaded profiles viewable, even if the users do not fulfill our criteria // These need to be downloaded one-by-one to prevent any host from learning our contacts/likes/chat recipients func DownloadMateOutlierViewableStatuses(outlierType string, networkType byte, knownOrUnknown string)error{ if (outlierType != "Contacts" && outlierType != "Likes" && outlierType != "ChatRecipients"){ return errors.New("DownloadMateOutlierViewableStatuses called with invalid outlierType: " + outlierType) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadMateOutlierViewableStatuses called with invalid networkType: " + networkTypeString) } if (knownOrUnknown != "Known" && knownOrUnknown != "Unknown"){ return errors.New("DownloadMateOutlierViewableStatuses called with invalid knownOrUnknown: " + knownOrUnknown) } //Outputs: // -[]string: List of identities // -error getMyOutlierIdentitiesList := func()([][16]byte, error){ if (outlierType == "Contacts"){ myContactIdentityHashesList, err := myContacts.GetMyContactsList("Mate") if (err != nil) { return nil, err } return myContactIdentityHashesList, nil } if (outlierType == "Likes"){ myLikedUsersList, err := myLikedUsers.GetMyLikedUsersList() if (err != nil) { return nil, err } return myLikedUsersList, nil } myIdentityExists, chatRecipientIdentityHashesList, err := myChatMessages.GetAllMyNonBlockedChatRecipients("Mate", networkType) if (err != nil) { return nil, err } if (myIdentityExists == false){ // Nothing to do, mate identity does not exist emptyList := make([][16]byte, 0) return emptyList, nil } return chatRecipientIdentityHashesList, nil } myOutlierIdentityHashesList, err := getMyOutlierIdentitiesList() if (err != nil) { return err } if (len(myOutlierIdentityHashesList) == 0){ return nil } helpers.RandomizeListOrder(myOutlierIdentityHashesList) for _, mateIdentityHash := range myOutlierIdentityHashesList{ statusIsKnown, identityIsFunded, err := fundedStatus.GetIdentityIsFundedStatus(mateIdentityHash, networkType) if (err != nil) { return err } if (statusIsKnown == true && identityIsFunded == false){ // The user's identity is not funded // This means that none of their profiles will be hosted by the network // We won't try to retrieve their viewable status, because their profile/identity will not exist on the network continue } identityHashList := [][16]byte{mateIdentityHash} // Outputs: // -map[[28]byte][16]byte: Map of profile hashes whose viewable status we want to retrieve (Profile Hash -> User identity Hash) // -error getProfileHashesToRetrieveMap := func()(map[[28]byte][16]byte, error){ profileExists, _, newestProfileHash, _, _, newestRawProfileMap, err := profileStorage.GetNewestUserProfile(mateIdentityHash, networkType) if (err != nil) { return nil, err } if (profileExists == false){ // We have no profiles downloaded for this user emptyMap := make(map[[28]byte][16]byte) return emptyMap, nil } isDisabled, _, err := readProfiles.GetFormattedProfileAttributeFromRawProfileMap(newestRawProfileMap, "Disabled") if (err != nil) { return nil, err } if (isDisabled == true){ // User's newest profile is disabled. // We don't need to download the profile's viewable status, because it cannot be banned emptyMap := make(map[[28]byte][16]byte) return emptyMap, nil } // We retrieve the viewable status for all of their downloaded profiles profileHashesToRetrieveMap := map[[28]byte][16]byte{ newestProfileHash: mateIdentityHash, } anyExist, identityProfileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(mateIdentityHash) if (err != nil) { return nil, err } if (anyExist == true){ for _, profileHash := range identityProfileHashesList{ profileHashesToRetrieveMap[profileHash] = mateIdentityHash } } return profileHashesToRetrieveMap, nil } profileHashesToRetrieveMap, err := getProfileHashesToRetrieveMap() if (err != nil) { return err } err = queryHosts.DownloadViewableStatusesFromHosts(false, networkType, knownOrUnknown, identityHashList, profileHashesToRetrieveMap, 1) if (err != nil) { return err } } return nil } // This function will download the cryptocurrency deposit history for all downloaded moderator identities // We download deposits for all moderators in bulk // Because users must download either all or no moderator profiles, there is no fingerprinting risk in downloading them in bulk // This should be called if Host/Moderator mode is enabled func DownloadModeratorIdentityDeposits(networkType byte, unknownOrKnown string)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("DownloadModeratorIdentityDeposits called with invalid networkType: " + networkTypeString) } err := queryHosts.DownloadModeratorAddressDepositsFromHosts(true, networkType, "Ethereum", unknownOrKnown, 6) if (err != nil) { return err } err = queryHosts.DownloadModeratorAddressDepositsFromHosts(true, networkType, "Cardano", unknownOrKnown, 6) if (err != nil) { return err } return nil }