func (fes *APIServer) AdminAddExemptPublicKey(ww http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(io.LimitReader(req.Body, MaxRequestBodySizeBytes))
	requestData := AdminAddExemptPublicKey{}
	if err := decoder.Decode(&requestData); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminAddExemptPublicKey: Problem parsing request body: %v", err))
		return
	}
	publicKeyBytes, _, err := lib.Base58CheckDecode(requestData.PublicKeyBase58Check)
	if err != nil || len(publicKeyBytes) != btcec.PubKeyBytesLenCompressed {
		_AddBadRequestError(ww, fmt.Sprintf("AdminAddExemptPublicKey: Problem decoding public key %s: %v",
			requestData.PublicKeyBase58Check, err))
	}
	dbKey := GlobalStateKeyExemptPublicKey(publicKeyBytes)
	if requestData.IsRemoval {
		if err = fes.GlobalStateDelete(dbKey); err != nil {
			_AddBadRequestError(ww, fmt.Sprintf("AdminAddExemptPublicKey: Error deleting key from global state: %v", err))
			return
		}
		delete(fes.ExemptPublicKeyMap, lib.PkToString(publicKeyBytes, fes.Params))
	} else {
		if err = fes.GlobalStatePut(dbKey, []byte{1}); err != nil {
			_AddBadRequestError(ww, fmt.Sprintf("AdminAddExemptPublicKey: Error adding key to global state: %v", err))
			return
		}
		fes.ExemptPublicKeyMap[lib.PkToString(publicKeyBytes, fes.Params)] = []byte{}
	}
}
AdminAddExemptPublicKey adds or removes a public key from the list of public keys exempt from node fees.
package routes
import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"fmt"
	"github.com/btcsuite/btcd/btcec"
	"github.com/deso-protocol/core/lib"
	"github.com/golang/glog"
	"io"
	"net/http"
)
type TransactionFee struct {
	PublicKeyBase58Check string
	ProfileEntryResponse *ProfileEntryResponse
	AmountNanos          uint64
}
type AdminSetTransactionFeeForTransactionTypeRequest struct {
	TransactionType     lib.TxnString
	NewTransactionFees  []TransactionFee
}
type AdminSetTransactionFeeForTransactionTypeResponse struct {
	TransactionFeeMap map[string][]TransactionFee
}
func (fes *APIServer) AdminSetTransactionFeeForTransactionType(ww http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(io.LimitReader(req.Body, MaxRequestBodySizeBytes))
	requestData := AdminSetTransactionFeeForTransactionTypeRequest{}
	if err := decoder.Decode(&requestData); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetTransactionFeeForTransactionType: Problem parsing request body: %v", err))
		return
	}
	txnType := lib.GetTxnTypeFromString(requestData.TransactionType)
	if txnType == lib.TxnTypeUnset {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetTransactionFeeForTransactionType: %v is not a valid TxnType", requestData.TransactionType))
		return
	}
	outputs, transactionFeeBuf, err := TransformAndEncodeTransactionFees(requestData.NewTransactionFees)
	if err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetTransactionFeeForTransactionType: Error Transforming and encoding transaction fees: %v", err))
		return
	}
	if err = fes.LogFeeSet(txnType, requestData.NewTransactionFees); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetTransactionFeeForTransactionType: Error logging fees in datadog for %v transactions: %v", txnType, err))
		return
	}
	if err = fes.GlobalStatePut(GlobalStateKeyTransactionFeeOutputsFromTxnType(txnType), transactionFeeBuf.Bytes()); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetTransactionFeeForTransactionType: Problem putting fee outputs in global state: %v", err))
		return
	}
	fes.TransactionFeeMap[txnType] = outputs
	res := AdminSetTransactionFeeForTransactionTypeResponse{
		TransactionFeeMap: fes.TxnFeeMapToResponse(false),
	}
	if err = json.NewEncoder(ww).Encode(res); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetTransactionFeeForTransactionType: Problem encoding response as JSON: %v", err))
		return
	}
}
type AdminSetAllTransactionFeesRequest struct {
	NewTransactionFees []TransactionFee
}
type AdminSetAllTransactionFeesResponse struct {
	TransactionFeeMap map[string][]TransactionFee
}
func (fes *APIServer) AdminSetAllTransactionFees(ww http.ResponseWriter, req *http.Request) {
	decoder := json.NewDecoder(io.LimitReader(req.Body, MaxRequestBodySizeBytes))
	requestData := AdminSetAllTransactionFeesRequest{}
	if err := decoder.Decode(&requestData); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetAllTransactionFees: Problem parsing request body: %v", err))
		return
	}
	outputs, transactionFeeBuf, err := TransformAndEncodeTransactionFees(requestData.NewTransactionFees)
	if err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetAllTransactionFees: Error Transforming and encoding transaction fees: %v", err))
		return
	}
	for _, txnType := range lib.AllTxnTypes {
		if err = fes.LogFeeSet(txnType, requestData.NewTransactionFees); err != nil {
			_AddBadRequestError(ww, fmt.Sprintf("AdminSetAllTransactionFees: Error logging fees in datadog for %v transactions: %v", txnType, err))
			return
		}
		if err = fes.GlobalStatePut(GlobalStateKeyTransactionFeeOutputsFromTxnType(txnType), transactionFeeBuf.Bytes()); err != nil {
			_AddBadRequestError(ww, fmt.Sprintf("AdminSetAllTransactionFees: Problem putting fee outputs in global state: %v", err))
			return
		}
		fes.TransactionFeeMap[txnType] = outputs
	}
	res := AdminSetAllTransactionFeesResponse{
		TransactionFeeMap: fes.TxnFeeMapToResponse(false),
	}
	if err = json.NewEncoder(ww).Encode(res); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminSetAllTransactionFees: Problem encoding response as JSON: %v", err))
		return
	}
}
type AdminGetTransactionFeeMapResponse struct {
	TransactionFeeMap map[string][]TransactionFee
}
func (fes *APIServer) AdminGetTransactionFeeMap(ww http.ResponseWriter, req *http.Request) {
	res := AdminGetTransactionFeeMapResponse{
		TransactionFeeMap: fes.TxnFeeMapToResponse(false),
	}
	if err := json.NewEncoder(ww).Encode(res); err != nil {
		_AddBadRequestError(ww, fmt.Sprintf("AdminGetTransactionFeeMap: Problem encoding response as JSON: %v", err))
		return
	}
}
func TransformAndEncodeTransactionFees(transactionFees []TransactionFee) (_outputs []*lib.DeSoOutput, _buf *bytes.Buffer, _err error) {
	if err := CheckTransactionFeeForDuplicatePublicKeys(transactionFees); err != nil {
		return nil, nil, err
	}
	outputs, err := TransformTransactionFeesToOutputs(transactionFees)
	if err != nil {
		return nil, nil, err
	}
	transactionFeeBuf := bytes.NewBuffer([]byte{})
	if err = gob.NewEncoder(transactionFeeBuf).Encode(outputs); err != nil {
		return nil, nil, err
	}
	return outputs, transactionFeeBuf, nil
}
func TransformTransactionFeesToOutputs(transactionFees []TransactionFee) (_outputs []*lib.DeSoOutput, _err error) {
	var outputs []*lib.DeSoOutput
	for _, output := range transactionFees {
		outputPublicKeyBytes, _, err := lib.Base58CheckDecode(output.PublicKeyBase58Check)
		if err != nil || len(outputPublicKeyBytes) != btcec.PubKeyBytesLenCompressed {
			return nil, fmt.Errorf("TransformTransactionFeesToOutputs: Problem decoding output public key %s: %v",
				output.PublicKeyBase58Check, err)
		}
		outputs = append(outputs, &lib.DeSoOutput{
			PublicKey: outputPublicKeyBytes,
			AmountNanos: output.AmountNanos,
		})
	}
	return outputs, nil
}
func (fes *APIServer) TxnFeeMapToResponse(skipProfileEntryResponses bool) map[string][]TransactionFee{
	txnFeeResponseMap := make(map[string][]TransactionFee)
	var utxoView *lib.UtxoView
	if !skipProfileEntryResponses {
		var err error
		if utxoView, err = fes.backendServer.GetMempool().GetAugmentedUniversalView(); err != nil {
			glog.Errorf("TxnFeeMapToResponse: Unable to get utxoView - you won't be able to see usernames and avatars")
		}
	}
	profileEntryResponseMap := make(map[*lib.PKID]*ProfileEntryResponse)
	for txnType, outputs := range fes.TransactionFeeMap {
		var txnOutputs []TransactionFee
		for _, output := range outputs {
			var profileEntryResponse *ProfileEntryResponse
			if !skipProfileEntryResponses && utxoView != nil {
				pkid := utxoView.GetPKIDForPublicKey(output.PublicKey)
				var exists bool
				profileEntryResponse, exists = profileEntryResponseMap[pkid.PKID]
				if !exists {
					profileEntry := utxoView.GetProfileEntryForPKID(pkid.PKID)
					if profileEntry != nil {
						profileEntryResponse = _profileEntryToResponse(profileEntry, fes.Params, nil, utxoView)
					}
					profileEntryResponseMap[pkid.PKID] = profileEntryResponse
				}
			}
			txnOutputs = append(txnOutputs, TransactionFee{
				PublicKeyBase58Check: lib.PkToString(output.PublicKey, fes.Params),
				AmountNanos: output.AmountNanos,
				ProfileEntryResponse: profileEntryResponse,
			})
		}
		txnFeeResponseMap[txnType.String()] = txnOutputs
	}
	return txnFeeResponseMap
}
func (fes *APIServer) GetTransactionFeeMapFromGlobalState() map[lib.TxnType][]*lib.DeSoOutput {
	transactionFeeMap := make(map[lib.TxnType][]*lib.DeSoOutput)
	for _, txnType := range lib.AllTxnTypes {
		desoOutputBytes, err := fes.GlobalStateGet(GlobalStateKeyTransactionFeeOutputsFromTxnType(txnType))
		if err != nil {
			glog.Errorf("Error getting Transaction Fee bytes from global state for transaction type %v (%d): %v - defaulting to no additional fees", txnType.String(), txnType, err)
			transactionFeeMap[txnType] = []*lib.DeSoOutput{}
		} else {
			var feeOutputs []*lib.DeSoOutput
			if err = gob.NewDecoder(bytes.NewReader(desoOutputBytes)).Decode(&feeOutputs); err != nil {
				glog.Errorf("Error decoding desoOutputBytes to slice of DeSoOutputs: %v - default to no additional fees", err)
				transactionFeeMap[txnType] = []*lib.DeSoOutput{}
			} else {
				transactionFeeMap[txnType] = feeOutputs
			}
		}
	}
	return transactionFeeMap
}
func CheckTransactionFeeForDuplicatePublicKeys(newTransactionFees []TransactionFee) error {
	publicKeyToTransactionFeeMap := make(map[string]TransactionFee)
	for _, transactionFee := range newTransactionFees {
		if _, exists := publicKeyToTransactionFeeMap[transactionFee.PublicKeyBase58Check]; exists {
			return fmt.Errorf("duplicate public key detected: %v", transactionFee.PublicKeyBase58Check)
		}
		publicKeyToTransactionFeeMap[transactionFee.PublicKeyBase58Check] = transactionFee
	}
	return nil
}
type AdminAddExemptPublicKey struct {
	PublicKeyBase58Check string
	IsRemoval            bool
}