985 lines
35 KiB
Go
985 lines
35 KiB
Go
|
|
||
|
// readReviews provides functions to read/verify moderator reviews
|
||
|
|
||
|
package readReviews
|
||
|
|
||
|
//TODO: Remove ReviewHash from ReadReview function outputs and create a seperate ReadReviewHash function
|
||
|
// This will prevent hashing from being performed every time we read reviews
|
||
|
|
||
|
import "seekia/internal/allowedText"
|
||
|
import "seekia/internal/cryptography/blake3"
|
||
|
import "seekia/internal/cryptography/edwardsKeys"
|
||
|
import "seekia/internal/encoding"
|
||
|
import "seekia/internal/helpers"
|
||
|
import "seekia/internal/identity"
|
||
|
import "seekia/internal/messaging/readMessages"
|
||
|
|
||
|
import messagepack "github.com/vmihailenco/msgpack/v5"
|
||
|
|
||
|
import "bytes"
|
||
|
import "errors"
|
||
|
|
||
|
func VerifyReview(inputReview []byte)(bool, error){
|
||
|
|
||
|
ableToRead, _, _, _, _, _, _, _, _, err := ReadReview(true, inputReview)
|
||
|
if (err != nil){ return false, err }
|
||
|
if (ableToRead == false){
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
func VerifyReviewHash(inputHash [29]byte, reviewTypeProvided bool, expectedReviewType string)(bool, error){
|
||
|
|
||
|
if (reviewTypeProvided == true){
|
||
|
if (expectedReviewType != "Identity" && expectedReviewType != "Profile" && expectedReviewType != "Attribute" && expectedReviewType != "Message"){
|
||
|
return false, errors.New("VerifyReviewHash called with invalid expectedReviewType: " + expectedReviewType)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
reviewType, err := GetReviewTypeFromReviewHash(inputHash)
|
||
|
if (err != nil){
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
if (reviewTypeProvided == true){
|
||
|
if (reviewType != expectedReviewType){
|
||
|
return false, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -string: Review type
|
||
|
// -error
|
||
|
func GetReviewTypeFromReviewHash(inputHash [29]byte)(string, error){
|
||
|
|
||
|
metadataByte := inputHash[28]
|
||
|
|
||
|
if (metadataByte == 1){
|
||
|
return "Identity", nil
|
||
|
}
|
||
|
if (metadataByte == 2){
|
||
|
return "Profile", nil
|
||
|
}
|
||
|
if (metadataByte == 3){
|
||
|
return "Attribute", nil
|
||
|
}
|
||
|
if (metadataByte == 4){
|
||
|
return "Message", nil
|
||
|
}
|
||
|
|
||
|
reviewHashHex := encoding.EncodeBytesToHexString(inputHash[:])
|
||
|
|
||
|
return "", errors.New("GetReviewTypeFromReviewHash called with invalid reviewHash: " + reviewHashHex)
|
||
|
}
|
||
|
|
||
|
// This function computes the review hash and returns it
|
||
|
//Outputs:
|
||
|
// -bool: Able to read
|
||
|
// -[29]byte: Review Hash
|
||
|
// -int: Review version
|
||
|
// -byte: Network Type (1 == Mainnet, 2 == Testnet1)
|
||
|
// -[16]byte: Identity Hash of reviewer (Review author)
|
||
|
// -int64: Broadcast time (alleged, can be faked)
|
||
|
// -string: Review type
|
||
|
// -[]byte: Reviewed hash
|
||
|
// -string: Review Verdict
|
||
|
// -map[string]string: Review Map
|
||
|
// -error (returns err if there is a bug in the code)
|
||
|
func ReadReviewAndHash(verifyReview bool, inputReview []byte)(bool, [29]byte, int, byte, [16]byte, int64, string, []byte, string, map[string]string, error){
|
||
|
|
||
|
ableToRead, reviewVersion, reviewNetworkType, reviewAuthor, reviewBroadcastTime, reviewType, reviewedHash, reviewVerdict, reviewMap, err := ReadReview(verifyReview, inputReview)
|
||
|
if (err != nil) { return false, [29]byte{}, 0, 0, [16]byte{}, 0, "", nil, "", nil, err }
|
||
|
if (ableToRead == false){
|
||
|
return false, [29]byte{}, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewHashWithoutMetadataByte, err := blake3.GetBlake3HashAsBytes(28, inputReview)
|
||
|
if (err != nil) { return false, [29]byte{}, 0, 0, [16]byte{}, 0, "", nil, "", nil, err }
|
||
|
|
||
|
getReviewHashMetadataByte := func()(byte, error){
|
||
|
|
||
|
if (reviewType == "Identity"){
|
||
|
|
||
|
return 1, nil
|
||
|
|
||
|
} else if (reviewType == "Profile"){
|
||
|
|
||
|
return 2, nil
|
||
|
|
||
|
} else if (reviewType == "Attribute"){
|
||
|
|
||
|
return 3, nil
|
||
|
|
||
|
} else if (reviewType == "Message"){
|
||
|
|
||
|
return 4, nil
|
||
|
}
|
||
|
|
||
|
return 0, errors.New("ReadReview returning invalid reviewType: " + reviewType)
|
||
|
}
|
||
|
|
||
|
reviewHashMetadataByte, err := getReviewHashMetadataByte()
|
||
|
if (err != nil) { return false, [29]byte{}, 0, 0, [16]byte{}, 0, "", nil, "", nil, err }
|
||
|
|
||
|
reviewHashSlice := append(reviewHashWithoutMetadataByte, reviewHashMetadataByte)
|
||
|
|
||
|
reviewHash := [29]byte(reviewHashSlice)
|
||
|
|
||
|
return true, reviewHash, reviewVersion, reviewNetworkType, reviewAuthor, reviewBroadcastTime, reviewType, reviewedHash, reviewVerdict, reviewMap, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
//TODO: Encode Verdict as an int to save space
|
||
|
|
||
|
// This function does not compute the review hash, thus it is faster
|
||
|
//Outputs:
|
||
|
// -bool: Able to read
|
||
|
// -int: Review version
|
||
|
// -byte: Network Type (1 == Mainnet, 2 == Testnet1)
|
||
|
// -[16]byte: Identity Hash of reviewer (Review author)
|
||
|
// -int64: Broadcast time (alleged, can be faked)
|
||
|
// -string: Review type
|
||
|
// -[]byte: Reviewed hash
|
||
|
// -string: Review Verdict
|
||
|
// -map[string]string: Review Map
|
||
|
// -error (returns err if there is a bug in the code)
|
||
|
func ReadReview(verifyReview bool, inputReview []byte)(bool, int, byte, [16]byte, int64, string, []byte, string, map[string]string, error){
|
||
|
|
||
|
var reviewSlice []messagepack.RawMessage
|
||
|
|
||
|
err := messagepack.Unmarshal(inputReview, &reviewSlice)
|
||
|
if (err != nil) {
|
||
|
// Invalid review: Malformed messagepack
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
if (len(reviewSlice) != 2){
|
||
|
// Invalid review: Malformed messagepack
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
signatureMessagepack := reviewSlice[0]
|
||
|
reviewContentMessagepack := reviewSlice[1]
|
||
|
|
||
|
reviewSignature, err := encoding.DecodeRawMessagePackTo64ByteArray(signatureMessagepack)
|
||
|
if (err != nil) {
|
||
|
// Invalid review: Malformed messagepack
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewContentMap := make(map[int]messagepack.RawMessage)
|
||
|
|
||
|
err = encoding.DecodeMessagePackBytes(false, reviewContentMessagepack, &reviewContentMap)
|
||
|
if (err != nil) {
|
||
|
// Invalid review: Malformed messagepack
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewVersionMessagepack, exists := reviewContentMap[1]
|
||
|
if (exists == false){
|
||
|
// Invalid Review: Missing ReviewVersion
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewVersion, err := encoding.DecodeRawMessagePackToInt(reviewVersionMessagepack)
|
||
|
if (err != nil) {
|
||
|
// Invalid Review: Invalid review version
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
if (reviewVersion != 1){
|
||
|
// We cannot read this review. It was created by a newer version of Seekia.
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewNetworkTypeMessagepack, exists := reviewContentMap[2]
|
||
|
if (exists == false){
|
||
|
// Invalid Review: Missing NetworkType
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewNetworkType, err := encoding.DecodeRawMessagePackToByte(reviewNetworkTypeMessagepack)
|
||
|
if (err != nil){
|
||
|
// Invalid Review: Invalid network type messagepack
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
isValid := helpers.VerifyNetworkType(reviewNetworkType)
|
||
|
if (isValid == false){
|
||
|
// Invalid Review: Invalid NetworkType
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
authorIdentityKeyMessagepack, exists := reviewContentMap[3]
|
||
|
if (exists == false){
|
||
|
// Invalid Review: Missing IdentityKey
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
authorIdentityKey, err := encoding.DecodeRawMessagePackTo32ByteArray(authorIdentityKeyMessagepack)
|
||
|
if (err != nil) {
|
||
|
// Invalid Review: Invalid IdentityKey
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
if (verifyReview == true){
|
||
|
|
||
|
contentHash, err := blake3.Get32ByteBlake3Hash(reviewContentMessagepack)
|
||
|
if (err != nil) { return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, err }
|
||
|
|
||
|
isValid := edwardsKeys.VerifySignature(authorIdentityKey, reviewSignature, contentHash)
|
||
|
if (isValid == false) {
|
||
|
// Invalid review: Invalid signature
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
authorIdentityHash, err := identity.ConvertIdentityKeyToIdentityHash(authorIdentityKey, "Moderator")
|
||
|
if (err != nil) { return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, err }
|
||
|
|
||
|
broadcastTimeMessagepack, exists := reviewContentMap[4]
|
||
|
if (exists == false){
|
||
|
// Invalid Review: Missing BroadcastTime
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
broadcastTime, err := encoding.DecodeRawMessagePackToInt64(broadcastTimeMessagepack)
|
||
|
if (err != nil) {
|
||
|
// Invalid Review: Invalid BroadcastTime
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewedHashMessagepack, exists := reviewContentMap[5]
|
||
|
if (exists == false){
|
||
|
// Invalid Review: Missing ReviewedHash
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewedHash, err := encoding.DecodeRawMessagePackToBytes(reviewedHashMessagepack)
|
||
|
if (err != nil) {
|
||
|
// Invalid Review: Invalid ReviewedHash
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewType, err := helpers.GetReviewedTypeFromReviewedHash(reviewedHash)
|
||
|
if (err != nil){
|
||
|
// Invalid Review: Invalid ReviewedHash
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
verdictMessagepack, exists := reviewContentMap[6]
|
||
|
if (exists == false){
|
||
|
// Invalid Review: Missing Verdict
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
reviewVerdict, err := encoding.DecodeRawMessagePackToString(verdictMessagepack)
|
||
|
if (err != nil) {
|
||
|
// Invalid Review: Invalid Verdict
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
// These fields are not included within all reviews
|
||
|
var reason string
|
||
|
var messageCipherKeyBytes []byte
|
||
|
|
||
|
reasonMessagepack, exists := reviewContentMap[7]
|
||
|
if (exists == true){
|
||
|
|
||
|
err = messagepack.Unmarshal(reasonMessagepack, &reason)
|
||
|
if (err != nil) {
|
||
|
// Invalid Review: Invalid Reason
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (reviewType == "Message"){
|
||
|
|
||
|
messageCipherKeyMessagepack, exists := reviewContentMap[8]
|
||
|
if (exists == false){
|
||
|
// Invalid Review: Message review missing MessageCipherKey
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
err = messagepack.Unmarshal(messageCipherKeyMessagepack, &messageCipherKeyBytes)
|
||
|
if (err != nil) {
|
||
|
// Invalid Review: Invalid MessageCipherKey
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (verifyReview == true){
|
||
|
|
||
|
isValid := helpers.VerifyBroadcastTime(broadcastTime)
|
||
|
if (isValid == false){
|
||
|
// Invalid review: Review contains contains invalid BroadcastTime
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
if (reviewVerdict != "Ban" && reviewVerdict != "Approve" && reviewVerdict != "None"){
|
||
|
// Invalid review: Review contains invalid Verdict
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
if (reviewType == "Identity" && reviewVerdict == "Approve"){
|
||
|
// Invalid review: Review contains approve verdict for identity review
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
if (reviewType == "Identity"){
|
||
|
|
||
|
areEqual := bytes.Equal(reviewedHash, authorIdentityHash[:])
|
||
|
if (areEqual == true){
|
||
|
// Invalid review: Author cannot ban themselves.
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
//TODO: Verify ErrantProfiles, ErrantMessages, ErrantReviews, and ErrantAttributes
|
||
|
|
||
|
} else if (reviewType == "Message"){
|
||
|
|
||
|
if (len(messageCipherKeyBytes) != 32){
|
||
|
// Invalid review: Message review contains invalid messageCipherKey
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (reason != "") {
|
||
|
|
||
|
if (len(reason) > 200) {
|
||
|
// Invalid review: ReviewMap contains invalid reason
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
|
||
|
isAllowed := allowedText.VerifyStringIsAllowed(reason)
|
||
|
if (isAllowed == false){
|
||
|
// Invalid review: ReviewMap contains invalid reason
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, nil
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
reviewNetworkTypeString := helpers.ConvertByteToString(reviewNetworkType)
|
||
|
|
||
|
authorIdentityKeyHex := encoding.EncodeBytesToHexString(authorIdentityKey[:])
|
||
|
|
||
|
broadcastTimeString := helpers.ConvertInt64ToString(broadcastTime)
|
||
|
|
||
|
reviewedHashString, err := helpers.EncodeReviewedHashBytesToString(reviewedHash)
|
||
|
if (err != nil){
|
||
|
return false, 0, 0, [16]byte{}, 0, "", nil, "", nil, errors.New("GetReviewedTypeFromReviewedHash not verifying reviewedHash.")
|
||
|
}
|
||
|
|
||
|
reviewMap := map[string]string{
|
||
|
"ReviewVersion": "1",
|
||
|
"NetworkType": reviewNetworkTypeString,
|
||
|
"IdentityKey": authorIdentityKeyHex,
|
||
|
"BroadcastTime": broadcastTimeString,
|
||
|
"ReviewedHash": reviewedHashString,
|
||
|
"Verdict": reviewVerdict,
|
||
|
}
|
||
|
|
||
|
if (reason != ""){
|
||
|
reviewMap["Reason"] = reason
|
||
|
}
|
||
|
if (reviewType == "Message"){
|
||
|
|
||
|
messageCipherKey := encoding.EncodeBytesToHexString(messageCipherKeyBytes)
|
||
|
|
||
|
reviewMap["MessageCipherKey"] = messageCipherKey
|
||
|
}
|
||
|
|
||
|
return true, 1, reviewNetworkType, authorIdentityHash, broadcastTime, reviewType, reviewedHash, reviewVerdict, reviewMap, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
// This struct is used to represent a review and its hash
|
||
|
// This is useful, because we do not need to hash each review to retrieve its hash
|
||
|
type ReviewWithHash struct{
|
||
|
ReviewHash [29]byte
|
||
|
ReviewBytes []byte
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -map[[16]byte]int64: Ban advocates map (identity hash -> Time of ban)
|
||
|
// -error
|
||
|
func GetIdentityBanAdvocatesMapFromReviewsList(reviewsList []ReviewWithHash, identityHash [16]byte, networkType byte)(map[[16]byte]int64, error){
|
||
|
|
||
|
checkIfReviewIsValid := func(_ map[string]string)(bool, error){
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
approveAdvocatesMap, banAdvocatesMap, err := getModeratorNewestVerdictMapsFromReviewsList(reviewsList, "Identity", identityHash[:], networkType, checkIfReviewIsValid)
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (len(approveAdvocatesMap) != 0){
|
||
|
return nil, errors.New("getModeratorNewestVerdictMapsFromReviewsList returning non-empty approve advocates map for identity.")
|
||
|
}
|
||
|
|
||
|
return banAdvocatesMap, nil
|
||
|
}
|
||
|
|
||
|
// 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 GetMessageVerdictMapsFromReviewsList(reviewsList []ReviewWithHash, messageHash [26]byte, messageNetworkType byte, messageCipherKeyHash [25]byte)(map[[16]byte]int64, map[[16]byte]int64, error){
|
||
|
|
||
|
// We must check to make sure review has a valid messageCipherKey
|
||
|
// This will function as a proof that the reviewer has seen the message
|
||
|
// If the cipher key hash is valid but the key cannot decrypt the message, we know the reviewer and message author are both malicious
|
||
|
// We can detect these kinds of reviews and automatically ban the authors
|
||
|
|
||
|
verifyReviewIsValidFunction := func(reviewMap map[string]string)(bool, error){
|
||
|
|
||
|
isValid, err := VerifyMessageReviewCipherKey(reviewMap, messageCipherKeyHash)
|
||
|
|
||
|
return isValid, err
|
||
|
}
|
||
|
|
||
|
approveAdvocatesMap, banAdvocatesMap, err := getModeratorNewestVerdictMapsFromReviewsList(reviewsList, "Message", messageHash[:], messageNetworkType, verifyReviewIsValidFunction)
|
||
|
if (err != nil) { return nil, nil, err }
|
||
|
|
||
|
return approveAdvocatesMap, banAdvocatesMap, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
// This function is used to verify that message reviews are valid
|
||
|
func VerifyMessageReviewCipherKey(reviewMap map[string]string, messageCipherKeyHash [25]byte)(bool, error){
|
||
|
|
||
|
reviewMessageCipherKey, exists := reviewMap["MessageCipherKey"]
|
||
|
if (exists == false) {
|
||
|
return false, errors.New("VerifyMessageReviewCipherKey called with message review missing MessageCipherKey")
|
||
|
}
|
||
|
|
||
|
reviewMessageCipherKeyArray, err := readMessages.ReadMessageCipherKeyHex(reviewMessageCipherKey)
|
||
|
if (err != nil){
|
||
|
return false, errors.New("VerifyMessageReviewCipherKey called with message review containing invalid MessageCipherKey: " + reviewMessageCipherKey + ". Reason: " + err.Error())
|
||
|
}
|
||
|
|
||
|
reviewCipherKeyHash, err := readMessages.ConvertMessageCipherKeyToCipherKeyHash(reviewMessageCipherKeyArray)
|
||
|
if (err != nil){ return false, err }
|
||
|
|
||
|
if (reviewCipherKeyHash != messageCipherKeyHash){
|
||
|
// Reviewer must be malicious. Skip review.
|
||
|
return false, nil
|
||
|
}
|
||
|
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
//Outputs:
|
||
|
// -map[[16]byte]int64: Approve advocates map (identity hash -> Time of approve)
|
||
|
// -map[[16]byte]int64: Ban advocates map (identity hash -> Time of ban)
|
||
|
// -error
|
||
|
func GetProfileVerdictMapsFromReviewsList(reviewsList []ReviewWithHash, profileHash [28]byte, profileNetworkType byte)(map[[16]byte]int64, map[[16]byte]int64, error){
|
||
|
|
||
|
verifyReviewIsValid := func(_ map[string]string)(bool, error){
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
approveAdvocatesMap, banAdvocatesMap, err := getModeratorNewestVerdictMapsFromReviewsList(reviewsList, "Profile", profileHash[:], profileNetworkType, verifyReviewIsValid)
|
||
|
if (err != nil) { return nil, nil, err }
|
||
|
|
||
|
return approveAdvocatesMap, banAdvocatesMap, nil
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -map[[16]byte]int64: Approve advocates map (identity hash -> Time of approval)
|
||
|
// -map[[16]byte]int64: Ban advocates map (identity hash -> Time of ban)
|
||
|
// -error
|
||
|
func GetProfileAttributeVerdictMapsFromReviewsList(reviewsList []ReviewWithHash, attributeHash [27]byte, attributeNetworkType byte)(map[[16]byte]int64, map[[16]byte]int64, error){
|
||
|
|
||
|
verifyReviewIsValid := func(_ map[string]string)(bool, error){
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
approveAdvocatesMap, banAdvocatesMap, err := getModeratorNewestVerdictMapsFromReviewsList(reviewsList, "Attribute", attributeHash[:], attributeNetworkType, verifyReviewIsValid)
|
||
|
if (err != nil) { return nil, nil, err }
|
||
|
|
||
|
return approveAdvocatesMap, banAdvocatesMap, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
// This function takes a list of reviews and a reviewedHash and returns maps for each moderator's newest verdict ("Approve"/"Ban")
|
||
|
// This requires keeping track of the newest review from each moderator. Moderators can undo reviews by using the None verdict.
|
||
|
//Outputs:
|
||
|
// -map[[16]byte]int64: Approve advocates map (identity hash -> Time approve review was submitted)
|
||
|
// -map[[16]byte]int64: Ban advocates map (identity hash -> Time ban review was submitted)
|
||
|
// -error
|
||
|
func getModeratorNewestVerdictMapsFromReviewsList(reviewsList []ReviewWithHash, reviewType string, reviewedHash []byte, networkType byte, verifyReviewIsValid func(map[string]string)(bool, error))(map[[16]byte]int64, map[[16]byte]int64, error){
|
||
|
|
||
|
if (reviewType != "Identity" && reviewType != "Profile" && reviewType != "Attribute" && reviewType != "Message"){
|
||
|
return nil, nil, errors.New("getModeratorNewestVerdictMapsFromReviewsList called with invalid ReviewType: " + reviewType)
|
||
|
}
|
||
|
|
||
|
reviewedType, err := helpers.GetReviewedTypeFromReviewedHash(reviewedHash)
|
||
|
if (err != nil){
|
||
|
reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
|
||
|
return nil, nil, errors.New("getModeratorNewestVerdictMapsFromReviewsList called with invalid reviewedHash: " + reviewedHashHex)
|
||
|
}
|
||
|
if (reviewedType != reviewType){
|
||
|
return nil, nil, errors.New("getModeratorNewestVerdictMapsFromReviewsList called with reviewType that does not match reviewedHash")
|
||
|
}
|
||
|
|
||
|
isValid := helpers.VerifyNetworkType(networkType)
|
||
|
if (isValid == false){
|
||
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
||
|
return nil, nil, errors.New("getModeratorNewestVerdictMapsFromReviewsList called with invalid networkType: " + networkTypeString)
|
||
|
}
|
||
|
|
||
|
// This stores info about a review
|
||
|
type ReviewInfoObject struct{
|
||
|
ReviewHash [29]byte
|
||
|
BroadcastTime int64
|
||
|
Verdict string
|
||
|
}
|
||
|
|
||
|
// This map stores info about each reviewer's newest review for the provided reviewedHash
|
||
|
// Map Structure: Reviewer Identity Hash -> Newest Review Info Object
|
||
|
newestReviewsMap := make(map[[16]byte]ReviewInfoObject)
|
||
|
|
||
|
for _, reviewWithHash := range reviewsList{
|
||
|
|
||
|
reviewHash := reviewWithHash.ReviewHash
|
||
|
reviewBytes := reviewWithHash.ReviewBytes
|
||
|
|
||
|
ableToRead, _, reviewNetworkType, reviewAuthor, reviewBroadcastTime, currentReviewType, currentReviewedHash, reviewVerdict, reviewMap, err := ReadReview(false, reviewBytes)
|
||
|
if (err != nil) { return nil, nil, err }
|
||
|
if (ableToRead == false){
|
||
|
return nil, nil, errors.New("getModeratorNewestVerdictMapsFromReviewsList called with invalid review.")
|
||
|
}
|
||
|
if (reviewNetworkType != networkType){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if (currentReviewType != reviewType){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
areEqual := bytes.Equal(currentReviewedHash, reviewedHash)
|
||
|
if (areEqual == false){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
isValid, err := verifyReviewIsValid(reviewMap)
|
||
|
if (err != nil) { return nil, nil, err }
|
||
|
if (isValid == false){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
existingNewestReviewInfoObject, exists := newestReviewsMap[reviewAuthor]
|
||
|
if (exists == true){
|
||
|
|
||
|
existingNewestReviewBroadcastTime := existingNewestReviewInfoObject.BroadcastTime
|
||
|
|
||
|
if (existingNewestReviewBroadcastTime == reviewBroadcastTime){
|
||
|
// The reviewer must be malicious, or their computer clock was skewed somehow
|
||
|
// We compare the review hashes to see which review to use
|
||
|
// This is necessary so that a moderator's newest review will always be calculated the same way by all Seekia clients.
|
||
|
|
||
|
existingNewestReviewHash := existingNewestReviewInfoObject.ReviewHash
|
||
|
|
||
|
compareValue := bytes.Compare(reviewHash[:], existingNewestReviewHash[:])
|
||
|
if (compareValue == 0){
|
||
|
// This should not happen, because this function should be called with a list of unique reviews
|
||
|
return nil, nil, errors.New("getModeratorNewestVerdictMapsFromReviewsList called with reviewsList containing duplicate review.")
|
||
|
}
|
||
|
|
||
|
if (compareValue == -1){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
} else if (existingNewestReviewBroadcastTime > reviewBroadcastTime){
|
||
|
// This review was overwritten/replaced by a newer review
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
newNewestReviewInfoObject := ReviewInfoObject{
|
||
|
ReviewHash: reviewHash,
|
||
|
BroadcastTime: reviewBroadcastTime,
|
||
|
Verdict: reviewVerdict,
|
||
|
}
|
||
|
|
||
|
newestReviewsMap[reviewAuthor] = newNewestReviewInfoObject
|
||
|
}
|
||
|
|
||
|
// Map Structure: Identity hash -> Time of approve review
|
||
|
approveAdvocatesMap := make(map[[16]byte]int64)
|
||
|
|
||
|
// Map Structure: Identity hash -> Time of ban review
|
||
|
banAdvocatesMap := make(map[[16]byte]int64)
|
||
|
|
||
|
for reviewerIdentityHash, reviewerNewestReviewInfoObject := range newestReviewsMap{
|
||
|
|
||
|
newestReviewVerdict := reviewerNewestReviewInfoObject.Verdict
|
||
|
reviewBroadcastTime := reviewerNewestReviewInfoObject.BroadcastTime
|
||
|
|
||
|
if (newestReviewVerdict == "Approve"){
|
||
|
|
||
|
approveAdvocatesMap[reviewerIdentityHash] = reviewBroadcastTime
|
||
|
|
||
|
continue
|
||
|
|
||
|
} else if (newestReviewVerdict == "Ban"){
|
||
|
|
||
|
banAdvocatesMap[reviewerIdentityHash] = reviewBroadcastTime
|
||
|
|
||
|
continue
|
||
|
|
||
|
} else if (newestReviewVerdict == "None"){
|
||
|
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
return nil, nil, errors.New("ReadReview returning invalid verdict: " + newestReviewVerdict)
|
||
|
}
|
||
|
|
||
|
return approveAdvocatesMap, banAdvocatesMap, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Review exists (this means the moderator has banned the identity)
|
||
|
// -[]byte: Newest Review bytes
|
||
|
// -map[string]string: Newest Review map
|
||
|
// -error
|
||
|
func GetModeratorNewestIdentityReviewFromReviewsList(inputReviewsList []ReviewWithHash, moderatorIdentityHash [16]byte, reviewedIdentityHash [16]byte, networkType byte)(bool, []byte, map[string]string, error){
|
||
|
|
||
|
isValid := helpers.VerifyNetworkType(networkType)
|
||
|
if (isValid == false){
|
||
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
||
|
return false, nil, nil, errors.New("GetModeratorNewestIdentityReviewFromReviewsList called with invalid networkType: " + networkTypeString)
|
||
|
}
|
||
|
|
||
|
checkIfReviewIsValid := func(_ map[string]string)(bool, error){
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
reviewExists, reviewBytes, reviewMap, _, _, err := getNewestModeratorReviewFromReviewsList(inputReviewsList, moderatorIdentityHash, "Identity", reviewedIdentityHash[:], networkType, checkIfReviewIsValid)
|
||
|
|
||
|
return reviewExists, reviewBytes, reviewMap, err
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Review exists
|
||
|
// -[]byte: Newest review bytes
|
||
|
// -map[string]string: Newest review map
|
||
|
// -string: Review verdict
|
||
|
// -int64: Time of review
|
||
|
// -error
|
||
|
func GetModeratorNewestProfileReviewFromReviewsList(inputReviewsList []ReviewWithHash, moderatorIdentityHash [16]byte, profileHash [28]byte, profileNetworkType byte)(bool, []byte, map[string]string, string, int64, error){
|
||
|
|
||
|
isValid := helpers.VerifyNetworkType(profileNetworkType)
|
||
|
if (isValid == false){
|
||
|
profileNetworkTypeString := helpers.ConvertByteToString(profileNetworkType)
|
||
|
return false, nil, nil, "", 0, errors.New("GetModeratorNewestProfileReviewFromReviewsList called with invalid profileNetworkType: " + profileNetworkTypeString)
|
||
|
}
|
||
|
|
||
|
verifyReviewIsValidFunction := func(_ map[string]string)(bool, error){
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
reviewExists, reviewBytes, reviewMap, reviewVerdict, reviewBroadcastTime, err := getNewestModeratorReviewFromReviewsList(inputReviewsList, moderatorIdentityHash, "Profile", profileHash[:], profileNetworkType, verifyReviewIsValidFunction)
|
||
|
|
||
|
return reviewExists, reviewBytes, reviewMap, reviewVerdict, reviewBroadcastTime, err
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Review exists
|
||
|
// -[]byte: Newest review bytes
|
||
|
// -map[string]string: Newest review map
|
||
|
// -string: Newest Review verdict
|
||
|
// -int64: Time of review
|
||
|
// -error
|
||
|
func GetModeratorNewestProfileAttributeReviewFromReviewsList(inputReviewsList []ReviewWithHash, moderatorIdentityHash [16]byte, attributeHash [27]byte, attributeNetworkType byte)(bool, []byte, map[string]string, string, int64, error){
|
||
|
|
||
|
isValid := helpers.VerifyNetworkType(attributeNetworkType)
|
||
|
if (isValid == false){
|
||
|
attributeNetworkTypeString := helpers.ConvertByteToString(attributeNetworkType)
|
||
|
return false, nil, nil, "", 0, errors.New("GetModeratorNewestProfileAttributeReviewFromReviewsList called with invalid attributeNetworkType: " + attributeNetworkTypeString)
|
||
|
}
|
||
|
|
||
|
verifyReviewIsValidFunction := func(_ map[string]string)(bool, error){
|
||
|
return true, nil
|
||
|
}
|
||
|
|
||
|
reviewExists, reviewBytes, reviewMap, reviewVerdict, verdictTime, err := getNewestModeratorReviewFromReviewsList(inputReviewsList, moderatorIdentityHash, "Attribute", attributeHash[:], attributeNetworkType, verifyReviewIsValidFunction)
|
||
|
|
||
|
return reviewExists, reviewBytes, reviewMap, reviewVerdict, verdictTime, err
|
||
|
}
|
||
|
|
||
|
//Outputs:
|
||
|
// -bool: Review exists
|
||
|
// -[]byte: Newest review bytes
|
||
|
// -map[string]string: Newest review map
|
||
|
// -string: Review verdict
|
||
|
// -error
|
||
|
func GetModeratorNewestMessageReviewFromReviewsList(inputReviewsList []ReviewWithHash, moderatorIdentityHash [16]byte, messageHash [26]byte, messageNetworkType byte, messageCipherKeyHash [25]byte)(bool, []byte, map[string]string, string, error){
|
||
|
|
||
|
isValid := helpers.VerifyNetworkType(messageNetworkType)
|
||
|
if (isValid == false){
|
||
|
messageNetworkTypeString := helpers.ConvertByteToString(messageNetworkType)
|
||
|
return false, nil, nil, "", errors.New("GetModeratorNewestMessageReviewFromReviewsList called with invalid messageNetworkType: " + messageNetworkTypeString)
|
||
|
}
|
||
|
|
||
|
verifyReviewIsValidFunction := func(reviewMap map[string]string)(bool, error){
|
||
|
|
||
|
isValid, err := VerifyMessageReviewCipherKey(reviewMap, messageCipherKeyHash)
|
||
|
|
||
|
return isValid, err
|
||
|
}
|
||
|
|
||
|
reviewExists, reviewBytes, reviewMap, reviewVerdict, _, err := getNewestModeratorReviewFromReviewsList(inputReviewsList, moderatorIdentityHash, "Message", messageHash[:], messageNetworkType, verifyReviewIsValidFunction)
|
||
|
|
||
|
return reviewExists, reviewBytes, reviewMap, reviewVerdict, err
|
||
|
}
|
||
|
|
||
|
// This function takes input of a list of reviews
|
||
|
// Returns newest review for a specified moderator, reviewedHash and reviewType
|
||
|
// Will omit review with "None" verdict
|
||
|
// Inputs:
|
||
|
// -[][]byte: List of reviews created by moderator
|
||
|
// -[16]byte: Identity hash of moderator
|
||
|
// -byte: Network type
|
||
|
// -string: Review Type
|
||
|
// -[]byte: Reviewed hash
|
||
|
// -func(map[string]string)(bool, error): Takes review map as input, returns if review is valid
|
||
|
//Outputs:
|
||
|
// -bool: Review exists (will be false if newest review is a None verdict)
|
||
|
// -[]byte: Newest review bytes
|
||
|
// -map[string]string: Newest review map
|
||
|
// -string: Newest review verdict
|
||
|
// -int64: Newest review broadcast time
|
||
|
// -error
|
||
|
func getNewestModeratorReviewFromReviewsList(inputReviewsList []ReviewWithHash, moderatorIdentityHash [16]byte, reviewType string, reviewedHash []byte, networkType byte, verifyReviewIsValid func(map[string]string)(bool, error))(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("getNewestModeratorReviewFromReviewsList called with invalid moderatorIdentityHash: " + moderatorIdentityHashHex)
|
||
|
}
|
||
|
|
||
|
if (reviewType != "Identity" && reviewType != "Profile" && reviewType != "Attribute" && reviewType != "Message"){
|
||
|
return false, nil, nil, "", 0, errors.New("getNewestModeratorReviewFromReviewsList called with invalid reviewType: " + reviewType)
|
||
|
}
|
||
|
|
||
|
isValid = helpers.VerifyNetworkType(networkType)
|
||
|
if (isValid == false){
|
||
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
||
|
return false, nil, nil, "", 0, errors.New("getNewestModeratorReviewFromReviewsList called with invalid networkType: " + networkTypeString)
|
||
|
}
|
||
|
|
||
|
anyReviewFound := false
|
||
|
newestReviewHash := [29]byte{}
|
||
|
newestReviewBytes := make([]byte, 0)
|
||
|
newestReviewMap := make(map[string]string)
|
||
|
newestReviewBroadcastTime := int64(0)
|
||
|
newestReviewVerdict := ""
|
||
|
|
||
|
for _, reviewWithHash := range inputReviewsList{
|
||
|
|
||
|
reviewHash := reviewWithHash.ReviewHash
|
||
|
reviewBytes := reviewWithHash.ReviewBytes
|
||
|
|
||
|
ableToRead, _, reviewNetworkType, reviewAuthor, reviewBroadcastTime, currentReviewType, currentReviewedHash, reviewVerdict, reviewMap, err := ReadReview(false, reviewBytes)
|
||
|
if (err != nil) { return false, nil, nil, "", 0, err }
|
||
|
if (ableToRead == false){
|
||
|
return false, nil, nil, "", 0, errors.New("getNewestModeratorReviewFromReviewsList called with invalid review")
|
||
|
}
|
||
|
if (reviewNetworkType != networkType){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if (reviewAuthor != moderatorIdentityHash){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if (currentReviewType != reviewType) {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
areEqual := bytes.Equal(currentReviewedHash, reviewedHash)
|
||
|
if (areEqual == false){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
reviewIsValid, err := verifyReviewIsValid(reviewMap)
|
||
|
if (err != nil){ return false, nil, nil, "", 0, err }
|
||
|
if (reviewIsValid == false){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if (anyReviewFound == false || reviewBroadcastTime >= newestReviewBroadcastTime){
|
||
|
|
||
|
if (reviewBroadcastTime == newestReviewBroadcastTime){
|
||
|
// The reviewer must be malicious, or their computer clock was skewed somehow
|
||
|
// We compare the review hashes to see which review to use
|
||
|
// This is necessary so that a moderator's newest review will always be calculated the same way by all Seekia clients.
|
||
|
|
||
|
compareValue := bytes.Compare(reviewHash[:], newestReviewHash[:])
|
||
|
if (compareValue == 0){
|
||
|
// This should not happen, because this function should be called with a list of unique reviews
|
||
|
return false, nil, nil, "", 0, errors.New("getNewestModeratorReviewFromReviewsList called with reviewsList containing duplicate review.")
|
||
|
}
|
||
|
|
||
|
if (compareValue == -1){
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
anyReviewFound = true
|
||
|
newestReviewHash = reviewHash
|
||
|
newestReviewBytes = reviewBytes
|
||
|
newestReviewMap = reviewMap
|
||
|
newestReviewBroadcastTime = reviewBroadcastTime
|
||
|
newestReviewVerdict = reviewVerdict
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (anyReviewFound == false){
|
||
|
return false, nil, nil, "", 0, nil
|
||
|
}
|
||
|
if (newestReviewVerdict == "None"){
|
||
|
return false, nil, nil, "", 0, nil
|
||
|
}
|
||
|
|
||
|
return true, newestReviewBytes, newestReviewMap, newestReviewVerdict, newestReviewBroadcastTime, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
// This function returns a map of reviews created by a particular moderator, of a specific reviewType (if provided)
|
||
|
// The map stores the moderator's newest review for each piece of content
|
||
|
// The function will omit reviews whose newest verdict is "None"
|
||
|
// The function will return error if any review is not created by moderator
|
||
|
// The function does not verify the reviews
|
||
|
//Outputs:
|
||
|
// -map[string][]byte: Reviewed Hash (bytes encoded as string) -> Newest Review bytes
|
||
|
// -error
|
||
|
func GetNewestModeratorReviewsMapFromReviewsList(inputReviewsList []ReviewWithHash, moderatorIdentityHash [16]byte, networkType byte, reviewTypeProvided bool, reviewType string)(map[string][]byte, error){
|
||
|
|
||
|
isValid, err := identity.VerifyIdentityHash(moderatorIdentityHash, true, "Moderator")
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (isValid == false){
|
||
|
|
||
|
moderatorIdentityHashHex := encoding.EncodeBytesToHexString(moderatorIdentityHash[:])
|
||
|
|
||
|
return nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList called with invalid moderatorIdentityHash: " + moderatorIdentityHashHex)
|
||
|
}
|
||
|
|
||
|
isValid = helpers.VerifyNetworkType(networkType)
|
||
|
if (isValid == false){
|
||
|
networkTypeString := helpers.ConvertByteToString(networkType)
|
||
|
|
||
|
return nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList called with invalid networkType: " + networkTypeString)
|
||
|
}
|
||
|
|
||
|
if (reviewTypeProvided == true){
|
||
|
if (reviewType != "Identity" && reviewType != "Profile" && reviewType != "Attribute" && reviewType != "Message"){
|
||
|
return nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList called with invalid reviewType: " + reviewType)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type ReviewInfoObject struct{
|
||
|
ReviewHash [29]byte
|
||
|
Verdict string
|
||
|
BroadcastTime int64
|
||
|
ReviewBytes []byte
|
||
|
}
|
||
|
|
||
|
// This map stores the newest review info for each reviewedHash
|
||
|
// Map Structure: Reviewed Hash -> Review info object of newest review
|
||
|
newestReviewInfoObjectsMap := make(map[string]ReviewInfoObject)
|
||
|
|
||
|
for _, reviewWithHash := range inputReviewsList{
|
||
|
|
||
|
reviewHash := reviewWithHash.ReviewHash
|
||
|
reviewBytes := reviewWithHash.ReviewBytes
|
||
|
|
||
|
ableToRead, _, reviewNetworkType, reviewAuthor, reviewBroadcastTime, currentReviewType, reviewedHash, reviewVerdict, _, err := ReadReview(false, reviewBytes)
|
||
|
if (err != nil) { return nil, err }
|
||
|
if (ableToRead == false){
|
||
|
return nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList called with invalid review")
|
||
|
}
|
||
|
if (reviewNetworkType != networkType){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if (reviewAuthor != moderatorIdentityHash){
|
||
|
return nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList receiving review by different reviewer")
|
||
|
}
|
||
|
|
||
|
if (reviewTypeProvided == true && currentReviewType != reviewType){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
existingNewestReviewInfoObject, exists := newestReviewInfoObjectsMap[string(reviewedHash)]
|
||
|
if (exists == true){
|
||
|
|
||
|
existingBroadcastTime := existingNewestReviewInfoObject.BroadcastTime
|
||
|
if (exists == false){
|
||
|
return nil, errors.New("reviewBroadcastTimesMap missing reviewHash entry")
|
||
|
}
|
||
|
|
||
|
if (existingBroadcastTime == reviewBroadcastTime){
|
||
|
// The reviewer must be malicious, or their computer clock was skewed somehow
|
||
|
// We compare the review hashes to see which review to use
|
||
|
// This is necessary so that a moderator's newest review will always be calculated the same way by all Seekia clients.
|
||
|
|
||
|
existingNewestReviewHash := existingNewestReviewInfoObject.ReviewHash
|
||
|
|
||
|
compareValue := bytes.Compare(reviewHash[:], existingNewestReviewHash[:])
|
||
|
if (compareValue == 0){
|
||
|
// This should not happen, because this function should be called with a list of unique reviews
|
||
|
return nil, errors.New("GetNewestModeratorReviewsMapFromReviewsList called with reviewsList containing duplicate review.")
|
||
|
}
|
||
|
|
||
|
if (compareValue == -1){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
} else if (existingBroadcastTime > reviewBroadcastTime){
|
||
|
// The user has replaced their review with a newer one
|
||
|
continue
|
||
|
}
|
||
|
}
|
||
|
|
||
|
newNewestReviewInfoObject := ReviewInfoObject{
|
||
|
ReviewHash: reviewHash,
|
||
|
Verdict: reviewVerdict,
|
||
|
BroadcastTime: reviewBroadcastTime,
|
||
|
ReviewBytes: reviewBytes,
|
||
|
}
|
||
|
|
||
|
newestReviewInfoObjectsMap[string(reviewedHash)] = newNewestReviewInfoObject
|
||
|
}
|
||
|
|
||
|
//Map Structure: Reviewed Hash -> Newest Review bytes
|
||
|
newestReviewsMap := make(map[string][]byte)
|
||
|
|
||
|
for reviewedHash, reviewInfoObject := range newestReviewInfoObjectsMap{
|
||
|
|
||
|
currentVerdict := reviewInfoObject.Verdict
|
||
|
|
||
|
if (currentVerdict == "None"){
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
reviewBytes := reviewInfoObject.ReviewBytes
|
||
|
|
||
|
newestReviewsMap[reviewedHash] = reviewBytes
|
||
|
}
|
||
|
|
||
|
return newestReviewsMap, nil
|
||
|
}
|
||
|
|
||
|
|
||
|
|