github.com/onflow/flow-go@v0.33.17/module/finalizer/consensus/finalizer.go (about) 1 // (c) 2019 Dapper Labs - ALL RIGHTS RESERVED 2 3 package consensus 4 5 import ( 6 "context" 7 "fmt" 8 9 "github.com/dgraph-io/badger/v2" 10 11 "github.com/onflow/flow-go/model/flow" 12 "github.com/onflow/flow-go/module" 13 "github.com/onflow/flow-go/module/trace" 14 "github.com/onflow/flow-go/state/protocol" 15 "github.com/onflow/flow-go/storage" 16 "github.com/onflow/flow-go/storage/badger/operation" 17 ) 18 19 // Finalizer is a simple wrapper around our temporary state to clean up after a 20 // block has been fully finalized to the persistent protocol state. 21 type Finalizer struct { 22 db *badger.DB 23 headers storage.Headers 24 state protocol.FollowerState 25 cleanup CleanupFunc 26 tracer module.Tracer 27 } 28 29 // NewFinalizer creates a new finalizer for the temporary state. 30 func NewFinalizer(db *badger.DB, 31 headers storage.Headers, 32 state protocol.FollowerState, 33 tracer module.Tracer, 34 options ...func(*Finalizer)) *Finalizer { 35 f := &Finalizer{ 36 db: db, 37 state: state, 38 headers: headers, 39 cleanup: CleanupNothing(), 40 tracer: tracer, 41 } 42 for _, option := range options { 43 option(f) 44 } 45 return f 46 } 47 48 // MakeFinal will finalize the block with the given ID and clean up the memory 49 // pools after it. 50 // 51 // This assumes that guarantees and seals are already in persistent state when 52 // included in a block proposal. Between entering the non-finalized chain state 53 // and being finalized, entities should be present in both the volatile memory 54 // pools and persistent storage. 55 // No errors are expected during normal operation. 56 func (f *Finalizer) MakeFinal(blockID flow.Identifier) error { 57 58 span, ctx := f.tracer.StartBlockSpan(context.Background(), blockID, trace.CONFinalizerFinalizeBlock) 59 defer span.End() 60 61 // STEP ONE: This is an idempotent operation. In case we are trying to 62 // finalize a block that is already below finalized height, we want to do 63 // one of two things: if it conflicts with the block already finalized at 64 // that height, it's an invalid operation. Otherwise, it is a no-op. 65 66 var finalized uint64 67 err := f.db.View(operation.RetrieveFinalizedHeight(&finalized)) 68 if err != nil { 69 return fmt.Errorf("could not retrieve finalized height: %w", err) 70 } 71 72 pending, err := f.headers.ByBlockID(blockID) 73 if err != nil { 74 return fmt.Errorf("could not retrieve pending header: %w", err) 75 } 76 77 if pending.Height <= finalized { 78 dupID, err := f.headers.BlockIDByHeight(pending.Height) 79 if err != nil { 80 return fmt.Errorf("could not retrieve finalized equivalent: %w", err) 81 } 82 if dupID != blockID { 83 return fmt.Errorf("cannot finalize pending block conflicting with finalized state (height: %d, pending: %x, finalized: %x)", pending.Height, blockID, dupID) 84 } 85 return nil 86 } 87 88 // STEP TWO: At least one block in the chain back to the finalized state is 89 // a valid candidate for finalization. Figure out all blocks between the 90 // to-be-finalized block and the last finalized block. If we can't trace 91 // back to the last finalized block, this is also an invalid call. 92 93 var finalID flow.Identifier 94 err = f.db.View(operation.LookupBlockHeight(finalized, &finalID)) 95 if err != nil { 96 return fmt.Errorf("could not retrieve finalized header: %w", err) 97 } 98 pendingIDs := []flow.Identifier{blockID} 99 ancestorID := pending.ParentID 100 for ancestorID != finalID { 101 ancestor, err := f.headers.ByBlockID(ancestorID) 102 if err != nil { 103 return fmt.Errorf("could not retrieve parent (%x): %w", ancestorID, err) 104 } 105 if ancestor.Height < finalized { 106 return fmt.Errorf("cannot finalize pending block unconnected to last finalized block (height: %d, finalized: %d)", ancestor.Height, finalized) 107 } 108 pendingIDs = append(pendingIDs, ancestorID) 109 ancestorID = ancestor.ParentID 110 } 111 112 // STEP THREE: We walk backwards through the collected ancestors, starting 113 // with the first block after finalizing state, and finalize them one by 114 // one in the protocol state. 115 116 for i := len(pendingIDs) - 1; i >= 0; i-- { 117 pendingID := pendingIDs[i] 118 err = f.state.Finalize(ctx, pendingID) 119 if err != nil { 120 return fmt.Errorf("could not finalize block (%x): %w", pendingID, err) 121 } 122 err := f.cleanup(pendingID) 123 if err != nil { 124 return fmt.Errorf("could not execute cleanup (%x): %w", pendingID, err) 125 } 126 } 127 128 return nil 129 }