package keeper import ( "fmt" "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" ) // SubmitProposal create new proposal given a content func (keeper Keeper) SubmitProposal(ctx sdk.Context, content v1beta1.Content) (v1beta1.Proposal, error) { if !keeper.router.HasRoute(content.ProposalRoute()) { return v1beta1.Proposal{}, sdkerrors.Wrap(types.ErrNoProposalHandlerExists, content.ProposalRoute()) } // Execute the proposal content in a new context branch (with branched store) // to validate the actual parameter changes before the proposal proceeds // through the governance process. State is not persisted. cacheCtx, _ := ctx.CacheContext() handler := keeper.router.GetRoute(content.ProposalRoute()) if err := handler(cacheCtx, content); err != nil { return v1beta1.Proposal{}, sdkerrors.Wrap(v1beta1.ErrInvalidProposalContent, err.Error()) } proposalID, err := keeper.GetProposalID(ctx) if err != nil { return v1beta1.Proposal{}, err } submitTime := ctx.BlockHeader().Time depositPeriod := keeper.GetDepositParams(ctx).MaxDepositPeriod proposal, err := v1beta1.NewProposal(content, proposalID, submitTime, submitTime.Add(depositPeriod)) if err != nil { return v1beta1.Proposal{}, err } keeper.SetProposal(ctx, proposal) keeper.InsertInactiveProposalQueue(ctx, proposalID, proposal.DepositEndTime) keeper.SetProposalID(ctx, proposalID+1) // called right after a proposal is submitted keeper.AfterProposalSubmission(ctx, proposalID) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeSubmitProposal, sdk.NewAttribute(types.AttributeKeyProposalID, fmt.Sprintf("%d", proposalID)), ), ) return proposal, nil } // GetProposal get proposal from store by ProposalID. // Panics if can't unmarshal the proposal. func (keeper Keeper) GetProposal(ctx sdk.Context, proposalID uint64) (v1beta1.Proposal, bool) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(types.ProposalKey(proposalID)) if bz == nil { return v1beta1.Proposal{}, false } var proposal v1beta1.Proposal if err := keeper.UnmarshalProposal(bz, &proposal); err != nil { panic(err) } return proposal, true } // SetProposal set a proposal to store. // Panics if can't marshal the proposal. func (keeper Keeper) SetProposal(ctx sdk.Context, proposal v1beta1.Proposal) { bz, err := keeper.MarshalProposal(proposal) if err != nil { panic(err) } store := ctx.KVStore(keeper.storeKey) store.Set(types.ProposalKey(proposal.ProposalId), bz) } // DeleteProposal deletes a proposal from store. // Panics if the proposal doesn't exist. func (keeper Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) proposal, ok := keeper.GetProposal(ctx, proposalID) if !ok { panic(fmt.Sprintf("couldn't find proposal with id#%d", proposalID)) } keeper.RemoveFromInactiveProposalQueue(ctx, proposalID, proposal.DepositEndTime) keeper.RemoveFromActiveProposalQueue(ctx, proposalID, proposal.VotingEndTime) store.Delete(types.ProposalKey(proposalID)) } // IterateProposals iterates over the all the proposals and performs a callback function. // Panics when the iterator encounters a proposal which can't be unmarshaled. func (keeper Keeper) IterateProposals(ctx sdk.Context, cb func(proposal v1beta1.Proposal) (stop bool)) { store := ctx.KVStore(keeper.storeKey) iterator := sdk.KVStorePrefixIterator(store, types.ProposalsKeyPrefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var proposal v1beta1.Proposal err := keeper.UnmarshalProposal(iterator.Value(), &proposal) if err != nil { panic(err) } if cb(proposal) { break } } } // GetProposals returns all the proposals from store func (keeper Keeper) GetProposals(ctx sdk.Context) (proposals v1beta1.Proposals) { keeper.IterateProposals(ctx, func(proposal v1beta1.Proposal) bool { proposals = append(proposals, proposal) return false }) return } // GetProposalsFiltered retrieves proposals filtered by a given set of params which // include pagination parameters along with voter and depositor addresses and a // proposal status. The voter address will filter proposals by whether or not // that address has voted on proposals. The depositor address will filter proposals // by whether or not that address has deposited to them. Finally, status will filter // proposals by status. // // NOTE: If no filters are provided, all proposals will be returned in paginated // form. func (keeper Keeper) GetProposalsFiltered(ctx sdk.Context, params v1beta1.QueryProposalsParams) v1beta1.Proposals { proposals := keeper.GetProposals(ctx) filteredProposals := make([]v1beta1.Proposal, 0, len(proposals)) for _, p := range proposals { matchVoter, matchDepositor, matchStatus := true, true, true // match status (if supplied/valid) if v1beta1.ValidProposalStatus(params.ProposalStatus) { matchStatus = p.Status == params.ProposalStatus } // match voter address (if supplied) if len(params.Voter) > 0 { _, matchVoter = keeper.GetVote(ctx, p.ProposalId, params.Voter) } // match depositor (if supplied) if len(params.Depositor) > 0 { _, matchDepositor = keeper.GetDeposit(ctx, p.ProposalId, params.Depositor) } if matchVoter && matchDepositor && matchStatus { filteredProposals = append(filteredProposals, p) } } start, end := client.Paginate(len(filteredProposals), params.Page, params.Limit, 100) if start < 0 || end < 0 { filteredProposals = []v1beta1.Proposal{} } else { filteredProposals = filteredProposals[start:end] } return filteredProposals } // GetProposalID gets the highest proposal ID func (keeper Keeper) GetProposalID(ctx sdk.Context) (proposalID uint64, err error) { store := ctx.KVStore(keeper.storeKey) bz := store.Get(types.ProposalIDKey) if bz == nil { return 0, sdkerrors.Wrap(types.ErrInvalidGenesis, "initial proposal ID hasn't been set") } proposalID = types.GetProposalIDFromBytes(bz) return proposalID, nil } // SetProposalID sets the new proposal ID to the store func (keeper Keeper) SetProposalID(ctx sdk.Context, proposalID uint64) { store := ctx.KVStore(keeper.storeKey) store.Set(types.ProposalIDKey, types.GetProposalIDBytes(proposalID)) } func (keeper Keeper) ActivateVotingPeriod(ctx sdk.Context, proposal v1beta1.Proposal) { proposal.VotingStartTime = ctx.BlockHeader().Time votingPeriod := keeper.GetVotingParams(ctx).VotingPeriod proposal.VotingEndTime = proposal.VotingStartTime.Add(votingPeriod) proposal.Status = v1beta1.StatusVotingPeriod keeper.SetProposal(ctx, proposal) keeper.RemoveFromInactiveProposalQueue(ctx, proposal.ProposalId, proposal.DepositEndTime) keeper.InsertActiveProposalQueue(ctx, proposal.ProposalId, proposal.VotingEndTime) } func (keeper Keeper) MarshalProposal(proposal v1beta1.Proposal) ([]byte, error) { bz, err := keeper.cdc.Marshal(&proposal) if err != nil { return nil, err } return bz, nil } func (keeper Keeper) UnmarshalProposal(bz []byte, proposal *v1beta1.Proposal) error { err := keeper.cdc.Unmarshal(bz, proposal) if err != nil { return err } return nil }