2024-05-01 06:08:58 +00:00
|
|
|
package keeper
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
errorsmod "cosmossdk.io/errors"
|
2024-08-02 10:34:24 +00:00
|
|
|
"github.com/cometbft/cometbft/libs/log"
|
2024-05-01 06:08:58 +00:00
|
|
|
"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
|
|
|
|
}
|