// bannedModeratorConsensus provides functions to determine which moderators are banned package bannedModeratorConsensus import "seekia/internal/badgerDatabase" import "seekia/internal/encoding" import "seekia/internal/helpers" import "seekia/internal/identity" import "seekia/internal/moderation/moderatorRanking" import "seekia/internal/moderation/moderatorScores" import "seekia/internal/moderation/readReviews" import "seekia/internal/parameters/getParameters" import "slices" import "sync" import "errors" var bannedModeratorsListMutex sync.RWMutex // This list stores all banned moderators // It will not necessarily be up to date var bannedModeratorsList [][16]byte // This variable keeps track of the networkType of the bannedModeratorsList var bannedModeratorsListNetworkType byte // This function should be called whenever the app network type is changed. func UpdateBannedModeratorsList(networkType byte)error{ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return errors.New("UpdateBannedModeratorsList called with invalid networkType: " + networkTypeString) } bannedModeratorsListMutex.Lock() bannedModeratorsList = nil bannedModeratorsListNetworkType = 0 bannedModeratorsListMutex.Unlock() _, _, _, err := GetBannedModeratorsList(false, networkType) if (err != nil) { return err } return nil } //Outputs: // -bool: Moderator reviews and profiles are being downloaded (required to check if a moderator is banned) // -bool: Parameters exist // -bool: Moderator is banned // -error func GetModeratorIsBannedStatus(allowCache bool, identityHash [16]byte, networkType byte)(bool, bool, bool, error){ isValid, err := identity.VerifyIdentityHash(identityHash, true, "Moderator") if (err != nil) { return false, false, false, err } if (isValid == false){ identityHashHex := encoding.EncodeBytesToHexString(identityHash[:]) return false, false, false, errors.New("GetModeratorIsBannedStatus called with invalid identity hash: " + identityHashHex) } //TODO: Make it possible to retrieve the isBanned status without copying the cache list downloadingRequiredData, parametersExist, bannedModeratorsList, err := GetBannedModeratorsList(allowCache, networkType) if (err != nil) { return false, false, false, err } if (downloadingRequiredData == false){ return false, false, false, nil } if (parametersExist == false){ return true, false, false, nil } isBanned := slices.Contains(bannedModeratorsList, identityHash) return true, true, isBanned, nil } //Outputs: // -bool: Moderator reviews and profiles are being downloaded (required to calculate the banned moderators list) // -bool: Parameters exist // -[][16]byte: List of banned moderators // -error func GetBannedModeratorsList(allowCache bool, networkType byte)(bool, bool, [][16]byte, error){ //TODO: Check if parameters exist and moderator reviews/profiles are being downloaded isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, false, nil, errors.New("GetBannedModeratorsList called with invalid networkType: " + networkTypeString) } if (allowCache == true){ bannedModeratorsListMutex.RLock() if (bannedModeratorsList != nil && bannedModeratorsListNetworkType == networkType){ // Cache list has been generated and belongs to the specified network type. listCopy := slices.Clone(bannedModeratorsList) bannedModeratorsListMutex.RUnlock() return true, true, listCopy, nil } bannedModeratorsListMutex.RUnlock() } // We must generate a new banned moderators cache list rankedModeratorsList, err := moderatorRanking.GetRankedModeratorsList(networkType) if (err != nil) { return false, false, nil, err } // We start with the highest ranked moderator, and ban moderators as we traverse the list // If an identity is found to be banned, all of its reviews are ignored // Supermoderators are all-powerful moderators who can only be banned by higher-ranked supermoderators // They are appointed by the admin(s) parametersExist, supermoderatorsList, err := getParameters.GetSupermoderatorIdentityHashesList(networkType) if (err != nil) { return false, false, nil, err } if (parametersExist == false){ return true, false, nil, nil } // Supermoderators are added to the beginning of the ranked moderators list rankedModeratorsList = append(supermoderatorsList, rankedModeratorsList...) // Approved Moderators are the moderators who were not banned by anyone of a higher rank // Map structure: Approved Moderator Identity Hash -> Nothing approvedModeratorsMap := make(map[[16]byte]struct{}) // This map stores all banned identities // Map structure: Banned Moderator Identity Hash -> Nothing bannedModeratorsMap := make(map[[16]byte]struct{}) for _, moderatorIdentityHash := range rankedModeratorsList{ _, exists := bannedModeratorsMap[moderatorIdentityHash] if (exists == true){ // This moderator has been banned, none of their reviews are counted. Skip them. continue } // We make sure moderator is eligible moderatorIsSupermoderator := slices.Contains(supermoderatorsList, moderatorIdentityHash) if (moderatorIsSupermoderator == false){ // Moderator is not a supermoderator, so we make sure they are funded enough to be able to ban other moderators scoreIsKnown, _, scoreIsSufficient, canBanModerators, _, err := moderatorScores.GetModeratorIdentityScore(moderatorIdentityHash) if (err != nil) { return false, false, nil, err } if (scoreIsKnown == false || scoreIsSufficient == false || canBanModerators == false){ // This moderator is not funded, or cannot ban moderators continue } } // This moderator is approved (not banned by any moderators with a higher rank) approvedModeratorsMap[moderatorIdentityHash] = struct{}{} // We create a list of all identity reviews created by this moderator exists, reviewHashesList, err := badgerDatabase.GetReviewerReviewHashesList(moderatorIdentityHash, "Identity") if (err != nil) { return false, false, nil, err } if (exists == false){ // This moderator has not authored any reviews continue } moderatorReviewsList := make([]readReviews.ReviewWithHash, 0) for _, reviewHash := range reviewHashesList{ exists, reviewBytes, err := badgerDatabase.GetReview(reviewHash) if (err != nil) { return false, false, 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, } moderatorReviewsList = append(moderatorReviewsList, reviewObject) } moderatorReviewsMap, err := readReviews.GetNewestModeratorReviewsMapFromReviewsList(moderatorReviewsList, moderatorIdentityHash, networkType, true, "Identity") if (err != nil){ return false, false, nil, err } for reviewedIdentityHashString, _ := range moderatorReviewsMap{ reviewedIdentityHashBytes := []byte(reviewedIdentityHashString) if (len(reviewedIdentityHashBytes) != 16){ reviewedIdentityHashHex := encoding.EncodeBytesToHexString(reviewedIdentityHashBytes) return false, false, nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList returning non-identity reviewedHash: " + reviewedIdentityHashHex) } reviewedIdentityHash := [16]byte(reviewedIdentityHashBytes) // Each reviewedIdentityHash is an identity this moderator has banned identityType, err := identity.GetIdentityTypeFromIdentityHash(reviewedIdentityHash) if (err != nil) { reviewedIdentityHashHex := encoding.EncodeBytesToHexString(reviewedIdentityHash[:]) return false, false, nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList returning non-identity reviewedHash: " + reviewedIdentityHashHex) } if (identityType != "Moderator"){ continue } _, exists = approvedModeratorsMap[reviewedIdentityHash] if (exists == true){ //The current moderator cannot ban this moderator, because they are of a lower rank. continue } bannedModeratorsMap[reviewedIdentityHash] = struct{}{} } } // bannedModeratorsMap is a map whose keys are the identity hashes of banned moderators newBannedModeratorsList := helpers.GetListOfMapKeys(bannedModeratorsMap) bannedModeratorsListMutex.Lock() bannedModeratorsList = newBannedModeratorsList bannedModeratorsListNetworkType = networkType listCopy := slices.Clone(bannedModeratorsList) bannedModeratorsListMutex.Unlock() return true, true, listCopy, nil }