github.com/koko1123/flow-go-1@v0.29.6/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/v3" 10 11 "github.com/koko1123/flow-go-1/model/flow" 12 "github.com/koko1123/flow-go-1/module" 13 "github.com/koko1123/flow-go-1/module/trace" 14 "github.com/koko1123/flow-go-1/state/protocol" 15 "github.com/koko1123/flow-go-1/storage" 16 "github.com/koko1123/flow-go-1/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.MutableState 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.MutableState, 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 func (f *Finalizer) MakeFinal(blockID flow.Identifier) error { 56 57 span, ctx := f.tracer.StartBlockSpan(context.Background(), blockID, trace.CONFinalizerFinalizeBlock) 58 defer span.End() 59 60 // STEP ONE: This is an idempotent operation. In case we are trying to 61 // finalize a block that is already below finalized height, we want to do 62 // one of two things: if it conflicts with the block already finalized at 63 // that height, it's an invalid operation. Otherwise, it is a no-op. 64 65 var finalized uint64 66 err := f.db.View(operation.RetrieveFinalizedHeight(&finalized)) 67 if err != nil { 68 return fmt.Errorf("could not retrieve finalized height: %w", err) 69 } 70 71 pending, err := f.headers.ByBlockID(blockID) 72 if err != nil { 73 return fmt.Errorf("could not retrieve pending header: %w", err) 74 } 75 76 if pending.Height <= finalized { 77 dup, err := f.headers.ByHeight(pending.Height) 78 if err != nil { 79 return fmt.Errorf("could not retrieve finalized equivalent: %w", err) 80 } 81 if dup.ID() != blockID { 82 return fmt.Errorf("cannot finalize pending block conflicting with finalized state (height: %d, pending: %x, finalized: %x)", pending.Height, blockID, dup.ID()) 83 } 84 return nil 85 } 86 87 // STEP TWO: At least one block in the chain back to the finalized state is 88 // a valid candidate for finalization. Figure out all blocks between the 89 // to-be-finalized block and the last finalized block. If we can't trace 90 // back to the last finalized block, this is also an invalid call. 91 92 var finalID flow.Identifier 93 err = f.db.View(operation.LookupBlockHeight(finalized, &finalID)) 94 if err != nil { 95 return fmt.Errorf("could not retrieve finalized header: %w", err) 96 } 97 pendingIDs := []flow.Identifier{blockID} 98 ancestorID := pending.ParentID 99 for ancestorID != finalID { 100 ancestor, err := f.headers.ByBlockID(ancestorID) 101 if err != nil { 102 return fmt.Errorf("could not retrieve parent (%x): %w", ancestorID, err) 103 } 104 if ancestor.Height < finalized { 105 return fmt.Errorf("cannot finalize pending block unconnected to last finalized block (height: %d, finalized: %d)", ancestor.Height, finalized) 106 } 107 pendingIDs = append(pendingIDs, ancestorID) 108 ancestorID = ancestor.ParentID 109 } 110 111 // STEP THREE: We walk backwards through the collected ancestors, starting 112 // with the first block after finalizing state, and finalize them one by 113 // one in the protocol state. 114 115 for i := len(pendingIDs) - 1; i >= 0; i-- { 116 pendingID := pendingIDs[i] 117 err = f.state.Finalize(ctx, pendingID) 118 if err != nil { 119 return fmt.Errorf("could not finalize block (%x): %w", pendingID, err) 120 } 121 err := f.cleanup(pendingID) 122 if err != nil { 123 return fmt.Errorf("could not execute cleanup (%x): %w", pendingID, err) 124 } 125 } 126 127 return nil 128 } 129 130 // MakeValid marks a block as having passed HotStuff validation. 131 func (f *Finalizer) MakeValid(blockID flow.Identifier) error { 132 err := f.state.MarkValid(blockID) 133 if err != nil { 134 return fmt.Errorf("could not mark block as valid (%x): %w", blockID, err) 135 } 136 return nil 137 }