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 }