
// serverResponse provides functions to create and read server responses

package serverResponse

// See a description of each response and its purpose in Specification.md

//TODO: Verify all received values and function inputs

import "seekia/internal/cryptography/blake3"
import "seekia/internal/cryptography/chaPolyShrink"
import "seekia/internal/cryptography/edwardsKeys"
import "seekia/internal/cryptography/mlkem"
import "seekia/internal/cryptography/nacl"
import "seekia/internal/cryptography/frodo"
import "seekia/internal/cryptography/csidh"
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/parameters/readParameters"
import "seekia/internal/profiles/readProfiles"

import messagepack "github.com/vmihailenco/msgpack/v5"

import "slices"
import "crypto/rand"
import "errors"

// The variables below specify the maximum number of items that can fit into a response
// They are used when making requests to know when to break up the request so it does not exceed the maximum size
//TODO: Make these numbers accurate

const MaximumProfilesInResponse_GetProfilesInfo int = 100000
const MaximumMessageHashesInResponse_GetMessageHashesList int = 100000
const MaximumMessagesInResponse_GetMessages int = 100000
const MaximumReviewHashesInResponse_GetReviewHashesList int = 100000
const MaximumReviewsInResponse_GetIdentityReviewsInfo int = 100000
const MaximumReviewsInResponse_GetMessageReviewsInfo int = 100000
const MaximumReviewsInResponse_GetReviews int = 100000
const MaximumReportsInResponse_GetIdentityReportsInfo int = 100000
const MaximumReportsInResponse_GetMessageReportsInfo int = 100000
const MaximumReportsInResponse_GetReports int = 100000
const MaximumBalancesInResponse_GetAddressDeposits int = 100000
const MaximumProfilesInResponse_GetProfileViewableStatuses int = 10000
const MaximumIdentitiesInResponse_GetIdentityViewableStatuses int = 1000

func GetMaximumProfilesInResponse_GetProfiles(profileType string)(int, error){

	if (profileType == "Mate"){
		return 1000, nil
	}
	if (profileType == "Host"){
		return 10000, nil
	}
	if (profileType == "Moderator"){
		return 10000, nil
	}

	return 0, errors.New("GetMaximumProfilesInResponse_GetProfiles called with invalid profile type: " + profileType)
}

//Outputs:
//	-[]byte: Response bytes
//	-[32]byte: Connection Key
//	-error
func CreateServerResponse_EstablishConnectionKey(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, requestIdentifier [16]byte, requestorNaclPublicKey [32]byte, requestorMlkemPublicKey [1568]byte, requestorCsidhPublicKey [64]byte, requestorFrodoPublicKey [9616]byte)([]byte, [32]byte, error){

	var connectionKey [32]byte
	_, err := rand.Read(connectionKey[:])
	if (err != nil) { return nil, [32]byte{}, err }

	type responseStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ConnectionKey [32]byte
	}

	responseObject := responseStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "EstablishConnectionKey",
		ConnectionKey: connectionKey,
	}

	responseContentBytes, err := encoding.EncodeMessagePackBytes(responseObject)
	if (err != nil) { return nil, [32]byte{}, err }

	responseSignedBytes, err := createResponseSignedContent(hostPrivateIdentityKey, responseContentBytes)
	if (err != nil) { return nil, [32]byte{}, err }

	keyPiece1, err := helpers.GetNewRandom32ByteArray()
	if (err != nil) { return nil, [32]byte{}, err }

	naclEncryptedKeyPiece1, err := nacl.EncryptKeyWithNacl(requestorNaclPublicKey, keyPiece1)
	if (err != nil) { return nil, [32]byte{}, err }

	keyPiece2, mlkemEncryptedKeyPiece2, err := mlkem.EncryptNewRandomKeyWithMlkem(requestorMlkemPublicKey)
	if (err != nil) { return nil, [32]byte{}, err }

	serverCsidhPublicKey, serverCsidhPrivateKey, err := csidh.GetNewRandomPublicPrivateCsidhKeys()
	if (err != nil) { return nil, [32]byte{}, err }

	keyPiece3, err := csidh.CalculateCsidhSharedKey(requestorCsidhPublicKey, serverCsidhPrivateKey)
	if (err != nil) { return nil, [32]byte{}, err }

	keyPiece4, frodoEncryptedKeyPiece4, err := frodo.EncryptNewRandomKeyWithFrodo(requestorFrodoPublicKey)
	if (err != nil) { return nil, [32]byte{}, err }

	connectionKeyDerivationHashInput := slices.Concat(keyPiece1[:], keyPiece2[:], keyPiece3[:], keyPiece4[:])

	chaPolyKey, err := blake3.Get32ByteBlake3Hash(connectionKeyDerivationHashInput)
	if (err != nil) { return nil, [32]byte{}, err }

	chaPolyNonce, err := helpers.GetNewRandom24ByteArray()
	if (err != nil) { return nil, [32]byte{}, err }

	cipheredContent, err := chaPolyShrink.EncryptChaPolyShrink(responseSignedBytes, chaPolyKey, chaPolyNonce, true, 50, false, [32]byte{})
	if (err != nil) { return nil, [32]byte{}, err }

	encryptedKeyPiecesList := slices.Concat(naclEncryptedKeyPiece1[:], mlkemEncryptedKeyPiece2[:], serverCsidhPublicKey[:], frodoEncryptedKeyPiece4[:])

	type encryptedResponseStruct struct{
		KeyPieces []byte
		ChaPolyNonce [24]byte
		CipheredContent []byte
	}

	encryptedResponseObject := encryptedResponseStruct{
		KeyPieces: encryptedKeyPiecesList,
		ChaPolyNonce: chaPolyNonce,
		CipheredContent: cipheredContent,
	}

	encryptedResponseBytes, err := encoding.EncodeMessagePackBytes(encryptedResponseObject)
	if (err != nil) { return nil, [32]byte{}, err }

	return encryptedResponseBytes, connectionKey, nil
}

