
// sendRequests provides functions for making requests to Seekia hosts
// Each function provides a way to send a server request to a host through an active connection
// This package is imported by queryHosts.
// queryHosts is a higher level package which provides functions to split requests up, to save retrieved data, and more

package sendRequests

//TODO: Add if request successful, remove host from unreachable list

//TODO: Verify that input does not exceed maximum size for request of that type

//TODO: Add ability to handle busy responses
// If host is busy, we will keep track of that in a new package and not try to recontact them for a while

import "seekia/internal/byteRange"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/identity"
import "seekia/internal/messaging/readMessages"
import "seekia/internal/moderation/readReports"
import "seekia/internal/moderation/readReviews"
import "seekia/internal/network/maliciousHosts"
import "seekia/internal/network/mateCriteria"
import "seekia/internal/network/peerClient"
import "seekia/internal/network/serverRequest"
import "seekia/internal/network/serverResponse"
import "seekia/internal/parameters/readParameters"
import "seekia/internal/profiles/readProfiles"
import "seekia/internal/readContent"

import "bytes"
import "errors"
import "slices"

//Outputs:
//	-bool: Download successsful
//	-map[string]int64: Map of ParametersType -> Parameters Creation time
//	-error
func GetParametersInfoFromHost(connectionIdentifier [21]byte, hostIdentityHash [16]byte, networkType byte)(bool, map[string]int64, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetParametersInfoFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetParametersInfoFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetParametersInfo(hostIdentityHash, connectionKey, networkType)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, parametersInfoMap, err := serverResponse.ReadServerResponse_GetParametersInfo(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Host sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){
			// Host sent invalid response, must be malicious

			return false, nil
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }	
	if (responseIsValid == false){

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	return true, parametersInfoMap, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-[][]byte: List of parameters
//	-error
func GetParametersFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		parametersTypesToRetrieveList []string)(bool, [][]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetParametersFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetParametersFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetParameters(hostIdentityHash, connectionKey, networkType, parametersTypesToRetrieveList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, parametersList, err := serverResponse.ReadServerResponse_GetParameters(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}
	
	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		for _, parametersBytes := range parametersList{

			ableToRead, _, parametersNetworkType, _, parametersType, _, _, err := readParameters.ReadParameters(false, parametersBytes)
			if (err != nil) { return false, err }
			if (ableToRead == false){
				return false, errors.New("ReadServerResponse_GetParameters not verifying network parameters")
			}

			if (parametersNetworkType != networkType){
				// We did not request parameters for this networkType.
				return false, nil
			}

			isExpected := slices.Contains(parametersTypesToRetrieveList, parametersType)
			if (isExpected == false){
				// We did not request this parameters type. Response is malformed.
				return false, nil
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }
	if (responseIsValid == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	return true, parametersList, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-[]serverResponse.ProfileInfoStruct: Profiles info map list
//	-error
func GetProfilesInfoFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		profileTypeToRetrieve string,
		rangeStart [16]byte,
		rangeEnd [16]byte,
		identityHashesList [][16]byte,
		criteria []byte,
		getNewestProfilesOnly bool,
		getViewableProfilesOnly bool)(bool, []serverResponse.ProfileInfoStruct, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetProfilesInfoFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetProfilesInfoFromHost called with networkType which does not match connection metadata.")
	}

	acceptableProfileVersionsList := []int{1}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetProfilesInfo(hostIdentityHash, connectionKey, networkType, acceptableProfileVersionsList, profileTypeToRetrieve, rangeStart, rangeEnd, identityHashesList, criteria, getNewestProfilesOnly, getViewableProfilesOnly)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, profileInfoObjectsList, err := serverResponse.ReadServerResponse_GetProfilesInfo(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){
			// Peer sent invalid response, must be malicious

			return false, nil
		}

		// Now we verify received profile identity hashes fulfill request filters

		// We use map to make sure that only 1 profile hash per identity is returned (if getNewestProfilesOnly == true)
		// Map structure: Identity Hash -> Nothing
		identitiesMap := make(map[[16]byte]struct{})

		for _, profileInfoObject := range profileInfoObjectsList{

			receivedProfileAuthor := profileInfoObject.ProfileAuthor

			identityType, err := identity.GetIdentityTypeFromIdentityHash(receivedProfileAuthor)
			if (err != nil){
				receivedProfileAuthorHex := encoding.EncodeBytesToHexString(receivedProfileAuthor[:])
				return false, errors.New("ReadServerResponse_GetProfilesInfo not verifying profileInfoObject receivedProfileAuthor: " + receivedProfileAuthorHex)
			}

			if (identityType != profileTypeToRetrieve){
				return false, nil
			}

			if (len(identityHashesList) != 0){
				identityIsInRequestedList := slices.Contains(identityHashesList, receivedProfileAuthor)
				if (identityIsInRequestedList == false){
					return false, nil
				}
			}

			isWithinRange, err := byteRange.CheckIfIdentityHashIsWithinRange(rangeStart, rangeEnd, receivedProfileAuthor)
			if (err != nil) { return false, err }
			if (isWithinRange == false){
				return false, nil
			}

			if (getNewestProfilesOnly == true){

				_, exists := identitiesMap[receivedProfileAuthor]
				if (exists == true){
					// We received two profiles with the same author in a getNewestProfilesOnly response.
					return false, nil
				}

				identitiesMap[receivedProfileAuthor] = struct{}{}
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }
	if (responseIsValid == false){

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	return true, profileInfoObjectsList, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-[][]byte: List of profiles
//	-error
func GetProfilesFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		profileTypeToRetrieve string,
		profileHashesList [][28]byte,
		expectedProfileIdentityHashesMap map[[28]byte][16]byte, // Profile Hash -> Profile Identity hash
		expectedProfileCreationTimesMap map[[28]byte]int64, // Profile Hash -> Profile Creation Time
		expectedCriteria []byte)(bool, [][]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetProfilesFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetProfilesFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetProfiles(hostIdentityHash, connectionKey, networkType, profileTypeToRetrieve, profileHashesList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, profilesList, err := serverResponse.ReadServerResponse_GetProfiles(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		if (len(profilesList) > len(profileHashesList)){
			return false, nil
		}

		for _, profileBytes := range profilesList{

			ableToRead, profileHash, profileVersion, profileNetworkType, profileIdentityHash, profileCreationTime, _, rawProfileMap, err := readProfiles.ReadProfileAndHash(false, profileBytes)
			if (err != nil) { return false, err }
			if (ableToRead == false){
				return false, errors.New("ReadServerResponse_GetProfiles not verifying received profile is valid.")
			}
			profileHashIsExpected := slices.Contains(profileHashesList, profileHash)
			if (profileHashIsExpected == false){
				return false, nil
			}
			if (profileNetworkType != networkType){
				// The host must have sent us a different networkType profile when we requested profilesInfo
				return false, nil
			}

			expectedProfileIdentityHash, profileExists := expectedProfileIdentityHashesMap[profileHash]
			if (profileExists == false){
				return false, errors.New("ExpectedProfileIdentityHashesMap missing entry for profile contained in ProfileHashesList")
			}
			if (profileIdentityHash != expectedProfileIdentityHash){
				return false, nil
			}

			expectedProfileCreationTime, exists := expectedProfileCreationTimesMap[profileHash]
			if (exists == false){
				return false, errors.New("expectedProfileCreationTimesMap missing entry for profile contained in ProfileHashesList")
			}
			if (profileCreationTime != expectedProfileCreationTime){
				// Profile is does not fulfill the expected CreationTime
				return false, nil
			}

			if (expectedCriteria != nil){

				criteriaIsValid, fulfillsCriteria, err := mateCriteria.CheckIfMateProfileFulfillsCriteria(false, profileVersion, rawProfileMap, expectedCriteria)
				if (err != nil) { return false, err }
				if (criteriaIsValid == false){
					return false, errors.New("GetProfilesFromHost called with invalid expectedCriteria")
				}
				if (fulfillsCriteria == false){
					return false, nil
				}
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }
	if (responseIsValid == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	return true, profilesList, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-[][26]byte: Received message hashes list
//	-error
func GetMessageHashesListFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		rangeStart [10]byte,
		rangeEnd [10]byte,
		inboxesToRetrieveList [][10]byte,
		getViewableMessagesOnly bool,
		getDecryptableMessagesOnly bool)(bool, [][26]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetMessageHashesListFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetMessageHashesListFromHost called with networkType which does not match connection metadata.")
	}

	acceptableMessageVersionsList := []int{1}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetMessageHashesList(hostIdentityHash, connectionKey, networkType, acceptableMessageVersionsList, rangeStart, rangeEnd, inboxesToRetrieveList, getViewableMessagesOnly, getDecryptableMessagesOnly)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, messageHashesList_Received, err := serverResponse.ReadServerResponse_GetMessageHashesList(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){
			// Peer sent invalid response, must be malicious

			return false, nil
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }	
	if (responseIsValid == false){

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	return true, messageHashesList_Received, nil
}



//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-[][]byte: List of messages
//	-error
func GetMessagesFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		messageHashesList [][26]byte,
		expectedInboxRangeStart [10]byte,
		expectedInboxRangeEnd [10]byte,
		expectedInboxesList [][10]byte)(bool, [][]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetMessagesFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetMessagesFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetMessages(hostIdentityHash, connectionKey, networkType, messageHashesList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, messagesList, err := serverResponse.ReadServerResponse_GetMessages(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		for _, messageBytes := range messagesList{

			ableToRead, messageHash, _, messageNetworkType, messageInbox, _, _, _, _, _, _, err := readMessages.ReadChatMessagePublicDataAndHash(false, messageBytes)
			if (err != nil) { return false, err }
			if (ableToRead == false){
				return false, errors.New("ReadServerResponse_GetMessages not verifying each message.")
			}

			isExpected := slices.Contains(messageHashesList, messageHash)
			if (isExpected == false){
				return false, nil
			}
			if (messageNetworkType != networkType){
				// Host must have sent us a different networkType messageHash when we got messageHashesList response
				return false, nil
			}

			if (len(expectedInboxesList) != 0){

				messageInboxIsExpected := slices.Contains(expectedInboxesList, messageInbox)
				if (messageInboxIsExpected == false){
					return false, nil
				}
			} else {

				inboxIsWithinRange, err := byteRange.CheckIfInboxIsWithinRange(expectedInboxRangeStart, expectedInboxRangeEnd, messageInbox)
				if (err != nil) { return false, err }
				if (inboxIsWithinRange == false){
					return false, nil
				}
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }
	if (responseIsValid == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	return true, messagesList, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-map[[29]byte][]byte: Received reviews info map (Review Hash -> Reviewed Hash)
//	-error
func GetIdentityReviewsInfoFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		identityTypeToRetrieve string,
		rangeStart [16]byte,
		rangeEnd [16]byte,
		reviewedIdentityHashesList [][16]byte,
		reviewersList [][16]byte)(bool, map[[29]byte][]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetIdentityReviewsInfoFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetIdentityReviewsInfoFromHost called with networkType which does not match connection metadata.")
	}

	acceptableReviewVersionList := []int{1}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetIdentityReviewsInfo(hostIdentityHash, connectionKey, networkType, acceptableReviewVersionList, identityTypeToRetrieve, rangeStart, rangeEnd, reviewedIdentityHashesList, reviewersList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseBytes, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, reviewsInfoMap, err := serverResponse.ReadServerResponse_GetIdentityReviewsInfo(responseBytes, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){
			// Peer sent invalid response, must be malicious

			return false, nil
		}

		// Now we verify received reviews info map fulfills request filters

		for _, reviewedHash := range reviewsInfoMap{

			// reviewedHash is either Profile hash or identity hash or attribute hash

			reviewedHashType, err := helpers.GetReviewedTypeFromReviewedHash(reviewedHash)
			if (err != nil) {
				reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
				return false, errors.New("ReadServerResponse_GetReviewsInfo not verifying reviewsInfoMap reviewedHash: " + reviewedHashHex)
			}

			if (reviewedHashType != "Identity" && reviewedHashType != "Profile" && reviewedHashType != "Attribute"){
				// Must be a message hash. Host is malicious.
				reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
				return false, errors.New("ReadServerResponse_GetReviewsInfo not verifying reviewsInfoMap reviewedHash: " + reviewedHashHex)
			}

			if (reviewedHashType == "Identity"){

				if (len(reviewedHash) != 16){
					reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
					return false, errors.New("GetReviewedTypeFromReviewedHash returning Identity for different length reviewedHash: " + reviewedHashHex)
				}

				reviewedIdentityHash := [16]byte(reviewedHash)
		
				if (len(reviewedIdentityHashesList) == 0){

					// We only need to verify identityType if a reviewedHashesList was not requested	

					identityType, err := identity.GetIdentityTypeFromIdentityHash(reviewedIdentityHash)
					if (err != nil){
						reviewedIdentityHashHex := encoding.EncodeBytesToHexString(reviewedIdentityHash[:])
						return false, errors.New("GetReviewedTypeFromReviewedHash fails to verify identity hash: " + reviewedIdentityHashHex)
					}

					if (identityType != identityTypeToRetrieve){									
						return false, nil		
					}
				}

				isWithinRange, err := byteRange.CheckIfIdentityHashIsWithinRange(rangeStart, rangeEnd, reviewedIdentityHash)
				if (err != nil) { return false, err }
				if (isWithinRange == false){
					return false, nil
				}

				if (len(reviewedIdentityHashesList) != 0){

					identityHashIsInRequestedList := slices.Contains(reviewedIdentityHashesList, reviewedIdentityHash)
					if (identityHashIsInRequestedList == false){
						return false, nil
					}
				}
				continue

			} else if (reviewedHashType == "Profile"){

				if (len(reviewedHash) != 28){
					reviewedHashHex := encoding.EncodeBytesToHexString(reviewedHash)
					return false, errors.New("GetReviewedTypeFromReviewedHash returning Profile for different length reviewedHash: " + reviewedHashHex)
				}

				reviewedProfileHash := [28]byte(reviewedHash)

				if (len(reviewedIdentityHashesList) == 0){

					// We only need to verify profileType if a reviewedIdentityHashesList was not requested

					profileType, isDisabled, err := readProfiles.ReadProfileHashMetadata(reviewedProfileHash)
					if (err != nil) {
						reviewedProfileHashHex := encoding.EncodeBytesToHexString(reviewedProfileHash[:])
						return false, errors.New("ReadServerResponse_GetReviewsInfo not verifying reviewsInfoMap reviewedHash: " + reviewedProfileHashHex)
					}
					if (isDisabled == true){
						return false, errors.New("ReadServerResponse_GetReviewsInfo not verifying reviewsInfoMap reviewedHash: Profile hash is disabled.")
					}
					if (profileType != identityTypeToRetrieve){
						return false, nil
					}
				}
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }	
	if (responseIsValid == false){

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	return true, reviewsInfoMap, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-map[[29]byte][26]byte: Received reviews info map (Review Hash -> Reviewed Message Hash)
//	-error
func GetMessageReviewsInfoFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		rangeStart [10]byte,
		rangeEnd [10]byte,
		reviewedMessageHashesList [][26]byte,
		reviewersList [][16]byte)(bool, map[[29]byte][26]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetMessageReviewsInfoFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetMessageReviewsInfoFromHost called with networkType which does not match connection metadata.")
	}

	acceptableReviewVersionList := []int{1}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetMessageReviewsInfo(hostIdentityHash, connectionKey, networkType, acceptableReviewVersionList, rangeStart, rangeEnd, reviewedMessageHashesList, reviewersList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseBytes, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, reviewsInfoMap, err := serverResponse.ReadServerResponse_GetMessageReviewsInfo(responseBytes, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){
			// Peer sent invalid response, must be malicious

			return false, nil
		}

		// Now we verify received reviews info map fulfills request filters

		for _, reviewedHash := range reviewsInfoMap{

			// reviewedHash is a message hash

			if (len(reviewedMessageHashesList) != 0){

				messageHashIsInRequestedList := slices.Contains(reviewedMessageHashesList, reviewedHash)
				if (messageHashIsInRequestedList == false){
					return false, nil
				}
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }	
	if (responseIsValid == false){

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	return true, reviewsInfoMap, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-[][]byte: Retrieved list of reviews
//	-error
func GetReviewsFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		reviewHashesList [][29]byte,
		expectedReviewsInfoMap map[[29]byte][]byte, // Review Hash -> Reviewed Hash
		expectedReviewersList [][16]byte)(bool, [][]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetReviewsFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetReviewsFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetReviews(hostIdentityHash, connectionKey, networkType, reviewHashesList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, reviewsList, err := serverResponse.ReadServerResponse_GetReviews(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		for _, reviewBytes := range reviewsList{

			ableToRead, reviewHash, _, reviewNetworkType, authorIdentityHash, _, _, reviewReviewedHash, _, _, err := readReviews.ReadReviewAndHash(false, reviewBytes)
			if (err != nil) { return false, err }
			if (ableToRead == false){
				return false, errors.New("ReadServerResponse_GetReviews not verifying each review")
			}
			if (reviewNetworkType != networkType){
				return false, nil
			}

			if (len(expectedReviewersList) != 0){

				isExpectedReviewer := slices.Contains(expectedReviewersList, authorIdentityHash)
				if (isExpectedReviewer == false){
					return false, nil
				}			
			}

			expectedReviewedHash, exists := expectedReviewsInfoMap[reviewHash]
			if (exists == false) {
				// We received an unexpected reviewHash
				return false, nil
			}

			areEqual := bytes.Equal(reviewReviewedHash, expectedReviewedHash)
			if (areEqual == false){
				// We received an unexpected reviewedHash for expected reviewHash				
				return false, nil
			}			
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }
	if (responseIsValid == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	return true, reviewsList, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-map[[30]byte][]byte: Reports info map (Report Hash -> Reported Hash)
//	-error
func GetIdentityReportsInfoFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		identityTypeToRetrieve string,
		rangeStart [16]byte,
		rangeEnd [16]byte,
		reportedIdentityHashesList [][16]byte)(bool, map[[30]byte][]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetIdentityReportsInfoFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetIdentityReportsInfoFromHost called with networkType which does not match connection metadata.")
	}

	acceptableReportVersionsList := []int{1}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetIdentityReportsInfo(hostIdentityHash, connectionKey, networkType, acceptableReportVersionsList, identityTypeToRetrieve, rangeStart, rangeEnd, reportedIdentityHashesList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseBytes, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, reportsInfoMap, err := serverResponse.ReadServerResponse_GetIdentityReportsInfo(responseBytes, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){
			// Peer sent invalid response, must be malicious

			return false, nil
		}

		// Now we verify received reports info map fulfills request filters

		for _, reportedHash := range reportsInfoMap{

			// reportedHash is either Profile hash or identity hash or Attribute hash

			reportedType, err := helpers.GetReportedTypeFromReportedHash(reportedHash)
			if (err != nil){
				reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
				return false, errors.New("ReadServerResponse_GetReportsInfo not verifying reportedHash: " + reportedHashHex)
			}

			if (reportedType != "Identity" && reportedType != "Profile" && reportedType != "Attribute"){
				reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
				return false, errors.New("ReadServerResponse_GetReportsInfo not verifying reportedHash: " + reportedHashHex)
			}

			if (reportedType == "Identity"){

				if (len(reportedHash) != 16){
					reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
					return false, errors.New("GetReportedTypeFromReportedHash returning Identity for different length reportedHash: " + reportedHashHex)
				}

				reportedIdentityHash := [16]byte(reportedHash)

				if (len(reportedIdentityHashesList) == 0){

					identityType, err := identity.GetIdentityTypeFromIdentityHash(reportedIdentityHash)
					if (err != nil){
						reportedIdentityHashHex := encoding.EncodeBytesToHexString(reportedIdentityHash[:])
						return false, errors.New("GetReportedTypeFromReportedHash not verifying identityHash: " + reportedIdentityHashHex)
					}

					if (identityType != identityTypeToRetrieve){
						// We only need to verify identityType if a reportedHashesList was not requested
						return false, nil	
					}
				}

				isWithinRange, err := byteRange.CheckIfIdentityHashIsWithinRange(rangeStart, rangeEnd, reportedIdentityHash)
				if (err != nil) { return false, err }
				if (isWithinRange == false){
					return false, nil
				}

				if (len(reportedIdentityHashesList) != 0){
					identityHashIsInRequestedList := slices.Contains(reportedIdentityHashesList, reportedIdentityHash)
					if (identityHashIsInRequestedList == false){
						return false, nil
					}
				}

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

				if (len(reportedHash) != 28){
					reportedHashHex := encoding.EncodeBytesToHexString(reportedHash)
					return false, errors.New("GetReportedTypeFromReportedHash returning Profile for different length reportedHash: " + reportedHashHex)
				}

				reportedProfileHash := [28]byte(reportedHash)

				if (len(reportedIdentityHashesList) == 0){

					// We only need to verify profileType if a reportedHashesList was not requested

					profileType, isDisabled, err := readProfiles.ReadProfileHashMetadata(reportedProfileHash)
					if (err != nil) {
						return false, errors.New("GetReportedTypeFromReportedHash not verifying profileHash")
					}
					if (isDisabled == true){
						return false, errors.New("GetReportedTypeFromReportedHash not verifying profileHash is not disabled.")
					}

					if (profileType != identityTypeToRetrieve){
						return false, nil
					}
				}
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }	
	if (responseIsValid == false){

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	return true, reportsInfoMap, nil
}


//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-map[[30]byte][26]byte: Reports info map (Report Hash -> Reported Message Hash)
//	-error
func GetMessageReportsInfoFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		rangeStart [10]byte,
		rangeEnd [10]byte,
		reportedMessageHashesList [][26]byte)(bool, map[[30]byte][26]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetMessageReportsInfoFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetMessageReportsInfoFromHost called with networkType which does not match connection metadata.")
	}

	acceptableReportVersionsList := []int{1}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetMessageReportsInfo(hostIdentityHash, connectionKey, networkType, acceptableReportVersionsList, rangeStart, rangeEnd, reportedMessageHashesList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseBytes, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, reportsInfoMap, err := serverResponse.ReadServerResponse_GetMessageReportsInfo(responseBytes, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){
			// Peer sent invalid response, must be malicious

			return false, nil
		}

		// Now we verify received reports info map fulfills request filters

		for _, reportedHash := range reportsInfoMap{

			// reportedHash is a message hash

			if (len(reportedMessageHashesList) != 0){

				messageHashIsInRequestedList := slices.Contains(reportedMessageHashesList, reportedHash)
				if (messageHashIsInRequestedList == false){
					return false, nil
				}
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }	
	if (responseIsValid == false){

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil){ return false, nil, err }

		return false, nil, nil
	}

	return true, reportsInfoMap, nil
}



//Outputs:
//	-bool: Download successful (could be unsuccessful because host keys not found, host is malicious, internet connection down, and more)
//	-[][]byte: Retrieved list of reports
//	-error
func GetReportsFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		reportHashesList [][30]byte,
		expectedReportsInfoMap map[[30]byte][]byte)(bool, [][]byte, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("GetReportsFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("GetReportsFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetReports(hostIdentityHash, connectionKey, networkType, reportHashesList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, reportsList, err := serverResponse.ReadServerResponse_GetReports(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){

		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		for _, reportBytes := range reportsList{

			ableToRead, reportHash, _, reportNetworkType, _, _, reportReportedHash, _, err := readReports.ReadReportAndHash(false, reportBytes)
			if (err != nil) { return false, err }
			if (ableToRead == false){
				// Report is invalid, peer must be malicious.
				return false, nil
			}
			if (reportNetworkType != networkType){
				return false, nil
			}

			expectedReportedHash, exists := expectedReportsInfoMap[reportHash]
			if (exists == false) {
				// We received an unexpected reportHash
				return false, nil
			}

			areEqual := bytes.Equal(reportReportedHash, expectedReportedHash)
			if (areEqual == false){
				// We received an unexpected reportedHash for expected reportHash				
				return false, nil
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }
	if (responseIsValid == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	return true, reportsList, nil
}


//Outputs:
//	-bool: Successful download
//	-[]serverResponse.DepositStruct
//	-[]string: List of addresses with no deposits
//	-error
func GetAddressDepositsFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		cryptocurrency string,
		cryptoAddressesList []string)(bool, []serverResponse.DepositStruct, []string, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, nil, err }
	if (connectionExists == false){
		return false, nil, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, nil, errors.New("GetAddressDepositsFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, nil, errors.New("GetAddressDepositsFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetAddressDeposits(hostIdentityHash, connectionKey, networkType, cryptocurrency, cryptoAddressesList)
	if (err != nil) { return false, nil, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, addressDepositObjectsList, err := serverResponse.ReadServerResponse_GetAddressDeposits(responseData, connectionKey)
	if (err != nil) { return false, nil, nil, err }
	if (ableToRead == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, nil, err }

		return false, nil, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

	// This map will store all addresses which have deposits
	addressesWithDepositsMap := make(map[string]struct{})

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		for _, depositObject := range addressDepositObjectsList{

			depositAddress := depositObject.Address

			addressExists := slices.Contains(cryptoAddressesList, depositAddress)
			if (addressExists == false){
				return false, nil
			}

			addressesWithDepositsMap[depositAddress] = struct{}{}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, nil, err }
	if (responseIsValid == false){
		// Host sent invalid response, they must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, nil, err }

		return false, nil, nil, nil
	}

	addressesWithoutDepositsList := make([]string, 0)

	for _, cryptoAddress := range cryptoAddressesList{

		_, exists := addressesWithDepositsMap[cryptoAddress]
		if (exists == false){
			addressesWithoutDepositsList = append(addressesWithoutDepositsList, cryptoAddress)
		}
	}

	return true, addressDepositObjectsList, addressesWithoutDepositsList, nil
}


//Inputs:
//	-[21]byte: Connection Identifier
//	-[16]byte: Host Identity Hash
//	-byte: Network Type
//	-[][16]byte: Identity hashes to retrieve viewable statuses for
//	-[][28]byte: Profiles hashes to retrieve viewable statuses for
//Outputs:
//	-bool: Successful download
//	-map[[16]byte]bool: Identity Hash Statuses Map (Identity Hash -> true/false) (true = viewable, false = unviewable)
//	-map[[28]byte]bool: Profile Hash Statuses Map (Profile Hash -> true/false) (true = viewable, false = unviewable)
//	-error
func GetViewableStatusesFromHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		identityHashesList [][16]byte,
		profileHashesList [][28]byte)(bool, map[[16]byte]bool, map[[28]byte]bool, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, nil, err }
	if (connectionExists == false){
		return false, nil, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, nil, errors.New("GetViewableStatusesFromHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, nil, errors.New("GetViewableStatusesFromHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_GetViewableStatuses(hostIdentityHash, connectionKey, networkType, identityHashesList, profileHashesList)
	if (err != nil) { return false, nil, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, identityHashStatusesMap, profileHashStatusesMap, err := serverResponse.ReadServerResponse_GetViewableStatuses(responseData, connectionKey)
	if (err != nil) { return false, nil, nil, err }
	if (ableToRead == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, nil, err }

		return false, nil, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		for identityHash, _ := range identityHashStatusesMap{

			isRequestedIdentityHash := slices.Contains(identityHashesList, identityHash)
			if (isRequestedIdentityHash == false){
				return false, nil
			}
		}

		for profileHash, _ := range profileHashStatusesMap{

			isRequestedProfileHash := slices.Contains(profileHashesList, profileHash)
			if (isRequestedProfileHash == false){
				return false, nil
			}
		}
		
		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, nil, err }
	if (responseIsValid == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, nil, err }

		return false, nil, nil, nil
	}

	return true, identityHashStatusesMap, profileHashStatusesMap, nil
}


//Inputs:
//	-[21]byte: Connection Identifier
//	-[16]byte: Host Identity Hash
//	-byte: Network Type
//	-string: Content Type ("Profile"/"Message"/"Review"/"Report"/"Parameters")
//	-[][]byte: Contents list
//Outputs:
//	-bool: Successful download
//	-map[string]bool: Received Content Accepted Info Map (Content Hash -> true/false)
//	-error
func BroadcastContentToHost(
		connectionIdentifier [21]byte,
		hostIdentityHash [16]byte,
		networkType byte,
		contentType string,
		contentList [][]byte)(bool, map[string]bool, error){

	connectionExists, connectionHostIdentityHash, connectionKey, connectionNetworkType, err := peerClient.GetConnectionMetadata(connectionIdentifier)
	if (err != nil) { return false, nil, err }
	if (connectionExists == false){
		return false, nil, nil
	}
	if (connectionHostIdentityHash != hostIdentityHash){
		return false, nil, errors.New("BroadcastContentToHost called with hostIdentityHash which does not match connection metadata.")
	}
	if (connectionNetworkType != networkType){
		return false, nil, errors.New("BroadcastContentToHost called with networkType which does not match connection metadata.")
	}

	requestBytes, requestIdentifier, err := serverRequest.CreateServerRequest_BroadcastContent(hostIdentityHash, connectionKey, networkType, contentType, contentList)
	if (err != nil) { return false, nil, err }

	requestSuccessful, responseData, err := peerClient.SendRequestThroughConnection(connectionIdentifier, requestBytes)
	if (err != nil) { return false, nil, err }
	if (requestSuccessful == false){
		return false, nil, nil
	}

	ableToRead, requestIdentifier_Received, hostIdentityHash_Received, contentAcceptedInfoMap, err := serverResponse.ReadServerResponse_BroadcastContent(responseData, connectionKey)
	if (err != nil) { return false, nil, err }
	if (ableToRead == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	// Now we check to make sure response is valid based on input request information

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

		if (requestIdentifier != requestIdentifier_Received){
			// Host sent invalid response, must be malicious
			return false, nil
		}

		if (hostIdentityHash != hostIdentityHash_Received){

			return false, nil
		}

		for _, contentBytes := range contentList{

			ableToRead, contentHash, err := readContent.GetContentHashFromContentBytes(false, contentType, contentBytes)
			if (err != nil) { return false, err }
			if (ableToRead == false){
				return false, errors.New("CreateServerRequest_BroadcastContent not verifying content is valid.")
			}

			_, exists := contentAcceptedInfoMap[string(contentHash)]
			if (exists == false){
				// Host did not tell us if they accepted the content
				// Host is malicious
				return false, nil
			}
		}

		return true, nil
	}

	responseIsValid, err := checkIfResponseIsValid()
	if (err != nil) { return false, nil, err }
	if (responseIsValid == false){
		// Peer sent invalid response, must be malicious

		err := maliciousHosts.AddHostToMaliciousHostsList(hostIdentityHash)
		if (err != nil) { return false, nil, err }

		return false, nil, nil
	}

	return true, contentAcceptedInfoMap, nil
}



