

// serverRequest provides functions to create and read server requests
// These are MessagePack encoded requests sent to Seekia hosts

package serverRequest

// Each encrypted request has a RequestTime and a RequestIdentifier
// RequestTime ensures an attacker cannot replay a request and compare the response size to gain information
// The host will reject any requests with a requestTime that is too old

// RequestIdentifier is random for each request and ensures that the response is intended for the specific request
// Otherwise, a man-in-the-middle attacker could replay old requests created during the same connection, to trick the requestor
// For example, the requestor could make a GetProfilesInfo request, get a response A, and later make a different GetProfilesInfo request
// The attacker could replay response A, and the requestor would wrongly designate the host as malicious (for providing invalid information)
// Request Identifiers prevent this attack

// Acceptable versions is a []int that describes the acceptable versions of profiles/messages/reviews/reports that the requestor can read
// As new versions are created, different hosts and clients will only be able to read certain versions of content
// This list allows hosts and clients who have not upgraded to newer versions to continue serving/downloading content on the network

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

//TODO: Add GetIdentityPaymentProofs and GetMessagePaymentProofs
//	Will be used to get payment proofs from hosts
//	We need this because hosts can provide us with invalid payment proofs for content
//	This is also needed because we don't download identity payment proofs when downloading profiles.
//	Identities can also broadcast multiple payment proofs

//TODO: Add payment proofs to the BroadcastContent, GetProfiles, GetMessages, etc... requests.
// Payment proofs must be shared whenever we download or broadcast a piece of content

import "seekia/internal/cryptography/chaPolyShrink"
import "seekia/internal/encoding"
import "seekia/internal/helpers"
import "seekia/internal/readContent"

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

import "crypto/rand"
import "time"
import "errors"

func getNewRequestIdentifier()([16]byte, error){

	var identifierArray [16]byte

	_, err := rand.Read(identifierArray[:])
	if (err != nil) { return [16]byte{}, err }

	return identifierArray, nil
}

func verifyAcceptableVersionsList(inputList []int)error{

	// We use this map to detect duplicates
	versionsMap := make(map[int]struct{})

	for _, versionInt := range inputList{

		if (versionInt < 1 || versionInt > 500){
			return errors.New("Acceptable versions list is invalid: Version int is not valid.")
		}

		_, exists := versionsMap[versionInt]
		if (exists == true){
			return errors.New("Acceptable versions list is invalid: Duplicate exists.")
		}
		versionsMap[versionInt] = struct{}{}
	}

	return nil
}

//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_EstablishConnectionKey(hostIdentityHash [16]byte, networkType byte, requestorNaclPublicKey [32]byte, requestorMlkemPublicKey [1568]byte, requestorCsidhPublicKey [64]byte, requestorFrodoPublicKey [9616]byte)([]byte, [16]byte, error){

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	type requestContentStruct struct{
		RequestVersion int
		RequestIdentifier [16]byte
		RequestType string
		RecipientHost [16]byte
		NetworkType byte
		RequestorNaclKey [32]byte
		RequestorMlkemKey [1568]byte
		RequestorCsidhKey [64]byte
		RequestorFrodoKey [9616]byte
	}

	requestContentObject := requestContentStruct{
		RequestVersion: 1,
		RequestIdentifier: requestIdentifier,
		RequestType: "EstablishConnectionKey",
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		RequestorNaclKey: requestorNaclPublicKey,
		RequestorMlkemKey: requestorMlkemPublicKey,
		RequestorCsidhKey: requestorCsidhPublicKey,
		RequestorFrodoKey: requestorFrodoPublicKey,
	}

	finalRequest, err := encoding.EncodeMessagePackBytes(requestContentObject)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalRequest, requestIdentifier, nil
}

//Outputs:
//	-[16]byte: Host identity being requested from
//	-[16]byte: Request identifier
//	-byte: Network type
//	-[32]byte: Requestor Nacl Key
//	-[1568]byte: Requestor Mlkem Key
//	-[64]byte: Requestor Csidh Key
//	-[9616]byte: Requestor Frodo Key
//	-error
func ReadServerRequest_EstablishConnectionKey(inputRequestBytes []byte)([16]byte, [16]byte, byte, [32]byte, [1568]byte, [64]byte, [9616]byte, error){

	//TODO: Verify everything

	type requestContentStruct struct{
		RequestIdentifier [16]byte
		RequestType string
		RecipientHost [16]byte
		NetworkType byte
		RequestorNaclKey [32]byte
		RequestorMlkemKey [1568]byte
		RequestorCsidhKey [64]byte
		RequestorFrodoKey [9616]byte
	}

	var requestContentObject requestContentStruct

	err := encoding.DecodeMessagePackBytes(false, inputRequestBytes, &requestContentObject)
	if (err != nil) { return [16]byte{}, [16]byte{}, 0, [32]byte{}, [1568]byte{}, [64]byte{}, [9616]byte{}, err }

	requestIdentifier := requestContentObject.RequestIdentifier
	requestType := requestContentObject.RequestType
	recipientHostIdentityHash := requestContentObject.RecipientHost
	networkType := requestContentObject.NetworkType
	requestorNaclKey := requestContentObject.RequestorNaclKey
	requestorMlkemKey := requestContentObject.RequestorMlkemKey
	requestorCsidhKey := requestContentObject.RequestorCsidhKey
	requestorFrodoKey := requestContentObject.RequestorFrodoKey

	if (requestType != "EstablishConnectionKey"){
		return [16]byte{}, [16]byte{}, 0, [32]byte{}, [1568]byte{}, [64]byte{}, [9616]byte{}, errors.New("Request is not EstablishConnectionKey")
	}

	return recipientHostIdentityHash, requestIdentifier, networkType, requestorNaclKey, requestorMlkemKey, requestorCsidhKey, requestorFrodoKey, nil
}

