// myReviews provides functions to retrieve a user's moderator reviews // This package retrieves our reviews from the database. // myBroadcasts keeps a backup of a moderator's reviews that is retained if the database is deleted. package myReviews import "seekia/internal/badgerDatabase" import "seekia/internal/contentMetadata" import "seekia/internal/encoding" import "seekia/internal/helpers" import "seekia/internal/identity" import "seekia/internal/moderation/createReviews" import "seekia/internal/moderation/readReviews" import "seekia/internal/myIdentity" import "seekia/internal/network/myBroadcasts" import "seekia/internal/profiles/profileStorage" import "seekia/internal/profiles/readProfiles" import "slices" import "bytes" import "sync" import "errors" // This function will create and broadcast a new moderator review //Outputs: // -bool: My moderator identity exists // -error func CreateAndBroadcastMyReview(reviewMap map[string]string)(bool, error){ exists, myIdentityPublicKey, myIdentityPrivateKey, err := myIdentity.GetMyPublicPrivateIdentityKeys("Moderator") if (err != nil) { return false, err } if (exists == false){ return false, nil } newReview, err := createReviews.CreateReview(myIdentityPublicKey, myIdentityPrivateKey, reviewMap) if (err != nil) { return false, err } err = myBroadcasts.BroadcastMyReview(newReview) if (err != nil) { return false, err } ableToRead, _, _, _, _, newReviewType, _, _, _, err := readReviews.ReadReview(false, newReview) if (err != nil) { return false, err } if (ableToRead == false){ return false, errors.New("CreateReview is returning an invalid review.") } err = DeleteMyReviewsListCache(newReviewType) if (err != nil) { return false, err } return true, nil } // This function will return what a user's current verdict is for an identity //Outputs: // -bool: My Moderator Identity exists // -bool: I have banned the identity // -error func GetMyNewestIdentityModerationVerdict(identityHash [16]byte, networkType byte)(bool, bool, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, false, errors.New("GetMyNewestIdentityModerationVerdict called with invalid networkType: " + networkTypeString) } myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, false, err } if (myIdentityExists == false){ return false, false, nil } isValid, err = identity.VerifyIdentityHash(identityHash, false, "") if (err != nil) { return false, false, err } if (isValid == false){ identityHashHex := encoding.EncodeBytesToHexString(identityHash[:]) return false, false, errors.New("GetMyNewestIdentityModerationVerdict called with invalid identityHash: " + identityHashHex) } myReviewsList, err := getMyReviewsList(myIdentityHash, "Identity") if (err != nil) { return false, false, err } if (len(myReviewsList) == 0){ // Moderator has no reviews yet return true, false, nil } reviewExists, _, _, err := readReviews.GetModeratorNewestIdentityReviewFromReviewsList(myReviewsList, myIdentityHash, identityHash, networkType) if (err != nil){ return false, false, err } if (reviewExists == true){ // Review must be a ban review, because Identities can only be banned return true, true, nil } return true, false, nil } // This function will return what a user's current verdict is for a profile // This will optionally check for profile attribute bans which would undo a full profile approval // It will not check if we have banned the author identity //Outputs: // -bool: My Moderator identity exists // -bool: Profile is disabled // -bool: Profile metadata exists (needed to determine verdict) // -bool: I have reviewed the profile (review exists and is not None) // -string: Verdict ("Ban"/"Approve") // -error func GetMyNewestProfileModerationVerdict(profileHash [28]byte, integrateAttributeBans bool)(bool, bool, bool, bool, string, error){ myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, false, false, false, "", err } if (myIdentityExists == false){ return false, false, false, false, "", nil } _, profileIsDisabled, err := readProfiles.ReadProfileHashMetadata(profileHash) if (err != nil){ profileHashHex := encoding.EncodeBytesToHexString(profileHash[:]) return false, false, false, false, "", errors.New("GetMyNewestProfileModerationVerdict called with invalid profile hash: " + profileHashHex) } metadataExists, _, profileNetworkType, _, _, profileIsDisabledB, _, attributeHashesMap, err := contentMetadata.GetProfileMetadata(profileHash) if (err != nil) { return false, false, false, false, "", err } if (metadataExists == false){ return true, profileIsDisabled, false, false, "", nil } if (profileIsDisabled != profileIsDisabledB){ return false, false, false, false, "", errors.New("GetProfileMetadata returning different profileIsDisabled status than ReadProfileHashMetadata.") } if (profileIsDisabled == true){ // Profile is disabled. It cannot be reviewed. return true, true, true, false, "", nil } myProfileReviewsList, err := getMyReviewsList(myIdentityHash, "Profile") if (err != nil) { return false, false, false, false, "", err } fullProfileReviewExists, _, _, fullProfileVerdict, fullProfileVerdictTime, err := readReviews.GetModeratorNewestProfileReviewFromReviewsList(myProfileReviewsList, myIdentityHash, profileHash, profileNetworkType) if (err != nil){ return false, false, false, false, "", err } if (integrateAttributeBans == false){ if (fullProfileReviewExists == true){ return true, false, true, true, fullProfileVerdict, nil } return true, false, true, false, "", nil } if (fullProfileReviewExists == true && fullProfileVerdict == "Ban"){ // We do not have to check if we have banned any profile attributes, because we have already banned the full profile return true, false, true, true, "Ban", nil } // Now we see if we have banned any attributes belonging to this profile for _, attributeHash := range attributeHashesMap{ myIdentityExists, attributeMetadataExists, reviewExists, myVerdict, myVerdictTime, err := GetMyNewestProfileAttributeModerationVerdict(attributeHash, false) if (err != nil) { return false, false, false, false, "", err } if (myIdentityExists == false){ return false, false, false, false, "", errors.New("My identity not found after being found already.") } if (attributeMetadataExists == false){ // We don't have knowledge of profiles belonging to this profile in the attributeProfileHashesList entry in the database // This should not happen unless those entries were deleted // We will interpret this the same as no other profiles existing which contain this attribute continue } if (reviewExists == true && myVerdict == "Ban"){ if (fullProfileReviewExists == true && myVerdictTime < fullProfileVerdictTime){ // We have approved the full profile after we banned this attribute continue } // We have banned a profile attribute, thus, we have banned the profile. return true, false, true, true, "Ban", nil } } if (fullProfileReviewExists == true){ // We could not find any attribute bans that undo the full profile approval return true, false, true, true, "Approve", nil } return true, false, true, false, "", nil } // This function will return a user's newest profile attribute verdict // It will optionally check for full profile approvals, which it considers the same as approving all of the profile's attributes //Outputs: // -bool: My Moderator identity exists // -bool: Attribute metadata exists (this is needed to determine verdict) // -bool: I have reviewed the attribute (review exists and is not None) // -string: Verdict ("Ban"/"Approve") // -int64: Time of verdict // -error func GetMyNewestProfileAttributeModerationVerdict(attributeHash [27]byte, integrateFullProfileApprovals bool)(bool, bool, bool, string, int64, error){ myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, false, false, "", 0, err } if (myIdentityExists == false){ return false, false, false, "", 0, nil } isValid, err := readProfiles.VerifyAttributeHash(attributeHash, false, "", false, false) if (err != nil) { return false, false, false, "", 0, err } if (isValid == false){ attributeHashHex := encoding.EncodeBytesToHexString(attributeHash[:]) return false, false, false, "", 0, errors.New("GetMyNewestProfileAttributeModerationVerdict called with invalid attributeHash: " + attributeHashHex) } attributeMetadataExists, _, _, attributeNetworkType, attributeProfileHashesList, err := profileStorage.GetProfileAttributeMetadata(attributeHash) if (err != nil) { return false, false, false, "", 0, err } if (attributeMetadataExists == false){ // We cannot determine verdict. return true, false, false, "", 0, nil } myAttributeReviewsList, err := getMyReviewsList(myIdentityHash, "Attribute") if (err != nil) { return false, false, false, "", 0, err } attributeReviewExists, _, _, attributeReviewVerdict, attributeReviewTime, err := readReviews.GetModeratorNewestProfileAttributeReviewFromReviewsList(myAttributeReviewsList, myIdentityHash, attributeHash, attributeNetworkType) if (err != nil){ return false, false, false, "", 0, err } if (integrateFullProfileApprovals == false){ if (attributeReviewExists == false){ return true, true, false, "", 0, nil } return true, true, true, attributeReviewVerdict, attributeReviewTime, nil } if (attributeReviewExists == true && attributeReviewVerdict == "Approve"){ // We approved the attribute // We don't have to check for full profile approvals, because they would not change this verdict return true, true, true, "Approve", attributeReviewTime, nil } // Now we check for full profile approvals // A full profile approval is equivalent to an attribute approval for all attributes within the profile // attributeProfileHashesList is a list of all profile hashes for profiles which contain the attribute myProfileReviewsList, err := getMyReviewsList(myIdentityHash, "Profile") if (err != nil) { return false, false, false, "", 0, err } for _, profileHash := range attributeProfileHashesList{ // We are only checking for full profile approvals // We don't need to check for profile attribute bans, because we are only concerned with the provided attribute's status // A full profile approval approves all attributes. // If we ban a full profile, it does not change the status of any of our attribute approvals of the profile fullProfileReviewExists, _, _, fullProfileVerdict, fullProfileVerdictTime, err := readReviews.GetModeratorNewestProfileReviewFromReviewsList(myProfileReviewsList, myIdentityHash, profileHash, attributeNetworkType) if (err != nil){ return false, false, false, "", 0, err } if (fullProfileReviewExists == true && fullProfileVerdict == "Approve"){ if (attributeReviewExists == true && fullProfileVerdictTime < attributeReviewTime){ // We banned the attribute after approving the full profile // The attribute ban is therefore disregarded. continue } // We have approved the full profile after banning the attribute, thus, the attribute is approved return true, true, true, "Approve", fullProfileVerdictTime, nil } } if (attributeReviewExists == true){ // We could not find any full profile approvals that undo the attribute ban return true, true, true, "Ban", attributeReviewTime, nil } return true, true, false, "", 0, nil } // This function will return what a user's current verdict is for a message // -bool: My Moderator Identity exists // -bool: Message metadata exists (this is needed to determine our verdict) // -bool: I have reviewed the message (review exists and is not None) // -string: Verdict ("Ban"/"Approve") // -error func GetMyNewestMessageModerationVerdict(messageHash [26]byte)(bool, bool, bool, string, error){ myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, false, false, "", err } if (myIdentityExists == false){ return false, false, false, "", nil } messageMetadataExists, _, messageNetworkType, _, _, messageCipherKeyHash, err := contentMetadata.GetMessageMetadata(messageHash) if (err != nil){ return false, false, false, "", err } if (messageMetadataExists == false){ // We cannot determine our verdict without message metadata return true, false, false, "", nil } myReviewsList, err := getMyReviewsList(myIdentityHash, "Message") if (err != nil) { return false, false, false, "", err } if (len(myReviewsList) == 0){ // Moderator has no reviews yet return true, true, false, "", nil } reviewExists, _, _, reviewVerdict, err := readReviews.GetModeratorNewestMessageReviewFromReviewsList(myReviewsList, myIdentityHash, messageHash, messageNetworkType, messageCipherKeyHash) if (err != nil){ return false, false, false, "", err } if (reviewExists == false){ return true, true, false, "", nil } return true, true, true, reviewVerdict, nil } // This function returns a list of all the identity hashes that the user has banned //Outputs: // -bool: My Moderator identity exists // -[][16]byte: List of identity hashes user has banned // -error func GetMyBannedIdentityHashesList(networkType byte)(bool, [][16]byte, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, nil, errors.New("GetMyBannedIdentityHashesList called with invalid networkType: " + networkTypeString) } myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, nil, err } if (myIdentityExists == false){ return false, nil, nil } myIdentityReviewsMap, err := getMyNewestReviewsMap(myIdentityHash, "Identity", networkType) if (err != nil) { return false, nil, err } bannedIdentityHashesList := make([][16]byte, 0, len(myIdentityReviewsMap)) for reviewedIdentityHashString, _ := range myIdentityReviewsMap{ reviewedIdentityHashBytes := []byte(reviewedIdentityHashString) if (len(reviewedIdentityHashBytes) != 16){ reviewedIdentityHashHex := encoding.EncodeBytesToHexString(reviewedIdentityHashBytes) return false, nil, errors.New("getMyNewestReviewsMap returning invalid reviewedIdentityHash: " + reviewedIdentityHashHex) } reviewedIdentityHash := [16]byte(reviewedIdentityHashBytes) bannedIdentityHashesList = append(bannedIdentityHashesList, reviewedIdentityHash) } return true, bannedIdentityHashesList, nil } //Outputs: // -bool: My Moderator identity exists // -map[[28]byte]string: Map of profile hashes user has reviewed (Profile hash -> "Approve"/"Ban") // -error func GetMyReviewedProfileHashesMap(profileType string, networkType byte)(bool, map[[28]byte]string, error){ if (profileType != "Mate" && profileType != "Host" && profileType != "Moderator"){ return false, nil, errors.New("GetMyReviewedProfileHashesMap called with invalid profile type: " + profileType) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, nil, errors.New("GetMyReviewedProfileHashesMap called with invalid networkType: " + networkTypeString) } myIdentityExists, _, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, nil, err } if (myIdentityExists == false){ return false, nil, nil } allProfileHashesList, err := badgerDatabase.GetAllProfileHashes(profileType) if (err != nil) { return false, nil, err } //Map structure: Profile hash -> My Verdict myReviewedProfileHashesMap := make(map[[28]byte]string) for _, profileHash := range allProfileHashesList{ myIdentityExists, profileIsDisabled, profileMetadataExists, iHaveReviewed, myVerdict, err := GetMyNewestProfileModerationVerdict(profileHash, true) if (err != nil) { return false, nil, err } if (myIdentityExists == false){ return false, nil, errors.New("My identity not found after being found already.") } if (profileIsDisabled == true){ continue } if (profileMetadataExists == false){ continue } if (iHaveReviewed == true){ myReviewedProfileHashesMap[profileHash] = myVerdict } } return true, myReviewedProfileHashesMap, nil } //Outputs: // -bool: My Moderator identity exists // -map[[27]byte]string: Map Structure: Attribute Hash -> Verdict // -Map of profile attribute hashes user has reviewed (approved or banned) // -error func GetMyReviewedProfileAttributeHashesMap(networkType byte)(bool, map[[27]byte]string, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, nil, errors.New("GetMyReviewedProfileAttributeHashesMap called with invalid networkType: " + networkTypeString) } myIdentityExists, _, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, nil, err } if (myIdentityExists == false){ return false, nil, nil } allProfileAttributeHashesList, err := badgerDatabase.GetAllReviewedProfileAttributeHashes() if (err != nil) { return false, nil, err } //Map structure: Attribute hash -> My verdict myAttributeVerdictsMap := make(map[[27]byte]string) for _, attributeHash := range allProfileAttributeHashesList{ myIdentityExists, attributeMetadataExists, myReviewExists, myVerdict, _, err := GetMyNewestProfileAttributeModerationVerdict(attributeHash, true) if (err != nil) { return false, nil, err } if (myIdentityExists == false){ return false, nil, errors.New("My moderator identity not found after being found already.") } if (attributeMetadataExists == false){ // We cannot determine our verdict for this attribute continue } if (myReviewExists == true){ myAttributeVerdictsMap[attributeHash] = myVerdict } } return true, myAttributeVerdictsMap, nil } //Outputs: // -bool: My Moderator identity exists // -[][26]byte: List of message hashes user has reviewed (approved or banned) // -error func GetMyReviewedMessageHashesList(networkType byte)(bool, [][26]byte, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, nil, errors.New("GetMyReviewedMessageHashesList called with invalid networkType: " + networkTypeString) } myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, nil, err } if (myIdentityExists == false){ return false, nil, nil } myMessageReviewsMap, err := getMyNewestReviewsMap(myIdentityHash, "Message", networkType) if (err != nil) { return false, nil, err } myReviewedMessageHashesList := make([][26]byte, 0, len(myMessageReviewsMap)) for messageHashString, reviewBytes := range myMessageReviewsMap{ messageHashBytes := []byte(messageHashString) if (len(messageHashBytes) != 26){ messageHashHex := encoding.EncodeBytesToHexString(messageHashBytes) return false, nil, errors.New("getMyNewestReviewsMap returning reviewsMap with invalid messageHash map key: " + messageHashHex) } messageHashArray := [26]byte(messageHashBytes) ableToRead, _, reviewNetworkType, reviewerIdentityHash, _, reviewType, reviewedHash, _, _, err := readReviews.ReadReview(false, reviewBytes) if (err != nil) { return false, nil, err } if (ableToRead == false){ return false, nil, errors.New("getMyNewestReviewsMap returning invalid review.") } if (reviewNetworkType != networkType){ return false, nil, errors.New("getMyNewestReviewsMap returning review of different networkType.") } if (reviewerIdentityHash != myIdentityHash){ return false, nil, errors.New("getMyNewestReviewsMap list contains review by different reviewer") } if (reviewType != "Message"){ return false, nil, errors.New("getMyNewestReviewsMap returns different reviewType review: " + reviewType) } areEqual := bytes.Equal(reviewedHash, messageHashBytes) if (areEqual == false){ return false, nil, errors.New("getMyNewestReviewsMap returns key with different review reviewedHash") } myReviewedMessageHashesList = append(myReviewedMessageHashesList, messageHashArray) } return true, myReviewedMessageHashesList, nil } //Outputs: // -bool: User moderator identity found // -[][]byte: List of all reviews created by user, sorted from newest to oldest (Reviews with None verdict not included) // -error func GetMyNewestReviewsListSorted(reviewType string, networkType byte)(bool, [][]byte, error){ if (reviewType != "Identity" && reviewType != "Profile" && reviewType != "Attribute" && reviewType != "Message"){ return false, nil, errors.New("GetMyNewestReviewsListSorted called with invalid reviewType: " + reviewType) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, nil, errors.New("GetMyNewestReviewsListSorted called with invalid networkType: " + networkTypeString) } myIdentityExists, myIdentityHash, err := myIdentity.GetMyIdentityHash("Moderator") if (err != nil) { return false, nil, err } if (myIdentityExists == false){ return false, nil, nil } myNewestReviewsMap, err := getMyNewestReviewsMap(myIdentityHash, reviewType, networkType) if (err != nil) { return false, nil, err } type ReviewObject struct{ ReviewBroadcastTime int64 ReviewBytes []byte } newestReviewObjectsList := make([]ReviewObject, 0, len(myNewestReviewsMap)) for reviewedHashString, reviewBytes := range myNewestReviewsMap{ reviewedHashBytes := []byte(reviewedHashString) ableToRead, _, reviewNetworkType, reviewAuthorIdentityHash, reviewBroadcastTime, currentReviewType, currentReviewedHash, _, _, err := readReviews.ReadReview(false, reviewBytes) if (err != nil) { return false, nil, err } if (ableToRead == false){ return false, nil, errors.New("getMyNewestReviewsMap returning invalid review") } if (reviewNetworkType != networkType){ return false, nil, errors.New("getMyNewestReviewsMap returning review for different networkType.") } if (reviewAuthorIdentityHash != myIdentityHash){ return false, nil, errors.New("getMyNewestReviewsMap returning review by different author.") } if (currentReviewType != reviewType){ return false, nil, errors.New("getMyNewestReviewsMap returning review of a different reviewType.") } areEqual := bytes.Equal(reviewedHashBytes, currentReviewedHash) if (areEqual == false){ return false, nil, errors.New("getMyNewestReviewsMap returning map where reviewedHash key does not match reviewBytes's.") } reviewObject := ReviewObject{ ReviewBroadcastTime: reviewBroadcastTime, ReviewBytes: reviewBytes, } newestReviewObjectsList = append(newestReviewObjectsList, reviewObject) } compareReviewsFunction := func(reviewA ReviewObject, reviewB ReviewObject)int{ reviewABroadcastTime := reviewA.ReviewBroadcastTime reviewBBroadcastTime := reviewB.ReviewBroadcastTime if (reviewABroadcastTime == reviewBBroadcastTime){ return 0 } if (reviewABroadcastTime < reviewBBroadcastTime){ return 1 } return -1 } slices.SortFunc(newestReviewObjectsList, compareReviewsFunction) newestReviewsList := make([][]byte, 0, len(newestReviewObjectsList)) for _, reviewObject := range newestReviewObjectsList{ reviewBytes := reviewObject.ReviewBytes newestReviewsList = append(newestReviewsList, reviewBytes) } return true, newestReviewsList, nil } //Outputs: // -map[string]string: Reviewed Hash -> Newest Review Bytes // -error func getMyNewestReviewsMap(myIdentityHash [16]byte, reviewType string, networkType byte)(map[string][]byte, error){ if (reviewType != "Identity" && reviewType != "Profile" && reviewType != "Attribute" && reviewType != "Message"){ return nil, errors.New("getMyNewestReviewsMap called with invalid reviewType: " + reviewType) } reviewsList, err := getMyReviewsList(myIdentityHash, reviewType) if (err != nil) { return nil, err } newestReviewsMap, err := readReviews.GetNewestModeratorReviewsMapFromReviewsList(reviewsList, myIdentityHash, networkType, true, reviewType) if (err != nil) { return nil, err } return newestReviewsMap, nil } // We read our reviews into memory so we don't have to retrieve them from the database each time var myIdentityReviewsList []readReviews.ReviewWithHash var myIdentityReviewsListMutex sync.RWMutex var myProfileReviewsList []readReviews.ReviewWithHash var myProfileReviewsListMutex sync.RWMutex var myAttributeReviewsList []readReviews.ReviewWithHash var myAttributeReviewsListMutex sync.RWMutex var myMessageReviewsList []readReviews.ReviewWithHash var myMessageReviewsListMutex sync.RWMutex // We call this function whenever we add a new review to the database func DeleteMyReviewsListCache(reviewType string)error{ if (reviewType == "Identity"){ myIdentityReviewsListMutex.Lock() myIdentityReviewsList = nil myIdentityReviewsListMutex.Unlock() return nil } else if (reviewType == "Profile"){ myProfileReviewsListMutex.Lock() myProfileReviewsList = nil myProfileReviewsListMutex.Unlock() return nil } else if (reviewType == "Attribute"){ myAttributeReviewsListMutex.Lock() myAttributeReviewsList = nil myAttributeReviewsListMutex.Unlock() return nil } else if (reviewType == "Message"){ myMessageReviewsListMutex.Lock() myMessageReviewsList = nil myMessageReviewsListMutex.Unlock() return nil } return errors.New("DeleteMyReviewsListCache called with invalid reviewType: " + reviewType) } // This function returns our reviews for all networkTypes. // It does not copy each review object's review bytes, so returned review bytes should never be edited func getMyReviewsList(myIdentityHash [16]byte, reviewType string)([]readReviews.ReviewWithHash, error){ // We first attempt to return the list stored in memory if (reviewType == "Identity"){ myIdentityReviewsListMutex.RLock() if (myIdentityReviewsList != nil){ listCopy := slices.Clone(myIdentityReviewsList) myIdentityReviewsListMutex.RUnlock() return listCopy, nil } myIdentityReviewsListMutex.RUnlock() } else if (reviewType == "Profile"){ myProfileReviewsListMutex.RLock() if (myProfileReviewsList != nil){ listCopy := slices.Clone(myProfileReviewsList) myProfileReviewsListMutex.RUnlock() return listCopy, nil } myProfileReviewsListMutex.RUnlock() } else if (reviewType == "Attribute"){ myAttributeReviewsListMutex.RLock() if (myAttributeReviewsList != nil){ listCopy := slices.Clone(myAttributeReviewsList) myAttributeReviewsListMutex.RUnlock() return listCopy, nil } myAttributeReviewsListMutex.RUnlock() } else if (reviewType == "Message"){ myMessageReviewsListMutex.RLock() if (myMessageReviewsList != nil){ listCopy := slices.Clone(myMessageReviewsList) myMessageReviewsListMutex.RUnlock() return listCopy, nil } myMessageReviewsListMutex.RUnlock() } else { return nil, errors.New("getMyReviewsList called with invalid reviewType: " + reviewType) } // The list does not exist in memory // We will create a new list exists, myReviewHashesList, err := badgerDatabase.GetReviewerReviewHashesList(myIdentityHash, reviewType) if (err != nil) { return nil, err } if (exists == false){ emptyList := make([]readReviews.ReviewWithHash, 0) return emptyList, nil } newReviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range myReviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return nil, err } if (exists == false) { // Reviewer reviews list is outdated, will be updated automatically continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } newReviewsList = append(newReviewsList, reviewObject) } if (reviewType == "Identity"){ myIdentityReviewsListMutex.Lock() myIdentityReviewsList = newReviewsList listCopy := slices.Clone(myIdentityReviewsList) myIdentityReviewsListMutex.Unlock() return listCopy, nil } else if (reviewType == "Profile"){ myProfileReviewsListMutex.Lock() myProfileReviewsList = newReviewsList listCopy := slices.Clone(myProfileReviewsList) myProfileReviewsListMutex.Unlock() return listCopy, nil } else if (reviewType == "Attribute"){ myAttributeReviewsListMutex.Lock() myAttributeReviewsList = newReviewsList listCopy := slices.Clone(myAttributeReviewsList) myAttributeReviewsListMutex.Unlock() return listCopy, nil } // reviewType == "Message"){ myMessageReviewsListMutex.Lock() myMessageReviewsList = newReviewsList listCopy := slices.Clone(myMessageReviewsList) myMessageReviewsListMutex.Unlock() return listCopy, nil }