func (v *Validators) SetToDrop(pubkey types.Pubkey) {
	vals := v.GetValidators()
	for _, val := range vals {
		val.lock.Lock()
		if val.PubKey == pubkey {
			val.toDrop = true
		}
		val.lock.Unlock()
	}
}
SetToDrop marks given validator as inactive for dropping it in the next block
package validators
import (
	"bytes"
	"fmt"
	"sort"
	"sync"
	"sync/atomic"
	"github.com/MinterTeam/minter-go-node/coreV2/dao"
	"github.com/MinterTeam/minter-go-node/coreV2/developers"
	eventsdb "github.com/MinterTeam/minter-go-node/coreV2/events"
	"github.com/MinterTeam/minter-go-node/coreV2/state/bus"
	"github.com/MinterTeam/minter-go-node/coreV2/state/candidates"
	"github.com/MinterTeam/minter-go-node/coreV2/types"
	"github.com/MinterTeam/minter-go-node/rlp"
	"github.com/MinterTeam/minter-go-node/upgrades"
	"github.com/cosmos/iavl"
	"math/big"
)
const (
	mainPrefix        = byte('v')
	totalStakePrefix  = byte('s')
	accumRewardPrefix = byte('r')
)
const (
	ValidatorMaxAbsentWindow = 24
	validatorMaxAbsentTimes  = 12
)
type Validators struct {
	list    []*Validator
	removed map[types.Pubkey]struct{}
	loaded  bool
	db   atomic.Value
	bus  *bus.Bus
	lock sync.RWMutex
}
type RValidators interface {
	GetValidators() []*Validator
	Export(state *types.AppState)
	GetByPublicKey(pubKey types.Pubkey) *Validator
	LoadValidators()
	GetByTmAddress(address types.TmAddress) *Validator
}
func NewValidators(bus *bus.Bus, db *iavl.ImmutableTree) *Validators {
	immutableTree := atomic.Value{}
	loaded := false
	if db != nil {
		immutableTree.Store(db)
	} else {
		loaded = true
	}
	validators := &Validators{db: immutableTree, bus: bus, loaded: loaded}
	return validators
}
func (v *Validators) immutableTree() *iavl.ImmutableTree {
	db := v.db.Load()
	if db == nil {
		return nil
	}
	return db.(*iavl.ImmutableTree)
}
func (v *Validators) SetImmutableTree(immutableTree *iavl.ImmutableTree) {
	if v.immutableTree() == nil && v.loaded {
		v.loaded = false
	}
	v.db.Store(immutableTree)
}
func (v *Validators) Commit(db *iavl.MutableTree, version int64) error {
	if v.hasDirtyValidators() {  
		v.lock.RLock()
		data, err := rlp.EncodeToBytes(v.list)
		v.lock.RUnlock()
		if err != nil {
			return fmt.Errorf("can't encode validators: %v", err)
		}
		path := []byte{mainPrefix}
		db.Set(path, data)
	}
	v.lock.Lock()
	defer v.lock.Unlock()
	for _, val := range v.list {
		if v.IsDirtyOrDirtyTotalStake(val) {
			path := []byte{mainPrefix}
			val.lock.Lock()
			path = append(path, val.PubKey.Bytes()...)
			val.lock.Unlock()
			path = append(path, totalStakePrefix)
			db.Set(path, val.GetTotalBipStake().Bytes())
		}
		if v.IsDirtyOrDirtyAccumReward(val) {
			path := []byte{mainPrefix}
			val.lock.Lock()
			path = append(path, val.PubKey.Bytes()...)
			val.lock.Unlock()
			path = append(path, accumRewardPrefix)
			db.Set(path, val.GetAccumReward().Bytes())
		}
	}
	for _, pubkey := range v.getOrderedRemoved() {
		path := append([]byte{mainPrefix}, pubkey.Bytes()...)
		db.Remove(append(path, totalStakePrefix))
		db.Remove(append(path, accumRewardPrefix))
	}
	v.removed = map[types.Pubkey]struct{}{}
	v.uncheckDirtyValidators()
	return nil
}
func (v *Validators) getOrderedRemoved() []types.Pubkey {
	keys := make([]types.Pubkey, 0, len(v.removed))
	for k := range v.removed {
		keys = append(keys, k)
	}
	sort.SliceStable(keys, func(i, j int) bool {
		return bytes.Compare(keys[i].Bytes(), keys[j].Bytes()) == 1
	})
	return keys
}
func (v *Validators) IsDirtyOrDirtyAccumReward(val *Validator) bool {
	val.lock.RLock()
	defer val.lock.RUnlock()
	return val.isDirty || val.isAccumRewardDirty
}
func (v *Validators) IsDirtyOrDirtyTotalStake(val *Validator) bool {
	val.lock.RLock()
	defer val.lock.RUnlock()
	return val.isDirty || val.isTotalStakeDirty
}
func (v *Validators) SetValidatorPresent(height uint64, address types.TmAddress) {
	validator := v.GetByTmAddress(address)
	if validator == nil {
		return
	}
	validator.SetPresent(height)
}
func (v *Validators) SetValidatorAbsent(height uint64, address types.TmAddress, grace *upgrades.Grace) {
	validator := v.GetByTmAddress(address)
	if validator == nil {
		return
	}
	validator.SetAbsent(height)
	if validator.CountAbsentTimes() > validatorMaxAbsentTimes {
		if !grace.IsGraceBlock(height) {
			v.punishValidator(height, address)
		}
		v.turnValidatorOff(address)
	}
}
func (v *Validators) GetValidators() []*Validator {
	v.lock.RLock()
	defer v.lock.RUnlock()
	return v.list
}
func (v *Validators) SetNewValidators(candidates []*candidates.Candidate) {
	old := v.GetValidators()
	oldValidatorsForRemove := map[types.Pubkey]struct{}{}
	for _, oldVal := range old {
		oldVal.lock.RLock()
		oldValidatorsForRemove[oldVal.PubKey] = struct{}{}
		oldVal.lock.RUnlock()
	}
	var newVals []*Validator
	for _, candidate := range candidates {
		accumReward := big.NewInt(0)
		absentTimes := types.NewBitArray(ValidatorMaxAbsentWindow)
		for _, oldVal := range old {
			if oldVal.GetAddress() == candidate.GetTmAddress() {
				oldVal.lock.RLock()
				accumReward = oldVal.accumReward
				absentTimes = oldVal.AbsentTimes
				delete(oldValidatorsForRemove, oldVal.PubKey)
				oldVal.lock.RUnlock()
			}
		}
		newVals = append(newVals, &Validator{
			PubKey:             candidate.PubKey,
			AbsentTimes:        absentTimes,
			totalStake:         candidate.GetTotalBipStake(),
			accumReward:        accumReward,
			isDirty:            true,
			isTotalStakeDirty:  true,
			isAccumRewardDirty: true,
			tmAddress:          candidate.GetTmAddress(),
			bus:                v.bus,
		})
	}
	v.lock.Lock()
	v.removed = oldValidatorsForRemove
	v.lock.Unlock()
	v.SetValidators(newVals)
}
func (v *Validators) PunishByzantineValidator(tmAddress [20]byte) {
	validator := v.GetByTmAddress(tmAddress)
	if validator != nil {
		validator.SetTotalBipStake(big.NewInt(0))
		validator.lock.Lock()
		validator.toDrop = true
		validator.isDirty = true
		validator.lock.Unlock()
	}
}
func (v *Validators) Create(pubkey types.Pubkey, stake *big.Int) {
	val := &Validator{
		PubKey:             pubkey,
		AbsentTimes:        types.NewBitArray(ValidatorMaxAbsentWindow),
		totalStake:         big.NewInt(0).Set(stake),
		accumReward:        big.NewInt(0),
		isDirty:            true,
		isTotalStakeDirty:  true,
		isAccumRewardDirty: true,
	}
	val.setTmAddress()
	v.lock.RLock()
	defer v.lock.RUnlock()
	v.list = append(v.list, val)
}
func (v *Validators) PayRewards() {
	vals := v.GetValidators()
	for _, validator := range vals {
		if validator.GetAccumReward().Sign() == 1 {
			candidate := v.bus.Candidates().GetCandidate(validator.PubKey)
			totalReward := big.NewInt(0).Set(validator.GetAccumReward())
			remainder := big.NewInt(0).Set(validator.GetAccumReward())
			DAOReward := big.NewInt(0).Set(totalReward)
			DAOReward.Mul(DAOReward, big.NewInt(int64(dao.Commission)))
			DAOReward.Div(DAOReward, big.NewInt(100))
			v.bus.Accounts().AddBalance(dao.Address, types.GetBaseCoinID(), DAOReward)
			remainder.Sub(remainder, DAOReward)
			v.bus.Events().AddEvent(&eventsdb.RewardEvent{
				Role:            eventsdb.RoleDAO.String(),
				Address:         dao.Address,
				Amount:          DAOReward.String(),
				ValidatorPubKey: validator.PubKey,
			})
			DevelopersReward := big.NewInt(0).Set(totalReward)
			DevelopersReward.Mul(DevelopersReward, big.NewInt(int64(developers.Commission)))
			DevelopersReward.Div(DevelopersReward, big.NewInt(100))
			v.bus.Accounts().AddBalance(developers.Address, types.GetBaseCoinID(), DevelopersReward)
			remainder.Sub(remainder, DevelopersReward)
			v.bus.Events().AddEvent(&eventsdb.RewardEvent{
				Role:            eventsdb.RoleDevelopers.String(),
				Address:         developers.Address,
				Amount:          DevelopersReward.String(),
				ValidatorPubKey: validator.PubKey,
			})
			totalReward.Sub(totalReward, DevelopersReward)
			totalReward.Sub(totalReward, DAOReward)
			validatorReward := big.NewInt(0).Set(totalReward)
			validatorReward.Mul(validatorReward, big.NewInt(int64(candidate.Commission)))
			validatorReward.Div(validatorReward, big.NewInt(100))
			totalReward.Sub(totalReward, validatorReward)
			v.bus.Accounts().AddBalance(candidate.RewardAddress, types.GetBaseCoinID(), validatorReward)
			remainder.Sub(remainder, validatorReward)
			v.bus.Events().AddEvent(&eventsdb.RewardEvent{
				Role:            eventsdb.RoleValidator.String(),
				Address:         candidate.RewardAddress,
				Amount:          validatorReward.String(),
				ValidatorPubKey: validator.PubKey,
			})
			stakes := v.bus.Candidates().GetStakes(validator.PubKey)
			for _, stake := range stakes {
				if stake.BipValue.Sign() == 0 {
					continue
				}
				reward := big.NewInt(0).Set(totalReward)
				reward.Mul(reward, stake.BipValue)
				reward.Div(reward, validator.GetTotalBipStake())
				if reward.Sign() < 1 {
					continue
				}
				v.bus.Accounts().AddBalance(stake.Owner, types.GetBaseCoinID(), reward)
				remainder.Sub(remainder, reward)
				v.bus.Events().AddEvent(&eventsdb.RewardEvent{
					Role:            eventsdb.RoleDelegator.String(),
					Address:         stake.Owner,
					Amount:          reward.String(),
					ValidatorPubKey: validator.PubKey,
				})
			}
			validator.SetAccumReward(big.NewInt(0))
			if remainder.Sign() != -1 {
				v.bus.App().AddTotalSlashed(remainder)
			} else {
				panic(fmt.Sprintf("Negative remainder: %s", remainder.String()))
			}
		}
	}
}
func (v *Validators) GetByTmAddress(address types.TmAddress) *Validator {
	v.lock.RLock()
	defer v.lock.RUnlock()
	for _, val := range v.list {
		val.lock.RLock()
		if val.tmAddress == address {
			val.lock.RUnlock()
			return val
		}
		val.lock.RUnlock()
	}
	return nil
}
func (v *Validators) GetByPublicKey(pubKey types.Pubkey) *Validator {
	v.lock.RLock()
	defer v.lock.RUnlock()
	for _, val := range v.list {
		val.lock.RLock()
		if val.PubKey == pubKey {
			val.lock.RUnlock()
			return val
		}
		val.lock.RUnlock()
	}
	return nil
}
func (v *Validators) LoadValidators() {
	v.lock.Lock()
	defer v.lock.Unlock()
	if v.loaded {
		return
	}
	v.loaded = true
	path := []byte{mainPrefix}
	_, enc := v.immutableTree().Get(path)
	if len(enc) == 0 {
		v.list = nil
		return
	}
	var validators []*Validator
	if err := rlp.DecodeBytes(enc, &validators); err != nil {
		panic(fmt.Sprintf("failed to decode validators: %s", err))
	}
	v.list = validators
	for _, validator := range validators {
		path = append([]byte{mainPrefix}, validator.PubKey.Bytes()...)
		path = append(path, totalStakePrefix)
		_, enc = v.immutableTree().Get(path)
		if len(enc) == 0 {
			validator.totalStake = big.NewInt(0)
		} else {
			validator.totalStake = big.NewInt(0).SetBytes(enc)
		}
		path = append([]byte{mainPrefix}, validator.PubKey.Bytes()...)
		path = append(path, accumRewardPrefix)
		_, enc = v.immutableTree().Get(path)
		if len(enc) == 0 {
			validator.accumReward = big.NewInt(0)
		} else {
			validator.accumReward = big.NewInt(0).SetBytes(enc)
		}
		validator.setTmAddress()
		validator.bus = v.bus
	}
}
func (v *Validators) hasDirtyValidators() bool {
	v.lock.RLock()
	defer v.lock.RUnlock()
	for _, val := range v.list {
		if val.isDirty {
			return true
		}
	}
	return false
}
func (v *Validators) uncheckDirtyValidators() {
	for _, val := range v.list {
		val.lock.Lock()
		val.isDirty = false
		val.isAccumRewardDirty = false
		val.isTotalStakeDirty = false
		val.lock.Unlock()
	}
}
func (v *Validators) punishValidator(height uint64, tmAddress types.TmAddress) {
	v.bus.Candidates().Punish(height, tmAddress)
}
func (v *Validators) SetValidators(vals []*Validator) {
	v.lock.Lock()
	v.list = vals
	v.lock.Unlock()
}
func (v *Validators) Export(state *types.AppState) {
	v.LoadValidators()
	for _, val := range v.GetValidators() {
		state.Validators = append(state.Validators, types.Validator{
			TotalBipStake: val.GetTotalBipStake().String(),
			PubKey:        val.PubKey,
			AccumReward:   val.GetAccumReward().String(),
			AbsentTimes:   val.AbsentTimes,
		})
	}
}