github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/beacon/blsync/block_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 blsync 18 19 import ( 20 "github.com/ethereum/go-ethereum/beacon/light/request" 21 "github.com/ethereum/go-ethereum/beacon/light/sync" 22 "github.com/ethereum/go-ethereum/beacon/params" 23 "github.com/ethereum/go-ethereum/beacon/types" 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/common/lru" 26 "github.com/ethereum/go-ethereum/event" 27 "github.com/ethereum/go-ethereum/log" 28 ) 29 30 // beaconBlockSync implements request.Module; it fetches the beacon blocks belonging 31 // to the validated and prefetch heads. 32 type beaconBlockSync struct { 33 recentBlocks *lru.Cache[common.Hash, *types.BeaconBlock] 34 locked map[common.Hash]request.ServerAndID 35 serverHeads map[request.Server]common.Hash 36 headTracker headTracker 37 38 lastHeadInfo types.HeadInfo 39 chainHeadFeed event.FeedOf[types.ChainHeadEvent] 40 } 41 42 type headTracker interface { 43 PrefetchHead() types.HeadInfo 44 ValidatedOptimistic() (types.OptimisticUpdate, bool) 45 ValidatedFinality() (types.FinalityUpdate, bool) 46 } 47 48 // newBeaconBlockSync returns a new beaconBlockSync. 49 func newBeaconBlockSync(headTracker headTracker) *beaconBlockSync { 50 return &beaconBlockSync{ 51 headTracker: headTracker, 52 recentBlocks: lru.NewCache[common.Hash, *types.BeaconBlock](10), 53 locked: make(map[common.Hash]request.ServerAndID), 54 serverHeads: make(map[request.Server]common.Hash), 55 } 56 } 57 58 func (s *beaconBlockSync) SubscribeChainHead(ch chan<- types.ChainHeadEvent) event.Subscription { 59 return s.chainHeadFeed.Subscribe(ch) 60 } 61 62 // Process implements request.Module. 63 func (s *beaconBlockSync) Process(requester request.Requester, events []request.Event) { 64 for _, event := range events { 65 switch event.Type { 66 case request.EvResponse, request.EvFail, request.EvTimeout: 67 sid, req, resp := event.RequestInfo() 68 blockRoot := common.Hash(req.(sync.ReqBeaconBlock)) 69 log.Debug("Beacon block event", "type", event.Type.Name, "hash", blockRoot) 70 if resp != nil { 71 s.recentBlocks.Add(blockRoot, resp.(*types.BeaconBlock)) 72 } 73 if s.locked[blockRoot] == sid { 74 delete(s.locked, blockRoot) 75 } 76 case sync.EvNewHead: 77 s.serverHeads[event.Server] = event.Data.(types.HeadInfo).BlockRoot 78 case request.EvUnregistered: 79 delete(s.serverHeads, event.Server) 80 } 81 } 82 s.updateEventFeed() 83 // request validated head block if unavailable and not yet requested 84 if vh, ok := s.headTracker.ValidatedOptimistic(); ok { 85 s.tryRequestBlock(requester, vh.Attested.Hash(), false) 86 } 87 // request prefetch head if the given server has announced it 88 if prefetchHead := s.headTracker.PrefetchHead().BlockRoot; prefetchHead != (common.Hash{}) { 89 s.tryRequestBlock(requester, prefetchHead, true) 90 } 91 } 92 93 func (s *beaconBlockSync) tryRequestBlock(requester request.Requester, blockRoot common.Hash, needSameHead bool) { 94 if _, ok := s.recentBlocks.Get(blockRoot); ok { 95 return 96 } 97 if _, ok := s.locked[blockRoot]; ok { 98 return 99 } 100 for _, server := range requester.CanSendTo() { 101 if needSameHead && (s.serverHeads[server] != blockRoot) { 102 continue 103 } 104 id := requester.Send(server, sync.ReqBeaconBlock(blockRoot)) 105 s.locked[blockRoot] = request.ServerAndID{Server: server, ID: id} 106 return 107 } 108 } 109 110 func blockHeadInfo(block *types.BeaconBlock) types.HeadInfo { 111 if block == nil { 112 return types.HeadInfo{} 113 } 114 return types.HeadInfo{Slot: block.Slot(), BlockRoot: block.Root()} 115 } 116 117 func (s *beaconBlockSync) updateEventFeed() { 118 optimistic, ok := s.headTracker.ValidatedOptimistic() 119 if !ok { 120 return 121 } 122 123 validatedHead := optimistic.Attested.Hash() 124 headBlock, ok := s.recentBlocks.Get(validatedHead) 125 if !ok { 126 return 127 } 128 129 var finalizedHash common.Hash 130 if finality, ok := s.headTracker.ValidatedFinality(); ok { 131 he := optimistic.Attested.Epoch() 132 fe := finality.Attested.Header.Epoch() 133 switch { 134 case he == fe: 135 finalizedHash = finality.Finalized.PayloadHeader.BlockHash() 136 case he < fe: 137 return 138 case he == fe+1: 139 parent, ok := s.recentBlocks.Get(optimistic.Attested.ParentRoot) 140 if !ok || parent.Slot()/params.EpochLength == fe { 141 return // head is at first slot of next epoch, wait for finality update 142 } 143 } 144 } 145 146 headInfo := blockHeadInfo(headBlock) 147 if headInfo == s.lastHeadInfo { 148 return 149 } 150 s.lastHeadInfo = headInfo 151 152 // new head block and finality info available; extract executable data and send event to feed 153 execBlock, err := headBlock.ExecutionPayload() 154 if err != nil { 155 log.Error("Error extracting execution block from validated beacon block", "error", err) 156 return 157 } 158 s.chainHeadFeed.Send(types.ChainHeadEvent{ 159 BeaconHead: optimistic.Attested.Header, 160 Block: execBlock, 161 Finalized: finalizedHash, 162 }) 163 }