func (sm *ShardStateManager) HasAny() map[chainhash.Hash]bool {
	sm.stateLock.RLock()
	defer sm.stateLock.RUnlock()
	hasBlock := make(map[chainhash.Hash]bool)
	for k := range sm.stateMap {
		hasBlock[k] = true
	}
	return hasBlock
}
HasAny checks what blocks are in the state map.
package state
import (
	"fmt"
	"sync"
	"github.com/phoreproject/synapse/bls"
	"github.com/phoreproject/synapse/chainhash"
	"github.com/phoreproject/synapse/csmt"
	"github.com/phoreproject/synapse/primitives"
	"github.com/prysmaticlabs/go-ssz"
	"github.com/sirupsen/logrus"
)
type stateInfo struct {
	db     csmt.TreeDatabase
	state  State
	parent *stateInfo
	slot   uint64
	hash chainhash.Hash
}
type ShardStateManager struct {
	stateLock *sync.RWMutex
	finalizedDB *stateInfo
	stateMap    map[chainhash.Hash]*stateInfo
	tipDB       *stateInfo
	shardInfo ShardInfo
}
func NewShardStateManager(stateDB csmt.TreeDatabase, stateSlot uint64, tipBlockHash chainhash.Hash, shardInfo ShardInfo) *ShardStateManager {
	stateHash, err := stateDB.Hash()
	if err != nil {
		panic(err)
	}
	tip := &stateInfo{
		db: stateDB,
		state: State{
			SmartContractRoot:   *stateHash,
			LastCrosslinkHash:   chainhash.Hash{},
			ProposerAssignments: []bls.PublicKey{},
		},
		hash: tipBlockHash,
		slot: stateSlot,
	}
	return &ShardStateManager{
		finalizedDB: tip,
		tipDB:       tip,
		shardInfo:   shardInfo,
		stateMap: map[chainhash.Hash]*stateInfo{
			tipBlockHash: tip,
		},
		stateLock: new(sync.RWMutex),
	}
}
func (sm *ShardStateManager) GetTip() (State, csmt.TreeDatabase) {
	sm.stateLock.Lock()
	defer sm.stateLock.Unlock()
	return sm.tipDB.state, sm.tipDB.db
}
func (sm *ShardStateManager) GetTipSlot() uint64 {
	sm.stateLock.Lock()
	defer sm.stateLock.Unlock()
	return sm.tipDB.slot
}
func (sm *ShardStateManager) has(c chainhash.Hash) bool {
	_, found := sm.stateMap[c]
	return found
}
func (sm *ShardStateManager) Has(c chainhash.Hash) bool {
	sm.stateLock.RLock()
	defer sm.stateLock.RUnlock()
	return sm.has(c)
}
func executeBlockTransactions(a csmt.TreeTransactionAccess, b primitives.ShardBlock, si ShardInfo) error {
	for _, tx := range b.Body.Transactions {
		_, err := Transition(a, tx.TransactionData, si)
		if err != nil {
			return err
		}
	}
	return nil
}
func (sm *ShardStateManager) Add(block *primitives.ShardBlock) (*chainhash.Hash, error) {
	sm.stateLock.Lock()
	defer sm.stateLock.Unlock()
	blockHash, _ := ssz.HashTreeRoot(block)
	if sm.has(blockHash) {
		return nil, nil
	}
	logrus.WithField("block hash", chainhash.Hash(blockHash)).WithField("shard", sm.shardInfo.ShardID).Debug("add block action")
	previousState, found := sm.stateMap[block.Header.PreviousBlockHash]
	if !found {
		return nil, fmt.Errorf("could not find parent block with hash: %s", block.Header.PreviousBlockHash)
	}
	newCache, err := csmt.NewTreeMemoryCache(previousState.db)
	if err != nil {
		return nil, err
	}
	newCacheTree := csmt.NewTree(newCache)
	err = newCacheTree.Update(func(access csmt.TreeTransactionAccess) error {
		return executeBlockTransactions(access, *block, sm.shardInfo)
	})
	if err != nil {
		return nil, err
	}
	sm.stateMap[blockHash] = &stateInfo{
		db:     newCache,
		parent: previousState,
		slot:   block.Header.Slot,
		hash: blockHash,
	}
	return newCache.Hash()
}
func (sm *ShardStateManager) Finalize(finalizedHash chainhash.Hash, finalizedSlot uint64) error {
	sm.stateLock.Lock()
	defer sm.stateLock.Unlock()
	if sm.finalizedDB.hash.IsEqual(&finalizedHash) {
		return nil
	}
	logrus.WithField("block hash", finalizedHash).WithField("shard", sm.shardInfo.ShardID).Debug("finalize block action")
	finalizeNode, found := sm.stateMap[finalizedHash]
	if !found {
		return fmt.Errorf("could not find block to finalize %s on slot %d %d", finalizedHash, finalizedSlot, sm.shardInfo.ShardID)
	}
	flushingNode := finalizeNode
	memCache, isCache := flushingNode.db.(*csmt.TreeMemoryCache)
	for isCache {
		if err := memCache.Flush(); err != nil {
			return err
		}
		prevBlock := flushingNode.parent
		memCache, isCache = prevBlock.db.(*csmt.TreeMemoryCache)
		flushingNode = prevBlock
	}
	diskCache := flushingNode.db
	finalizeNode.db = diskCache
	for k, node := range sm.stateMap {
		if node.slot > finalizedSlot {
			continue
		}
		if k.IsEqual(&finalizedHash) {
			continue
		}
		if node.slot < finalizedSlot {
			delete(sm.stateMap, k)
		}
		if node.slot == finalizedSlot && !k.IsEqual(&finalizedHash) {
			delete(sm.stateMap, k)
		}
		if node.parent == finalizeNode {
			childCache, isCache := node.db.(*csmt.TreeMemoryCache)
			if isCache {
				if err := childCache.UpdateUnderlying(diskCache); err != nil {
					return err
				}
			}
		}
	}
	sm.finalizedDB = sm.stateMap[finalizedHash]
	return nil
}
func (sm *ShardStateManager) SetTip(newTipHash chainhash.Hash) error {
	sm.stateLock.Lock()
	defer sm.stateLock.Unlock()
	logrus.WithField("block hash", newTipHash).WithField("shard", sm.shardInfo.ShardID).Debug("new tip block action")
	newTip, found := sm.stateMap[newTipHash]
	if !found {
		return fmt.Errorf("couldn't find state root for tip: %s", newTipHash)
	}
	sm.tipDB = newTip
	return nil
}