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