github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/storage/badger/dkg_state.go (about) 1 package badger 2 3 import ( 4 "fmt" 5 6 "github.com/dgraph-io/badger/v2" 7 "github.com/onflow/crypto" 8 9 "github.com/onflow/flow-go/model/encodable" 10 "github.com/onflow/flow-go/model/flow" 11 "github.com/onflow/flow-go/module" 12 "github.com/onflow/flow-go/module/metrics" 13 "github.com/onflow/flow-go/storage/badger/operation" 14 "github.com/onflow/flow-go/storage/badger/transaction" 15 ) 16 17 // DKGState stores state information about in-progress and completed DKGs, including 18 // computed keys. Must be instantiated using secrets database. 19 type DKGState struct { 20 db *badger.DB 21 keyCache *Cache[uint64, *encodable.RandomBeaconPrivKey] 22 } 23 24 // NewDKGState returns the DKGState implementation backed by Badger DB. 25 func NewDKGState(collector module.CacheMetrics, db *badger.DB) (*DKGState, error) { 26 err := operation.EnsureSecretDB(db) 27 if err != nil { 28 return nil, fmt.Errorf("cannot instantiate dkg state storage in non-secret db: %w", err) 29 } 30 31 storeKey := func(epochCounter uint64, info *encodable.RandomBeaconPrivKey) func(*transaction.Tx) error { 32 return transaction.WithTx(operation.InsertMyBeaconPrivateKey(epochCounter, info)) 33 } 34 35 retrieveKey := func(epochCounter uint64) func(*badger.Txn) (*encodable.RandomBeaconPrivKey, error) { 36 return func(tx *badger.Txn) (*encodable.RandomBeaconPrivKey, error) { 37 var info encodable.RandomBeaconPrivKey 38 err := operation.RetrieveMyBeaconPrivateKey(epochCounter, &info)(tx) 39 return &info, err 40 } 41 } 42 43 cache := newCache[uint64, *encodable.RandomBeaconPrivKey](collector, metrics.ResourceBeaconKey, 44 withLimit[uint64, *encodable.RandomBeaconPrivKey](10), 45 withStore(storeKey), 46 withRetrieve(retrieveKey), 47 ) 48 49 dkgState := &DKGState{ 50 db: db, 51 keyCache: cache, 52 } 53 54 return dkgState, nil 55 } 56 57 func (ds *DKGState) storeKeyTx(epochCounter uint64, key *encodable.RandomBeaconPrivKey) func(tx *transaction.Tx) error { 58 return ds.keyCache.PutTx(epochCounter, key) 59 } 60 61 func (ds *DKGState) retrieveKeyTx(epochCounter uint64) func(tx *badger.Txn) (*encodable.RandomBeaconPrivKey, error) { 62 return func(tx *badger.Txn) (*encodable.RandomBeaconPrivKey, error) { 63 val, err := ds.keyCache.Get(epochCounter)(tx) 64 if err != nil { 65 return nil, err 66 } 67 return val, nil 68 } 69 } 70 71 // InsertMyBeaconPrivateKey stores the random beacon private key for an epoch. 72 // 73 // CAUTION: these keys are stored before they are validated against the 74 // canonical key vector and may not be valid for use in signing. Use SafeBeaconKeys 75 // to guarantee only keys safe for signing are returned 76 func (ds *DKGState) InsertMyBeaconPrivateKey(epochCounter uint64, key crypto.PrivateKey) error { 77 if key == nil { 78 return fmt.Errorf("will not store nil beacon key") 79 } 80 encodableKey := &encodable.RandomBeaconPrivKey{PrivateKey: key} 81 return operation.RetryOnConflictTx(ds.db, transaction.Update, ds.storeKeyTx(epochCounter, encodableKey)) 82 } 83 84 // RetrieveMyBeaconPrivateKey retrieves the random beacon private key for an epoch. 85 // 86 // CAUTION: these keys are stored before they are validated against the 87 // canonical key vector and may not be valid for use in signing. Use SafeBeaconKeys 88 // to guarantee only keys safe for signing are returned 89 func (ds *DKGState) RetrieveMyBeaconPrivateKey(epochCounter uint64) (crypto.PrivateKey, error) { 90 tx := ds.db.NewTransaction(false) 91 defer tx.Discard() 92 encodableKey, err := ds.retrieveKeyTx(epochCounter)(tx) 93 if err != nil { 94 return nil, err 95 } 96 return encodableKey.PrivateKey, nil 97 } 98 99 // SetDKGStarted sets the flag indicating the DKG has started for the given epoch. 100 func (ds *DKGState) SetDKGStarted(epochCounter uint64) error { 101 return ds.db.Update(operation.InsertDKGStartedForEpoch(epochCounter)) 102 } 103 104 // GetDKGStarted checks whether the DKG has been started for the given epoch. 105 func (ds *DKGState) GetDKGStarted(epochCounter uint64) (bool, error) { 106 var started bool 107 err := ds.db.View(operation.RetrieveDKGStartedForEpoch(epochCounter, &started)) 108 return started, err 109 } 110 111 // SetDKGEndState stores that the DKG has ended, and its end state. 112 func (ds *DKGState) SetDKGEndState(epochCounter uint64, endState flow.DKGEndState) error { 113 return ds.db.Update(operation.InsertDKGEndStateForEpoch(epochCounter, endState)) 114 } 115 116 // GetDKGEndState retrieves the DKG end state for the epoch. 117 func (ds *DKGState) GetDKGEndState(epochCounter uint64) (flow.DKGEndState, error) { 118 var endState flow.DKGEndState 119 err := ds.db.Update(operation.RetrieveDKGEndStateForEpoch(epochCounter, &endState)) 120 return endState, err 121 } 122 123 // SafeBeaconPrivateKeys is the safe beacon key storage backed by Badger DB. 124 type SafeBeaconPrivateKeys struct { 125 state *DKGState 126 } 127 128 // NewSafeBeaconPrivateKeys returns a safe beacon key storage backed by Badger DB. 129 func NewSafeBeaconPrivateKeys(state *DKGState) *SafeBeaconPrivateKeys { 130 return &SafeBeaconPrivateKeys{state: state} 131 } 132 133 // RetrieveMyBeaconPrivateKey retrieves my beacon private key for the given 134 // epoch, only if my key has been confirmed valid and safe for use. 135 // 136 // Returns: 137 // - (key, true, nil) if the key is present and confirmed valid 138 // - (nil, false, nil) if the key has been marked invalid or unavailable 139 // -> no beacon key will ever be available for the epoch in this case 140 // - (nil, false, storage.ErrNotFound) if the DKG has not ended 141 // - (nil, false, error) for any unexpected exception 142 func (keys *SafeBeaconPrivateKeys) RetrieveMyBeaconPrivateKey(epochCounter uint64) (key crypto.PrivateKey, safe bool, err error) { 143 err = keys.state.db.View(func(txn *badger.Txn) error { 144 145 // retrieve the end state 146 var endState flow.DKGEndState 147 err = operation.RetrieveDKGEndStateForEpoch(epochCounter, &endState)(txn) 148 if err != nil { 149 key = nil 150 safe = false 151 return err // storage.ErrNotFound or exception 152 } 153 154 // for any end state besides success, the key is not safe 155 if endState != flow.DKGEndStateSuccess { 156 key = nil 157 safe = false 158 return nil 159 } 160 161 // retrieve the key - any storage error (including not found) is an exception 162 var encodableKey *encodable.RandomBeaconPrivKey 163 encodableKey, err = keys.state.retrieveKeyTx(epochCounter)(txn) 164 if err != nil { 165 key = nil 166 safe = false 167 return fmt.Errorf("[unexpected] could not retrieve beacon key for epoch %d with successful DKG: %v", epochCounter, err) 168 } 169 170 // return the key only for successful end state 171 safe = true 172 key = encodableKey.PrivateKey 173 return nil 174 }) 175 return 176 }