func (s *ShardSyncManager) ProposalAnnounced(shardProposals []uint64, startState chainhash.Hash, startSlot uint64) {
	oldSlotProposals := s.slotProposals
	newProposals := make(map[uint64]*SlotProposalManager)
	for _, slot := range shardProposals {
		root := startState
		stateSlot := startSlot
		if manager, found := oldSlotProposals[slot]; found {
			root = manager.bestProposal.TransactionPackage.StartRoot
			stateSlot = manager.bestProposal.startSlot
		}
		newProposals[slot] = NewSlotProposalManager(slot, root, stateSlot, s)
		currentSlot := slot
		proposalManager, found := oldSlotProposals[currentSlot]
		for !found && currentSlot > 0 {
			currentSlot--
			proposalManager, found = oldSlotProposals[currentSlot]
		}
		if found {
			newProposals[slot].bestProposal = proposalManager.bestProposal
		}
	}
	s.slotProposals = newProposals
}
ProposalAnnounced registers a slot to be proposed.
package chain
import (
	"bytes"
	"fmt"
	"sync"
	"time"
	"github.com/golang/protobuf/proto"
	"github.com/libp2p/go-libp2p-core/network"
	"github.com/libp2p/go-libp2p-core/peer"
	"github.com/libp2p/go-libp2p-core/protocol"
	"github.com/phoreproject/synapse/chainhash"
	"github.com/phoreproject/synapse/p2p"
	"github.com/phoreproject/synapse/pb"
	"github.com/phoreproject/synapse/primitives"
	relayerp2p "github.com/phoreproject/synapse/relayer/p2p"
	"github.com/phoreproject/synapse/shard/state"
	"github.com/phoreproject/synapse/shard/transfer"
	"github.com/prysmaticlabs/go-ssz"
	"github.com/sirupsen/logrus"
)
type shardProtocols struct {
	shard   *p2p.ProtocolHandler
	relayer *p2p.ProtocolHandler
}
type SlotProposalManager struct {
	proposalSlot uint64
	bestProposal *ProposalInformation
	lock         sync.Mutex
	mgr          *ShardSyncManager
	requested    bool
}
func NewSlotProposalManager(slot uint64, epochState chainhash.Hash, epochSlot uint64, mgr *ShardSyncManager) *SlotProposalManager {
	return &SlotProposalManager{
		proposalSlot: slot,
		bestProposal: &ProposalInformation{
			TransactionPackage: primitives.TransactionPackage{
				StartRoot:     epochState,
				EndRoot:       epochState,
				Updates:       nil,
				Verifications: nil,
				Transactions:  nil,
			},
			startSlot: epochSlot,
		},
		mgr:       mgr,
		requested: false,
	}
}
func (s *SlotProposalManager) RequestPackages(stateRoot chainhash.Hash) {
	s.lock.Lock()
	defer s.lock.Unlock()
	s.requestPackages(stateRoot)
}
func (s *SlotProposalManager) requestPackages(stateRoot chainhash.Hash) {
	if s.requested {
		return
	}
	logrus.WithField("slot", s.proposalSlot).WithField("stateRoot", stateRoot.String()).Debug("requesting for slot")
	s.requested = true
	msg := pb.GetPackagesMessage{
		TipStateRoot: stateRoot[:],
	}
	msgBytes, err := proto.Marshal(&msg)
	if err != nil {
		logrus.Warn(err)
		return
	}
	err = s.mgr.hostNode.Broadcast(fmt.Sprintf("/packageRequests/%d", s.mgr.shardID), msgBytes)
	if err != nil {
		logrus.Warn(err)
	}
}
func (s *SlotProposalManager) requestPackagesAtSlotBoundary() {
	waitUntil := uint64(s.mgr.manager.Config.SlotDuration)*s.proposalSlot + s.mgr.manager.InitializationParameters.GenesisTime
	time.Sleep(time.Until(time.Unix(int64(waitUntil), 0)))
	s.lock.Lock()
	defer s.lock.Unlock()
	startState, err := s.mgr.manager.Chain.GetNodeBySlot(s.proposalSlot - 1)
	if err != nil {
		logrus.Warn(err)
	}
	s.requestPackages(startState.StateRoot)
}
func (s *SlotProposalManager) getProposal() *ProposalInformation {
	s.lock.Lock()
	defer s.lock.Unlock()
	return s.bestProposal
}
func (s *SlotProposalManager) processIncomingProposal(p *ProposalInformation) {
	if p.startSlot >= s.proposalSlot {
		return
	}
	s.lock.Lock()
	defer s.lock.Unlock()
	if p.isBetterThan(s.bestProposal) {
		s.bestProposal = p
	}
}
type ShardSyncManager struct {
	hostNode *p2p.HostNode
	manager   *ShardManager
	protocols shardProtocols
	shardID   uint64
	slotProposals map[uint64]*SlotProposalManager
}
func (s *ShardSyncManager) AddBlock(block *primitives.ShardBlock, newTip bool) {
	slot := block.Header.Slot
	if !newTip {
		return
	}
	for proposalSlot, mgr := range s.slotProposals {
		mgr.lock.Lock()
		if slot < proposalSlot && !mgr.bestProposal.TransactionPackage.StartRoot.IsEqual(&block.Header.StateRoot) {
			mgr.bestProposal = &ProposalInformation{
				TransactionPackage: primitives.TransactionPackage{
					StartRoot:     block.Header.StateRoot,
					EndRoot:       block.Header.StateRoot,
					Updates:       nil,
					Verifications: nil,
					Transactions:  nil,
				},
				startSlot: block.Header.Slot,
			}
		}
		mgr.lock.Unlock()
	}
	for proposalSlot, mgr := range s.slotProposals {
		if slot >= proposalSlot-1 {
			time.Sleep(200 * time.Millisecond)
			mgr.RequestPackages(block.Header.StateRoot)
		}
	}
}
func (s *ShardSyncManager) FinalizeBlockHash(blockHash chainhash.Hash, slot uint64) {}
func (s *ShardSyncManager) PeerConnected(id peer.ID, dir network.Direction) {
	if dir == network.DirInbound {
		err := s.protocols.shard.SendMessage(id, &pb.ShardVersionMessage{
			Version: 0,
			Height:  s.manager.Chain.Height(),
		})
		if err != nil {
			logrus.Error(err)
			return
		}
	}
}