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  }