github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/protocol/badger/params.go (about)

     1  package badger
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/dgraph-io/badger/v2"
     7  
     8  	"github.com/onflow/flow-go/model/flow"
     9  	"github.com/onflow/flow-go/state/protocol"
    10  	"github.com/onflow/flow-go/state/protocol/inmem"
    11  	"github.com/onflow/flow-go/storage"
    12  	"github.com/onflow/flow-go/storage/badger/operation"
    13  )
    14  
    15  type Params struct {
    16  	protocol.GlobalParams
    17  	protocol.InstanceParams
    18  }
    19  
    20  var _ protocol.Params = (*Params)(nil)
    21  
    22  // InstanceParams implements the interface protocol.InstanceParams. All functions
    23  // are served on demand directly from the database, _without_ any caching.
    24  type InstanceParams struct {
    25  	db *badger.DB
    26  	// finalizedRoot marks the cutoff of the history this node knows about. It is the block at the tip
    27  	// of the root snapshot used to bootstrap this node - all newer blocks are synced from the network.
    28  	finalizedRoot *flow.Header
    29  	// sealedRoot is the latest sealed block with respect to `finalizedRoot`.
    30  	sealedRoot *flow.Header
    31  	// rootSeal is the seal for block `sealedRoot` - the newest incorporated seal with respect to `finalizedRoot`.
    32  	rootSeal *flow.Seal
    33  }
    34  
    35  var _ protocol.InstanceParams = (*InstanceParams)(nil)
    36  
    37  // ReadInstanceParams reads the instance parameters from the database and returns them as in-memory representation.
    38  // No errors are expected during normal operation.
    39  func ReadInstanceParams(db *badger.DB, headers storage.Headers, seals storage.Seals) (*InstanceParams, error) {
    40  	params := &InstanceParams{
    41  		db: db,
    42  	}
    43  
    44  	// in next section we will read data from the database and cache them,
    45  	// as they are immutable for the runtime of the node.
    46  	err := db.View(func(txn *badger.Txn) error {
    47  		var (
    48  			finalizedRootHeight uint64
    49  			sealedRootHeight    uint64
    50  		)
    51  
    52  		// root height
    53  		err := db.View(operation.RetrieveRootHeight(&finalizedRootHeight))
    54  		if err != nil {
    55  			return fmt.Errorf("could not read root block to populate cache: %w", err)
    56  		}
    57  		// sealed root height
    58  		err = db.View(operation.RetrieveSealedRootHeight(&sealedRootHeight))
    59  		if err != nil {
    60  			return fmt.Errorf("could not read sealed root block to populate cache: %w", err)
    61  		}
    62  
    63  		// look up 'finalized root block'
    64  		var finalizedRootID flow.Identifier
    65  		err = db.View(operation.LookupBlockHeight(finalizedRootHeight, &finalizedRootID))
    66  		if err != nil {
    67  			return fmt.Errorf("could not look up finalized root height: %w", err)
    68  		}
    69  		params.finalizedRoot, err = headers.ByBlockID(finalizedRootID)
    70  		if err != nil {
    71  			return fmt.Errorf("could not retrieve finalized root header: %w", err)
    72  		}
    73  
    74  		// look up the sealed block as of the 'finalized root block'
    75  		var sealedRootID flow.Identifier
    76  		err = db.View(operation.LookupBlockHeight(sealedRootHeight, &sealedRootID))
    77  		if err != nil {
    78  			return fmt.Errorf("could not look up sealed root height: %w", err)
    79  		}
    80  		params.sealedRoot, err = headers.ByBlockID(sealedRootID)
    81  		if err != nil {
    82  			return fmt.Errorf("could not retrieve sealed root header: %w", err)
    83  		}
    84  
    85  		// retrieve the root seal
    86  		params.rootSeal, err = seals.HighestInFork(finalizedRootID)
    87  		if err != nil {
    88  			return fmt.Errorf("could not retrieve root seal: %w", err)
    89  		}
    90  
    91  		return nil
    92  	})
    93  	if err != nil {
    94  		return nil, fmt.Errorf("could not read InstanceParams data to populate cache: %w", err)
    95  	}
    96  
    97  	return params, nil
    98  }
    99  
   100  // EpochFallbackTriggered returns whether epoch fallback mode [EFM] has been triggered.
   101  // EFM is a permanent, spork-scoped state which is triggered when the next
   102  // epoch fails to be committed in the allocated time. Once EFM is triggered,
   103  // it will remain in effect until the next spork.
   104  // TODO for 'leaving Epoch Fallback via special service event'
   105  // No errors are expected during normal operation.
   106  func (p *InstanceParams) EpochFallbackTriggered() (bool, error) {
   107  	var triggered bool
   108  	err := p.db.View(operation.CheckEpochEmergencyFallbackTriggered(&triggered))
   109  	if err != nil {
   110  		return false, fmt.Errorf("could not check epoch fallback triggered: %w", err)
   111  	}
   112  	return triggered, nil
   113  }
   114  
   115  // FinalizedRoot returns the finalized root header of the current protocol state. This will be
   116  // the head of the protocol state snapshot used to bootstrap this state and
   117  // may differ from node to node for the same protocol state.
   118  func (p *InstanceParams) FinalizedRoot() *flow.Header {
   119  	return p.finalizedRoot
   120  }
   121  
   122  // SealedRoot returns the sealed root block. If it's different from FinalizedRoot() block,
   123  // it means the node is bootstrapped from mid-spork.
   124  func (p *InstanceParams) SealedRoot() *flow.Header {
   125  	return p.sealedRoot
   126  }
   127  
   128  // Seal returns the root block seal of the current protocol state. This is the seal for the
   129  // `SealedRoot` block that was used to bootstrap this state. It may differ from node to node.
   130  func (p *InstanceParams) Seal() *flow.Seal {
   131  	return p.rootSeal
   132  }
   133  
   134  // ReadGlobalParams reads the global parameters from the database and returns them as in-memory representation.
   135  // No errors are expected during normal operation.
   136  func ReadGlobalParams(db *badger.DB) (*inmem.Params, error) {
   137  	var sporkID flow.Identifier
   138  	err := db.View(operation.RetrieveSporkID(&sporkID))
   139  	if err != nil {
   140  		return nil, fmt.Errorf("could not get spork id: %w", err)
   141  	}
   142  
   143  	var sporkRootBlockHeight uint64
   144  	err = db.View(operation.RetrieveSporkRootBlockHeight(&sporkRootBlockHeight))
   145  	if err != nil {
   146  		return nil, fmt.Errorf("could not get spork root block height: %w", err)
   147  	}
   148  
   149  	var threshold uint64
   150  	err = db.View(operation.RetrieveEpochCommitSafetyThreshold(&threshold))
   151  	if err != nil {
   152  		return nil, fmt.Errorf("could not get epoch commit safety threshold")
   153  	}
   154  
   155  	var version uint
   156  	err = db.View(operation.RetrieveProtocolVersion(&version))
   157  	if err != nil {
   158  		return nil, fmt.Errorf("could not get protocol version: %w", err)
   159  	}
   160  
   161  	root, err := ReadFinalizedRoot(db) // retrieve root header
   162  	if err != nil {
   163  		return nil, fmt.Errorf("could not get root: %w", err)
   164  	}
   165  
   166  	return inmem.NewParams(
   167  		inmem.EncodableParams{
   168  			ChainID:                    root.ChainID,
   169  			SporkID:                    sporkID,
   170  			SporkRootBlockHeight:       sporkRootBlockHeight,
   171  			ProtocolVersion:            version,
   172  			EpochCommitSafetyThreshold: threshold,
   173  		},
   174  	), nil
   175  }
   176  
   177  // ReadFinalizedRoot retrieves the root block's header from the database.
   178  // This information is immutable for the runtime of the software and may be cached.
   179  func ReadFinalizedRoot(db *badger.DB) (*flow.Header, error) {
   180  	var finalizedRootHeight uint64
   181  	var rootID flow.Identifier
   182  	var rootHeader flow.Header
   183  	err := db.View(func(tx *badger.Txn) error {
   184  		err := operation.RetrieveRootHeight(&finalizedRootHeight)(tx)
   185  		if err != nil {
   186  			return fmt.Errorf("could not retrieve finalized root height: %w", err)
   187  		}
   188  		err = operation.LookupBlockHeight(finalizedRootHeight, &rootID)(tx) // look up root block ID
   189  		if err != nil {
   190  			return fmt.Errorf("could not retrieve root header's ID by height: %w", err)
   191  		}
   192  		err = operation.RetrieveHeader(rootID, &rootHeader)(tx) // retrieve root header
   193  		if err != nil {
   194  			return fmt.Errorf("could not retrieve root header: %w", err)
   195  		}
   196  		return nil
   197  	})
   198  	if err != nil {
   199  		return nil, fmt.Errorf("failed to read root information from database: %w", err)
   200  	}
   201  	return &rootHeader, nil
   202  }