
// craftResponses provides a function to craft server responses to peer requests

package craftResponses

//TODO: Restrict responses based on if host is in HostViewable..Only mode
//TODO: Add a response called Response Too Large, which means that the requestor has to
//		 reduce their requested range for the host's response to fit within the maximum allowed size
//		We can check if a response has become too large as we craft each response

import "seekia/internal/badgerDatabase"
import "seekia/internal/byteRange"
import "seekia/internal/contentMetadata"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/logger"
import "seekia/internal/messaging/chatMessageStorage"
import "seekia/internal/messaging/readMessages"
import "seekia/internal/moderation/moderatorScores"
import "seekia/internal/moderation/readReports"
import "seekia/internal/moderation/readReviews"
import "seekia/internal/moderation/reportStorage"
import "seekia/internal/moderation/reviewStorage"
import "seekia/internal/moderation/verifiedStickyStatus"
import "seekia/internal/myIdentity"
import "seekia/internal/myRanges"
import "seekia/internal/network/getFundedStatuses"
import "seekia/internal/network/mateCriteria"
import "seekia/internal/network/serverRequest"
import "seekia/internal/network/serverResponse"
import "seekia/internal/parameters/parametersStorage"
import "seekia/internal/parameters/readParameters"
import "seekia/internal/profiles/profileStorage"
import "seekia/internal/profiles/readProfiles"
import "seekia/internal/profiles/viewableProfiles"

import "errors"
import "slices"
 
var invalidRequestResponse []byte = []byte("Invalid Request")

//Outputs:
//	-bool: Response successfully crafted (request was well formed)
//	-bool: Identity hash of request matches our own
//	-bool: Network type of request matches provided network type
//	-[]byte: establishConnectionKey response
//	-[32]byte: Connection key in establishConnectionKey response
//	-error
func GetEstablishConnectionKeyResponse(requestBytes []byte, networkType byte)(bool, bool, bool, []byte, [32]byte, error){

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return false, false, false, nil, [32]byte{}, errors.New("GetEstablishConnectionKeyResponse called with invalid networkType: " + networkTypeString)
	}

	myIdentityExists, myHostPublicIdentityKey, myHostPrivateIdentityKey, err := myIdentity.GetMyPublicPrivateIdentityKeys("Host")
	if (err != nil) { return false, false, false, nil, [32]byte{}, err }
	if (myIdentityExists == false){
		return false, false, false, nil, [32]byte{}, errors.New("GetEstablishConnectionKeyResponse called when my host identity is missing.")
	}

	exists, myHostIdentityHash, err := myIdentity.GetMyIdentityHash("Host")
	if (err != nil) { return false, false, false, nil, [32]byte{}, err }
	if (exists == false){
		return false, false, false, nil, [32]byte{}, errors.New("My identity not found after being found already.")
	}

	recipientHost, requestIdentifier, requestNetworkType, requestorNaclKey, requestorMlkemKey, requestorCsidhKey, requestorFrodoKey, err := serverRequest.ReadServerRequest_EstablishConnectionKey(requestBytes)
	if (err != nil) {
		// Requestor must be malicious
		return false, false, false, nil, [32]byte{}, nil
	}

	if (recipientHost != myHostIdentityHash){
		// Requestor is sending request to invalid recipient
		// Requestor could be malicious or another host could be posting our clearnet/onion address as their own
		// Either way, we don't continue connection

		return true, false, false, nil, [32]byte{}, nil
	}
	if (requestNetworkType != networkType){
		// Either requestor is malicious, or we recently changed our network type
		// If we recently changed our network type, our old host profile may still exist on the old network
		// This request could be coming from someone who has our old host profile downloaded

		// The Seekia app should broadcast a disabled host profile for the old network type before switching network types
		// Doing this will never solve this issue fully, because the new profile will take time to propagate to users
		// Users may also connect to outdated host profiles upon startup, so this problem will always exist, which is fine.

		return true, true, false, nil, [32]byte{}, nil
	}

	responseBytes, newConnectionKey, err := serverResponse.CreateServerResponse_EstablishConnectionKey(myHostPublicIdentityKey, myHostPrivateIdentityKey, requestIdentifier, requestorNaclKey, requestorMlkemKey, requestorCsidhKey, requestorFrodoKey)
	if (err != nil) { return false, false, false, nil, [32]byte{}, err }

	return true, true, true, responseBytes, newConnectionKey, nil
}

