func (vm ValidatorManager) fireIncompetentValidator(ctx sdk.Context,
	byzantineValidators []abci.Evidence) sdk.Error {
	param := vm.paramHolder.GetValidatorParam(ctx)
	committingValidators := vm.GetCommittingValidators(ctx)
	for _, validatorName := range committingValidators {
		validator, err := vm.storage.GetValidator(ctx, validatorName)
		if err != nil {
			return err
		}
		for _, evidence := range byzantineValidators {
			if reflect.DeepEqual(validator.ABCIValidator.Address, evidence.Validator.Address) {
				if err := vm.PunishCommittingValidator(ctx, validator.Username, param.PenaltyByzantine,
					linotypes.PunishByzantine); err != nil {
					return err
				}
				break
			}
		}
		if validator.AbsentCommit > param.AbsentCommitLimitation {
			if err := vm.PunishCommittingValidator(ctx, validator.Username, param.PenaltyMissCommit,
				linotypes.PunishAbsentCommit); err != nil {
				return err
			}
		}
	}
	return nil
}
FireIncompetentValidator - fire oncall validator if
1. absent commit > absent limitation.
2. byzantine
package manager
import (
	"fmt"
	"math"
	"reflect"
	codec "github.com/cosmos/cosmos-sdk/codec"
	sdk "github.com/cosmos/cosmos-sdk/types"
	abci "github.com/tendermint/tendermint/abci/types"
	crypto "github.com/tendermint/tendermint/crypto"
	tmtypes "github.com/tendermint/tendermint/types"
	"github.com/lino-network/lino/param"
	linotypes "github.com/lino-network/lino/types"
	"github.com/lino-network/lino/utils"
	acc "github.com/lino-network/lino/x/account"
	"github.com/lino-network/lino/x/global"
	"github.com/lino-network/lino/x/validator/model"
	"github.com/lino-network/lino/x/validator/types"
	"github.com/lino-network/lino/x/vote"
	votetypes "github.com/lino-network/lino/x/vote/types"
)
const (
	exportVersion = 1
	importVersion = 1
)
type ValidatorManager struct {
	storage model.ValidatorStorage
	paramHolder param.ParamKeeper
	vote        vote.VoteKeeper
	global      global.GlobalKeeper
	acc         acc.AccountKeeper
}
func NewValidatorManager(key sdk.StoreKey, holder param.ParamKeeper, vote vote.VoteKeeper,
	global global.GlobalKeeper, acc acc.AccountKeeper) ValidatorManager {
	return ValidatorManager{
		storage:     model.NewValidatorStorage(key),
		paramHolder: holder,
		vote:        vote,
		global:      global,
		acc:         acc,
	}
}
func (vm ValidatorManager) InitGenesis(ctx sdk.Context) {
	lst := &model.ValidatorList{
		LowestOncallVotes:  linotypes.NewCoinFromInt64(0),
		LowestStandbyVotes: linotypes.NewCoinFromInt64(0),
	}
	vm.storage.SetValidatorList(ctx, lst)
}
func (vm ValidatorManager) OnBeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) {
	validatorList := vm.GetValidatorList(ctx)
	vals := vm.GetCommittingValidators(ctx)
	validatorList.PreBlockValidators = vals
	vm.SetValidatorList(ctx, validatorList)
	updateErr := vm.updateSigningStats(ctx, req.LastCommitInfo.Votes)
	if updateErr != nil {
		panic(updateErr)
	}
	if err := vm.fireIncompetentValidator(ctx, req.ByzantineValidators); err != nil {
		panic(err)
	}
}
func (vm ValidatorManager) UpdateValidator(ctx sdk.Context, username linotypes.AccountKey, link string) sdk.Error {
	val, err := vm.storage.GetValidator(ctx, username)
	if err != nil {
		return err
	}
	val.Link = link
	vm.storage.SetValidator(ctx, username, val)
	return nil
}
func (vm ValidatorManager) RegisterValidator(ctx sdk.Context, username linotypes.AccountKey, valPubKey crypto.PubKey, link string) sdk.Error {
	lst := vm.storage.GetValidatorList(ctx)
	if linotypes.FindAccountInList(username, lst.Jail) != -1 {
		return vm.rejoinFromJail(ctx, username)
	}
	if vm.IsLegalValidator(ctx, username) {
		return types.ErrValidatorAlreadyExist()
	}
	if duty, err := vm.vote.GetVoterDuty(ctx, username); err != nil || duty != votetypes.DutyVoter {
		return types.ErrInvalidVoterDuty()
	}
	param := vm.paramHolder.GetValidatorParam(ctx)
	if err := vm.vote.AssignDuty(ctx, username, votetypes.DutyValidator, param.ValidatorMinDeposit); err != nil {
		return err
	}
	if err := vm.checkDupPubKey(ctx, valPubKey); err != nil {
		return err
	}
	prevVotes := vm.getPrevVotes(ctx, username)
	validator := &model.Validator{
		ABCIValidator: abci.Validator{
			Address: valPubKey.Address(),
			Power:   0,
		},
		ReceivedVotes: prevVotes,
		PubKey:        valPubKey,
		Username:      username,
		Link:          link,
	}
	vm.storage.SetValidator(ctx, username, validator)
	if err := vm.addValidatortToCandidateList(ctx, username); err != nil {
		return err
	}
	if err := vm.onCandidateVotesInc(ctx, username); err != nil {
		return err
	}
	if err := vm.VoteValidator(ctx, username, []linotypes.AccountKey{username}); err != nil {
		return err
	}
	return nil
}
func (vm ValidatorManager) RevokeValidator(ctx sdk.Context, username linotypes.AccountKey) sdk.Error {
	if !vm.IsLegalValidator(ctx, username) {
		return types.ErrInvalidValidator()
	}
	me, err := vm.storage.GetValidator(ctx, username)
	if err != nil {
		return err
	}
	me.HasRevoked = true
	vm.storage.SetValidator(ctx, username, me)
	if err := vm.removeValidatorFromAllLists(ctx, username); err != nil {
		return err
	}
	if err := vm.balanceValidatorList(ctx); err != nil {
		return err
	}
	param := vm.paramHolder.GetValidatorParam(ctx)
	if err = vm.vote.UnassignDuty(ctx, username, param.ValidatorRevokePendingSec); err != nil {
		return err
	}
	return nil
}
func (vm ValidatorManager) VoteValidator(ctx sdk.Context, username linotypes.AccountKey,
	votedValidators []linotypes.AccountKey) sdk.Error {
	param := vm.paramHolder.GetValidatorParam(ctx)
	if int64(len(votedValidators)) > param.MaxVotedValidators {
		return types.ErrInvalidVotedValidators()
	}
	for _, valName := range votedValidators {
		if !vm.IsLegalValidator(ctx, valName) {
			return types.ErrValidatorNotFound(valName)
		}
	}
	updates, err := vm.getElectionVoteListUpdates(ctx, username, votedValidators)
	if err != nil {
		return err
	}
	if err := vm.updateValidatorReceivedVotes(ctx, updates); err != nil {
		return err
	}
	if err := vm.setNewElectionVoteList(ctx, username, votedValidators); err != nil {
		return err
	}
	if err := vm.vote.ClaimInterest(ctx, username); err != nil {
		return err
	}
	return nil
}
func (vm ValidatorManager) DistributeInflationToValidator(ctx sdk.Context) sdk.Error {
	coin, err := vm.acc.GetPool(ctx, linotypes.InflationValidatorPool)
	if err != nil {
		return err
	}
	param := vm.paramHolder.GetValidatorParam(ctx)
	lst := vm.storage.GetValidatorList(ctx)
	totalWeight := int64(len(lst.Oncall))*param.OncallInflationWeight +
		int64(len(lst.Standby))*param.StandbyInflationWeight
	index := int64(0)
	for _, oncall := range lst.Oncall {
		ratPerOncall := coin.ToDec().Mul(sdk.NewDec(param.OncallInflationWeight)).Quo(sdk.NewDec(totalWeight - index))
		err := vm.acc.MoveFromPool(ctx, linotypes.InflationValidatorPool,
			linotypes.NewAccOrAddrFromAcc(oncall),
			linotypes.DecToCoin(ratPerOncall))
		if err != nil {
			return err
		}
		coin = coin.Minus(linotypes.DecToCoin(ratPerOncall))
		index += param.OncallInflationWeight
	}
	for _, standby := range lst.Standby {
		ratPerStandby := coin.ToDec().Mul(sdk.NewDec(param.StandbyInflationWeight)).Quo(sdk.NewDec(totalWeight - index))
		err := vm.acc.MoveFromPool(ctx, linotypes.InflationValidatorPool,
			linotypes.NewAccOrAddrFromAcc(standby),
			linotypes.DecToCoin(ratPerStandby))
		if err != nil {
			return err
		}
		coin = coin.Minus(linotypes.DecToCoin(ratPerStandby))
		index += param.StandbyInflationWeight
	}
	return nil
}
func (vm ValidatorManager) rejoinFromJail(ctx sdk.Context, username linotypes.AccountKey) sdk.Error {
	param := vm.paramHolder.GetValidatorParam(ctx)
	totalStake, err := vm.vote.GetLinoStake(ctx, username)
	if err != nil {
		return err
	}
	if !totalStake.IsGTE(param.ValidatorMinDeposit) {
		return types.ErrInsufficientDeposit()
	}
	vm.removeValidatorFromJailList(ctx, username)
	if err := vm.addValidatortToCandidateList(ctx, username); err != nil {
		return err
	}
	if err := vm.onCandidateVotesInc(ctx, username); err != nil {
		return err
	}
	return nil
}
func (vm ValidatorManager) getElectionVoteListUpdates(ctx sdk.Context, username linotypes.AccountKey,
	votedValidators []linotypes.AccountKey) ([]*model.ElectionVote, sdk.Error) {
	res := []*model.ElectionVote{}
	prevList := vm.storage.GetElectionVoteList(ctx, username)
	totalStake, err := vm.vote.GetLinoStake(ctx, username)
	if err != nil {
		return nil, err
	}
	if len(prevList.ElectionVotes) == 0 && len(votedValidators) == 0 {
		return nil, nil
	}
	if len(votedValidators) == 0 {
		return nil, types.ErrInvalidVotedValidators()
	}
	voteStake := linotypes.DecToCoin(
		totalStake.ToDec().Quo(sdk.NewDec(int64(len(votedValidators)))))
	for _, oldVote := range prevList.ElectionVotes {
		changeDec := oldVote.Vote.Neg()
		res = append(res, &model.ElectionVote{
			ValidatorName: oldVote.ValidatorName,
			Vote:          changeDec,
		})
	}
	findInPrev := func(valName linotypes.AccountKey) *model.ElectionVote {
		for _, oldVote := range res {
			if oldVote.ValidatorName == valName {
				return oldVote
			}
		}
		return nil
	}
	for _, validatorName := range votedValidators {
		if prev := findInPrev(validatorName); prev != nil {
			prev.Vote = prev.Vote.Plus(voteStake)
		} else {
			res = append(res, &model.ElectionVote{
				ValidatorName: validatorName,
				Vote:          voteStake,
			})
		}
	}
	return res, nil
}
func (vm ValidatorManager) updateValidatorReceivedVotes(ctx sdk.Context, updates []*model.ElectionVote) sdk.Error {
	for _, update := range updates {
		lst := vm.storage.GetValidatorList(ctx)
		if update.Vote.IsZero() {
			continue
		}
		validator, err := vm.storage.GetValidator(ctx, update.ValidatorName)
		if err != nil {
			return err
		}
		validator.ReceivedVotes = validator.ReceivedVotes.Plus(update.Vote)
		vm.storage.SetValidator(ctx, update.ValidatorName, validator)
		if update.Vote.IsPositive() {
			if linotypes.FindAccountInList(update.ValidatorName, lst.Oncall) != -1 {
				if err := vm.onOncallVotesInc(ctx, update.ValidatorName); err != nil {
					return err
				}
			}
			if linotypes.FindAccountInList(update.ValidatorName, lst.Standby) != -1 {
				if err := vm.onStandbyVotesInc(ctx, update.ValidatorName); err != nil {
					return err
				}
			}
			if linotypes.FindAccountInList(update.ValidatorName, lst.Candidates) != -1 {
				if err := vm.onCandidateVotesInc(ctx, update.ValidatorName); err != nil {
					return err
				}
			}
		} else {
			if linotypes.FindAccountInList(update.ValidatorName, lst.Oncall) != -1 {
				if err := vm.onOncallVotesDec(ctx, update.ValidatorName); err != nil {
					return err
				}
			}
			if linotypes.FindAccountInList(update.ValidatorName, lst.Standby) != -1 {
				if err := vm.onStandbyVotesDec(ctx, update.ValidatorName); err != nil {
					return err
				}
			}
		}
	}
	return nil
}
func (vm ValidatorManager) setNewElectionVoteList(ctx sdk.Context, username linotypes.AccountKey,
	votedValidators []linotypes.AccountKey) sdk.Error {
	if len(votedValidators) == 0 {
		return nil
	}
	lst := &model.ElectionVoteList{}
	totalStake, err := vm.vote.GetLinoStake(ctx, username)
	if err != nil {
		return err
	}
	voteStakeDec := totalStake.ToDec().Quo(sdk.NewDec(int64(len(votedValidators))))
	for _, validatorName := range votedValidators {
		electionVote := model.ElectionVote{
			ValidatorName: validatorName,
			Vote:          linotypes.DecToCoin(voteStakeDec),
		}
		lst.ElectionVotes = append(lst.ElectionVotes, electionVote)
	}
	vm.storage.SetElectionVoteList(ctx, username, lst)
	return nil
}
func (vm ValidatorManager) IsLegalValidator(ctx sdk.Context, accKey linotypes.AccountKey) bool {
	val, err := vm.storage.GetValidator(ctx, accKey)
	if err != nil {
		return false
	}
	return !val.HasRevoked
}
func (vm ValidatorManager) GetInitValidators(ctx sdk.Context) ([]abci.ValidatorUpdate, sdk.Error) {
	committingValidators := vm.GetCommittingValidators(ctx)
	updates := []abci.ValidatorUpdate{}
	for _, curValidator := range committingValidators {
		validator, err := vm.storage.GetValidator(ctx, curValidator)
		if err != nil {
			return nil, err
		}
		updates = append(updates, abci.ValidatorUpdate{
			PubKey: tmtypes.TM2PB.PubKey(validator.PubKey),
			Power:  validator.ABCIValidator.Power,
		})
	}
	return updates, nil
}
func (vm ValidatorManager) GetValidatorUpdates(ctx sdk.Context) ([]abci.ValidatorUpdate, sdk.Error) {
	validatorList := vm.storage.GetValidatorList(ctx)
	updates := []abci.ValidatorUpdate{}
	committingValidators := vm.GetCommittingValidators(ctx)
	committingSet := linotypes.AccountListToSet(committingValidators)
	for _, preValidator := range validatorList.PreBlockValidators {
		if committingSet[preValidator] == false {
			validator, err := vm.storage.GetValidator(ctx, preValidator)
			if err != nil {
				return nil, err
			}
			updates = append(updates, abci.ValidatorUpdate{
				PubKey: tmtypes.TM2PB.PubKey(validator.PubKey),
				Power:  0,
			})
		}
	}
	for _, curValidator := range committingValidators {
		validator, err := vm.storage.GetValidator(ctx, curValidator)
		if err != nil {
			return nil, err
		}
		updates = append(updates, abci.ValidatorUpdate{
			PubKey: tmtypes.TM2PB.PubKey(validator.PubKey),
			Power:  validator.ABCIValidator.Power,
		})
	}
	return updates, nil
}
func (vm ValidatorManager) updateSigningStats(ctx sdk.Context, voteInfos []abci.VoteInfo) sdk.Error {
	addressSigned := make(map[string]bool)
	for _, voteInfo := range voteInfos {
		addressSigned[string(voteInfo.Validator.Address)] = voteInfo.SignedLastBlock
	}
	committingValidators := vm.GetCommittingValidators(ctx)
	for _, curValidator := range committingValidators {
		validator, err := vm.storage.GetValidator(ctx, curValidator)
		if err != nil {
			return err
		}
		signed, exist := addressSigned[string(validator.ABCIValidator.Address)]
		if !exist || !signed {
			validator.AbsentCommit++
		} else {
			validator.ProducedBlocks++
			if validator.AbsentCommit > 0 {
				validator.AbsentCommit--
			}
		}
		vm.storage.SetValidator(ctx, curValidator, validator)
	}
	return nil
}
func (vm ValidatorManager) PunishCommittingValidator(ctx sdk.Context, username linotypes.AccountKey,
	penalty linotypes.Coin, punishType linotypes.PunishType) sdk.Error {
	_, err := vm.vote.SlashStake(ctx, username, penalty, linotypes.InflationValidatorPool)
	if err != nil {
		return err
	}
	validator, err := vm.storage.GetValidator(ctx, username)
	if err != nil {
		return err
	}
	validator.NumSlash++
	if punishType == linotypes.PunishAbsentCommit {
		validator.AbsentCommit = 0
	}
	vm.storage.SetValidator(ctx, username, validator)
	totalStake, err := vm.vote.GetLinoStake(ctx, username)
	if err != nil {
		return err
	}
	param := vm.paramHolder.GetValidatorParam(ctx)
	if punishType == linotypes.PunishByzantine ||
		!totalStake.IsGTE(param.ValidatorMinDeposit) ||
		validator.NumSlash > param.SlashLimitation {
		if err := vm.removeValidatorFromAllLists(ctx, username); err != nil {
			return err
		}
		if err := vm.addValidatortToJailList(ctx, username); err != nil {
			return err
		}
		if err := vm.balanceValidatorList(ctx); err != nil {
			return err
		}
	}
	return nil
}