package keeper import ( "fmt" errorsmod "cosmossdk.io/errors" "github.com/cometbft/cometbft/libs/log" "github.com/coniks-sys/coniks-go/crypto/vrf" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/store/prefix" storetypes "github.com/cosmos/cosmos-sdk/store/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/0glabs/0g-chain/x/council/v1/types" ) // Keeper of the inflation store type Keeper struct { storeKey storetypes.StoreKey cdc codec.BinaryCodec stakingKeeper types.StakingKeeper } // NewKeeper creates a new mint Keeper instance func NewKeeper( storeKey storetypes.StoreKey, cdc codec.BinaryCodec, stakingKeeper types.StakingKeeper, ) Keeper { return Keeper{ storeKey: storeKey, cdc: cdc, stakingKeeper: stakingKeeper, } } // Logger returns a module-specific logger. func (k Keeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger().With("module", "x/"+types.ModuleName) } // ------------------------------------------ // Councils // ------------------------------------------ func (k Keeper) SetCurrentCouncilID(ctx sdk.Context, id uint64) { store := ctx.KVStore(k.storeKey) store.Set(types.CurrentCouncilIDKey, types.GetKeyFromID(id)) } func (k Keeper) GetCurrentCouncilID(ctx sdk.Context) (uint64, error) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.CurrentCouncilIDKey) if bz == nil { return 0, errorsmod.Wrap(types.ErrInvalidGenesis, "current council ID not set at genesis") } return types.Uint64FromBytes(bz), nil } func (k Keeper) IncrementCurrentCouncilID(ctx sdk.Context) error { id, err := k.GetCurrentCouncilID(ctx) if err != nil { return err } k.SetCurrentCouncilID(ctx, id+1) return nil } func (k Keeper) SetVotingStartHeight(ctx sdk.Context, votingStartHeight uint64) { store := ctx.KVStore(k.storeKey) store.Set(types.VotingStartHeightKey, types.GetKeyFromID(votingStartHeight)) } func (k Keeper) GetVotingStartHeight(ctx sdk.Context) (uint64, error) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.VotingStartHeightKey) if bz == nil { return 0, errorsmod.Wrap(types.ErrInvalidGenesis, "voting start height not set at genesis") } return types.Uint64FromBytes(bz), nil } func (k Keeper) SetVotingPeriod(ctx sdk.Context, votingPeriod uint64) { store := ctx.KVStore(k.storeKey) store.Set(types.VotingPeriodKey, types.GetKeyFromID(votingPeriod)) } func (k Keeper) GetVotingPeriod(ctx sdk.Context) (uint64, error) { store := ctx.KVStore(k.storeKey) bz := store.Get(types.VotingPeriodKey) if bz == nil { return 0, errorsmod.Wrap(types.ErrInvalidGenesis, "voting period not set at genesis") } return types.Uint64FromBytes(bz), nil } // StoreNewCouncil stores a council, adding a new ID func (k Keeper) StoreNewCouncil(ctx sdk.Context, votingStartHeight uint64) error { currentCouncilID, err := k.GetCurrentCouncilID(ctx) if err != nil { return err } votingPeriod, err := k.GetVotingPeriod(ctx) if err != nil { return err } com := types.Council{ ID: currentCouncilID + 1, VotingStartHeight: votingStartHeight, StartHeight: votingStartHeight + votingPeriod, EndHeight: votingStartHeight + votingPeriod*2, Votes: []types.Vote{}, Members: []sdk.ValAddress{}, } k.SetCouncil(ctx, com) return nil } func (k Keeper) GetCouncil(ctx sdk.Context, councilID uint64) (types.Council, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.CouncilKeyPrefix) bz := store.Get(types.GetKeyFromID(councilID)) if bz == nil { return types.Council{}, false } var com types.Council k.cdc.MustUnmarshal(bz, &com) return com, true } // SetCouncil puts a council into the store. func (k Keeper) SetCouncil(ctx sdk.Context, council types.Council) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.CouncilKeyPrefix) bz := k.cdc.MustMarshal(&council) store.Set(types.GetKeyFromID(council.ID), bz) } // // DeleteProposal removes a proposal from the store. // func (k Keeper) DeleteProposal(ctx sdk.Context, proposalID uint64) { // store := prefix.NewStore(ctx.KVStore(k.storeKey), types.ProposalKeyPrefix) // store.Delete(types.GetKeyFromID(proposalID)) // } // IterateProposals provides an iterator over all stored proposals. // For each proposal, cb will be called. If cb returns true, the iterator will close and stop. func (k Keeper) IterateCouncil(ctx sdk.Context, cb func(proposal types.Council) (stop bool)) { iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.CouncilKeyPrefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var council types.Council k.cdc.MustUnmarshal(iterator.Value(), &council) if cb(council) { break } } } func (k Keeper) GetCouncils(ctx sdk.Context) types.Councils { results := types.Councils{} k.IterateCouncil(ctx, func(prop types.Council) bool { results = append(results, prop) return false }) return results } // // DeleteProposalAndVotes removes a proposal and its associated votes. // func (k Keeper) DeleteProposalAndVotes(ctx sdk.Context, proposalID uint64) { // votes := k.GetVotesByProposal(ctx, proposalID) // k.DeleteProposal(ctx, proposalID) // for _, v := range votes { // k.DeleteVote(ctx, v.ProposalID, v.Voter) // } // } // ------------------------------------------ // Votes // ------------------------------------------ // GetVote gets a vote from the store. func (k Keeper) GetVote(ctx sdk.Context, epochID uint64, voter sdk.ValAddress) (types.Vote, bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.VoteKeyPrefix) bz := store.Get(types.GetVoteKey(epochID, voter)) if bz == nil { return types.Vote{}, false } var vote types.Vote k.cdc.MustUnmarshal(bz, &vote) return vote, true } // SetVote puts a vote into the store. func (k Keeper) SetVote(ctx sdk.Context, vote types.Vote) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.VoteKeyPrefix) bz := k.cdc.MustMarshal(&vote) store.Set(types.GetVoteKey(vote.CouncilID, vote.Voter), bz) } // DeleteVote removes a Vote from the store. func (k Keeper) DeleteVote(ctx sdk.Context, councilID uint64, voter sdk.ValAddress) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.VoteKeyPrefix) store.Delete(types.GetVoteKey(councilID, voter)) } // IterateVotes provides an iterator over all stored votes. // For each vote, cb will be called. If cb returns true, the iterator will close and stop. func (k Keeper) IterateVotes(ctx sdk.Context, cb func(vote types.Vote) (stop bool)) { iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.VoteKeyPrefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var vote types.Vote k.cdc.MustUnmarshal(iterator.Value(), &vote) if cb(vote) { break } } } // GetVotes returns all stored votes. func (k Keeper) GetVotes(ctx sdk.Context) []types.Vote { results := []types.Vote{} k.IterateVotes(ctx, func(vote types.Vote) bool { results = append(results, vote) return false }) return results } // GetVotesByProposal returns all votes for one proposal. func (k Keeper) GetVotesByCouncil(ctx sdk.Context, councilID uint64) []types.Vote { results := []types.Vote{} iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), append(types.VoteKeyPrefix, types.GetKeyFromID(councilID)...)) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { var vote types.Vote k.cdc.MustUnmarshal(iterator.Value(), &vote) results = append(results, vote) } return results } // ------------------------------------------ // Voters // ------------------------------------------ func (k Keeper) SetVoter(ctx sdk.Context, voter sdk.ValAddress, pk vrf.PublicKey) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.VoterKeyPrefix) store.Set(types.GetVoterKey(voter), pk) fmt.Printf("voterStoreKey: %v, publicKey: %v\n", types.GetVoterKey(voter), pk) } func (k Keeper) IterateVoters(ctx sdk.Context, cb func(voter sdk.ValAddress, pk vrf.PublicKey) (stop bool)) { iterator := sdk.KVStorePrefixIterator(ctx.KVStore(k.storeKey), types.VoterKeyPrefix) defer iterator.Close() for ; iterator.Valid(); iterator.Next() { if cb(sdk.ValAddress(iterator.Key()[1:]), vrf.PublicKey(iterator.Value())) { break } } } // GetVotes returns all stored voters func (k Keeper) GetVoters(ctx sdk.Context) []sdk.ValAddress { results := []sdk.ValAddress{} k.IterateVoters(ctx, func(voter sdk.ValAddress, _ vrf.PublicKey) bool { results = append(results, voter) return false }) return results } func (k Keeper) AddVoter(ctx sdk.Context, voter sdk.ValAddress, key []byte) error { if len(key) != vrf.PublicKeySize { return types.ErrInvalidPublicKey } k.SetVoter(ctx, voter, vrf.PublicKey(key)) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeRegister, sdk.NewAttribute(types.AttributeKeyVoter, voter.String()), // TODO: types.AttributeKeyPublicKey ), ) return nil } func (k Keeper) AddVote(ctx sdk.Context, councilID uint64, voter sdk.ValAddress, ballots []*types.Ballot) error { // Validate com, found := k.GetCouncil(ctx, councilID) if !found { return errorsmod.Wrapf(types.ErrUnknownCouncil, "%d", councilID) } if com.HasVotingEndedBy(ctx.BlockHeight()) { return errorsmod.Wrapf(types.ErrProposalExpired, "%d ≥ %d", ctx.BlockHeight(), com.StartHeight) } // TODO: verify if the voter is registered // TODO: verify whether ballots are valid or not // Store vote, overwriting any prior vote k.SetVote(ctx, types.NewVote(councilID, voter, ballots)) ctx.EventManager().EmitEvent( sdk.NewEvent( types.EventTypeVote, sdk.NewAttribute(types.AttributeKeyCouncilID, fmt.Sprintf("%d", com.ID)), sdk.NewAttribute(types.AttributeKeyVoter, voter.String()), // TODO: types.AttributeKeyBallots ), ) return nil }