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  }