//TODO: Deal with parameters versions somehow
//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetParametersInfo(hostIdentityHash [16]byte, connectionKey [32]byte, networkType byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetParametersInfo",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }
	
	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}

//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetParameters(hostIdentityHash [16]byte, connectionKey [32]byte, networkType byte, parametersTypesList []string)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (len(parametersTypesList) == 0){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetParameters called with invalid parametersTypesList: Empty list")
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		ParametersTypesList []string
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetParameters",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		ParametersTypesList: parametersTypesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }
	
	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[]string: Parameters types list
//	-error
func ReadDecryptedServerRequest_GetParameters(decryptedRequestBytes []byte)([]string, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		ParametersTypesList []string
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(false, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	parametersTypesList := decryptedRequestObject.ParametersTypesList

	if (requestVersion != 1){
		return nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetParameters"){
		return nil, errors.New("Invalid request: requestType not GetParameters")
	}
	
	//TODO: Verify everything

	return parametersTypesList, nil
}

//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetProfilesInfo(hostIdentityHash [16]byte,
										connectionKey [32]byte,
										networkType byte,
										acceptableVersionsList []int,
										profileTypeToRetrieve string,
										rangeStart [16]byte,
										rangeEnd [16]byte,
										identityHashesList [][16]byte,
										criteria []byte,
										getNewestOnly bool,
										getViewableOnly bool)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (profileTypeToRetrieve != "Mate" && profileTypeToRetrieve != "Host" && profileTypeToRetrieve != "Moderator"){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetProfilesInfo called with invalid profileTypeToRetrieve: " + profileTypeToRetrieve)
	}

	err := verifyAcceptableVersionsList(acceptableVersionsList)
	if (err != nil) { return nil, [16]byte{}, err }

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		AcceptableVersions []int
		ProfileType string
		GetNewestOnly bool
		GetViewableOnly bool
		IdentityHashesList [][16]byte
		RangeStart [16]byte
		RangeEnd [16]byte
		Criteria []byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetProfilesInfo",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		AcceptableVersions: acceptableVersionsList,
		ProfileType: profileTypeToRetrieve,
		GetNewestOnly: getNewestOnly,
		GetViewableOnly: getViewableOnly,
		IdentityHashesList: identityHashesList,
		RangeStart: rangeStart,
		RangeEnd: rangeEnd,
	}

	if (profileTypeToRetrieve == "Mate"){
		innerRequestObject.Criteria = criteria
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }
	
	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}



//Outputs:
//	-[]int: Acceptable versions list
//	-string: Profile Type To Retrieve
//	-[16]byte: Range Start
//	-[16]byte: Range End
//	-[][16]byte: Identity hashes list
//	-[]byte: Criteria
//	-bool: Get Newest profiles only
//	-bool: Get Viewable profiles only
//	-error
func ReadDecryptedServerRequest_GetProfilesInfo(decryptedRequestBytes []byte)([]int, string, [16]byte, [16]byte, [][16]byte, []byte, bool, bool, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		AcceptableVersions []int
		ProfileType string
		IdentityHashesList [][16]byte
		RangeStart [16]byte
		RangeEnd [16]byte
		Criteria []byte
		GetNewestOnly bool
		GetViewableOnly bool
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(false, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, "", [16]byte{}, [16]byte{}, nil, nil, false, false, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	acceptableVersionsList := decryptedRequestObject.AcceptableVersions
	profileTypeToRetrieve := decryptedRequestObject.ProfileType
	identityHashesList := decryptedRequestObject.IdentityHashesList
	rangeStart := decryptedRequestObject.RangeStart
	rangeEnd := decryptedRequestObject.RangeEnd
	getNewestOnly := decryptedRequestObject.GetNewestOnly
	getViewableOnly := decryptedRequestObject.GetViewableOnly

	if (requestVersion != 1){
		return nil, "", [16]byte{}, [16]byte{}, nil, nil, false, false, errors.New("Invalid request: Unknown requestVersion")
	}

	if (requestType != "GetProfilesInfo"){
		return nil, "", [16]byte{}, [16]byte{}, nil, nil, false, false, errors.New("Invalid request: requestType not GetProfilesInfo")
	}	

	err = verifyAcceptableVersionsList(acceptableVersionsList)	
	if (err != nil){
		return nil, "", [16]byte{}, [16]byte{}, nil, nil, false, false, err
	}

	//TODO: Verify everything

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

		if (profileTypeToRetrieve != "Mate"){
			return nil, nil
		}

		criteria := decryptedRequestObject.Criteria

		return criteria, nil
	}

	profileCriteria, err := getProfileCriteria()
	if (err != nil) { return nil, "", [16]byte{}, [16]byte{}, nil, nil, false, false, err }

	return acceptableVersionsList, profileTypeToRetrieve, rangeStart, rangeEnd, identityHashesList, profileCriteria, getNewestOnly, getViewableOnly, nil
}



