github.com/koko1123/flow-go-1@v0.29.6/engine/common/synchronization/finalized_snapshot.go (about) 1 package synchronization 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/rs/zerolog" 8 9 "github.com/koko1123/flow-go-1/consensus/hotstuff/model" 10 "github.com/koko1123/flow-go-1/consensus/hotstuff/notifications/pubsub" 11 "github.com/koko1123/flow-go-1/engine" 12 "github.com/koko1123/flow-go-1/model/flow" 13 "github.com/koko1123/flow-go-1/module/lifecycle" 14 "github.com/koko1123/flow-go-1/state/protocol" 15 ) 16 17 // FinalizedHeaderCache represents the cached value of the latest finalized header. 18 // It is used in Engine to access latest valid data. 19 type FinalizedHeaderCache struct { 20 mu sync.RWMutex 21 22 log zerolog.Logger 23 state protocol.State 24 lastFinalizedHeader *flow.Header 25 finalizationEventNotifier engine.Notifier // notifier for finalization events 26 27 lm *lifecycle.LifecycleManager 28 stopped chan struct{} 29 } 30 31 // NewFinalizedHeaderCache creates a new finalized header cache. 32 func NewFinalizedHeaderCache(log zerolog.Logger, state protocol.State, finalizationDistributor *pubsub.FinalizationDistributor) (*FinalizedHeaderCache, error) { 33 cache := &FinalizedHeaderCache{ 34 state: state, 35 lm: lifecycle.NewLifecycleManager(), 36 log: log.With().Str("component", "finalized_snapshot_cache").Logger(), 37 finalizationEventNotifier: engine.NewNotifier(), 38 stopped: make(chan struct{}), 39 } 40 41 snapshot, err := cache.getHeader() 42 if err != nil { 43 return nil, fmt.Errorf("could not apply last finalized state") 44 } 45 46 cache.lastFinalizedHeader = snapshot 47 48 finalizationDistributor.AddOnBlockFinalizedConsumer(cache.onFinalizedBlock) 49 50 return cache, nil 51 } 52 53 // Get returns the last locally cached finalized header. 54 func (f *FinalizedHeaderCache) Get() *flow.Header { 55 f.mu.RLock() 56 defer f.mu.RUnlock() 57 return f.lastFinalizedHeader 58 } 59 60 func (f *FinalizedHeaderCache) getHeader() (*flow.Header, error) { 61 finalSnapshot := f.state.Final() 62 head, err := finalSnapshot.Head() 63 if err != nil { 64 return nil, fmt.Errorf("could not get last finalized header: %w", err) 65 } 66 67 return head, nil 68 } 69 70 // updateHeader updates latest locally cached finalized header. 71 func (f *FinalizedHeaderCache) updateHeader() error { 72 f.log.Debug().Msg("updating header") 73 74 head, err := f.getHeader() 75 if err != nil { 76 f.log.Err(err).Msg("failed to get header") 77 return err 78 } 79 80 f.log.Debug(). 81 Str("block_id", head.ID().String()). 82 Uint64("height", head.Height). 83 Msg("got new header") 84 85 f.mu.Lock() 86 defer f.mu.Unlock() 87 88 if f.lastFinalizedHeader.Height < head.Height { 89 f.lastFinalizedHeader = head 90 } 91 92 return nil 93 } 94 95 func (f *FinalizedHeaderCache) Ready() <-chan struct{} { 96 f.lm.OnStart(func() { 97 go f.finalizationProcessingLoop() 98 }) 99 return f.lm.Started() 100 } 101 102 func (f *FinalizedHeaderCache) Done() <-chan struct{} { 103 f.lm.OnStop(func() { 104 <-f.stopped 105 }) 106 return f.lm.Stopped() 107 } 108 109 // onFinalizedBlock implements the `OnFinalizedBlock` callback from the `hotstuff.FinalizationConsumer` 110 // (1) Updates local state of last finalized snapshot. 111 // 112 // CAUTION: the input to this callback is treated as trusted; precautions should be taken that messages 113 // from external nodes cannot be considered as inputs to this function 114 func (f *FinalizedHeaderCache) onFinalizedBlock(block *model.Block) { 115 f.log.Debug().Str("block_id", block.BlockID.String()).Msg("received new block finalization callback") 116 // notify that there is new finalized block 117 f.finalizationEventNotifier.Notify() 118 } 119 120 // finalizationProcessingLoop is a separate goroutine that performs processing of finalization events 121 func (f *FinalizedHeaderCache) finalizationProcessingLoop() { 122 defer close(f.stopped) 123 124 f.log.Debug().Msg("starting finalization processing loop") 125 notifier := f.finalizationEventNotifier.Channel() 126 for { 127 select { 128 case <-f.lm.ShutdownSignal(): 129 return 130 case <-notifier: 131 err := f.updateHeader() 132 if err != nil { 133 f.log.Fatal().Err(err).Msg("could not process latest finalized block") 134 } 135 } 136 } 137 }