github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/light/sync/head_sync.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package sync
    18  
    19  import (
    20  	"github.com/ethereum/go-ethereum/beacon/light/request"
    21  	"github.com/ethereum/go-ethereum/beacon/types"
    22  	"github.com/ethereum/go-ethereum/log"
    23  )
    24  
    25  type headTracker interface {
    26  	ValidateOptimistic(update types.OptimisticUpdate) (bool, error)
    27  	ValidateFinality(head types.FinalityUpdate) (bool, error)
    28  	ValidatedFinality() (types.FinalityUpdate, bool)
    29  	SetPrefetchHead(head types.HeadInfo)
    30  }
    31  
    32  // HeadSync implements request.Module; it updates the validated and prefetch
    33  // heads of HeadTracker based on the EvHead and EvSignedHead events coming from
    34  // registered servers.
    35  // It can also postpone the validation of the latest announced signed head
    36  // until the committee chain is synced up to at least the required period.
    37  type HeadSync struct {
    38  	headTracker           headTracker
    39  	chain                 committeeChain
    40  	nextSyncPeriod        uint64
    41  	chainInit             bool
    42  	unvalidatedOptimistic map[request.Server]types.OptimisticUpdate
    43  	unvalidatedFinality   map[request.Server]types.FinalityUpdate
    44  	serverHeads           map[request.Server]types.HeadInfo
    45  	reqFinalityEpoch      map[request.Server]uint64 // next epoch to request finality update
    46  	headServerCount       map[types.HeadInfo]headServerCount
    47  	headCounter           uint64
    48  	prefetchHead          types.HeadInfo
    49  }
    50  
    51  // headServerCount is associated with most recently seen head infos; it counts
    52  // the number of servers currently having the given head info as their announced
    53  // head and a counter signaling how recent that head is.
    54  // This data is used for selecting the prefetch head.
    55  type headServerCount struct {
    56  	serverCount int
    57  	headCounter uint64
    58  }
    59  
    60  // NewHeadSync creates a new HeadSync.
    61  func NewHeadSync(headTracker headTracker, chain committeeChain) *HeadSync {
    62  	s := &HeadSync{
    63  		headTracker:           headTracker,
    64  		chain:                 chain,
    65  		unvalidatedOptimistic: make(map[request.Server]types.OptimisticUpdate),
    66  		unvalidatedFinality:   make(map[request.Server]types.FinalityUpdate),
    67  		serverHeads:           make(map[request.Server]types.HeadInfo),
    68  		headServerCount:       make(map[types.HeadInfo]headServerCount),
    69  		reqFinalityEpoch:      make(map[request.Server]uint64),
    70  	}
    71  	return s
    72  }
    73  
    74  // Process implements request.Module.
    75  func (s *HeadSync) Process(requester request.Requester, events []request.Event) {
    76  	nextPeriod, chainInit := s.chain.NextSyncPeriod()
    77  	if nextPeriod != s.nextSyncPeriod || chainInit != s.chainInit {
    78  		s.nextSyncPeriod, s.chainInit = nextPeriod, chainInit
    79  		s.processUnvalidatedUpdates()
    80  	}
    81  
    82  	for _, event := range events {
    83  		switch event.Type {
    84  		case EvNewHead:
    85  			s.setServerHead(event.Server, event.Data.(types.HeadInfo))
    86  		case EvNewOptimisticUpdate:
    87  			update := event.Data.(types.OptimisticUpdate)
    88  			s.newOptimisticUpdate(event.Server, update)
    89  			epoch := update.Attested.Epoch()
    90  			if epoch < s.reqFinalityEpoch[event.Server] {
    91  				continue
    92  			}
    93  			if finality, ok := s.headTracker.ValidatedFinality(); ok && finality.Attested.Header.Epoch() >= epoch {
    94  				continue
    95  			}
    96  			requester.Send(event.Server, ReqFinality{})
    97  			s.reqFinalityEpoch[event.Server] = epoch + 1
    98  		case EvNewFinalityUpdate:
    99  			s.newFinalityUpdate(event.Server, event.Data.(types.FinalityUpdate))
   100  		case request.EvResponse:
   101  			_, _, resp := event.RequestInfo()
   102  			s.newFinalityUpdate(event.Server, resp.(types.FinalityUpdate))
   103  		case request.EvUnregistered:
   104  			s.setServerHead(event.Server, types.HeadInfo{})
   105  			delete(s.serverHeads, event.Server)
   106  			delete(s.unvalidatedOptimistic, event.Server)
   107  			delete(s.unvalidatedFinality, event.Server)
   108  		}
   109  	}
   110  }
   111  
   112  // newOptimisticUpdate handles received optimistic update; either validates it if
   113  // the chain is properly synced or stores it for further validation.
   114  func (s *HeadSync) newOptimisticUpdate(server request.Server, optimisticUpdate types.OptimisticUpdate) {
   115  	if !s.chainInit || types.SyncPeriod(optimisticUpdate.SignatureSlot) > s.nextSyncPeriod {
   116  		s.unvalidatedOptimistic[server] = optimisticUpdate
   117  		return
   118  	}
   119  	if _, err := s.headTracker.ValidateOptimistic(optimisticUpdate); err != nil {
   120  		log.Debug("Error validating optimistic update", "error", err)
   121  	}
   122  }
   123  
   124  // newFinalityUpdate handles received finality update; either validates it if
   125  // the chain is properly synced or stores it for further validation.
   126  func (s *HeadSync) newFinalityUpdate(server request.Server, finalityUpdate types.FinalityUpdate) {
   127  	if !s.chainInit || types.SyncPeriod(finalityUpdate.SignatureSlot) > s.nextSyncPeriod {
   128  		s.unvalidatedFinality[server] = finalityUpdate
   129  		return
   130  	}
   131  	if _, err := s.headTracker.ValidateFinality(finalityUpdate); err != nil {
   132  		log.Debug("Error validating finality update", "error", err)
   133  	}
   134  }
   135  
   136  // processUnvalidatedUpdates iterates the list of unvalidated updates and validates
   137  // those which can be validated.
   138  func (s *HeadSync) processUnvalidatedUpdates() {
   139  	if !s.chainInit {
   140  		return
   141  	}
   142  	for server, optimisticUpdate := range s.unvalidatedOptimistic {
   143  		if types.SyncPeriod(optimisticUpdate.SignatureSlot) <= s.nextSyncPeriod {
   144  			if _, err := s.headTracker.ValidateOptimistic(optimisticUpdate); err != nil {
   145  				log.Debug("Error validating deferred optimistic update", "error", err)
   146  			}
   147  			delete(s.unvalidatedOptimistic, server)
   148  		}
   149  	}
   150  	for server, finalityUpdate := range s.unvalidatedFinality {
   151  		if types.SyncPeriod(finalityUpdate.SignatureSlot) <= s.nextSyncPeriod {
   152  			if _, err := s.headTracker.ValidateFinality(finalityUpdate); err != nil {
   153  				log.Debug("Error validating deferred finality update", "error", err)
   154  			}
   155  			delete(s.unvalidatedFinality, server)
   156  		}
   157  	}
   158  }
   159  
   160  // setServerHead processes non-validated server head announcements and updates
   161  // the prefetch head if necessary.
   162  func (s *HeadSync) setServerHead(server request.Server, head types.HeadInfo) bool {
   163  	if oldHead, ok := s.serverHeads[server]; ok {
   164  		if head == oldHead {
   165  			return false
   166  		}
   167  		h := s.headServerCount[oldHead]
   168  		if h.serverCount--; h.serverCount > 0 {
   169  			s.headServerCount[oldHead] = h
   170  		} else {
   171  			delete(s.headServerCount, oldHead)
   172  		}
   173  	}
   174  	if head != (types.HeadInfo{}) {
   175  		h, ok := s.headServerCount[head]
   176  		if !ok {
   177  			s.headCounter++
   178  			h.headCounter = s.headCounter
   179  		}
   180  		h.serverCount++
   181  		s.headServerCount[head] = h
   182  		s.serverHeads[server] = head
   183  	} else {
   184  		delete(s.serverHeads, server)
   185  	}
   186  	var (
   187  		bestHead     types.HeadInfo
   188  		bestHeadInfo headServerCount
   189  	)
   190  	for head, headServerCount := range s.headServerCount {
   191  		if headServerCount.serverCount > bestHeadInfo.serverCount ||
   192  			(headServerCount.serverCount == bestHeadInfo.serverCount && headServerCount.headCounter > bestHeadInfo.headCounter) {
   193  			bestHead, bestHeadInfo = head, headServerCount
   194  		}
   195  	}
   196  	if bestHead == s.prefetchHead {
   197  		return false
   198  	}
   199  	s.prefetchHead = bestHead
   200  	s.headTracker.SetPrefetchHead(bestHead)
   201  	return true
   202  }