//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetProfiles(hostIdentityHash [16]byte,
									connectionKey [32]byte,
									networkType byte,
									profileTypeToRetrieve string,
									profileHashesList [][28]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (profileTypeToRetrieve != "Mate" && profileTypeToRetrieve != "Host" && profileTypeToRetrieve != "Moderator"){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetProfiles called with invalid profileTypeToRetrieve: " + profileTypeToRetrieve)
	}
	
	if (len(profileHashesList) == 0){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetProfiles called with empty profileHashesList.")
	}

	containsDuplicates, _ := helpers.CheckIfListContainsDuplicates(profileHashesList)
	if (containsDuplicates == true){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetProfiles called with profileHashesList containing duplicates.")
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		ProfileType string
		ProfileHashesList [][28]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetProfiles",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		ProfileType: profileTypeToRetrieve,
		ProfileHashesList: profileHashesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}



//Outputs:
//	-string: Profile Type To Retrieve
//	-[][28]byte: Profile hashes list
//	-error
func ReadDecryptedServerRequest_GetProfiles(decryptedRequestBytes []byte)(string, [][28]byte, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		ProfileType string
		ProfileHashesList [][28]byte
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(false, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return "", nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	profileTypeToRetrieve := decryptedRequestObject.ProfileType	
	profileHashesList := decryptedRequestObject.ProfileHashesList

	if (requestVersion != 1){
		return "", nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetProfiles"){
		return "", nil, errors.New("Invalid request: requestType not GetProfiles")
	}

	//TODO: Verify everything

	return profileTypeToRetrieve, profileHashesList, nil
}


//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetMessageHashesList(hostIdentityHash [16]byte,
											connectionKey [32]byte,
											networkType byte,
											acceptableVersionsList []int,
											rangeStart [10]byte,
											rangeEnd [10]byte,
											inboxesList [][10]byte,
											getViewableOnly bool,
											getDecryptableOnly bool)([]byte, [16]byte, error){

	//TODO: Verify inputs

	err := verifyAcceptableVersionsList(acceptableVersionsList)
	if (err != nil) { return nil, [16]byte{}, err }

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		AcceptableVersions []int
		GetViewableOnly bool
		GetDecryptableOnly bool
		RangeStart [10]byte
		RangeEnd [10]byte
		InboxesList [][10]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetMessageHashesList",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		AcceptableVersions: acceptableVersionsList,
		GetViewableOnly: getViewableOnly,
		GetDecryptableOnly: getDecryptableOnly,
		RangeStart: rangeStart,
		RangeEnd: rangeEnd,
		InboxesList: inboxesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[]int: Acceptable versions list
//	-[10]byte: Inbox Range Start
//	-[10]byte: Inbox Range End
//	-[][10]byte: Inboxes list
//	-bool: Get Viewable messages only
//	-bool: Get Decryptable messages only
//	-error
func ReadDecryptedServerRequest_GetMessageHashesList(decryptedRequestBytes []byte)([]int, [10]byte, [10]byte, [][10]byte, bool, bool, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		AcceptableVersions []int
		InboxesList [][10]byte
		RangeStart [10]byte
		RangeEnd [10]byte
		GetViewableOnly bool
		GetDecryptableOnly bool
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(false, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, [10]byte{}, [10]byte{}, nil, false, false, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	acceptableVersionsList := decryptedRequestObject.AcceptableVersions
	inboxesList := decryptedRequestObject.InboxesList
	rangeStart := decryptedRequestObject.RangeStart
	rangeEnd := decryptedRequestObject.RangeEnd
	getViewableOnly := decryptedRequestObject.GetViewableOnly
	getDecryptableOnly := decryptedRequestObject.GetDecryptableOnly

	if (requestVersion != 1){
		return nil, [10]byte{}, [10]byte{}, nil, false, false, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetMessageHashesList"){
		return nil, [10]byte{}, [10]byte{}, nil, false, false, errors.New("Invalid request: requestType not GetMessageHashesList")
	}

	err = verifyAcceptableVersionsList(acceptableVersionsList)	
	if (err != nil){ return nil, [10]byte{}, [10]byte{}, nil, false, false, err }

	//TODO: Verify everything

	return acceptableVersionsList, rangeStart, rangeEnd, inboxesList, getViewableOnly, getDecryptableOnly, nil
}



//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetMessages(hostIdentityHash [16]byte,
									connectionKey [32]byte,
									networkType byte,
									messageHashesList [][26]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	containsDuplicate, _ := helpers.CheckIfListContainsDuplicates(messageHashesList)
	if (containsDuplicate == true){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetMessages called with messageHashesList containing duplicates.")
	}

	if (len(messageHashesList) == 0){
		return nil, [16]byte{}, errors.New("Invalid messageHashesList: Empty list")
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		MessageHashesList [][26]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetMessages",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		MessageHashesList: messageHashesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[][26]byte: Message Hashes List
//	-error
func ReadDecryptedServerRequest_GetMessages(decryptedRequestBytes []byte)([][26]byte, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		MessageHashesList [][26]byte
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	messageHashesList := decryptedRequestObject.MessageHashesList

	if (requestVersion != 1){
		return nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetMessages"){
		return nil, errors.New("Invalid request: requestType not GetMessages")
	}	
	
	//TODO: Verify everything

	return messageHashesList, nil
}


//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetIdentityReviewsInfo(hostIdentityHash [16]byte,
												connectionKey [32]byte,
												networkType byte,
												acceptableVersionsList []int,
												identityTypeToRetrieve string,
												rangeStart [16]byte,
												rangeEnd [16]byte,
												reviewedIdentityHashesList [][16]byte, // A list of identity hashes
												reviewersList [][16]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (identityTypeToRetrieve != "Mate" && identityTypeToRetrieve != "Host" && identityTypeToRetrieve != "Moderator"){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetIdentityReviewsInfo called with invalid identityTypeToRetrieve: " + identityTypeToRetrieve)
	}

	err := verifyAcceptableVersionsList(acceptableVersionsList)
	if (err != nil) { return nil, [16]byte{}, err }

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		AcceptableVersions []int
		IdentityType string
		ReviewedIdentitiesList [][16]byte
		ReviewersList [][16]byte
		RangeStart [16]byte
		RangeEnd [16]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetIdentityReviewsInfo",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		AcceptableVersions: acceptableVersionsList,
		IdentityType: identityTypeToRetrieve,
		ReviewedIdentitiesList: reviewedIdentityHashesList,
		ReviewersList: reviewersList,
		RangeStart: rangeStart,
		RangeEnd: rangeEnd,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}

//Outputs:
//	-[]int: Acceptable review versions
//	-string: IdentityType
//	-[16]byte: Range Start
//	-[16]byte: Range End
//	-[][16]byte: Reviewed identity hashes list (If empty, ignore)
//	-[][16]byte: Reviewers list (identity hashes of reviewers)
//	-error
func ReadDecryptedServerRequest_GetIdentityReviewsInfo(decryptedRequestBytes []byte)([]int, string, [16]byte, [16]byte, [][16]byte, [][16]byte, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		AcceptableVersions []int
		ReviewedIdentitiesList [][16]byte
		ReviewersList [][16]byte
		IdentityType string
		RangeStart [16]byte
		RangeEnd [16]byte
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, "", [16]byte{}, [16]byte{}, nil, nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	acceptableVersionsList := decryptedRequestObject.AcceptableVersions
	reviewedIdentityHashesList := decryptedRequestObject.ReviewedIdentitiesList
	reviewersList := decryptedRequestObject.ReviewersList
	identityType := decryptedRequestObject.IdentityType
	rangeStart := decryptedRequestObject.RangeStart
	rangeEnd := decryptedRequestObject.RangeEnd

	if (requestVersion != 1){
		return nil, "", [16]byte{}, [16]byte{}, nil, nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetIdentityReviewsInfo"){
		return nil, "", [16]byte{}, [16]byte{}, nil, nil, errors.New("Invalid request: requestType not GetIdentityReviewsInfo")
	}

 	err = verifyAcceptableVersionsList(acceptableVersionsList)	
	if (err != nil){ return nil, "", [16]byte{}, [16]byte{}, nil, nil, err }
	
	//TODO: Verify everything

	return acceptableVersionsList, identityType, rangeStart, rangeEnd, reviewedIdentityHashesList, reviewersList, nil
}


//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetMessageReviewsInfo(hostIdentityHash [16]byte,
												connectionKey [32]byte,
												networkType byte,
												acceptableVersionsList []int,
												rangeStart [10]byte,
												rangeEnd [10]byte,
												reviewedMessageHashesList [][26]byte,
												reviewersList [][16]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	err := verifyAcceptableVersionsList(acceptableVersionsList)
	if (err != nil) { return nil, [16]byte{}, err }

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		AcceptableVersions []int
		ReviewedMessagesList [][26]byte
		ReviewersList [][16]byte
		RangeStart [10]byte
		RangeEnd [10]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetMessageReviewsInfo",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		AcceptableVersions: acceptableVersionsList,
		ReviewedMessagesList: reviewedMessageHashesList,
		ReviewersList: reviewersList,
		RangeStart: rangeStart,
		RangeEnd: rangeEnd,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[]int: Acceptable review versions
//	-[10]byte: Range Start
//	-[10]byte: Range End
//	-[][26]byte: Reviewed hashes list (A list of message hashes)
//	-[][16]byte: Reviewers list (identity hashes of reviewers)
//	-error
func ReadDecryptedServerRequest_GetMessageReviewsInfo(decryptedRequestBytes []byte)([]int, [10]byte, [10]byte, [][26]byte, [][16]byte, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		AcceptableVersions []int
		ReviewedMessagesList [][26]byte
		ReviewersList [][16]byte
		RangeStart [10]byte
		RangeEnd [10]byte
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, [10]byte{}, [10]byte{}, nil, nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	acceptableVersionsList := decryptedRequestObject.AcceptableVersions
	reviewedMessageHashesList := decryptedRequestObject.ReviewedMessagesList
	reviewersList := decryptedRequestObject.ReviewersList
	rangeStart := decryptedRequestObject.RangeStart
	rangeEnd := decryptedRequestObject.RangeEnd

	if (requestVersion != 1){
		return nil, [10]byte{}, [10]byte{}, nil, nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetMessageReviewsInfo"){
		return nil, [10]byte{}, [10]byte{}, nil, nil, errors.New("Invalid request: requestType not GetMessageReviewsInfo")
	}

 	err = verifyAcceptableVersionsList(acceptableVersionsList)	
	if (err != nil){ return nil, [10]byte{}, [10]byte{}, nil, nil, err }
	
	//TODO: Verify everything

	return acceptableVersionsList, rangeStart, rangeEnd, reviewedMessageHashesList, reviewersList, nil
}


//Outputs:
//	-[]byte: Request Bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetReviews(hostIdentityHash [16]byte,
									connectionKey [32]byte,
									networkType byte,
									reviewHashesList [][29]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (len(reviewHashesList) == 0){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetReviews called with empty reviewHashesList")
	}

	containsDuplicate, _ := helpers.CheckIfListContainsDuplicates(reviewHashesList)
	if (containsDuplicate == true){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetReviews called with reviewHashesList containing duplicates.")
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		ReviewHashesList [][29]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetReviews",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		ReviewHashesList: reviewHashesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }
	
	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[][29]byte: Review Hashes List
//	-error
func ReadDecryptedServerRequest_GetReviews(decryptedRequestBytes []byte)([][29]byte, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		ReviewHashesList [][29]byte
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	reviewHashesList := decryptedRequestObject.ReviewHashesList

	if (requestVersion != 1){
		return nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetReviews"){
		return nil, errors.New("Invalid request: requestType not GetReviews")
	}
	
	//TODO: Verify review hashes list
	
	return reviewHashesList, nil
}


//Outputs:
//	-[]byte: Request bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetIdentityReportsInfo(hostIdentityHash [16]byte,
												connectionKey [32]byte,
												networkType byte,
												acceptableVersionsList []int,
												identityTypeToRetrieve string,
												rangeStart [16]byte,
												rangeEnd [16]byte,
												reportedIdentityHashesList [][16]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (identityTypeToRetrieve != "Mate" && identityTypeToRetrieve != "Host" && identityTypeToRetrieve != "Moderator"){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetIdentityReportsInfo called with invalid identityTypeToRetrieve: " + identityTypeToRetrieve)
	}

	err := verifyAcceptableVersionsList(acceptableVersionsList)
	if (err != nil) { return nil, [16]byte{}, err }

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		AcceptableVersions []int
		IdentityType string
		ReportedIdentitiesList [][16]byte
		RangeStart [16]byte
		RangeEnd [16]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetIdentityReportsInfo",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		AcceptableVersions: acceptableVersionsList,
		ReportedIdentitiesList: reportedIdentityHashesList,
		RangeStart: rangeStart,
		RangeEnd: rangeEnd,
		IdentityType: identityTypeToRetrieve,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }
	
	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[]int: Acceptable versions list
//	-string: IdentityType
//	-[16]byte: Range Start
//	-[16]byte: Range End
//	-[][16]byte: Reported identity hashes list
//	-error
func ReadDecryptedServerRequest_GetIdentityReportsInfo(decryptedRequestBytes []byte)([]int, string, [16]byte, [16]byte, [][16]byte, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		AcceptableVersions []int
		IdentityType string
		ReportedIdentitiesList [][16]byte
		RangeStart [16]byte
		RangeEnd [16]byte
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, "", [16]byte{}, [16]byte{}, nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	acceptableVersionsList := decryptedRequestObject.AcceptableVersions
	identityTypeToRetrieve := decryptedRequestObject.IdentityType
	reportedIdentitiesList := decryptedRequestObject.ReportedIdentitiesList
	rangeStart := decryptedRequestObject.RangeStart
	rangeEnd := decryptedRequestObject.RangeEnd

	if (requestVersion != 1){
		return nil, "", [16]byte{}, [16]byte{}, nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetIdentityReportsInfo"){
		return nil, "", [16]byte{}, [16]byte{}, nil, errors.New("Invalid request: requestType not GetIdentityReportsInfo: " + requestType)
	}

	//TODO: Verify everything

	err = verifyAcceptableVersionsList(acceptableVersionsList)	
	if (err != nil){ return nil, "", [16]byte{}, [16]byte{}, nil, err }

	return acceptableVersionsList, identityTypeToRetrieve, rangeStart, rangeEnd, reportedIdentitiesList, nil
}


//Outputs:
//	-[]byte: Request bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetMessageReportsInfo(hostIdentityHash [16]byte,
												connectionKey [32]byte,
												networkType byte,
												acceptableVersionsList []int,
												rangeStart [10]byte,
												rangeEnd [10]byte,
												reportedMessageHashesList [][26]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	err := verifyAcceptableVersionsList(acceptableVersionsList)
	if (err != nil) { return nil, [16]byte{}, err }

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		AcceptableVersions []int
		ReportedMessagesList [][26]byte
		RangeStart [10]byte
		RangeEnd [10]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetMessageReportsInfo",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		AcceptableVersions: acceptableVersionsList,
		ReportedMessagesList: reportedMessageHashesList,
		RangeStart: rangeStart,
		RangeEnd: rangeEnd,	
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[]int: Acceptable versions list
//	-[10]byte: Range Start
//	-[10]byte: Range End
//	-[][26]byte: Reported message hashes list
//	-error
func ReadDecryptedServerRequest_GetMessageReportsInfo(decryptedRequestBytes []byte)([]int, [10]byte, [10]byte, [][26]byte, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		AcceptableVersions []int
		ReportedMessagesList [][26]byte
		RangeStart [10]byte
		RangeEnd [10]byte
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, [10]byte{}, [10]byte{}, nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	acceptableVersionsList := decryptedRequestObject.AcceptableVersions
	reportedMessagesList := decryptedRequestObject.ReportedMessagesList
	rangeStart := decryptedRequestObject.RangeStart
	rangeEnd := decryptedRequestObject.RangeEnd

	if (requestVersion != 1){
		return nil, [10]byte{}, [10]byte{}, nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetMessageReportsInfo"){
		return nil, [10]byte{}, [10]byte{}, nil, errors.New("Invalid request: requestType not GetMessageReportsInfo: " + requestType)
	}	

	//TODO: Verify everything

	err = verifyAcceptableVersionsList(acceptableVersionsList)	
	if (err != nil){ return nil, [10]byte{}, [10]byte{}, nil, err }

	return acceptableVersionsList, rangeStart, rangeEnd, reportedMessagesList, nil
}


//Outputs:
//	-[]byte: Request bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetReports(hostIdentityHash [16]byte,
									connectionKey [32]byte,
									networkType byte,
									reportHashesList [][30]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (reportHashesList == nil){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetReports called with nil reportHashesList")
	}

	if (len(reportHashesList) == 0){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetReports called with empty reportHashesList")
	}

	containsDuplicate, _ := helpers.CheckIfListContainsDuplicates(reportHashesList)
	if (containsDuplicate == true){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetReports called with reportHashesList containing duplicates.")
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		ReportHashesList [][30]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetReports",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		ReportHashesList: reportHashesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[][30]byte: Report Hashes List
//	-error
func ReadDecryptedServerRequest_GetReports(decryptedRequestBytes []byte)([][30]byte, error){

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		ReportHashesList [][30]byte
	}

	var innerRequestObject innerRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &innerRequestObject)
	if (err != nil) { return nil, err }

	requestVersion := innerRequestObject.RequestVersion
	requestType := innerRequestObject.RequestType
	reportHashesList := innerRequestObject.ReportHashesList

	if (requestVersion != 1){
		return nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetReports"){
		return nil, errors.New("Invalid request: requestType not GetReports: " + requestType)
	}	

	//TODO: Verify reportHashesList

	return reportHashesList, nil
}


//Outputs:
//	-[]byte: Request bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetAddressDeposits(hostIdentityHash [16]byte,
											connectionKey [32]byte,
											networkType byte,
											cryptocurrencyName string,
											addressesList []string)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (cryptocurrencyName != "Ethereum" && cryptocurrencyName != "Cardano"){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetAddressDeposits called with invalid cryptocurrencyName: " + cryptocurrencyName)
	}

	if (len(addressesList) == 0){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetAddressDeposits called with empty addressesList.")
	}

	containsDuplicates, _ := helpers.CheckIfListContainsDuplicates(addressesList)
	if (containsDuplicates == true){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetAddressDeposits called with addressesList containing duplicates.")
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		Cryptocurrency string
		AddressesList []string
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetAddressDeposits",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		Cryptocurrency: cryptocurrencyName,
		AddressesList: addressesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-string: Cryptocurrency
//	-[]string: Addresses list
//	-error
func ReadDecryptedServerRequest_GetAddressDeposits(decryptedRequestBytes []byte)(string, []string, error){

	type decryptedRequestStruct struct{
		RequestVersion int
		RequestType string
		Cryptocurrency string
		AddressesList []string
	}

	var decryptedRequestObject decryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return "", nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	cryptocurrency := decryptedRequestObject.Cryptocurrency
	addressesList := decryptedRequestObject.AddressesList

	if (requestVersion != 1){
		return "", nil, errors.New("Invalid request: Unknown requestVersion.")
	}

	if (requestType != "GetAddressDeposits"){
		return "", nil, errors.New("Invalid request: requestType not GetAddressDeposits: " + requestType)
	}	

	//TODO: Verify everything

	return cryptocurrency, addressesList, nil
}


// The reason we don't need a Messages option is because hosts will always use verifiedStickyStatuses when hosting messages
// A user would never need to know the banned status of a message they have received
// The message could not be banned unless the sender or recipient reported the message
// The recipient will only care if the identity is banned, not the message
//Outputs:
//	-[]byte: Request bytes
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_GetViewableStatuses(hostIdentityHash [16]byte,
											connectionKey [32]byte,
											networkType byte,
											identityHashesList [][16]byte,
											profileHashesList [][28]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (len(identityHashesList) == 0 && len(profileHashesList) == 0){
		return nil, [16]byte{}, errors.New("CreateServerRequest_GetViewableStatuses called when identity hashes list and profile hashes list are both empty.")
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type innerRequestStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		IdentityHashesList [][16]byte
		ProfileHashesList [][28]byte
	}

	innerRequestObject := innerRequestStruct{
		RequestVersion: 1,
		RequestType: "GetViewableStatuses",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		IdentityHashesList: identityHashesList,
		ProfileHashesList: profileHashesList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(innerRequestObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-[][16]byte: Identity Hashes List
//	-[][28]byte: Profile hashes list
//	-error
func ReadDecryptedServerRequest_GetViewableStatuses(decryptedRequestBytes []byte)([][16]byte, [][28]byte, error){

	type requestStruct struct{
		RequestVersion int
		RequestType string
		IdentityHashesList [][16]byte
		ProfileHashesList [][28]byte
	}

	var decryptedRequestObject requestStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedRequestBytes, &decryptedRequestObject)
	if (err != nil) { return nil, nil, err }

	requestVersion := decryptedRequestObject.RequestVersion
	requestType := decryptedRequestObject.RequestType
	identityHashesList := decryptedRequestObject.IdentityHashesList
	profileHashesList := decryptedRequestObject.ProfileHashesList

	if (requestVersion != 1){
		return nil, nil, errors.New("Invalid request: Unknown request version.")
	}

	if (requestType != "GetViewableStatuses"){
		return nil, nil, errors.New("Invalid request: requestType not GetViewableStatuses: " + requestType)
	}

	//TODO: Verify everything

	if (len(identityHashesList) == 0 && len(profileHashesList) == 0){
		return nil, nil, errors.New("GetViewableStatuses request contains empty identityHashesList and profileHashesList")
	}

	return identityHashesList, profileHashesList, nil
}


//Outputs:
//	-[]byte: Final Request
//	-[16]byte: Request identifier
//	-error
func CreateServerRequest_BroadcastContent(hostIdentityHash [16]byte,
										connectionKey [32]byte,
										networkType byte,
										contentType string,
										contentToBroadcastList [][]byte)([]byte, [16]byte, error){

	//TODO: Verify inputs

	if (contentType != "Profile" && contentType != "Message" && contentType != "Review" && contentType != "Report" && contentType != "Parameters"){
		return nil, [16]byte{}, errors.New("CreateServerRequest_BroadcastContent called with invalid contentType: " + contentType)
	}

	if (len(contentToBroadcastList) == 0){
		return nil, [16]byte{}, errors.New("Empty contentToBroadcast list")
	}

	messagepackContentList := make([]messagepack.RawMessage, 0, len(contentToBroadcastList))

	for _, contentBytes := range contentToBroadcastList{

		messagepackContentList = append(messagepackContentList, contentBytes)
	}

	requestIdentifier, err := getNewRequestIdentifier()
	if (err != nil) { return nil, [16]byte{}, err }

	currentTime := time.Now().Unix()

	type requestContentStruct struct{
		RequestVersion int
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
		ContentType string
		ContentList []messagepack.RawMessage
	}

	requestContentObject := requestContentStruct{
		RequestVersion: 1,
		RequestType: "BroadcastContent",
		RequestTime: currentTime,
		RequestIdentifier: requestIdentifier,
		RecipientHost: hostIdentityHash,
		NetworkType: networkType,
		ContentType: contentType,
		ContentList: messagepackContentList,
	}

	innerRequestBytes, err := encoding.EncodeMessagePackBytes(requestContentObject)
	if (err != nil) { return nil, [16]byte{}, err }

	finalEncryptedRequest, err := createEncryptedRequest(connectionKey, innerRequestBytes)
	if (err != nil) { return nil, [16]byte{}, err }

	return finalEncryptedRequest, requestIdentifier, nil
}


//Outputs:
//	-string: Content Type
//	-[][]byte: Content list
//	-error
func ReadDecryptedServerRequest_BroadcastContent(decryptedMessagePackBytes []byte)(string, [][]byte, error){

	type requestContentStruct struct{
		RequestVersion int
		RequestType string
		ContentType string
		ContentList []messagepack.RawMessage
	}

	var requestContentObject requestContentStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedMessagePackBytes, &requestContentObject)
	if (err != nil) { return "", nil, err }

	requestVersion := requestContentObject.RequestVersion
	requestType := requestContentObject.RequestType
	contentType := requestContentObject.ContentType
	messagepackContentList := requestContentObject.ContentList

	if (requestVersion != 1){
		return "", nil, errors.New("Invalid request: Unknown request version.")
	}

	if (requestType != "BroadcastContent"){
		return "", nil, errors.New("Invalid request: requestType not BroadcastContent: " + requestType)
	}

	if (contentType != "Profile" && contentType != "Message" && contentType != "Review" && contentType != "Report" && contentType != "Parameters"){
		return "", nil, errors.New("Invalid BroadcastContent request: Invalid contentType: " + contentType)
	}

	//TODO: Verify everything
	
	// We use the map below to ensure we only received each piece of content once
	receivedContentHashesMap := make(map[string]struct{})

	contentList := make([][]byte, 0, len(messagepackContentList))

	for _, contentBytes := range messagepackContentList{

		ableToRead, contentHash, err := readContent.GetContentHashFromContentBytes(true, contentType, contentBytes)
		if (err != nil) { return "", nil, err }
		if (ableToRead == false){
			// Requestor sent invalid content. Requestor is malicious.
			return "", nil, errors.New("Invalid BroadcastContent request: Invalid " + contentType)
		}

		_, exists := receivedContentHashesMap[string(contentHash)]
		if (exists == true){
			// We received a duplicate content hash. Requestor is malicious.
			return "", nil, errors.New("Invalid BroadcastContent request: received duplicate content hash")
		}
		receivedContentHashesMap[string(contentHash)] = struct{}{}

		contentList = append(contentList, contentBytes)
	}

	if (len(contentList) == 0){
		return "", nil, errors.New("Invalid BroadcastContent request: Empty content list")
	}

	return contentType, contentList, nil
}


// This function will read the standard fields that are contained within all encrypted requests
//Outputs:
//	-[16]byte: Request recipient host
//	-string: Request Type
//	-[16]byte: Request Identifier
//	-byte: Network Type
//	-error
func ReadDecryptedServerRequest_StandardData(decryptedMessagePackBytes []byte)([16]byte, string, [16]byte, byte, error){

	type standardRequestContentStruct struct{
		RequestType string
		RequestTime int64
		RequestIdentifier [16]byte
		RecipientHost [16]byte
		NetworkType byte
	}

	var standardRequestContentObject standardRequestContentStruct

	err := encoding.DecodeMessagePackBytes(true, decryptedMessagePackBytes, &standardRequestContentObject)
	if (err != nil) { return [16]byte{}, "", [16]byte{}, 0, err }

	//TODO: Verify everything

	requestType := standardRequestContentObject.RequestType
	recipientHost := standardRequestContentObject.RecipientHost
	requestIdentifier := standardRequestContentObject.RequestIdentifier
	requestTime := standardRequestContentObject.RequestTime
	networkType := standardRequestContentObject.NetworkType

	currentTime := time.Now().Unix()
	timePassed := currentTime - requestTime
	if (timePassed > 3600){
		//Request has timed out, probably sent by malicious party
		return [16]byte{}, "", [16]byte{}, 0, errors.New(requestType + " Request has timed out.")
	}

	return recipientHost, requestType, requestIdentifier, networkType, nil
}


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

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

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

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

	encryptedRequestObject := encryptedRequestStruct{
		ChaPolyNonce: chaPolyNonce,
		CipheredContent: cipheredContent,
	}

	encryptedRequestBytes, err := encoding.EncodeMessagePackBytes(encryptedRequestObject)
	if (err != nil) { return nil, err }

	return encryptedRequestBytes, nil
}

//Outputs:
//	-bool: Request is well formed and able to decrypt
//	-[]byte: Decrypted request messagePack
//	-error (decryption key inputs are malformed)
func ReadEncryptedRequest(inputRequest []byte, connectionKey [32]byte)(bool, []byte, error){

	//TODO: Verify everything

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

	var encryptedRequestObject encryptedRequestStruct

	err := encoding.DecodeMessagePackBytes(false, inputRequest, &encryptedRequestObject)
	if (err != nil) {
		return false, nil, nil
	}

	chaPolyNonce := encryptedRequestObject.ChaPolyNonce
	cipheredContent := encryptedRequestObject.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
}