//Outputs:
//	-[]byte: Response Bytes
//	-error
func GetServerResponseForRequest(requestBytes []byte, networkType byte, connectionKey [32]byte)([]byte, error){

	isValid := helpers.VerifyNetworkType(networkType)
	if (isValid == false){
		networkTypeString := helpers.ConvertByteToString(networkType)
		return nil, errors.New("GetServerResponseForRequest called with invalid networkType: " + networkTypeString)
	}

	myIdentityExists, myHostPublicIdentityKey, myHostPrivateIdentityKey, err := myIdentity.GetMyPublicPrivateIdentityKeys("Host")
	if (err != nil) { return nil, err }
	if (myIdentityExists == false){
		return nil, errors.New("GetServerResponseForRequest called when my host identity is missing.")
	}

	exists, myHostIdentityHash, err := myIdentity.GetMyIdentityHash("Host")
	if (err != nil) { return nil, err }
	if (exists == false){
		return nil, errors.New("My host identity not found after being found already.")	
	}

	// First we decrypt the request

	ableToRead, decryptedRequestBytes, err := serverRequest.ReadEncryptedRequest(requestBytes, connectionKey)
	if (err != nil) { return nil, err }
	if (ableToRead == false){
		// Cannot read, request is malformed
		return invalidRequestResponse, nil
	}

	// Now we read request to retrieve and verify attributes included in all requests

	//TODO: Verify everything

	requestRecipientHost, requestType, requestIdentifier, requestNetworkType, err := serverRequest.ReadDecryptedServerRequest_StandardData(decryptedRequestBytes)
	if (err != nil){
		// decryptedRequestBytes are malformed, requestor must be malicious
		return invalidRequestResponse, nil
	}
	if (requestRecipientHost != myHostIdentityHash){
		// Requestor must be malicious, because identity hash never changes during connection
		return invalidRequestResponse, nil
	}
	if (requestNetworkType != networkType){
		// Requestor must be malicious, because network type never changes during connection
		return invalidRequestResponse, nil
	}

	// Now we get response based on the requestType

	if (requestType == "GetParametersInfo"){

		//TODO: Check if host is hosting parameters

		allParametersTypesList := readParameters.GetAllParametersTypesList()

		parametersInfoMap := make(map[string]int64)

		for _, parametersType := range allParametersTypesList{

			parametersFound, _, _, parametersCreationTime, err := parametersStorage.GetAuthorizedParameters(parametersType, networkType)
			if (err != nil) { return nil, err }
			if (parametersFound == false){
				continue
			}
			parametersInfoMap[parametersType] = parametersCreationTime
		}

		responseBytes, err := serverResponse.CreateServerResponse_GetParametersInfo(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, parametersInfoMap)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetParameters"){

		//TODO: Verify host is hosting network parameters

		requestedParametersTypesList, err := serverRequest.ReadDecryptedServerRequest_GetParameters(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		parametersList := make([][]byte, 0)

		for _, parametersType := range requestedParametersTypesList{

			parametersFound, parametersBytes, _, _, err := parametersStorage.GetAuthorizedParameters(parametersType, networkType)
			if (err != nil) { return nil, err }
			if (parametersFound == false){
				continue
			}
			parametersList = append(parametersList, parametersBytes)
		}

		responseBytes, err := serverResponse.CreateServerResponse_GetParameters(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, parametersList)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}

	if (requestType == "GetProfilesInfo"){

		acceptableProfileVersionsList, profileTypeToRetrieve, requestRangeStart, requestRangeEnd, requestedIdentityHashesList, requestCriteria, getNewestProfilesOnly, getViewableProfilesOnly, err := serverRequest.ReadDecryptedServerRequest_GetProfilesInfo(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		getRelevantIdentityHashesList := func()([][16]byte, error){

			// We will find the relevant identity hashes based on either the request range or requested identities list

			if (len(requestedIdentityHashesList) != 0){

				return requestedIdentityHashesList, nil
			}

			storedProfileIdentityHashesList, err := badgerDatabase.GetAllProfileIdentityHashes(profileTypeToRetrieve)
			if (err != nil) { return nil, err }

			anyExist, identityHashesInRequestRange, err := byteRange.GetAllIdentityHashesInListWithinRange(requestRangeStart, requestRangeEnd, storedProfileIdentityHashesList)
			if (err != nil) { return nil, err }
			if (anyExist == false){

				emptyList := make([][16]byte, 0)
				return emptyList, nil
			}

			return identityHashesInRequestRange, nil
		}

		relevantIdentityHashesList, err := getRelevantIdentityHashesList()
		if (err != nil) { return nil, err }

		// We limit request to profiles hosted by host
		// This is to prevent the requestor from learning information about profiles the host has downloaded
		// This would be a privacy leak if the host is using Seekia to find matches/moderate.
		// The requestor would know which profiles outside of the hosted range the host had downloaded
		// This could potentially tell them the host's location and desires

		// We reduce requested identity hashes list based on our host range

		getIdentitiesInMyHostRangeList := func()([][16]byte, error){

			hostModeEnabled, hostingAny, myHostRangeStart, myHostRangeEnd, err := myRanges.GetMyIdentitiesToHostRange(profileTypeToRetrieve)
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// User must have disabled host mode
				emptyList := make([][16]byte, 0)
				return emptyList, nil
			}
			if (hostingAny == false){
				// We are not hosting any of the requested ProfileType
				emptyList := make([][16]byte, 0)
				return emptyList, nil
			}

			anyExist, identityHashesInMyHostRangeList, err := byteRange.GetAllIdentityHashesInListWithinRange(myHostRangeStart, myHostRangeEnd, relevantIdentityHashesList)
			if (err != nil) { return nil, err }
			if (anyExist == false){
				emptyList := make([][16]byte, 0)
				return emptyList, nil
			}

			return identityHashesInMyHostRangeList, nil
		}

		requestedIdentitiesInMyHostRangeList, err := getIdentitiesInMyHostRangeList()
		if (err != nil){ return nil, err }

		// Now we reduce list to make sure identities are funded

		fundedIdentitiesList := make([][16]byte, 0)

		for _, identityHash := range requestedIdentitiesInMyHostRangeList{

			if (profileTypeToRetrieve == "Moderator"){

				statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(identityHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || scoreIsSufficient == false){
					continue
				}

			} else {

				// profileType == "Mate" or "Host"

				statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(identityHash, networkType)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || isFunded == false){
					continue
				}
			}

			fundedIdentitiesList = append(fundedIdentitiesList, identityHash)
		}

		// Now we iterate through all relevant identity hashes, find which profile(s) to send in response
		// Then we add those profiles to profilesInfoMapList

		profileInfoObjectsList := make([]serverResponse.ProfileInfoStruct, 0)

		for _, identityHash := range fundedIdentitiesList{

			//Outputs:
			//	-[][28]byte: List of profile hashes
			//	-error
			getUserProfileHashesForRequest := func()([][28]byte, error){

				if (getNewestProfilesOnly == true){

					if (getViewableProfilesOnly == true){

						profileExists, _, profileHash, _, _, _, err := viewableProfiles.GetNewestViewableUserProfile(identityHash, networkType, false, false, false)
						if (err != nil) { return nil, err }
						if (profileExists == false){
							emptyList := make([][28]byte, 0)
							return emptyList, nil
						}
						profileHashesList := [][28]byte{profileHash}

						return profileHashesList, nil
					}
					// getNewestProfilesOnly = true & getViewableProfilesOnly = false

					profileExists, _, profileHash, _, _, _, err := profileStorage.GetNewestUserProfile(identityHash, networkType)
					if (err != nil) { return nil, err }
					if (profileExists == false){
						emptyList := make([][28]byte, 0)
						return emptyList, nil
					}
					profileHashesList := [][28]byte{profileHash}

					return profileHashesList, nil
				}
				// getNewestProfilesOnly == false

				if (getViewableProfilesOnly == true){

					// We will check to see if identity is banned

					downloadingRequiredReviews, parametersExist, stickyStatusEstablished, identityIsViewable, err := verifiedStickyStatus.GetVerifiedIdentityIsViewableStickyStatus(identityHash, networkType)
					if (err != nil) { return nil, err }
					if (downloadingRequiredReviews == false || parametersExist == false || stickyStatusEstablished == false){
						//We do not know if this identity is banned or not.
						// We cannot seed any profiles from this user
						emptyList := make([][28]byte, 0)
						return emptyList, nil
					}
					if (identityIsViewable == false){
						// Identity is banned, therefore all profiles are unviewable
						emptyList := make([][28]byte, 0)
						return emptyList, nil
					}
				}

				getAllProfileHashesForIdentity := func()([][28]byte, error){

					exists, profileHashesList, err := badgerDatabase.GetIdentityProfileHashesList(identityHash)
					if (err != nil) { return nil, err }
					if (exists == false){
						emptyList := make([][28]byte, 0)
						return emptyList, nil
					}
					return profileHashesList, nil
				}

				allProfileHashesForIdentityList, err := getAllProfileHashesForIdentity()
				if (err != nil) { return nil, err }

				if (getViewableProfilesOnly == false){
					return allProfileHashesForIdentityList, nil
				}

				// Now we prune unviewable profiles

				viewableProfileHashesList := make([][28]byte, 0)

				for _, profileHash := range allProfileHashesForIdentityList{

					profileIsDisabled, profileMetadataIsKnown, profileNetworkType, profileAuthor, downloadingRequiredReviews, parametersExist, stickyStatusEstablished, profileIsViewableStatus, err := verifiedStickyStatus.GetVerifiedProfileIsViewableStickyStatus(profileHash)
					if (err != nil) { return nil, err }
					if (profileMetadataIsKnown == false){
						continue
					}
					if (profileNetworkType != networkType){
						continue
					}
					if (profileIsDisabled == true){
						viewableProfileHashesList = append(viewableProfileHashesList, profileHash)
						continue
					}
					if (profileAuthor != identityHash){
						return nil, errors.New("GetVerifiedProfileIsViewableStickyStatus returning different profileAuthor")
					}
					if (downloadingRequiredReviews == false){
						// This should not happen, because this was checked earlier in function
						// Maybe client changed hosted range
						logger.AddLogError("Network", errors.New("Profile not in hosted range anymore."))
						break
					}
					if (parametersExist == false){
						// We cannot determine sticky consensus status for this profile.
						// We shouldn't be hosting without the parameters anyway
						emptyList := make([][28]byte, 0)
						return emptyList, nil
					}
					if (stickyStatusEstablished == false){
						// We cannot determine sticky consensus for this profile
						// Continue to next profile
						continue
					}
					if (profileIsViewableStatus == false){
						continue
					}
					viewableProfileHashesList = append(viewableProfileHashesList, profileHash)
				}

				return viewableProfileHashesList, nil
			}

			userProfileHashesForRequestList, err := getUserProfileHashesForRequest()
			if (err != nil) { return nil, err }

			for _, profileHash := range userProfileHashesForRequestList{

				if (profileTypeToRetrieve == "Mate"){

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(profileHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				profileExists, profileBytes, err := badgerDatabase.GetUserProfile(profileTypeToRetrieve, profileHash)
				if (err != nil) { return nil, err }
				if (profileExists == false){
					// Profile must have been deleted
					continue
				}

				ableToRead, profileHash_Retrieved, profileVersion, profileNetworkType, profileAuthor, profileCreationTime, profileIsDisabled, rawProfileMap, err := readProfiles.ReadProfileAndHash(false, profileBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: contains invalid " + profileTypeToRetrieve + " profile")
				}
				if (profileHash != profileHash_Retrieved){
					return nil, errors.New("Database corrupt: Contains profile with different hash than entry key.")
				}
				if (profileAuthor != identityHash){
					return nil, errors.New("getIdentityProfilesForRequest returning profile from a different identity hash")
				}
				if (profileNetworkType != networkType){
					continue
				}

				isRequestedVersion := slices.Contains(acceptableProfileVersionsList, profileVersion)
				if (isRequestedVersion == false){
					// The requestor cannot accept this profile's version
					// We must have a newer or older Seekia application version
					continue				
				}

				if (requestCriteria != nil){

					if (profileIsDisabled == true){
						// Disabled profiles will not fulfill any criteria
						continue	
					}

					criteriaIsValid, fulfillsCriteria, err := mateCriteria.CheckIfMateProfileFulfillsCriteria(true, profileVersion, rawProfileMap, requestCriteria)
					if (err != nil) { return nil, err }
					if (criteriaIsValid == false){
						return nil, errors.New("ReadDecryptedServerRequest_GetProfilesInfo not verifying criteria.")
					}
					if (fulfillsCriteria == false){
						continue
					}
				}

				profileInfoObject := serverResponse.ProfileInfoStruct{
					ProfileHash: profileHash,
					ProfileAuthor: profileAuthor,
					ProfileCreationTime: profileCreationTime,
				}

				profileInfoObjectsList = append(profileInfoObjectsList, profileInfoObject)
			}
		}

		responseBytes, err := serverResponse.CreateServerResponse_GetProfilesInfo(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, profileInfoObjectsList)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}

	if (requestType == "GetProfiles"){

		profileTypeToRetrieve, profileHashesToRetrieveList, err := serverRequest.ReadDecryptedServerRequest_GetProfiles(decryptedRequestBytes)
		if (err != nil){
			// Request is malformed
			return invalidRequestResponse, nil
		}

		// First we get all profiles that we have that the peer is requesting
		// Then we prune requested profiles list
		// We prune profiles whose authors are not funded, and profiles that are not funded
		// We prune profiles whose author is not within our hosted range
		// This is done for host privacy
		// Otherwise a malicious requestor could try to find other profiles the host has
		// This could fingerprint the host and make it possible to link the host identity to their mate/moderator identity/activities

		getPrunedProfilesList := func()([][]byte, error){

			hostModeEnabled, hostingAny, myHostRangeStart, myHostRangeEnd, err := myRanges.GetMyIdentitiesToHostRange(profileTypeToRetrieve)
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// Host must have disabled host mode in the background
				emptyList := make([][]byte, 0)
				return emptyList, nil
			}
			if (hostingAny == false){
				// Host is not hosting any of the requested profiles
				emptyList := make([][]byte, 0)				
				return emptyList, nil
			}

			requestedProfilesList := make([][]byte, 0)

			for _, profileHash := range profileHashesToRetrieveList{

				if (profileTypeToRetrieve == "Mate"){

					// We make sure profile is funded

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(profileHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				profileExists, profileBytes, err := badgerDatabase.GetUserProfile(profileTypeToRetrieve, profileHash)
				if (err != nil) { return nil, err }
				if (profileExists == false){
					// Profile could have been deleted after profile hashes response was made, or peer is malicious
					// Either way, skip profile
					continue
				}

				ableToRead, profileHash_Retrieved, _, profileNetworkType, profileAuthor, _, _, _, err := readProfiles.ReadProfileAndHash(false, profileBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains invalid profile.")
				}
				if (profileHash_Retrieved != profileHash){
					return nil, errors.New("Database corrupt: Contains profile with different hash than entry key.")
				}
				if (profileNetworkType != networkType){
					// This should not happen unless requestor is malicious
					// Requestor should only request profile hashes which we served in our GetProfilesInfo response
					continue
				}

				if (profileTypeToRetrieve == "Mate"){
					// Because hosts will provide all or no moderator/host profiles
					// We only need to check if identity is in our range if profileType is Mate

					isWithinRange, err := byteRange.CheckIfIdentityHashIsWithinRange(myHostRangeStart, myHostRangeEnd, profileAuthor)
					if (err != nil) { return nil, err }
					if (isWithinRange == false){
						continue
					}
				}

				// We check if author is funded

				if (profileTypeToRetrieve == "Moderator"){

					statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(profileAuthor)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || scoreIsSufficient == false){
						continue
					}

				} else {

					// profileType == "Mate" or "Host"

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(profileAuthor, networkType)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				requestedProfilesList = append(requestedProfilesList, profileBytes)
			}

			return requestedProfilesList, nil
		}

		prunedProfilesList, err := getPrunedProfilesList()
		if (err != nil) { return nil, err } 

		responseBytes, err := serverResponse.CreateServerResponse_GetProfiles(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, prunedProfilesList)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}

	if (requestType == "GetMessageHashesList"){

		acceptableMessageVersionsList, requestInboxRangeStart, requestInboxRangeEnd, requestedInboxesList, getViewableMessagesOnly, getDecryptableMessagesOnly, err := serverRequest.ReadDecryptedServerRequest_GetMessageHashesList(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		getMessageHashesForResponseList := func()([][26]byte, error){

			hostModeEnabled, hostingAnyInboxes, myHostedInboxesRangeStart, myHostedInboxesRangeEnd, err := myRanges.GetMyInboxesToHostRange()
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// User must have disabled host mode recently
				emptyList := make([][26]byte, 0)
				return emptyList, nil
			}
			if (hostingAnyInboxes == false){
				emptyList := make([][26]byte, 0)
				return emptyList, nil
			}

			getRequestedMessageInboxesList := func()([][10]byte, error){

				if (len(requestedInboxesList) != 0){
					// We will ignore range and only retrieve based on inboxes list

					return requestedInboxesList, nil
				}

				// We will retrieve messages based on request inbox range

				allMessageInboxesList, err := badgerDatabase.GetAllMessageInboxes()
				if (err != nil) { return nil, err }

				anyFound, intersectionInboxesList, err := byteRange.GetAllInboxesInListWithinRange(requestInboxRangeStart, requestInboxRangeEnd, allMessageInboxesList)
				if (err != nil) { return nil, err }
				if (anyFound == false){
					emptyList := make([][10]byte, 0)
					return emptyList, nil
				}

				return intersectionInboxesList, nil
			}

			requestedMessageInboxesList, err := getRequestedMessageInboxesList()
			if (err != nil) { return nil, err }

			messageHashesForResponseList := make([][26]byte, 0)

			for _, inboxToRetrieve := range requestedMessageInboxesList{

				// We check if inbox is in our range

				isWithinMyRange, err := byteRange.CheckIfInboxIsWithinRange(myHostedInboxesRangeStart, myHostedInboxesRangeEnd, inboxToRetrieve)
				if (err != nil) { return nil, err }
				if (isWithinMyRange == false){
					continue
				}

				anyExist, messageHashesList, err := badgerDatabase.GetChatInboxMessageHashesList(inboxToRetrieve)
				if (err != nil) { return nil, err }
				if (anyExist == false){
					continue
				}

				for _, messageHash := range messageHashesList{

					// We make sure each message is of an acceptable version

					metadataExists, messageVersion, messageNetworkType, _, messageInbox, messageCipherKeyHash, err := contentMetadata.GetMessageMetadata(messageHash)
					if (err != nil) { return nil, err }
					if (metadataExists == false){
						// We don't have the message stored.
						continue
					}
					if (messageInbox != inboxToRetrieve){
						return nil, errors.New("GetChatInboxMessageHashesList returning list with message of different inbox.")
					}
					if (messageNetworkType != networkType){
						// Message belongs to a different networkType
						continue
					}

					isAcceptable := slices.Contains(acceptableMessageVersionsList, messageVersion)
					if (isAcceptable == false){
						continue
					}

					// We make sure each message is funded

					statusIsKnown, messageIsFunded, _, err := getFundedStatuses.GetMessageIsFundedStatus(messageHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || messageIsFunded == false){
						continue
					}

					if (getViewableMessagesOnly == true){

						// We make sure message is viewable

						messageMetadataIsKnown, messageNetworkType, messageInbox, _, downloadingRequiredReviews, parametersExist, statusEstablished, messageIsViewableStatus, err := verifiedStickyStatus.GetVerifiedMessageIsViewableStickyStatus(messageHash)
						if (err != nil) { return nil, err }
						if (messageMetadataIsKnown == false){
							// Message was deleted, skip to next message
							continue
						}
						if (messageNetworkType != networkType){
							return nil, errors.New("GetVerifiedMessageIsViewableStickyStatus returning different message networkType than contentMetadata.")
						}
						if (messageInbox != inboxToRetrieve){
							return nil, errors.New("GetVerifiedMessageIsViewableStickyStatus returning invalid message inbox")
						}
						if (downloadingRequiredReviews == false){
							// This should only happen if hosted inbox range was changed during this operation
							// All messages for this inbox will be out of range
							break
						}
						if (parametersExist == false){
							//We cannot determine sticky status for any messages. Return empty response to peer.
							emptyList := make([][26]byte, 0)
							return emptyList, nil
						}
						if (statusEstablished == false){
							// We do not know sticky consensus for message. Skip message.
							continue
						}
						if (messageIsViewableStatus == false){
							continue
						}
					}

					if (getDecryptableMessagesOnly == true){

						// We make sure at least 1 review/report with valid cipher key exists for message
						checkIfMessageCipherKeyExists := func()(bool, error){

							cipherKeyFound, _, err := reportStorage.GetMessageCipherKeyFromAnyReport(messageHash, messageNetworkType, messageCipherKeyHash)
							if (err != nil) { return false, err }
							if (cipherKeyFound == true){
								return true, nil
							}

							cipherKeyFound, _, err = reviewStorage.GetMessageCipherKeyFromAnyReview(messageHash, messageNetworkType, messageCipherKeyHash)
							if (err != nil) { return false, err }
							if (cipherKeyFound == true){
								return true, nil
							}

							return false, nil
						}

						messageCipherKeyExists, err := checkIfMessageCipherKeyExists()
						if (err != nil) { return nil, err }
						if (messageCipherKeyExists == false){
							continue
						}
					}

					messageHashesForResponseList = append(messageHashesForResponseList, messageHash)
				}
			}

			return messageHashesForResponseList, nil
		}

		messageHashesForResponseList, err := getMessageHashesForResponseList()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetMessageHashesList(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, messageHashesForResponseList)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetMessages"){

		requestedMessageHashesList, err := serverRequest.ReadDecryptedServerRequest_GetMessages(decryptedRequestBytes)
		if (err != nil) { return nil, err }

		getMessagesListForResponse := func()([][]byte, error){

			hostModeEnabled, hostingAny, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToHostRange()
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// User must have disabled host mode
				emptyList := make([][]byte, 0)
				return emptyList, nil
			}

			if (hostingAny == false){
				// We are not hosting any messages
				emptyList := make([][]byte, 0)
				return emptyList, nil
			}

			messagesListForResponse := make([][]byte, 0)

			for _, messageHash := range requestedMessageHashesList{

				// We make sure each message is funded

				statusIsKnown, messageIsFunded, _, err := getFundedStatuses.GetMessageIsFundedStatus(messageHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || messageIsFunded == false){
					continue
				}

				messageExists, messageBytes, err := badgerDatabase.GetChatMessage(messageHash)
				if (err != nil) { return nil, err }
				if (messageExists == false){
					continue
				}

				ableToRead, currentMessageHash, _, messageNetworkType, messageInbox, _, _, _, _, _, _, err := readMessages.ReadChatMessagePublicDataAndHash(false, messageBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains malformed message.")
				}
				if (messageHash != currentMessageHash){
					return nil, errors.New("Database corrupt: Contains message with mismatched messageHash entry key")
				}
				if (messageNetworkType != networkType){
					// This should not happen unless requestor is malicious
					// The requestor should only requests messages which we already said were on the connection networkType.
					continue
				}

				isWithinMyRange, err := byteRange.CheckIfInboxIsWithinRange(myInboxRangeStart, myInboxRangeEnd, messageInbox)
				if (err != nil) { return nil, err }
				if (isWithinMyRange == false){
					continue
				}

				messagesListForResponse = append(messagesListForResponse, messageBytes)				
			}

			return messagesListForResponse, nil
		}
		
		messagesListForResponse, err := getMessagesListForResponse()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetMessages(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, messagesListForResponse)
		if (err != nil) { return nil, err }

		return responseBytes, nil	
	}
	if (requestType == "GetIdentityReviewsInfo"){

		acceptableReviewVersions, identityTypeToRetrieve, requestRangeStart, requestRangeEnd, requestedReviewedIdentityHashesList, requestedReviewersList, err := serverRequest.ReadDecryptedServerRequest_GetIdentityReviewsInfo(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		getReviewsInfoMap := func()(map[[29]byte][]byte, error){

			hostModeEnabled, hostingAny, myIdentityRangeStart, myIdentityRangeEnd, err := myRanges.GetMyIdentitiesToHostRange(identityTypeToRetrieve)
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// We must have turned off host mode recently
				emptyReviewsInfoMap := make(map[[29]byte][]byte)
				return emptyReviewsInfoMap, nil
			}
			if (hostingAny == false){
				emptyReviewsInfoMap := make(map[[29]byte][]byte)
				return emptyReviewsInfoMap, nil
			}

			// We include identity, profile and attribute reviews

			relevantReviewHashesList := make([][29]byte, 0)

			allReviewedIdentityHashesList, err := badgerDatabase.GetAllReviewedIdentityHashes()
			if (err != nil) { return nil, err }

			for _, identityHash := range allReviewedIdentityHashesList{

				anyExist, reviewHashesList, err := badgerDatabase.GetIdentityReviewsList(identityHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReviewHashesList = append(relevantReviewHashesList, reviewHashesList...)
				}
			}

			allReviewedProfileHashesList, err := badgerDatabase.GetAllReviewedProfileHashes()
			if (err != nil) { return nil, err }

			for _, profileHash := range allReviewedProfileHashesList{

				anyExist, reviewHashesList, err := badgerDatabase.GetProfileReviewsList(profileHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReviewHashesList = append(relevantReviewHashesList, reviewHashesList...)
				}
			}

			allReviewedAttributeHashesList, err := badgerDatabase.GetAllReviewedProfileAttributeHashes()
			if (err != nil) { return nil, err }

			for _, attributeHash := range allReviewedAttributeHashesList{

				anyExist, reviewHashesList, err := badgerDatabase.GetProfileAttributeReviewsList(attributeHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReviewHashesList = append(relevantReviewHashesList, reviewHashesList...)
				}
			}

			// Map structure: Review hash -> Reviewed hash
			reviewsInfoMap := make(map[[29]byte][]byte)

			for _, reviewHash := range relevantReviewHashesList{

				reviewExists, reviewBytes, err := badgerDatabase.GetReview(reviewHash)
				if (err != nil) { return nil, err }
				if (reviewExists == false){
					// Review must have been deleted automatically.
					// The database entry that referenced the review will be deleted automatically
					continue
				}

				ableToRead, currentReviewHash, reviewVersion, reviewNetworkType, reviewerIdentityHash, _, reviewType, reviewedHash, _, _, err := readReviews.ReadReviewAndHash(false, reviewBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains invalid review.")
				}
				if (reviewHash != currentReviewHash){
					return nil, errors.New("Database corrupt: Review entry key does not match review hash")
				}
				isAcceptableVersion := slices.Contains(acceptableReviewVersions, reviewVersion)
				if (isAcceptableVersion == false){
					// Review is of a different version than requestor can accept
					// We must have a different Seekia app version than them.
					continue
				}
				if (reviewNetworkType != networkType){
					// Review belongs to a different networkType
					continue
				}

				if (len(requestedReviewersList) != 0){
					isInRequestedList := slices.Contains(requestedReviewersList, reviewerIdentityHash)
					if (isInRequestedList == false){
						continue
					}
				}

				statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(reviewerIdentityHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || scoreIsSufficient == false){
					continue
				}

				//Outputs:
				//	-bool: Review is malicious
				//	-bool: Reviewed identity hash is known
				//	-[16]byte: Reviewed identity hash
				//	-error
				getReviewedIdentityHash := func()(bool, bool, [16]byte, error){

					if (reviewType == "Identity"){

						if (len(reviewedHash) != 16){
							reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
							return false, false, [16]byte{}, errors.New("ReadReview returning invalid reviewedHash length for Identity review: " + reviewedHashHex)
						}

						reviewedIdentityHash := [16]byte(reviewedHash)

						return false, true, reviewedIdentityHash, nil
					}
					if (reviewType == "Profile"){

						if (len(reviewedHash) != 28){
							reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
							return false, false, [16]byte{}, errors.New("ReadReview returning invalid reviewedHash length for Profile review: " + reviewedHashHex)
						}

						reviewedProfileHash := [28]byte(reviewedHash)

						metadataExists, _, profileNetworkType, profileAuthor, _, _, _, _, err := contentMetadata.GetProfileMetadata(reviewedProfileHash)
						if (err != nil) { return false, false, [16]byte{}, err }
						if (metadataExists == false){
							return false, false, [16]byte{}, nil
						}
						if (profileNetworkType != reviewNetworkType){
							// The review is reviewing a profile from a different networkType
							// The review author must be malicious
							return true, false, [16]byte{}, nil
						}

						return false, true, profileAuthor, nil
					}
					if (reviewType == "Attribute"){

						if (len(reviewedHash) != 27){
							reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
							return false, false, [16]byte{}, errors.New("ReadReview returning invalid reviewedHash length for Attribute review: " + reviewedHashHex)
						}

						reviewedAttributeHash := [27]byte(reviewedHash)

						attributeMetadataFound, _, authorIdentityHash, attributeNetworkType, _, err := profileStorage.GetProfileAttributeMetadata(reviewedAttributeHash)
						if (err != nil) { return false, false, [16]byte{}, err }
						if (attributeMetadataFound == false){
							return false, false, [16]byte{}, nil
						}
						if (attributeNetworkType != reviewNetworkType){
							// The review is reviewing an attribute from a different networkType
							// The review author must be malicious
							return true, false, [16]byte{}, nil
						}

						return false, true, authorIdentityHash, nil
					}

					return false, false, [16]byte{}, errors.New("getReviewedIdentityHash reached during GetIdentityReviewsInfo response craft with invalid reviewType: " + reviewType)
				}

				reviewIsMalicious, reviewedIdentityHashIsKnown, reviewedIdentityHash, err := getReviewedIdentityHash()
				if (err != nil) { return nil, err }
				if (reviewIsMalicious == true){
					// Review is malicious. We will not seed reviews which are known to be malicious
					// TODO: Skipping these kinds of reviews could be a fingerprinting risk?
					continue
				}
				if (reviewedIdentityHashIsKnown == false){
					// We cannot seed reviews whose reviewed identity hash is not known.
					continue
				}

				reviewedIdentityType, err := identity.GetIdentityTypeFromIdentityHash(reviewedIdentityHash)
				if (err != nil){ return nil, err }
				if (reviewedIdentityType != identityTypeToRetrieve){
					continue
				}

				if (len(requestedReviewedIdentityHashesList) != 0){

					// We make sure all reviewed identity hashes are within this list

					isWithinList := slices.Contains(requestedReviewedIdentityHashesList, reviewedIdentityHash)
					if (isWithinList == false){
						continue
					}				
				} else {

					identityIsWithinRequestRange, err := byteRange.CheckIfIdentityHashIsWithinRange(requestRangeStart, requestRangeEnd, reviewedIdentityHash)
					if (err != nil) { return nil, err }
					if (identityIsWithinRequestRange == false){
						continue
					}
				}

				// We make sure identity is within our range

				isWithinMyRange, err := byteRange.CheckIfIdentityHashIsWithinRange(myIdentityRangeStart, myIdentityRangeEnd, reviewedIdentityHash)
				if (err != nil) { return nil, err }
				if (isWithinMyRange == false){
					continue
				}

				// We make sure reviewed identity is funded

				if (identityTypeToRetrieve == "Moderator"){

					statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(reviewedIdentityHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || scoreIsSufficient == false){
						continue
					}

				} else {

					// identityTypeToRetrieve == "Mate" or "Host"

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(reviewedIdentityHash, networkType)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				if (identityTypeToRetrieve == "Mate" && reviewType == "Profile"){

					// We make sure the mate profile is funded

					if (len(reviewedHash) != 28){
						reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
						return nil, errors.New("ReadReview returning invalid length reviewedHash for Profile review: " + reviewedHashHex)
					}

					reviewedProfileHash := [28]byte(reviewedHash)

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(reviewedProfileHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}

				} else if (identityTypeToRetrieve == "Mate" && reviewType == "Attribute"){

					// We make sure at least 1 profile with this attribute is funded

					checkIfAnyAttributeProfileIsFunded := func()(bool, error){

						if (len(reviewedHash) != 27){
							reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
							return false, errors.New("ReadReview returning invalid length reviewedHash for attribute review: " + reviewedHashHex)
						}

						reviewedAttributeHash := [27]byte(reviewedHash)

						anyExist, attributeProfileHashesList, err := badgerDatabase.GetAttributeProfilesList(reviewedAttributeHash)
						if (err != nil) { return false, err }
						if (anyExist == false){
							return false, nil
						}

						for _, profileHash := range attributeProfileHashesList{

							statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(profileHash)
							if (err != nil) { return false, err }
							if (statusIsKnown == true && isFunded == true){
								return true, nil
							}
						}
						return false, nil
					}

					anyAttributeProfileIsFunded, err := checkIfAnyAttributeProfileIsFunded()
					if (err != nil) { return nil, err }
					if (anyAttributeProfileIsFunded == false){
						continue
					}
				}

				reviewsInfoMap[reviewHash] = reviewedHash
			}

			return reviewsInfoMap, nil
		}

		reviewsInfoMap, err := getReviewsInfoMap()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetIdentityReviewsInfo(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, reviewsInfoMap)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetMessageReviewsInfo"){

		acceptableReviewVersions, requestRangeStart, requestRangeEnd, requestedReviewedMessageHashesList, requestedReviewersList, err := serverRequest.ReadDecryptedServerRequest_GetMessageReviewsInfo(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		getReviewsInfoMap := func()(map[[29]byte][26]byte, error){

			hostModeEnabled, hostingAnyInboxes, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToHostRange()
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// Host mode must have been disabled recently
				reviewsInfoMap := make(map[[29]byte][26]byte)
				return reviewsInfoMap, nil
			}
			if (hostingAnyInboxes == false){
				// We are not hosting any messages/ message reviews
				reviewsInfoMap := make(map[[29]byte][26]byte)
				return reviewsInfoMap, nil
			}

			getRelevantMessageHashesList := func()([][26]byte, error){

				if (len(requestedReviewedMessageHashesList) != 0){

					// We only want to return reviews that review message hashes that are within the requestedReviewedMessageHashesList

					return requestedReviewedMessageHashesList, nil
				}

				// We want to share reviews which review all reviewed message hashes
				// We will reduce based on requested range later

				allReviewedMessageHashesList, err := badgerDatabase.GetAllReviewedMessageHashes()
				if (err != nil) { return nil, err }
				
				return allReviewedMessageHashesList, nil
			}

			relevantMessageHashesList, err := getRelevantMessageHashesList()
			if (err != nil){ return nil, err }

			relevantReviewHashesList := make([][29]byte, 0)

			for _, messageHash := range relevantMessageHashesList{

				anyExist, reviewHashesList, err := badgerDatabase.GetMessageReviewsList(messageHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReviewHashesList = append(relevantReviewHashesList, reviewHashesList...)
				}
			}

			// Map Structure: Review hash -> Reviewed message hash
			reviewsInfoMap := make(map[[29]byte][26]byte)

			for _, reviewHash := range relevantReviewHashesList{

				reviewExists, reviewBytes, err := badgerDatabase.GetReview(reviewHash)
				if (err != nil) { return nil, err }
				if (reviewExists == false){
					continue
				}

				ableToRead, currentReviewHash, reviewVersion, reviewNetworkType, reviewerIdentityHash, _, reviewType, reviewedHash, _, _, err := readReviews.ReadReviewAndHash(false, reviewBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains invalid review.")
				}
				if (reviewHash != currentReviewHash){
					return nil, errors.New("Database corrupt: Review entry key does not match review hash")
				}
				isAcceptableVersion := slices.Contains(acceptableReviewVersions, reviewVersion)
				if (isAcceptableVersion == false){
					// Review is of a different version than requestor can accept
					// We must have a different Seekia app version than them.
					continue
				}
				if (reviewNetworkType != networkType){
					// Review belongs to a different networkType
					continue
				}

				if (len(requestedReviewersList) != 0){
					isInRequestedList := slices.Contains(requestedReviewersList, reviewerIdentityHash)
					if (isInRequestedList == false){
						continue
					}
				}

				if (reviewType != "Message"){
					return nil, errors.New("Database corrupt: Message reviews list contains non-message review")
				}

				// We will reduce the requested reviews based on requested inbox range

				if (len(reviewedHash) != 26){
					reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
					return nil, errors.New("ReadReview returning invalid length reviewedHash for Message review: " + reviewedHashHex)
				}

				reviewedMessageHash := [26]byte(reviewedHash)

				metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(reviewedMessageHash)
				if (err != nil){ return nil, err }
				if (metadataExists == false){
					// We cannot seed reviews for messages whose metadata we do not know
					continue
				}
				if (messageNetworkType != reviewNetworkType){
					// The review author must be malicious
					continue
				}

				isWithinMyRange, err := byteRange.CheckIfInboxIsWithinRange(myInboxRangeStart, myInboxRangeEnd, messageInbox)
				if (err != nil) { return nil, err }
				if (isWithinMyRange == false){
					continue
				}

				// We make sure message inbox is within requested inbox range

				isWithinRequestRange, err := byteRange.CheckIfInboxIsWithinRange(requestRangeStart, requestRangeEnd, messageInbox)
				if (err != nil) { return nil, err }
				if (isWithinRequestRange == false){
					continue
				}

				// We make sure message is funded

				statusIsKnown, messageIsFunded, _, err := getFundedStatuses.GetMessageIsFundedStatus(reviewedMessageHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || messageIsFunded == false){
					continue
				}

				reviewsInfoMap[reviewHash] = reviewedMessageHash
			}

			return reviewsInfoMap, nil		
		}

		reviewsInfoMap, err := getReviewsInfoMap()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetMessageReviewsInfo(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, reviewsInfoMap)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetReviews"){

		requestedReviewHashesList, err := serverRequest.ReadDecryptedServerRequest_GetReviews(decryptedRequestBytes)
		if (err != nil) { return nil, err }

		getReviewsListForResponse := func()([][]byte, error){

			// We will reduce reviews to only our hosted range
			// This is done to prevent fingerprinting
			// We also make sure all reviewers, reviewed content, and reviewed identities are funded

			reviewsListForResponse := make([][]byte, 0)

			for _, reviewHash := range requestedReviewHashesList{

				reviewExists, reviewBytes, err := badgerDatabase.GetReview(reviewHash)
				if (err != nil) { return nil, err }
				if (reviewExists == false){
					continue
				}

				ableToRead, currentReviewHash, _, reviewNetworkType, reviewerIdentityHash, _, reviewType, reviewedHash, _, _, err := readReviews.ReadReviewAndHash(false, reviewBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains invalid review.")
				}
				if (reviewHash != currentReviewHash){
					return nil, errors.New("Database corrupt: Contains review with mismatched reviewHash entry key")
				}
				if (reviewNetworkType != networkType){
					// Requestor must be malicious
					// They should only request reviews which we told them were on the connection networkType
					continue
				}

				statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(reviewerIdentityHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || scoreIsSufficient == false){
					continue
				}

				if (reviewType == "Message"){

					if (len(reviewedHash) != 26){
						reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
						return nil, errors.New("ReadReview returning invalid length reviewedHash for Message review: " + reviewedHashHex)
					}

					reviewedMessageHash := [26]byte(reviewedHash)

					metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(reviewedMessageHash)
					if (err != nil) { return nil, err }
					if (metadataExists == false){
						// We cannot serve reviews whose reviewed content we do not have saved
						continue
					}
					if (messageNetworkType != reviewNetworkType){
						// Review author must be malicious
						// We will not serve malicious reviews
						// Requestor must be malicious, because we would have already checked for this condition
						//		before we offered this review to requestor
						continue
					}

					hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfMessageInboxIsWithinMyHostedRange(messageInbox)
					if (err != nil) { return nil, err }
					if (hostModeEnabled == false){
						// Host mode must have been disabled after earlier check.
						emptyList := make([][]byte, 0)
						return emptyList, nil
					}
					if (isWithinMyRange == false){
						continue
					}

					statusIsKnown, messageIsFunded, _, err := getFundedStatuses.GetMessageIsFundedStatus(reviewedMessageHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || messageIsFunded == false){
						continue
					}

				} else {
					// reviewType == "Identity" or "Profile" or "Attribute"

					//Outputs:
					//	-bool: Review is malicious
					//	-bool: Identity hash is known
					//	-[16]byte: Reviewed identity hash
					//	-error
					getReviewedIdentityHash := func()(bool, bool, [16]byte, error){

						if (reviewType == "Identity"){

							if (len(reviewedHash) != 16){
								reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
								return false, false, [16]byte{}, errors.New("ReadReview returning invalid length reviewedHash for Identity review: " + reviewedHashHex)
							}

							reviewedIdentityHash := [16]byte(reviewedHash)

							return false, true, reviewedIdentityHash, nil
						}

						if (reviewType == "Profile"){

							if (len(reviewedHash) != 28){
								reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
								return false, false, [16]byte{}, errors.New("ReadReview returning invalid length reviewedHash for Profile review: " + reviewedHashHex)
							}

							reviewedProfileHash := [28]byte(reviewedHash)

							metadataExists, _, profileNetworkType, profileAuthor, _, _, _, _, err := contentMetadata.GetProfileMetadata(reviewedProfileHash)
							if (err != nil) { return false, false, [16]byte{}, err }
							if (metadataExists == false){
								return false, false, [16]byte{}, nil
							}
							if (profileNetworkType != reviewNetworkType){
								// Review author must be malicious
								return true, false, [16]byte{}, nil
							}

							return false, true, profileAuthor, nil
						}
						if (reviewType == "Attribute"){

							if (len(reviewedHash) != 27){
								reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
								return false, false, [16]byte{}, errors.New("ReadReview returning invalid length reviewedHash for Attribute review: " + reviewedHashHex)
							}

							reviewedAttributeHash := [27]byte(reviewedHash)

							attributeMetadataFound, _, authorIdentityHash, attributeNetworkType, _, err := profileStorage.GetProfileAttributeMetadata(reviewedAttributeHash)
							if (err != nil) { return false, false, [16]byte{}, err }
							if (attributeMetadataFound == false){
								return false, false, [16]byte{}, nil
							}
							if (attributeNetworkType != reviewNetworkType){
								// The review is reviewing an attribute from a different networkType
								// The review author must be malicious
								return true, true, [16]byte{}, nil
							}

							return false, true, authorIdentityHash, nil
						}

						return false, false, [16]byte{}, errors.New("ReadReview returning invalid reviewType: " + reviewType)
					}

					reviewIsMalicious, metadataExists, reviewedIdentityHash, err := getReviewedIdentityHash()
					if (err != nil) { return nil, err }
					if (reviewIsMalicious == true){
						// We will not serve malicious reviews
						// Requestor must be malicious, because we should have already checked for this before
						//		offering a review hash in the Get...ReviewsInfo responses
						continue
					}
					if (metadataExists == false){
						// We cannot seed reviews whose reviewed identity hash we do not know
						continue
					}

					hostModeEnabled, isInMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyHostedRange(reviewedIdentityHash)
					if (err != nil) { return nil, err }
					if (hostModeEnabled == false){
						// Host mode must have been disabled after earlier check. Return empty list.
						emptyList := make([][]byte, 0)
						return emptyList, nil
					}

					if (isInMyRange == false){
						continue
					}

					// We make sure reviewed identity is funded

					reviewedIdentityType, err := identity.GetIdentityTypeFromIdentityHash(reviewedIdentityHash)
					if (err != nil) { return nil, err }
					if (reviewedIdentityType == "Moderator"){

						statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(reviewedIdentityHash)
						if (err != nil) { return nil, err }
						if (statusIsKnown == false || scoreIsSufficient == false){
							continue
						}

					} else {

						// reviewedIdentityType == "Mate" or "Host"

						statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(reviewedIdentityHash, networkType)
						if (err != nil) { return nil, err }
						if (statusIsKnown == false || isFunded == false){
							continue
						}
					}

					if (reviewedIdentityType == "Mate" && reviewType == "Profile"){

						// We make sure profile is funded

						if (len(reviewedHash) != 28){
							reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
							return nil, errors.New("ReadReview returning invalid length reviewedHash for Profile review: " + reviewedHashHex)
						}

						reviewedProfileHash := [28]byte(reviewedHash)

						statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(reviewedProfileHash)
						if (err != nil) { return nil, err }
						if (statusIsKnown == false || isFunded == false){
							continue
						}

					} else if (reviewedIdentityType == "Mate" && reviewType == "Attribute"){

						// We make sure at least 1 profile with this attribute is funded

						checkIfAnyAttributeProfileIsFunded := func()(bool, error){

							if (len(reviewedHash) != 27){
								reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
								return false, errors.New("ReadReview returning invalid length reviewedHash for Attribute review: " + reviewedHashHex)
							}

							reviewedAttributeHash := [27]byte(reviewedHash)

							anyExist, attributeProfileHashesList, err := badgerDatabase.GetAttributeProfilesList(reviewedAttributeHash)
							if (err != nil) { return false, err }
							if (anyExist == false){
								return false, nil
							}

							for _, profileHash := range attributeProfileHashesList{

								statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(profileHash)
								if (err != nil) { return false, err }
								if (statusIsKnown == true && isFunded == true){
									return true, nil
								}
							}

							return false, nil
						}

						anyAttributeProfileIsFunded, err := checkIfAnyAttributeProfileIsFunded()
						if (err != nil) { return nil, err }
						if (anyAttributeProfileIsFunded == false){
							continue
						}
					}
				}

				reviewsListForResponse = append(reviewsListForResponse, reviewBytes)
			}

			return reviewsListForResponse, nil
		}

		reviewsListForResponse, err := getReviewsListForResponse()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetReviews(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, reviewsListForResponse)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetIdentityReportsInfo"){

		acceptableVersionsList, identityTypeToRetrieve, requestRangeStart, requestRangeEnd, requestedReportedIdentityHashesList, err := serverRequest.ReadDecryptedServerRequest_GetIdentityReportsInfo(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		getReportsInfoMap := func()(map[[30]byte][]byte, error){

			hostModeEnabled, hostingAny, myIdentityRangeStart, myIdentityRangeEnd, err := myRanges.GetMyIdentitiesToHostRange(identityTypeToRetrieve)
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// We must have turned off host mode recently
				reportsInfoMap := make(map[[30]byte][]byte)
				return reportsInfoMap, nil
			}
			if (hostingAny == false){
				reportsInfoMap := make(map[[30]byte][]byte)
				return reportsInfoMap, nil
			}

			// We include identity, profile and attribute reports

			relevantReportHashesList := make([][30]byte, 0)

			allReportedIdentityHashesList, err := badgerDatabase.GetAllReportedIdentityHashes()
			if (err != nil) { return nil, err }

			for _, identityHash := range allReportedIdentityHashesList{

				anyExist, reportHashesList, err := badgerDatabase.GetIdentityReportsList(identityHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReportHashesList = append(relevantReportHashesList, reportHashesList...)
				}
			}

			allReportedProfileHashesList, err := badgerDatabase.GetAllReportedProfileHashes()
			if (err != nil) { return nil, err }

			for _, profileHash := range allReportedProfileHashesList{

				anyExist, reportHashesList, err := badgerDatabase.GetProfileReportsList(profileHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReportHashesList = append(relevantReportHashesList, reportHashesList...)
				}
			}

			allReportedAttributeHashesList, err := badgerDatabase.GetAllReportedProfileAttributeHashes()
			if (err != nil) { return nil, err }

			for _, attributeHash := range allReportedAttributeHashesList{

				anyExist, reportHashesList, err := badgerDatabase.GetProfileAttributeReportsList(attributeHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReportHashesList = append(relevantReportHashesList, reportHashesList...)
				}
			}

			// Map Structure: Report Hash -> Reported Hash
			reportsInfoMap := make(map[[30]byte][]byte)

			for _, reportHash := range relevantReportHashesList{

				statusIsKnown, reportIsFunded, _, err := getFundedStatuses.GetReportIsFundedStatus(reportHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || reportIsFunded == false){
					continue
				}

				reportExists, reportBytes, err := badgerDatabase.GetReport(reportHash)
				if (err != nil) { return nil, err }
				if (reportExists == false){
					continue
				}

				ableToRead, currentReportHash, reportVersion, reportNetworkType, _, reportType, reportedHash, _, err := readReports.ReadReportAndHash(false, reportBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains invalid report.")
				}
				if (reportHash != currentReportHash){
					return nil, errors.New("Database corrupt: Report entry key does not match report hash")
				}
				isAcceptableVersion := slices.Contains(acceptableVersionsList, reportVersion)
				if (isAcceptableVersion == false){
					// This report is of a version that the requestor cannot accept
					// We must be running a different version of Seekia than them.
					continue
				}
				if (reportNetworkType != networkType){
					// Report belongs to a different networkType
					continue
				}

				//Outputs:
				//	-bool: Report is malicious
				//	-bool: Metadata exists
				//	-[16]byte: Reported identity hash
				//	-error
				getReportedIdentityHash := func()(bool, bool, [16]byte, error){

					if (reportType == "Identity"){

						if (len(reportedHash) != 16){
							reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
							return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Identity report: " + reportedHashHex)
						}

						reportedIdentityHash := [16]byte(reportedHash)

						return false, true, reportedIdentityHash, nil
					}
					if (reportType == "Profile"){

						if (len(reportedHash) != 28){
							reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
							return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Profile report: " + reportedHashHex)
						}

						reportedProfileHash := [28]byte(reportedHash)

						metadataExists, _, profileNetworkType, profileAuthor, _, _, _, _, err := contentMetadata.GetProfileMetadata(reportedProfileHash)
						if (err != nil) { return false, false, [16]byte{}, err }
						if (metadataExists == false){
							return false, false, [16]byte{}, nil
						}
						if (profileNetworkType != reportNetworkType){
							// Report author must be malicious
							// Report is reporting a profile on a different network
							return true, false, [16]byte{}, nil
						}

						return false, true, profileAuthor, nil
					}
					if (reportType == "Attribute"){

						if (len(reportedHash) != 27){
							reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
							return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Attribute report: " + reportedHashHex)
						}

						reportedAttributeHash := [27]byte(reportedHash)

						metadataExists, _, authorIdentityHash, attributeNetworkType, _, err := profileStorage.GetProfileAttributeMetadata(reportedAttributeHash)
						if (err != nil){ return false, false, [16]byte{}, err }
						if (metadataExists == false){
							return false, false, [16]byte{}, nil
						}
						if (attributeNetworkType != networkType){
							// Report author must be malicious
							// Report is reporting an attribute on a different network
							return true, false, [16]byte{}, nil
						}

						return false, true, authorIdentityHash, nil
					}

					return false, false, [16]byte{}, errors.New("getReportedIdentityHash reached with invalid reportType: " + reportType)
				}

				reportIsMalicious, metadataExists, reportedIdentityHash, err := getReportedIdentityHash()
				if (err != nil) { return nil, err }
				if (reportIsMalicious == true){
					// We will not serve malicious reports
					continue
				}
				if (metadataExists == false){
					// We cannot serve reports whose reported author we do not know.
					continue
				}

				reportedIdentityType, err := identity.GetIdentityTypeFromIdentityHash(reportedIdentityHash)
				if (err != nil){ return nil, err }
				if (reportedIdentityType != identityTypeToRetrieve){
					continue
				}

				if (len(requestedReportedIdentityHashesList) != 0){

					// We will find report hashes from requested reportedHashes list

					isWithinList := slices.Contains(requestedReportedIdentityHashesList, reportedIdentityHash)
					if (isWithinList == false){
						continue
					}
				} else {

					// We make sure reported identity is within requested range

					identityIsInRequestRange, err := byteRange.CheckIfIdentityHashIsWithinRange(requestRangeStart, requestRangeEnd, reportedIdentityHash)
					if (err != nil) { return nil, err }
					if (identityIsInRequestRange == false){
						continue
					}
				}

				// We make sure identity is within our range

				identityIsInMyRange, err := byteRange.CheckIfIdentityHashIsWithinRange(myIdentityRangeStart, myIdentityRangeEnd, reportedIdentityHash)
				if (err != nil) { return nil, err }
				if (identityIsInMyRange == false){
					continue
				}

				// We make sure that the profile/identity being reported is funded

				if (reportedIdentityType == "Moderator"){

					statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(reportedIdentityHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || scoreIsSufficient == false){
						continue
					}

				} else {

					// reviewedIdentityType == "Mate" or "Host"

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(reportedIdentityHash, networkType)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				if (reportedIdentityType == "Mate" && reportType == "Profile"){

					// We make sure profile is funded

					if (len(reportedHash) != 28){
						reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
						return nil, errors.New("ReadReport returning invalid length reportedHash for Profile report: " + reportedHashHex)
					}

					reportedProfileHash := [28]byte(reportedHash)

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(reportedProfileHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}

				} else if (reportedIdentityType == "Mate" && reportType == "Attribute"){

					// We make sure at least 1 profile with this attribute is funded

					checkIfAnyAttributeProfileIsFunded := func()(bool, error){

						if (len(reportedHash) != 27){
							reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
							return false, errors.New("ReadReport returning invalid length reportedHash for Attribute report: " + reportedHashHex)
						}

						reportedAttributeHash := [27]byte(reportedHash)

						anyExist, attributeProfileHashesList, err := badgerDatabase.GetAttributeProfilesList(reportedAttributeHash)
						if (err != nil) { return false, err }
						if (anyExist == false){
							return false, nil
						}

						for _, profileHash := range attributeProfileHashesList{

							statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(profileHash)
							if (err != nil) { return false, err }
							if (statusIsKnown == true && isFunded == true){
								return true, nil
							}
						}

						return false, nil
					}

					anyAttributeProfileIsFunded, err := checkIfAnyAttributeProfileIsFunded()
					if (err != nil) { return nil, err }
					if (anyAttributeProfileIsFunded == false){
						continue
					}
				}

				reportsInfoMap[reportHash] = reportedHash
			}

			return reportsInfoMap, nil
		}

		reportsInfoMap, err := getReportsInfoMap()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetIdentityReportsInfo(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, reportsInfoMap)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetMessageReportsInfo"){

		acceptableVersionsList, requestRangeStart, requestRangeEnd, requestedReportedMessageHashesList, err := serverRequest.ReadDecryptedServerRequest_GetMessageReportsInfo(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		getReportsInfoMap := func()(map[[30]byte][26]byte, error){

			hostModeEnabled, hostingAnyInboxes, myInboxRangeStart, myInboxRangeEnd, err := myRanges.GetMyInboxesToHostRange()
			if (err != nil) { return nil, err }
			if (hostModeEnabled == false){
				// We must have disabled host mode recently
				reportsInfoMap := make(map[[30]byte][26]byte)
				return reportsInfoMap, nil
			}
			if (hostingAnyInboxes == false){
				// We are not hosting any messages/ message reports
				reportsInfoMap := make(map[[30]byte][26]byte)
				return reportsInfoMap, nil
			}

			getRelevantMessageHashesList := func()([][26]byte, error){

				if (len(requestedReportedMessageHashesList) != 0){

					// We will get reports from requested message hashes list

					return requestedReportedMessageHashesList, nil
				}

				allReportedMessageHashesList, err := badgerDatabase.GetAllReportedMessageHashes()
				if (err != nil) { return nil, err }

				return allReportedMessageHashesList, nil
			}

			relevantMessageHashesList, err := getRelevantMessageHashesList()
			if (err != nil) { return nil, err }

			relevantReportHashesList := make([][30]byte, 0)

			for _, messageHash := range relevantMessageHashesList{

				anyExist, reportHashesList, err := badgerDatabase.GetMessageReportsList(messageHash)
				if (err != nil) { return nil, err }
				if (anyExist == true){
					relevantReportHashesList = append(relevantReportHashesList, reportHashesList...)
				}
			}

			// Map Structure: Report Hash -> Reported Message Hash
			reportsInfoMap := make(map[[30]byte][26]byte)

			for _, reportHash := range relevantReportHashesList{

				statusIsKnown, reportIsFunded, _, err := getFundedStatuses.GetReportIsFundedStatus(reportHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || reportIsFunded == false){
					continue
				}

				reportExists, reportBytes, err := badgerDatabase.GetReport(reportHash)
				if (err != nil) { return nil, err }
				if (reportExists == false){
					continue
				}

				ableToRead, currentReportHash, reportVersion, reportNetworkType, _, reportType, reportedHash, _, err := readReports.ReadReportAndHash(false, reportBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains invalid report.")
				}
				if (reportHash != currentReportHash){
					return nil, errors.New("Database corrupt: Report entry key does not match report hash")
				}

				if (reportType != "Message"){
					return nil, errors.New("Database corrupt: Message reports list contains non-message report")
				}

				if (len(reportedHash) != 26){
					reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
					return nil, errors.New("ReadReport returning invalid length reportedHash for Message report: " + reportedHashHex)
				}

				reportedMessageHash := [26]byte(reportedHash)

				isAcceptableVersion := slices.Contains(acceptableVersionsList, reportVersion)
				if (isAcceptableVersion == false){
					// This report is of a version that the requestor cannot accept
					// We must be running a different version of Seekia than them.
					continue
				}
				if (reportNetworkType != networkType){
					// Report belongs to a different networkType
					continue
				}

				metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(reportedMessageHash)
				if (err != nil) { return nil, err }
				if (metadataExists == false){
					// We cannot seed reports for messages whose metadata we do not know
					continue
				}
				if (messageNetworkType != networkType){
					// Report author must be malicious
					// They have created a report for a message belonging to a different networkType
					continue
				}

				isWithinMyRange, err := byteRange.CheckIfInboxIsWithinRange(myInboxRangeStart, myInboxRangeEnd, messageInbox)
				if (err != nil) { return nil, err }
				if (isWithinMyRange == false){
					continue
				}

				// We reduce the requested reports based on requested inbox range

				isWithinRequestRange, err := byteRange.CheckIfInboxIsWithinRange(requestRangeStart, requestRangeEnd, messageInbox)
				if (err != nil) { return nil, err }
				if (isWithinRequestRange == false){
					continue
				}

				// We make sure message is funded

				statusIsKnown, messageIsFunded, _, err := getFundedStatuses.GetMessageIsFundedStatus(reportedMessageHash)
				if (err != nil) { return nil, err }
				if (statusIsKnown == false || messageIsFunded == false){
					continue
				}

				reportsInfoMap[reportHash] = reportedMessageHash
			}

			return reportsInfoMap, nil
		}

		reportsInfoMap, err := getReportsInfoMap()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetMessageReportsInfo(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, reportsInfoMap)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetReports"){

		requestedReportHashesList, err := serverRequest.ReadDecryptedServerRequest_GetReports(decryptedRequestBytes)
		if (err != nil) { return nil, err }

		getReportsListForResponse := func()([][]byte, error){

			// We will reduce report list to only include reports within our hosted range
			// This is done to prevent fingerprinting

			// We also make sure reported identities/profiles/messages are funded

			reportsListForResponse := make([][]byte, 0)

			for _, reportHash := range requestedReportHashesList{

				reportExists, reportBytes, err := badgerDatabase.GetReport(reportHash)
				if (err != nil) { return nil, err }
				if (reportExists == false){
					continue
				}

				ableToRead, currentReportHash, _, reportNetworkType, _, reportType, reportedHash, _, err := readReports.ReadReportAndHash(false, reportBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("Database corrupt: Contains invalid report.")
				}
				if (reportHash != currentReportHash){
					return nil, errors.New("Database corrupt: Contains report with mismatched reportHash entry key")
				}
				if (reportNetworkType != networkType){
					// Requestor must be malicious
					// They should only request reports which we offered them
					// We will only have offered them reports on the current networkType
					continue
				}

				if (reportType == "Message"){

					if (len(reportedHash) != 26){
						reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
						return nil, errors.New("ReadReport returning invalid length reportedHash for Message report: " + reportedHashHex)
					}

					reportedMessageHash := [26]byte(reportedHash)

					metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(reportedMessageHash)
					if (err != nil){ return nil, err }
					if (metadataExists == false){
						// We cannot host reports for messages whose metadata we do not have.
						continue
					}
					if (messageNetworkType != networkType){
						// This should not happen because we would have already checked for this before serving report info to requestor 
						// Requestor must be maliciously requesting reports we never offered them
						continue
					}

					hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfMessageInboxIsWithinMyHostedRange(messageInbox)
					if (err != nil) { return nil, err }
					if (hostModeEnabled == false){
						// Host mode must have been disabled after earlier check. Return empty list.
						emptyList := make([][]byte, 0)
						return emptyList, nil
					}
					if (isWithinMyRange == false){
						continue
					}

					// We make sure message is funded

					statusIsKnown, messageIsFunded, _, err := getFundedStatuses.GetMessageIsFundedStatus(reportedMessageHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || messageIsFunded == false){
						continue
					}

				} else {

					// reportType == "Identity" or "Profile" or "Attribute"

					//Outputs:
					//	-bool: Report is malicious
					//	-bool: Reported identity hash found
					//	-[16]byte: Reported identity hash
					//	-error
					getReportedIdentityHash := func()(bool, bool, [16]byte, error){

						if (reportType == "Identity"){

							if (len(reportedHash) != 16){
								reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
								return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Identity report: " + reportedHashHex)
							}

							reportedIdentityHash := [16]byte(reportedHash)

							return false, true, reportedIdentityHash, nil
						}
						if (reportType == "Profile"){

							if (len(reportedHash) != 28){
								reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
								return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Profile report: " + reportedHashHex)
							}

							reportedProfileHash := [28]byte(reportedHash)

							metadataExists, _, profileNetworkType, profileAuthor, _, _, _, _, err := contentMetadata.GetProfileMetadata(reportedProfileHash)
							if (err != nil) { return false, false, [16]byte{}, err }
							if (metadataExists == false){
								return false, false, [16]byte{}, nil
							}
							if (profileNetworkType != networkType){
								return true, false, [16]byte{}, nil
							}

							return false, true, profileAuthor, nil
						}
						if (reportType == "Attribute"){

							if (len(reportedHash) != 27){
								reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
								return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Attribute report: " + reportedHashHex)
							}

							reportedAttributeHash := [27]byte(reportedHash)

							metadataExists, _, authorIdentityHash, attributeNetworkType, _, err := profileStorage.GetProfileAttributeMetadata(reportedAttributeHash)
							if (err != nil){ return false, false, [16]byte{}, err }
							if (metadataExists == false){
								return false, false, [16]byte{}, nil
							}
							if (attributeNetworkType != networkType){
								return true, false, [16]byte{}, nil
							}

							return false, true, authorIdentityHash, nil
						}

						return false, false, [16]byte{}, errors.New("ReadReport returning invalid reportType: " + reportType)
					}

					reportIsMalicious, metadataExists, reportedIdentityHash, err := getReportedIdentityHash()
					if (err != nil) { return nil, err }
					if (reportIsMalicious == true){
						// The requestor must be malicious, because we should have already checked for this condition
						//		before serving this report hash in our Get...ReportsInfo responses.
						continue
					}
					if (metadataExists == false){
						// We cannot seed a report whose reported identity hash we do not know
						continue
					}			

					// We make sure identity is within our hosted range

					hostModeEnabled, isInMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyHostedRange(reportedIdentityHash)
					if (err != nil) { return nil, err }
					if (hostModeEnabled == false){
						// Host mode must have been disabled after earlier check. Return empty list.
						emptyList := make([][]byte, 0)
						return emptyList, nil
					}

					if (isInMyRange == false){
						continue
					}

					// We make sure identity is funded

					reportedIdentityType, err := identity.GetIdentityTypeFromIdentityHash(reportedIdentityHash)
					if (err != nil){ return nil, err }

					if (reportedIdentityType == "Moderator"){

						statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(reportedIdentityHash)
						if (err != nil) { return nil, err }
						if (statusIsKnown == false || scoreIsSufficient == false){
							continue
						}

					} else {

						// reportedIdentityType == "Mate" or "Host"

						statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(reportedIdentityHash, networkType)
						if (err != nil) { return nil, err }
						if (statusIsKnown == false || isFunded == false){
							continue
						}
					}

					if (reportedIdentityType == "Mate" && reportType == "Profile"){

						// We make sure profile is funded

						if (len(reportedHash) != 28){
							reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
							return nil, errors.New("ReadReport returning invalid length reportedHash for Profile report: " + reportedHashHex)
						}

						reportedProfileHash := [28]byte(reportedHash)

						statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(reportedProfileHash)
						if (err != nil) { return nil, err }
						if (statusIsKnown == false || isFunded == false){
							continue
						}
					} else if (reportedIdentityType == "Mate" && reportType == "Attribute"){

						// We make sure at least 1 profile with this attribute is funded

						if (len(reportedHash) != 27){
							reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
							return nil, errors.New("ReadReport returning invalid length reportedHash for Attribute report: " + reportedHashHex)
						}

						reportedAttributeHash := [27]byte(reportedHash)

						checkIfAnyAttributeProfileIsFunded := func()(bool, error){

							anyExist, attributeProfileHashesList, err := badgerDatabase.GetAttributeProfilesList(reportedAttributeHash)
							if (err != nil) { return false, err }
							if (anyExist == false){
								return false, nil
							}

							for _, profileHash := range attributeProfileHashesList{

								statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(profileHash)
								if (err != nil) { return false, err }
								if (statusIsKnown == true && isFunded == true){
									return true, nil
								}
							}

							return false, nil
						}

						anyAttributeProfileIsFunded, err := checkIfAnyAttributeProfileIsFunded()
						if (err != nil) { return nil, err }
						if (anyAttributeProfileIsFunded == false){
							continue
						}
					}
				}

				reportsListForResponse = append(reportsListForResponse, reportBytes)
			}

			return reportsListForResponse, nil
		}

		reportsListForResponse, err := getReportsListForResponse()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetReports(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, reportsListForResponse)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "GetViewableStatuses"){

		requestedIdentityHashesList, requestedProfileHashesList, err := serverRequest.ReadDecryptedServerRequest_GetViewableStatuses(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		getIdentityHashStatusesMap := func()(map[[16]byte]bool, error){

			// Map Structure: Identity Hash -> Is Viewable status
			identityHashStatusesMap := make(map[[16]byte]bool)

			for _, userIdentityHash := range requestedIdentityHashesList{

				hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyHostedRange(userIdentityHash)
				if (err != nil) { return nil, err }
				if (hostModeEnabled == false){
					// Host mode has been disabled. We return empty response.
					emptyMap := make(map[[16]byte]bool)
					return emptyMap, nil
				}
				if (isWithinMyRange == false){
					continue
				}

				// We make sure identity is funded

				userIdentityType, err := identity.GetIdentityTypeFromIdentityHash(userIdentityHash)
				if (err != nil) { return nil, err }

				if (userIdentityType == "Moderator"){

					statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(userIdentityHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || scoreIsSufficient == false){
						continue
					}

				} else {

					// userIdentityType == "Mate" or "Host"

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(userIdentityHash, networkType)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				downloadingRequiredReviews, parametersExist, stickyStatusEstablished, identityIsViewableStatus, err := verifiedStickyStatus.GetVerifiedIdentityIsViewableStickyStatus(userIdentityHash, networkType)
				if (err != nil) { return nil, err }
				if (downloadingRequiredReviews == false){
					continue
				}
				if (parametersExist == false){
					// Host needs to download required moderation parameters to know sticky consensuses.
					// We skip identities.
					break
				}
				if (stickyStatusEstablished == false){
					continue
				}

				identityHashStatusesMap[userIdentityHash] = identityIsViewableStatus
			}

			return identityHashStatusesMap, nil
		}

		identityHashStatusesMap, err := getIdentityHashStatusesMap()
		if (err != nil) { return nil, err }

		getProfileHashStatusesMap := func()(map[[28]byte]bool, error){

			// Map Structure: Profile Hash -> Is Viewable status
			profileHashStatusesMap := make(map[[28]byte]bool)

			for _, profileHash := range requestedProfileHashesList{

				profileIsDisabled, profileMetadataIsKnown, profileNetworkType, profileAuthor, downloadingRequiredReviews, parametersFound, stickyStatusEstablished, profileIsViewableStatus, err := verifiedStickyStatus.GetVerifiedProfileIsViewableStickyStatus(profileHash)
				if (err != nil) { return nil, err }
				if (profileMetadataIsKnown == false){
					// We don't know profile author, so we cannot check if author is banned, or if profile is banned.
					// We don't know sticky status. We skip profile.
					continue
				}
				if (profileNetworkType != networkType){
					// Requestor must be malicious
					// They should only request viewable statuses for profiles on our networkType
					continue
				}

				if (profileIsDisabled == false){
					if (downloadingRequiredReviews == false){
						continue
					}
					if (parametersFound == false){
						// Host needs to download required network parameters to host sticky consensuses.
						// We skip all profiles.
						break
					}
					if (stickyStatusEstablished == false){
						continue
					}
				}

				hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyHostedRange(profileAuthor)
				if (err != nil) { return nil, err }
				if (hostModeEnabled == false){
					// Host mode has been disabled. Return empty response.
					emptyMap := make(map[[28]byte]bool)
					return emptyMap, nil
				}
				if (isWithinMyRange == false){
					continue
				}

				// We make sure identity is funded

				profileIdentityType, err := identity.GetIdentityTypeFromIdentityHash(profileAuthor)
				if (err != nil){ return nil, err }

				if (profileIdentityType == "Moderator"){

					statusIsKnown, _, scoreIsSufficient, _, _, err := moderatorScores.GetModeratorIdentityScore(profileAuthor)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || scoreIsSufficient == false){
						continue
					}

				} else {

					// profileIdentityType == "Mate" or "Host"

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetIdentityIsFundedStatus(profileAuthor, networkType)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				// If profile is Mate, we make sure it is funded

				if (profileIdentityType == "Mate"){

					statusIsKnown, isFunded, _, err := getFundedStatuses.GetMateProfileIsFundedStatus(profileHash)
					if (err != nil) { return nil, err }
					if (statusIsKnown == false || isFunded == false){
						continue
					}
				}

				if (profileIsDisabled == false && profileIsViewableStatus == false){
					// Profile is unviewable
					profileHashStatusesMap[profileHash] = false
					continue
				}

				// Profile is viewable
				// Now we check to make sure identity is not banned

				downloadingRequiredReviews, parametersExist, stickyStatusEstablished, identityIsViewableStatus, err := verifiedStickyStatus.GetVerifiedIdentityIsViewableStickyStatus(profileAuthor, networkType)
				if (err != nil) { return nil, err }
				if (downloadingRequiredReviews == false){
					continue
				}
				if (parametersExist == false){
					// Host needs to download required moderation parameters to host sticky consensuses.
					// We skip identity hashes for now.
					break
				}

				if (stickyStatusEstablished == false){
					continue
				}

				profileHashStatusesMap[profileHash] = identityIsViewableStatus
			}

			return profileHashStatusesMap, nil
		}

		profileHashStatusesMap, err := getProfileHashStatusesMap()
		if (err != nil) { return nil, err }

		responseBytes, err := serverResponse.CreateServerResponse_GetViewableStatuses(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, identityHashStatusesMap, profileHashStatusesMap)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}
	if (requestType == "BroadcastContent"){

		contentTypeToBroadcast, contentBytesList, err := serverRequest.ReadDecryptedServerRequest_BroadcastContent(decryptedRequestBytes)
		if (err != nil){
			return invalidRequestResponse, nil
		}

		// Map Structure: Content Hash -> We will accept and host
		contentAcceptedInfoMap := make(map[string]bool)

		// If host mode is disabled during this process, we will set this bool to true
		// If true, we will return an response which has contentAccepted = "No" for all content hashes
		hostModeDisabled := false

		for _, contentBytes := range contentBytesList{

			if (contentTypeToBroadcast == "Parameters"){

				ableToRead, parametersHash, _, parametersNetworkType, _, _, _, _, err := readParameters.ReadParametersAndHash(false, contentBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("ReadDecryptedServerRequest_BroadcastContent not verifying parameters are valid")
				}
				if (parametersNetworkType != networkType){
					// Requestor must be malicious
					// They should only broadcast content which belongs to our networkType.
					contentAcceptedInfoMap[string(parametersHash[:])] = false
					continue
				}

				//TODO: Check if we are hosting parameters

				contentAcceptedInfoMap[string(parametersHash[:])] = true

				_, _, err = parametersStorage.AddParameters(contentBytes)
				if (err != nil) { return nil, err }

				continue
			}
			if (contentTypeToBroadcast == "Message"){

				ableToRead, messageHash, _, messageNetworkType, messageInbox, _, _, _, _, _, _, err := readMessages.ReadChatMessagePublicDataAndHash(false, contentBytes)
				if (err != nil){
					return nil, errors.New("ReadDecryptedServerRequest_BroadcastContent not verifying message is valid")
				}
				if (ableToRead == false){
					return nil, errors.New("ReadDecryptedServerRequest_BroadcastContent not verifying message is valid")
				}
				if (messageNetworkType != networkType){
					// Requestor must be malicious
					// They should only broadcast content which belongs to our networkType.
					contentAcceptedInfoMap[string(messageHash[:])] = false
					continue
				}

				hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfMessageInboxIsWithinMyHostedRange(messageInbox)
				if (err != nil) { return nil, err }
				if (hostModeEnabled == false){
					hostModeDisabled = true
					contentAcceptedInfoMap[string(messageHash[:])] = false
					continue
				}

				contentAcceptedInfoMap[string(messageHash[:])] = isWithinMyRange

				if (isWithinMyRange == true){

					// We add message to database

					messageIsWellFormed, err := chatMessageStorage.AddMessage(contentBytes)
					if (err != nil) { return nil, err }
					if (messageIsWellFormed == false){
						return nil, errors.New("AddMessage not verifying message the same way ReadChatMessagePublicData is")
					}
				}

				continue
			}
			if (contentTypeToBroadcast == "Profile"){

				ableToRead, profileHash, _, profileNetworkType, profileAuthor, _, _, _, err := readProfiles.ReadProfileAndHash(false, contentBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("ReadDecryptedServerRequest_BroadcastContent not verifying profile is valid.")	
				}
				if (profileNetworkType != networkType){
					// Requestor must be malicious
					// They should only broadcast content which belongs to our networkType.
					contentAcceptedInfoMap[string(profileHash[:])] = false
					continue
				}

				hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyHostedRange(profileAuthor)
				if (err != nil) { return nil, err }
				if (hostModeEnabled == false){
					hostModeDisabled = true
					contentAcceptedInfoMap[string(profileHash[:])] = false
					continue
				}

				contentAcceptedInfoMap[string(profileHash[:])] = isWithinMyRange

				if (isWithinMyRange == true){

					// We add profile to database

					profileIsWellFormed, addedProfileHash, err := profileStorage.AddUserProfile(contentBytes)
					if (err != nil){ return nil, err }
					if (profileIsWellFormed == false){
						return nil, errors.New("AddUserProfile not verifying profile the same way ReadProfile is")
					}
					if (addedProfileHash != profileHash){
						return nil, errors.New("AddUserProfile returning mismatched profile hash")
					}
				}

				continue
			}
			if (contentTypeToBroadcast == "Review"){

				ableToRead, reviewHash, _, reviewNetworkType, _, _, reviewType, reviewedHash, _, _, err := readReviews.ReadReviewAndHash(false, contentBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("ReadDecryptedServerRequest_BroadcastContent not verifying review is valid.")
				}
				if (reviewNetworkType != networkType){
					// Requestor must be malicious
					// They should only broadcast content which belongs to our networkType.
					contentAcceptedInfoMap[string(reviewHash[:])] = false
					continue
				}

				//Outputs:
				//	-bool: Host mode is enabled
				//	-bool: We will host review
				//	-error
				checkIfWeWillHostReview := func()(bool, bool, error){

					if (reviewType == "Message"){

						if (len(reviewedHash) != 26){
							reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
							return false, false, errors.New("ReadReview returning invalid length reviewedHash for Message review: " + reviewedHashHex)
						}

						reviewedMessageHash := [26]byte(reviewedHash)

						metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(reviewedMessageHash)
						if (err != nil) { return false, false, err }
						if (metadataExists == false){
							// We cannot accept reviews for messages whose metadata we do not know
							// This is because we cannot seed reviews for messages whose metadata we do not know
							return true, false, nil
						}
						if (messageNetworkType != reviewNetworkType){
							// The review is reviewing a message from a different network type
							// It is a malicious review. We will not host it.
							return true, false, nil
						}

						hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfMessageInboxIsWithinMyHostedRange(messageInbox)
						if (err != nil) { return false, false, err }

						return hostModeEnabled, isWithinMyRange, nil
					}
					// reviewType == "Identity" or "Profile" or "Attribute"

					//Outputs:
					//	-bool: Reviewed identity hash is known
					//	-bool: Review is malicious
					//	-[16]byte: Reviewed identity hash
					//	-error
					getReviewedIdentityHash := func()(bool, bool, [16]byte, error){

						if (reviewType == "Identity"){

							if (len(reviewedHash) != 16){
								reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
								return false, false, [16]byte{}, errors.New("ReadReview returning invalid length reviewedHash for Identity review: " + reviewedHashHex)
							}

							reviewedIdentityHash := [16]byte(reviewedHash)

							return true, false, reviewedIdentityHash, nil
						}
						if (reviewType == "Profile"){

							if (len(reviewedHash) != 28){
								reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
								return false, false, [16]byte{}, errors.New("ReadReview returning invalid length reviewedHash for Profile review: " + reviewedHashHex)
							}

							reviewedProfileHash := [28]byte(reviewedHash)

							metadataExists, _, profileNetworkType, reviewedProfileAuthor, _, _, _, _, err := contentMetadata.GetProfileMetadata(reviewedProfileHash)
							if (err != nil) { return false, false, [16]byte{}, err }
							if (metadataExists == false){
								return false, false, [16]byte{}, nil
							}
							if (profileNetworkType != reviewNetworkType){
								// Review is reviewing a profile from a different network type
								// Review and author of review are malicious
								return true, true, [16]byte{}, nil
							}

							return true, false, reviewedProfileAuthor, nil
						}
						if (reviewType == "Attribute"){

							if (len(reviewedHash) != 27){
								reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
								return false, false, [16]byte{}, errors.New("ReadReview returning invalid length reviewedHash for Attribute review: " + reviewedHashHex)
							}

							reviewedAttributeHash := [27]byte(reviewedHash)

							metadataExists, _, authorIdentityHash, attributeNetworkType, _, err := profileStorage.GetProfileAttributeMetadata(reviewedAttributeHash)
							if (err != nil) { return false, false, [16]byte{}, err }
							if (metadataExists == false){
								return false, false, [16]byte{}, nil	
							}
							if (attributeNetworkType != reviewNetworkType){
								// Review is reviewing an attribute from a different network type
								// Review and author of review are malicious
								return true, true, [16]byte{}, nil
							}

							return true, false, authorIdentityHash, nil
						}

						return false, false, [16]byte{}, errors.New("ReadReview returning invalid reviewType: " + reviewType)	
					}

					reviewedIdentityHashIsKnown, reviewIsMalicious, reviewedIdentityHash, err := getReviewedIdentityHash()
					if (err != nil) { return false, false, err }
					if (reviewedIdentityHashIsKnown == false){
						return true, false, nil
					}
					if (reviewIsMalicious == true){
						return true, false, nil
					}

					hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyHostedRange(reviewedIdentityHash)
					if (err != nil) { return false, false, err }
					
					return hostModeEnabled, isWithinMyRange, nil
				}

				hostModeIsEnabled, weWillHostReview, err := checkIfWeWillHostReview()
				if (err != nil) { return nil, err }
				if (hostModeIsEnabled == false){
					hostModeDisabled = true
					contentAcceptedInfoMap[string(reviewHash[:])] = false
					continue
				}
				if (weWillHostReview == false){
					contentAcceptedInfoMap[string(reviewHash[:])] = false
					continue
				}

				contentAcceptedInfoMap[string(reviewHash[:])] = true

				// We add review to database

				reviewIsWellFormed, err := reviewStorage.AddReview(contentBytes)
				if (err != nil) { return nil, err }
				if (reviewIsWellFormed == false){
					return nil, errors.New("ReadReview not verifying review the same way that AddReview is")
				}

				continue
			}
			if (contentTypeToBroadcast == "Report"){

				ableToRead, reportHash, _, reportNetworkType, _, reportType, reportedHash, _, err := readReports.ReadReportAndHash(false, contentBytes)
				if (err != nil) { return nil, err }
				if (ableToRead == false){
					return nil, errors.New("ReadDecryptedServerRequest_BroadcastContent not verifying report")
				}
				if (reportNetworkType != networkType){
					// Requestor must be malicious
					// They should only broadcast content which belongs to our networkType.
					contentAcceptedInfoMap[string(reportHash[:])] = false
					continue
				}

				//Outputs:
				//	-bool: Host mode enabled
				//	-bool: We will host Report
				//	-error
				checkIfWeWillHostReport := func()(bool, bool, error){

					if (reportType == "Message"){

						// We see if we are hosting the reported message

						if (len(reportedHash) != 26){
							reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
							return false, false, errors.New("ReadReport returning invalid length reportedHash for Message report: " + reportedHashHex)
						}

						reportedMessageHash := [26]byte(reportedHash)

						metadataExists, _, messageNetworkType, _, messageInbox, _, err := contentMetadata.GetMessageMetadata(reportedMessageHash)
						if (err != nil) { return false, false, err }
						if (metadataExists == false){
							// We cannot accept reports for messages whose metadata we do not know
							// This is because we cannot seed reports for messages whose metadata we do not know
							return true, false, nil
						}
						if (messageNetworkType != reportNetworkType){
							// Report is reporting a message from a different networkType
							// Report and report author must be malicious
							return true, false, nil
						}

						hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfMessageInboxIsWithinMyHostedRange(messageInbox)
						if (err != nil) { return false, false, err }

						return hostModeEnabled, isWithinMyRange, nil
					}
					// reportType = "Identity" or "Profile" or "Attribute"
						
					//Outputs:
					//	-bool: Reported identity hash is known
					//	-bool: Report is malicious
					//	-[16]byte: Reported identity hash
					//	-error
					getReportedIdentityHash := func()(bool, bool, [16]byte, error){

						if (reportType == "Identity"){

							if (len(reportedHash) != 16){
								reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
								return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Identity report: " + reportedHashHex)
							}

							reportedIdentityHash := [16]byte(reportedHash)

							return true, false, reportedIdentityHash, nil
						}
						if (reportType == "Profile"){

							if (len(reportedHash) != 28){
								reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
								return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Profile report: " + reportedHashHex)
							}

							reportedProfileHash := [28]byte(reportedHash)

							metadataExists, _, profileNetworkType, reportedProfileAuthor, _, _, _, _, err := contentMetadata.GetProfileMetadata(reportedProfileHash)
							if (err != nil) { return false, false, [16]byte{}, err }
							if (metadataExists == false){
								return false, false, [16]byte{}, nil
							}
							if (profileNetworkType != reportNetworkType){
								// Report is reporting a profile from a different networkType
								// Report and report author must be malicious
								return true, true, [16]byte{}, nil
							}

							return true, false, reportedProfileAuthor, nil
						}
						if (reportType == "Attribute"){

							if (len(reportedHash) != 27){
								reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
								return false, false, [16]byte{}, errors.New("ReadReport returning invalid length reportedHash for Attribute report: " + reportedHashHex)
							}

							reportedAttributeHash := [27]byte(reportedHash)

							metadataExists, _, authorIdentityHash, attributeNetworkType, _, err := profileStorage.GetProfileAttributeMetadata(reportedAttributeHash)
							if (err != nil) { return false, false, [16]byte{}, err }
							if (metadataExists == false){
								return false, false, [16]byte{}, nil
							}
							if (attributeNetworkType != reportNetworkType){
								// Report is reporting an attribute from a different networkType
								// Report and report author must be malicious
								return true, true, [16]byte{}, nil
							}

							return true, false, authorIdentityHash, nil
						}

						return false, false, [16]byte{}, errors.New("ReadReport returning invalid reportType: " + reportType)	
					}

					reportedIdentityHashIsKnown, reportIsMalicious, reportedIdentityHash, err := getReportedIdentityHash()
					if (err != nil) { return false, false, err }
					if (reportedIdentityHashIsKnown == false){
						return true, false, nil
					}
					if (reportIsMalicious == true){
						return true, false, nil
					}

					hostModeEnabled, isWithinMyRange, err := myRanges.CheckIfIdentityHashIsWithinMyHostedRange(reportedIdentityHash)
					if (err != nil) { return false, false, err }

					return hostModeEnabled, isWithinMyRange, nil
				}

				hostModeIsEnabled, weWillHostReport, err := checkIfWeWillHostReport()
				if (err != nil) { return nil, err }
				if (hostModeIsEnabled == false){
					hostModeDisabled = true
					contentAcceptedInfoMap[string(reportHash[:])] = false
					continue
				}
				if (weWillHostReport == false){
					contentAcceptedInfoMap[string(reportHash[:])] = false
					continue
				}

				contentAcceptedInfoMap[string(reportHash[:])] = true

				// We add report to database

				reportIsWellFormed, err := reportStorage.AddReport(contentBytes)
			 	if (err != nil) { return nil, err }
				if (reportIsWellFormed == false){
					return nil, errors.New("ReadReport not verifying report the same way as AddReport is")
				}

				continue
			}

			return nil, errors.New("ReadDecryptedServerRequest_BroadcastContent returning invalid contentTypeToBroadcast: " + contentTypeToBroadcast)
		}

		if (hostModeDisabled == true){

			// Host mode is disabled, so we cannot accept any content

			for contentHash, _ := range contentAcceptedInfoMap{
				contentAcceptedInfoMap[contentHash] = false
			}
		}

		responseBytes, err := serverResponse.CreateServerResponse_BroadcastContent(myHostPublicIdentityKey, myHostPrivateIdentityKey, connectionKey, requestIdentifier, contentAcceptedInfoMap)
		if (err != nil) { return nil, err }

		return responseBytes, nil
	}

	//TODO: GetAddressDeposits, GetIdentityPaymentProofs, GetMessagePaymentProofs

	// Request contains unknown requestType
	return invalidRequestResponse, nil
}



