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 }