// reviewStorage provides functions to manage stored moderator reviews // Reviews are created by moderators and determine the moderation verdict of messages, profiles, and identities. package reviewStorage import "seekia/internal/badgerDatabase" import "seekia/internal/contentMetadata" import "seekia/internal/encoding" import "seekia/internal/helpers" import "seekia/internal/identity" import "seekia/internal/messaging/readMessages" import "seekia/internal/moderation/readReviews" import "seekia/internal/mySettings" import "seekia/internal/network/appNetworkType/getAppNetworkType" import "seekia/internal/network/backgroundDownloads" import "seekia/internal/profiles/readProfiles" import "bytes" import "errors" func GetNumberOfStoredReviews()(int64, error){ numberOfReviews, err := badgerDatabase.GetNumberOfReviews() if (err != nil) { return 0, err } return numberOfReviews, nil } //Outputs: // -bool: Review is well formed // -error func AddReview(newReview []byte)(bool, error){ ableToRead, reviewHash, _, _, reviewerIdentityHash, _, reviewType, reviewedHash, _, _, err := readReviews.ReadReviewAndHash(true, newReview) if (err != nil){ return false, err } if (ableToRead == false){ // Review is malformed, do nothing // Host who sent review must be malicious. return false, nil } exists, _, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return false, err } if (exists == true){ // Review already imported, nothing to do. return true, nil } err = badgerDatabase.AddReview(reviewHash, newReview) if (err != nil) { return false, err } err = mySettings.SetSetting("ViewedContentNeedsRefreshYesNo", "Yes") if (err != nil) { return false, err } err = badgerDatabase.AddReviewerReviewHash(reviewerIdentityHash, reviewHash) if (err != nil) { return false, err } if (reviewType == "Identity"){ if (len(reviewedHash) != 16){ reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash) return false, errors.New("ReadReview returning invalid length reviewed Identity hash: " + reviewedHashHex) } reviewedIdentityHash := [16]byte(reviewedHash) err := badgerDatabase.AddIdentityReviewToList(reviewedIdentityHash, reviewHash) if (err != nil) { return false, err } return true, nil } if (reviewType == "Profile"){ if (len(reviewedHash) != 28){ reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash) return false, errors.New("ReadReview returning invalid length reviewed Profile hash: " + reviewedHashHex) } reviewedProfileHash := [28]byte(reviewedHash) err = badgerDatabase.AddProfileReviewToList(reviewedProfileHash, reviewHash) if (err != nil) { return false, err } return true, nil } if (reviewType == "Attribute"){ if (len(reviewedHash) != 27){ reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash) return false, errors.New("ReadReview returning invalid length reviewed Attribute hash: " + reviewedHashHex) } reviewedAttributeHash := [27]byte(reviewedHash) err = badgerDatabase.AddProfileAttributeReviewToList(reviewedAttributeHash, reviewHash) if (err != nil) { return false, err } return true, nil } if (reviewType == "Message"){ if (len(reviewedHash) != 26){ reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash) return false, errors.New("ReadReview returning invalid length reviewed Message hash: " + reviewedHashHex) } reviewedMessageHash := [26]byte(reviewedHash) err = badgerDatabase.AddMessageReviewToList(reviewedMessageHash, reviewHash) if (err != nil) { return false, err } return true, nil } return false, errors.New("ReadReview returning invalid reviewType: " + reviewType) } //Outputs: // -bool: Review exists // -[]byte: Review bytes // -map[string]string: Review map // -error func GetModeratorNewestIdentityReview(moderatorIdentityHash [16]byte, reviewedIdentityHash [16]byte, networkType byte)(bool, []byte, map[string]string, error){ isValid, err := identity.VerifyIdentityHash(moderatorIdentityHash, true, "Moderator") if (err != nil) { return false, nil, nil, err } if (isValid == false){ moderatorIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityHash[:]) return false, nil, nil, errors.New("GetModeratorNewestIdentityReview called with invalid moderatorIdentityHash: " + moderatorIdentityHashHex) } isValid, err = identity.VerifyIdentityHash(reviewedIdentityHash, false, "") if (err != nil) { return false, nil, nil, err } if (isValid == false){ reviewedIdentityHashHex := encoding.EncodeBytesToHexString(reviewedIdentityHash[:]) return false, nil, nil, errors.New("GetModeratorNewestIdentityReview called with invalid reviewedIdentityHash: " + reviewedIdentityHashHex) } isValid = helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, nil, nil, errors.New("GetModeratorNewestIdentityReview called with invalid networkType: " + networkTypeString) } anyReviewsExist, reviewHashesList, err := badgerDatabase.GetIdentityReviewsList(reviewedIdentityHash) if (err != nil) { return false, nil, nil, err } if (anyReviewsExist == false){ return false, nil, nil, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return false, nil, nil, err } if (exists == false){ // Review has been deleted. This missing entry will be removed automatically. continue } newReviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, newReviewObject) } verdictExists, reviewBytes, newestReviewMap, err := readReviews.GetModeratorNewestIdentityReviewFromReviewsList(reviewsList, moderatorIdentityHash, reviewedIdentityHash, networkType) if (err != nil) { return false, nil, nil, err } if (verdictExists == false){ return false, nil, nil, nil } return true, reviewBytes, newestReviewMap, nil } //Outputs: // -bool: Profile verdict exists (At least 1 attribute ban, or a full profile approve/ban review exists) // -string: Profile verdict "Ban"/"Approve" (incorporates full profile reviews and attribute bans) // -bool: Full profile review exists // -[]byte: Full profile review bytes // -map[string]string: Full profile review map // -map[[27]byte][]byte: Map of attribute approve reviews (Attribute hash -> Attribute review) // -map[[27]byte][]byte: Map of attribute ban reviews (Attribute hash -> Attribute review) // -error func GetModeratorNewestProfileReviews(moderatorIdentityHash [16]byte, profileHash [28]byte, profileNetworkType byte, profileAttributeHashesList [][27]byte)(bool, string, bool, []byte, map[string]string, map[[27]byte][]byte, map[[27]byte][]byte, error){ isValid := helpers.VerifyNetworkType(profileNetworkType) if (isValid == false){ profileNetworkTypeString := helpers.ConvertByteToString(profileNetworkType) return false, "", false, nil, nil, nil, nil, errors.New("GetModeratorNewestProfileReviews called with invalid profileNetworkType: " + profileNetworkTypeString) } fullProfileReviewExists, fullProfileReviewBytes, fullProfileReviewMap, fullProfileReviewVerdict, fullProfileReviewTime, err := getModeratorNewestFullProfileReview(moderatorIdentityHash, profileHash, profileNetworkType) if (err != nil) { return false, "", false, nil, nil, nil, nil, err } // Full profile approvals can be overwritten by an attribute ban of any attribute within the profile // We check for attribute reviews //Map Structure: Attribute hash -> Attribute review bytes approvedAttributesMap := make(map[[27]byte][]byte) //Map Structure: Attribute hash -> Attribute review bytes bannedAttributesMap := make(map[[27]byte][]byte) for _, attributeHash := range profileAttributeHashesList{ attributeReviewExists, attributeReviewType, attributeReviewBytes, _, attributeReviewVerdict, attributeReviewTime, err := GetModeratorNewestProfileAttributeReview(moderatorIdentityHash, attributeHash, profileNetworkType, false) if (err != nil){ return false, "", false, nil, nil, nil, nil, err } if (attributeReviewExists == false){ continue } if (attributeReviewType != "Attribute"){ return false, "", false, nil, nil, nil, nil, errors.New("GetModeratorNewestProfileAttributeReview returning non-attribute review when integrateFullProfileApprovals == false") } if (attributeReviewVerdict == "Approve"){ // Attribute approve reviews do not change full profile ban reviews approvedAttributesMap[attributeHash] = attributeReviewBytes continue } if (fullProfileReviewExists == true && fullProfileReviewVerdict == "Approve"){ if (fullProfileReviewTime > attributeReviewTime){ // This attribute ban was created before the moderator approved the full profile // It has therefore been overwritten continue } } bannedAttributesMap[attributeHash] = attributeReviewBytes } if (len(bannedAttributesMap) > 0){ // Profile is banned by moderator if (fullProfileReviewExists == true && fullProfileReviewVerdict == "Approve"){ // An attribute was banned, which overwrites the full profile review return true, "Ban", false, nil, nil, approvedAttributesMap, bannedAttributesMap, nil } return true, "Ban", fullProfileReviewExists, fullProfileReviewBytes, fullProfileReviewMap, approvedAttributesMap, bannedAttributesMap, nil } if (fullProfileReviewExists == false){ return false, "", false, nil, nil, approvedAttributesMap, bannedAttributesMap, nil } return true, fullProfileReviewVerdict, true, fullProfileReviewBytes, fullProfileReviewMap, approvedAttributesMap, bannedAttributesMap, nil } // This function does not integrate the moderator's attribute reviews //Outputs: // -bool: Review exists // -[]byte: Review bytes // -map[string]string: Review map // -string: Review verdict // -int64: Review creation time // -error func getModeratorNewestFullProfileReview(moderatorIdentityHash [16]byte, profileHash [28]byte, profileNetworkType byte)(bool, []byte, map[string]string, string, int64, error){ isValid, err := identity.VerifyIdentityHash(moderatorIdentityHash, true, "Moderator") if (err != nil) { return false, nil, nil, "", 0, err } if (isValid == false){ moderatorIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityHash[:]) return false, nil, nil, "", 0, errors.New("getModeratorNewestFullProfileReview called with invalid moderatorIdentityHash: " + moderatorIdentityHashHex) } isValid, err = readProfiles.VerifyProfileHash(profileHash, false, "", true, false) if (err != nil){ return false, nil, nil, "", 0, err } if (isValid == false){ profileHashHex := encoding.EncodeBytesToHexString(profileHash[:]) return false, nil, nil, "", 0, errors.New("getModeratorNewestFullProfileReview called with invalid profileHash: " + profileHashHex) } isValid = helpers.VerifyNetworkType(profileNetworkType) if (isValid == false){ profileNetworkTypeString := helpers.ConvertByteToString(profileNetworkType) return false, nil, nil, "", 0, errors.New("getModeratorNewestFullProfileReview called with invalid profileNetworkType: " + profileNetworkTypeString) } anyReviewsExist, reviewHashesList, err := badgerDatabase.GetProfileReviewsList(profileHash) if (err != nil) { return false, nil, nil, "", 0, err } if (anyReviewsExist == false){ return false, nil, nil, "", 0, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return false, nil, nil, "", 0, err } if (exists == false){ // Review has been deleted. This missing entry will be removed automatically. continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } reviewExists, reviewBytes, reviewMap, reviewVerdict, reviewTime, err := readReviews.GetModeratorNewestProfileReviewFromReviewsList(reviewsList, moderatorIdentityHash, profileHash, profileNetworkType) if (err != nil) { return false, nil, nil, "", 0, err } if (reviewExists == false){ return false, nil, nil, "", 0, nil } return true, reviewBytes, reviewMap, reviewVerdict, reviewTime, nil } //Outputs: // -bool: Review exists // -string: Review Type ("Profile"/"Attribute") // -[]byte: Review bytes // -map[string]string: Review map // -string: Review verdict // -int64: Time of review // -error func GetModeratorNewestProfileAttributeReview(moderatorIdentityHash [16]byte, attributeHash [27]byte, attributeNetworkType byte, integrateFullProfileApprovals bool)(bool, string, []byte, map[string]string, string, int64, error){ isValid, err := identity.VerifyIdentityHash(moderatorIdentityHash, true, "Moderator") if (err != nil) { return false, "", nil, nil, "", 0, err } if (isValid == false){ moderatorIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityHash[:]) return false, "", nil, nil, "", 0, errors.New("GetModeratorNewestProfileAttributeReview called with invalid moderatorIdentityHash: " + moderatorIdentityHashHex) } isValid, err = readProfiles.VerifyAttributeHash(attributeHash, false, "", false, false) if (err != nil) { return false, "", nil, nil, "", 0, err } if (isValid == false){ attributeHashHex := encoding.EncodeBytesToHexString(attributeHash[:]) return false, "", nil, nil, "", 0, errors.New("GetModeratorNewestProfileAttributeReview called with invalid attributeHash: " + attributeHashHex) } isValid = helpers.VerifyNetworkType(attributeNetworkType) if (isValid == false){ attributeNetworkTypeString := helpers.ConvertByteToString(attributeNetworkType) return false, "", nil, nil, "", 0, errors.New("GetModeratorNewestProfileAttributeReview called with invalid attributeNetworkType: " + attributeNetworkTypeString) } //Outputs: // -bool: Attribute review exists // -[]byte: Review bytes // -map[string]string: Review map // -string: Review verdict // -int64: Review creation time // -error getModeratorNewestAttributeReview := func()(bool, []byte, map[string]string, string, int64, error){ anyReviewsExist, reviewHashesList, err := badgerDatabase.GetProfileAttributeReviewsList(attributeHash) if (err != nil) { return false, nil, nil, "", 0, err } if (anyReviewsExist == false){ return false, nil, nil, "", 0, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return false, nil, nil, "", 0, err } if (exists == false){ // Review has been deleted. This missing entry will be removed automatically. continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } attributeReviewExists, attributeReviewBytes, attributeReviewMap, attributeReviewVerdict, attributeReviewTime, err := readReviews.GetModeratorNewestProfileAttributeReviewFromReviewsList(reviewsList, moderatorIdentityHash, attributeHash, attributeNetworkType) if (err != nil) { return false, nil, nil, "", 0, err } if (attributeReviewExists == false){ return false, nil, nil, "", 0, nil } return true, attributeReviewBytes, attributeReviewMap, attributeReviewVerdict, attributeReviewTime, nil } attributeReviewExists, attributeReviewBytes, attributeReviewMap, attributeReviewVerdict, attributeReviewTime, err := getModeratorNewestAttributeReview() if (err != nil) { return false, "", nil, nil, "", 0, err } if (integrateFullProfileApprovals == false){ if (attributeReviewExists == false){ return false, "", nil, nil, "", 0, nil } return true, "Attribute", attributeReviewBytes, attributeReviewMap, attributeReviewVerdict, attributeReviewTime, nil } if (attributeReviewExists == true && attributeReviewVerdict == "Approve"){ // The user approved the attribute // We don't have to check for full profile approvals, because they would not change this verdict return true, "Attribute", attributeReviewBytes, attributeReviewMap, "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 anyExist, attributeProfileHashesList, err := badgerDatabase.GetAttributeProfilesList(attributeHash) if (err != nil) { return false, "", nil, nil, "", 0, err } if (anyExist == true){ 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 a user bans a full profile, it does not change the status of any of their attribute approvals of the profile fullProfileReviewExists, fullProfileReviewBytes, fullProfileReviewMap, fullProfileVerdict, fullProfileVerdictTime, err := getModeratorNewestFullProfileReview(moderatorIdentityHash, profileHash, attributeNetworkType) if (err != nil){ return false, "", nil, nil, "", 0, err } if (fullProfileReviewExists == true && fullProfileVerdict == "Approve"){ if (attributeReviewExists == true && fullProfileVerdictTime < attributeReviewTime){ // The moderator banned the attribute after approving the full profile // The profile approval is therefore disregarded. continue } // The moderator approved the full profile after banning the attribute, thus, the attribute is approved return true, "Profile", fullProfileReviewBytes, fullProfileReviewMap, "Approve", fullProfileVerdictTime, nil } } } if (attributeReviewExists == true){ // We could not find any full profile approvals that undo the attribute ban return true, "Attribute", attributeReviewBytes, attributeReviewMap, "Ban", attributeReviewTime, nil } return false, "", nil, nil, "", 0, nil } //Outputs: // -bool: Review exists // -[]byte: Review bytes // -map[string]string: Review map // -error func GetModeratorNewestMessageReview(moderatorIdentityHash [16]byte, messageHash [26]byte, messageNetworkType byte, messageCipherKey [25]byte)(bool, []byte, map[string]string, error){ isValid, err := identity.VerifyIdentityHash(moderatorIdentityHash, true, "Moderator") if (err != nil) { return false, nil, nil, err } if (isValid == false){ moderatorIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityHash[:]) return false, nil, nil, errors.New("GetModeratorNewestMessageReview called with invalid moderatorIdentityHash: " + moderatorIdentityHashHex) } isValid = helpers.VerifyNetworkType(messageNetworkType) if (isValid == false){ messageNetworkTypeString := helpers.ConvertByteToString(messageNetworkType) return false, nil, nil, errors.New("GetModeratorNewestMessageReview called with invalid messageNetworkType: " + messageNetworkTypeString) } anyReviewsExist, reviewHashesList, err := badgerDatabase.GetMessageReviewsList(messageHash) if (err != nil) { return false, nil, nil, err } if (anyReviewsExist == false){ return false, nil, nil, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return false, nil, nil, err } if (exists == false){ // Review has been deleted. This missing entry will be removed automatically. continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } reviewExists, reviewBytes, reviewMap, _, err := readReviews.GetModeratorNewestMessageReviewFromReviewsList(reviewsList, moderatorIdentityHash, messageHash, messageNetworkType, messageCipherKey) if (err != nil) { return false, nil, nil, err } if (reviewExists == false){ return false, nil, nil, nil } return true, reviewBytes, reviewMap, nil } // This function returns all reviews by a provided moderator, omitting older reviews for the same reviewed hashes // Will omit None reviews //Outputs: // -[][]byte: List of reviews // -error func GetAllNewestReviewsCreatedByModerator(moderatorIdentityHash [16]byte, reviewType string, networkType byte)([][]byte, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return nil, errors.New("GetAllNewestReviewsCreatedByModerator called with invalid networkType: " + networkTypeString) } exists, reviewHashesList, err := badgerDatabase.GetReviewerReviewHashesList(moderatorIdentityHash, reviewType) if (err != nil) { return nil, err } if (exists == false){ emptyList := make([][]byte, 0) return emptyList, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return nil, err } if (exists == false){ // Review has been deleted. The missing entry will be removed automatically in the background. continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } reviewsMap, err := readReviews.GetNewestModeratorReviewsMapFromReviewsList(reviewsList, moderatorIdentityHash, networkType, true, reviewType) if (err != nil) { return nil, err } newestReviewsList := helpers.GetListOfMapValues(reviewsMap) return newestReviewsList, nil } // This function returns the number of ban advocates for a user // This returns all advocates, banned or not //Outputs: // -bool: Downloading required reviews and profiles // -int: Number of ban advocates // -error func GetNumberOfBanAdvocatesForIdentity(identityHash [16]byte, networkType byte)(bool, int, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, 0, errors.New("GetNumberOfBanAdvocatesForIdentity called with invalid networkType: " + networkTypeString) } appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { return false, 0, err } if (appNetworkType != networkType){ // We are not downloading reviews for this network type. return false, 0, nil } downloadingRequiredReviews, err := backgroundDownloads.CheckIfAppCanDetermineIdentityVerdicts(identityHash) if (err != nil) { return false, 0, err } if (downloadingRequiredReviews == false){ return false, 0, nil } banAdvocatesMap, err := GetIdentityBanAdvocatesMap(identityHash, networkType) if (err != nil) { return false, 0, err } numberOfBanAdvocates := len(banAdvocatesMap) return true, numberOfBanAdvocates, nil } //Outputs: // -bool: Message metadata is known // -bool: Downloading required reviews and profiles // -int: Number of reviewers // -error func GetNumberOfMessageReviewers(messageHash [26]byte)(bool, bool, int, error){ metadataExists, _, messageNetworkType, _, messageInbox, messageCipherKeyHash, err := contentMetadata.GetMessageMetadata(messageHash) if (err != nil) { return false, false, 0, err } if (metadataExists == false){ return false, false, 0, nil } downloadingRequiredData, err := backgroundDownloads.CheckIfAppCanDetermineMessageVerdict(messageNetworkType, messageInbox, true, messageHash) if (err != nil) { return false, false, 0, err } if (downloadingRequiredData == false){ return true, false, 0, nil } approveAdvocatesMap, banAdvocatesMap, err := GetMessageVerdictMaps(messageHash, messageNetworkType, messageCipherKeyHash) if (err != nil) { return false, false, 0, err } numberOfReviewers := len(approveAdvocatesMap) + len(banAdvocatesMap) return true, true, numberOfReviewers, nil } // This function will integrate attribute bans //Outputs: // -bool: Profile metadata is known // -bool: Downloading required reviews and profiles // -int: Number of reviewers // -error func GetNumberOfProfileReviewers(profileHash [28]byte)(bool, bool, int, error){ metadataExists, _, profileNetworkType, profileAuthor, _, profileIsDisabled, _, profileAttributeHashesMap, err := contentMetadata.GetProfileMetadata(profileHash) if (err != nil) { return false, false, 0, err } if (metadataExists == false){ return false, false, 0, nil } if (profileIsDisabled == true){ // Disabled profiles cannot be reviewed return true, true, 0, nil } appNetworkType, err := getAppNetworkType.GetAppNetworkType() if (err != nil) { return false, false, 0, err } if (appNetworkType != profileNetworkType){ // We are not downloading reviews for this network type. return true, false, 0, nil } downloadingRequiredReviews, err := backgroundDownloads.CheckIfAppCanDetermineIdentityVerdicts(profileAuthor) if (err != nil) { return false, false, 0, err } if (downloadingRequiredReviews == false){ return true, false, 0, nil } // profileAttributeHashesMap is a map whose values are the attribute hashes of the profile profileAttributeHashesList := helpers.GetListOfMapValues(profileAttributeHashesMap) approveAdvocatesMap, banAdvocatesMap, err := GetProfileVerdictMaps(profileHash, profileNetworkType, true, profileAttributeHashesList) if (err != nil) { return false, false, 0, err } numberOfReviewers := len(approveAdvocatesMap) + len(banAdvocatesMap) return true, true, numberOfReviewers, nil } // This function will go through all reviews for an identity and return the ban advocates //Outputs: // -map[[16]byte]int64: Ban advocate identity hash -> Time of ban // -error func GetIdentityBanAdvocatesMap(identityHash [16]byte, networkType byte)(map[[16]byte]int64, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return nil, errors.New("GetIdentityBanAdvocatesMap called with invalid networkType: " + networkTypeString) } exists, reviewHashesList, err := badgerDatabase.GetIdentityReviewsList(identityHash) if (err != nil) { return nil, err } if (exists == false){ emptyMap := make(map[[16]byte]int64) return emptyMap, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return nil, err } if (exists == false){ // Review must have been deleted. The missing review entry will be pruned automatically continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } banAdvocatesMap, err := readReviews.GetIdentityBanAdvocatesMapFromReviewsList(reviewsList, identityHash, networkType) if (err != nil) { return nil, err } return banAdvocatesMap, nil } // This function will go through all reviews for a profile and return the approve and ban advocates // If integrateAttributeBans == false, we will only check for full profile approvals/bans //Outputs: // -map[[16]byte]int64: Approve advocates map (Moderator identity hash -> Time of review) // -map[[16]byte]int64: Ban advocates map (Moderator identity hash -> Time of review) // -error func GetProfileVerdictMaps(profileHash [28]byte, profileNetworkType byte, integrateAttributeBans bool, attributeHashesList [][27]byte)(map[[16]byte]int64, map[[16]byte]int64, error){ isValid := helpers.VerifyNetworkType(profileNetworkType) if (isValid == false){ profileNetworkTypeString := helpers.ConvertByteToString(profileNetworkType) return nil, nil, errors.New("GetProfileVerdictMaps called with invalid profileNetworkType: " + profileNetworkTypeString) } exists, reviewHashesList, err := badgerDatabase.GetProfileReviewsList(profileHash) if (err != nil) { return nil, nil, err } if (exists == false){ emptyMapA := make(map[[16]byte]int64) emptyMapB := make(map[[16]byte]int64) return emptyMapA, emptyMapB, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return nil, nil, err } if (exists == false){ // Review has been deleted. The missing review entry will be pruned automatically. continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } approveAdvocatesMap, banAdvocatesMap, err := readReviews.GetProfileVerdictMapsFromReviewsList(reviewsList, profileHash, profileNetworkType) if (err != nil) { return nil, nil, err } if (integrateAttributeBans == false){ return approveAdvocatesMap, banAdvocatesMap, nil } // Now we check for attribute ban advocates // A ban of any attribute within the profile is equivalent to a profile ban // attributeHashesList is a list of all attribute hashes within the provided profile integrateProfileAttributeBans := func()error{ for _, attributeHash := range attributeHashesList{ // We have to check for full profile reviews of all profiles by the user that contain this attribute // Any of these full profile approvals are equivalent to approving all profile attributes getAttributeProfileHashesList := func()([][28]byte, error){ exists, attributeProfilesList, err := badgerDatabase.GetAttributeProfilesList(attributeHash) if (err != nil) { return nil, err } if (exists == false){ emptyList := make([][28]byte, 0) return emptyList, nil } // We can omit the current profileHash, because we are already checking it attributeProfileHashesList, _ := helpers.DeleteAllMatchingItemsFromList(attributeProfilesList, profileHash) return attributeProfileHashesList, nil } attributeProfileHashesList, err := getAttributeProfileHashesList() if (err != nil) { return err } _, attributeBanAdvocatesMap, err := GetProfileAttributeVerdictMaps(attributeHash, profileNetworkType, true, attributeProfileHashesList) if (err != nil) { return err } for moderatorIdentityHash, attributeBanTime := range attributeBanAdvocatesMap{ existingApproveTime, exists := approveAdvocatesMap[moderatorIdentityHash] if (exists == true){ if (existingApproveTime < attributeBanTime){ // The moderator banned a profile attribute after approving the profile delete(approveAdvocatesMap, moderatorIdentityHash) banAdvocatesMap[moderatorIdentityHash] = attributeBanTime // We don't have to check for any more attribute bans // They can only cause an full profile approval to change to a ban return nil } } } } return nil } err = integrateProfileAttributeBans() if (err != nil) { return nil, nil, err } return approveAdvocatesMap, banAdvocatesMap, nil } // This function will go through all reviews for a profile attribute and return the approve and ban advocates // This also finds full profile approve reviews and treats them as approvals for all attributes within the profile //Outputs: // -map[[16]byte]int64: Approve advocates map (Moderator identity hash -> Time of approval) // -map[[16]byte]int64: Ban advocates map (Moderator identity hash -> Time of ban) // -error func GetProfileAttributeVerdictMaps(attributeHash [27]byte, attributeNetworkType byte, integrateFullProfileApprovals bool, attributeProfileHashesList [][28]byte)(map[[16]byte]int64, map[[16]byte]int64, error){ isValid := helpers.VerifyNetworkType(attributeNetworkType) if (isValid == false){ attributeNetworkTypeString := helpers.ConvertByteToString(attributeNetworkType) return nil, nil, errors.New("GetProfileAttributeVerdictMaps called with invalid attributeNetworkType: " + attributeNetworkTypeString) } exists, reviewHashesList, err := badgerDatabase.GetProfileAttributeReviewsList(attributeHash) if (err != nil) { return nil, nil, err } if (exists == false){ emptyMapA := make(map[[16]byte]int64) emptyMapB := make(map[[16]byte]int64) return emptyMapA, emptyMapB, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return nil, nil, err } if (exists == false){ // Review has been deleted. The missing review entry will be pruned automatically continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } approveAdvocatesMap, banAdvocatesMap, err := readReviews.GetProfileAttributeVerdictMapsFromReviewsList(reviewsList, attributeHash, attributeNetworkType) if (err != nil) { return nil, nil, err } if (integrateFullProfileApprovals == false){ return approveAdvocatesMap, banAdvocatesMap, nil } // Now we check for full profile approve advocates // 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 integrateProfileApprovals := func()error{ 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 the user bans a profile's attribute after approving the full profile, the other attributes are still considered approved by the user fullProfileApproveAdvocatesMap, _, err := GetProfileVerdictMaps(profileHash, attributeNetworkType, false, nil) if (err != nil) { return err } for moderatorIdentityHash, fullProfileApproveTime := range fullProfileApproveAdvocatesMap{ existingBanTime, exists := banAdvocatesMap[moderatorIdentityHash] if (exists == true){ if (existingBanTime < fullProfileApproveTime){ // The moderator approved the full profile after banning the attribute delete(banAdvocatesMap, moderatorIdentityHash) approveAdvocatesMap[moderatorIdentityHash] = fullProfileApproveTime // We don't have to check for any more full profile approvals // They can only cause an attribute ban to change to an approval return nil } } } } return nil } err = integrateProfileApprovals() if (err != nil) { return nil, nil, err } return approveAdvocatesMap, banAdvocatesMap, nil } // This function will go through all reviews for a message and return the approve and ban advocates // We use the provided message CipherKeyHash to ensure the reviewers have seen the message //Outputs: // -map[[16]byte]int64: Approve advocate identity hash -> Time of approval // -map[[16]byte]int64: Ban advocate identity hash -> Time of ban // -error func GetMessageVerdictMaps(messageHash [26]byte, messageNetworkType byte, messageCipherKeyHash [25]byte)(map[[16]byte]int64, map[[16]byte]int64, error){ isValid := helpers.VerifyNetworkType(messageNetworkType) if (isValid == false){ messageNetworkTypeString := helpers.ConvertByteToString(messageNetworkType) return nil, nil, errors.New("GetMessageVerdictMaps called with invalid messageNetworkType: " + messageNetworkTypeString) } exists, reviewHashesList, err := badgerDatabase.GetMessageReviewsList(messageHash) if (err != nil) { return nil, nil, err } if (exists == false){ // There are no reviews for this message emptyMapA := make(map[[16]byte]int64) emptyMapB := make(map[[16]byte]int64) return emptyMapA, emptyMapB, nil } reviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return nil, nil, err } if (exists == false){ // Review has been deleted. Missing review entry will be pruned automatically continue } reviewObject := readReviews.ReviewWithHash{ ReviewHash: reviewHash, ReviewBytes: reviewBytes, } reviewsList = append(reviewsList, reviewObject) } approveAdvocatesMap, banAdvocatesMap, err := readReviews.GetMessageVerdictMapsFromReviewsList(reviewsList, messageHash, messageNetworkType, messageCipherKeyHash) if (err != nil) { return nil, nil, err } return approveAdvocatesMap, banAdvocatesMap, nil } // We use this function to find a valid message cipher key to decrypt a message for moderation // We use the message's CipherKeyHash to verify that the author of the review has seen the decrypted message // If the cipher key does not decrypt the message, then we know the author of the review and the message are malicious //Outputs: // -bool: Message cipher key found // -[32]byte: Message Cipher key // -error func GetMessageCipherKeyFromAnyReview(messageHash [26]byte, messageNetworkType byte, messageCipherKeyHash [25]byte)(bool, [32]byte, error){ isValid := helpers.VerifyNetworkType(messageNetworkType) if (isValid == false){ messageNetworkTypeString := helpers.ConvertByteToString(messageNetworkType) return false, [32]byte{}, errors.New("GetMessageCipherKeyFromAnyReview called with invalid messageNetworkType: " + messageNetworkTypeString) } exists, reviewHashesList, err := badgerDatabase.GetMessageReviewsList(messageHash) if (err != nil) { return false, [32]byte{}, err } if (exists == false){ return false, [32]byte{}, nil } if (len(reviewHashesList) == 0){ return false, [32]byte{}, nil } for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return false, [32]byte{}, err } if (exists == false){ // Message must have been deleted. // The missing reviewsList entry will be removed automatically. continue } ableToRead, _, reviewNetworkType, _, _, reviewType, reviewedHash, _, reviewMap, err := readReviews.ReadReview(false, reviewBytes) if (err != nil) { return false, [32]byte{}, err } if (ableToRead == false){ return false, [32]byte{}, errors.New("Database corrupt: Contains invalid review.") } if (reviewNetworkType != messageNetworkType){ // The author of this review is malicious // We will not try to get the message cipher key from the review, even if it exists // This is because we want all moderators to see the same messages to review, regardless of if they were previously on a different network. continue } if (reviewType != "Message") { return false, [32]byte{}, errors.New("Corrupt database: ReviewType does not match reviews list.") } areEqual := bytes.Equal(reviewedHash, messageHash[:]) if (areEqual == false){ return false, [32]byte{}, errors.New("Corrupt database: Reviewed message hash does not match reviews list.") } messageCipherKeyString, exists := reviewMap["MessageCipherKey"] if (exists == false) { return false, [32]byte{}, errors.New("Corrupt database: Contains review missing MessageCipherKey") } messageCipherKey, err := readMessages.ReadMessageCipherKeyHex(messageCipherKeyString) if (err != nil){ return false, [32]byte{}, errors.New("Corrupt database: Contains review with invalid MessageCipherKey: " + messageCipherKeyString + ". Reason: " + err.Error()) } currentMessageCipherKeyHash, err := readMessages.ConvertMessageCipherKeyToCipherKeyHash(messageCipherKey) if (err != nil){ return false, [32]byte{}, err } if (currentMessageCipherKeyHash != messageCipherKeyHash){ // Reviewer is malicious. continue } return true, messageCipherKey, nil } // No valid reviews found for message return false, [32]byte{}, nil }