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 }