github.com/Finschia/finschia-sdk@v0.48.1/x/foundation/keeper/internal/tally.go (about)

     1  package internal
     2  
     3  import (
     4  	sdk "github.com/Finschia/finschia-sdk/types"
     5  	sdkerrors "github.com/Finschia/finschia-sdk/types/errors"
     6  	"github.com/Finschia/finschia-sdk/x/foundation"
     7  )
     8  
     9  // doTallyAndUpdate performs a tally, and, if the tally result is final, then:
    10  // - updates the proposal's `Status` and `FinalTallyResult` fields,
    11  // - prune all the votes.
    12  func (k Keeper) doTallyAndUpdate(ctx sdk.Context, p *foundation.Proposal) error {
    13  	tallyResult, err := k.tally(ctx, *p)
    14  	if err != nil {
    15  		return err
    16  	}
    17  
    18  	info := k.GetFoundationInfo(ctx)
    19  	policy := info.GetDecisionPolicy()
    20  	sinceSubmission := ctx.BlockTime().Sub(p.SubmitTime) // duration passed since proposal submission.
    21  	result, err := policy.Allow(tallyResult, info.TotalWeight, sinceSubmission)
    22  	if err != nil {
    23  		return err
    24  	}
    25  
    26  	// If the result was final (i.e. enough votes to pass) or if the voting
    27  	// period ended, then we consider the proposal as final.
    28  	if isFinal := result.Final || ctx.BlockTime().After(p.VotingPeriodEnd); isFinal {
    29  		k.pruneVotes(ctx, p.Id)
    30  		p.FinalTallyResult = tallyResult
    31  		if result.Allow {
    32  			p.Status = foundation.PROPOSAL_STATUS_ACCEPTED
    33  		} else {
    34  			p.Status = foundation.PROPOSAL_STATUS_REJECTED
    35  		}
    36  	}
    37  
    38  	return nil
    39  }
    40  
    41  // tally is a function that tallies a proposal by iterating through its votes,
    42  // and returns the tally result without modifying the proposal or any state.
    43  func (k Keeper) tally(ctx sdk.Context, p foundation.Proposal) (foundation.TallyResult, error) {
    44  	// If proposal has already been tallied and updated, then its status is
    45  	// accepted/rejected, in which case we just return the previously stored result.
    46  	//
    47  	// In all other cases (including withdrawn, aborted...) we do the tally
    48  	// again.
    49  	if p.Status == foundation.PROPOSAL_STATUS_ACCEPTED || p.Status == foundation.PROPOSAL_STATUS_REJECTED {
    50  		return p.FinalTallyResult, nil
    51  	}
    52  
    53  	tallyResult := foundation.DefaultTallyResult()
    54  	var errIter error
    55  	k.iterateVotes(ctx, p.Id, func(vote foundation.Vote) (stop bool) {
    56  		voter := sdk.MustAccAddressFromBech32(vote.Voter)
    57  
    58  		_, err := k.GetMember(ctx, voter)
    59  		switch {
    60  		case sdkerrors.ErrNotFound.Is(err):
    61  			// If the member left the foundation after voting, then we simply skip the
    62  			// vote.
    63  			return false
    64  		case err != nil:
    65  			// For any other errors, we stop and return the error.
    66  			errIter = err
    67  			return true
    68  		}
    69  
    70  		if err := tallyResult.Add(vote.Option); err != nil {
    71  			panic(err)
    72  		}
    73  
    74  		return false
    75  	})
    76  
    77  	if errIter != nil {
    78  		return tallyResult, errIter
    79  	}
    80  
    81  	return tallyResult, nil
    82  }