func (c *Candidates) GetCandidate(pubkey types.Pubkey) *Candidate {
	return c.getFromMap(pubkey)
}
GetCandidate returns candidate by a public key
package candidates
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"sync/atomic"
	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/types"
	"github.com/MinterTeam/minter-go-node/formula"
	"github.com/MinterTeam/minter-go-node/helpers"
	"github.com/MinterTeam/minter-go-node/rlp"
	"github.com/cosmos/iavl"
	"math/big"
	"sort"
	"sync"
)
const (
	CandidateStatusOffline = 0x01
	CandidateStatusOnline  = 0x02
	MaxDelegatorsPerCandidate = 1000
)
const (
	mainPrefix       = 'c'
	pubKeyIDPrefix   = mainPrefix + 'p'
	blockListPrefix  = mainPrefix + 'b'
	maxIDPrefix      = mainPrefix + 'i'
	stakesPrefix     = 's'
	totalStakePrefix = 't'
	updatesPrefix    = 'u'
)
var (
	minValidatorBipStake = helpers.BipToPip(big.NewInt(1000))
)
type RCandidates interface {
	ExportV1(state *types.AppState, height uint64, validators []*types.Candidate) []uint32
	Export(state *types.AppState)
	Exists(pubkey types.Pubkey) bool
	IsBlockedPubKey(pubkey types.Pubkey) bool
	PubKey(id uint32) types.Pubkey
	Count() int
	IsNewCandidateStakeSufficient(coin types.CoinID, stake *big.Int, limit int) bool
	IsDelegatorStakeSufficient(address types.Address, pubkey types.Pubkey, coin types.CoinID, amount *big.Int) bool
	GetStakeValueOfAddress(pubkey types.Pubkey, address types.Address, coin types.CoinID) *big.Int
	GetCandidateOwner(pubkey types.Pubkey) types.Address
	GetCandidateControl(pubkey types.Pubkey) types.Address
	GetTotalStake(pubkey types.Pubkey) *big.Int
	LoadCandidates()
	LoadStakesOfCandidate(pubkey types.Pubkey)
	GetCandidate(pubkey types.Pubkey) *Candidate
	LoadStakes()
	GetCandidates() []*Candidate
	GetStakes(pubkey types.Pubkey) []*stake
	IsCandidateJailed(pubkey types.Pubkey, block uint64) bool
}
type Candidates struct {
	list map[uint32]*Candidate
	isDirty   bool
	blockList map[types.Pubkey]struct{}
	pubKeyIDs map[types.Pubkey]uint32
	maxID     uint32
	db  atomic.Value
	bus *bus.Bus
	lock                sync.RWMutex
	loaded              bool
	isChangedPublicKeys bool
}
func NewCandidates(bus *bus.Bus, db *iavl.ImmutableTree) *Candidates {
	immutableTree := atomic.Value{}
	loaded := false
	if db != nil {
		immutableTree.Store(db)
	} else {
		loaded = true
	}
	candidates := &Candidates{
		db:        immutableTree,
		loaded:    loaded,
		bus:       bus,
		blockList: map[types.Pubkey]struct{}{},
		pubKeyIDs: map[types.Pubkey]uint32{},
		list:      map[uint32]*Candidate{},
	}
	candidates.bus.SetCandidates(NewBus(candidates))
	return candidates
}
func (c *Candidates) immutableTree() *iavl.ImmutableTree {
	db := c.db.Load()
	if db == nil {
		return nil
	}
	return db.(*iavl.ImmutableTree)
}
func (c *Candidates) SetImmutableTree(immutableTree *iavl.ImmutableTree) {
	if c.immutableTree() == nil && c.loaded {
		c.loaded = false
	}
	c.db.Store(immutableTree)
}
func (c *Candidates) IsChangedPublicKeys() bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.isChangedPublicKeys
}
func (c *Candidates) ResetIsChangedPublicKeys() {
	c.lock.Lock()
	defer c.lock.Unlock()
	c.isChangedPublicKeys = false
}
func (c *Candidates) IsCandidateJailed(pubkey types.Pubkey, block uint64) bool {
	candidate := c.GetCandidate(pubkey)
	return candidate.JailedUntil >= block
}
func (c *Candidates) Commit(db *iavl.MutableTree, version int64) error {
	keys := c.getOrderedCandidates()
	hasDirty := false
	for _, candidate := range keys {
		candidate.lock.RLock()
		if candidate.isDirty {
			hasDirty = true
			candidate.lock.RUnlock()
			break
		}
		candidate.lock.RUnlock()
	}
	if hasDirty {
		var candidates []Candidate
		for _, candidate := range keys {
			candidates = append(candidates, *candidate)
		}
		data, err := rlp.EncodeToBytes(candidates)
		if err != nil {
			return fmt.Errorf("can't encode candidates: %v", err)
		}
		path := []byte{mainPrefix}
		db.Set(path, data)
	}
	c.lock.Lock()
	if c.isDirty {
		c.isDirty = false
		var pubIDs []pubkeyID
		for pk, v := range c.pubKeyIDs {
			pubIDs = append(pubIDs, pubkeyID{
				PubKey: pk,
				ID:     v,
			})
		}
		sort.SliceStable(pubIDs, func(i, j int) bool {
			return pubIDs[i].ID < pubIDs[j].ID
		})
		pubIDData, err := rlp.EncodeToBytes(pubIDs)
		if err != nil {
			panic(fmt.Sprintf("failed to encode candidates public key with ID: %s", err))
		}
		db.Set([]byte{pubKeyIDPrefix}, pubIDData)
		var blockList []types.Pubkey
		for pubKey := range c.blockList {
			blockList = append(blockList, pubKey)
		}
		sort.SliceStable(blockList, func(i, j int) bool {
			return bytes.Compare(blockList[i].Bytes(), blockList[j].Bytes()) == 1
		})
		blockListData, err := rlp.EncodeToBytes(blockList)
		if err != nil {
			return fmt.Errorf("can't encode block list of candidates: %v", err)
		}
		db.Set([]byte{blockListPrefix}, blockListData)
		db.Set([]byte{maxIDPrefix}, c.maxIDBytes())
	}
	c.lock.Unlock()
	for _, candidate := range keys {
		candidate.lock.Lock()
		candidate.isDirty = false
		dirty := candidate.isTotalStakeDirty
		candidate.lock.Unlock()
		if dirty {
			candidate.lock.Lock()
			candidate.isTotalStakeDirty = false
			totalStakeBytes := candidate.totalBipStake.Bytes()
			candidate.lock.Unlock()
			path := []byte{mainPrefix}
			path = append(path, candidate.idBytes()...)
			path = append(path, totalStakePrefix)
			db.Set(path, totalStakeBytes)
		}
		for index, stake := range candidate.stakes {
			candidate.lock.RLock()
			dirtyStakes := candidate.dirtyStakes[index]
			candidate.lock.RUnlock()
			if !dirtyStakes {
				continue
			}
			candidate.lock.Lock()
			candidate.dirtyStakes[index] = false
			candidate.lock.Unlock()
			path := []byte{mainPrefix}
			path = append(path, candidate.idBytes()...)
			path = append(path, stakesPrefix)
			path = append(path, big.NewInt(int64(index)).Bytes()...)
			isEmpty := stake == nil
			if !isEmpty {
				stake.lock.RLock()
				isEmpty = stake.Value.Sign() == 0
				stake.lock.RUnlock()
			}
			if isEmpty {
				db.Remove(path)
				candidate.lock.Lock()
				candidate.stakes[index] = nil
				candidate.lock.Unlock()
				continue
			}
			data, err := rlp.EncodeToBytes(stake)
			if err != nil {
				return fmt.Errorf("can't encode stake: %v", err)
			}
			db.Set(path, data)
		}
		candidate.lock.RLock()
		updatesDirty := candidate.isUpdatesDirty
		candidate.lock.RUnlock()
		if updatesDirty {
			candidate.lock.Lock()
			candidate.isUpdatesDirty = false
			data, err := rlp.EncodeToBytes(candidate.updates)
			candidate.lock.Unlock()
			if err != nil {
				return fmt.Errorf("can't encode candidates updates: %v", err)
			}
			path := []byte{mainPrefix}
			path = append(path, candidate.idBytes()...)
			path = append(path, updatesPrefix)
			db.Set(path, data)
		}
	}
	return nil
}
func (c *Candidates) GetNewCandidates(valCount int) []*Candidate {
	var result []*Candidate
	candidates := c.GetCandidates()
	for _, candidate := range candidates {
		if candidate.GetStatus() != CandidateStatusOnline {
			continue
		}
		if candidate.GetTotalBipStake().Cmp(minValidatorBipStake) == -1 {
			continue
		}
		result = append(result, candidate)
	}
	if len(result) > valCount {
		result = result[:valCount]
	}
	return result
}
func (c *Candidates) Create(ownerAddress, rewardAddress, controlAddress types.Address, pubkey types.Pubkey, commission uint32, block uint64, jailedUntil uint64) {
	candidate := &Candidate{
		ID:                       0,
		PubKey:                   pubkey,
		RewardAddress:            rewardAddress,
		OwnerAddress:             ownerAddress,
		ControlAddress:           controlAddress,
		Commission:               commission,
		LastEditCommissionHeight: block,
		JailedUntil:              jailedUntil,
		Status:                   CandidateStatusOffline,
		totalBipStake:            big.NewInt(0),
		stakes:                   [MaxDelegatorsPerCandidate]*stake{},
		isDirty:                  true,
		isTotalStakeDirty:        true,
	}
	candidate.setTmAddress()
	c.setToMap(pubkey, candidate)
}
func (c *Candidates) CreateWithID(ownerAddress, rewardAddress, controlAddress types.Address, pubkey types.Pubkey, commission, id uint32, commissionEditHeight, jail uint64) {
	c.setPubKeyID(pubkey, id)
	c.Create(ownerAddress, rewardAddress, controlAddress, pubkey, commission, commissionEditHeight, jail)
}
func (c *Candidates) PunishByzantineCandidate(height uint64, tmAddress types.TmAddress) {
	candidate := c.GetCandidateByTendermintAddress(tmAddress)
	stakes := c.GetStakes(candidate.PubKey)
	for _, stake := range stakes {
		newValue := big.NewInt(0).Set(stake.Value)
		newValue.Mul(newValue, big.NewInt(95))
		newValue.Div(newValue, big.NewInt(100))
		slashed := big.NewInt(0).Set(stake.Value)
		slashed.Sub(slashed, newValue)
		if !stake.Coin.IsBaseCoin() {
			coin := c.bus.Coins().GetCoin(stake.Coin)
			ret := formula.CalculateSaleReturn(coin.Volume, coin.Reserve, coin.Crr, slashed)
			c.bus.Coins().SubCoinVolume(coin.ID, slashed)
			c.bus.Coins().SubCoinReserve(coin.ID, ret)
			c.bus.App().AddTotalSlashed(ret)
		} else {
			c.bus.App().AddTotalSlashed(slashed)
		}
		c.bus.Checker().AddCoin(stake.Coin, big.NewInt(0).Neg(slashed))
		c.bus.Events().AddEvent(&eventsdb.SlashEvent{
			Address:         stake.Owner,
			Amount:          slashed.String(),
			Coin:            uint64(stake.Coin),
			ValidatorPubKey: candidate.PubKey,
		})
		c.bus.Checker().AddCoin(stake.Coin, big.NewInt(0).Neg(newValue))
		c.bus.FrozenFunds().AddFrozenFund(height+types.GetUnbondPeriod(), stake.Owner, &candidate.PubKey, candidate.ID, stake.Coin, newValue)
		stake.setValue(big.NewInt(0))
	}
}
func (c *Candidates) GetCandidateByTendermintAddress(address types.TmAddress) *Candidate {
	candidates := c.GetCandidates()
	for _, candidate := range candidates {
		if candidate.GetTmAddress() == address {
			return candidate
		}
	}
	return nil
}
func (c *Candidates) RecalculateStakes(height uint64) {
	c.recalculateStakes(height)
}
func (c *Candidates) recalculateStakes(height uint64) {
	coinsCache := newCoinsCache()
	for _, candidate := range c.getOrderedCandidates() {
		stakes := &candidate.stakes
		for _, stake := range stakes {
			if stake == nil {
				continue
			}
			stake.setBipValue(c.calculateBipValue(stake.Coin, stake.Value, false, true, coinsCache))
		}
		for _, update := range candidate.updates {
			stake := c.GetStakeOfAddress(candidate.PubKey, update.Owner, update.Coin)
			if stake != nil {
				stake.addValue(update.Value)
				update.setValue(big.NewInt(0))
				stake.setBipValue(c.calculateBipValue(stake.Coin, stake.Value, false, true, coinsCache))
			}
		}
		candidate.filterUpdates()
		for _, update := range candidate.updates {
			update.setBipValue(c.calculateBipValue(update.Coin, update.Value, false, true, coinsCache))
		}
		for _, update := range candidate.updates {
			index := -1
			smallestStake := big.NewInt(0)
			for i, stake := range stakes {
				if stake == nil {
					index = i
					smallestStake = big.NewInt(0)
					break
				}
				if index == -1 || smallestStake.Cmp(stake.BipValue) == 1 {
					smallestStake = big.NewInt(0).Set(stake.BipValue)
					index = i
				}
			}
			if smallestStake.Cmp(update.BipValue) == 1 {
				c.stakeKick(update.Owner, update.Value, update.Coin, candidate.PubKey, height)
				update.setValue(big.NewInt(0))
				continue
			}
			if stakes[index] != nil {
				c.stakeKick(stakes[index].Owner, stakes[index].Value, stakes[index].Coin, candidate.PubKey, height)
			}
			candidate.setStakeAtIndex(index, update, true)
		}
		candidate.clearUpdates()
		totalBipValue := big.NewInt(0)
		for _, stake := range stakes {
			if stake == nil {
				continue
			}
			totalBipValue.Add(totalBipValue, stake.BipValue)
		}
		candidate.setTotalBipStake(totalBipValue)
	}
}
func (c *Candidates) stakeKick(owner types.Address, value *big.Int, coin types.CoinID, pubKey types.Pubkey, height uint64) {
	c.bus.WaitList().AddToWaitList(owner, pubKey, coin, value)
	c.bus.Events().AddEvent(&eventsdb.StakeKickEvent{
		Address:         owner,
		Amount:          value.String(),
		Coin:            uint64(coin),
		ValidatorPubKey: pubKey,
	})
	c.bus.Checker().AddCoin(coin, big.NewInt(0).Neg(value))
}
func (c *Candidates) Exists(pubkey types.Pubkey) bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.existPubKey(pubkey)
}
func (c *Candidates) existPubKey(pubKey types.Pubkey) bool {
	_, exists := c.pubKeyIDs[pubKey]
	return exists
}
func (c *Candidates) IsBlockedPubKey(pubkey types.Pubkey) bool {
	return c.isBlocked(pubkey)
}
func (c *Candidates) isBlocked(pubKey types.Pubkey) bool {
	c.lock.Lock()
	defer c.lock.Unlock()
	_, exists := c.blockList[pubKey]
	return exists
}
func (c *Candidates) Count() int {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return len(c.list)
}
func (c *Candidates) IsNewCandidateStakeSufficient(coin types.CoinID, stake *big.Int, limit int) bool {
	c.lock.RLock()
	defer c.lock.RUnlock()
	bipValue := c.calculateBipValue(coin, stake, true, true, nil)
	var stakes []*big.Int
	for _, candidate := range c.list {
		stakes = append(stakes, big.NewInt(0).Set(candidate.totalBipStake))
	}
	sort.SliceStable(stakes, func(i, j int) bool {
		return stakes[i].Cmp(stakes[j]) == 1
	})
	for _, stake := range stakes[:limit] {
		if stake.Cmp(bipValue) == -1 {
			return true
		}
	}
	return false
}