//Outputs:
//	-bool: Able to read
//	-[16]byte: Request identifier
//	-[16]byte: Host Identity Hash
//	-[32]byte: Connection key
//	-error (if inputs to function are invalid)
func ReadServerResponse_EstablishConnectionKey(responseBytes []byte, requestorNaclPublicKey [32]byte, requestorNaclPrivateKey [32]byte, requestorMlkemPrivateKey [64]byte, requestorCsidhPrivateKey [37]byte, requestorFrodoPrivateKey [19888]byte)(bool, [16]byte, [16]byte, [32]byte, error){

	type encryptedResponseStruct struct{
		KeyPieces [11432]byte
		ChaPolyNonce [24]byte
		CipheredContent []byte
	}

	var encryptedResponseObject encryptedResponseStruct

	err := encoding.DecodeMessagePackBytes(false, responseBytes, &encryptedResponseObject)
	if (err != nil) {
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	keyPiecesListArray := encryptedResponseObject.KeyPieces
	chaPolyNonce := encryptedResponseObject.ChaPolyNonce
	cipheredContent := encryptedResponseObject.CipheredContent

	naclEncryptedKeyPiece1 := [80]byte(keyPiecesListArray[:80])
	mlkemEncryptedKeyPiece2 := [1568]byte(keyPiecesListArray[80:1648])
	serverCsidhPublicKey := [64]byte(keyPiecesListArray[1648:1712])
	frodoEncryptedKeyPiece4 := [9720]byte(keyPiecesListArray[1712:])

	ableToDecrypt, keyPiece1, err := nacl.DecryptNaclEncryptedKey(naclEncryptedKeyPiece1, requestorNaclPublicKey, requestorNaclPrivateKey)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, [32]byte{}, err }
	if (ableToDecrypt == false){
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	ableToDecrypt, keyPiece2, err := mlkem.DecryptMlkemEncryptedKey(mlkemEncryptedKeyPiece2, requestorMlkemPrivateKey)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, [32]byte{}, err }
	if (ableToDecrypt == false){
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	isValid := csidh.VerifyCsidhPublicKey(serverCsidhPublicKey)
	if (isValid == false){
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	keyPiece3, err := csidh.CalculateCsidhSharedKey(serverCsidhPublicKey, requestorCsidhPrivateKey)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, [32]byte{}, err }

	ableToDecrypt, keyPiece4, err := frodo.DecryptFrodoEncryptedKey(frodoEncryptedKeyPiece4, requestorFrodoPrivateKey)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, [32]byte{}, err }
	if (ableToDecrypt == false){
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	contentChatPolyKeyDerivationHashInput := slices.Concat(keyPiece1[:], keyPiece2[:], keyPiece3[:], keyPiece4[:])

	contentChaPolyKey, err := blake3.Get32ByteBlake3Hash(contentChatPolyKeyDerivationHashInput)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, [32]byte{}, err }

	ableToDecrypt, decryptedResponseBytes, err := chaPolyShrink.DecryptChaPolyShrink(cipheredContent, contentChaPolyKey, chaPolyNonce, false, [32]byte{})
	if (err != nil) {
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}
	if (ableToDecrypt == false){
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	ableToRead, requestIdentifier, hostIdentityHash, contentBytes, err := readResponseSignedContent(decryptedResponseBytes, "EstablishConnectionKey")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, [32]byte{}, err }
	if (ableToRead == false){
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	type responseContentStruct struct{
		ConnectionKey [32]byte
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, contentBytes, &responseContentObject)
	if (err != nil) {
		return false, [16]byte{}, [16]byte{}, [32]byte{}, nil
	}

	connectionKey := responseContentObject.ConnectionKey

	return true, requestIdentifier, hostIdentityHash, connectionKey, nil
}


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetParametersInfo(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, parametersInfoMap map[string]int64)([]byte, error){

	if (len(parametersInfoMap) == 0){
		return nil, errors.New("CreateServerResponse_GetParametersInfo called with empty ParametersInfoMap")
	}

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ParametersInfo map[string]int64 // Parameters Type -> Parameters creation time
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetParametersInfo",
		ParametersInfo: parametersInfoMap,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil,  err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}

//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-map[string]int64: Parameters Info map (Parameters Type -> Parameters creation time)
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetParametersInfo(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, map[string]int64, error){

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetParametersInfo")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ParametersInfo map[string]int64
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	parametersInfoMap := responseContentObject.ParametersInfo

	//TODO: Verify parameters

	return true, requestIdentifier, hostIdentityHash, parametersInfoMap, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetParameters(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, parametersList [][]byte)([]byte, error){

	//TODO: Verify inputs

	messagepackParametersList := make([]messagepack.RawMessage, 0, len(parametersList))

	for _, parametersBytes := range parametersList{
		messagepackParametersList = append(messagepackParametersList, parametersBytes)
	}

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ParametersList []messagepack.RawMessage
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetParameters",
		ParametersList: messagepackParametersList,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil,  err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[][]byte: Parameters list
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetParameters(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, [][]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetParameters")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ParametersList []messagepack.RawMessage
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	parametersListRawMessagepack := responseContentObject.ParametersList

	// We use the map below to ensure we only received each parameter type once
	receivedParametersTypesMap := make(map[string]struct{})

	// We use this to ensure that all parameters network types are the same
	parametersNetworkType := byte(0)

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

	for index, parametersBytes := range parametersListRawMessagepack{

		ableToRead, _, currentNetworkType, _, parametersType, _, _, err := readParameters.ReadParameters(true, parametersBytes)
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (ableToRead == false){
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		if (index == 0){
			parametersNetworkType = currentNetworkType

		} else if (parametersNetworkType != currentNetworkType){

			// We received two parameters with different network types.
			// Response is malformed.
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		_, exists := receivedParametersTypesMap[parametersType]
		if (exists == true){
			// We received a duplicate parameters type. Response is malformed.
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
		receivedParametersTypesMap[parametersType] = struct{}{}

		parametersList = append(parametersList, parametersBytes)
	}

	return true, requestIdentifier, hostIdentityHash, parametersList, nil
} 

// This is used for the GetProfilesInfo request
type ProfileInfoStruct struct{
	ProfileHash [28]byte
	ProfileAuthor [16]byte
	ProfileCreationTime int64
}

//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetProfilesInfo(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, profileInfoObjectsList []ProfileInfoStruct)([]byte, error){

	//TODO: Verify inputs

	for _, profileInfoObject := range profileInfoObjectsList{

		profileHash := profileInfoObject.ProfileHash
		profileAuthor := profileInfoObject.ProfileAuthor
		profileCreationTime := profileInfoObject.ProfileCreationTime

		profileType, _, err := readProfiles.ReadProfileHashMetadata(profileHash)
		if (err != nil){
			profileHashHex := encoding.EncodeBytesToHexString(profileHash[:])
			return nil, errors.New("Cannot create response: Invalid profileInfoObjectsList: Invalid ProfileHash: " + profileHashHex + ". Reason: " + err.Error())
		}

		authorIdentityType, err := identity.GetIdentityTypeFromIdentityHash(profileAuthor)
		if (err != nil){
			profileAuthorHex := encoding.EncodeBytesToHexString(profileAuthor[:])
			return nil, errors.New("Cannot create response: invalid profileInfoObjectsList: Invalid profileAuthor: " + profileAuthorHex)
		}

		if (authorIdentityType != profileType){
			return nil, errors.New("Cannot create response: Invalid profileInfoObjectsList: ProfileHash profile type does not match profileAuthor identity type.")
		}

		isValid := helpers.VerifyCreationTime(profileCreationTime)
		if (isValid == false){
			creationTimeString := helpers.ConvertInt64ToString(profileCreationTime)
			return nil, errors.New("Cannot create response: Invalid profileInfoObjectsList: Invalid creationTime: " + creationTimeString)
		}
	}

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ProfilesInfo []ProfileInfoStruct
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetProfilesInfo",
		ProfilesInfo: profileInfoObjectsList,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[]ProfileInfoStruct: Profiles Info map list
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetProfilesInfo(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, []ProfileInfoStruct, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetProfilesInfo")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ProfilesInfo []ProfileInfoStruct
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	//TODO: Validate info
	profileInfoObjectsList := responseContentObject.ProfilesInfo

	return true, requestIdentifier, hostIdentityHash, profileInfoObjectsList, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetProfiles(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, profilesList [][]byte)([]byte, error){

	//TODO: Verify inputs

	messagepackProfilesList := make([]messagepack.RawMessage, 0, len(profilesList))

	for _, profileBytes := range profilesList{

		messagepackProfilesList = append(messagepackProfilesList, profileBytes)
	}

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ProfilesList []messagepack.RawMessage
	}
	
	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetProfiles",
		ProfilesList: messagepackProfilesList,
	}
	
	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil,  err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[][]byte: Profiles list
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetProfiles(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, [][]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetProfiles")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ProfilesList []messagepack.RawMessage
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	messagepackProfilesList := responseContentObject.ProfilesList

	// We use the map below to ensure we only received each profile once
	receivedProfileHashesMap := make(map[[28]byte]struct{})

	profilesList := make([][]byte, 0, len(messagepackProfilesList))

	// We use this variable to make sure the network type of each profile is the same
	profilesNetworkType := byte(0)

	for index, profileBytes := range messagepackProfilesList{

		ableToRead, profileHash, _, profileNetworkType, _, _, _, _, err := readProfiles.ReadProfileAndHash(true, profileBytes)
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (ableToRead == false){
			// Profile is malformed. Host is malicious.
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		_, exists := receivedProfileHashesMap[profileHash]
		if (exists == true){
			// We received a duplicate profile. Host is malicious.
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
		receivedProfileHashesMap[profileHash] = struct{}{}

		if (index == 0){
			profilesNetworkType = profileNetworkType

		} else if (profilesNetworkType != profileNetworkType){
			// Profiles of differing network type cannot be sent in the same response
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		profilesList = append(profilesList, profileBytes)
	}

	return true, requestIdentifier, hostIdentityHash, profilesList, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetMessageHashesList(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, messageHashesList [][26]byte)([]byte, error){

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		MessageHashesList [][26]byte
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetMessageHashesList",
		MessageHashesList: messageHashesList,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil,  err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[][26]byte: Message Hashes List
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetMessageHashesList(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, [][26]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetMessageHashesList")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		MessageHashesList [][26]byte
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	messageHashesList := responseContentObject.MessageHashesList

	return true, requestIdentifier, hostIdentityHash, messageHashesList, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetMessages(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, messagesList [][]byte)([]byte, error){

	//TODO: Verify inputs

	messagepackMessagesList := make([]messagepack.RawMessage, 0, len(messagesList))

	for _, messageBytes := range messagesList{
		messagepackMessagesList = append(messagepackMessagesList, messageBytes)
	}

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		MessagesList []messagepack.RawMessage
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetMessages",
		MessagesList: messagepackMessagesList,
	}
	
	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil,  err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[][]byte: Messages list
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetMessages(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, [][]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetMessages")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		MessagesList []messagepack.RawMessage
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	messagepackMessagesList := responseContentObject.MessagesList

	// We use this map to detect duplicates
	messageHashesMap := make(map[[26]byte]struct{})

	messagesList := make([][]byte, 0, len(messagepackMessagesList))

	// We use this variable to make sure the network type of all messages in the response is the same
	messagesNetworkType := byte(0)

	for index, messageBytes := range messagepackMessagesList{

		ableToRead, messageHash, _, messageNetworkType, _, _, _, _, _, _, _, err := readMessages.ReadChatMessagePublicDataAndHash(true, messageBytes)
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (ableToRead == false){
			// Message is invalid, host must be malicious.
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		_, exists := messageHashesMap[messageHash]
		if (exists == true){
			// Host sent duplicate message.
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		messageHashesMap[messageHash] = struct{}{}

		if (index == 0){
			messagesNetworkType = messageNetworkType

		} else if (messagesNetworkType != messageNetworkType){
			// All messages in the response must contain the same networkType
			return false, [16]byte{}, [16]byte{}, nil, nil	
		}

		messagesList = append(messagesList, messageBytes)
	}

	return true, requestIdentifier, hostIdentityHash, messagesList, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetIdentityReviewsInfo(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, reviewsInfoMap map[[29]byte][]byte)([]byte, error){

	//TODO: Verify inputs

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ReviewsInfo map[[29]byte][]byte // ReviewHash -> ReviewedHash
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetIdentityReviewsInfo",
		ReviewsInfo: reviewsInfoMap,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-map[[29]byte][]byte: Reviews Info map (Review Hash -> Reviewed Hash)
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetIdentityReviewsInfo(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, map[[29]byte][]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetIdentityReviewsInfo")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ReviewsInfo map[[29]byte][]byte
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	reviewsInfoMap := responseContentObject.ReviewsInfo	

	for reviewHash, reviewedHash := range reviewsInfoMap{

		reviewedType, err := helpers.GetReviewedTypeFromReviewedHash(reviewedHash)
		if (err != nil) {
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
		if (reviewedType != "Identity" && reviewedType != "Profile" && reviewedType != "Attribute"){
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		isValid, err := readReviews.VerifyReviewHash(reviewHash, true, reviewedType)
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (isValid == false){
			// Received reviewsInfoMap contains invalid reviewHash
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
	}

	return true, requestIdentifier, hostIdentityHash, reviewsInfoMap, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetMessageReviewsInfo(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, reviewsInfoMap map[[29]byte][26]byte)([]byte, error){

	//TODO: Verify inputs

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ReviewsInfo map[[29]byte][26]byte // Review Hash -> Reviewed Message Hash
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetMessageReviewsInfo",
		ReviewsInfo: reviewsInfoMap,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-map[[29]byte][26]byte: Reviews Info map (Review Hash -> Reviewed Message Hash)
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetMessageReviewsInfo(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, map[[29]byte][26]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetMessageReviewsInfo")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ReviewsInfo map[[29]byte][26]byte
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	reviewsInfoMap := responseContentObject.ReviewsInfo	

	for reviewHash, _ := range reviewsInfoMap{

		isValid, err := readReviews.VerifyReviewHash(reviewHash, true, "Message")
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (isValid == false){
			// Received reviewsInfoMap contains invalid reviewHash
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
	}

	return true, requestIdentifier, hostIdentityHash, reviewsInfoMap, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetReviews(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, reviewsList [][]byte)([]byte, error){

	//TODO: Verify inputs

	messagepackReviewsList := make([]messagepack.RawMessage, 0, len(reviewsList))

	for _, reviewBytes := range reviewsList{
		messagepackReviewsList = append(messagepackReviewsList, reviewBytes)
	}

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ReviewsList []messagepack.RawMessage
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetReviews",
		ReviewsList: messagepackReviewsList,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil,  err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[][]byte: Reviews list
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetReviews(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, [][]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetReviews")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ReviewsList []messagepack.RawMessage
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	messagepackReviewsList := responseContentObject.ReviewsList

	// We use a map to detect duplicates
	reviewHashesMap := make(map[[29]byte]struct{})

	reviewsList := make([][]byte, 0, len(messagepackReviewsList))

	// We use this variable to ensure that all reviews in the response belong to the same networkType
	reviewsNetworkType := byte(0)

	for index, reviewBytes := range messagepackReviewsList{

		ableToRead, reviewHash, _, reviewNetworkType, _, _, _, _, _, _, err := readReviews.ReadReviewAndHash(true, reviewBytes)
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (ableToRead == false){
			// Review is invalid, host must be malicious.
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		_, exists := reviewHashesMap[reviewHash]
		if (exists == true){
			// Duplicate review exists
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		reviewHashesMap[reviewHash] = struct{}{}

		if (index == 0){
			reviewsNetworkType = reviewNetworkType

		} else if (reviewsNetworkType != reviewNetworkType){

			// Response contains two reviews with differing networkType
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		reviewsList = append(reviewsList, reviewBytes)
	}

	return true, requestIdentifier, hostIdentityHash, reviewsList, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetIdentityReportsInfo(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, reportsInfoMap map[[30]byte][]byte)([]byte, error){

	//TODO: Verify inputs

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ReportsInfo map[[30]byte][]byte // Report hash -> Reported hash
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetIdentityReportsInfo",
		ReportsInfo: reportsInfoMap,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-map[[30]byte][]byte: Reports Info map (Report Hash -> Reported hash)
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetIdentityReportsInfo(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, map[[30]byte][]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetIdentityReportsInfo")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ReportsInfo map[[30]byte][]byte
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	reportsInfoMap := responseContentObject.ReportsInfo

	for reportHash, reportedHash := range reportsInfoMap{

		reportedType, err := helpers.GetReportedTypeFromReportedHash(reportedHash)
		if (err != nil) {
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
		if (reportedType != "Identity" && reportedType != "Profile" && reportedType != "Attribute"){
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		isValid, err := readReports.VerifyReportHash(reportHash, true, reportedType)
		if (err != nil){ return false, [16]byte{}, [16]byte{}, nil, err }
		if (isValid == false){
			//Response contains invalid reportHash		
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
	}

	return true, requestIdentifier, hostIdentityHash, reportsInfoMap, nil
} 



//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetMessageReportsInfo(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, reportsInfoMap map[[30]byte][26]byte)([]byte, error){

	//TODO: Verify inputs

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ReportsInfo map[[30]byte][26]byte // Report Hash -> Reported message hash
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetMessageReportsInfo",
		ReportsInfo: reportsInfoMap,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-map[[30]byte][26]byte: Reports Info map (Report hash -> Reported message hash)
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetMessageReportsInfo(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, map[[30]byte][26]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetMessageReportsInfo")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ReportsInfo map[[30]byte][26]byte
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	reportsInfoMap := responseContentObject.ReportsInfo

	for reportHash, _ := range reportsInfoMap{

		isValid, err := readReports.VerifyReportHash(reportHash, true, "Message")
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (isValid == false){
			// Response contains invalid reportHash		
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
	}

	return true, requestIdentifier, hostIdentityHash, reportsInfoMap, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetReports(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, reportsList [][]byte)([]byte, error){

	//TODO: Verify inputs

	messagepackReportsList := make([]messagepack.RawMessage, 0, len(reportsList))

	for _, reportBytes := range reportsList{

		messagepackReportsList = append(messagepackReportsList, reportBytes)
	}

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ReportsList []messagepack.RawMessage
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetReports",
		ReportsList: messagepackReportsList,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil,  err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[][]byte: Reports list
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetReports(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, [][]byte, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetReports")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ReportsList []messagepack.RawMessage
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	messagepackReportsList := responseContentObject.ReportsList

	// We use this map to detect duplicates
	reportHashesMap := make(map[[30]byte]struct{})

	reportsList := make([][]byte, 0, len(messagepackReportsList))

	// We use this variable to ensure all reports in response belong to the same network type
	reportsNetworkType := byte(0)

	for index, reportBytes := range messagepackReportsList{

		ableToRead, reportHash, _, reportNetworkType, _, _, _, _, err := readReports.ReadReportAndHash(true, reportBytes)
		if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
		if (ableToRead == false){
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		_, exists := reportHashesMap[reportHash]
		if (exists == true){
			// A duplicate report exists
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
		reportHashesMap[reportHash] = struct{}{}

		if (index == 0){
			reportsNetworkType = reportNetworkType

		} else if (reportsNetworkType != reportNetworkType){
			// All reports in response must belong to the same network type 
			// Response is malformed
			return false, [16]byte{}, [16]byte{}, nil, nil
		}

		reportsList = append(reportsList, reportBytes)
	}

	return true, requestIdentifier, hostIdentityHash, reportsList, nil
} 

// This object type is used to represent a deposit to an address
type DepositStruct struct{

	// The cryptocurrency address where funds were deposited
	Address string

	// The unix time of the block when deposit(s) were made
	DepositTime int64

	// The sum of all deposit amounts in the block to the specified address, in crypto atomic units (example: wei)
	//TODO: Change this to big.Int
	DepositAmount int64
}

//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetAddressDeposits(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, depositObjectsList []DepositStruct)([]byte, error){

	//TODO: Verify inputs

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		AddressDepositsList []DepositStruct
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetAddressDeposits",
		AddressDepositsList: depositObjectsList,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-[]DepositStruct: Deposit objects list
//	-error (returns err if input keys are invalid)
func ReadServerResponse_GetAddressDeposits(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, []DepositStruct, error){

	//TODO: Verify inputs

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetAddressDeposits")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		AddressDepositsList []DepositStruct
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	addressDepositsList := responseContentObject.AddressDepositsList
	
	//TODO: validate info

	return true, requestIdentifier, hostIdentityHash, addressDepositsList, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_GetViewableStatuses(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, identityHashStatusesMap map[[16]byte]bool, profileHashStatusesMap map[[28]byte]bool)([]byte, error){

	//TODO: Verify inputs

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		IdentityHashStatuses map[[16]byte]bool
		ProfileHashStatuses map[[28]byte]bool
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "GetViewableStatuses",
		IdentityHashStatuses: identityHashStatusesMap,
		ProfileHashStatuses: profileHashStatusesMap,
	}
	
	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-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 (returns err if input keys are invalid)
func ReadServerResponse_GetViewableStatuses(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, map[[16]byte]bool, map[[28]byte]bool, error){

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "GetViewableStatuses")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil, nil
	}

	type responseContentStruct struct{
		IdentityHashStatuses map[[16]byte]bool
		ProfileHashStatuses map[[28]byte]bool
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response malformed
		return false, [16]byte{}, [16]byte{}, nil, nil, nil
	}

	identityHashStatusesMap := responseContentObject.IdentityHashStatuses
	profileHashStatusesMap := responseContentObject.ProfileHashStatuses

	//TODO: Verify maps

	return true, requestIdentifier, hostIdentityHash, identityHashStatusesMap, profileHashStatusesMap, nil
} 


//Outputs:
//	-[]byte: Response bytes
//	-error
func CreateServerResponse_BroadcastContent(hostPublicIdentityKey [32]byte, hostPrivateIdentityKey [64]byte, connectionKey [32]byte, requestIdentifier [16]byte, contentAcceptedInfoMap map[string]bool)([]byte, error){

	//TODO: Verify inputs

	type responseContentStruct struct{
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
		ContentAcceptedInfo map[string]bool //Content Hash -> true/false
	}

	responseContentObject := responseContentStruct{
		HostIdentityKey: hostPublicIdentityKey,
		RequestIdentifier: requestIdentifier,
		ResponseType: "BroadcastContent",
		ContentAcceptedInfo: contentAcceptedInfoMap,
	}

	innerResponseBytes, err := encoding.EncodeMessagePackBytes(responseContentObject)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedAndSignedResponse(connectionKey, hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}


//Outputs:
//	-bool: Able to read server response
//	-[16]byte: Request Identifier
//	-[16]byte: Host Identity Hash
//	-map[string]bool: Content Accepted Info Map (Content Hash -> true/false)
//	-error (returns err if input keys are invalid)
func ReadServerResponse_BroadcastContent(responseBytes []byte, connectionKey [32]byte)(bool, [16]byte, [16]byte, map[string]bool, error){

	//TODO: Verify stuff

	ableToRead, requestIdentifier, hostIdentityHash, decryptedContentBytes, err := readEncryptedAndSignedResponse(responseBytes, connectionKey, "BroadcastContent")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	type responseContentStruct struct{
		ContentAcceptedInfo map[string]bool
	}

	var responseContentObject responseContentStruct

	err = encoding.DecodeMessagePackBytes(true, decryptedContentBytes, &responseContentObject)
	if (err != nil) {
		// Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	contentAcceptedInfoMap := responseContentObject.ContentAcceptedInfo

	for contentHash, _ := range contentAcceptedInfoMap{

		_, err := helpers.GetContentTypeFromContentHash([]byte(contentHash))
		if (err != nil){
			// Invalid content hash
			return false, [16]byte{}, [16]byte{}, nil, nil
		}
	}

	return true, requestIdentifier, hostIdentityHash, contentAcceptedInfoMap, nil
}

func createEncryptedAndSignedResponse(connectionKey [32]byte, hostPrivateIdentityKey [64]byte, innerResponseBytes []byte)([]byte, error){

	responseSignedBytes, err := createResponseSignedContent(hostPrivateIdentityKey, innerResponseBytes)
	if (err != nil) { return nil, err }

	encryptedResponse, err := createEncryptedResponse(responseSignedBytes, connectionKey)
	if (err != nil) { return nil, err }

	return encryptedResponse, nil
}

//Outputs:
//	-bool: Able to read (response is valid)
//	-[16]byte: Request identifier
//	-[16]byte: Host identity hash
//	-[]byte: Content Bytes
//	-error (return err if there is a bug in the function)
func readEncryptedAndSignedResponse(inputResponse []byte, connectionKey [32]byte, expectedResponseType string)(bool, [16]byte, [16]byte, []byte, error){

	ableToDecrypt, decryptedResponseBytes, err := readEncryptedResponse(inputResponse, connectionKey)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToDecrypt == false){
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	ableToRead, requestIdentifier, hostIdentityHash, contentBytes, err := readResponseSignedContent(decryptedResponseBytes, expectedResponseType)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }
	if (ableToRead == false){
		//Response is malformed
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	return true, requestIdentifier, hostIdentityHash, contentBytes, nil
}


func createEncryptedResponse(contentToEncrypt []byte, connectionKey [32]byte)([]byte, error){

	chaPolyNonce, err := helpers.GetNewRandom24ByteArray()
	if (err != nil) { return nil, err }

	cipheredContent, err := chaPolyShrink.EncryptChaPolyShrink(contentToEncrypt, connectionKey, chaPolyNonce, true, 100, false, [32]byte{})
	if (err != nil) { return nil, err }

	type encryptedResponseStruct struct{
		ChaPolyNonce [24]byte
		CipheredContent []byte
	}

	encryptedResponseObject := encryptedResponseStruct{
		ChaPolyNonce: chaPolyNonce,
		CipheredContent: cipheredContent,
	}

	encryptedResponseBytes, err := encoding.EncodeMessagePackBytes(encryptedResponseObject)
	if (err != nil) { return nil, err }

	return encryptedResponseBytes, nil
}

//Outputs:
//	-bool: Response well formed and able to decrypt
//	-[]byte: Decrypted response
//	-error (decryption key inputs are malformed)
func readEncryptedResponse(inputResponse []byte, connectionKey [32]byte)(bool, []byte, error){

	type encryptedResponseStruct struct{
		ChaPolyNonce [24]byte
		CipheredContent []byte
	}

	var encryptedResponseObject encryptedResponseStruct

	err := encoding.DecodeMessagePackBytes(true, inputResponse, &encryptedResponseObject)
	if (err != nil) {
		return false, nil, nil
	}

	chaPolyNonce := encryptedResponseObject.ChaPolyNonce
	cipheredContent := encryptedResponseObject.CipheredContent

	ableToDecrypt, decryptedBytes, err := chaPolyShrink.DecryptChaPolyShrink(cipheredContent, connectionKey, chaPolyNonce, false, [32]byte{})
	if (err != nil) {
		return false, nil, nil
	}
	if (ableToDecrypt == false){
		return false, nil, nil
	}

	return true, decryptedBytes, nil
}


func createResponseSignedContent(hostIdentityPrivateKey [64]byte, responseContent messagepack.RawMessage)([]byte, error){

	contentHash, err := blake3.Get32ByteBlake3Hash(responseContent)
	if (err != nil) { return nil, err }

	responseSignature := edwardsKeys.CreateSignature(hostIdentityPrivateKey, contentHash)

	type finalResponseStruct struct {
		Signature [64]byte
		Content messagepack.RawMessage
	}

	finalResponseObject := finalResponseStruct{
		Signature: responseSignature,
		Content: responseContent,
	}

	finalResponseBytes, err := encoding.EncodeMessagePackBytes(finalResponseObject)
	if (err != nil) { return nil, err }

	return finalResponseBytes, nil
}

//Outputs:
//	-bool: Able to read (response is valid)
//	-[16]byte: Request identifier
//	-[16]byte: Host identity hash
//	-[]byte: Content bytes
//	-error (return err if there is a bug in the function)
func readResponseSignedContent(inputBytes []byte, expectedResponseType string)(bool, [16]byte, [16]byte, []byte, error){

	type outerResponseStruct struct{
		Signature [64]byte
		Content messagepack.RawMessage
	}

	var outerResponseObject outerResponseStruct

	err := encoding.DecodeMessagePackBytes(true, inputBytes, &outerResponseObject)
	if (err != nil) {
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	responseSignature := outerResponseObject.Signature
	responseContentBytes := outerResponseObject.Content

	if (len(responseContentBytes) == 0){
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	responseContentHashed, err := blake3.Get32ByteBlake3Hash(responseContentBytes)
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }

	type partialResponseStruct struct {
		HostIdentityKey [32]byte
		RequestIdentifier [16]byte
		ResponseType string
	}

	var partialResponseObject partialResponseStruct

	err = encoding.DecodeMessagePackBytes(true, responseContentBytes, &partialResponseObject)
	if (err != nil) {
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	requestIdentifier := partialResponseObject.RequestIdentifier
	hostIdentityKey := partialResponseObject.HostIdentityKey
	responseType := partialResponseObject.ResponseType

	isValid := edwardsKeys.VerifySignature(hostIdentityKey, responseSignature, responseContentHashed)
	if (isValid == false){
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	hostIdentityHash, err := identity.ConvertIdentityKeyToIdentityHash(hostIdentityKey, "Host")
	if (err != nil) { return false, [16]byte{}, [16]byte{}, nil, err }

	if (responseType != expectedResponseType){
		return false, [16]byte{}, [16]byte{}, nil, nil
	}

	return true, requestIdentifier, hostIdentityHash, responseContentBytes, nil
}



