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 }