You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: proposal.go
    path of file: ./repos/cosmos-sdk/x/gov/keeper
    
    the code of the file until where you have to start completion: package keeper

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"slices"
	"strings"
	"time"

	"cosmossdk.io/collections"
	"cosmossdk.io/core/event"
	errorsmod "cosmossdk.io/errors"
	sdkmath "cosmossdk.io/math"
	"cosmossdk.io/x/gov/types"
	v1 "cosmossdk.io/x/gov/types/v1"

	sdk "github.com/cosmos/cosmos-sdk/types"
)

// SubmitProposal creates a new proposal given an array of messages
func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata, title, summary string, proposer sdk.AccAddress, proposalType v1.ProposalType) (v1.Proposal, error) {
	params, err := k.Params.Get(ctx)
	if err != nil {
		return v1.Proposal{}, err
	}

	// additional checks per proposal types
	switch proposalType {
	case v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC:
		proposerStr, _ := k.authKeeper.AddressCodec().BytesToString(proposer)
		if len(params.OptimisticAuthorizedAddresses) > 0 && !slices.Contains(params.OptimisticAuthorizedAddresses, proposerStr) {
			return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidP
    
func (k Keeper) SubmitProposal(ctx context.Context, messages []sdk.Msg, metadata, title, summary string, proposer sdk.AccAddress, proposalType v1.ProposalType) (v1.Proposal, error) {
	params, err := k.Params.Get(ctx)
	if err != nil {
		return v1.Proposal{}, err
	}

	// additional checks per proposal types
	switch proposalType {
	case v1.ProposalType_PROPOSAL_TYPE_OPTIMISTIC:
		proposerStr, _ := k.authKeeper.AddressCodec().BytesToString(proposer)
		if len(params.OptimisticAuthorizedAddresses) > 0 && !slices.Contains(params.OptimisticAuthorizedAddresses, proposerStr) {
			return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposer, "proposer is not authorized to submit optimistic proposal")
		}
	case v1.ProposalType_PROPOSAL_TYPE_MULTIPLE_CHOICE:
		if len(messages) > 0 { // cannot happen, except when the proposal is created via keeper call instead of message server.
			return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposalMsg, "multiple choice proposal should not contain any messages")
		}
	}

	msgs := []string{} // will hold a string slice of all Msg type URLs.

	// Loop through all messages and confirm that each has a handler and the gov module account as the only signer
	for _, msg := range messages {
		msgs = append(msgs, sdk.MsgTypeURL(msg))

		// check if any of the message has message based params
		hasMessagedBasedParams, err := k.MessageBasedParams.Has(ctx, sdk.MsgTypeURL(msg))
		if err != nil {
			return v1.Proposal{}, err
		}

		if hasMessagedBasedParams {
			// TODO(@julienrbrt), in the future, we can check if all messages have the same params
			// and if so, we can allow the proposal.
			if len(messages) > 1 {
				return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposalMsg, "cannot submit multiple messages proposal with message based params")
			}

			if proposalType != v1.ProposalType_PROPOSAL_TYPE_STANDARD {
				return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposalType, "cannot submit non standard proposal with message based params")
			}
		}

		// perform a basic validation of the message
		if m, ok := msg.(sdk.HasValidateBasic); ok {
			if err := m.ValidateBasic(); err != nil {
				return v1.Proposal{}, errorsmod.Wrap(types.ErrInvalidProposalMsg, err.Error())
			}
		}

		signers, _, err := k.cdc.GetMsgV1Signers(msg)
		if err != nil {
			return v1.Proposal{}, err
		}
		if len(signers) != 1 {
			return v1.Proposal{}, types.ErrInvalidSigner
		}

		// assert that the governance module account is the only signer of the messages
		if !bytes.Equal(signers[0], k.GetGovernanceAccount(ctx).GetAddress()) {
			return v1.Proposal{}, errorsmod.Wrapf(types.ErrInvalidSigner, sdk.AccAddress(signers[0]).String())
		}

		if err := k.environment.RouterService.MessageRouterService().CanInvoke(ctx, sdk.MsgTypeURL(msg)); err != nil {
			return v1.Proposal{}, errorsmod.Wrap(types.ErrUnroutableProposalMsg, err.Error())
		}

		// Only if it's a MsgExecLegacyContent we try to execute the
		// proposal in a cached context.
		// For other Msgs, we do not verify the proposal messages any further.
		// They may fail upon execution.
		// ref: https://github.com/cosmos/cosmos-sdk/pull/10868#discussion_r784872842
		msg, ok := msg.(*v1.MsgExecLegacyContent)
		if !ok {
			continue
		}

		content, err := v1.LegacyContentFromMessage(msg)
		if err != nil {
			return v1.Proposal{}, errorsmod.Wrapf(types.ErrInvalidProposalContent, "%+v", err)
		}

		// Ensure that the content has a respective handler
		if !k.legacyRouter.HasRoute(content.ProposalRoute()) {
			return v1.Proposal{}, errorsmod.Wrap(types.ErrNoProposalHandlerExists, content.ProposalRoute())
		}

		if err = k.environment.BranchService.Execute(ctx, func(ctx context.Context) error {
			handler := k.legacyRouter.GetRoute(content.ProposalRoute())
			if err := handler(ctx, content); err != nil {
				return types.ErrInvalidProposalContent.Wrapf("failed to run legacy handler %s, %+v", content.ProposalRoute(), err)
			}

			return errors.New("we don't want to execute the proposal, we just want to check if it can be executed")
		}); errors.Is(err, types.ErrInvalidProposalContent) {
			return v1.Proposal{}, err
		}
	}

	proposalID, err := k.ProposalID.Next(ctx)
	if err != nil {
		return v1.Proposal{}, err
	}

	submitTime := k.environment.HeaderService.GetHeaderInfo(ctx).Time
	proposal, err := v1.NewProposal(messages, proposalID, submitTime, submitTime.Add(*params.MaxDepositPeriod), metadata, title, summary, proposer, proposalType)
	if err != nil {
		return v1.Proposal{}, err
	}

	if err = k.Proposals.Set(ctx, proposal.Id, proposal); err != nil {
		return v1.Proposal{}, err
	}
	err = k.InactiveProposalsQueue.Set(ctx, collections.Join(*proposal.DepositEndTime, proposalID), proposalID)
	if err != nil {
		return v1.Proposal{}, err
	}

	// called right after a proposal is submitted
	err = k.Hooks().AfterProposalSubmission(ctx, proposalID)
	if err != nil {
		return v1.Proposal{}, err
	}

	if err := k.environment.EventService.EventManager(ctx).EmitKV(
		types.EventTypeSubmitProposal,
		event.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)),
		event.NewAttribute(types.AttributeKeyProposalMessages, strings.Join(msgs, ",")),
	); err != nil {
		return v1.Proposal{}, fmt.Errorf("failed to emit event: %w", err)
	}

	return proposal, nil
}