github.com/koko1123/flow-go-1@v0.29.6/state/cluster/badger/state.go (about)

     1  package badger
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/dgraph-io/badger/v3"
     8  
     9  	"github.com/koko1123/flow-go-1/model/flow"
    10  	"github.com/koko1123/flow-go-1/module"
    11  	"github.com/koko1123/flow-go-1/state/cluster"
    12  	"github.com/koko1123/flow-go-1/storage"
    13  	"github.com/koko1123/flow-go-1/storage/badger/operation"
    14  	"github.com/koko1123/flow-go-1/storage/badger/procedure"
    15  )
    16  
    17  type State struct {
    18  	db        *badger.DB
    19  	clusterID flow.ChainID
    20  }
    21  
    22  // Bootstrap initializes the persistent cluster state with a genesis block.
    23  // The genesis block must have height 0, a parent hash of 32 zero bytes,
    24  // and an empty collection as payload.
    25  func Bootstrap(db *badger.DB, stateRoot *StateRoot) (*State, error) {
    26  	isBootstrapped, err := IsBootstrapped(db, stateRoot.ClusterID())
    27  	if err != nil {
    28  		return nil, fmt.Errorf("failed to determine whether database contains bootstrapped state: %w", err)
    29  	}
    30  	if isBootstrapped {
    31  		return nil, fmt.Errorf("expected empty cluster state for cluster ID %s", stateRoot.ClusterID())
    32  	}
    33  	state := newState(db, stateRoot.ClusterID())
    34  
    35  	genesis := stateRoot.Block()
    36  	// bootstrap cluster state
    37  	err = operation.RetryOnConflict(state.db.Update, func(tx *badger.Txn) error {
    38  		chainID := genesis.Header.ChainID
    39  		// insert the block
    40  		err := procedure.InsertClusterBlock(genesis)(tx)
    41  		if err != nil {
    42  			return fmt.Errorf("could not insert genesis block: %w", err)
    43  		}
    44  		// insert block height -> ID mapping
    45  		err = operation.IndexClusterBlockHeight(chainID, genesis.Header.Height, genesis.ID())(tx)
    46  		if err != nil {
    47  			return fmt.Errorf("failed to map genesis block height to block: %w", err)
    48  		}
    49  		// insert boundary
    50  		err = operation.InsertClusterFinalizedHeight(chainID, genesis.Header.Height)(tx)
    51  		// insert started view for hotstuff
    52  		if err != nil {
    53  			return fmt.Errorf("could not insert genesis boundary: %w", err)
    54  		}
    55  		err = operation.InsertStartedView(chainID, genesis.Header.View)(tx)
    56  		if err != nil {
    57  			return fmt.Errorf("could not insert started view: %w", err)
    58  		}
    59  		// insert voted view for hotstuff
    60  		err = operation.InsertVotedView(chainID, genesis.Header.View)(tx)
    61  		if err != nil {
    62  			return fmt.Errorf("could not insert voted view: %w", err)
    63  		}
    64  
    65  		return nil
    66  	})
    67  	if err != nil {
    68  		return nil, fmt.Errorf("bootstrapping failed: %w", err)
    69  	}
    70  
    71  	return state, nil
    72  }
    73  
    74  func OpenState(db *badger.DB, tracer module.Tracer, headers storage.Headers, payloads storage.ClusterPayloads, clusterID flow.ChainID) (*State, error) {
    75  	isBootstrapped, err := IsBootstrapped(db, clusterID)
    76  	if err != nil {
    77  		return nil, fmt.Errorf("failed to determine whether database contains bootstrapped state: %w", err)
    78  	}
    79  	if !isBootstrapped {
    80  		return nil, fmt.Errorf("expected database to contain bootstrapped state")
    81  	}
    82  	state := newState(db, clusterID)
    83  	return state, nil
    84  }
    85  
    86  func newState(db *badger.DB, clusterID flow.ChainID) *State {
    87  	state := &State{
    88  		db:        db,
    89  		clusterID: clusterID,
    90  	}
    91  	return state
    92  }
    93  
    94  func (s *State) Params() cluster.Params {
    95  	params := &Params{
    96  		state: s,
    97  	}
    98  	return params
    99  }
   100  
   101  func (s *State) Final() cluster.Snapshot {
   102  	// get the finalized block ID
   103  	var blockID flow.Identifier
   104  	err := s.db.View(func(tx *badger.Txn) error {
   105  		var boundary uint64
   106  		err := operation.RetrieveClusterFinalizedHeight(s.clusterID, &boundary)(tx)
   107  		if err != nil {
   108  			return fmt.Errorf("could not retrieve finalized boundary: %w", err)
   109  		}
   110  
   111  		err = operation.LookupClusterBlockHeight(s.clusterID, boundary, &blockID)(tx)
   112  		if err != nil {
   113  			return fmt.Errorf("could not retrieve finalized ID: %w", err)
   114  		}
   115  
   116  		return nil
   117  	})
   118  	if err != nil {
   119  		return &Snapshot{
   120  			err: err,
   121  		}
   122  	}
   123  
   124  	snapshot := &Snapshot{
   125  		state:   s,
   126  		blockID: blockID,
   127  	}
   128  	return snapshot
   129  }
   130  
   131  func (s *State) AtBlockID(blockID flow.Identifier) cluster.Snapshot {
   132  	snapshot := &Snapshot{
   133  		state:   s,
   134  		blockID: blockID,
   135  	}
   136  	return snapshot
   137  }
   138  
   139  // IsBootstrapped returns whether or not the database contains a bootstrapped state
   140  func IsBootstrapped(db *badger.DB, clusterID flow.ChainID) (bool, error) {
   141  	var finalized uint64
   142  	err := db.View(operation.RetrieveClusterFinalizedHeight(clusterID, &finalized))
   143  	if errors.Is(err, storage.ErrNotFound) {
   144  		return false, nil
   145  	}
   146  	if err != nil {
   147  		return false, fmt.Errorf("retrieving finalized height failed: %w", err)
   148  	}
   149  	return true, nil
   150  }