// viewableProfiles provides functions to retrieve viewable user profiles package viewableProfiles // A viewable profile is one that can be shown to non-moderators during normal use of Seekia // A viewable Mate profile is one that has been approved. // A viewable Host/Moderator profile is one that has not been banned. // A viewable profile must also be created by a non-banned identity // All viewable statuses are derived from the profile/identity sticky consensus statuses, not their realtime consensus statuses import "seekia/internal/badgerDatabase" import "seekia/internal/encoding" import "seekia/internal/helpers" import "seekia/internal/identity" import "seekia/internal/moderation/verifiedStickyStatus" import "seekia/internal/moderation/trustedViewableStatus" import "seekia/internal/profiles/calculatedAttributes" import "seekia/internal/profiles/readProfiles" import messagepack "github.com/vmihailenco/msgpack/v5" import "errors" // This function gets any attribute from a user's newest viewable profile. It includes calculated attributes. //Outputs: // -bool: Profile exists // -int: Profile version // -bool: Attribute exists // -string: Attribute value // -error func GetAnyAttributeFromNewestViewableUserProfile(identityHash [16]byte, networkType byte, attribute string, allowTrustedStatus bool, allowUnknownStatus bool, alwaysAllowDisabled bool)(bool, int, bool, string, error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, 0, false, "", errors.New("GetAnyAttributeFromNewestViewableUserProfile called with invalid networkType: " + networkTypeString) } profileExists, _, retrieveAnyAttributeFunction, err := GetRetrieveAnyNewestViewableUserProfileAttributeFunction(identityHash, networkType, allowTrustedStatus, allowUnknownStatus, alwaysAllowDisabled) if (err != nil) { return false, 0, false, "", err } if (profileExists == false){ return false, 0, false, "", nil } exists, profileVersion, retrievedAttribute, err := retrieveAnyAttributeFunction(attribute) if (err != nil) { return false, 0, false, "", err } if (exists == false){ return true, 0, false, "", nil } return true, profileVersion, true, retrievedAttribute, nil } // This function returns a function to retrieve any attribute from a user's newest viewable profile. // This makes retrieving multiple attributes faster. // The newest viewable profile only needs to be found and read into memory once. //Outputs: // -bool: Profile exists // -[28]byte: Profile hash // -func(string)(bool, int, string, error) - Retrieve any attribute function // -error func GetRetrieveAnyNewestViewableUserProfileAttributeFunction(identityHash [16]byte, networkType byte, allowTrustedStatus bool, allowUnknownStatus bool, alwaysAllowDisabled bool)(bool, [28]byte, func(string)(bool, int, string, error), error){ isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, [28]byte{}, nil, errors.New("GetRetrieveAnyNewestViewableUserProfileAttributeFunction called with invalid networkType: " + networkTypeString) } profileExists, profileVersion, profileHash, _, _, rawProfileMap, err := GetNewestViewableUserProfile(identityHash, networkType, allowTrustedStatus, allowUnknownStatus, alwaysAllowDisabled) if (err != nil) { return false, [28]byte{}, nil, err } if (profileExists == false){ return false, [28]byte{}, nil, nil } getAnyAttributeFunction, err := calculatedAttributes.GetRetrieveAnyProfileAttributeIncludingCalculatedFunction(profileVersion, rawProfileMap) if (err != nil){ return false, [28]byte{}, nil, err } return true, profileHash, getAnyAttributeFunction, nil } //Inputs: // -[16]byte: Identity hash of user whose profile we are retrieving // -byte: Network type to retrieve profile for // -bool: Allow trusted viewable status to be retrieved. // -If false, only verified viewable statuses will be allowed (this is used for hosts) // -bool: Allow Unknown Status // -This is only used to find hosts to query from. This must be false for hosts retrieving viewable host profiles to share. // -bool: Always allow disabled profiles // -If true, the function will return disabled profiles, even if the identity is banned. // -If false, a disabled profile will be considered unviewable if the identity is banned. //Outputs: // -bool: Viewable Profile exists // -int: Profile version // -[28]byte: Viewable Profile hash // -[]byte: Viewable Profile bytes // -int64: Profile creation time // -map[int]messagepack.RawMessage: Viewable Profile Map // -error func GetNewestViewableUserProfile(identityHash [16]byte, networkType byte, allowTrustedStatus bool, allowUnknownStatus bool, alwaysAllowDisabled bool)(bool, int, [28]byte, []byte, int64, map[int]messagepack.RawMessage, error){ identityType, err := identity.GetIdentityTypeFromIdentityHash(identityHash) if (err != nil) { identityHashHex := encoding.EncodeBytesToHexString(identityHash[:]) return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetNewestViewableUserProfile called with invalid identityHash: " + identityHashHex) } isValid := helpers.VerifyNetworkType(networkType) if (isValid == false){ networkTypeString := helpers.ConvertByteToString(networkType) return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetNewestViewableUserProfile called with invalid networkType: " + networkTypeString) } // We check if the identity is banned getIdentityIsViewableStatus := func()(bool, error){ downloadingRequiredReviews, parametersExist, stickyConsensusEstablished, identityIsViewableStatus, err := verifiedStickyStatus.GetVerifiedIdentityIsViewableStickyStatus(identityHash, networkType) if (err != nil) { return false, err } if (downloadingRequiredReviews == true && parametersExist == true && stickyConsensusEstablished == true){ return identityIsViewableStatus, nil } if (allowTrustedStatus == false){ // The trusted viewable status of the identity is unknown // We are not downloading the required reviews if (allowUnknownStatus == true){ return true, nil } return false, nil } statusIsKnown, identityIsViewable, _, err := trustedViewableStatus.GetTrustedIdentityIsViewableStatus(identityHash, networkType) if (err != nil) { return false, err } if (statusIsKnown == true){ return identityIsViewable, nil } if (allowUnknownStatus == true){ return true, nil } return false, nil } identityIsViewableStatus, err := getIdentityIsViewableStatus() if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err } if (identityIsViewableStatus == false && alwaysAllowDisabled == false){ return false, 0, [28]byte{}, nil, 0, nil, nil } exists, profileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(identityHash) if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err } if (exists == false){ return false, 0, [28]byte{}, nil, 0, nil, nil } newestViewableProfileFound := false newestViewableProfileVersion := 0 var newestViewableProfileHash [28]byte newestViewableProfileIsDisabled := false newestViewableProfileRawProfileMap := make(map[int]messagepack.RawMessage) newestViewableProfileBytes := make([]byte, 0) newestViewableProfileCreationTime := int64(0) for _, profileHash := range profileHashesList{ exists, profileBytes, err := badgerDatabase.GetUserProfile(identityType, profileHash) if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err } if (exists == false){ // Identity profile hashes list is outdated, it will be updated automatically in the background continue } ableToRead, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, profileIsDisabled, rawProfileMap, err := readProfiles.ReadProfile(false, profileBytes) if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err } if (ableToRead == false){ return false, 0, [28]byte{}, nil, 0, nil, errors.New("Database corrupt: Contains invalid profile.") } if (profileIdentityHash != identityHash){ return false, 0, [28]byte{}, nil, 0, nil, errors.New("Database corrupt: Identity Profiles list profile does not match identity hash.") } if (profileNetworkType != networkType){ // The profile belongs to a different networkType // We skip it. continue } // Disabled profiles are always viewable, unless the author identity is banned. if (profileIsDisabled == false){ profileIsDisabled, profileMetadataIsKnown, profileNetworkType, profileIdentityHash, downloadingRequiredReviews, networkParametersExist, stickyConsensusEstablished, profileIsViewableStatus, err := verifiedStickyStatus.GetVerifiedProfileIsViewableStickyStatus(profileHash) if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err } if (profileIsDisabled == true){ return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetVerifiedProfileIsViewableStickyStatus returning mismatched profileIsDisabled status") } if (profileMetadataIsKnown == false){ // The metadata must have been deleted after we retrieved profiles from database. Skip profile continue } if (profileNetworkType != networkType){ return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetVerifiedProfileIsViewableStickyStatus returning mismatched profileNetworkType.") } if (profileIdentityHash != identityHash){ return false, 0, [28]byte{}, nil, 0, nil, errors.New("GetVerifiedProfileIsViewableStickyStatus returning invalid profileIdentityHash") } // Outputs: // -bool: Profile is viewable // -error getProfileIsViewableStatus := func()(bool, error){ if (downloadingRequiredReviews == true && networkParametersExist == true && stickyConsensusEstablished == true){ return profileIsViewableStatus, nil } // Verified status is unknown. We must attempt to get trusted status if (allowTrustedStatus == false){ // The trusted viewable status of the profile is unknown // We are not downloading the required reviews, or the profile is not downloaded if (allowUnknownStatus == true){ return true, nil } // The profile cannot be verified as Viewable return false, nil } statusIsKnown, isViewable, _, err := trustedViewableStatus.GetTrustedProfileIsViewableStatus(profileHash) if (err != nil) { return false, err } if (statusIsKnown == true){ return isViewable, nil } // We do not have a trusted or verified verdict. Profile's viewable status is unknown. if (allowUnknownStatus == true){ return true, nil } return false, nil } profileIsViewable, err := getProfileIsViewableStatus() if (err != nil) { return false, 0, [28]byte{}, nil, 0, nil, err } if (profileIsViewable == false){ // Profile is not viewable, so we skip it if (newestViewableProfileFound == true && newestViewableProfileIsDisabled == true && newestViewableProfileCreationTime < profileCreationTime){ // This user created a non-disabled profile after creating their disabled profile // We reset the newestViewableProfileFound status so that we do not return the disabled profile // We only allow the disabled profile to be returned if it is the newest profile the user has created // Otherwise, users could see a user as being disabled, when in reality their newest profile has not been approved yet newestViewableProfileFound = false } continue } } if (newestViewableProfileFound == false || newestViewableProfileCreationTime < profileCreationTime){ newestViewableProfileFound = true newestViewableProfileVersion = profileVersion newestViewableProfileHash = profileHash newestViewableProfileIsDisabled = profileIsDisabled newestViewableProfileBytes = profileBytes newestViewableProfileRawProfileMap = rawProfileMap newestViewableProfileCreationTime = profileCreationTime } } if (newestViewableProfileFound == false){ return false, 0, [28]byte{}, nil, 0, nil, nil } if (newestViewableProfileIsDisabled == true && alwaysAllowDisabled == true){ return true, newestViewableProfileVersion, newestViewableProfileHash, newestViewableProfileBytes, newestViewableProfileCreationTime, newestViewableProfileRawProfileMap, nil } if (identityIsViewableStatus == false){ return false, 0, [28]byte{}, nil, 0, nil, nil } return true, newestViewableProfileVersion, newestViewableProfileHash, newestViewableProfileBytes, newestViewableProfileCreationTime, newestViewableProfileRawProfileMap, nil }