github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/light/head_tracker.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 light
    18  
    19  import (
    20  	"errors"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/beacon/types"
    25  	"github.com/ethereum/go-ethereum/log"
    26  )
    27  
    28  // HeadTracker keeps track of the latest validated head and the "prefetch" head
    29  // which is the (not necessarily validated) head announced by the majority of
    30  // servers.
    31  type HeadTracker struct {
    32  	lock                sync.RWMutex
    33  	committeeChain      *CommitteeChain
    34  	minSignerCount      int
    35  	optimisticUpdate    types.OptimisticUpdate
    36  	hasOptimisticUpdate bool
    37  	finalityUpdate      types.FinalityUpdate
    38  	hasFinalityUpdate   bool
    39  	prefetchHead        types.HeadInfo
    40  	changeCounter       uint64
    41  }
    42  
    43  // NewHeadTracker creates a new HeadTracker.
    44  func NewHeadTracker(committeeChain *CommitteeChain, minSignerCount int) *HeadTracker {
    45  	return &HeadTracker{
    46  		committeeChain: committeeChain,
    47  		minSignerCount: minSignerCount,
    48  	}
    49  }
    50  
    51  // ValidatedOptimistic returns the latest validated optimistic update.
    52  func (h *HeadTracker) ValidatedOptimistic() (types.OptimisticUpdate, bool) {
    53  	h.lock.RLock()
    54  	defer h.lock.RUnlock()
    55  
    56  	return h.optimisticUpdate, h.hasOptimisticUpdate
    57  }
    58  
    59  // ValidatedFinality returns the latest validated finality update.
    60  func (h *HeadTracker) ValidatedFinality() (types.FinalityUpdate, bool) {
    61  	h.lock.RLock()
    62  	defer h.lock.RUnlock()
    63  
    64  	return h.finalityUpdate, h.hasFinalityUpdate
    65  }
    66  
    67  // ValidateOptimistic validates the given optimistic update. If the update is
    68  // successfully validated and it is better than the old validated update (higher
    69  // slot or same slot and more signers) then ValidatedOptimistic is updated.
    70  // The boolean return flag signals if ValidatedOptimistic has been changed.
    71  func (h *HeadTracker) ValidateOptimistic(update types.OptimisticUpdate) (bool, error) {
    72  	h.lock.Lock()
    73  	defer h.lock.Unlock()
    74  
    75  	if err := update.Validate(); err != nil {
    76  		return false, err
    77  	}
    78  	replace, err := h.validate(update.SignedHeader(), h.optimisticUpdate.SignedHeader())
    79  	if replace {
    80  		h.optimisticUpdate, h.hasOptimisticUpdate = update, true
    81  		h.changeCounter++
    82  	}
    83  	return replace, err
    84  }
    85  
    86  // ValidateFinality validates the given finality update. If the update is
    87  // successfully validated and it is better than the old validated update (higher
    88  // slot or same slot and more signers) then ValidatedFinality is updated.
    89  // The boolean return flag signals if ValidatedFinality has been changed.
    90  func (h *HeadTracker) ValidateFinality(update types.FinalityUpdate) (bool, error) {
    91  	h.lock.Lock()
    92  	defer h.lock.Unlock()
    93  
    94  	if err := update.Validate(); err != nil {
    95  		return false, err
    96  	}
    97  	replace, err := h.validate(update.SignedHeader(), h.finalityUpdate.SignedHeader())
    98  	if replace {
    99  		h.finalityUpdate, h.hasFinalityUpdate = update, true
   100  		h.changeCounter++
   101  	}
   102  	return replace, err
   103  }
   104  
   105  func (h *HeadTracker) validate(head, oldHead types.SignedHeader) (bool, error) {
   106  	signerCount := head.Signature.SignerCount()
   107  	if signerCount < h.minSignerCount {
   108  		return false, errors.New("low signer count")
   109  	}
   110  	if head.Header.Slot < oldHead.Header.Slot || (head.Header.Slot == oldHead.Header.Slot && signerCount <= oldHead.Signature.SignerCount()) {
   111  		return false, nil
   112  	}
   113  	sigOk, age, err := h.committeeChain.VerifySignedHeader(head)
   114  	if err != nil {
   115  		return false, err
   116  	}
   117  	if age < 0 {
   118  		log.Warn("Future signed head received", "age", age)
   119  	}
   120  	if age > time.Minute*2 {
   121  		log.Warn("Old signed head received", "age", age)
   122  	}
   123  	if !sigOk {
   124  		return false, errors.New("invalid header signature")
   125  	}
   126  	return true, nil
   127  }
   128  
   129  // PrefetchHead returns the latest known prefetch head's head info.
   130  // This head can be used to start fetching related data hoping that it will be
   131  // validated soon.
   132  // Note that the prefetch head cannot be validated cryptographically so it should
   133  // only be used as a performance optimization hint.
   134  func (h *HeadTracker) PrefetchHead() types.HeadInfo {
   135  	h.lock.RLock()
   136  	defer h.lock.RUnlock()
   137  
   138  	return h.prefetchHead
   139  }
   140  
   141  // SetPrefetchHead sets the prefetch head info.
   142  // Note that HeadTracker does not verify the prefetch head, just acts as a thread
   143  // safe bulletin board.
   144  func (h *HeadTracker) SetPrefetchHead(head types.HeadInfo) {
   145  	h.lock.Lock()
   146  	defer h.lock.Unlock()
   147  
   148  	if head == h.prefetchHead {
   149  		return
   150  	}
   151  	h.prefetchHead = head
   152  	h.changeCounter++
   153  }
   154  
   155  // ChangeCounter implements request.targetData
   156  func (h *HeadTracker) ChangeCounter() uint64 {
   157  	h.lock.RLock()
   158  	defer h.lock.RUnlock()
   159  
   160  	return h.changeCounter
   161  }