github.com/KiraCore/sekai@v0.3.43/x/gov/abci.go (about)

     1  package gov
     2  
     3  import (
     4  	"fmt"
     5  	"time"
     6  
     7  	"github.com/KiraCore/sekai/x/gov/keeper"
     8  	"github.com/KiraCore/sekai/x/gov/types"
     9  	sdk "github.com/cosmos/cosmos-sdk/types"
    10  )
    11  
    12  func EndBlocker(ctx sdk.Context, k keeper.Keeper) {
    13  	enactmentIterator := k.GetEnactmentProposalsWithFinishedEnactmentEndTimeIterator(ctx, ctx.BlockTime())
    14  	defer enactmentIterator.Close()
    15  	for ; enactmentIterator.Valid(); enactmentIterator.Next() {
    16  		proposalID := keeper.BytesToProposalID(enactmentIterator.Value())
    17  		slash := k.GetAverageVotesSlash(ctx, proposalID)
    18  		processEnactmentProposal(ctx, k, proposalID, slash)
    19  	}
    20  
    21  	activeIterator := k.GetActiveProposalsWithFinishedVotingEndTimeIterator(ctx, ctx.BlockTime())
    22  	defer activeIterator.Close()
    23  	for ; activeIterator.Valid(); activeIterator.Next() {
    24  		processProposal(ctx, k, keeper.BytesToProposalID(activeIterator.Value()))
    25  	}
    26  
    27  	pollIterator := k.GetPollsWithFinishedVotingEndTimeIterator(ctx, time.Now())
    28  	defer pollIterator.Close()
    29  	for ; pollIterator.Valid(); pollIterator.Next() {
    30  		processPoll(ctx, k, sdk.BigEndianToUint64(pollIterator.Value()))
    31  	}
    32  }
    33  
    34  func processPoll(ctx sdk.Context, k keeper.Keeper, pollID uint64) {
    35  	var totalVoters int
    36  	var actors []types.NetworkActor
    37  	var duplicateMap = make(map[string]bool)
    38  
    39  	poll, err := k.GetPoll(ctx, pollID)
    40  	if err != nil {
    41  		panic(err)
    42  	}
    43  
    44  	for _, role := range poll.Roles {
    45  		availableVoters := k.GetNetworkActorsByRole(ctx, role)
    46  
    47  		for ; availableVoters.Valid(); availableVoters.Next() {
    48  			if _, ok := duplicateMap[sdk.AccAddress(availableVoters.Value()).String()]; !ok {
    49  				duplicateMap[sdk.AccAddress(availableVoters.Value()).String()] = true
    50  				actors = append(actors, k.GetNetworkActorOrFail(ctx, availableVoters.Value()))
    51  			}
    52  		}
    53  	}
    54  
    55  	votes := k.GetPollVotes(ctx, pollID)
    56  	totalVoters += len(actors)
    57  	numVotes := len(votes)
    58  	properties := k.GetNetworkProperties(ctx)
    59  	quorum := properties.VoteQuorum
    60  
    61  	isQuorum, err := types.IsQuorum(quorum, uint64(numVotes), uint64(totalVoters))
    62  	if err != nil {
    63  		panic(fmt.Sprintf("Invalid quorum on proposal: pollID=%d, err=%+v", pollID, err))
    64  	}
    65  
    66  	if isQuorum {
    67  		numActorsWithVeto := len(types.GetActorsWithVoteWithVeto(actors))
    68  		calculatedVote := types.CalculatePollVotes(votes, uint64(numActorsWithVeto))
    69  		poll.Result = calculatedVote.ProcessResult(properties)
    70  	} else {
    71  		poll.Result = types.PollQuorumNotReached
    72  	}
    73  
    74  	k.SavePoll(ctx, poll)
    75  	k.RemoveActivePoll(ctx, poll)
    76  }
    77  
    78  func processProposal(ctx sdk.Context, k keeper.Keeper, proposalID uint64) {
    79  	proposal, found := k.GetProposal(ctx, proposalID)
    80  	if !found {
    81  		panic("proposal was expected to exist")
    82  	}
    83  
    84  	// skip execution if block height condition does not meet
    85  	if proposal.MinVotingEndBlockHeight > ctx.BlockHeight() {
    86  		return
    87  	}
    88  
    89  	votes := k.GetProposalVotes(ctx, proposalID)
    90  
    91  	availableVoters := k.GetNetworkActorsByAbsoluteWhitelistPermission(ctx, proposal.GetContent().VotePermission())
    92  	totalVoters := len(availableVoters)
    93  
    94  	// councilor rank update function on absent
    95  	voteMap := make(map[string]bool)
    96  	for _, voter := range availableVoters {
    97  		voteMap[voter.Address.String()] = true
    98  	}
    99  	councilors := k.GetAllCouncilors(ctx)
   100  	for _, councilor := range councilors {
   101  		if !voteMap[councilor.Address.String()] {
   102  			k.OnCouncilorAbsent(ctx, councilor.Address)
   103  		}
   104  	}
   105  
   106  	// update to spending pool users if it's spending pool proposal
   107  	content := proposal.GetContent()
   108  	if content.VotePermission() == types.PermZero {
   109  		router := k.GetProposalRouter()
   110  		totalVoters = len(router.AllowedAddressesDynamicProposal(ctx, content))
   111  		if totalVoters == 0 {
   112  			totalVoters = 1
   113  		}
   114  	}
   115  	numVotes := len(votes)
   116  
   117  	properties := k.GetNetworkProperties(ctx)
   118  
   119  	quorum := properties.VoteQuorum
   120  	if content.VotePermission() == types.PermZero {
   121  		router := k.GetProposalRouter()
   122  		quorum = router.QuorumDynamicProposal(ctx, content)
   123  	}
   124  
   125  	isQuorum, err := types.IsQuorum(quorum, uint64(numVotes), uint64(totalVoters))
   126  	if err != nil {
   127  		panic(fmt.Sprintf("Invalid quorum on proposal: proposalID=%d, proposalType=%s, err=%+v", proposalID, proposal.GetContent().ProposalType(), err))
   128  	}
   129  
   130  	if isQuorum {
   131  		numActorsWithVeto := len(types.GetActorsWithVoteWithVeto(availableVoters))
   132  		calculatedVote := types.CalculateVotes(votes, uint64(numActorsWithVeto))
   133  
   134  		proposal.Result = calculatedVote.ProcessResult()
   135  		if proposal.Result == types.Passed { // This is done in order to show that proposal is in enactment, but after enactment passes it will be passed.
   136  			proposal.Result = types.Enactment
   137  		}
   138  	} else {
   139  		proposal.Result = types.QuorumNotReached
   140  	}
   141  
   142  	// enactment time should be at least 1 block from voting period finish
   143  	proposal.MinEnactmentEndBlockHeight = ctx.BlockHeight() + int64(properties.MinProposalEnactmentBlocks)
   144  	k.SaveProposal(ctx, proposal)
   145  	k.RemoveActiveProposal(ctx, proposal)
   146  	k.AddToEnactmentProposals(ctx, proposal)
   147  
   148  	ctx.EventManager().EmitEvent(
   149  		sdk.NewEvent(
   150  			types.EventTypeAddToEnactment,
   151  			sdk.NewAttribute(types.AttributeKeyProposalId, fmt.Sprintf("%d", proposal.ProposalId)),
   152  			sdk.NewAttribute(types.AttributeKeyProposalDescription, proposal.Description),
   153  		),
   154  	)
   155  }
   156  
   157  func processEnactmentProposal(ctx sdk.Context, k keeper.Keeper, proposalID uint64, slash sdk.Dec) {
   158  	router := k.GetProposalRouter()
   159  	proposal, found := k.GetProposal(ctx, proposalID)
   160  	if !found {
   161  		panic("proposal was expected to exist")
   162  	}
   163  
   164  	// skip execution if block height condition does not meet
   165  	if proposal.MinEnactmentEndBlockHeight > ctx.BlockHeight() {
   166  		return
   167  	}
   168  
   169  	if proposal.Result == types.Enactment {
   170  		err := router.ApplyProposal(ctx, proposalID, proposal.GetContent(), slash)
   171  		if err != nil {
   172  			proposal.ExecResult = "execution failed"
   173  		} else {
   174  			proposal.ExecResult = "executed successfully"
   175  		}
   176  		proposal.Result = types.Passed
   177  		k.SaveProposal(ctx, proposal)
   178  	}
   179  
   180  	k.RemoveEnactmentProposal(ctx, proposal)
   181  	ctx.EventManager().EmitEvent(
   182  		sdk.NewEvent(
   183  			types.EventTypeRemoveEnactment,
   184  			sdk.NewAttribute(types.AttributeKeyProposalId, fmt.Sprintf("%d", proposal.ProposalId)),
   185  			sdk.NewAttribute(types.AttributeKeyProposalDescription, proposal.Description),
   186  		),
   187  	)
   188  }