github.com/prysmaticlabs/prysm@v1.4.4/beacon-chain/sync/service.go (about)

     1  // Package sync includes all chain-synchronization logic for the beacon node,
     2  // including gossip-sub validators for blocks, attestations, and other p2p
     3  // messages, as well as ability to process and respond to block requests
     4  // by peers.
     5  package sync
     6  
     7  import (
     8  	"context"
     9  	"sync"
    10  	"time"
    11  
    12  	lru "github.com/hashicorp/golang-lru"
    13  	"github.com/libp2p/go-libp2p-core/peer"
    14  	"github.com/libp2p/go-libp2p-core/protocol"
    15  	gcache "github.com/patrickmn/go-cache"
    16  	"github.com/pkg/errors"
    17  	"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
    18  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed"
    19  	blockfeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/block"
    20  	"github.com/prysmaticlabs/prysm/beacon-chain/core/feed/operation"
    21  	statefeed "github.com/prysmaticlabs/prysm/beacon-chain/core/feed/state"
    22  	"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
    23  	"github.com/prysmaticlabs/prysm/beacon-chain/db"
    24  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/attestations"
    25  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/slashings"
    26  	"github.com/prysmaticlabs/prysm/beacon-chain/operations/voluntaryexits"
    27  	"github.com/prysmaticlabs/prysm/beacon-chain/p2p"
    28  	"github.com/prysmaticlabs/prysm/beacon-chain/state/stategen"
    29  	"github.com/prysmaticlabs/prysm/cmd/beacon-chain/flags"
    30  	ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
    31  	"github.com/prysmaticlabs/prysm/shared"
    32  	"github.com/prysmaticlabs/prysm/shared/abool"
    33  	"github.com/prysmaticlabs/prysm/shared/params"
    34  	"github.com/prysmaticlabs/prysm/shared/runutil"
    35  	"github.com/prysmaticlabs/prysm/shared/slotutil"
    36  	"github.com/prysmaticlabs/prysm/shared/timeutils"
    37  )
    38  
    39  var _ shared.Service = (*Service)(nil)
    40  
    41  const rangeLimit = 1024
    42  const seenBlockSize = 1000
    43  const seenUnaggregatedAttSize = 20000
    44  const seenAggregatedAttSize = 1024
    45  const seenExitSize = 100
    46  const seenProposerSlashingSize = 100
    47  const badBlockSize = 1000
    48  const syncMetricsInterval = 10 * time.Second
    49  
    50  var (
    51  	// Seconds in one epoch.
    52  	pendingBlockExpTime = time.Duration(params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().SecondsPerSlot)) * time.Second
    53  	// time to allow processing early blocks.
    54  	earlyBlockProcessingTolerance = slotutil.MultiplySlotBy(2)
    55  	// time to allow processing early attestations.
    56  	earlyAttestationProcessingTolerance = params.BeaconNetworkConfig().MaximumGossipClockDisparity
    57  )
    58  
    59  // Config to set up the regular sync service.
    60  type Config struct {
    61  	P2P                 p2p.P2P
    62  	DB                  db.NoHeadAccessDatabase
    63  	AttPool             attestations.Pool
    64  	ExitPool            voluntaryexits.PoolManager
    65  	SlashingPool        slashings.PoolManager
    66  	Chain               blockchainService
    67  	InitialSync         Checker
    68  	StateNotifier       statefeed.Notifier
    69  	BlockNotifier       blockfeed.Notifier
    70  	AttestationNotifier operation.Notifier
    71  	StateGen            *stategen.State
    72  }
    73  
    74  // This defines the interface for interacting with block chain service
    75  type blockchainService interface {
    76  	blockchain.BlockReceiver
    77  	blockchain.HeadFetcher
    78  	blockchain.FinalizationFetcher
    79  	blockchain.ForkFetcher
    80  	blockchain.AttestationReceiver
    81  	blockchain.TimeFetcher
    82  	blockchain.GenesisFetcher
    83  	blockchain.CanonicalFetcher
    84  }
    85  
    86  // Service is responsible for handling all run time p2p related operations as the
    87  // main entry point for network messages.
    88  type Service struct {
    89  	cfg                              *Config
    90  	ctx                              context.Context
    91  	cancel                           context.CancelFunc
    92  	slotToPendingBlocks              *gcache.Cache
    93  	seenPendingBlocks                map[[32]byte]bool
    94  	blkRootToPendingAtts             map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof
    95  	pendingAttsLock                  sync.RWMutex
    96  	pendingQueueLock                 sync.RWMutex
    97  	chainStarted                     *abool.AtomicBool
    98  	validateBlockLock                sync.RWMutex
    99  	rateLimiter                      *limiter
   100  	seenBlockLock                    sync.RWMutex
   101  	seenBlockCache                   *lru.Cache
   102  	seenAggregatedAttestationLock    sync.RWMutex
   103  	seenAggregatedAttestationCache   *lru.Cache
   104  	seenUnAggregatedAttestationLock  sync.RWMutex
   105  	seenUnAggregatedAttestationCache *lru.Cache
   106  	seenExitLock                     sync.RWMutex
   107  	seenExitCache                    *lru.Cache
   108  	seenProposerSlashingLock         sync.RWMutex
   109  	seenProposerSlashingCache        *lru.Cache
   110  	seenAttesterSlashingLock         sync.RWMutex
   111  	seenAttesterSlashingCache        map[uint64]bool
   112  	badBlockCache                    *lru.Cache
   113  	badBlockLock                     sync.RWMutex
   114  }
   115  
   116  // NewService initializes new regular sync service.
   117  func NewService(ctx context.Context, cfg *Config) *Service {
   118  	c := gcache.New(pendingBlockExpTime /* exp time */, 2*pendingBlockExpTime /* prune time */)
   119  
   120  	rLimiter := newRateLimiter(cfg.P2P)
   121  	ctx, cancel := context.WithCancel(ctx)
   122  	r := &Service{
   123  		cfg:                  cfg,
   124  		ctx:                  ctx,
   125  		cancel:               cancel,
   126  		chainStarted:         abool.New(),
   127  		slotToPendingBlocks:  c,
   128  		seenPendingBlocks:    make(map[[32]byte]bool),
   129  		blkRootToPendingAtts: make(map[[32]byte][]*ethpb.SignedAggregateAttestationAndProof),
   130  		rateLimiter:          rLimiter,
   131  	}
   132  
   133  	go r.registerHandlers()
   134  
   135  	return r
   136  }
   137  
   138  // Start the regular sync service.
   139  func (s *Service) Start() {
   140  	if err := s.initCaches(); err != nil {
   141  		panic(err)
   142  	}
   143  
   144  	s.cfg.P2P.AddConnectionHandler(s.reValidatePeer, s.sendGoodbye)
   145  	s.cfg.P2P.AddDisconnectionHandler(func(_ context.Context, _ peer.ID) error {
   146  		// no-op
   147  		return nil
   148  	})
   149  	s.cfg.P2P.AddPingMethod(s.sendPingRequest)
   150  	s.processPendingBlocksQueue()
   151  	s.processPendingAttsQueue()
   152  	s.maintainPeerStatuses()
   153  	if !flags.Get().DisableSync {
   154  		s.resyncIfBehind()
   155  	}
   156  
   157  	// Update sync metrics.
   158  	runutil.RunEvery(s.ctx, syncMetricsInterval, s.updateMetrics)
   159  }
   160  
   161  // Stop the regular sync service.
   162  func (s *Service) Stop() error {
   163  	defer func() {
   164  		if s.rateLimiter != nil {
   165  			s.rateLimiter.free()
   166  		}
   167  	}()
   168  	// Removing RPC Stream handlers.
   169  	for _, p := range s.cfg.P2P.Host().Mux().Protocols() {
   170  		s.cfg.P2P.Host().RemoveStreamHandler(protocol.ID(p))
   171  	}
   172  	// Deregister Topic Subscribers.
   173  	for _, t := range s.cfg.P2P.PubSub().GetTopics() {
   174  		if err := s.cfg.P2P.PubSub().UnregisterTopicValidator(t); err != nil {
   175  			log.Errorf("Could not successfully unregister for topic %s: %v", t, err)
   176  		}
   177  	}
   178  	defer s.cancel()
   179  	return nil
   180  }
   181  
   182  // Status of the currently running regular sync service.
   183  func (s *Service) Status() error {
   184  	// If our head slot is on a previous epoch and our peers are reporting their head block are
   185  	// in the most recent epoch, then we might be out of sync.
   186  	if headEpoch := helpers.SlotToEpoch(s.cfg.Chain.HeadSlot()); headEpoch+1 < helpers.SlotToEpoch(s.cfg.Chain.CurrentSlot()) &&
   187  		headEpoch+1 < s.cfg.P2P.Peers().HighestEpoch() {
   188  		return errors.New("out of sync")
   189  	}
   190  	return nil
   191  }
   192  
   193  // This initializes the caches to update seen beacon objects coming in from the wire
   194  // and prevent DoS.
   195  func (s *Service) initCaches() error {
   196  	blkCache, err := lru.New(seenBlockSize)
   197  	if err != nil {
   198  		return err
   199  	}
   200  	aggregatedAttCache, err := lru.New(seenAggregatedAttSize)
   201  	if err != nil {
   202  		return err
   203  	}
   204  	unAggregatedAttCache, err := lru.New(seenUnaggregatedAttSize)
   205  	if err != nil {
   206  		return err
   207  	}
   208  	exitCache, err := lru.New(seenExitSize)
   209  	if err != nil {
   210  		return err
   211  	}
   212  	proposerSlashingCache, err := lru.New(seenProposerSlashingSize)
   213  	if err != nil {
   214  		return err
   215  	}
   216  	badBlockCache, err := lru.New(badBlockSize)
   217  	if err != nil {
   218  		return err
   219  	}
   220  	s.seenBlockCache = blkCache
   221  	s.seenAggregatedAttestationCache = aggregatedAttCache
   222  	s.seenUnAggregatedAttestationCache = unAggregatedAttCache
   223  	s.seenExitCache = exitCache
   224  	s.seenAttesterSlashingCache = make(map[uint64]bool)
   225  	s.seenProposerSlashingCache = proposerSlashingCache
   226  	s.badBlockCache = badBlockCache
   227  
   228  	return nil
   229  }
   230  
   231  func (s *Service) registerHandlers() {
   232  	// Wait until chain start.
   233  	stateChannel := make(chan *feed.Event, 1)
   234  	stateSub := s.cfg.StateNotifier.StateFeed().Subscribe(stateChannel)
   235  	defer stateSub.Unsubscribe()
   236  	for {
   237  		select {
   238  		case event := <-stateChannel:
   239  			switch event.Type {
   240  			case statefeed.Initialized:
   241  				data, ok := event.Data.(*statefeed.InitializedData)
   242  				if !ok {
   243  					log.Error("Event feed data is not type *statefeed.InitializedData")
   244  					return
   245  				}
   246  				startTime := data.StartTime
   247  				log.WithField("starttime", startTime).Debug("Received state initialized event")
   248  
   249  				// Register respective rpc handlers at state initialized event.
   250  				s.registerRPCHandlers()
   251  				// Wait for chainstart in separate routine.
   252  				go func() {
   253  					if startTime.After(timeutils.Now()) {
   254  						time.Sleep(timeutils.Until(startTime))
   255  					}
   256  					log.WithField("starttime", startTime).Debug("Chain started in sync service")
   257  					s.markForChainStart()
   258  				}()
   259  			case statefeed.Synced:
   260  				_, ok := event.Data.(*statefeed.SyncedData)
   261  				if !ok {
   262  					log.Error("Event feed data is not type *statefeed.SyncedData")
   263  					return
   264  				}
   265  				// Register respective pubsub handlers at state synced event.
   266  				s.registerSubscribers()
   267  				return
   268  			}
   269  		case <-s.ctx.Done():
   270  			log.Debug("Context closed, exiting goroutine")
   271  			return
   272  		case err := <-stateSub.Err():
   273  			log.WithError(err).Error("Could not subscribe to state notifier")
   274  			return
   275  		}
   276  	}
   277  }
   278  
   279  // marks the chain as having started.
   280  func (s *Service) markForChainStart() {
   281  	s.chainStarted.Set()
   282  }
   283  
   284  // Checker defines a struct which can verify whether a node is currently
   285  // synchronizing a chain with the rest of peers in the network.
   286  type Checker interface {
   287  	Initialized() bool
   288  	Syncing() bool
   289  	Synced() bool
   290  	Status() error
   291  	Resync() error
   292  }