seekia/internal/moderation/bannedModeratorConsensus/bannedModeratorConsensus.go

245 lines
8.5 KiB
Go
Raw Permalink Normal View History

// 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
}